startx 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) hide show
  1. package/.editorconfig +20 -0
  2. package/.prettierignore +24 -0
  3. package/.prettierrc.js +52 -0
  4. package/.vscode/settings.json +3 -0
  5. package/LICENSE +21 -0
  6. package/apps/core-server/.env.example +24 -0
  7. package/apps/core-server/Dockerfile +61 -0
  8. package/apps/core-server/eslint.config.mjs +47 -0
  9. package/apps/core-server/package.json +73 -0
  10. package/apps/core-server/src/config/custom-type.ts +54 -0
  11. package/apps/core-server/src/events/index.ts +37 -0
  12. package/apps/core-server/src/index.ts +19 -0
  13. package/apps/core-server/src/middlewares/auth-middleware.ts +50 -0
  14. package/apps/core-server/src/middlewares/cors-middleware.ts +6 -0
  15. package/apps/core-server/src/middlewares/error-middleware.ts +23 -0
  16. package/apps/core-server/src/middlewares/logger-middleware.ts +21 -0
  17. package/apps/core-server/src/middlewares/notfound-middleware.ts +14 -0
  18. package/apps/core-server/src/middlewares/serve-static.ts +24 -0
  19. package/apps/core-server/src/routes/files/router.ts +7 -0
  20. package/apps/core-server/src/routes/server.ts +36 -0
  21. package/apps/core-server/tsconfig.json +10 -0
  22. package/apps/core-server/tsdown.config.ts +14 -0
  23. package/biome.json +62 -0
  24. package/configs/eslint-config/package.json +60 -0
  25. package/configs/eslint-config/plugins.d.ts +1 -0
  26. package/configs/eslint-config/src/configs/base.ts +237 -0
  27. package/configs/eslint-config/src/configs/frontend.ts +62 -0
  28. package/configs/eslint-config/src/configs/node.ts +10 -0
  29. package/configs/eslint-config/src/plugin.ts +25 -0
  30. package/configs/eslint-config/src/rules/index.ts +30 -0
  31. package/configs/eslint-config/src/rules/no-argument-spread.test.ts +47 -0
  32. package/configs/eslint-config/src/rules/no-argument-spread.ts +96 -0
  33. package/configs/eslint-config/src/rules/no-dynamic-import-template.ts +32 -0
  34. package/configs/eslint-config/src/rules/no-internal-package-import.ts +40 -0
  35. package/configs/eslint-config/src/rules/no-interpolation-in-regular-string.ts +32 -0
  36. package/configs/eslint-config/src/rules/no-json-parse-json-stringify.test.ts +34 -0
  37. package/configs/eslint-config/src/rules/no-json-parse-json-stringify.ts +49 -0
  38. package/configs/eslint-config/src/rules/no-plain-errors.ts +50 -0
  39. package/configs/eslint-config/src/rules/no-skipped-tests.ts +61 -0
  40. package/configs/eslint-config/src/rules/no-top-level-relative-imports-in-backend-module.ts +27 -0
  41. package/configs/eslint-config/src/rules/no-type-unsafe-event-emitter.ts +33 -0
  42. package/configs/eslint-config/src/rules/no-uncaught-json-parse.test.ts +21 -0
  43. package/configs/eslint-config/src/rules/no-uncaught-json-parse.ts +45 -0
  44. package/configs/eslint-config/src/rules/no-untyped-config-class-field.ts +26 -0
  45. package/configs/eslint-config/src/rules/no-unused-param-catch-clause.ts +33 -0
  46. package/configs/eslint-config/src/rules/no-useless-catch-throw.test.ts +34 -0
  47. package/configs/eslint-config/src/rules/no-useless-catch-throw.ts +47 -0
  48. package/configs/eslint-config/src/utils/json.ts +21 -0
  49. package/configs/eslint-config/tsconfig.json +8 -0
  50. package/configs/eslint-config/tsdown.config.ts +11 -0
  51. package/configs/eslint-config/vitest.config.ts +3 -0
  52. package/configs/tsdown-config/package.json +14 -0
  53. package/configs/tsdown-config/src/config/tsdown.base.ts +13 -0
  54. package/configs/typescript-config/package.json +10 -0
  55. package/configs/typescript-config/tsconfig.common.json +32 -0
  56. package/configs/typescript-config/tsconfig.frontend.json +14 -0
  57. package/configs/typescript-config/tsconfig.node.json +9 -0
  58. package/configs/vitest-config/package.json +25 -0
  59. package/configs/vitest-config/src/base.ts +34 -0
  60. package/configs/vitest-config/src/frontend.ts +15 -0
  61. package/configs/vitest-config/src/node.ts +5 -0
  62. package/configs/vitest-config/tsconfig.json +7 -0
  63. package/package.json +47 -0
  64. package/packages/@repo/constants/eslint.config.mjs +21 -0
  65. package/packages/@repo/constants/package.json +19 -0
  66. package/packages/@repo/constants/src/api.ts +1 -0
  67. package/packages/@repo/constants/src/index.ts +8 -0
  68. package/packages/@repo/constants/src/time.ts +23 -0
  69. package/packages/@repo/constants/tsconfig.json +7 -0
  70. package/packages/@repo/db/eslint.config.mjs +21 -0
  71. package/packages/@repo/db/package.json +30 -0
  72. package/packages/@repo/db/src/functions.ts +122 -0
  73. package/packages/@repo/db/src/index.ts +20 -0
  74. package/packages/@repo/db/src/schema/common.ts +49 -0
  75. package/packages/@repo/db/src/schema/index.ts +1 -0
  76. package/packages/@repo/db/tsconfig.json +13 -0
  77. package/packages/@repo/lib/eslint.config.mjs +49 -0
  78. package/packages/@repo/lib/package.json +57 -0
  79. package/packages/@repo/lib/src/bucket-module/file-storage.ts +49 -0
  80. package/packages/@repo/lib/src/bucket-module/s3-storage.ts +114 -0
  81. package/packages/@repo/lib/src/bucket-module/utils.ts +11 -0
  82. package/packages/@repo/lib/src/command-module.ts +77 -0
  83. package/packages/@repo/lib/src/constants.ts +3 -0
  84. package/packages/@repo/lib/src/cookie-module.ts +42 -0
  85. package/packages/@repo/lib/src/custom-type.ts +54 -0
  86. package/packages/@repo/lib/src/env.ts +13 -0
  87. package/packages/@repo/lib/src/error-handlers-module/index.ts +11 -0
  88. package/packages/@repo/lib/src/file-system/index.ts +90 -0
  89. package/packages/@repo/lib/src/hashing-module.ts +9 -0
  90. package/packages/@repo/lib/src/index.ts +27 -0
  91. package/packages/@repo/lib/src/logger-module/log-config.ts +16 -0
  92. package/packages/@repo/lib/src/logger-module/logger.ts +78 -0
  93. package/packages/@repo/lib/src/logger-module/memory-profiler.ts +65 -0
  94. package/packages/@repo/lib/src/mail-module/api.ts +0 -0
  95. package/packages/@repo/lib/src/mail-module/mock.ts +8 -0
  96. package/packages/@repo/lib/src/mail-module/nodemailer.ts +45 -0
  97. package/packages/@repo/lib/src/notification-module/index.ts +172 -0
  98. package/packages/@repo/lib/src/notification-module/push-notification.ts +90 -0
  99. package/packages/@repo/lib/src/oauth2-client.ts +109 -0
  100. package/packages/@repo/lib/src/otp-module.ts +98 -0
  101. package/packages/@repo/lib/src/pagination-module.ts +49 -0
  102. package/packages/@repo/lib/src/token-module.ts +35 -0
  103. package/packages/@repo/lib/src/user-session.ts +117 -0
  104. package/packages/@repo/lib/src/utils.ts +42 -0
  105. package/packages/@repo/lib/src/validation-module.ts +187 -0
  106. package/packages/@repo/lib/tsconfig.json +7 -0
  107. package/packages/@repo/mail/package.json +29 -0
  108. package/packages/@repo/mail/src/emails/admin/OtpEmail.tsx +168 -0
  109. package/packages/@repo/mail/src/index.ts +13 -0
  110. package/packages/@repo/mail/tsconfig.build.json +14 -0
  111. package/packages/@repo/mail/tsconfig.json +13 -0
  112. package/packages/@repo/mail/tsdown.config.ts +9 -0
  113. package/packages/@repo/redis/eslint.config.mjs +8 -0
  114. package/packages/@repo/redis/package.json +31 -0
  115. package/packages/@repo/redis/src/index.ts +2 -0
  116. package/packages/@repo/redis/src/lib/redis-client.ts +23 -0
  117. package/packages/@repo/redis/src/lib/redis-module.ts +3 -0
  118. package/packages/@repo/redis/tsconfig.json +12 -0
  119. package/packages/ui/components.json +17 -0
  120. package/packages/ui/eslint.config.mjs +18 -0
  121. package/packages/ui/package.json +67 -0
  122. package/packages/ui/postcss.config.mjs +9 -0
  123. package/packages/ui/src/components/custom/form-wrapper.tsx +551 -0
  124. package/packages/ui/src/components/custom/grid-component.tsx +23 -0
  125. package/packages/ui/src/components/custom/hover-tool.tsx +38 -0
  126. package/packages/ui/src/components/custom/image-picker.tsx +109 -0
  127. package/packages/ui/src/components/custom/no-content.tsx +37 -0
  128. package/packages/ui/src/components/custom/page-container.tsx +24 -0
  129. package/packages/ui/src/components/custom/page-section.tsx +59 -0
  130. package/packages/ui/src/components/custom/simple-popover.tsx +29 -0
  131. package/packages/ui/src/components/custom/switch-component.tsx +20 -0
  132. package/packages/ui/src/components/custom/theme-provider.tsx +74 -0
  133. package/packages/ui/src/components/custom/typography.tsx +111 -0
  134. package/packages/ui/src/components/extensions/carousel.tsx +392 -0
  135. package/packages/ui/src/components/hooks/event/use-click.tsx +39 -0
  136. package/packages/ui/src/components/hooks/time/useDebounce.tsx +21 -0
  137. package/packages/ui/src/components/hooks/time/useInterval.tsx +35 -0
  138. package/packages/ui/src/components/hooks/time/useTimeout.tsx +19 -0
  139. package/packages/ui/src/components/hooks/time/useTimer.tsx +51 -0
  140. package/packages/ui/src/components/hooks/use-media-query.tsx +19 -0
  141. package/packages/ui/src/components/hooks/use-persistent-storage.tsx +52 -0
  142. package/packages/ui/src/components/hooks/use-update-effect.tsx +13 -0
  143. package/packages/ui/src/components/hooks/use-window-dimension.tsx +30 -0
  144. package/packages/ui/src/components/lib/utils.ts +242 -0
  145. package/packages/ui/src/components/lucide.tsx +3 -0
  146. package/packages/ui/src/components/sonner.tsx +1 -0
  147. package/packages/ui/src/components/ui/alert-dialog.tsx +116 -0
  148. package/packages/ui/src/components/ui/avatar.tsx +53 -0
  149. package/packages/ui/src/components/ui/badge.tsx +46 -0
  150. package/packages/ui/src/components/ui/breadcrumb.tsx +109 -0
  151. package/packages/ui/src/components/ui/button.tsx +96 -0
  152. package/packages/ui/src/components/ui/card.tsx +92 -0
  153. package/packages/ui/src/components/ui/carousel.tsx +243 -0
  154. package/packages/ui/src/components/ui/checkbox.tsx +32 -0
  155. package/packages/ui/src/components/ui/command.tsx +155 -0
  156. package/packages/ui/src/components/ui/dialog.tsx +127 -0
  157. package/packages/ui/src/components/ui/dropdown-menu.tsx +226 -0
  158. package/packages/ui/src/components/ui/form.tsx +165 -0
  159. package/packages/ui/src/components/ui/input-otp.tsx +76 -0
  160. package/packages/ui/src/components/ui/input.tsx +21 -0
  161. package/packages/ui/src/components/ui/label.tsx +24 -0
  162. package/packages/ui/src/components/ui/multiple-select.tsx +510 -0
  163. package/packages/ui/src/components/ui/popover.tsx +42 -0
  164. package/packages/ui/src/components/ui/select.tsx +170 -0
  165. package/packages/ui/src/components/ui/separator.tsx +28 -0
  166. package/packages/ui/src/components/ui/sheet.tsx +130 -0
  167. package/packages/ui/src/components/ui/skeleton.tsx +13 -0
  168. package/packages/ui/src/components/ui/spinner.tsx +16 -0
  169. package/packages/ui/src/components/ui/switch.tsx +28 -0
  170. package/packages/ui/src/components/ui/table.tsx +116 -0
  171. package/packages/ui/src/components/ui/tabs.tsx +54 -0
  172. package/packages/ui/src/components/ui/textarea.tsx +18 -0
  173. package/packages/ui/src/components/ui/timeline.tsx +118 -0
  174. package/packages/ui/src/components/ui/tooltip.tsx +30 -0
  175. package/packages/ui/src/components/util/n-formattor.ts +22 -0
  176. package/packages/ui/src/components/util/storage.ts +37 -0
  177. package/packages/ui/src/globals.css +87 -0
  178. package/packages/ui/tailwind.config.ts +94 -0
  179. package/packages/ui/tsconfig.json +12 -0
  180. package/pnpm-workspace.yaml +43 -0
  181. package/turbo.json +77 -0
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "./tsconfig.common.json",
3
+ "compilerOptions": {
4
+ "module": "nodenext",
5
+ "moduleResolution": "nodenext",
6
+ "experimentalDecorators": true,
7
+ "types": ["node"]
8
+ }
9
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "vitest-config",
3
+ "version": "1.5.0",
4
+ "type": "module",
5
+ "peerDependencies": {
6
+ "vite": "catalog:",
7
+ "vitest": "catalog:"
8
+ },
9
+ "devDependencies": {
10
+ "typescript-config": "workspace:*",
11
+ "vite": "catalog:"
12
+ },
13
+ "exports": {
14
+ "./frontend": "./src/frontend.ts",
15
+ "./node": "./src/node.ts",
16
+ "./base": "./src/base.ts"
17
+ },
18
+ "scripts": {
19
+ "clean": "rimraf dist .turbo",
20
+ "deep:clean": "rimraf node_modules dist .turbo",
21
+ "typecheck": "tsc --noEmit",
22
+ "format": "biome format --write .",
23
+ "format:check": "biome ci ."
24
+ }
25
+ }
@@ -0,0 +1,34 @@
1
+ import { defineConfig } from "vitest/config";
2
+ import type { InlineConfig } from "vitest/node";
3
+
4
+ // Detect if we are running in "dev" mode (npm run test:dev)
5
+ const isDev = process.env.npm_lifecycle_event === "test:dev";
6
+ export const baseVitestConfig = (options: InlineConfig = {}) =>
7
+ defineConfig({
8
+ test: {
9
+ silent: true,
10
+ globals: true,
11
+
12
+ // Smart Include/Exclude Logic (Shared)
13
+ // Dev: Run TS source | Prod: Run compiled JS in dist
14
+ include: isDev
15
+ ? ["src/**/*.{test,spec}.{ts,tsx}"]
16
+ : ["dist/**/*.{test,spec}.{js,mjs,cjs}"],
17
+ exclude: isDev
18
+ ? ["**/node_modules/**", "**/dist/**"]
19
+ : ["**/node_modules/**", "**/src/**"],
20
+
21
+ // Standardized Coverage Logic
22
+ coverage:
23
+ process.env.COVERAGE_ENABLED === "true"
24
+ ? {
25
+ enabled: true,
26
+ provider: "v8",
27
+ ...options.coverage,
28
+ }
29
+ : { enabled: false },
30
+
31
+ // Merge whatever specific options are passed
32
+ ...options,
33
+ },
34
+ });
@@ -0,0 +1,15 @@
1
+ import { baseVitestConfig } from "./base.js";
2
+
3
+ export default baseVitestConfig({
4
+ environment: "jsdom",
5
+ setupFiles: ["./src/__tests__/setup.ts"],
6
+ css: {
7
+ modules: {
8
+ classNameStrategy: "non-scoped",
9
+ },
10
+ },
11
+
12
+ coverage: {
13
+ reporter: ["text-summary", "lcov", "html"],
14
+ }
15
+ });
@@ -0,0 +1,5 @@
1
+ import { baseVitestConfig } from "./base.js";
2
+
3
+ export default baseVitestConfig({
4
+ environment: "node",
5
+ });
@@ -0,0 +1,7 @@
1
+ {
2
+ "extends": "typescript-config/tsconfig.node.json",
3
+ "compilerOptions": {
4
+ "baseUrl": "./src"
5
+ },
6
+ "include": ["src/**/*.ts"]
7
+ }
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "startx",
3
+ "version": "0.0.1",
4
+ "repository": {
5
+ "type": "git",
6
+ "url": "git+https://github.com/avinashid/startx.git"
7
+ },
8
+ "scripts": {
9
+ "config:build": "turbo run config:build",
10
+ "build": "turbo run build",
11
+ "dev": "turbo run dev",
12
+ "lint": "turbo run lint",
13
+ "typecheck": "turbo run typecheck",
14
+ "clean": "turbo run clean",
15
+ "test": "turbo run test",
16
+ "test:dev": "turbo run test:dev",
17
+ "format": "turbo run format"
18
+ },
19
+ "devDependencies": {
20
+ "@biomejs/biome": "catalog:",
21
+ "@vitest/coverage-v8": "catalog:",
22
+ "prettier": "catalog:",
23
+ "rimraf": "catalog:",
24
+ "turbo": "catalog:",
25
+ "typescript": "catalog:",
26
+ "eslint": "catalog:",
27
+ "tsdown": "catalog:",
28
+ "unrun": "catalog:",
29
+ "vitest": "catalog:",
30
+ "@types/node": "catalog:"
31
+ },
32
+ "dependencies": {
33
+ "zod": "catalog:"
34
+ },
35
+ "engines": {
36
+ "node": ">=22"
37
+ },
38
+ "keywords": [
39
+ "startx",
40
+ "turborepo",
41
+ "scaffold",
42
+ "express"
43
+ ],
44
+ "author": "avinashid",
45
+ "license": "MIT",
46
+ "packageManager": "pnpm@10.28.2"
47
+ }
@@ -0,0 +1,21 @@
1
+ import { baseConfig } from "eslint-config/base";
2
+
3
+ /** @type {import('eslint').Linter.Config[]} */
4
+ export default [
5
+ // Spread the base config array
6
+ ...baseConfig,
7
+ {
8
+ rules: {
9
+ "unicorn/filename-case": ["error", { case: "kebabCase" }],
10
+ },
11
+ // Note: basePath belongs inside languageOptions or simply isn't standard in flat config
12
+ // unless defined by a specific plugin, so I omitted it here to avoid future errors.
13
+ },
14
+ // Add this block to disable the rule for config files
15
+ {
16
+ files: ["./src/**/*.ts"],
17
+ rules: {
18
+ "import-x/no-default-export": "off",
19
+ },
20
+ },
21
+ ];
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "@repo/constants",
3
+ "version": "0.12.0",
4
+ "scripts": {
5
+ "clean": "rimraf dist .turbo",
6
+ "watch:dev": "pnpm watch",
7
+ "typecheck": "tsc --noEmit",
8
+ "format": "biome format --write .",
9
+ "format:check": "biome ci .",
10
+ "lint": "eslint .",
11
+ "lint:fix": "eslint . --fix",
12
+ "watch": "tsc -p tsconfig.build.json --watch"
13
+ },
14
+ "exports": "./src/index.ts",
15
+ "devDependencies": {
16
+ "typescript-config": "workspace:*",
17
+ "eslint-config": "workspace:*"
18
+ }
19
+ }
@@ -0,0 +1 @@
1
+ export const BASE_API_URL = '';
@@ -0,0 +1,8 @@
1
+ export * from "./api";
2
+
3
+ export { Time } from "./time";
4
+
5
+ export const MIN_PASSWORD_CHAR_LENGTH = 8;
6
+
7
+ export const MAX_PASSWORD_CHAR_LENGTH = 64;
8
+ export const ping = "pong";
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Convert time from any time unit to any other unit
3
+ */
4
+ export const Time = {
5
+ milliseconds: {
6
+ toMinutes: 1 / (60 * 1000),
7
+ toSeconds: 1 / 1000,
8
+ },
9
+ seconds: {
10
+ toMilliseconds: 1000,
11
+ },
12
+ minutes: {
13
+ toMilliseconds: 60 * 1000,
14
+ },
15
+ hours: {
16
+ toMilliseconds: 60 * 60 * 1000,
17
+ toSeconds: 60 * 60,
18
+ },
19
+ days: {
20
+ toSeconds: 24 * 60 * 60,
21
+ toMilliseconds: 24 * 60 * 60 * 1000,
22
+ },
23
+ };
@@ -0,0 +1,7 @@
1
+ {
2
+ "extends": "typescript-config/tsconfig.common.json",
3
+ "compilerOptions": {
4
+ "baseUrl": "./src"
5
+ },
6
+ "include": ["src/**/*.ts"]
7
+ }
@@ -0,0 +1,21 @@
1
+ import { defineConfig } from "eslint/config";
2
+ import { baseConfig } from "eslint-config/base";
3
+
4
+ export default defineConfig(
5
+ baseConfig,
6
+ {
7
+ rules: {
8
+ "unicorn/filename-case": ["error", { case: "kebabCase" }],
9
+ "local-rules/no-unneeded-backticks": "off",
10
+ },
11
+ },
12
+ {
13
+ files: ["**/*.test.ts"],
14
+ rules: {
15
+ "@typescript-eslint/no-unused-expressions": "warn",
16
+ "@typescript-eslint/no-unsafe-assignment": "warn",
17
+ "@typescript-eslint/unbound-method": "warn",
18
+ "import-x/no-duplicates": "warn",
19
+ },
20
+ },
21
+ );
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@repo/db",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "scripts": {
6
+ "clean": "rimraf dist .turbo",
7
+ "watch:dev": "pnpm watch",
8
+ "typecheck": "tsc --noEmit",
9
+ "format": "biome format --write .",
10
+ "format:check": "biome ci .",
11
+ "lint": "eslint .",
12
+ "lint:fix": "eslint . --fix",
13
+ "watch": "tsc -p tsconfig.build.json --watch"
14
+ },
15
+ "exports": "./src/index.ts",
16
+ "devDependencies": {
17
+ "@types/eslint": "^8.56.5",
18
+ "@types/node": "^20.16.5",
19
+ "@types/pg": "^8.16.0",
20
+ "eslint": "catalog:",
21
+ "eslint-config": "workspace:*",
22
+ "tsc-alias": "^1.8.10",
23
+ "typescript": "5.5.4",
24
+ "typescript-config": "workspace:*"
25
+ },
26
+ "dependencies": {
27
+ "drizzle-orm": "^0.36.0",
28
+ "pg": "^8.16.3"
29
+ }
30
+ }
@@ -0,0 +1,122 @@
1
+ import { type AnyColumn, sql, type SQL } from "drizzle-orm";
2
+ import type { PgColumn } from "drizzle-orm/pg-core";
3
+
4
+ export const increment = (column: AnyColumn, value = 1) => {
5
+ return sql`${column} + ${value}`;
6
+ };
7
+
8
+ export const decrement = (column: AnyColumn, value = 1) => {
9
+ return sql`${column} - ${value}`;
10
+ };
11
+
12
+ export const push = (column: AnyColumn, value: unknown) => {
13
+ return sql`array_append(${column}, ${value})`;
14
+ };
15
+
16
+ export const concatArray = (column: AnyColumn, values: unknown[]) => {
17
+ const columnType = column.getSQLType().replace("[]", "");
18
+
19
+ return sql`
20
+ array_cat(
21
+ ${column},
22
+ ${sql`ARRAY[${sql.join(
23
+ values.map((value) => sql`CAST(${value} AS ${sql.raw(columnType)})`),
24
+ sql`', '`
25
+ )}]`}
26
+ )
27
+ `;
28
+ };
29
+
30
+ export const sqlConcat = (
31
+ columns: Array<AnyColumn | null | undefined>,
32
+ separator = " "
33
+ ) => {
34
+ const filteredColumns = columns
35
+ .filter((col): col is AnyColumn => col !== null)
36
+ .map((col) => sql`TRIM(COALESCE(${col}, ''))`);
37
+
38
+ if (filteredColumns.length === 0) {
39
+ const separator = sql<string>`' '`;
40
+ return separator
41
+ }
42
+
43
+ return sql<string>`
44
+ TRIM(
45
+ CONCAT_WS(
46
+ ${separator},
47
+ ${sql.join(filteredColumns, sql`', '`)}
48
+ )
49
+ )
50
+ `;
51
+ };
52
+
53
+ export const removeFromArray = (column: AnyColumn, values: unknown[]) => {
54
+ const columnType = column.getSQLType().replace("[]", "");
55
+
56
+ return values.reduce<AnyColumn | SQL>(
57
+ (acc, value) =>
58
+ sql`array_remove(${acc}, ${sql`CAST(${value} AS ${sql.raw(columnType)})`})`,
59
+ column
60
+ );
61
+ };
62
+
63
+ export function castToText(column: AnyColumn) {
64
+ return sql`CAST(${column} AS TEXT)`;
65
+ }
66
+
67
+ export function objectBuilder<T extends Record<string, PgColumn | SQL>>(
68
+ shape: T,
69
+ aggregate: true
70
+ ): SQL<Array<UnwrapColumns<T>>>;
71
+
72
+ export function objectBuilder<T extends Record<string, PgColumn | SQL>>(
73
+ shape: T,
74
+ aggregate?: false
75
+ ): SQL<UnwrapColumns<T>>;
76
+
77
+ export function objectBuilder<T extends Record<string, PgColumn | SQL>>(
78
+ shape: T,
79
+ aggregate = false
80
+ ): SQL<UnwrapColumns<T> | Array<UnwrapColumns<T>>> {
81
+ const chunks: SQL[] = [];
82
+ const filter: SQL[] = [];
83
+
84
+ for (const [key, value] of Object.entries(shape)) {
85
+ if (chunks.length > 0) {
86
+ chunks.push(sql.raw(","));
87
+ }
88
+
89
+ chunks.push(sql.raw(`'${key}',`));
90
+ chunks.push(sql`${value}`);
91
+ filter.push(sql`${value} IS NOT NULL`);
92
+ }
93
+
94
+ // const or = sql<string>`" OR "`;
95
+
96
+ const query = aggregate
97
+ ? sql`
98
+ COALESCE(
99
+ json_agg(
100
+ DISTINCT jsonb_build_object(${sql.join(chunks)})
101
+ ) FILTER (WHERE ${sql.join(filter, sql`' OR '`)}),
102
+ '[]'
103
+ )
104
+ `
105
+ : sql`jsonb_build_object(${sql.join(chunks)})`;
106
+
107
+ return query as SQL<UnwrapColumns<T>>;
108
+ }
109
+
110
+ type UnwrapColumns<T> = T extends Record<string, unknown>
111
+ ? {
112
+ [K in keyof T]: T[K] extends PgColumn<infer C>
113
+ ? C["notNull"] extends true
114
+ ? C["data"]
115
+ : C["data"] | null
116
+ : T[K] extends SQL<infer S>
117
+ ? S
118
+ : never;
119
+ }
120
+ : never;
121
+
122
+ export type Condition = boolean | SQL<unknown>;
@@ -0,0 +1,20 @@
1
+ import type { ExtractTablesWithRelations } from "drizzle-orm";
2
+ import { drizzle, type NodePgQueryResultHKT } from "drizzle-orm/node-postgres";
3
+ import { type PgTransaction } from "drizzle-orm/pg-core";
4
+ import Pg from "pg";
5
+
6
+ import * as schema from "./schema/index.js";
7
+
8
+ export const client = new Pg.Pool({
9
+ connectionString: process.env.DATABASE_URL,
10
+ });
11
+ const db = drizzle({ client, schema });
12
+ export type DrizzleTransaction = PgTransaction<
13
+ NodePgQueryResultHKT,
14
+ typeof schema,
15
+ ExtractTablesWithRelations<typeof schema>
16
+ >;
17
+ export type DrizzleDB = typeof db;
18
+ export {db};
19
+ export * from "./functions.js";
20
+ export * from "./schema/index.js";
@@ -0,0 +1,49 @@
1
+ import { pgTable, uuid, varchar, timestamp, pgEnum, text } from "drizzle-orm/pg-core";
2
+
3
+ // Helper function to generate current timestamp
4
+
5
+ export const userRoleEnum = pgEnum("user_role", ["user", "admin", "superuser"]);
6
+
7
+ export const usersTable = pgTable("users", {
8
+ id: uuid("id").primaryKey().defaultRandom(),
9
+ email: varchar("email", { length: 255 }).notNull().unique(),
10
+ fullName: varchar("full_name", { length: 255 }).default("Guest").notNull(),
11
+ password: varchar("password", { length: 255 }).default("foresight").notNull(),
12
+ countries: varchar("countries", { length: 255 }).array().notNull().default([]),
13
+ verifiedAt: timestamp("verified_at"),
14
+ pwdUpdatedAt: timestamp("pwd_updated_at"),
15
+ deletedAt: timestamp("deleted_at"),
16
+ createdAt: timestamp("created_at").defaultNow(),
17
+ lastLoginAt: timestamp("last_login_at").defaultNow(),
18
+ updatedAt: timestamp("updated_at")
19
+ .defaultNow()
20
+ .$onUpdate(() => new Date()),
21
+ });
22
+
23
+ export const filesTable = pgTable("files", {
24
+ id: uuid("id").primaryKey().defaultRandom(),
25
+ fileName: varchar("file_name", { length: 255 }).notNull(),
26
+ url: text("url").notNull(),
27
+ mimetype: text("mimetype").notNull(),
28
+ preview: text("preview"),
29
+ createdAt: timestamp("created_at").defaultNow(),
30
+ updatedAt: timestamp("updated_at")
31
+ .defaultNow()
32
+ .$onUpdate(() => new Date()),
33
+ });
34
+
35
+ export const otps = pgTable("otp", {
36
+ id: uuid("id").primaryKey().defaultRandom(),
37
+ email: varchar("email", { length: 255 }).notNull().unique(),
38
+ phone: varchar("phone", { length: 50 }),
39
+ status: varchar("status", { enum: ["pending", "verified"] })
40
+ .notNull()
41
+ .default("pending"),
42
+ otp: varchar("otp", { length: 255 }).notNull(),
43
+ createdAt: timestamp("created_at").defaultNow(),
44
+ updatedAt: timestamp("updated_at")
45
+ .defaultNow()
46
+ .$onUpdate(() => new Date()),
47
+ });
48
+
49
+ // ----------------------------------------------------------------------------
@@ -0,0 +1 @@
1
+ export * from "./common.js";
@@ -0,0 +1,13 @@
1
+ {
2
+ "extends": "typescript-config/tsconfig.common.json",
3
+ "compilerOptions": {
4
+ "rootDir": ".",
5
+ "types": ["node"],
6
+ "baseUrl": "src",
7
+ "tsBuildInfoFile": "dist/typecheck.tsbuildinfo",
8
+ "experimentalDecorators": true,
9
+ "emitDecoratorMetadata": true,
10
+ "allowSyntheticDefaultImports": true
11
+ },
12
+ "include": ["src/**/*.ts"]
13
+ }
@@ -0,0 +1,49 @@
1
+ import { defineConfig } from "eslint/config";
2
+ import { baseConfig } from "eslint-config/base";
3
+
4
+ export default defineConfig(
5
+ baseConfig,
6
+ {
7
+ rules: {
8
+ "unicorn/filename-case": ["error", { case: "kebabCase" }],
9
+ complexity: ["error", 23],
10
+
11
+ // TODO: remove these
12
+ "no-empty": "warn",
13
+ "id-denylist": "warn",
14
+ "no-fallthrough": "warn",
15
+ "no-useless-escape": "warn",
16
+ "import-x/order": "warn",
17
+ "no-extra-boolean-cast": "warn",
18
+ "no-case-declarations": "warn",
19
+ "no-prototype-builtins": "warn",
20
+ "@typescript-eslint/naming-convention": "warn",
21
+ "@typescript-eslint/no-base-to-string": "warn",
22
+ "@typescript-eslint/no-redundant-type-constituents": "warn",
23
+ "@typescript-eslint/prefer-nullish-coalescing": "warn",
24
+ "@typescript-eslint/prefer-optional-chain": "warn",
25
+ "@typescript-eslint/return-await": ["error", "always"],
26
+ "@typescript-eslint/no-empty-object-type": "warn",
27
+ "@typescript-eslint/no-unsafe-function-type": "warn",
28
+ "@typescript-eslint/no-duplicate-type-constituents": "warn",
29
+ "@typescript-eslint/no-unsafe-return": "warn",
30
+ "@typescript-eslint/no-unsafe-call": "warn",
31
+ "@typescript-eslint/no-unsafe-member-access": "warn",
32
+ "@typescript-eslint/no-unsafe-assignment": "warn",
33
+ "@typescript-eslint/no-explicit-any": "warn",
34
+ },
35
+ },
36
+ {
37
+ files: ["**/*.test.ts"],
38
+ rules: {
39
+ // TODO: remove these
40
+ "prefer-const": "warn",
41
+ "@typescript-eslint/no-unused-expressions": "warn",
42
+ "@typescript-eslint/no-explicit-any": "warn",
43
+ "@typescript-eslint/no-unsafe-member-access": "warn",
44
+ "@typescript-eslint/no-unsafe-assignment": "warn",
45
+ "@typescript-eslint/no-unsafe-return": "warn",
46
+ "@typescript-eslint/ban-ts-comment": ["warn", { "ts-ignore": true }],
47
+ },
48
+ },
49
+ );
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "@repo/lib",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "scripts": {
6
+ "clean": "rimraf dist .turbo",
7
+ "watch:dev": "pnpm watch",
8
+ "typecheck": "tsc --noEmit",
9
+ "format": "biome format --write .",
10
+ "format:check": "biome ci .",
11
+ "lint": "eslint .",
12
+ "lint:fix": "eslint . --fix",
13
+ "watch": "tsc -p tsconfig.build.json --watch"
14
+ },
15
+ "files": [
16
+ "dist/**/*"
17
+ ],
18
+ "exports": "./src/index.ts",
19
+ "devDependencies": {
20
+ "@repo/email": "workspace:*",
21
+ "@repo/redis": "workspace:*",
22
+ "@types/eslint": "^8.56.5",
23
+ "@types/express": "^5.0.0",
24
+ "@types/node": "^20.16.5",
25
+ "@types/jsonwebtoken": "^9.0.7",
26
+ "@types/nodemailer": "^6.4.16",
27
+ "eslint-config": "workspace:*",
28
+ "tsc-alias": "^1.8.10",
29
+ "typescript-config": "workspace:*",
30
+ "vitest": "catalog:",
31
+ "vitest-config": "workspace:*",
32
+ "vitest-mock-extended": "^3.1.0",
33
+ "vine": "link:@types/vinejs/vine"
34
+ },
35
+ "dependencies": {
36
+ "@aws-sdk/client-s3": "^3.693.0",
37
+ "@repo/db": "workspace:^",
38
+ "@types/express-fileupload": "^1.5.1",
39
+ "@vinejs/vine": "^4.3.0",
40
+ "@repo/email": "workspace:*",
41
+ "axios": "^1.7.7",
42
+ "bcryptjs": "^3.0.2",
43
+ "country-state-city": "^3.2.1",
44
+ "dayjs": "^1.11.13",
45
+ "dayjs-plugin-utc": "^0.1.2",
46
+ "dotenv": "^16.6.1",
47
+ "drizzle-orm": "^0.36.0",
48
+ "express": "^4.21.1",
49
+ "express-fileupload": "^1.5.1",
50
+ "firebase-admin": "^13.0.2",
51
+ "fs.promises.exists": "^1.1.4",
52
+ "jsonwebtoken": "^9.0.2",
53
+ "nodemailer": "^6.9.16",
54
+ "winston": "^3.16.0",
55
+ "winston-daily-rotate-file": "^5.0.0"
56
+ }
57
+ }
@@ -0,0 +1,49 @@
1
+ import type { UploadedFile } from "express-fileupload";
2
+ import fs from "fs";
3
+ import path from "path";
4
+
5
+ import { logger } from "../logger-module/logger";
6
+ import { Random } from "../utils";
7
+
8
+ export class FileBasedBucket {
9
+ static async upload(file: UploadedFile, subpath?: string) {
10
+ const fileName = Random.generateString(4, "hex") + path.extname(file.name);
11
+ const filePath = path.join(process.cwd(), "storage", subpath ?? "", fileName);
12
+ await FileBasedBucket.createDirectoryIfNotExists(`storage/${subpath ?? ""}`);
13
+ await file.mv(filePath);
14
+ const publicUrl = FileBasedBucket.getPublicUrl(`${subpath ?? ""}/${fileName}`);
15
+ return publicUrl;
16
+ }
17
+ static deleteFile(url: string) {
18
+ const filePath = FileBasedBucket.getPath(url);
19
+ try {
20
+ fs.unlinkSync(filePath);
21
+ } catch (error) {
22
+ logger.error("Error deleting file:", error);
23
+ }
24
+ }
25
+
26
+ static getPath(url: string) {
27
+ const path = url.replace(`${process.env.SERVER_URL}/files/`, `${process.cwd()}/storage/`);
28
+ return path;
29
+ }
30
+
31
+ static getPublicUrl(key: string) {
32
+ return path.join(process.env.SERVER_URL, "/files", key);
33
+ }
34
+
35
+ static async createDirectoryIfNotExists(path: string) {
36
+ return await new Promise<boolean>((resolve, reject) => {
37
+ if (fs.existsSync(path)) {
38
+ resolve(true);
39
+ } else {
40
+ fs.mkdir(path, { recursive: true }, (err) => {
41
+ if (err) {
42
+ reject(err);
43
+ }
44
+ resolve(true);
45
+ });
46
+ }
47
+ });
48
+ }
49
+ }