├── .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 | 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 | 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 | 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 |