├── .github
├── contributing.md
├── PULL_REQUEST_TEMPLATE
│ ├── gardening.md
│ ├── proposal.md
│ ├── refactor.md
│ ├── documentation.md
│ ├── feature.md
│ └── bug.md
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── proposal.yml
│ ├── config.yml
│ ├── refactor.yml
│ ├── gardening.yml
│ ├── documentation.yml
│ ├── bug.yml
│ └── feature-request.yml
├── workflows
│ ├── changelog.yml
│ └── release.yml
└── commit-convention.md
├── packages
├── hono
│ ├── src
│ │ ├── sentry
│ │ │ ├── index.ts
│ │ │ └── sentry.test.mts
│ │ ├── swagger
│ │ │ ├── index.ts
│ │ │ └── swagger.test.mts
│ │ ├── oauth-providers
│ │ │ ├── index.ts
│ │ │ ├── oauth-providers.test.mts
│ │ │ ├── oauth-providers.utils.ts
│ │ │ └── providers
│ │ │ │ ├── x
│ │ │ │ └── index.ts
│ │ │ │ ├── facebook
│ │ │ │ └── index.ts
│ │ │ │ ├── github
│ │ │ │ └── index.ts
│ │ │ │ └── google
│ │ │ │ └── index.ts
│ │ ├── index.ts
│ │ ├── common.ts
│ │ ├── firebase-auth
│ │ │ └── index.ts
│ │ ├── valibot-validator
│ │ │ ├── index.ts
│ │ │ └── valibot-validator.test.mts
│ │ ├── metrics
│ │ │ └── index.ts
│ │ └── secure-header
│ │ │ └── index.ts
│ ├── README.md
│ ├── biome.json
│ ├── moon.yml
│ ├── tsconfig.build.json
│ ├── CHANGELOG.md
│ ├── tsconfig.json
│ └── package.json
├── http
│ ├── test
│ │ ├── http.headers.test.mts
│ │ └── http.test.mts
│ ├── moon.yml
│ ├── src
│ │ ├── exceptions
│ │ │ ├── index.ts
│ │ │ ├── exception.base.ts
│ │ │ ├── exceptions.ts
│ │ │ └── exception.codes.ts
│ │ ├── index.ts
│ │ ├── http.const.ts
│ │ └── http.utils.ts
│ ├── README.md
│ ├── biome.json
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ ├── package.json
│ └── CHANGELOG.md
├── usage-limit
│ ├── src
│ │ ├── cf.usage.limit.ts
│ │ ├── cf.do.usage-limit.ts
│ │ ├── index.ts
│ │ └── usage-limit.schema.ts
│ ├── moon.yml
│ ├── README.md
│ ├── biome.json
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ ├── package.json
│ └── CHANGELOG.md
├── logger
│ ├── src
│ │ ├── index.ts
│ │ ├── console.ts
│ │ ├── pino.ts
│ │ └── axiom.ts
│ ├── README.md
│ ├── biome.json
│ ├── moon.yml
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ ├── CHANGELOG.md
│ └── package.json
├── metrics
│ ├── src
│ │ ├── index.ts
│ │ └── axiom.metrics.ts
│ ├── moon.yml
│ ├── README.md
│ ├── biome.json
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ ├── CHANGELOG.md
│ └── package.json
├── auth
│ ├── moon.yml
│ ├── src
│ │ ├── jwt
│ │ │ ├── index.ts
│ │ │ ├── verify-decode-jwt.ts
│ │ │ └── types.ts
│ │ ├── index.ts
│ │ ├── const.ts
│ │ └── google-identity
│ │ │ ├── index.ts
│ │ │ ├── fetch-public-keys.ts
│ │ │ ├── usecases
│ │ │ ├── lookup-account.ts
│ │ │ ├── signup-basic.ts
│ │ │ ├── get-exchange-token.ts
│ │ │ ├── signin-basic.ts
│ │ │ └── signin-custom-token.ts
│ │ │ └── error.ts
│ ├── README.md
│ ├── biome.json
│ ├── CHANGELOG.md
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ └── package.json
├── cache
│ ├── moon.yml
│ ├── README.md
│ ├── src
│ │ ├── index.ts
│ │ ├── hono.tiered.cache.ts
│ │ ├── tiered.cache.ts
│ │ ├── metrics.cache.ts
│ │ └── memory.cache.ts
│ ├── biome.json
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ ├── CHANGELOG.md
│ └── package.json
├── rate-limit
│ ├── src
│ │ ├── index.ts
│ │ ├── noop.rate-limit.ts
│ │ ├── cloudflare.do.rate-limit.ts
│ │ └── cloudflare.rate-limit.ts
│ ├── README.md
│ ├── biome.json
│ ├── moon.yml
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ ├── package.json
│ └── CHANGELOG.md
├── vue-pinia-persist
│ ├── moon.yml
│ ├── biome.json
│ ├── tsconfig.json
│ ├── tsconfig.build.json
│ ├── package.json
│ ├── README.md
│ ├── CHANGELOG.md
│ └── src
│ │ ├── types.ts
│ │ └── index.ts
├── nuxt-partytown
│ ├── playground
│ │ ├── tsconfig.json
│ │ ├── server
│ │ │ └── tsconfig.json
│ │ ├── package.json
│ │ ├── nuxt.config.ts
│ │ ├── app.vue
│ │ └── layouts
│ │ │ └── default.vue
│ ├── moon.yml
│ ├── tsconfig.json
│ ├── CHANGELOG.md
│ └── package.json
├── nuxt-pinia-persist
│ ├── playground
│ │ ├── tsconfig.json
│ │ ├── server
│ │ │ └── tsconfig.json
│ │ ├── package.json
│ │ ├── stores
│ │ │ ├── auth.store.ts
│ │ │ └── user.store.ts
│ │ ├── app.vue
│ │ └── nuxt.config.ts
│ ├── src
│ │ ├── runtime
│ │ │ ├── composables.ts
│ │ │ └── plugin.ts
│ │ └── module.ts
│ ├── moon.yml
│ ├── tsconfig.json
│ ├── package.json
│ ├── README.md
│ └── CHANGELOG.md
├── types
│ ├── tsconfig.json
│ ├── moon.yml
│ ├── src
│ │ ├── index.d.ts
│ │ ├── rate-limit.port.d.ts
│ │ ├── logger.port.d.ts
│ │ ├── usage-limit.port.d.ts
│ │ ├── literalType.d.ts
│ │ ├── metrics.port.d.ts
│ │ ├── common.d.ts
│ │ └── cache.port.d.ts
│ ├── README.md
│ ├── package.json
│ └── CHANGELOG.md
├── reset-css
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── package.json
│ └── reset.min.css
├── valibot-stream
│ └── README.md
└── text-annotation
│ └── README.md
├── bun.lockb
├── .husky
├── pre-push
├── commit-msg
├── pre-commit
└── prepare-commit-msg
├── .vscode
├── extensions.json
├── ISSUE_TEMPLATE
│ ├── feature_request.md
│ └── bug_report.md
├── extentions.json
├── es2077.code-snippets
└── settings.json
├── .editorconfig
├── Makefile
├── .changeset
└── config.json
├── bundlesize.config.json
├── .moon
├── toolchain.yml
├── workspace.yml
└── tasks
│ └── typescript.yml
├── codecov.yml
├── tsconfig.json
├── vitest.config.mts
├── tsconfig.base.json
├── biome.json
├── LICENSE
├── package.json
└── README.md
/.github/contributing.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/hono/src/sentry/index.ts:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/hono/src/swagger/index.ts:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE/gardening.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE/proposal.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE/refactor.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/hono/src/oauth-providers/index.ts:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/hono/src/sentry/sentry.test.mts:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/hono/src/swagger/swagger.test.mts:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/http/test/http.headers.test.mts:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/usage-limit/src/cf.usage.limit.ts:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE/documentation.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/usage-limit/src/cf.do.usage-limit.ts:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/hono/src/oauth-providers/oauth-providers.test.mts:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/hono/src/oauth-providers/oauth-providers.utils.ts:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/hono/src/oauth-providers/providers/x/index.ts:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/hono/src/oauth-providers/providers/facebook/index.ts:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/hono/src/oauth-providers/providers/github/index.ts:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/hono/src/oauth-providers/providers/google/index.ts:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/logger/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./console";
2 |
--------------------------------------------------------------------------------
/packages/metrics/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./axiom.metrics";
2 |
--------------------------------------------------------------------------------
/packages/auth/moon.yml:
--------------------------------------------------------------------------------
1 | type: "library"
2 | language: "typescript"
3 |
--------------------------------------------------------------------------------
/packages/cache/moon.yml:
--------------------------------------------------------------------------------
1 | type: "library"
2 | language: "typescript"
3 |
--------------------------------------------------------------------------------
/packages/hono/README.md:
--------------------------------------------------------------------------------
1 | # All the things relate to Techmely's Hono version
--------------------------------------------------------------------------------
/packages/http/moon.yml:
--------------------------------------------------------------------------------
1 | type: "library"
2 | language: "typescript"
3 |
--------------------------------------------------------------------------------
/packages/metrics/moon.yml:
--------------------------------------------------------------------------------
1 | type: "library"
2 | language: "typescript"
3 |
--------------------------------------------------------------------------------
/packages/rate-limit/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./noop.rate-limit";
2 |
--------------------------------------------------------------------------------
/packages/usage-limit/moon.yml:
--------------------------------------------------------------------------------
1 | type: "library"
2 | language: "typescript"
3 |
--------------------------------------------------------------------------------
/packages/usage-limit/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./usage-limit.schema";
2 |
--------------------------------------------------------------------------------
/bun.lockb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/techmely/essential-packages/HEAD/bun.lockb
--------------------------------------------------------------------------------
/packages/vue-pinia-persist/moon.yml:
--------------------------------------------------------------------------------
1 | type: "library"
2 | language: "typescript"
3 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: harrytran998
2 | patreon: demonslight
3 | ko_fi: harrytran998
4 |
--------------------------------------------------------------------------------
/.husky/pre-push:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | echo "\nStart run units test\n"
4 | bun run test.unit.run
5 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "arcanis.vscode-zipfs"
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/packages/auth/src/jwt/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./types";
2 | export * from "./verify-decode-jwt";
3 |
--------------------------------------------------------------------------------
/packages/nuxt-partytown/playground/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./.nuxt/tsconfig.json"
3 | }
4 |
--------------------------------------------------------------------------------
/packages/nuxt-pinia-persist/playground/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./.nuxt/tsconfig.json"
3 | }
4 |
--------------------------------------------------------------------------------
/packages/nuxt-partytown/playground/server/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../.nuxt/tsconfig.server.json"
3 | }
4 |
--------------------------------------------------------------------------------
/packages/nuxt-pinia-persist/playground/server/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../.nuxt/tsconfig.server.json"
3 | }
4 |
--------------------------------------------------------------------------------
/packages/http/src/exceptions/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./exceptions";
2 | export * from "./exception.codes";
3 | export * from "./exception.base";
4 |
--------------------------------------------------------------------------------
/packages/auth/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./const";
2 | export * from "./jwt";
3 | export * from "./google-identity";
4 | export * from "./types";
5 |
--------------------------------------------------------------------------------
/packages/usage-limit/README.md:
--------------------------------------------------------------------------------
1 | # @techmely/usage-limit
2 |
3 |
4 | ## License
5 |
6 | MIT © [TechMeLy](https://github.com/sponsors/TechMeLy)
7 |
--------------------------------------------------------------------------------
/packages/metrics/README.md:
--------------------------------------------------------------------------------
1 | # @techmely/metrics
2 |
3 | Metrics
4 |
5 | ## License
6 |
7 | MIT © [TechMeLy](https://github.com/sponsors/TechMeLy)
8 |
--------------------------------------------------------------------------------
/packages/nuxt-pinia-persist/src/runtime/composables.ts:
--------------------------------------------------------------------------------
1 | import { useNuxtApp } from "nuxt/app";
2 |
3 | export const usePinia = () => useNuxtApp().$pinia;
4 |
--------------------------------------------------------------------------------
/packages/cache/README.md:
--------------------------------------------------------------------------------
1 | # @techmely/cache
2 |
3 | Cache cache cache
4 |
5 | ## License
6 |
7 | MIT © [TechMeLy](https://github.com/sponsors/TechMeLy)
8 |
--------------------------------------------------------------------------------
/packages/hono/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./firebase-auth";
2 | export * from "./secure-header";
3 | export * from "./common";
4 | export * from "./valibot-validator";
5 |
--------------------------------------------------------------------------------
/packages/auth/README.md:
--------------------------------------------------------------------------------
1 | # @techmely/auth
2 |
3 | # Techmely auth's utils solutions
4 |
5 | ## License
6 |
7 | MIT © [TechMeLy](https://github.com/sponsors/TechMeLy)
8 |
--------------------------------------------------------------------------------
/packages/cache/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./hono.tiered.cache";
2 | export * from "./memory.cache";
3 | export * from "./metrics.cache";
4 | export * from "./tiered.cache";
5 |
--------------------------------------------------------------------------------
/packages/nuxt-partytown/moon.yml:
--------------------------------------------------------------------------------
1 | type: "library"
2 | language: "typescript"
3 |
4 | workspace:
5 | inheritedTasks:
6 | exclude: ['build', 'lint', 'clean', 'publish']
7 |
--------------------------------------------------------------------------------
/packages/http/README.md:
--------------------------------------------------------------------------------
1 | # @techmely/logger
2 |
3 | Logger for every runtime environment
4 |
5 | ## License
6 |
7 | MIT © [TechMeLy](https://github.com/sponsors/TechMeLy)
8 |
--------------------------------------------------------------------------------
/packages/logger/README.md:
--------------------------------------------------------------------------------
1 | # @techmely/logger
2 |
3 | Logger for every runtime environment
4 |
5 | ## License
6 |
7 | MIT © [TechMeLy](https://github.com/sponsors/TechMeLy)
8 |
--------------------------------------------------------------------------------
/packages/nuxt-pinia-persist/moon.yml:
--------------------------------------------------------------------------------
1 | type: "library"
2 | language: "typescript"
3 |
4 | workspace:
5 | inheritedTasks:
6 | exclude: ['build', 'lint', 'clean', 'publish']
7 |
--------------------------------------------------------------------------------
/packages/rate-limit/README.md:
--------------------------------------------------------------------------------
1 | # @techmely/rate-limit
2 |
3 | Logger for every runtime environment
4 |
5 | ## License
6 |
7 | MIT © [TechMeLy](https://github.com/sponsors/TechMeLy)
8 |
--------------------------------------------------------------------------------
/packages/auth/biome.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "../../node_modules/@biomejs/biome/configuration_schema.json",
3 | "extends": ["../../biome.json"],
4 | "files": {
5 | "include": ["src"]
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/packages/cache/biome.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "../../node_modules/@biomejs/biome/configuration_schema.json",
3 | "extends": ["../../biome.json"],
4 | "files": {
5 | "include": ["src"]
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/packages/hono/biome.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "../../node_modules/@biomejs/biome/configuration_schema.json",
3 | "extends": ["../../biome.json"],
4 | "files": {
5 | "include": ["src"]
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/packages/http/biome.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "../../node_modules/@biomejs/biome/configuration_schema.json",
3 | "extends": ["../../biome.json"],
4 | "files": {
5 | "include": ["src"]
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/packages/nuxt-partytown/playground/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "my-module-playground",
4 | "type": "module",
5 | "devDependencies": {
6 | "nuxt": "^3.10.1"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/nuxt-partytown/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./playground/.nuxt/tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../.moon/cache/types/packages/nuxt-partytown"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/.vscode/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: enhancement, feature_request
6 | assignees: harrytran998
7 | ---
8 |
--------------------------------------------------------------------------------
/packages/logger/biome.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "../../node_modules/@biomejs/biome/configuration_schema.json",
3 | "extends": ["../../biome.json"],
4 | "files": {
5 | "include": ["src"]
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/packages/metrics/biome.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "../../node_modules/@biomejs/biome/configuration_schema.json",
3 | "extends": ["../../biome.json"],
4 | "files": {
5 | "include": ["src"]
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/packages/rate-limit/biome.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "../../node_modules/@biomejs/biome/configuration_schema.json",
3 | "extends": ["../../biome.json"],
4 | "files": {
5 | "include": ["src"]
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/packages/types/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "outDir": "../../.moon/cache/types/packages/types"
5 | },
6 | "include": ["src/**/*"]
7 | }
8 |
--------------------------------------------------------------------------------
/.vscode/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: bug
6 | assignees: harrytran998
7 | ---
8 |
9 | **Describe the bug**
10 |
--------------------------------------------------------------------------------
/packages/usage-limit/biome.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "../../node_modules/@biomejs/biome/configuration_schema.json",
3 | "extends": ["../../biome.json"],
4 | "files": {
5 | "include": ["src"]
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/packages/vue-pinia-persist/biome.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "../../node_modules/@biomejs/biome/configuration_schema.json",
3 | "extends": ["../../biome.json"],
4 | "files": {
5 | "include": ["src"]
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/packages/hono/moon.yml:
--------------------------------------------------------------------------------
1 | type: "library"
2 | language: "typescript"
3 |
4 | tasks:
5 | build:
6 | options:
7 | mergeArgs: "replace"
8 | args: "src/**/*.ts --clean --dts --format esm --tsconfig tsconfig.build.json"
9 |
--------------------------------------------------------------------------------
/packages/hono/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "composite": false,
5 | "outDir": "dist",
6 | "rootDir": "src"
7 | },
8 | "include": ["src/**/*", "tests"]
9 | }
10 |
--------------------------------------------------------------------------------
/packages/nuxt-pinia-persist/playground/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "@playground/nuxt-pinia-persist",
4 | "type": "module",
5 | "devDependencies": {
6 | "nuxt": "^3.10.1",
7 | "@nuxt/kit": "^3.7.0"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 | [*.md]
13 | trim_trailing_whitespace = false
--------------------------------------------------------------------------------
/.vscode/extentions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "arcanis.vscode-zipfs",
4 | "esbenp.prettier-vscode",
5 | "Orta.vscode-jest",
6 | "mikestead.dotenv",
7 | "usernamehw.errorlens",
8 | "rome.rome",
9 | "arcanis.vscode-zipfs"
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/packages/logger/moon.yml:
--------------------------------------------------------------------------------
1 | type: "library"
2 | language: "typescript"
3 |
4 | tasks:
5 | build:
6 | options:
7 | mergeArgs: "replace"
8 | args: "--entry src/index.ts --entry src/axiom.ts --entry src/pino.ts --clean --dts --format esm --tsconfig tsconfig.build.json"
9 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/proposal.yml:
--------------------------------------------------------------------------------
1 | name: Proposal 📜
2 | description: Propose a new feature, or a change to something existing
3 | title: "[Proposal]: "
4 | labels: ["proposal 📜"]
5 | body:
6 | - type: markdown
7 | attributes:
8 | value: |
9 | Thanks for taking the time to fill this out!
--------------------------------------------------------------------------------
/packages/rate-limit/moon.yml:
--------------------------------------------------------------------------------
1 | type: "library"
2 | language: "typescript"
3 |
4 | tasks:
5 | build:
6 | options:
7 | mergeArgs: "replace"
8 | args: "--entry src/index.ts --entry src/cloudflare.do.rate-limit.ts --entry src/cloudflare.rate-limit.ts --clean --dts --format esm --tsconfig tsconfig.build.json"
9 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | e2e.headless:
2 | bun playwright test --headed
3 |
4 | e2e.open:
5 | bun playwright test --project=chromium --ui
6 |
7 | build:
8 | moon :build
9 |
10 | clean:
11 | moon :clean
12 |
13 | publish:
14 | moon :publish
15 |
16 | upgrade.deps:
17 | upgrade.deps
18 | moon :upgrade.deps
19 | bun install
20 |
--------------------------------------------------------------------------------
/packages/auth/src/const.ts:
--------------------------------------------------------------------------------
1 | export const GOOGLE_IDENTIFY_TOOLKIT_URL = "https://identitytoolkit.googleapis.com";
2 | export const GOOGLE_SECURE_TOKEN_API_URL = "https://securetoken.googleapis.com";
3 | // https://securetoken.google.com
4 | export const GOOGLE_TOOKIT_ACCOUNTS_URL = `${GOOGLE_IDENTIFY_TOOLKIT_URL}/v1/accounts"`;
5 |
--------------------------------------------------------------------------------
/packages/types/moon.yml:
--------------------------------------------------------------------------------
1 | type: "library"
2 | language: "typescript"
3 |
4 | workspace:
5 | inheritedTasks:
6 | exclude: ['build', 'lint', 'clean', "publish"]
7 |
8 | tasks:
9 | publish:
10 | command: "npm publish"
11 | inputs:
12 | - "@globs(sources)"
13 | options:
14 | shell: true
15 |
--------------------------------------------------------------------------------
/packages/auth/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @techmely/auth
2 |
3 | ## 1.0.2
4 |
5 | ### Patch Changes
6 |
7 | - Upgrade deps
8 |
9 | ## 1.0.1
10 |
11 | ### Patch Changes
12 |
13 | - Update unessary logic + typing name decoded id token
14 |
15 | ## 1.0.0
16 |
17 | ### Major Changes
18 |
19 | - Release techmely auth utils solution
20 |
--------------------------------------------------------------------------------
/packages/auth/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "composite": false,
5 | "outDir": "dist",
6 | "rootDir": "src"
7 | },
8 | "include": ["src/**/*", "tests"],
9 | "references": [
10 | {
11 | "path": "../types"
12 | }
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/packages/cache/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "composite": false,
5 | "outDir": "dist",
6 | "rootDir": "src"
7 | },
8 | "include": ["src/**/*", "tests"],
9 | "references": [
10 | {
11 | "path": "../types"
12 | }
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/packages/logger/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "composite": false,
5 | "outDir": "dist",
6 | "rootDir": "src"
7 | },
8 | "include": ["src/**/*", "tests"],
9 | "references": [
10 | {
11 | "path": "../types"
12 | }
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/packages/metrics/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "composite": false,
5 | "outDir": "dist",
6 | "rootDir": "src"
7 | },
8 | "include": ["src/**/*", "tests"],
9 | "references": [
10 | {
11 | "path": "../types"
12 | }
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/packages/metrics/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "outDir": "../../.moon/cache/types/packages/metrics"
5 | },
6 | "references": [
7 | {
8 | "path": "./tsconfig.build.json"
9 | },
10 | {
11 | "path": "../types"
12 | }
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/packages/nuxt-pinia-persist/playground/stores/auth.store.ts:
--------------------------------------------------------------------------------
1 | type AuthStore = {
2 | accessToken?: string;
3 | refreshToken?: string;
4 | };
5 | const key = "auth";
6 |
7 | export const useAuthStore = defineStore(key, {
8 | state: () => ({ accessToken: undefined, refreshToken: undefined }),
9 | });
10 |
--------------------------------------------------------------------------------
/packages/auth/src/google-identity/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./error";
2 | export * from "./fetch-public-keys";
3 |
4 | export * from "./usecases/get-exchange-token";
5 | export * from "./usecases/lookup-account";
6 | export * from "./usecases/signin-basic";
7 | export * from "./usecases/signup-basic";
8 | export * from "./usecases/signin-custom-token";
9 |
--------------------------------------------------------------------------------
/packages/hono/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @techmely/hono
2 |
3 | ## 1.1.0
4 |
5 | ### Minor Changes
6 |
7 | - Updated dependencies [[`93340fa`](https://github.com/techmely/essential-packages/commit/93340fab455bf57fd852703bdd589c88d0e5c8c0)]:
8 | - @techmely/utils@3.3.0
9 |
10 | ## 1.0.0
11 |
12 | ### Major Changes
13 |
14 | - Init hono packages
15 |
--------------------------------------------------------------------------------
/packages/types/src/index.d.ts:
--------------------------------------------------------------------------------
1 | import "@total-typescript/ts-reset";
2 |
3 | export * from "ts-essentials";
4 | export * from "./literalType";
5 | export * from "./common";
6 | export * from "./cache.port";
7 | export * from "./logger.port";
8 | export * from "./metrics.port";
9 | export * from "./rate-limit.port";
10 | export * from "./usage-limit.port";
11 |
--------------------------------------------------------------------------------
/.changeset/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json",
3 | "changelog": [
4 | "@changesets/changelog-github",
5 | {
6 | "repo": "techmely/essential-packages"
7 | }
8 | ],
9 | "commit": false,
10 | "access": "public",
11 | "baseBranch": "main",
12 | "updateInternalDependencies": "patch"
13 | }
14 |
--------------------------------------------------------------------------------
/packages/nuxt-pinia-persist/playground/stores/user.store.ts:
--------------------------------------------------------------------------------
1 | type UserStore = {
2 | info?: Record;
3 | isVIP?: boolean;
4 | };
5 | const key = "user";
6 |
7 | export const useUserStore = defineStore(key, () => {
8 | const user = ref({
9 | info: undefined,
10 | isVIP: undefined,
11 | });
12 |
13 | return { user };
14 | });
15 |
--------------------------------------------------------------------------------
/packages/logger/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "outDir": "../../.moon/cache/types/packages/logger"
5 | },
6 | "include": ["src/**/*", "tests/**/*"],
7 | "references": [
8 | {
9 | "path": "./tsconfig.build.json"
10 | },
11 | {
12 | "path": "../types"
13 | }
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/packages/http/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "composite": false,
5 | "outDir": "dist",
6 | "rootDir": "src"
7 | },
8 | "include": ["src/**/*", "tests"],
9 | "references": [
10 | {
11 | "path": "../types"
12 | },
13 | {
14 | "path": "../utils"
15 | }
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/.husky/commit-msg:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # regex to validate in commit msg
4 | commit_regex='(^[A-Z0-9]{2,8}-[0-9]{1,5}|merge|hotfix|release)'
5 | error_msg="Aborting commit. Your commit message must start with Jira/Clickup/Github code (eg: 'TML-123', 'ONEMIND-1111') or 'Merge'"
6 |
7 | if ! grep -iqE "$commit_regex" "$1"; then
8 | echo "$error_msg" >&2
9 | exit 1
10 | fi
11 |
--------------------------------------------------------------------------------
/packages/types/src/rate-limit.port.d.ts:
--------------------------------------------------------------------------------
1 | export interface RateLimitRequest {
2 | id: string;
3 | limit: number;
4 | interval: number;
5 | }
6 |
7 | export interface RateLimitResponse {
8 | current: number;
9 | reset: number;
10 | passed: boolean;
11 | }
12 |
13 | export interface RateLimiterPort {
14 | limit: (req: RateLimitRequest) => Promise;
15 | }
16 |
--------------------------------------------------------------------------------
/packages/usage-limit/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "composite": false,
5 | "outDir": "dist",
6 | "rootDir": "src"
7 | },
8 | "include": ["src/**/*", "tests"],
9 | "references": [
10 | {
11 | "path": "../logger"
12 | },
13 | {
14 | "path": "../metrics"
15 | }
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/packages/rate-limit/src/noop.rate-limit.ts:
--------------------------------------------------------------------------------
1 | import type { RateLimitRequest, RateLimiterPort } from "@techmely/types";
2 |
3 | export class NoopRateLimiter implements RateLimiterPort {
4 | async limit(_req: RateLimitRequest) {
5 | console.log("Noop limit");
6 | return Promise.resolve({
7 | current: 0,
8 | reset: 0,
9 | passed: true,
10 | });
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/packages/vue-pinia-persist/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "outDir": "../../.moon/cache/types/packages/vue-pinia-persist"
5 | },
6 | "include": ["src/**/*", "tests/**/*"],
7 | "references": [
8 | {
9 | "path": "./tsconfig.build.json"
10 | },
11 | {
12 | "path": "../utils"
13 | }
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/packages/nuxt-partytown/playground/nuxt.config.ts:
--------------------------------------------------------------------------------
1 | export default defineNuxtConfig({
2 | modules: ["../src/module"],
3 | runtimeConfig: {
4 | public: {
5 | gaMeasurementId: process.env.NUXT_PUBLIC_GA_MEASUREMENT_ID,
6 | gtmId: process.env.NUXT_PUBLIC_GTM_ID,
7 | },
8 | },
9 | partytown: {
10 | forward: ["gtag"],
11 | },
12 | devtools: { enabled: true },
13 | });
14 |
--------------------------------------------------------------------------------
/packages/vue-pinia-persist/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "types": ["../vike-react/src/typing.d.ts"],
5 | "composite": false,
6 | "outDir": "dist",
7 | "rootDir": "src"
8 | },
9 | "include": ["src/**/*", "tests/**/*"],
10 | "references": [
11 | {
12 | "path": "../utils"
13 | }
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/packages/usage-limit/src/usage-limit.schema.ts:
--------------------------------------------------------------------------------
1 | import { boolean, number, object, optional, string } from "valibot";
2 |
3 | export const limitRequestSchema = object({
4 | keyId: string(),
5 | });
6 |
7 | export const limitResponseSchema = object({
8 | valid: boolean(),
9 | remaining: optional(number()),
10 | });
11 |
12 | export const revalidateRequestSchema = object({
13 | keyId: string(),
14 | });
15 |
--------------------------------------------------------------------------------
/packages/http/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "outDir": "../../.moon/cache/types/packages/http"
5 | },
6 | "include": ["src/**/*", "tests/**/*"],
7 | "references": [
8 | {
9 | "path": "./tsconfig.build.json"
10 | },
11 | {
12 | "path": "../utils"
13 | },
14 | {
15 | "path": "../types"
16 | }
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/packages/auth/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "outDir": "../../.moon/cache/types/packages/auth"
5 | },
6 | "include": ["src/**/*", "tests/**/*", "moon.yml"],
7 | "references": [
8 | {
9 | "path": "./tsconfig.build.json"
10 | },
11 | {
12 | "path": "../metrics"
13 | },
14 | {
15 | "path": "../types"
16 | }
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/packages/cache/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "outDir": "../../.moon/cache/types/packages/cache"
5 | },
6 | "include": ["src/**/*", "tests/**/*", "moon.yml"],
7 | "references": [
8 | {
9 | "path": "./tsconfig.build.json"
10 | },
11 | {
12 | "path": "../metrics"
13 | },
14 | {
15 | "path": "../types"
16 | }
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/packages/nuxt-pinia-persist/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./playground/.nuxt/tsconfig.json",
3 | "compilerOptions": {
4 | "noImplicitAny": false,
5 | "useUnknownInCatchVariables": false,
6 | "outDir": "../../.moon/cache/types/packages/nuxt-pinia-persist"
7 | },
8 | "references": [
9 | {
10 | "path": "../utils"
11 | },
12 | {
13 | "path": "../vue-pinia-persist"
14 | }
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/packages/rate-limit/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "types": ["@cloudflare/workers-types"],
5 | "composite": false,
6 | "outDir": "dist",
7 | "rootDir": "src"
8 | },
9 | "include": ["src/**/*", "tests"],
10 | "references": [
11 | {
12 | "path": "../logger"
13 | },
14 | {
15 | "path": "../metrics"
16 | }
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/packages/types/src/logger.port.d.ts:
--------------------------------------------------------------------------------
1 | import type { Records } from "./common";
2 |
3 | export interface LoggerPort {
4 | info(message: string, meta?: Records): void;
5 | error(message: string, meta?: Records): void;
6 | warn(message: string, meta?: Records): void;
7 | debug(message: string, meta?: Records): void;
8 | flush(): Promise;
9 | }
10 |
11 | export type LoggerPortLevel = "info" | "error" | "warn" | "debug";
12 |
--------------------------------------------------------------------------------
/packages/usage-limit/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "outDir": "../../.moon/cache/types/packages/usage-limit"
5 | },
6 | "include": ["src/**/*", "tests/**/*"],
7 | "references": [
8 | {
9 | "path": "./tsconfig.build.json"
10 | },
11 | {
12 | "path": "../logger"
13 | },
14 | {
15 | "path": "../metrics"
16 | }
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/bundlesize.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": [
3 | {
4 | "path": "packages/utils/dist/index.js",
5 | "maxSize": "6kB"
6 | },
7 | {
8 | "path": "packages/utils/dist/index.mjs",
9 | "maxSize": "5kB"
10 | },
11 | {
12 | "path": "packages/types/dist/index.mjs",
13 | "maxSize": "5kB"
14 | },
15 | {
16 | "path": "packages/types/dist/index.js",
17 | "maxSize": "5kB"
18 | }
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | textBase='\033[0m'
4 | textInformation='\033[1;34m'
5 | textSuccess='\033[1;32m'
6 |
7 | echo "$textInformation \nStart lint code quality with rome"
8 | bun run lint.check
9 | echo "$textSuccess \nCheck rome format successfully."
10 |
11 | # echo "$textInformation Start to check typings in projects"
12 | # bun run lint:duplicate
13 | # echo "$textSuccess Start to check typings in projects"
14 |
15 | echo "$textBase"
16 |
--------------------------------------------------------------------------------
/packages/types/src/usage-limit.port.d.ts:
--------------------------------------------------------------------------------
1 | export type UsageLimitResponse = {
2 | keyId: string;
3 | };
4 | export type UsageLimitRevalidateRequest = {
5 | valid: boolean;
6 | remaining?: number;
7 | };
8 |
9 | export type UsageLimitRequest = {
10 | keyId: string;
11 | };
12 |
13 | export interface UsageLimiterPort {
14 | limit: (req: UsageLimitRequest) => Promise;
15 | revalidate: (req: UsageLimitRevalidateRequest) => Promise;
16 | }
17 |
--------------------------------------------------------------------------------
/.moon/toolchain.yml:
--------------------------------------------------------------------------------
1 | $schema: 'https://moonrepo.dev/schemas/toolchain.json'
2 |
3 | bun:
4 | version: "1.1.12"
5 | dependencyVersionFormat: "workspace"
6 | inferTasksFromScripts: true
7 | rootPackageOnly: true
8 | syncProjectWorkspaceDependencies: true
9 |
10 | typescript:
11 | projectConfigFileName: "tsconfig.json"
12 | rootConfigFileName: "tsconfig.json"
13 | routeOutDirToCache: true
14 | syncProjectReferences: true
15 |
16 | rust:
17 | version: "1.77.2"
18 | syncToolchainConfig: true
19 |
--------------------------------------------------------------------------------
/packages/hono/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "include": ["src/**/*", "tests/**/*"],
4 | "compilerOptions": {
5 | "outDir": "../../.moon/cache/types/packages/hono"
6 | },
7 | "references": [
8 | {
9 | "path": "./tsconfig.build.json"
10 | },
11 | {
12 | "path": "../auth"
13 | },
14 | {
15 | "path": "../http"
16 | },
17 | {
18 | "path": "../types"
19 | },
20 | {
21 | "path": "../utils"
22 | }
23 | ]
24 | }
25 |
--------------------------------------------------------------------------------
/packages/hono/src/common.ts:
--------------------------------------------------------------------------------
1 | import type { MiddlewareHandler } from "hono";
2 |
3 | export function commonContext(): MiddlewareHandler {
4 | return (c, next) => {
5 | c.set(
6 | "location",
7 | c.req.header("True-Client-IP") ??
8 | c.req.header("CF-Connecting-IP") ??
9 | // @ts-expect-error - the cf object will be there on cloudflare
10 | c.req.raw?.cf?.colo ??
11 | "",
12 | );
13 | c.set("userAgent", c.req.header("User-Agent"));
14 |
15 | return next();
16 | };
17 | }
18 |
--------------------------------------------------------------------------------
/packages/rate-limit/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "outDir": "../../.moon/cache/types/packages/rate-limit",
5 | "types": ["@cloudflare/workers-types"]
6 | },
7 | "include": ["src/**/*", "tests/**/*"],
8 | "references": [
9 | {
10 | "path": "./tsconfig.build.json"
11 | },
12 | {
13 | "path": "../logger"
14 | },
15 | {
16 | "path": "../metrics"
17 | },
18 | {
19 | "path": "../types"
20 | }
21 | ]
22 | }
23 |
--------------------------------------------------------------------------------
/packages/nuxt-pinia-persist/playground/app.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | Nuxt module playground!
4 | {{ auth.accessToken }}
5 | {{ user.user }}
6 |
7 |
8 |
9 |
22 |
--------------------------------------------------------------------------------
/packages/nuxt-pinia-persist/playground/nuxt.config.ts:
--------------------------------------------------------------------------------
1 | export default defineNuxtConfig({
2 | modules: ["../src/module.ts"],
3 | "pinia-persist": {
4 | persistAllExcept: ["user"],
5 | onAfterRestore: (context) => {
6 | context.store.payment = "VISA";
7 | console.log("onAfterRestore");
8 | },
9 | },
10 | devtools: { enabled: true },
11 | experimental: {
12 | headNext: true,
13 | componentIslands: true,
14 | viewTransition: true,
15 | writeEarlyHints: true,
16 | typedPages: true,
17 | },
18 | });
19 |
--------------------------------------------------------------------------------
/codecov.yml:
--------------------------------------------------------------------------------
1 | # configuration related to pull request comments
2 | comment: false # do not comment PR with the result
3 |
4 | ignore:
5 | - '**/deprecated/**'
6 | coverage:
7 | range: 50..90 # coverage lower than 50 is red, higher than 90 green, between color code
8 |
9 | status:
10 | project: # settings affecting project coverage
11 | default:
12 | target: auto # auto % coverage target
13 | threshold: 1% # allow for 1% reduction of coverage without failing
14 |
15 | # do not run coverage on patch nor changes
16 | patch: off
17 |
--------------------------------------------------------------------------------
/.vscode/es2077.code-snippets:
--------------------------------------------------------------------------------
1 | {
2 | "Import type": {
3 | "prefix": "impt",
4 | "body": ["import type { $2 } from '$1'"],
5 | "description": "Import type"
6 | },
7 | "Console log": {
8 | "prefix": "clg",
9 | "body": ["console.log($1)"],
10 | "description": "Console log"
11 | },
12 | "Export * from XXX": {
13 | "prefix": "exs",
14 | "body": ["export * from './${0}';"]
15 | },
16 | "Export * from XXX + Type": {
17 | "prefix": "exst",
18 | "body": ["export * from './${0}';", "export * from './${0}.types';"]
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/packages/reset-css/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @techmely/reset-css
2 |
3 | ## 1.0.6
4 |
5 | ### Patch Changes
6 |
7 | - Upgrade deps
8 |
9 | ## 1.0.5
10 |
11 | ### Patch Changes
12 |
13 | - Update the workflow we use to build this project
14 |
15 | ## 1.0.4
16 |
17 | ### Patch Changes
18 |
19 | - Bump version & upgrade deps
20 |
21 | ## 1.0.3
22 |
23 | ### Patch Changes
24 |
25 | - Fix: add border none to button
26 |
27 | ## 1.0.2
28 |
29 | ### Patch Changes
30 |
31 | - Fix exports files
32 |
33 | ## 1.0.1
34 |
35 | ### Patch Changes
36 |
37 | - Remove font-family + update heading styles
38 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | name: Sample name
2 | description: Sample description
3 | blank_issues_enabled: true
4 | contact_links:
5 | - name: Discord Chat
6 | url: https://chat.techmely.com
7 | about: Ask questions and discuss with Techmely's team in real time.
8 | - name: Questions & Discussions
9 | url: https://github.com/harrytran998/techmely/discussions
10 | about: Use GitHub discussions for message-board style questions and discussions.
11 | - name: Donate
12 | url: https://opencollective.com/techmely/donate
13 | about: Love Techmely product? Please consider supporting us via Open Collective.
14 |
--------------------------------------------------------------------------------
/packages/reset-css/README.md:
--------------------------------------------------------------------------------
1 | # @techmely/reset-css
2 |
3 | The customize reset css with the minimal config work for every modern browsers.
4 |
5 | ## How to use
6 |
7 | Here is some example using in our projects
8 |
9 | ### Nuxtjs
10 |
11 | ```ts
12 | // In nuxt.config.ts
13 | export default defineNuxtConfig({
14 | css: ['@techmely/reset-css'],
15 | // Or
16 | // css: ['@techmely/reset-css/minify']
17 | })
18 | ```
19 |
20 | ### Nextjs
21 |
22 | ```ts
23 | // In _app.tsx
24 | import '@techmely/reset-css'
25 | ```
26 |
27 |
28 | ### Vite App
29 |
30 | ```ts
31 | // in main.ts
32 | import '@techmely/reset-css'
33 | ```
34 |
--------------------------------------------------------------------------------
/packages/types/README.md:
--------------------------------------------------------------------------------
1 | # @techmely/utils
2 |
3 |
4 | Opinionated collection of helpful JavaScript / TypeScript utils which we use on the real world applications
5 |
6 |
7 | > Attention: This package is designed to be used as `devDependencies` and bundled into your dist.
8 |
9 |
10 |
11 | ## Features
12 |
13 | Use this when your project install dayjs
14 | export * from './dayjs';
15 |
16 | Node.js only --> Import nested modules to use them in the node environment
17 | export *from './git';
18 | export* from './file';
19 |
20 | ## License
21 |
22 | MIT © [TechMeLy](https://github.com/sponsors/TechMeLy)
23 |
--------------------------------------------------------------------------------
/packages/auth/src/google-identity/fetch-public-keys.ts:
--------------------------------------------------------------------------------
1 | /**
2 | // Grab the public key from https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com
3 | // and use a JWT library to verify the signature. Use the value of max-age in the Cache-Control header of the response
4 | // from that endpoint to know when to refresh the public keys.
5 | */
6 | export const fetchGooglePublicKeys = async (): Promise> => {
7 | const response = await fetch(
8 | "https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com",
9 | );
10 | const json = await response.json();
11 | return json;
12 | };
13 |
--------------------------------------------------------------------------------
/.moon/workspace.yml:
--------------------------------------------------------------------------------
1 | $schema: https://moonrepo.dev/schemas/workspace.json
2 | projects:
3 | - packages/*
4 | vcs:
5 | defaultBranch: main
6 | manager: git
7 | hooks:
8 | commit-msg:
9 | - echo "commit msg"
10 | pre-commit:
11 | - echo "pre commit msg"
12 | pre-push:
13 | - echo "pre push"
14 | prepare-commit-msg:
15 | - echo "prepare commit msg"
16 |
17 | hasher:
18 | optimization: "performance"
19 |
20 | runner:
21 | cacheLifetime: "24 hours"
22 | archivableTargets:
23 | - ":clean"
24 | - ":format"
25 | - ":lint"
26 | - ":test"
27 | - ":typecheck"
28 | - ":publish"
29 | - ":upgrade.deps"
30 | logRunningCommand: true
31 |
32 | telemetry: false
33 |
--------------------------------------------------------------------------------
/packages/http/test/http.test.mts:
--------------------------------------------------------------------------------
1 | // import { afterEach, beforeEach, describe, expect, it, test, vi } from "vitest";
2 | // import Http from "../src";
3 |
4 | // describe("Main test", () => {
5 | // let testServer: CreateTestServer;
6 | // beforeEach(async () => {
7 | // testServer = await createTestServer();
8 | // });
9 |
10 | // afterEach(() => {
11 | // vi.resetAllMocks();
12 | // testServer.app.close();
13 | // });
14 |
15 | // test("Http", () => {
16 | // testServer.app.get("/api", (res, req) => {
17 | // const result = JSON.stringify({ method: req.getMethod() });
18 | // res.writeStatus(String(200)).end(result);
19 | // });
20 | // const http = Http.create({ headers: { hello: "world" } });
21 | // });
22 | // });
23 |
--------------------------------------------------------------------------------
/packages/auth/src/google-identity/usecases/lookup-account.ts:
--------------------------------------------------------------------------------
1 | import { GOOGLE_TOOKIT_ACCOUNTS_URL } from "../../const";
2 | import type { AuthGetAccountInfoResponse, AuthLookUpRequest, FirebaseConfigs } from "../../types";
3 | import { GoogleIdentityError } from "../error";
4 |
5 | export async function googleAccountLookUp(
6 | config: FirebaseConfigs,
7 | payload: AuthLookUpRequest,
8 | ): Promise {
9 | const response = await fetch(`${GOOGLE_TOOKIT_ACCOUNTS_URL}:lookup?key=${config.apiKey}`, {
10 | method: "POST",
11 | body: JSON.stringify(payload),
12 | headers: {
13 | "Content-Type": "application/json",
14 | },
15 | });
16 |
17 | const json = await response.json();
18 | if (!response.ok) {
19 | throw new GoogleIdentityError(json);
20 | }
21 | return json;
22 | }
23 |
--------------------------------------------------------------------------------
/packages/auth/src/google-identity/error.ts:
--------------------------------------------------------------------------------
1 | import type { AuthGoogleIdentifyErrorResponse } from "../types";
2 |
3 | export class GoogleIdentityError extends Error {
4 | readonly errorResponse: AuthGoogleIdentifyErrorResponse;
5 | constructor(errorResponse: AuthGoogleIdentifyErrorResponse) {
6 | super();
7 | this.errorResponse = errorResponse;
8 | }
9 |
10 | getResponse(): Response {
11 | const { code, message, status } = this.errorResponse.error;
12 | return Response.json(
13 | {
14 | success: false,
15 | error: {
16 | message,
17 | status: code,
18 | statusCode: status,
19 | },
20 | },
21 | {
22 | status: code,
23 | headers: {
24 | "Content-Type": "application/json",
25 | },
26 | },
27 | );
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.base.json",
3 | "files": [],
4 | "references": [
5 | {
6 | "path": "packages/auth"
7 | },
8 | {
9 | "path": "packages/cache"
10 | },
11 | {
12 | "path": "packages/hono"
13 | },
14 | {
15 | "path": "packages/http"
16 | },
17 | {
18 | "path": "packages/logger"
19 | },
20 | {
21 | "path": "packages/metrics"
22 | },
23 | {
24 | "path": "packages/nuxt-partytown"
25 | },
26 | {
27 | "path": "packages/nuxt-pinia-persist"
28 | },
29 | {
30 | "path": "packages/rate-limit"
31 | },
32 | {
33 | "path": "packages/types"
34 | },
35 | {
36 | "path": "packages/usage-limit"
37 | },
38 | {
39 | "path": "packages/vue-pinia-persist"
40 | }
41 | ]
42 | }
43 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/refactor.yml:
--------------------------------------------------------------------------------
1 | name: Refactor
2 | description: Refactoring code
3 | title: "[Refactor]: "
4 | labels: ["refactor"]
5 | body:
6 | - type: markdown
7 | attributes:
8 | value: |
9 | Thanks for taking the time to fill this out!
10 | - type: textarea
11 | id: describe-need
12 | attributes:
13 | label: Describe the need
14 | description: What do you want to happen?
15 | placeholder: Describe the Refactor need here.
16 | validations:
17 | required: true
18 | - type: checkboxes
19 | id: terms
20 | attributes:
21 | label: Code of Conduct
22 | description: By submitting this issue, you agree to follow our [Code of Conduct](../../docs/CODE_OF_CONDUCT.md)
23 | options:
24 | - label: I agree to follow this project's Code of Conduct
25 | required: true
--------------------------------------------------------------------------------
/packages/types/src/literalType.d.ts:
--------------------------------------------------------------------------------
1 | export type ComputeRange<
2 | N extends number,
3 | Result extends unknown[] = [],
4 | > = Result["length"] extends N ? Result : ComputeRange;
5 |
6 | type Enumerate = Acc["length"] extends N
7 | ? Acc[number]
8 | : Enumerate;
9 |
10 | export type INT =
11 | | Exclude, Enumerate>
12 | | To;
13 |
14 | export type CamelToSnakeCase = S extends `${infer T}${infer U}`
15 | ? `${T extends Capitalize ? "_" : ""}${Lowercase}${CamelToSnakeCase}`
16 | : S;
17 |
18 | export type CamelToSnakeNested = T extends object
19 | ? {
20 | [K in keyof T as CamelToSnakeCase]: CamelToSnakeNested;
21 | }
22 | : T;
23 |
--------------------------------------------------------------------------------
/.husky/prepare-commit-msg:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | textBase='\033[0m'
4 | textInformation='\033[1;34m'
5 | textSuccess='\033[1;32m'
6 |
7 | branchName=$(git rev-parse --abbrev-ref HEAD)
8 | techmelyTicketRegex='^[A-Z0-9]{2,8}-[0-9]{1,5}|merge|hotfix|release'
9 |
10 | gitCommitMsg=$(head -n 1 "$1") # $1 is path = .git/COMMIT_EDITMSG
11 |
12 | if [[ $gitCommitMsg =~ $techmelyTicketRegex ]];then
13 | echo "$textInformation Already exist prefix branch-name, ignore appending prefix."
14 | echo "$textBase"
15 | else
16 | echo "$textInformation Starting append prefix branch-name..."
17 | if [[ -n "$branchName" ]] && [[ $branchName =~ $techmelyTicketRegex ]]; then
18 | # Append prefix branch name into commit message
19 | sed -i.bak "1s~^~$branchName ~" "$1"
20 | echo "$textSuccess Append prefix branch-name successfully."
21 | echo "$textBase"
22 | fi
23 | fi
24 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/gardening.yml:
--------------------------------------------------------------------------------
1 | name: Gardening
2 | description: Dependencies, cleanup, reworking of code
3 | title: "[Garden 🍌🥦🍏]: "
4 | labels: ["Type: gardening", "Status: Triage"]
5 | body:
6 | - type: markdown
7 | attributes:
8 | value: |
9 | Thanks for taking the time to fill this out!
10 | - type: textarea
11 | id: describe-need
12 | attributes:
13 | label: Describe the need
14 | description: What do you want to happen?
15 | placeholder: Describe the gardening need here.
16 | validations:
17 | required: true
18 | - type: checkboxes
19 | id: terms
20 | attributes:
21 | label: Code of Conduct
22 | description: By submitting this issue, you agree to follow our [Code of Conduct](../../docs/CODE_OF_CONDUCT.md)
23 | options:
24 | - label: I agree to follow this project's Code of Conduct
25 | required: true
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/documentation.yml:
--------------------------------------------------------------------------------
1 | name: Documentation
2 | description: Update or add documentation
3 | title: "[DOCS]: "
4 | labels: ["documentation"]
5 | body:
6 | - type: markdown
7 | attributes:
8 | value: |
9 | Thanks for taking the time to fill this out!
10 | - type: textarea
11 | id: describe-need
12 | attributes:
13 | label: Describe the need
14 | description: What do you wish was different about our docs?
15 | placeholder: Describe the need for documentation updates here.
16 | validations:
17 | required: true
18 | - type: checkboxes
19 | id: terms
20 | attributes:
21 | label: Code of Conduct
22 | description: By submitting this issue, you agree to follow our [Code of Conduct](../../docs/CODE_OF_CONDUCT.md)
23 | options:
24 | - label: I agree to follow this project's Code of Conduct
25 | required: true
--------------------------------------------------------------------------------
/vitest.config.mts:
--------------------------------------------------------------------------------
1 | import path from "path";
2 | import { fileURLToPath } from "url";
3 | import type { AliasOptions } from "vite";
4 | import { defineConfig } from "vitest/config";
5 |
6 | const r = (p: string) => path.resolve(path.dirname(fileURLToPath(import.meta.url)), p);
7 |
8 | export const alias: AliasOptions = {
9 | "@techmely/types": r("./packages/types/src/"),
10 | "@techmely/utils": r("./packages/utils/src/"),
11 | "@techmely/domain-driven": r("./packages/domain-driven/src/"),
12 | };
13 |
14 | export default defineConfig({
15 | resolve: {
16 | alias,
17 | },
18 | test: {
19 | globals: true,
20 | setupFiles: ["./packages/utils/vitest-setup.ts"],
21 | environment: "happy-dom",
22 | coverage: {
23 | reporter: ["lcovonly"],
24 | },
25 | include: ["packages/**/**/**/*.test.?(m)ts?(x)"],
26 | exclude: ["node_modules", "packages/**/node_modules", "packages/**/dist"],
27 | },
28 | });
29 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug.yml:
--------------------------------------------------------------------------------
1 | name: Bug Report
2 | description: File a bug report
3 | title: "[BUG]: "
4 | labels: ["bug"]
5 | body:
6 | - type: markdown
7 | attributes:
8 | value: |
9 | Thanks for taking the time to fill out this bug report!
10 | - type: textarea
11 | id: what-happened
12 | attributes:
13 | label: What happened?
14 | description: What did you do? What happened? What did you expect to happen?
15 | placeholder: Put your description of the bug here.
16 | validations:
17 | required: true
18 | - type: textarea
19 | id: user-info
20 | attributes:
21 | label: User Info
22 | description: |
23 | Please fill the following information to help us investigate the bug more easier.
24 | - User ID: 01122121212
25 | - Please goto this page to get check your network information and paste it here
26 | - https://ping.techmely.com
27 | render: Shell
--------------------------------------------------------------------------------
/tsconfig.base.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "display": "BaseTsConfig",
4 | "compilerOptions": {
5 | "target": "ES2022",
6 | "lib": ["ESNext", "DOM"],
7 | "moduleResolution": "Bundler",
8 | "module": "ESNext",
9 | "composite": true,
10 | "useUnknownInCatchVariables": false,
11 | "noEmit": false,
12 | "noEmitOnError": true,
13 | "noUnusedLocals": true,
14 | "noImplicitAny": false,
15 | "noImplicitOverride": true,
16 | "declaration": true,
17 | "importHelpers": true,
18 | "allowJs": true,
19 | "alwaysStrict": true,
20 | "allowSyntheticDefaultImports": true,
21 | "isolatedModules": true,
22 | "strict": true,
23 | "pretty": true,
24 | "esModuleInterop": true,
25 | "resolveJsonModule": true,
26 | "skipLibCheck": true,
27 | "removeComments": true,
28 | "forceConsistentCasingInFileNames": true,
29 | "verbatimModuleSyntax": true,
30 | "jsx": "preserve"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/packages/reset-css/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@techmely/reset-css",
3 | "version": "1.0.6",
4 | "description": "Techmely Reset CSS",
5 | "author": "Harry Tran ",
6 | "license": "MIT",
7 | "repository": "https://github.com/techmely/essential-packages/tree/dev/packages/types",
8 | "files": [".", "./reset.css", "./reset.min.css"],
9 | "exports": {
10 | ".": {
11 | "default": "./reset.css"
12 | }
13 | },
14 | "scripts": {
15 | "publish": "npm publish"
16 | },
17 | "funding": [
18 | {
19 | "type": "ko-fi",
20 | "url": "https://ko-fi.com/harrytran998"
21 | },
22 | {
23 | "type": "patreon",
24 | "url": "https://www.patreon.com/harrytran998"
25 | }
26 | ],
27 | "bugs": {
28 | "url": "https://github.com/techmely/techmely/issues"
29 | },
30 | "keywords": ["techmely", "reset-css"],
31 | "publishConfig": {
32 | "access": "public",
33 | "directory": "dist",
34 | "tag": "latest"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/packages/auth/src/google-identity/usecases/signup-basic.ts:
--------------------------------------------------------------------------------
1 | import { GOOGLE_TOOKIT_ACCOUNTS_URL } from "../../const";
2 | import type {
3 | AuthGoogleIdentityRequest,
4 | AuthGoogleIdentityResponse,
5 | AuthSignUpRequest,
6 | } from "../../types";
7 | import { GoogleIdentityError } from "../error";
8 |
9 | export const signUpBasic =
10 | ({ config, options = { returnSecureToken: true } }: AuthGoogleIdentityRequest) =>
11 | async (payload: AuthSignUpRequest): Promise => {
12 | const response = await fetch(`${GOOGLE_TOOKIT_ACCOUNTS_URL}:signUp?key=${config.apiKey}`, {
13 | method: "POST",
14 | body: JSON.stringify({
15 | ...payload,
16 | returnSecureToken: options.returnSecureToken,
17 | }),
18 | headers: {
19 | "Content-Type": "application/json",
20 | },
21 | });
22 |
23 | const json = await response.json();
24 | if (!response.ok) {
25 | throw new GoogleIdentityError(json);
26 | }
27 | return json;
28 | };
29 |
--------------------------------------------------------------------------------
/packages/nuxt-partytown/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @techmely/nuxt-partytown
2 |
3 | ## 1.0.9
4 |
5 | ### Patch Changes
6 |
7 | - Upgrade deps
8 |
9 | ## 1.0.8
10 |
11 | ### Patch Changes
12 |
13 | - Fix typing + packaging
14 |
15 | ## 1.0.7
16 |
17 | ### Patch Changes
18 |
19 | - [`4fb8e01`](https://github.com/techmely/essential-packages/commit/4fb8e018133c2abaf622762e1b53667191b624d8) Thanks [@harrytran998](https://github.com/harrytran998)! - Fix replace build packages
20 |
21 | ## 1.0.6
22 |
23 | ### Patch Changes
24 |
25 | - Update the workflow we use to build this project
26 |
27 | ## 1.0.5
28 |
29 | ### Patch Changes
30 |
31 | - Upgrade deps
32 |
33 | ## 1.0.4
34 |
35 | ### Patch Changes
36 |
37 | - Upgrade deps
38 |
39 | ## 1.0.3
40 |
41 | ### Patch Changes
42 |
43 | - upgrade deps
44 |
45 | ## 1.0.2
46 |
47 | ### Patch Changes
48 |
49 | - Upgrade deps + upgrade ultils package
50 |
51 | ## 1.0.1
52 |
53 | ### Patch Changes
54 |
55 | - Upgrade deps
56 |
57 | ## 1.0.0
58 |
59 | ### Major Changes
60 |
61 | - Add partytown modules
62 |
--------------------------------------------------------------------------------
/packages/cache/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @techmely/cache
2 |
3 | ## 1.0.10
4 |
5 | ### Patch Changes
6 |
7 | - Upgrade deps
8 |
9 | ## 1.0.9
10 |
11 | ### Patch Changes
12 |
13 | - Fix typing + packaging
14 |
15 | ## 1.0.8
16 |
17 | ### Patch Changes
18 |
19 | - [`4fb8e01`](https://github.com/techmely/essential-packages/commit/4fb8e018133c2abaf622762e1b53667191b624d8) Thanks [@harrytran998](https://github.com/harrytran998)! - Fix replace build packages
20 |
21 | ## 1.0.7
22 |
23 | ### Patch Changes
24 |
25 | - Update the workflow we use to build this project
26 |
27 | ## 1.0.6
28 |
29 | ### Patch Changes
30 |
31 | - Upgrade deps
32 |
33 | ## 1.0.4
34 |
35 | ### Patch Changes
36 |
37 | - Update all the things
38 |
39 | ## 1.0.3
40 |
41 | ### Patch Changes
42 |
43 | - Bump version & upgrade deps
44 |
45 | ## 1.0.2
46 |
47 | ### Patch Changes
48 |
49 | - upgrade deps
50 |
51 | ## 1.0.1
52 |
53 | ### Patch Changes
54 |
55 | - Upgrade deps
56 |
57 | ## 1.0.0
58 |
59 | ### Major Changes
60 |
61 | - Update typing using port from techmely.types
62 |
--------------------------------------------------------------------------------
/packages/cache/src/hono.tiered.cache.ts:
--------------------------------------------------------------------------------
1 | import type { Records } from "@techmely/types";
2 | import type { Context } from "hono";
3 | import { TieredCache } from "./tiered.cache";
4 |
5 | export class HonoTieredCache extends TieredCache {
6 | async withCache(
7 | c: Context,
8 | namespace: Name,
9 | key: string,
10 | loadDistributedCache: (key: string) => Promise,
11 | ) {
12 | // First get from memory
13 | const [cached, stale] = await this.get(namespace, key);
14 | if (cached) {
15 | if (stale) {
16 | c.executionCtx.waitUntil(
17 | loadDistributedCache(key)
18 | .then((v) => this.set(namespace, key, v))
19 | .catch((error) => {
20 | console.error(error);
21 | }),
22 | );
23 | }
24 | return cached;
25 | }
26 |
27 | const value = await loadDistributedCache(key);
28 | this.set(namespace, key, value);
29 | return value;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/packages/nuxt-partytown/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@techmely/nuxt-partytown",
3 | "version": "1.0.9",
4 | "description": "Integration with Partytown effortless",
5 | "repository": "https://github.com/techmely/essential-packages/tree/main/packages/nuxt-partytown",
6 | "license": "MIT",
7 | "type": "module",
8 | "publishConfig": {
9 | "access": "public",
10 | "directory": "dist",
11 | "tag": "latest"
12 | },
13 | "exports": {
14 | ".": {
15 | "types": "./dist/types.d.ts",
16 | "import": "./dist/module.mjs",
17 | "require": "./dist/module.cjs"
18 | }
19 | },
20 | "main": "./dist/module.cjs",
21 | "types": "./dist/types.d.ts",
22 | "files": ["dist"],
23 | "dependencies": {
24 | "@builder.io/partytown": "^0.10.2",
25 | "@nuxt/kit": "^3.12.3"
26 | },
27 | "devDependencies": {
28 | "@nuxt/devtools": "^1.3.9",
29 | "@nuxt/module-builder": "^0.8.1",
30 | "@nuxt/schema": "^3.12.3",
31 | "@nuxt/test-utils": "3.13.1",
32 | "@types/node": "20.14.9",
33 | "nuxt": "^3.12.3"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/packages/http/src/index.ts:
--------------------------------------------------------------------------------
1 | import { Http } from "./http";
2 | import { REQUEST_METHODS } from "./http.const";
3 | import type { HttpFetchOptions, HttpInput, HttpInstance } from "./http.types";
4 |
5 | export * from "./exceptions";
6 | export * from "./http";
7 | export * from "./http.const";
8 | export * from "./http.types";
9 |
10 | const createInstance = (defaults?: Partial): HttpInstance => {
11 | const http = (input: HttpInput, _options?: HttpFetchOptions) =>
12 | Http.create(input, { ...defaults, ..._options });
13 |
14 | for (const method of REQUEST_METHODS) {
15 | http[method] = (input: HttpInput, _options?: HttpFetchOptions) =>
16 | Http.create(input, { ...defaults, ..._options, method });
17 | }
18 |
19 | http.create = (newDefaults?: Partial) => createInstance(newDefaults);
20 | http.extend = (newDefaults?: Partial) =>
21 | createInstance({ ...defaults, ...newDefaults });
22 | return http as HttpInstance;
23 | };
24 |
25 | const http = createInstance();
26 |
27 | export default http;
28 |
--------------------------------------------------------------------------------
/packages/auth/src/google-identity/usecases/get-exchange-token.ts:
--------------------------------------------------------------------------------
1 | import { GOOGLE_SECURE_TOKEN_API_URL } from "../../const";
2 | import type {
3 | AuthExchangeTokenRequest,
4 | AuthExchangeTokenResponse,
5 | FirebaseConfigs,
6 | } from "../../types";
7 | import { GoogleIdentityError } from "../error";
8 |
9 | const SECURE_TOKEN_URL = `${GOOGLE_SECURE_TOKEN_API_URL}/v1/token`;
10 |
11 | export async function googleExchangeToken(
12 | config: FirebaseConfigs,
13 | request: AuthExchangeTokenRequest,
14 | ): Promise {
15 | const body = new URLSearchParams();
16 | body.append("grant_type", request.grant_type);
17 | body.append("refresh_token", request.refresh_token);
18 |
19 | const response = await fetch(`${SECURE_TOKEN_URL}?key=${config.apiKey}`, {
20 | method: "POST",
21 | body,
22 | headers: {
23 | "Content-Type": "application/x-www-form-urlencoded",
24 | },
25 | });
26 |
27 | const json = await response.json();
28 | if (!response.ok) {
29 | throw new GoogleIdentityError(json);
30 | }
31 | return json;
32 | }
33 |
--------------------------------------------------------------------------------
/packages/metrics/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @techmely/metrics
2 |
3 | ## 1.0.10
4 |
5 | ### Patch Changes
6 |
7 | - Upgrade deps
8 |
9 | ## 1.0.9
10 |
11 | ### Patch Changes
12 |
13 | - Fix typing + packaging
14 |
15 | ## 1.0.8
16 |
17 | ### Patch Changes
18 |
19 | - [`4fb8e01`](https://github.com/techmely/essential-packages/commit/4fb8e018133c2abaf622762e1b53667191b624d8) Thanks [@harrytran998](https://github.com/harrytran998)! - Fix replace build packages
20 |
21 | ## 1.0.7
22 |
23 | ### Patch Changes
24 |
25 | - Update the workflow we use to build this project
26 |
27 | ## 1.0.6
28 |
29 | ### Patch Changes
30 |
31 | - Upgrade deps
32 |
33 | ## 1.0.4
34 |
35 | ### Patch Changes
36 |
37 | - Update all the things
38 |
39 | ## 1.0.3
40 |
41 | ### Patch Changes
42 |
43 | - Bump version & upgrade deps
44 |
45 | ## 1.0.2
46 |
47 | ### Patch Changes
48 |
49 | - upgrade deps
50 |
51 | ## 1.0.1
52 |
53 | ### Patch Changes
54 |
55 | - Update typing using port from techmely.types
56 |
57 | ## 1.0.0
58 |
59 | ### Major Changes
60 |
61 | - Init metrics interface & metrics with axiom
62 |
--------------------------------------------------------------------------------
/packages/types/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@techmely/types",
3 | "version": "1.9.0",
4 | "description": "Techmely types definitions",
5 | "author": "Harry Tran ",
6 | "license": "MIT",
7 | "repository": "https://github.com/techmely/essential-packages/tree/dev/packages/types",
8 | "types": "src/index.d.ts",
9 | "publishConfig": {
10 | "access": "public",
11 | "directory": "dist",
12 | "tag": "latest"
13 | },
14 | "files": ["src"],
15 | "dependencies": {
16 | "ts-essentials": "10.0.1",
17 | "@total-typescript/ts-reset": "0.5.1"
18 | },
19 | "devDependencies": {
20 | "@cloudflare/workers-types": "4.20240620.0",
21 | "typescript": "5.5.3"
22 | },
23 | "funding": [
24 | {
25 | "type": "ko-fi",
26 | "url": "https://ko-fi.com/harrytran998"
27 | },
28 | {
29 | "type": "patreon",
30 | "url": "https://www.patreon.com/harrytran998"
31 | }
32 | ],
33 | "bugs": {
34 | "url": "https://github.com/techmely/techmely/issues"
35 | },
36 | "keywords": ["techmely", "types"]
37 | }
38 |
--------------------------------------------------------------------------------
/biome.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
3 | "organizeImports": {
4 | "enabled": true
5 | },
6 | "linter": {
7 | "enabled": true,
8 | "rules": {
9 | "recommended": true,
10 | "correctness": {
11 | "recommended": true
12 | },
13 | "style": {
14 | "recommended": true,
15 | "noArguments": "off",
16 | "noParameterAssign": "warn"
17 | },
18 | "suspicious": {
19 | "noExplicitAny": "off",
20 | "noConfusingVoidType": "off"
21 | }
22 | }
23 | },
24 | "formatter": {
25 | "enabled": true,
26 | "indentWidth": 2,
27 | "indentStyle": "space",
28 | "lineWidth": 100,
29 | "formatWithErrors": false
30 | },
31 | "files": {
32 | "include": ["packages", "scripts"],
33 | "ignore": [
34 | "node_modules",
35 | "coverage",
36 | "dist",
37 | "packages/**/node_modules",
38 | "packages/**/dist",
39 | "partytown",
40 | "packages/**/**/*.d.ts",
41 | ".vscode"
42 | ]
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/packages/auth/src/google-identity/usecases/signin-basic.ts:
--------------------------------------------------------------------------------
1 | import { GOOGLE_TOOKIT_ACCOUNTS_URL } from "../../const";
2 | import type {
3 | AuthGoogleIdentityRequest,
4 | AuthGoogleIdentityResponse,
5 | AuthSignInBasicRequest,
6 | } from "../../types";
7 | import { GoogleIdentityError } from "../error";
8 |
9 | export const signInBasic =
10 | ({ config, options = { returnSecureToken: true } }: AuthGoogleIdentityRequest) =>
11 | async (payload: AuthSignInBasicRequest): Promise => {
12 | const response = await fetch(
13 | `${GOOGLE_TOOKIT_ACCOUNTS_URL}:signInWithPassword?key=${config.apiKey}`,
14 | {
15 | method: "POST",
16 | body: JSON.stringify({
17 | ...payload,
18 | returnSecureToken: options.returnSecureToken,
19 | }),
20 | headers: {
21 | "Content-Type": "application/json",
22 | },
23 | },
24 | );
25 |
26 | const json = await response.json();
27 | if (!response.ok) {
28 | throw new GoogleIdentityError(json);
29 | }
30 | return json;
31 | };
32 |
--------------------------------------------------------------------------------
/packages/logger/src/console.ts:
--------------------------------------------------------------------------------
1 | import type { LoggerPort, Records } from "@techmely/types";
2 |
3 | export class ConsoleLogger implements LoggerPort {
4 | readonly #fields?: Records;
5 |
6 | constructor(fields?: Records) {
7 | this.#fields = fields;
8 | }
9 |
10 | #combineMessage(message: string, fields?: Records) {
11 | const _fields = { ...fields, ...this.#fields };
12 | if (Object.keys(_fields).length > 0) {
13 | return [message, JSON.stringify(_fields)];
14 | }
15 | return [message];
16 | }
17 |
18 | info(message: string, meta?: Records): void {
19 | console.info(...this.#combineMessage(message, meta));
20 | }
21 | error(message: string, meta?: Records): void {
22 | console.error(...this.#combineMessage(message, meta));
23 | }
24 | warn(message: string, meta?: Records): void {
25 | console.warn(...this.#combineMessage(message, meta));
26 | }
27 | debug(message: string, meta?: Records): void {
28 | console.debug(...this.#combineMessage(message, meta));
29 | }
30 | flush(): Promise {
31 | return Promise.resolve();
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/packages/hono/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@techmely/hono",
3 | "description": "Techmely Hono Package",
4 | "version": "1.1.0",
5 | "homepage": "https://about.techmely.com/products",
6 | "bugs": {
7 | "url": "https://github.com/techmely/techmely/issues"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://github.com/techmely/techmely.git",
12 | "directory": "packages/hono"
13 | },
14 | "funding": "https://github.com/sponsors/techmely",
15 | "license": "MIT",
16 | "type": "module",
17 | "main": "dist/index.js",
18 | "types": "dist/index.d.ts",
19 | "files": ["dist", "README.md", "CHANGELOG.md"],
20 | "peerDependencies": {
21 | "@techmely/utils": "^3.3.0",
22 | "@techmely/auth": "workspace:*",
23 | "valibot": "0.35.0",
24 | "jose": "^5.6.2",
25 | "hono": "^4.4.10",
26 | "toucan-js": "^3.4.0"
27 | },
28 | "devDependencies": {
29 | "@techmely/types": "workspace:*",
30 | "@techmely/http": "workspace:*",
31 | "rimraf": "^5.0.7",
32 | "tsup": "8.1.0"
33 | },
34 | "publishConfig": {
35 | "access": "public"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/packages/vue-pinia-persist/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@techmely/vue-pinia-persist",
3 | "version": "1.0.19",
4 | "description": "Persist and rehydrate pinia store",
5 | "homepage": "https://about.techmely.com/libraries/vue-pinia-persist",
6 | "bugs": {
7 | "url": "https://github.com/techmely/essential-packages/issues"
8 | },
9 | "repository": {
10 | "url": "git+https://github.com/techmely/essential-packages.git#main",
11 | "directory": "ts-package/vue-pinia-persist"
12 | },
13 | "license": "MIT",
14 | "type": "module",
15 | "main": "dist/index.js",
16 | "types": "dist/index.d.ts",
17 | "files": ["dist"],
18 | "dependencies": {
19 | "@techmely/utils": "^3.3.0"
20 | },
21 | "peerDependencies": {
22 | "pinia": "^2.1.7",
23 | "vue": "^3.4.31"
24 | },
25 | "devDependencies": {
26 | "cookie": "^0.6.0",
27 | "pinia": "^2.1.7",
28 | "tsup": "8.1.0",
29 | "typescript": "5.5.3",
30 | "vitest": "1.6.0",
31 | "vue": "^3.4.31"
32 | },
33 | "publishConfig": {
34 | "access": "public",
35 | "directory": "dist",
36 | "tag": "latest"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021-present, HarryTran998
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10 |
--------------------------------------------------------------------------------
/.github/workflows/changelog.yml:
--------------------------------------------------------------------------------
1 | name: Update Changelog RC
2 |
3 | on:
4 | push:
5 | paths:
6 | - '.changeset/**'
7 | branches:
8 | - main
9 |
10 | jobs:
11 | update-changelog:
12 | runs-on: ubuntu-latest
13 | steps:
14 | - name: Install pnpm
15 | uses: pnpm/action-setup@v2
16 |
17 | - name: Set node version to 16
18 | uses: actions/setup-node@v2
19 | with:
20 | node-version: 16
21 | cache: 'pnpm'
22 |
23 | - run: pnpm install
24 |
25 | - name: Setup Git
26 | run: |
27 | git config user.name "github-actions[bot]"
28 | git config user.email "github-actions[bot]@users.noreply.github.com"
29 |
30 | - name: Generate the new changelog
31 | run: pnpm run changelog:gen
32 |
33 | - name: Commit changelog
34 | run: |
35 | git add .
36 | git diff --staged --quiet || git commit -m "docs(changelog): $GITHUB_SHA"
37 |
38 | - name: Create Pull Request
39 | uses: peter-evans/create-pull-request@v4
40 | with:
41 | delete-branch: true
42 | title: 'docs: update .changelogrc'
43 | branch: docs/changelog-updates
44 |
--------------------------------------------------------------------------------
/packages/auth/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@techmely/auth",
3 | "version": "1.0.3",
4 | "description": "Techmely auth's utils solutions",
5 | "author": "Harry Tran ",
6 | "license": "MIT",
7 | "homepage": "https://about.techmely.com/libraries/auth",
8 | "bugs": {
9 | "url": "https://github.com/techmely/essential-packages/issues"
10 | },
11 | "repository": {
12 | "url": "git+https://github.com/techmely/essential-packages.git#main",
13 | "directory": "ts-package/cache"
14 | },
15 | "type": "module",
16 | "main": "dist/index.js",
17 | "types": "dist/index.d.ts",
18 | "publishConfig": {
19 | "access": "public",
20 | "directory": "dist",
21 | "tag": "latest"
22 | },
23 | "peerDependencies": {
24 | "jose": "^5.6.3"
25 | },
26 | "devDependencies": {
27 | "hono": "^4.4.11",
28 | "tslib": "2.6.3",
29 | "tsup": "8.1.0",
30 | "typescript": "5.5.3"
31 | },
32 | "funding": [
33 | {
34 | "type": "ko-fi",
35 | "url": "https://ko-fi.com/harrytran998"
36 | },
37 | {
38 | "type": "patreon",
39 | "url": "https://www.patreon.com/harrytran998"
40 | }
41 | ],
42 | "keywords": ["techmely", "cache"]
43 | }
44 |
--------------------------------------------------------------------------------
/packages/metrics/src/axiom.metrics.ts:
--------------------------------------------------------------------------------
1 | import { Axiom } from "@axiomhq/js";
2 | import type { MetricEventPort, MetricsPort, Records, RuntimeEnv } from "@techmely/types";
3 |
4 | type AxiomMetricsOptions = {
5 | environment: RuntimeEnv;
6 | dataset?: string;
7 | meta?: Records;
8 | };
9 |
10 | export class AxiomMetrics implements MetricsPort {
11 | readonly #axiomDataset: string;
12 | readonly #ax: Axiom;
13 | #meta: Records;
14 |
15 | constructor(token: string, options: AxiomMetricsOptions) {
16 | this.#axiomDataset = options.dataset || `cf_api_metrics_${options.environment}`;
17 | this.#ax = new Axiom({ token });
18 | this.#meta = options.meta || {};
19 | }
20 |
21 | emit(
22 | metric: TMetric,
23 | event: MetricEventPort[TMetric],
24 | ): void {
25 | this.#ax.ingest(this.#axiomDataset, [
26 | {
27 | _time: Date.now(),
28 | metric,
29 | ...this.#meta,
30 | ...event,
31 | },
32 | ]);
33 | }
34 |
35 | public async flush(): Promise {
36 | try {
37 | await this.#ax.flush();
38 | } catch (error) {
39 | console.error("Unable to flush logs to axiom", error);
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/packages/http/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@techmely/http",
3 | "version": "1.2.12",
4 | "description": "Techmely Http",
5 | "author": "Harry Tran ",
6 | "license": "MIT",
7 | "homepage": "https://about.techmely.com/libraries/http",
8 | "bugs": {
9 | "url": "https://github.com/techmely/essential-packages/issues"
10 | },
11 | "repository": {
12 | "url": "git+https://github.com/techmely/essential-packages.git#main",
13 | "directory": "ts-package/http"
14 | },
15 | "type": "module",
16 | "main": "dist/index.js",
17 | "types": "dist/index.d.ts",
18 | "publishConfig": {
19 | "access": "public",
20 | "directory": "dist",
21 | "tag": "latest"
22 | },
23 | "files": ["dist"],
24 | "dependencies": {
25 | "@techmely/utils": "^3.3.0"
26 | },
27 | "devDependencies": {
28 | "@techmely/types": "workspace:*",
29 | "tslib": "2.6.3",
30 | "tsup": "8.1.0",
31 | "typescript": "5.5.3"
32 | },
33 | "funding": [
34 | {
35 | "type": "ko-fi",
36 | "url": "https://ko-fi.com/harrytran998"
37 | },
38 | {
39 | "type": "patreon",
40 | "url": "https://www.patreon.com/harrytran998"
41 | }
42 | ],
43 | "keywords": ["techmely", "http"]
44 | }
45 |
--------------------------------------------------------------------------------
/packages/auth/src/google-identity/usecases/signin-custom-token.ts:
--------------------------------------------------------------------------------
1 | import type { AuthGoogleIdentityRequest } from "../../types";
2 | import { GoogleIdentityError } from "../error";
3 |
4 | export type SignInWithCustomTokenPayload = {
5 | token: string;
6 | };
7 |
8 | export type SignInWithCustomTokenResponse = {
9 | idToken: string;
10 | refreshToken: string;
11 | expiresIn: string;
12 | };
13 |
14 | export const signInWithCustomToken =
15 | ({ config, options = { returnSecureToken: true } }: AuthGoogleIdentityRequest) =>
16 | async (payload: SignInWithCustomTokenPayload): Promise => {
17 | const response = await fetch(
18 | `https://identitytoolkit.googleapis.com/v1/accounts:signInWithCustomToken?key=${config.apiKey}`,
19 | {
20 | method: "POST",
21 | body: JSON.stringify({
22 | ...payload,
23 | returnSecureToken: options.returnSecureToken,
24 | }),
25 | headers: {
26 | "Content-Type": "application/json",
27 | },
28 | },
29 | );
30 |
31 | const json = await response.json();
32 | if (!response.ok) {
33 | throw new GoogleIdentityError(json);
34 | }
35 | return json as SignInWithCustomTokenResponse;
36 | };
37 |
--------------------------------------------------------------------------------
/packages/auth/src/jwt/verify-decode-jwt.ts:
--------------------------------------------------------------------------------
1 | import { decodeProtectedHeader, importX509, jwtVerify } from "jose";
2 | import { GOOGLE_SECURE_TOKEN_API_URL } from "../const";
3 | import type { UserFromDecodedIdToken } from "./types";
4 |
5 | export const verifyAndDecodeJwt = async (
6 | jwtToken: string,
7 | publicKeys: Record,
8 | projectId: string,
9 | ): Promise => {
10 | try {
11 | const { kid } = decodeProtectedHeader(jwtToken);
12 | if (!kid) {
13 | throw new TypeError("invalid jwt header does not contain kid");
14 | }
15 | if (!publicKeys[kid]) {
16 | throw new TypeError("invalid kid or google public key has been updated recently");
17 | }
18 | const x509 = publicKeys[kid];
19 | const publicKey = await importX509(x509, "RS256");
20 | const { payload } = await jwtVerify(jwtToken, publicKey, {
21 | audience: projectId,
22 | issuer: `${GOOGLE_SECURE_TOKEN_API_URL}/${projectId}`,
23 | });
24 |
25 | return payload as UserFromDecodedIdToken;
26 | } catch (e: unknown) {
27 | console.error(e);
28 | if (e instanceof TypeError) {
29 | throw new Error(e.message);
30 | }
31 | throw new Error("uncaught jwt decode exception");
32 | }
33 | };
34 |
--------------------------------------------------------------------------------
/packages/cache/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@techmely/cache",
3 | "version": "1.0.10",
4 | "description": "Techmely cache",
5 | "author": "Harry Tran ",
6 | "license": "MIT",
7 | "homepage": "https://about.techmely.com/libraries/cache",
8 | "bugs": {
9 | "url": "https://github.com/techmely/essential-packages/issues"
10 | },
11 | "repository": {
12 | "url": "git+https://github.com/techmely/essential-packages.git#main",
13 | "directory": "ts-package/cache"
14 | },
15 | "type": "module",
16 | "main": "dist/index.js",
17 | "types": "dist/index.d.ts",
18 | "publishConfig": {
19 | "access": "public",
20 | "directory": "dist",
21 | "tag": "latest"
22 | },
23 | "files": ["dist"],
24 | "devDependencies": {
25 | "@techmely/metrics": "workspace:*",
26 | "@techmely/types": "workspace:*",
27 | "hono": "^4.4.11",
28 | "tslib": "2.6.3",
29 | "tsup": "8.1.0",
30 | "typescript": "5.5.3"
31 | },
32 | "funding": [
33 | {
34 | "type": "ko-fi",
35 | "url": "https://ko-fi.com/harrytran998"
36 | },
37 | {
38 | "type": "patreon",
39 | "url": "https://www.patreon.com/harrytran998"
40 | }
41 | ],
42 | "keywords": ["techmely", "cache"]
43 | }
44 |
--------------------------------------------------------------------------------
/packages/http/src/exceptions/exception.base.ts:
--------------------------------------------------------------------------------
1 | export class ExceptionBase extends Error {
2 | /**
3 | * @param {string} exMessage
4 | *
5 | * @param {number} statusCode
6 | *
7 | * ^ Consider adding optional `metadata` object to
8 | * exceptions (if language doesn't support anything
9 | * similar by default) and pass some useful technical
10 | * information about the exception when throwing.
11 | * This will make debugging easier.
12 | * @param {Record} [metadata={}]
13 | *
14 | * **BE CAREFUL** not to include sensitive info in 'metadata'
15 | * to prevent leaks since all exception's data will end up
16 | * in application's log files. Only include non-sensitive
17 | * info that may help with debugging.
18 | */
19 | constructor(
20 | readonly exMessage: string,
21 | readonly statusCode: number,
22 | readonly code: string,
23 | readonly metadata?: Record,
24 | ) {
25 | super(exMessage);
26 | }
27 |
28 | toJSON() {
29 | return {
30 | message: this.exMessage,
31 | statusCode: this.statusCode,
32 | code: this.code,
33 | stack: this.stack,
34 | cause: JSON.stringify(this.cause),
35 | metadata: this.metadata,
36 | };
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/packages/nuxt-pinia-persist/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@techmely/nuxt-pinia-persist",
3 | "version": "1.0.19",
4 | "description": "Pinia Persist like Redux-Persist for Nuxt 3",
5 | "repository": "https://github.com/techmely/essential-packages/tree/main/packages/nuxt-pinia-persist",
6 | "license": "MIT",
7 | "type": "module",
8 | "exports": {
9 | ".": {
10 | "types": "./dist/types.d.ts",
11 | "import": "./dist/module.mjs",
12 | "require": "./dist/module.cjs"
13 | }
14 | },
15 | "main": "./dist/module.cjs",
16 | "types": "./dist/types.d.ts",
17 | "files": ["dist"],
18 | "dependencies": {
19 | "@nuxt/kit": "3.12.3",
20 | "@techmely/utils": "^3.3.0",
21 | "@techmely/vue-pinia-persist": "workspace:*",
22 | "defu": "^6.1.4"
23 | },
24 | "peerDependencies": {
25 | "pinia": "^2.1.7"
26 | },
27 | "devDependencies": {
28 | "@nuxt/devtools": "^1.3.9",
29 | "@nuxt/kit": "3.12.3",
30 | "@nuxt/module-builder": "^0.8.1",
31 | "@nuxt/schema": "^3.12.3",
32 | "@nuxt/test-utils": "3.13.1",
33 | "@types/node": "20.14.9",
34 | "nuxt": "^3.12.3",
35 | "pinia": "^2.1.7"
36 | },
37 | "publishConfig": {
38 | "access": "public",
39 | "directory": "dist",
40 | "tag": "latest"
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/packages/logger/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @techmely/logger
2 |
3 | ## 1.1.3
4 |
5 | ### Patch Changes
6 |
7 | - Upgrade deps
8 |
9 | ## 1.1.2
10 |
11 | ### Patch Changes
12 |
13 | - Fix typing + packaging
14 |
15 | ## 1.1.1
16 |
17 | ### Patch Changes
18 |
19 | - [`4fb8e01`](https://github.com/techmely/essential-packages/commit/4fb8e018133c2abaf622762e1b53667191b624d8) Thanks [@harrytran998](https://github.com/harrytran998)! - Fix replace build packages
20 |
21 | ## 1.1.0
22 |
23 | ### Minor Changes
24 |
25 | - [`e496334`](https://github.com/techmely/essential-packages/commit/e49633413e30c9350a3c6bb27137bc1da6a7bd07) Thanks [@harrytran998](https://github.com/harrytran998)! - Add pino logger
26 |
27 | ### Patch Changes
28 |
29 | - Update the workflow we use to build this project
30 |
31 | ## 1.0.7
32 |
33 | ### Patch Changes
34 |
35 | - Upgrade deps
36 |
37 | ## 1.0.5
38 |
39 | ### Patch Changes
40 |
41 | - Update all the things
42 |
43 | ## 1.0.4
44 |
45 | ### Patch Changes
46 |
47 | - Bump version & upgrade deps
48 |
49 | ## 1.0.3
50 |
51 | ### Patch Changes
52 |
53 | - upgrade deps
54 |
55 | ## 1.0.2
56 |
57 | ### Patch Changes
58 |
59 | - Update typing using port from techmely.types
60 |
61 | ## 1.0.1
62 |
63 | ### Patch Changes
64 |
65 | - update axiom dataset name
66 |
67 | ## 1.0.0
68 |
69 | ### Major Changes
70 |
71 | - Init logger-agnostic env module
72 |
--------------------------------------------------------------------------------
/packages/logger/src/pino.ts:
--------------------------------------------------------------------------------
1 | import type { LoggerPort, Records } from "@techmely/types";
2 | import pino, { type Logger, type LoggerOptions } from "pino";
3 |
4 | export class PinoLogger implements LoggerPort {
5 | #pino: Logger;
6 |
7 | constructor(_options?: LoggerOptions) {
8 | const options: LoggerOptions = {
9 | ..._options,
10 | level: process.env.LOG_LEVEL || "debug",
11 | timestamp: () => `,"ts": "${new Date(Date.now()).toString()}"`,
12 | formatters: {
13 | level(label: string) {
14 | return { level: label };
15 | },
16 | },
17 | };
18 | const log = pino(options);
19 | this.#pino = log;
20 | }
21 |
22 | get instance() {
23 | return this.#pino;
24 | }
25 |
26 | info(message: string, meta?: Records | undefined): void {
27 | this.#pino.info(message, meta);
28 | }
29 | error(message: string, meta?: Records | undefined): void {
30 | this.#pino.error(message, meta);
31 | }
32 | warn(message: string, meta?: Records | undefined): void {
33 | this.#pino.warn(message, meta);
34 | }
35 | debug(message: string, meta?: Records | undefined): void {
36 | this.#pino.debug(message, meta);
37 | }
38 | async flush(): Promise {
39 | try {
40 | this.#pino.flush();
41 | } catch (error) {
42 | this.#pino.error("Unnable to flush logs to axiom", error);
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/packages/metrics/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@techmely/metrics",
3 | "version": "1.0.10",
4 | "description": "Techmely metrics",
5 | "author": "Harry Tran ",
6 | "license": "MIT",
7 | "homepage": "https://about.techmely.com/libraries/metrics",
8 | "bugs": {
9 | "url": "https://github.com/techmely/essential-packages/issues"
10 | },
11 | "repository": {
12 | "url": "git+https://github.com/techmely/essential-packages.git#main",
13 | "directory": "ts-package/metrics"
14 | },
15 | "type": "module",
16 | "main": "dist/index.js",
17 | "types": "dist/index.d.ts",
18 | "publishConfig": {
19 | "access": "public",
20 | "directory": "dist",
21 | "tag": "latest"
22 | },
23 | "files": ["dist"],
24 | "peerDependencies": {
25 | "@axiomhq/js": "^1.0.0"
26 | },
27 | "peerDependenciesMeta": {
28 | "@axiomhq/js": {
29 | "optional": true
30 | }
31 | },
32 | "devDependencies": {
33 | "@axiomhq/js": "^1.0.0",
34 | "@techmely/types": "workspace:*",
35 | "tslib": "2.6.3",
36 | "tsup": "8.1.0",
37 | "typescript": "5.5.3"
38 | },
39 | "funding": [
40 | {
41 | "type": "ko-fi",
42 | "url": "https://ko-fi.com/harrytran998"
43 | },
44 | {
45 | "type": "patreon",
46 | "url": "https://www.patreon.com/harrytran998"
47 | }
48 | ],
49 | "keywords": ["techmely", "metrics"]
50 | }
51 |
--------------------------------------------------------------------------------
/packages/types/src/metrics.port.d.ts:
--------------------------------------------------------------------------------
1 | import type { BaseCachePort } from "./cache.port";
2 |
3 | export interface MetricsPort {
4 | /**
5 | * Emit stores a new metric event
6 | */
7 | emit(metric: TMetric, e: MetricEventPort[TMetric]): void;
8 |
9 | /**
10 | * flush persists all metrics to durable storage
11 | */
12 | flush(): Promise;
13 | }
14 |
15 | interface MetricCacheReadPort extends BaseCachePort {
16 | hit: boolean;
17 | latency: number;
18 | }
19 |
20 | export type MetricEventPort = {
21 | cacheRead: MetricCacheReadPort;
22 | cacheWrite: BaseCachePort;
23 | cachePurge: BaseCachePort;
24 | httpRequest: {
25 | requestId: string;
26 | path: string;
27 | method: string;
28 | status: number;
29 | latency: number;
30 | continent?: string;
31 | country?: string;
32 | city?: string;
33 | userAgent?: string;
34 | /**
35 | * Get this from redirect request header
36 | */
37 | fromAgent?: string;
38 | error?: string;
39 |
40 | // Cloudflare specific
41 | /**
42 | * See @link https://developers.cloudflare.com/workers/runtime-apis/request/#incomingrequestcfproperties
43 | */
44 | colo?: string;
45 | };
46 | rateLimit: {
47 | keyId: string;
48 | latency: number;
49 | tier: "memory" | "durable" | "total";
50 | };
51 | usageLimit: {
52 | keyId: string;
53 | latency: number;
54 | };
55 | };
56 |
--------------------------------------------------------------------------------
/packages/http/src/http.const.ts:
--------------------------------------------------------------------------------
1 | import type { HttpRetryOptions } from "./http.types";
2 |
3 | export const retryStatusCodes = new Set([
4 | 408, // Request Timeout
5 | 409, // Conflict
6 | 425, // Too Early
7 | 429, // Too Many Requests
8 | 500, // Internal Server Error
9 | 502, // Bad Gateway
10 | 503, // Service Unavailable
11 | 504, // Gateway Timeout
12 | ]);
13 |
14 | export const RESPONSE_TYPES = {
15 | json: "application/json",
16 | text: "text/*",
17 | formData: "multipart/form-data",
18 | arrayBuffer: "*/*",
19 | blob: "*/*",
20 | } as const;
21 |
22 | export const REQUEST_METHODS = [
23 | "GET",
24 | "HEAD",
25 | "PATCH",
26 | "POST",
27 | "PUT",
28 | "DELETE",
29 | "CONNECT",
30 | "OPTIONS",
31 | "TRACE",
32 | ] as const;
33 |
34 | export type HttpMethod = (typeof REQUEST_METHODS)[number];
35 |
36 | const httpRetryMethods = ["get", "put", "head", "delete", "options", "trace"];
37 |
38 | export const httpRetryStatusCodes = [408, 413, 429, 500, 502, 503, 504];
39 |
40 | const httpRetryAfterStatusCodes = [413, 429, 503];
41 |
42 | export const httpDefaultRetry: Required = {
43 | limit: 2,
44 | methods: httpRetryMethods,
45 | statusCodes: httpRetryStatusCodes,
46 | afterStatusCodes: httpRetryAfterStatusCodes,
47 | maxRetryAfter: Number.POSITIVE_INFINITY,
48 | backoffLimit: Number.POSITIVE_INFINITY,
49 | delay: (attemptCount: number) => 0.3 * 2 ** (attemptCount - 1) * 1000,
50 | };
51 |
--------------------------------------------------------------------------------
/packages/nuxt-pinia-persist/src/runtime/plugin.ts:
--------------------------------------------------------------------------------
1 | import { piniaPersist } from "@techmely/vue-pinia-persist";
2 | import { defineNuxtPlugin, useRuntimeConfig } from "nuxt/app";
3 | import type { Pinia, StateTree } from "pinia";
4 | import { createPinia, setActivePinia } from "pinia";
5 |
6 | export default defineNuxtPlugin<{ pinia: Pinia }>(({ vueApp, payload }) => {
7 | const config = useRuntimeConfig();
8 | const { onAfterRestore, onBeforeRestore, ...restOptions } = config.public.persist;
9 |
10 | const pinia = createPinia();
11 | pinia.use(
12 | piniaPersist({
13 | ...restOptions,
14 | // biome-ignore lint/security/noGlobalEval: I knew this
15 | onAfterRestore: onAfterRestore ? eval(onAfterRestore) : undefined,
16 | // biome-ignore lint/security/noGlobalEval: I knew this
17 | onBeforeRestore: onBeforeRestore ? eval(onBeforeRestore) : undefined,
18 | }),
19 | );
20 |
21 | vueApp.use(pinia);
22 |
23 | setActivePinia(pinia);
24 |
25 | if (process.server) {
26 | payload.pinia = pinia.state.value;
27 | } else if (payload?.pinia) {
28 | pinia.state.value = payload.pinia as Record;
29 | }
30 |
31 | return {
32 | provide: {
33 | pinia,
34 | },
35 | };
36 | });
37 | declare module "#app" {
38 | interface NuxtApp {
39 | $pinia: Pinia;
40 | }
41 | }
42 |
43 | declare module "vue" {
44 | interface ComponentCustomProperties {
45 | $pinia: Pinia;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/packages/usage-limit/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@techmely/usage-limit",
3 | "version": "2.0.3",
4 | "description": "Techmely usage limit",
5 | "author": "Harry Tran ",
6 | "license": "MIT",
7 | "homepage": "https://about.techmely.com/libraries/usage-limit",
8 | "bugs": {
9 | "url": "https://github.com/techmely/essential-packages/issues"
10 | },
11 | "repository": {
12 | "url": "git+https://github.com/techmely/essential-packages.git#main",
13 | "directory": "ts-package/usage-limit"
14 | },
15 | "type": "module",
16 | "main": "dist/index.js",
17 | "types": "dist/index.d.ts",
18 | "publishConfig": {
19 | "access": "public",
20 | "directory": "dist",
21 | "tag": "latest"
22 | },
23 | "files": ["dist"],
24 | "peerDependencies": {
25 | "@techmely/logger": "workspace:*",
26 | "@techmely/metrics": "workspace:*",
27 | "valibot": "^0.35.0"
28 | },
29 | "devDependencies": {
30 | "@cloudflare/workers-types": "4.20240620.0",
31 | "@techmely/logger": "workspace:*",
32 | "@techmely/metrics": "workspace:*",
33 | "tslib": "2.6.3",
34 | "tsup": "8.1.0",
35 | "typescript": "5.5.3",
36 | "valibot": "^0.35.0"
37 | },
38 | "funding": [
39 | {
40 | "type": "ko-fi",
41 | "url": "https://ko-fi.com/harrytran998"
42 | },
43 | {
44 | "type": "patreon",
45 | "url": "https://www.patreon.com/harrytran998"
46 | }
47 | ],
48 | "keywords": ["techmely", "usage-limit"]
49 | }
50 |
--------------------------------------------------------------------------------
/packages/rate-limit/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@techmely/rate-limit",
3 | "version": "2.0.3",
4 | "description": "Techmely Rate Limit",
5 | "author": "Harry Tran ",
6 | "license": "MIT",
7 | "homepage": "https://about.techmely.com/libraries/rate-limit",
8 | "bugs": {
9 | "url": "https://github.com/techmely/essential-packages/issues"
10 | },
11 | "repository": {
12 | "url": "git+https://github.com/techmely/essential-packages.git#main",
13 | "directory": "ts-package/rate-limit"
14 | },
15 | "type": "module",
16 | "main": "dist/index.js",
17 | "types": "dist/index.d.ts",
18 | "publishConfig": {
19 | "access": "public",
20 | "directory": "dist",
21 | "tag": "latest"
22 | },
23 | "files": ["dist"],
24 | "peerDependencies": {
25 | "@techmely/logger": "workspace:*",
26 | "@techmely/metrics": "workspace:*",
27 | "valibot": "^0.35.0"
28 | },
29 | "devDependencies": {
30 | "@cloudflare/workers-types": "4.20240620.0",
31 | "@techmely/logger": "workspace:*",
32 | "@techmely/metrics": "workspace:*",
33 | "@techmely/types": "workspace:*",
34 | "tslib": "2.6.3",
35 | "tsup": "8.1.0",
36 | "typescript": "5.5.3",
37 | "valibot": "^0.35.0"
38 | },
39 | "funding": [
40 | {
41 | "type": "ko-fi",
42 | "url": "https://ko-fi.com/harrytran998"
43 | },
44 | {
45 | "type": "patreon",
46 | "url": "https://www.patreon.com/harrytran998"
47 | }
48 | ],
49 | "keywords": ["techmely", "rate limit"]
50 | }
51 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE/feature.md:
--------------------------------------------------------------------------------
1 | # Feature merge request template
2 |
3 | ## What does this MR do and why?
4 |
5 | What do you want to happen? What problem are you trying to solve?
6 |
7 | ## How we setup and verify this pull request on localhost?
8 | Please tell us how to setup and verify this pull request on localhost. This will lead to a faster review and merge PR.
9 |
10 | ## MR acceptance checklist
11 | The standard criteria that a feature must satisfy to be accepted
12 |
13 |
14 | ### Semantic
15 |
16 | **Required**
17 |
18 | - [ ] I have linked an issue or discussion
19 | - [ ] MR follows the basic team's coding conventions.
20 | - [ ] This PR follow [the coding conventions](../../docs/coding-convention.md) of this project
21 | - [ ] This PR resolve all comments of the reviewers and have at least 1 approval
22 | - [ ] Files change do not exceed 40 files(Normally should be 20~30 files).
23 |
24 | *Optional*
25 | - [ ] I have updated the documentation accordingly
26 |
27 | ### Testing
28 |
29 | **Required**
30 | - [ ] Pass all pipeline
31 | - [ ] All logic functions must have the unit tests guarantee the code coverage of obtaining at least 80%
32 | - [ ] MR should not reduce the entire project's test coverage.
33 |
34 | *Optional*
35 |
36 | - [ ] UI tests must guarantee the code coverage of obtaining at least 40%(include all fields, exact words, animations and behaviors...)
37 |
38 | ## The way we review code together
39 |
40 | Follow theo [Code Review Guidelines](https://www.pluralsight.com/blog/software-development/code-review-checklist)
41 |
42 | /closes #
43 |
--------------------------------------------------------------------------------
/packages/nuxt-partytown/playground/app.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | Nuxt module playground!
4 |
5 |
6 |
7 |
46 |
--------------------------------------------------------------------------------
/packages/types/src/common.d.ts:
--------------------------------------------------------------------------------
1 | export type Nullable = T | null;
2 | export type UnDef = T | undefined;
3 | export type NullList = T | undefined | null;
4 | export type EntityId = number | string;
5 | export type Entity = number | string | symbol;
6 |
7 | export interface DictNum {
8 | [id: number]: T;
9 | }
10 | export interface Dict extends DictNum {
11 | [id: string]: T;
12 | }
13 |
14 | export interface EntityState {
15 | ids: string[];
16 | entities: Dict;
17 | }
18 |
19 | export type VoidFunc = (value: T) => void;
20 | export type StringEnum = T | (string & Record);
21 |
22 | export type Records = Record;
23 |
24 | // test if we are going the left AND right path in the condition
25 | export type IsAny = true | false extends (T extends never ? true : false)
26 | ? True
27 | : False;
28 |
29 | export type PreventAny = IsAny, S>;
30 |
31 | export type Comparer = (a: T, b: T) => number;
32 |
33 | export type MergeInsertions = T extends Record
34 | ? {
35 | [K in keyof T]: MergeInsertions;
36 | }
37 | : T;
38 |
39 | export type MergeDeep = MergeInsertions<{
40 | [K in keyof F | keyof S]: K extends keyof S & keyof F
41 | ? MergeDeep
42 | : K extends keyof S
43 | ? S[K]
44 | : K extends keyof F
45 | ? F[K]
46 | : never;
47 | }>;
48 |
49 | export type RuntimeEnv = "development" | "staging" | "production";
50 | export type NodeEnv = "development" | "test" | "production";
51 |
--------------------------------------------------------------------------------
/packages/cache/src/tiered.cache.ts:
--------------------------------------------------------------------------------
1 | import type { CachePort, Records } from "@techmely/types";
2 |
3 | /**
4 | * TieredCache is a cache that will first check the memory cache, then the zone cache
5 | * TODO: move to L1 and L2 cache
6 | */
7 | export class TieredCache implements CachePort {
8 | #tiers: CachePort[];
9 |
10 | constructor(...caches: CachePort[]) {
11 | this.#tiers = caches;
12 | }
13 |
14 | async get(
15 | namespace: Name,
16 | key: string,
17 | ): Promise<[NameSpaces[Name] | undefined, boolean]> {
18 | if (this.#tiers.length === 0) {
19 | return [undefined, false];
20 | }
21 | for (let i = 0; i < this.#tiers.length; i++) {
22 | const [cached, stale] = await this.#tiers[i].get(namespace, key);
23 | // Found in memory
24 | if (cached) {
25 | // Set to distributed cached
26 | for (let j = 0; j < i; j++) {
27 | this.#tiers[j].set(namespace, key, cached);
28 | }
29 | return [cached, stale];
30 | }
31 | }
32 | return [undefined, false];
33 | }
34 |
35 | async set(
36 | namespace: keyof NameSpaces,
37 | key: string,
38 | value: NameSpaces[Name],
39 | ): Promise {
40 | await Promise.all(this.#tiers.map((t) => t.set(namespace, key, value)));
41 | }
42 |
43 | async remove(namespace: keyof NameSpaces, key: string): Promise {
44 | await Promise.all(this.#tiers.map((t) => t.remove(namespace, key)));
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/packages/types/src/cache.port.d.ts:
--------------------------------------------------------------------------------
1 | import type { StringEnum } from "./common";
2 |
3 | export type CacheTier = "memory" | "zone";
4 |
5 | export interface BaseCachePort {
6 | key: string;
7 | namespace: string;
8 | tier: StringEnum | StringEnum[];
9 | }
10 |
11 | export type CachePortConfig = {
12 | /**
13 | * How long an entry should be fresh in milliseconds
14 | */
15 | fresh: number;
16 | /**
17 | * How long an entry should be stale in milliseconds
18 | *
19 | * Stale entries are still valid but should be refreshed in the background
20 | */
21 | stale: number;
22 | };
23 |
24 | export type CacheEntry = {
25 | value: TValue;
26 |
27 | // Before this time the entry is considered fresh and valid
28 | freshUntil: number;
29 |
30 | staleUntil: number;
31 | };
32 |
33 | export interface CachePort> {
34 | /**
35 | * Return the cached value
36 | *
37 | * The response will be `undefined` for cache misses or `null` when the key was not found in the origin
38 | *
39 | * The second value is true if the entry is stale and should be re-fetched from the origin
40 | */
41 | get(
42 | namespace: Name,
43 | key: string,
44 | ): [Namespaces[Name] | undefined, boolean] | Promise<[Namespaces[Name] | undefined, boolean]>;
45 |
46 | set(
47 | namespace: keyof Namespaces,
48 | key: string,
49 | value: Namespaces[Name],
50 | ): void;
51 |
52 | remove(namespace: keyof Namespaces, key: string): void;
53 | }
54 |
--------------------------------------------------------------------------------
/packages/rate-limit/src/cloudflare.do.rate-limit.ts:
--------------------------------------------------------------------------------
1 | import { number, object, safeParse } from "valibot";
2 | type CfMemory = {
3 | current: number;
4 | alarmScheduled?: number;
5 | };
6 |
7 | const requestSchema = object({
8 | reset: number(),
9 | });
10 |
11 | export class CfDoRateLimiter {
12 | #state: DurableObjectState;
13 | #memory: CfMemory;
14 | #storageKey = "rl";
15 |
16 | constructor(state: DurableObjectState) {
17 | this.#state = state;
18 | this.#state.blockConcurrencyWhile(async () => {
19 | const m = await this.#state.storage.get(this.#storageKey);
20 | if (m) this.#memory = m;
21 | });
22 | this.#memory ??= {
23 | current: 0,
24 | };
25 | }
26 |
27 | async fetch(request: Request) {
28 | const reqValue = await request.json();
29 | const req = safeParse(requestSchema, reqValue);
30 | if (!req.success) {
31 | console.log("Invalid DO req", req.issues[0].message);
32 | return Response.json({ current: 0 });
33 | }
34 |
35 | this.#memory.current += 1;
36 | if (!this.#memory.alarmScheduled) {
37 | this.#memory.alarmScheduled = req.output.reset;
38 | await this.#state.storage.setAlarm(this.#memory.alarmScheduled);
39 | }
40 | await this.#state.storage.put(this.#storageKey, this.#memory);
41 |
42 | return Response.json({
43 | current: this.#memory.current,
44 | });
45 | }
46 |
47 | /**
48 | * alarm is called to clean up all state, which will remove the durable object from existence.
49 | */
50 | async alarm() {
51 | await this.#state.storage.deleteAll();
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/packages/cache/src/metrics.cache.ts:
--------------------------------------------------------------------------------
1 | import type { CachePort, CacheTier, MetricsPort, Records } from "@techmely/types";
2 |
3 | export class MetricsCache implements CachePort {
4 | #cache: CachePort;
5 | readonly #tier: CacheTier;
6 | readonly #metrics: MetricsPort;
7 |
8 | constructor(cache: CachePort, tier: CacheTier, metrics: MetricsPort) {
9 | this.#cache = cache;
10 | this.#tier = tier;
11 | this.#metrics = metrics;
12 | }
13 |
14 | async get(
15 | namespace: Name,
16 | key: string,
17 | ): Promise<[NameSpaces[Name] | undefined, boolean]> {
18 | const start = performance.now();
19 | const [cached, stale] = await this.#cache.get(namespace, key);
20 | this.#metrics.emit("cacheRead", {
21 | hit: typeof cached !== "undefined",
22 | latency: performance.now() - start,
23 | tier: this.#tier,
24 | namespace: String(namespace),
25 | key,
26 | });
27 | return [cached, stale];
28 | }
29 | set(
30 | namespace: keyof NameSpaces,
31 | key: string,
32 | value: NameSpaces[Name],
33 | ): void {
34 | this.#metrics.emit("cacheWrite", {
35 | tier: this.#tier,
36 | namespace: String(namespace),
37 | key,
38 | });
39 | this.#cache.set(namespace, key, value);
40 | }
41 | remove(namespace: keyof NameSpaces, key: string): void {
42 | this.#metrics.emit("cachePurge", {
43 | tier: this.#tier,
44 | namespace: String(namespace),
45 | key,
46 | });
47 | this.#cache.remove(namespace, key);
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/packages/nuxt-partytown/playground/layouts/default.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE/bug.md:
--------------------------------------------------------------------------------
1 | # Bug pull request template
2 |
3 |
4 |
5 | ## Checklist
6 |
7 |
8 | - [ ] Bug fix (non-breaking change which fixes an issue)
9 | - [ ] New feature (non-breaking change which adds functionality)
10 | - [ ] Breaking change (fix or feature that would cause existing functionality to change)
11 | - [ ] I have read the **CONTRIBUTING** document.
12 | - [ ] My code follows the code style of this project.
13 | - [ ] My change requires a change to the documentation.
14 | - [ ] I have updated the documentation accordingly.
15 | - [ ] I have added tests to cover my changes.
16 | - [ ] All new and existing tests passed.
17 |
18 | ## Description
19 |
20 |
21 | ## Related Issue
22 |
23 |
24 |
25 |
26 |
27 | ## Motivation and Context
28 |
29 |
30 |
31 | ## How Has This Been Tested?
32 |
33 |
34 |
35 |
36 | ## Screenshots (if appropriate)
37 |
--------------------------------------------------------------------------------
/packages/cache/src/memory.cache.ts:
--------------------------------------------------------------------------------
1 | import type { CacheEntry, CachePort, CachePortConfig, Records } from "@techmely/types";
2 |
3 | export class MemoryCache implements CachePort {
4 | readonly #state: Map<`${string}:${string}`, CacheEntry>;
5 | readonly #config: CachePortConfig;
6 |
7 | constructor(config: CachePortConfig) {
8 | this.#state = new Map();
9 | this.#config = config;
10 | }
11 | get(
12 | namespace: Name,
13 | key: string,
14 | ): [NameSpaces[Name] | undefined, boolean] | Promise<[NameSpaces[Name] | undefined, boolean]> {
15 | const nameKey = `${String(namespace)}:${key}` as const;
16 | const cached = this.#state.get(nameKey) as CacheEntry | undefined;
17 | if (!cached) return [undefined, false];
18 | const now = Date.now();
19 | if (now > cached.staleUntil) {
20 | this.#state.delete(nameKey);
21 | return [undefined, false];
22 | }
23 | if (now >= cached.freshUntil) {
24 | return [cached.value, true];
25 | }
26 | return [cached.value, false];
27 | }
28 | set(
29 | namespace: keyof NameSpaces,
30 | key: string,
31 | value: NameSpaces[Name],
32 | ): void {
33 | const now = Date.now();
34 | const nameKey = `${String(namespace)}:${key}` as const;
35 | this.#state.set(nameKey, {
36 | value,
37 | freshUntil: now + this.#config.fresh,
38 | staleUntil: now + this.#config.stale,
39 | });
40 | }
41 | remove(namespace: keyof NameSpaces, key: string): void {
42 | const nameKey = `${String(namespace)}:${key}` as const;
43 | this.#state.delete(nameKey);
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/packages/reset-css/reset.min.css:
--------------------------------------------------------------------------------
1 | @layer reset{body,html{line-height:1.5}a,hr{color:inherit}*,blockquote,dd,dl,fieldset,figure,h1,h2,h3,h4,h5,h6,hr,menu,ol,p,pre,ul{margin:0}fieldset,legend,menu,ol,ul{padding:0}*,::after,::before{box-sizing:border-box}html{-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4}#__next,#__nuxt,#root,body,html{height:100%}body{-webkit-font-smoothing:antialiased}audio,canvas,embed,iframe,img,object,picture,svg,video{display:block;max-width:100%}button,input,select,textarea{font:inherit;color:inherit;margin:0;padding:0}h1,h2,h3,h4,h5,h6,p{font-size:inherit;font-weight:inherit;overflow-wrap:break-word}#__next,#__nuxt,#root{isolation:isolate}hr{height:0;border-top-width:1px}abbr:where([title]){text-decoration:underline dotted}a{text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-size:1em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{background-color:transparent;background-image:none;border:none;}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}menu,ol,ul{list-style:none}textarea{resize:vertical}input::placeholder,textarea::placeholder{opacity:1;color:#9ba3af}[role=button],button{cursor:pointer}:disabled{cursor:default}img,video{max-width:100%;height:auto}@media (prefers-reduced-motion:reduce){html:focus-within{scroll-behavior:auto}*,::after,::before{animation-duration:0s!important;animation-iteration-count:1!important;transition-duration:0s!important;scroll-behavior:auto!important}}}
2 |
--------------------------------------------------------------------------------
/.moon/tasks/typescript.yml:
--------------------------------------------------------------------------------
1 | $schema: "https://moonrepo.dev/schemas/tasks.json"
2 |
3 | implicitInputs:
4 | - "package.json"
5 |
6 | fileGroups:
7 | configs:
8 | - "*.{js,json,yml,yaml}"
9 | sources:
10 | - "src/**/*"
11 | tests:
12 | - "src/**/*.test.mts"
13 | - "tests/**/*.test.mts"
14 | - "tests/**/*.stories.*"
15 | - "**/__tests__/**/*"
16 |
17 | tasks:
18 | build:
19 | command: "tsup"
20 | args: "src/index.ts --clean --format esm --dts --tsconfig tsconfig.build.json"
21 | inputs:
22 | - "@globs(sources)"
23 | - "package.json"
24 | outputs:
25 | - "dist"
26 |
27 | clean:
28 | command:
29 | - "rm -rf dist"
30 | options:
31 | shell: true
32 |
33 | lint:
34 | command: "bunx"
35 | args: "@biomejs/biome check --apply ."
36 | inputs:
37 | - "/biome.json"
38 | - "/tsconfig.base.json"
39 | - "tsconfig.json"
40 | - "@globs(sources)"
41 | - "@globs(tests)"
42 |
43 | test:
44 | command:
45 | - "vitest"
46 | args: "run --passWithNoTests --isolate"
47 | inputs:
48 | - "@globs(sources)"
49 | - "@globs(tests)"
50 | - "vitest.config.*"
51 |
52 | typecheck:
53 | command:
54 | - "tsc"
55 | args: "--noEmit"
56 | inputs:
57 | - "@globs(sources)"
58 | - "@globs(tests)"
59 | - "tsconfig.json"
60 | - "tsconfig.*.json"
61 | - "/tsconfig.base.json"
62 |
63 | publish:
64 | command: "node ../../scripts/publishPkg.mjs"
65 | inputs:
66 | - "@globs(sources)"
67 | - "@globs(tests)"
68 | options:
69 | allowFailure: true
70 |
71 | upgrade.deps:
72 | command: "npm-check-updates --dep dev,prod,peer -u -x react -x react-dom -x @types/react -x @types/react-dom"
73 | options:
74 | allowFailure: true
75 |
--------------------------------------------------------------------------------
/packages/logger/src/axiom.ts:
--------------------------------------------------------------------------------
1 | import { Axiom } from "@axiomhq/js";
2 | import type { LoggerPort, LoggerPortLevel, Records, RuntimeEnv } from "@techmely/types";
3 | import { ConsoleLogger } from "./console";
4 |
5 | type AxiomLoggerOptions = {
6 | dataset: string;
7 | environment: RuntimeEnv;
8 | meta?: Records;
9 | };
10 |
11 | export class AxiomLogger implements LoggerPort {
12 | #consoleLogger: LoggerPort;
13 | #axiomDataset: string;
14 | #ax: Axiom;
15 | #meta: Records;
16 |
17 | constructor(token: string, options: AxiomLoggerOptions) {
18 | this.#consoleLogger = new ConsoleLogger();
19 | this.#axiomDataset = options.dataset || `cf_api_log_${options.environment}`;
20 | this.#ax = new Axiom({ token });
21 | this.#meta = options.meta || {};
22 | }
23 |
24 | #combineIngestEvents(level: LoggerPortLevel, message: string, meta?: Records) {
25 | this.#consoleLogger[level](message, meta);
26 | this.#ax.ingest(this.#axiomDataset, [
27 | {
28 | level,
29 | _time: Date.now(),
30 | message,
31 | ...this.#meta,
32 | ...meta,
33 | },
34 | ]);
35 | }
36 |
37 | info(message: string, meta?: Records): void {
38 | this.#combineIngestEvents("info", message, meta);
39 | }
40 | error(message: string, meta?: Records): void {
41 | this.#combineIngestEvents("error", message, meta);
42 | }
43 | warn(message: string, meta?: Records): void {
44 | this.#combineIngestEvents("warn", message, meta);
45 | }
46 | debug(message: string, meta?: Records): void {
47 | this.#combineIngestEvents("debug", message, meta);
48 | }
49 | async flush(): Promise {
50 | try {
51 | await this.#ax.flush();
52 | } catch (error) {
53 | this.#consoleLogger.error("Unnable to flush logs to axiom", error);
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/packages/hono/src/firebase-auth/index.ts:
--------------------------------------------------------------------------------
1 | import {
2 | type UserFromDecodedIdToken,
3 | fetchGooglePublicKeys,
4 | verifyAndDecodeJwt,
5 | } from "@techmely/auth";
6 | import type { MiddlewareHandler } from "hono";
7 |
8 | export type FirebaseBaseAuthOptions = {
9 | projectId: string;
10 | transformUser?: (decodedToken: UserFromDecodedIdToken) => T;
11 | /**
12 | * @default "firebaseUser"
13 | */
14 | userContextKey?: string;
15 | /**
16 | * @default "Bearer"
17 | */
18 | tokenHeaderPrefix?: string;
19 | };
20 |
21 | const transformCurrentUser = (decodedToken: UserFromDecodedIdToken) => {
22 | return decodedToken;
23 | };
24 |
25 | /**
26 | * If you want to access the user through c.get("user") with typing
27 | * Please try to add like `{ user: UserFromDecodedIdToken }` to your hono binding variables
28 | */
29 | export function validateFirebaseAuth(options: FirebaseBaseAuthOptions): MiddlewareHandler {
30 | const {
31 | projectId,
32 | tokenHeaderPrefix = "Bearer",
33 | transformUser = transformCurrentUser,
34 | userContextKey = "firebaseUser",
35 | } = options;
36 |
37 | return async (c, next) => {
38 | const tokenHeader = c.req.header("Authorization");
39 | // When not pass headers ==> By pass verify
40 | if (!tokenHeader || !tokenHeader.startsWith(tokenHeaderPrefix)) {
41 | return await next();
42 | }
43 |
44 | const token = tokenHeader.substring(tokenHeaderPrefix.length + 1);
45 | try {
46 | const publicKeys = await fetchGooglePublicKeys();
47 | const user = await verifyAndDecodeJwt(token, publicKeys, projectId);
48 | const _user = transformUser(user);
49 | c.set(userContextKey, _user);
50 | await next();
51 | } catch (err) {
52 | console.error("Error when verify firebase auth", err);
53 | throw new Error("Error when verify firebase auth");
54 | }
55 | };
56 | }
57 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature-request.yml:
--------------------------------------------------------------------------------
1 | name: 🦄 Feature Request
2 | description: Suggest an idea for a new feature or enhancement
3 | title: "[FEAT]: "
4 | labels: ["feature"]
5 | assignees: ["techmely"]
6 | body:
7 | - type: markdown
8 | attributes:
9 | value: |
10 | Thanks for taking the time to complete this feature!
11 | Please carefully read the contribution guidelines before request a review from others.
12 | https://github.com/techmely/essential-packages/blob/main/docs/contribution.md#create-feature-request
13 | - type: textarea
14 | id: describe-feature
15 | attributes:
16 | label: What does this MR do and why?
17 | description: What do you want to happen? What problem are you trying to solve?
18 | placeholder: Describe the need for the feature.
19 | validations:
20 | required: true
21 | - type: textarea
22 | id: resources
23 | attributes:
24 | label: The resources that you have researched
25 | description: To solve this problem, what's resources we need to access?
26 |
27 | - type: checkboxes
28 | id: Acceptance-Criteria
29 | attributes:
30 | label: Acceptance Criteria Checklist
31 | description: The standard criteria that a feature must satisfy to be accepted
32 | options:
33 | - label: Read the [contribution guide](https://github.com/techmely/essential-packages/blob/main/docs/contribution.md)
34 | - label: Check existing [discussions](https://github.com/techmely/essential-packages/discussions) and [issues](https://github.com/techmely/essential-packages/issues).
35 | required: true
36 | - label: The scope of the feature is clear and small enough to be implemented in a single MR - Not exceeding 500 lines of code and 40 files changed.
37 | required: true
38 | - label: The time doing this feature does not exceed 1 day.
39 | required: true
40 |
--------------------------------------------------------------------------------
/packages/valibot-stream/README.md:
--------------------------------------------------------------------------------
1 | # Valibot Stream
2 |
3 | Define structured response models for OpenAI or Anyscale completions using Valibot schemas and enable partial streaming of that json so that it can be used safely and right away.
4 |
5 | ## Basic Usage
6 |
7 | ```ts
8 | import { OAIStream } from "@techmely/valibot-stream/OAIStream";
9 | import { withResponseModel } from "@techmely/valibot-stream/response-model";
10 |
11 | import OpenAI from "openai";
12 | import * as v from "valibot";
13 |
14 | const oai = new OpenAI({
15 | apiKey: process.env["OPENAI_API_KEY"] ?? undefined,
16 | organization: process.env["OPENAI_ORG_ID"] ?? undefined,
17 | });
18 |
19 | export async function POST(request: Request) {
20 | const { messages } = await request.json();
21 |
22 | const params = withResponseModel({
23 | response_model: { schema: v.object({ content: v.string() }), name: "Content response" },
24 | params: {
25 | messages,
26 | model: "gpt-4",
27 | },
28 | mode: "TOOLS",
29 | });
30 |
31 | const extractionStream = await oai.chat.completions.create({
32 | ...params,
33 | stream: true,
34 | });
35 |
36 | return new Response(
37 | OAIStream({
38 | res: extractionStream,
39 | }),
40 | );
41 | }
42 | ```
43 |
44 | Consuming the structured stream elsewhere, maybe in the browser.
45 |
46 | ```ts
47 | const client = new ValibotStream();
48 |
49 | const stream = await client.create({
50 | completionPromise: async () => {
51 | const response = fetch("/api/get-stream", {
52 | body: JSON.stringify({ messages: [] }),
53 | method: "POST",
54 | });
55 |
56 | return response.body;
57 | },
58 | response_model: {
59 | // should match model expected to be returned by the completion.
60 | schema: v.object({
61 | content: v.string(),
62 | }),
63 | },
64 | });
65 |
66 | for await (const chunk of extractionStream) {
67 | console.log(chunk); // safe to parse partial json
68 | }
69 | ```
70 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | push:
5 | paths:
6 | - '.changeset/**'
7 | - 'packages/**'
8 | branches:
9 | - main
10 |
11 | jobs:
12 | Release:
13 | runs-on: ubuntu-latest
14 | steps:
15 | - name: Install pnpm
16 | uses: pnpm/action-setup@v2
17 |
18 | - name: Set node version to 16
19 | uses: actions/setup-node@v2
20 | with:
21 | node-version: 16
22 | cache: 'pnpm'
23 |
24 | - run: pnpm install
25 |
26 | - name: Setup Git
27 | run: |
28 | git config user.name "Harry Tran"
29 | git config user.email "nhattq.coding@gmail.com"
30 |
31 | - name: Configure npm
32 | run: |
33 | cat << EOF > "$HOME/.npmrc"
34 | //registry.npmjs.org/:_authToken=$NPM_TOKEN
35 | EOF
36 | env:
37 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
38 |
39 | - name: Build packages
40 | run: pnpm run build:deps
41 |
42 | - name: Create release Pull Request or publish to NPM
43 | id: changesets
44 | uses: changesets/action@v1
45 | with:
46 | publish: pnpm changeset publish
47 | commit: 'ci(changesets): version packages'
48 | title: 'ci(changesets): version packages'
49 | env:
50 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
51 | GH_TOKEN: ${{ secrets.GH_TOKEN }}
52 |
53 | - name: Release to @dev tag
54 | if: steps.changesets.outputs.published != 'true'
55 | run: |
56 | git checkout main
57 | pnpm changeset version --snapshot dev
58 | pnpm changeset publish --tag dev
59 | env:
60 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
61 | GH_TOKEN: ${{ secrets.GH_TOKEN }}
62 |
63 | - name: Update changelog
64 | if: steps.changesets.outputs.published == 'true'
65 | run: |
66 | pnpm run changelog:write
67 | pnpm run changelog:commit
68 |
--------------------------------------------------------------------------------
/packages/rate-limit/src/cloudflare.rate-limit.ts:
--------------------------------------------------------------------------------
1 | import type {
2 | LoggerPort,
3 | MetricsPort,
4 | RateLimitRequest,
5 | RateLimitResponse,
6 | RateLimiterPort,
7 | } from "@techmely/types";
8 |
9 | type CfRateLimiterOptions = {
10 | domain?: string;
11 | logger: LoggerPort;
12 | metrics: MetricsPort;
13 | };
14 |
15 | export class CfRateLimiter implements RateLimiterPort {
16 | readonly #namespace: DurableObjectNamespace;
17 | readonly #domain: string;
18 | readonly #logger: LoggerPort;
19 | readonly #metrics: MetricsPort;
20 |
21 | constructor(namespace: DurableObjectNamespace, options: CfRateLimiterOptions) {
22 | this.#namespace = namespace;
23 | this.#domain = options.domain || "techmely.com";
24 | this.#logger = options.logger;
25 | this.#metrics = options.metrics;
26 | }
27 | async limit(req: RateLimitRequest): Promise {
28 | const start = performance.now();
29 | const now = Date.now();
30 | const window = Math.floor(now / req.interval);
31 | const reset = (window + 1) & req.interval;
32 | const keyWindow = [req.id, window].join(":");
33 |
34 | try {
35 | const obj = this.#namespace.get(this.#namespace.idFromName(keyWindow));
36 | const url = `https://${this.#domain}/rate-limit`;
37 | const res = await obj.fetch(url, {
38 | method: "POST",
39 | headers: { "Content-Type": "application/json" },
40 | body: JSON.stringify({ reset }),
41 | });
42 | const json = await res.json<{ current: number }>();
43 | const current = json.current;
44 | return {
45 | current,
46 | reset,
47 | passed: current < req.limit,
48 | };
49 | } catch (error) {
50 | this.#logger.error("Rate limit failed", { key: req.id });
51 | return {
52 | current: 0,
53 | reset,
54 | passed: false,
55 | };
56 | } finally {
57 | this.#metrics.emit("usageLimit", {
58 | latency: performance.now() - start,
59 | keyId: req.id,
60 | });
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/packages/nuxt-pinia-persist/src/module.ts:
--------------------------------------------------------------------------------
1 | import {
2 | addImports,
3 | addImportsSources,
4 | addPlugin,
5 | createResolver,
6 | defineNuxtModule,
7 | useLogger,
8 | } from "@nuxt/kit";
9 | import { removeEmptyObj } from "@techmely/utils";
10 | import type { PersistGlobalConfig } from "@techmely/vue-pinia-persist";
11 | import defu from "defu";
12 |
13 | const logger = useLogger("nuxt:pinia-persist");
14 |
15 | export type ModuleOptions = PersistGlobalConfig;
16 |
17 | export default defineNuxtModule({
18 | meta: {
19 | name: "@techmely/nuxt-pinia-persist",
20 | configKey: "pinia-persist",
21 | compatibility: {
22 | nuxt: ">=3.0.0",
23 | },
24 | },
25 | async setup(options, nuxt) {
26 | const { onAfterRestore, onBeforeRestore, ...restOptions } = options;
27 | const { resolve } = createResolver(import.meta.url);
28 |
29 | nuxt.options.build.transpile.push(resolve("./runtime"));
30 |
31 | // Add runtime plugin before the router plugin
32 | // https://github.com/nuxt/framework/issues/9130
33 | nuxt.hook("modules:done", () => {
34 | addPlugin(resolve("./runtime/plugin"));
35 | });
36 |
37 | nuxt.options.runtimeConfig.public.persist = defu(
38 | nuxt.options.runtimeConfig.public.persist,
39 | restOptions,
40 | removeEmptyObj({
41 | onAfterRestore: onAfterRestore ? String(onAfterRestore) : undefined,
42 | onBeforeRestore: onBeforeRestore ? String(onBeforeRestore) : undefined,
43 | }),
44 | );
45 |
46 | // Add auto imports usePinia
47 | const composables = resolve("./runtime/composables");
48 | addImports([{ from: composables, name: "usePinia" }]);
49 |
50 | addImportsSources({
51 | from: "pinia",
52 | imports: ["defineStore", "acceptHMRUpdate"],
53 | });
54 |
55 | logger.success("Complete setup module");
56 | },
57 | });
58 |
59 | declare module "@nuxt/schema" {
60 | interface PublicRuntimeConfig {
61 | persist: Omit & {
62 | onBeforeRestore: string;
63 | onAfterRestore: string;
64 | };
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/packages/usage-limit/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @techmely/usage-limit
2 |
3 | ## 2.0.3
4 |
5 | ### Patch Changes
6 |
7 | - Upgrade deps
8 |
9 | - Updated dependencies []:
10 | - @techmely/logger@1.1.3
11 | - @techmely/metrics@1.0.10
12 |
13 | ## 2.0.2
14 |
15 | ### Patch Changes
16 |
17 | - Fix typing + packaging
18 |
19 | - Updated dependencies []:
20 | - @techmely/logger@1.1.2
21 | - @techmely/metrics@1.0.9
22 |
23 | ## 2.0.1
24 |
25 | ### Patch Changes
26 |
27 | - [`4fb8e01`](https://github.com/techmely/essential-packages/commit/4fb8e018133c2abaf622762e1b53667191b624d8) Thanks [@harrytran998](https://github.com/harrytran998)! - Fix replace build packages
28 |
29 | - Updated dependencies [[`4fb8e01`](https://github.com/techmely/essential-packages/commit/4fb8e018133c2abaf622762e1b53667191b624d8)]:
30 | - @techmely/logger@1.1.1
31 | - @techmely/metrics@1.0.8
32 |
33 | ## 2.0.0
34 |
35 | ### Patch Changes
36 |
37 | - Update the workflow we use to build this project
38 |
39 | - Updated dependencies [[`e496334`](https://github.com/techmely/essential-packages/commit/e49633413e30c9350a3c6bb27137bc1da6a7bd07)]:
40 | - @techmely/logger@1.1.0
41 | - @techmely/metrics@1.0.7
42 |
43 | ## 1.0.7
44 |
45 | ### Patch Changes
46 |
47 | - Upgrade deps
48 |
49 | - Updated dependencies []:
50 | - @techmely/metrics@1.0.5
51 | - @techmely/logger@1.0.6
52 |
53 | ## 1.0.5
54 |
55 | ### Patch Changes
56 |
57 | - Update all the things
58 |
59 | - Updated dependencies []:
60 | - @techmely/logger@1.0.5
61 | - @techmely/metrics@1.0.4
62 |
63 | ## 1.0.4
64 |
65 | ### Patch Changes
66 |
67 | - Bump version & upgrade deps
68 |
69 | - Updated dependencies []:
70 | - @techmely/logger@1.0.4
71 | - @techmely/metrics@1.0.3
72 |
73 | ## 1.0.3
74 |
75 | ### Patch Changes
76 |
77 | - upgrade deps
78 |
79 | - Updated dependencies []:
80 | - @techmely/logger@1.0.3
81 | - @techmely/metrics@1.0.2
82 |
83 | ## 1.0.2
84 |
85 | ### Patch Changes
86 |
87 | - Upgrade deps + upgrade ultils package
88 |
89 | ## 1.0.1
90 |
91 | ### Patch Changes
92 |
93 | - Update typing using port from techmely.types
94 |
95 | - Updated dependencies []:
96 | - @techmely/logger@1.0.2
97 | - @techmely/metrics@1.0.1
98 |
--------------------------------------------------------------------------------
/packages/text-annotation/README.md:
--------------------------------------------------------------------------------
1 | # Text Annotation
2 |
3 | Structured text extraction for llms, designed for simplicity, transparency, and control.
4 |
5 | ## Basic use
6 |
7 | ```ts
8 | import Annotator from "@techmely/text-annotation";
9 | import OpenAI from "openai";
10 | import * as v from "valibot";
11 |
12 | const oai = new OpenAI({
13 | apiKey: process.env.OPENAI_API_KEY ?? undefined,
14 | organization: process.env.OPENAI_ORG_ID ?? undefined,
15 | });
16 |
17 | const client = Annotator({
18 | client: oai,
19 | mode: "TOOLS",
20 | });
21 |
22 | const UserSchema = v.object({
23 | // Description will be used in the prompt
24 | age: v.number().describe("The age of the user"),
25 | name: v.string(),
26 | character: v.string(),
27 | });
28 |
29 | // User will be of type z.infer
30 | const user = await client.chat.completions.create({
31 | messages: [{ role: "user", content: "Harry Tran is 30 years old. He is handsome guy" }],
32 | model: "gpt-4",
33 | response_model: {
34 | schema: UserSchema,
35 | name: "User",
36 | },
37 | });
38 |
39 | console.log(user);
40 | // { age: 30, name: "Harry Tran", character: "handsome" }
41 | ```
42 |
43 | ## Advanced Use
44 |
45 | ### Automate Detect Right Schema
46 |
47 | ```ts
48 | import Annotator from "@techmely/text-annotation";
49 | import OpenAI from "openai";
50 | import * as v from "valibot";
51 |
52 | const oai = new OpenAI({
53 | apiKey: process.env.OPENAI_API_KEY ?? undefined,
54 | organization: process.env.OPENAI_ORG_ID ?? undefined,
55 | });
56 |
57 | const UserSchema = v.object({
58 | // Description will be used in the prompt
59 | age: v.number().describe("The age of the user"),
60 | name: v.string(),
61 | character: v.string(),
62 | });
63 |
64 | const client = Annotator({
65 | client: oai,
66 | mode: "auto",
67 | pools: [UserSchema, PostSchema, XyzSchema],
68 | });
69 |
70 | // User will be of type z.infer
71 | const user = await client.chat.completions.create({
72 | messages: [{ role: "user", content: "Harry Tran is 30 years old. He is handsome guy" }],
73 | model: "gpt-4",
74 | });
75 |
76 | console.log(user);
77 | // { age: 30, name: "Harry Tran", character: "handsome" }
78 | ```
79 |
--------------------------------------------------------------------------------
/packages/vue-pinia-persist/README.md:
--------------------------------------------------------------------------------
1 | # Vue Pinia Persist
2 |
3 | Persist and rehydrate pinia store
4 |
5 | ## Features
6 |
7 | - Persist Pinia stores with a friendly API inspired by [Redux Persist](https://github.com/rt2zz/redux-persist)
8 | - You can custom storage, serializer, paths picking and more...
9 | - Support Transform/Migration like Redux Persist does
10 | - Lightweight + Performances for most cases
11 | - Tiny package (<2kB gzip)
12 | - SSR friendly
13 | ## Quick Start
14 |
15 | 1. Install package
16 | ```bash
17 | npm i @techmely/vue-pinia-persist
18 | ```
19 |
20 | ```bash
21 | pnpm add @techmely/vue-pinia-persist
22 | ```
23 |
24 | ```bash
25 | yarn add @techmely/vue-pinia-persist
26 | ```
27 | 2. Add the plugin to pinia
28 |
29 | ```ts
30 | import { createPinia } from 'pinia'
31 | import { piniaPersist } from '@techmely/vue-pinia-persist'
32 |
33 | const pinia = createPinia()
34 | pinia.use(piniaPersist())
35 | ```
36 |
37 | 3. Enjoy
38 |
39 | ## Configuration
40 |
41 |
42 | ## Limitations
43 |
44 | ## Contribute
45 |
46 | ## License
47 |
48 | MIT License
49 |
50 | Copyright (c) 2023-present, Techmely
51 |
52 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
53 |
54 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
55 |
56 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
57 |
--------------------------------------------------------------------------------
/packages/logger/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@techmely/logger",
3 | "version": "1.1.3",
4 | "description": "Techmely logger",
5 | "author": "Harry Tran ",
6 | "license": "MIT",
7 | "homepage": "https://about.techmely.com/libraries/logger",
8 | "bugs": {
9 | "url": "https://github.com/techmely/essential-packages/issues"
10 | },
11 | "repository": {
12 | "url": "git+https://github.com/techmely/essential-packages.git#main",
13 | "directory": "ts-package/logger"
14 | },
15 | "type": "module",
16 | "main": "dist/index.js",
17 | "types": "dist/index.d.ts",
18 | "publishConfig": {
19 | "access": "public",
20 | "directory": "dist",
21 | "tag": "latest"
22 | },
23 | "files": ["dist"],
24 | "peerDependencies": {
25 | "@axiomhq/js": "^1.0.0",
26 | "pino": "^9.2.0",
27 | "pino-pretty": "^11.2.1"
28 | },
29 | "peerDependenciesMeta": {
30 | "@axiomhq/js": {
31 | "optional": true
32 | },
33 | "pino": {
34 | "optional": true
35 | },
36 | "pino-pretty": {
37 | "optional": true
38 | },
39 | "@techmely/types": {
40 | "optional": true
41 | }
42 | },
43 | "devDependencies": {
44 | "@axiomhq/js": "^1.0.0",
45 | "pino": "^9.2.0",
46 | "@cloudflare/workers-types": "4.20240620.0",
47 | "@techmely/types": "workspace:*",
48 | "tslib": "2.6.3",
49 | "tsup": "8.1.0",
50 | "typescript": "5.5.3"
51 | },
52 | "exports": {
53 | "./package.json": "./package.json",
54 | ".": {
55 | "import": {
56 | "types": "./dist/index.d.ts",
57 | "import": "./dist/index.js"
58 | }
59 | },
60 | "./axiom": {
61 | "import": {
62 | "types": "./dist/axiom.d.ts",
63 | "import": "./dist/axiom.js"
64 | }
65 | },
66 | "./pino": {
67 | "import": {
68 | "types": "./dist/pino.d.ts",
69 | "import": "./dist/pino.js"
70 | }
71 | }
72 | },
73 | "funding": [
74 | {
75 | "type": "ko-fi",
76 | "url": "https://ko-fi.com/harrytran998"
77 | },
78 | {
79 | "type": "patreon",
80 | "url": "https://www.patreon.com/harrytran998"
81 | }
82 | ],
83 | "keywords": ["techmely", "logger"]
84 | }
85 |
--------------------------------------------------------------------------------
/packages/hono/src/valibot-validator/index.ts:
--------------------------------------------------------------------------------
1 | import type { Context, Env, Input as HonoInput, MiddlewareHandler, ValidationTargets } from "hono";
2 | import { validator } from "hono/validator";
3 | import type {
4 | GenericSchema,
5 | GenericSchemaAsync,
6 | InferInput,
7 | InferOutput,
8 | SafeParseResult,
9 | } from "valibot";
10 | import { safeParseAsync } from "valibot";
11 |
12 | type Hook = (
13 | result: SafeParseResult,
14 | c: Context,
15 | ) => Response | Promise | void | Promise;
16 |
17 | type HasUndefined = undefined extends T ? true : false;
18 |
19 | export const vValidator = <
20 | T extends GenericSchema | GenericSchemaAsync,
21 | Target extends keyof ValidationTargets,
22 | E extends Env,
23 | P extends string,
24 | In = InferInput,
25 | Out = InferOutput,
26 | I extends HonoInput = {
27 | in: HasUndefined extends true
28 | ? {
29 | [K in Target]?: K extends "json"
30 | ? In
31 | : HasUndefined extends true
32 | ? { [K2 in keyof In]?: ValidationTargets[K][K2] }
33 | : { [K2 in keyof In]: ValidationTargets[K][K2] };
34 | }
35 | : {
36 | [K in Target]: K extends "json"
37 | ? In
38 | : HasUndefined extends true
39 | ? { [K2 in keyof In]?: ValidationTargets[K][K2] }
40 | : { [K2 in keyof In]: ValidationTargets[K][K2] };
41 | };
42 | out: { [K in Target]: Out };
43 | },
44 | V extends I = I,
45 | >(
46 | target: Target,
47 | schema: T,
48 | hook?: Hook,
49 | ): MiddlewareHandler =>
50 | // @ts-expect-error not typed well
51 | validator(target, async (value, c) => {
52 | const result = await safeParseAsync(schema, value);
53 |
54 | if (hook) {
55 | const hookResult = hook(result, c);
56 | if (hookResult instanceof Response || hookResult instanceof Promise) {
57 | return hookResult;
58 | }
59 | }
60 |
61 | if (!result.success) {
62 | return c.json(result, 400);
63 | }
64 |
65 | const data = result.output as InferOutput;
66 | return data;
67 | });
68 |
--------------------------------------------------------------------------------
/packages/http/src/http.utils.ts:
--------------------------------------------------------------------------------
1 | import type { Headers } from "undici-types";
2 | import { TimeOutException } from "./exceptions";
3 | import { httpDefaultRetry, httpRetryStatusCodes } from "./http.const";
4 | import type { HttpHeadersInit, HttpRetryOptions, HttpTimeoutOptions } from "./http.types";
5 |
6 | export const normalizeHttpRetryOptions = (
7 | retry: number | HttpRetryOptions = {},
8 | ): Required => {
9 | if (typeof retry === "number") {
10 | return {
11 | ...httpDefaultRetry,
12 | limit: retry,
13 | };
14 | }
15 |
16 | if (retry.methods && !Array.isArray(retry.methods)) {
17 | throw new Error("retry.methods must be an array");
18 | }
19 |
20 | if (retry.statusCodes && !Array.isArray(retry.statusCodes)) {
21 | throw new Error("retry.statusCodes must be an array");
22 | }
23 |
24 | return {
25 | ...httpDefaultRetry,
26 | ...retry,
27 | afterStatusCodes: httpRetryStatusCodes,
28 | };
29 | };
30 |
31 | function createHeaders(source: HttpHeadersInit = {}) {
32 | return new globalThis.Headers(source as RequestInit["headers"]) as unknown as Headers;
33 | }
34 |
35 | export const mergeHttpHeaders = (source1: HttpHeadersInit = {}, source2: HttpHeadersInit = {}) => {
36 | const result = createHeaders(source1);
37 | const isHeadersInstance = source2 instanceof globalThis.Headers;
38 | const source = createHeaders(source2);
39 |
40 | for (const [key, value] of source.entries()) {
41 | if ((isHeadersInstance && value === "undefined") || value === undefined) {
42 | result.delete(key);
43 | } else {
44 | result.set(key, value);
45 | }
46 | }
47 |
48 | return result as any;
49 | };
50 |
51 | export const fetchTimeOut = async (
52 | request: Request,
53 | init: RequestInit,
54 | abortController: AbortController | null,
55 | options: HttpTimeoutOptions,
56 | ): Promise => {
57 | return new Promise((resolve, reject) => {
58 | const timeoutId = setTimeout(() => {
59 | if (abortController) {
60 | abortController.abort();
61 | }
62 | reject(new TimeOutException(JSON.stringify(request)));
63 | }, options.timeout);
64 |
65 | options
66 | .fetch(request, init)
67 | .then(resolve)
68 | .catch(reject)
69 | .then(() => {
70 | clearTimeout(timeoutId);
71 | });
72 | });
73 | };
74 |
--------------------------------------------------------------------------------
/packages/hono/src/metrics/index.ts:
--------------------------------------------------------------------------------
1 | import type { MiddlewareHandler } from "hono";
2 |
3 | export function metricsMiddleware(): MiddlewareHandler {
4 | return async (c, next) => {
5 | const { logger, analytics, metrics } = c.get("container");
6 | const start = performance.now();
7 | const payload: Record = {
8 | metric: "",
9 | path: c.req.path,
10 | // @ts-ignore
11 | continent: c.req.raw?.cf?.continent,
12 | // @ts-ignore - this is a bug in the types
13 | country: c.req.raw?.cf?.country,
14 | // @ts-ignore - this is a bug in the types
15 | colo: c.req.raw?.cf?.colo,
16 | // @ts-ignore - this is a bug in the types
17 | city: c.req.raw?.cf?.city,
18 | userAgent: c.req.header("user-agent"),
19 | };
20 |
21 | try {
22 | payload.requestId = c.get("requestId");
23 | const telemetry = {
24 | runtime: c.req.header("Techmely-Telemetry-Runtime"),
25 | platform: c.req.header("Techmely-Telemetry-Platform"),
26 | versions: c.req.header("Techmely-Telemetry-SDK")?.split(","),
27 | };
28 | if (telemetry.runtime || telemetry.platform || telemetry.versions) {
29 | c.executionCtx.waitUntil(
30 | analytics
31 | .ingestSdkTelemetry({
32 | runtime: telemetry.runtime || "unknown",
33 | platform: telemetry.platform || "unknown",
34 | versions: telemetry.versions || [],
35 | requestId: payload.requestId,
36 | time: Date.now(),
37 | })
38 | .catch((err) => {
39 | logger.error("Error ingesting SDK telemetry", {
40 | method: c.req.method,
41 | path: c.req.path,
42 | error: err.message,
43 | });
44 | }),
45 | );
46 | }
47 |
48 | await next();
49 | } catch (error) {
50 | payload.error = (error as Error).message;
51 | logger.error("request", {
52 | method: c.req.method,
53 | path: c.req.path,
54 | error: error,
55 | });
56 | throw error;
57 | } finally {
58 | payload.status = c.res.status;
59 | payload.serviceLatency = performance.now() - start;
60 | c.res.headers.append("Techmely-Latency", `service=${payload.serviceLatency}ms`);
61 | c.res.headers.append("Techmely-Version", c.env.VERSION);
62 | // metrics?.emit(payload);
63 | c.executionCtx.waitUntil(Promise.all([metrics?.flush(), logger.flush()]));
64 | }
65 | };
66 | }
67 |
--------------------------------------------------------------------------------
/packages/rate-limit/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @techmely/rate-limit
2 |
3 | ## 2.0.3
4 |
5 | ### Patch Changes
6 |
7 | - Upgrade deps
8 |
9 | - Updated dependencies []:
10 | - @techmely/logger@1.1.3
11 | - @techmely/metrics@1.0.10
12 |
13 | ## 2.0.2
14 |
15 | ### Patch Changes
16 |
17 | - Fix typing + packaging
18 |
19 | - Updated dependencies []:
20 | - @techmely/logger@1.1.2
21 | - @techmely/metrics@1.0.9
22 |
23 | ## 2.0.1
24 |
25 | ### Patch Changes
26 |
27 | - [`4fb8e01`](https://github.com/techmely/essential-packages/commit/4fb8e018133c2abaf622762e1b53667191b624d8) Thanks [@harrytran998](https://github.com/harrytran998)! - Fix replace build packages
28 |
29 | - Updated dependencies [[`4fb8e01`](https://github.com/techmely/essential-packages/commit/4fb8e018133c2abaf622762e1b53667191b624d8)]:
30 | - @techmely/logger@1.1.1
31 | - @techmely/metrics@1.0.8
32 |
33 | ## 2.0.0
34 |
35 | ### Patch Changes
36 |
37 | - Update the workflow we use to build this project
38 |
39 | - Updated dependencies [[`e496334`](https://github.com/techmely/essential-packages/commit/e49633413e30c9350a3c6bb27137bc1da6a7bd07)]:
40 | - @techmely/logger@1.1.0
41 | - @techmely/metrics@1.0.7
42 |
43 | ## 1.1.7
44 |
45 | ### Patch Changes
46 |
47 | - Upgrade deps
48 |
49 | - Updated dependencies []:
50 | - @techmely/metrics@1.0.5
51 | - @techmely/logger@1.0.6
52 |
53 | ## 1.1.6
54 |
55 | ### Patch Changes
56 |
57 | - Update all the things
58 |
59 | - Updated dependencies []:
60 | - @techmely/logger@1.0.5
61 | - @techmely/metrics@1.0.4
62 |
63 | ## 1.1.5
64 |
65 | ### Patch Changes
66 |
67 | - Bump version & upgrade deps
68 |
69 | - Updated dependencies []:
70 | - @techmely/logger@1.0.4
71 | - @techmely/metrics@1.0.3
72 |
73 | ## 1.1.4
74 |
75 | ### Patch Changes
76 |
77 | - upgrade deps
78 |
79 | - Updated dependencies []:
80 | - @techmely/logger@1.0.3
81 | - @techmely/metrics@1.0.2
82 |
83 | ## 1.1.3
84 |
85 | ### Patch Changes
86 |
87 | - Upgrade deps + upgrade ultils package
88 |
89 | ## 1.1.2
90 |
91 | ### Patch Changes
92 |
93 | - Fix peer deps using workspace
94 |
95 | ## 1.1.1
96 |
97 | ### Patch Changes
98 |
99 | - Update typing using port from techmely.types
100 |
101 | - Updated dependencies []:
102 | - @techmely/logger@1.0.2
103 | - @techmely/metrics@1.0.1
104 |
105 | ## 1.1.0
106 |
107 | ### Minor Changes
108 |
109 | - Add cloud flare ratelimiter
110 |
111 | ## 1.0.0
112 |
113 | ### Major Changes
114 |
115 | - Init rate limit interface + cloudflare ratelimit
116 |
--------------------------------------------------------------------------------
/packages/nuxt-pinia-persist/README.md:
--------------------------------------------------------------------------------
1 | # Nuxt partytown
2 |
3 | [![npm version][npm-version-src]][npm-version-href]
4 | [![npm downloads][npm-downloads-src]][npm-downloads-href]
5 | [![License][license-src]][license-href]
6 | [![Nuxt][nuxt-src]][nuxt-href]
7 |
8 | My new Nuxt module for doing amazing things.
9 |
10 | - [✨ Release Notes](/CHANGELOG.md)
11 |
12 |
13 |
14 | ## Features
15 |
16 | - Lightweight - Just only import partytown lib
17 | - Integrate with Partytown easy on Nuxt 3! Just need to focus on configs partytown
18 |
19 | ## Quick Setup
20 |
21 | 1. Add `@techmely/nuxt-pinia-persist` dependency to your project
22 |
23 | ```bash
24 | # Using pnpm
25 | pnpm add -D @techmely/nuxt-pinia-persist
26 |
27 | # Using yarn
28 | yarn add --dev @techmely/nuxt-pinia-persist
29 |
30 | # Using npm
31 | npm install --save-dev @techmely/nuxt-pinia-persist
32 | ```
33 |
34 | 2. Add `@techmely/nuxt-pinia-persist` to the `modules` section of `nuxt.config.ts`
35 |
36 | ```ts
37 | export default defineNuxtConfig({
38 | modules: [
39 | '@techmely/nuxt-pinia-persist'
40 | ]
41 | })
42 | ```
43 |
44 | 3. Enjoy
45 |
46 | ## Development
47 |
48 | ```bash
49 | # Install dependencies
50 | npm install
51 |
52 | # Generate type stubs
53 | npm run dev:prepare
54 |
55 | # Copy public/partytown from this folder --> Playground
56 | mkdir -p modules/nuxt-pinia-persist/playground/public/partytown
57 | cp -R modules/nuxt-pinia-persist/public/partytown modules/nuxt-pinia-persist/playground/public/partytown
58 |
59 | # Develop with the playground
60 | npm run dev
61 |
62 | # Build the playground
63 | npm run dev:build
64 |
65 | # Run ESLint
66 | npm run lint
67 |
68 | # Run Vitest
69 | npm run test
70 | npm run test:watch
71 |
72 | # Release new version
73 | npm run release
74 | ```
75 |
76 |
77 | [npm-version-src]: https://img.shields.io/npm/v/@techmely/nuxt-pinia-persist/latest.svg?style=flat&colorA=18181B&colorB=28CF8D
78 | [npm-version-href]: https://npmjs.com/package/@techmely/nuxt-pinia-persist
79 |
80 | [npm-downloads-src]: https://img.shields.io/npm/dm/@techmely/nuxt-pinia-persist.svg?style=flat&colorA=18181B&colorB=28CF8D
81 | [npm-downloads-href]: https://npmjs.com/package/@techmely/nuxt-pinia-persist
82 |
83 | [license-src]: https://img.shields.io/npm/l/@techmely/nuxt-pinia-persist.svg?style=flat&colorA=18181B&colorB=28CF8D
84 | [license-href]: https://npmjs.com/package/@techmely/nuxt-pinia-persist
85 |
86 | [nuxt-src]: https://img.shields.io/badge/Nuxt-18181B?logo=nuxt.js
87 | [nuxt-href]: https://nuxt.com
88 | ****
89 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@techmely/monorepo",
3 | "description": "Techmely Open Sources Communities Packages",
4 | "author": "Harry Tran ",
5 | "license": "MIT",
6 | "repository": {
7 | "type": "git",
8 | "url": "https://github.com/techmely/techmely.git"
9 | },
10 | "workspaces": [
11 | "packages/*"
12 | ],
13 | "scripts": {
14 | "postinstall": "husky",
15 | "build": "moon :build",
16 | "clean": "moon :clean",
17 | "test.unit": "vitest --passWithNoTests",
18 | "test.unit.run": "NODE_ENV=test vitest run --passWithNoTests --isolate",
19 | "test.unit.coverage": "vitest --coverage",
20 | "publish": "moon :publish",
21 | "lint.format": "bunx @biomejs/biome check --write packages",
22 | "lint.check": "bunx @biomejs/biome check packages",
23 | "lint.types": "moon :type-check",
24 | "lint.bundlesize": "bundlesize",
25 | "lint.duplicate": "jscpd",
26 | "lint.perf": "oxlint",
27 | "upgrade.deps": "npm-check-updates --dep dev,prod,peer -u -x react -x react-dom -x @types/react -x @types/react-dom",
28 | "moon": "moon --color"
29 | },
30 | "devDependencies": {
31 | "@biomejs/biome": "1.8.3",
32 | "@changesets/changelog-github": "0.5.0",
33 | "@changesets/cli": "2.27.7",
34 | "@cloudflare/workers-types": "4.20240620.0",
35 | "@playwright/test": "1.45.1",
36 | "@testing-library/jest-dom": "6.4.6",
37 | "@testing-library/react": "16.0.0",
38 | "@types/bun": "1.1.6",
39 | "@types/node": "20.14.9",
40 | "@types/react": "18.2.0",
41 | "@types/react-dom": "^18.2.0",
42 | "@vitest/coverage-v8": "1.6.0",
43 | "bun": "1.1.17",
44 | "bundlesize": "0.18.2",
45 | "cross-env": "7.0.3",
46 | "fast-glob": "^3.3.2",
47 | "fs-extra": "^11.2.0",
48 | "happy-dom": "14.12.3",
49 | "husky": "9.0.11",
50 | "jscpd": "4.0.5",
51 | "jsdom": "^24.1.0",
52 | "kolorist": "1.8.0",
53 | "npm-check-updates": "16.14.20",
54 | "oxlint": "0.5.2",
55 | "react": "^18.2.0",
56 | "react-dom": "^18.2.0",
57 | "rimraf": "5.0.7",
58 | "ts-node": "^10.9.2",
59 | "tslib": "^2.6.3",
60 | "tsup": "8.1.0",
61 | "typescript": "5.5.3",
62 | "vite": "^5.3.3",
63 | "vitest": "1.6.0",
64 | "zx": "8.1.3"
65 | },
66 | "engines": {
67 | "node": "~20"
68 | },
69 | "jscpd": {
70 | "threshold": 0.1,
71 | "pattern": "packages/**/src/**/*.ts",
72 | "reporters": [
73 | "html",
74 | "console"
75 | ],
76 | "ignore": [
77 | "**/__snapshots__/**",
78 | "packages/**/test/**/*.ts"
79 | ],
80 | "absolute": true,
81 | "gitignore": true
82 | },
83 | "funding": [
84 | {
85 | "type": "ko-fi",
86 | "url": "https://ko-fi.com/harrytran998"
87 | },
88 | {
89 | "type": "patreon",
90 | "url": "https://www.patreon.com/harrytran998"
91 | }
92 | ]
93 | }
94 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | // ===
3 | // Spacing
4 | // ===
5 | "editor.insertSpaces": true,
6 | "editor.tabSize": 2,
7 | "editor.trimAutoWhitespace": true,
8 | "editor.quickSuggestionsDelay": 0,
9 | // Integrate with windowns user
10 | "files.eol": "\n",
11 | "files.trimTrailingWhitespace": true,
12 | "files.insertFinalNewline": true,
13 | "files.trimFinalNewlines": true,
14 | // ===
15 | // Files
16 | // ===
17 | "files.exclude": {
18 | "**/*.log": true,
19 | "**/*.log*": true
20 | },
21 | "search.exclude": {
22 | "**/dist": true,
23 | "dist": true,
24 | "node_modules": true,
25 | "**/coverge": true,
26 | "**/node_modules": true,
27 | "libs": true,
28 | "**/.yarn": true,
29 | "**/.pnp.*": true
30 | },
31 | // ===
32 | // Event Triggers
33 | // ===
34 | "editor.formatOnSave": true,
35 | "editor.defaultFormatter": "biomejs.biome",
36 | "editor.codeActionsOnSave": {
37 | "source.organizeImports.biome": "explicit",
38 | "quickfix.biome": "always",
39 | "source.fixAll": "explicit",
40 | "source.addMissingImports": "explicit"
41 | },
42 | "[typescript]": {
43 | "editor.defaultFormatter": "biomejs.biome"
44 | },
45 | "npm.packageManager": "yarn",
46 | "cSpell.words": [
47 | "Apivt",
48 | "argb",
49 | "avif",
50 | "axiomhq",
51 | "bahmutov",
52 | "biomejs",
53 | "bumpp",
54 | "bundlesize",
55 | "bunx",
56 | "cacheable",
57 | "changesets",
58 | "codacy",
59 | "codeql",
60 | "combobox",
61 | "compat",
62 | "computeds",
63 | "decamelize",
64 | "decamelized",
65 | "eslintignore",
66 | "exceljs",
67 | "execa",
68 | "fsevents",
69 | "harrytran",
70 | "heic",
71 | "homemage",
72 | "Hono",
73 | "inmemory",
74 | "kebabize",
75 | "kolorist",
76 | "kungfutech",
77 | "lerp",
78 | "letiables",
79 | "listify",
80 | "lozad",
81 | "Mergeable",
82 | "Minh",
83 | "moonrepo",
84 | "msvideo",
85 | "npmjs",
86 | "npmrc",
87 | "nrwl",
88 | "nums",
89 | "nuxi",
90 | "nuxt",
91 | "oxlint",
92 | "Partytown",
93 | "patreon",
94 | "pinia",
95 | "pino",
96 | "plusplus",
97 | "pnpm",
98 | "pnpx",
99 | "posva",
100 | "Preact",
101 | "preactjs",
102 | "preinstall",
103 | "pressable",
104 | "progressbar",
105 | "quicktime",
106 | "sarif",
107 | "shipjs",
108 | "stylelint",
109 | "superjson",
110 | "tanstack",
111 | "techmely",
112 | "treeshake",
113 | "tseep",
114 | "tsup",
115 | "typedefs",
116 | "unfetch",
117 | "unocss",
118 | "valibot",
119 | "vike",
120 | "vite",
121 | "vitest"
122 | ],
123 | "yaml.schemas": {
124 | "https://moonrepo.dev/schemas/tasks.json": "file:///Users/techmely/Desktop/Code/Me/_self/essential-packages/.moon/tasks/typescript.yml"
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/packages/types/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @techmely/types
2 |
3 | ## 1.8.0
4 |
5 | ### Minor Changes
6 |
7 | - Add ts reset to types package
8 |
9 | ## 1.7.0
10 |
11 | ### Minor Changes
12 |
13 | - Add default expose ts-essential in types
14 |
15 | ## 1.6.10
16 |
17 | ### Patch Changes
18 |
19 | - Upgrade deps
20 |
21 | ## 1.6.9
22 |
23 | ### Patch Changes
24 |
25 | - Fix export files not correct types
26 |
27 | ## 1.6.8
28 |
29 | ### Patch Changes
30 |
31 | - Fix typing + packaging
32 |
33 | ## 1.6.7
34 |
35 | ### Patch Changes
36 |
37 | - [`4fb8e01`](https://github.com/techmely/essential-packages/commit/4fb8e018133c2abaf622762e1b53667191b624d8) Thanks [@harrytran998](https://github.com/harrytran998)! - Fix replace build packages
38 |
39 | ## 1.6.6
40 |
41 | ### Patch Changes
42 |
43 | - Update the workflow we use to build this project
44 |
45 | ## 1.6.5
46 |
47 | ### Patch Changes
48 |
49 | - Upgrade deps
50 |
51 | ## 1.6.4
52 |
53 | ### Patch Changes
54 |
55 | - Update all the things
56 |
57 | ## 1.6.3
58 |
59 | ### Patch Changes
60 |
61 | - Bump version & upgrade deps
62 |
63 | ## 1.6.2
64 |
65 | ### Patch Changes
66 |
67 | - upgrade deps
68 |
69 | ## 1.6.1
70 |
71 | ### Patch Changes
72 |
73 | - Add node env + rename some typing
74 |
75 | ## 1.6.0
76 |
77 | ### Minor Changes
78 |
79 | - Add essential port of techmely
80 |
81 | ## 1.5.3
82 |
83 | ### Patch Changes
84 |
85 | - Upgrade typings for project
86 |
87 | ## 1.5.2
88 |
89 | ### Patch Changes
90 |
91 | - Update typing name + add unique object array function
92 |
93 | ## 1.5.1
94 |
95 | ### Patch Changes
96 |
97 | - Chore: remove unnecessary engine in package
98 |
99 | ## 1.5.0
100 |
101 | ### Minor Changes
102 |
103 | - Add Tree Entity + Pick object function
104 |
105 | ## 1.4.2
106 |
107 | ### Patch Changes
108 |
109 | - Add entity-id type + number precision functions
110 |
111 | ## 1.4.1
112 |
113 | ### Patch Changes
114 |
115 | - Add some typings + remove unnessary files
116 |
117 | ### Patch Changes
118 |
119 | - [`d423a21`](undefined) - Update Dayjs utils + Bump latest dependencies
120 |
121 | ## 1.2.0
122 |
123 | ### Minor Changes
124 |
125 | - [`87aa3f4`](https://github.com/techmely/utils/commit/87aa3f4fcc8fc239db3b1343f47a6d9bf056de43) Thanks [@harrytran998](https://github.com/harrytran998)! - Upgrade deps + Restructure Project + Using Rome Tools + Add more utils func
126 |
127 | ### Patch Changes
128 |
129 | - [`0c9638e`](undefined) - Fix the way export Progress Bar
130 |
131 | - Updated dependencies [[`0c9638e`](undefined), [`87aa3f4`](https://github.com/techmely/utils/commit/87aa3f4fcc8fc239db3b1343f47a6d9bf056de43)]:
132 | - @techmely/build-configs@1.1.0
133 |
134 | ## 1.0.5
135 |
136 | ### Patch Changes
137 |
138 | - Add utility dayjs and upgrade deps
139 |
140 | - Updated dependencies []:
141 | - @techmely/build-configs@1.0.4
142 |
143 | ## 1.0.4
144 |
145 | ### Patch Changes
146 |
147 | - Release new version
148 |
149 | ## 1.0.3
150 |
151 | ### Patch Changes
152 |
153 | - Test changeset
154 |
155 | - Updated dependencies []:
156 | - @techmely/build-configs@1.0.3
157 |
--------------------------------------------------------------------------------
/.github/commit-convention.md:
--------------------------------------------------------------------------------
1 | ## Git Commit Message Convention
2 |
3 | > This is adapted from [Angular commit convention](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular).
4 |
5 | #### TL;DR
6 |
7 | Messages must be matched by the following regex:
8 |
9 | ``` js
10 | /^(revert: )?(feat|fix|docs|dx|style|refactor|perf|test|workflow|build|ci|chore|types|wip)(\(.+\))?: .{1,50}/
11 | ```
12 |
13 | #### Examples
14 |
15 | Appears under "Features" header, `compiler` subheader:
16 |
17 | ```
18 | feat(compiler): add 'comments' option
19 | ```
20 |
21 | Appears under "Bug Fixes" header, `v-model` subheader, with a link to issue #28:
22 |
23 | ```
24 | fix(v-model): handle events on blur
25 |
26 | close #28
27 | ```
28 |
29 | Appears under "Performance Improvements" header, and under "Breaking Changes" with the breaking change explanation:
30 |
31 | ```
32 | perf(core): improve vdom diffing by removing 'foo' option
33 |
34 | BREAKING CHANGE: The 'foo' option has been removed.
35 | ```
36 |
37 | The following commit and commit `667ecc1` do not appear in the changelog if they are under the same release. If not, the revert commit appears under the "Reverts" header.
38 |
39 | ```
40 | revert: feat(compiler): add 'comments' option
41 |
42 | This reverts commit 667ecc1654a317a13331b17617d973392f415f02.
43 | ```
44 |
45 | ### Full Message Format
46 |
47 | A commit message consists of a **header**, **body** and **footer**. The header has a **type**, **scope** and **subject**:
48 |
49 | ```
50 | ():
51 |
52 |
53 |
54 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | ---
38 |
39 |
40 | Opinionated collection of helpful Rust/Dart/TypeScript utils which we use on the real world applications
41 |
42 |
43 |
44 |
45 | ## Goals of project
46 |
47 | - Typescript support
48 | - Tree-shaking
49 | - Have no dependencies as possible
50 | - High performance(Design with modular in mind - minimal, readable and well thought out API)
51 | - Full tested with the code coverage 100%
52 | - Documentation
53 |
54 | ## Features
55 |
56 | ### Typescript Lib
57 |
58 | | Project | Features |
59 | | ---------------------------- | ------------------------------------------------------------------------------ |
60 | | @techmely/auth | 📦 # Techmely auth's utils solutions |
61 | | @techmely/utils | 📦 Tree-shakable ESM Unit Test coverage 80% |
62 | | @techmely/reset-css | The customize reset css with the minimal config work for every modern browsers |
63 | | @techmely/vike-react | React integration for Vike of Techmely Version |
64 | | @techmely/vike-react-query | ReactQuery integration for Vike of Techmely Version |
65 | | @techmely/cache | Cache layer for all runtime. Memory, Zone CF, redis cache |
66 | | @techmely/logger | Logger for all runtime: Console, Axiom, Pino |
67 | | @techmely/metrics | Metrics with axiom |
68 | | @techmely/rate-limit | Rate limit for all runtime: Console, Axiom |
69 | | @techmely/usage-limit | Usage limit for all runtime: Currently only Cloudflare |
70 | | @techmely/nuxt-partytown | Integrate with Partytown easy on Nuxt 3 |
71 | | @techmely/nuxt-pinia-persist | Integrate with Pinia easy on Nuxt 3 |
72 | | @techmely/vue-pinia-persist | Persist store like Redux, but for Vue |
73 |
74 |
75 | ## Install to use in your project
76 |
77 | You just need to change the `postfix` of project like `utils`, `reset-css`, `vue-partytown`...
78 |
79 |
80 | Example:
81 |
82 | ```bash
83 | npm i --save-dev @techmely/utils
84 | yarn add @techmely/utils
85 | pnpm add @techmely/utils
86 | bun add @techmely/utils
87 | ```
88 | ## How to contributes
89 |
90 | 1. Run `yarn` to install all dependencies
91 |
92 | 2. Install VsCode Extension: Rome
93 | 3. CRUD feature and do not forget to add unit test for them
94 | - Remember add typing for all functions
95 |
96 | 4. Create the pull request --> Please request me for review code
97 | 5. After Techmely Team approved PR --> Merge to main --> CI/CD automation release new version
98 |
99 | ## License
100 |
101 | MIT © [TechMeLy](https://github.com/sponsors/TechMeLy)
102 |
--------------------------------------------------------------------------------
/packages/auth/src/jwt/types.ts:
--------------------------------------------------------------------------------
1 | export interface UserFromDecodedIdToken {
2 | /**
3 | * The audience for which this token is intended.
4 | *
5 | * This value is a string equal to your Firebase project ID, the unique
6 | * identifier for your Firebase project, which can be found in [your project's
7 | * settings](https://console.firebase.google.com/project/_/settings/general/android:com.random.android).
8 | */
9 | aud: string;
10 |
11 | /**
12 | * Time, in seconds since the Unix epoch, when the end-user authentication
13 | * occurred.
14 | *
15 | * This value is not set when this particular ID token was created, but when the
16 | * user initially logged in to this session. In a single session, the Firebase
17 | * SDKs will refresh a user's ID tokens every hour. Each ID token will have a
18 | * different [`iat`](#iat) value, but the same `auth_time` value.
19 | */
20 | auth_time: number;
21 |
22 | /**
23 | * The email of the user to whom the ID token belongs, if available.
24 | */
25 | email?: string;
26 |
27 | /**
28 | * Whether or not the email of the user to whom the ID token belongs is
29 | * verified, provided the user has an email.
30 | */
31 | email_verified?: boolean;
32 |
33 | /**
34 | * The ID token's expiration time, in seconds since the Unix epoch. That is, the
35 | * time at which this ID token expires and should no longer be considered valid.
36 | *
37 | * The Firebase SDKs transparently refresh ID tokens every hour, issuing a new
38 | * ID token with up to a one hour expiration.
39 | */
40 | exp: number;
41 |
42 | /**
43 | * Information about the sign in event, including which sign in provider was
44 | * used and provider-specific identity details.
45 | *
46 | * This data is provided by the Firebase Authentication service and is a
47 | * reserved claim in the ID token.
48 | */
49 | firebase: {
50 | /**
51 | * Provider-specific identity details corresponding
52 | * to the provider used to sign in the user.
53 | */
54 | identities: {
55 | [key: string]: any;
56 | };
57 |
58 | /**
59 | * The ID of the provider used to sign in the user.
60 | * One of `"anonymous"`, `"password"`, `"facebook.com"`, `"github.com"`,
61 | * `"google.com"`, `"twitter.com"`, `"apple.com"`, `"microsoft.com"`,
62 | * `"yahoo.com"`, `"phone"`, `"playgames.google.com"`, `"gc.apple.com"`,
63 | * or `"custom"`.
64 | *
65 | * Additional Identity Platform provider IDs include `"linkedin.com"`,
66 | * OIDC and SAML identity providers prefixed with `"saml."` and `"oidc."`
67 | * respectively.
68 | */
69 | sign_in_provider: string;
70 |
71 | /**
72 | * The type identifier or `factorId` of the second factor, provided the
73 | * ID token was obtained from a multi-factor authenticated user.
74 | * For phone, this is `"phone"`.
75 | */
76 | sign_in_second_factor?: string;
77 |
78 | /**
79 | * The `uid` of the second factor used to sign in, provided the
80 | * ID token was obtained from a multi-factor authenticated user.
81 | */
82 | second_factor_identifier?: string;
83 |
84 | /**
85 | * The ID of the tenant the user belongs to, if available.
86 | */
87 | tenant?: string;
88 | [key: string]: any;
89 | };
90 |
91 | /**
92 | * The ID token's issued-at time, in seconds since the Unix epoch. That is, the
93 | * time at which this ID token was issued and should start to be considered
94 | * valid.
95 | *
96 | * The Firebase SDKs transparently refresh ID tokens every hour, issuing a new
97 | * ID token with a new issued-at time. If you want to get the time at which the
98 | * user session corresponding to the ID token initially occurred, see the
99 | * [`auth_time`](#auth_time) property.
100 | */
101 | iat: number;
102 |
103 | /**
104 | * The issuer identifier for the issuer of the response.
105 | *
106 | * This value is a URL with the format
107 | * `https://securetoken.google.com/`, where `` is the
108 | * same project ID specified in the [`aud`](#aud) property.
109 | */
110 | iss: string;
111 |
112 | /**
113 | * The phone number of the user to whom the ID token belongs, if available.
114 | */
115 | phone_number?: string;
116 |
117 | /**
118 | * The photo URL for the user to whom the ID token belongs, if available.
119 | */
120 | picture?: string;
121 |
122 | /**
123 | * The `uid` corresponding to the user who the ID token belonged to.
124 | *
125 | * As a convenience, this value is copied over to the [`uid`](#uid) property.
126 | */
127 | sub: string;
128 |
129 | /**
130 | * The `uid` corresponding to the user who the ID token belonged to.
131 | *
132 | * This value is not actually in the JWT token claims itself. It is added as a
133 | * convenience, and is set as the value of the [`sub`](#sub) property.
134 | */
135 | uid: string;
136 |
137 | /**
138 | * Other arbitrary claims included in the ID token.
139 | */
140 | [key: string]: any;
141 | }
142 |
--------------------------------------------------------------------------------
/packages/http/src/exceptions/exception.codes.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Adding a `code` string with a custom status code for every
3 | * exception is a good practice.
4 | *
5 | * Since when that exception is transferred to another process `instanceof`
6 | * check cannot be performed anymore so a `code` string is used instead.
7 | *
8 | * Code constants can be stored in a separate file so they
9 | * can be shared and reused on a receiving side (code sharing is
10 | * useful when developing fullstack apps or microservices)
11 | */
12 | export const CODE_ARGUMENT_INVALID = "GENERIC.ARGUMENT_INVALID";
13 | export const CODE_ARGUMENT_OUT_OF_RANGE = "GENERIC.ARGUMENT_OUT_OF_RANGE";
14 | export const CODE_ARGUMENT_NOT_PROVIDED = "GENERIC.ARGUMENT_NOT_PROVIDED";
15 | export const CODE_NOT_FOUND = "GENERIC.NOT_FOUND";
16 | export const CODE_CONFLICT = "GENERIC.CONFLICT";
17 | export const CODE_TIMEOUT = "GENERIC.TIMEOUT";
18 | export const CODE_INTERNAL_SERVER_ERROR = "GENERIC.INTERNAL_SERVER_ERROR";
19 |
20 | /**
21 | * 1xx_MESSAGE: Indicates an interim response for communicating connection status or request progress prior to completing the requested action and sending a final response.
22 | * 2xx_MESSAGE: Indicates that the client\'s request was successfully received, understood, and accepted.
23 | * 3xx_MESSAGE: Indicates that further action needs to be taken by the user agent in order to fulfill the request.
24 | * 4xx_MESSAGE: Indicates that the client seems to have erred.
25 | * 5xx_MESSAGE: Indicates that the server is aware that it has erred or is incapable of performing the requested method.
26 | */
27 | export const HTTP_CONTINUE = 100;
28 | export const HTTP_SWITCHING_PROTOCOLS = 101;
29 | export const HTTP_PROCESSING = 102; // RFC2518
30 | export const HTTP_EARLY_HINTS = 103; // RFC8297
31 | export const HTTP_OK = 200;
32 | export const HTTP_CREATED = 201;
33 | export const HTTP_ACCEPTED = 202;
34 | export const HTTP_NON_AUTHORITATIVE_INFORMATION = 203;
35 | export const HTTP_NO_CONTENT = 204;
36 | export const HTTP_RESET_CONTENT = 205;
37 | export const HTTP_PARTIAL_CONTENT = 206;
38 | export const HTTP_MULTI_STATUS = 207; // RFC4918
39 | export const HTTP_ALREADY_REPORTED = 208; // RFC5842
40 | export const HTTP_IM_USED = 226; // RFC3229
41 | export const HTTP_MULTIPLE_CHOICES = 300;
42 | export const HTTP_MOVED_PERMANENTLY = 301;
43 | export const HTTP_FOUND = 302;
44 | export const HTTP_SEE_OTHER = 303;
45 | export const HTTP_NOT_MODIFIED = 304;
46 | export const HTTP_USE_PROXY = 305;
47 | export const HTTP_RESERVED = 306;
48 | export const HTTP_TEMPORARY_REDIRECT = 307;
49 | export const HTTP_PERMANENTLY_REDIRECT = 308; // RFC7238
50 | export const HTTP_BAD_REQUEST = 400;
51 | export const HTTP_UNAUTHORIZED = 401;
52 | export const HTTP_PAYMENT_REQUIRED = 402;
53 | export const HTTP_FORBIDDEN = 403;
54 | export const HTTP_NOT_FOUND = 404;
55 | export const HTTP_METHOD_NOT_ALLOWED = 405;
56 | export const HTTP_NOT_ACCEPTABLE = 406;
57 | export const HTTP_PROXY_AUTHENTICATION_REQUIRED = 407;
58 | export const HTTP_REQUEST_TIMEOUT = 408;
59 | export const HTTP_CONFLICT = 409;
60 | export const HTTP_GONE = 410;
61 | export const HTTP_LENGTH_REQUIRED = 411;
62 | export const HTTP_PRECONDITION_FAILED = 412;
63 | export const HTTP_REQUEST_ENTITY_TOO_LARGE = 413;
64 | export const HTTP_REQUEST_URI_TOO_LONG = 414;
65 | export const HTTP_UNSUPPORTED_MEDIA_TYPE = 415;
66 | export const HTTP_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
67 | export const HTTP_EXPECTATION_FAILED = 417;
68 | export const HTTP_I_AM_A_TEAPOT = 418; // RFC2324
69 | export const HTTP_MISDIRECTED_REQUEST = 421; // RFC7540
70 | export const HTTP_UNPROCESSABLE_ENTITY = 422; // RFC4918
71 | export const HTTP_LOCKED = 423; // RFC4918
72 | export const HTTP_FAILED_DEPENDENCY = 424; // RFC4918
73 | export const HTTP_RESERVED_FOR_WEBDAV_ADVANCED_COLLECTIONS_EXPIRED_PROPOSAL = 425; // RFC2817
74 | export const HTTP_UPGRADE_REQUIRED = 426; // RFC2817
75 | export const HTTP_PRECONDITION_REQUIRED = 428; // RFC6585
76 | export const HTTP_TOO_MANY_REQUESTS = 429; // RFC6585
77 | export const HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE = 431; // RFC6585
78 | export const HTTP_UNAVAILABLE_FOR_LEGAL_REASONS = 451;
79 | export const HTTP_INTERNAL_SERVER_ERROR = 500;
80 | export const HTTP_NOT_IMPLEMENTED = 501;
81 | export const HTTP_BAD_GATEWAY = 502;
82 | export const HTTP_SERVICE_UNAVAILABLE = 503;
83 | export const HTTP_GATEWAY_TIMEOUT = 504;
84 | export const HTTP_VERSION_NOT_SUPPORTED = 505;
85 | export const HTTP_VARIANT_ALSO_NEGOTIATES_EXPERIMENTAL = 506; // RFC2295
86 | export const HTTP_INSUFFICIENT_STORAGE = 507; // RFC4918
87 | export const HTTP_LOOP_DETECTED = 508; // RFC5842
88 | export const HTTP_NOT_EXTENDED = 510; // RFC2774
89 | export const HTTP_NETWORK_AUTHENTICATION_REQUIRED = 511; // RFC6585
90 |
91 | // Cloudflare HTTP Status Codes
92 | export const HTTP_CF_UNKNOWN = 520;
93 | export const HTTP_CF_WEB_SERVER_IS_DOWN = 521;
94 | export const HTTP_CF_CONNECTION_TIMED_OUT = 522;
95 | export const HTTP_CF_ORIGIN_IS_UNREACHABLE = 523;
96 | export const HTTP_CF_A_TIMEOUT_OCCURRED = 524;
97 | export const HTTP_CF_SSL_HANDSHAKE_FAILED = 525;
98 | export const HTTP_CF_INVALID_SSL_CERTIFICATE = 526;
99 | export const HTTP_CF_RAILGUN_ERROR = 527;
100 |
--------------------------------------------------------------------------------
/packages/hono/src/secure-header/index.ts:
--------------------------------------------------------------------------------
1 | import { kebabize } from "@techmely/utils";
2 | import type { Context, MiddlewareHandler } from "hono";
3 |
4 | interface ContentSecurityPolicyOptions {
5 | defaultSrc?: string[];
6 | baseUri?: string[];
7 | childSrc?: string[];
8 | connectSrc?: string[];
9 | fontSrc?: string[];
10 | formAction?: string[];
11 | frameAncestors?: string[];
12 | frameSrc?: string[];
13 | imgSrc?: string[];
14 | manifestSrc?: string[];
15 | mediaSrc?: string[];
16 | objectSrc?: string[];
17 | reportTo?: string;
18 | sandbox?: string[];
19 | scriptSrc?: string[];
20 | scriptSrcAttr?: string[];
21 | scriptSrcElem?: string[];
22 | styleSrc?: string[];
23 | styleSrcAttr?: string[];
24 | styleSrcElem?: string[];
25 | upgradeInsecureRequests?: string[];
26 | workerSrc?: string[];
27 | }
28 |
29 | interface ReportToOptions {
30 | group: string;
31 | max_age: number;
32 | endpoints: ReportToEndpoint[];
33 | }
34 |
35 | interface ReportToEndpoint {
36 | url: string;
37 | }
38 |
39 | interface ReportingEndpointOptions {
40 | name: string;
41 | url: string;
42 | }
43 |
44 | type overridableHeader = boolean | string;
45 |
46 | interface SecureHeadersOptions {
47 | contentSecurityPolicy?: ContentSecurityPolicyOptions;
48 | crossOriginEmbedderPolicy?: overridableHeader;
49 | crossOriginResourcePolicy?: overridableHeader;
50 | crossOriginOpenerPolicy?: overridableHeader;
51 | originAgentCluster: overridableHeader;
52 | referrerPolicy?: overridableHeader;
53 | reportingEndpoints?: ReportingEndpointOptions[];
54 | reportTo?: ReportToOptions[];
55 | strictTransportSecurity?: overridableHeader;
56 | xContentTypeOptions?: overridableHeader;
57 | xDnsPrefetchControl?: overridableHeader;
58 | xDownloadOptions?: overridableHeader;
59 | xFrameOptions?: overridableHeader;
60 | xPermittedCrossDomainPolicies?: overridableHeader;
61 | xXssProtection?: overridableHeader;
62 | }
63 |
64 | type HeadersMap = {
65 | [key in keyof SecureHeadersOptions]: [string, string];
66 | };
67 |
68 | const HEADERS_MAP: HeadersMap = {
69 | crossOriginEmbedderPolicy: ["Cross-Origin-Embedder-Policy", "require-corp"],
70 | crossOriginResourcePolicy: ["Cross-Origin-Resource-Policy", "same-origin"],
71 | crossOriginOpenerPolicy: ["Cross-Origin-Opener-Policy", "same-origin"],
72 | originAgentCluster: ["Origin-Agent-Cluster", "?1"],
73 | referrerPolicy: ["Referrer-Policy", "no-referrer"],
74 | strictTransportSecurity: ["Strict-Transport-Security", "max-age=15552000; includeSubDomains"],
75 | xContentTypeOptions: ["X-Content-Type-Options", "nosniff"],
76 | xDnsPrefetchControl: ["X-DNS-Prefetch-Control", "off"],
77 | xDownloadOptions: ["X-Download-Options", "noopen"],
78 | xFrameOptions: ["X-Frame-Options", "SAMEORIGIN"],
79 | xPermittedCrossDomainPolicies: ["X-Permitted-Cross-Domain-Policies", "none"],
80 | xXssProtection: ["X-XSS-Protection", "0"],
81 | };
82 |
83 | const DEFAULT_OPTIONS: SecureHeadersOptions = {
84 | crossOriginEmbedderPolicy: false,
85 | crossOriginResourcePolicy: true,
86 | crossOriginOpenerPolicy: true,
87 | originAgentCluster: true,
88 | referrerPolicy: true,
89 | strictTransportSecurity: true,
90 | xContentTypeOptions: true,
91 | xDnsPrefetchControl: true,
92 | xDownloadOptions: true,
93 | xFrameOptions: true,
94 | xPermittedCrossDomainPolicies: true,
95 | xXssProtection: true,
96 | };
97 |
98 | export const secureHeadersMiddleware = (
99 | customOptions?: Partial,
100 | ): MiddlewareHandler => {
101 | const options = { ...DEFAULT_OPTIONS, ...customOptions };
102 | const headersToSet = getFilteredHeaders(options);
103 |
104 | if (options.contentSecurityPolicy) {
105 | headersToSet.push(["Content-Security-Policy", getCSPDirectives(options.contentSecurityPolicy)]);
106 | }
107 |
108 | if (options.reportingEndpoints) {
109 | headersToSet.push(["Reporting-Endpoints", getReportingEndpoints(options.reportingEndpoints)]);
110 | }
111 |
112 | if (options.reportTo) {
113 | headersToSet.push(["Report-To", getReportToOptions(options.reportTo)]);
114 | }
115 |
116 | return async function secureHeaders(ctx, next) {
117 | await next();
118 | setHeaders(ctx, headersToSet);
119 | ctx.res.headers.delete("X-Powered-By");
120 | };
121 | };
122 |
123 | function getFilteredHeaders(options: SecureHeadersOptions): [string, string][] {
124 | return Object.entries(HEADERS_MAP)
125 | .filter(([key]) => options[key as keyof SecureHeadersOptions])
126 | .map(([key, defaultValue]) => {
127 | const overrideValue = options[key as keyof SecureHeadersOptions];
128 | return typeof overrideValue === "string" ? [defaultValue[0], overrideValue] : defaultValue;
129 | });
130 | }
131 |
132 | function getCSPDirectives(
133 | contentSecurityPolicy: SecureHeadersOptions["contentSecurityPolicy"],
134 | ): string {
135 | return Object.entries(contentSecurityPolicy || [])
136 | .map(([directive, value]) => {
137 | return `${kebabize(directive)} ${Array.isArray(value) ? value.join(" ") : value}`;
138 | })
139 | .join("; ");
140 | }
141 |
142 | function getReportingEndpoints(
143 | reportingEndpoints: SecureHeadersOptions["reportingEndpoints"] = [],
144 | ): string {
145 | return reportingEndpoints.map((endpoint) => `${endpoint.name}="${endpoint.url}"`).join(", ");
146 | }
147 |
148 | function getReportToOptions(reportTo: SecureHeadersOptions["reportTo"] = []): string {
149 | return reportTo.map((option) => JSON.stringify(option)).join(", ");
150 | }
151 |
152 | function setHeaders(ctx: Context, headersToSet: [string, string][]) {
153 | for (const [header, value] of headersToSet) {
154 | ctx.res.headers.set(header, value);
155 | }
156 | }
157 |
--------------------------------------------------------------------------------