create-blitzpack 0.1.0 → 0.1.2

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 (259) hide show
  1. package/dist/index.js +92 -94
  2. package/package.json +5 -6
  3. package/template/.dockerignore +0 -59
  4. package/template/.github/workflows/ci.yml +0 -157
  5. package/template/.husky/pre-commit +0 -1
  6. package/template/.husky/pre-push +0 -1
  7. package/template/.lintstagedrc.cjs +0 -4
  8. package/template/.nvmrc +0 -1
  9. package/template/.prettierrc +0 -9
  10. package/template/.vscode/settings.json +0 -13
  11. package/template/CLAUDE.md +0 -175
  12. package/template/CONTRIBUTING.md +0 -32
  13. package/template/Dockerfile +0 -90
  14. package/template/GETTING_STARTED.md +0 -35
  15. package/template/LICENSE +0 -21
  16. package/template/README.md +0 -116
  17. package/template/apps/api/.dockerignore +0 -51
  18. package/template/apps/api/.env.local.example +0 -62
  19. package/template/apps/api/emails/account-deleted-email.tsx +0 -69
  20. package/template/apps/api/emails/components/email-layout.tsx +0 -154
  21. package/template/apps/api/emails/config.ts +0 -22
  22. package/template/apps/api/emails/password-changed-email.tsx +0 -88
  23. package/template/apps/api/emails/password-reset-email.tsx +0 -86
  24. package/template/apps/api/emails/verification-email.tsx +0 -85
  25. package/template/apps/api/emails/welcome-email.tsx +0 -70
  26. package/template/apps/api/package.json +0 -84
  27. package/template/apps/api/prisma/migrations/20251012111439_init/migration.sql +0 -13
  28. package/template/apps/api/prisma/migrations/20251018162629_add_better_auth_fields/migration.sql +0 -67
  29. package/template/apps/api/prisma/migrations/20251019142208_add_user_role_enum/migration.sql +0 -5
  30. package/template/apps/api/prisma/migrations/20251019182151_user_auth/migration.sql +0 -7
  31. package/template/apps/api/prisma/migrations/20251019211416_faster_session_lookup/migration.sql +0 -2
  32. package/template/apps/api/prisma/migrations/20251119124337_add_upload_model/migration.sql +0 -26
  33. package/template/apps/api/prisma/migrations/20251120071241_add_scope_to_account/migration.sql +0 -2
  34. package/template/apps/api/prisma/migrations/20251120072608_add_oauth_token_expiration_fields/migration.sql +0 -10
  35. package/template/apps/api/prisma/migrations/20251120144705_add_audit_logs/migration.sql +0 -29
  36. package/template/apps/api/prisma/migrations/20251127123614_remove_impersonated_by/migration.sql +0 -8
  37. package/template/apps/api/prisma/migrations/20251127125630_remove_audit_logs/migration.sql +0 -11
  38. package/template/apps/api/prisma/migrations/migration_lock.toml +0 -3
  39. package/template/apps/api/prisma/schema.prisma +0 -116
  40. package/template/apps/api/prisma/seed.ts +0 -159
  41. package/template/apps/api/prisma.config.ts +0 -14
  42. package/template/apps/api/src/app.ts +0 -377
  43. package/template/apps/api/src/common/logger.service.ts +0 -227
  44. package/template/apps/api/src/config/env.ts +0 -60
  45. package/template/apps/api/src/config/rate-limit.ts +0 -29
  46. package/template/apps/api/src/hooks/auth.ts +0 -122
  47. package/template/apps/api/src/plugins/auth.ts +0 -198
  48. package/template/apps/api/src/plugins/database.ts +0 -45
  49. package/template/apps/api/src/plugins/logger.ts +0 -33
  50. package/template/apps/api/src/plugins/multipart.ts +0 -16
  51. package/template/apps/api/src/plugins/scalar.ts +0 -20
  52. package/template/apps/api/src/plugins/schedule.ts +0 -52
  53. package/template/apps/api/src/plugins/services.ts +0 -66
  54. package/template/apps/api/src/plugins/swagger.ts +0 -56
  55. package/template/apps/api/src/routes/accounts.ts +0 -91
  56. package/template/apps/api/src/routes/admin-sessions.ts +0 -92
  57. package/template/apps/api/src/routes/metrics.ts +0 -71
  58. package/template/apps/api/src/routes/password.ts +0 -46
  59. package/template/apps/api/src/routes/sessions.ts +0 -53
  60. package/template/apps/api/src/routes/stats.ts +0 -38
  61. package/template/apps/api/src/routes/uploads-serve.ts +0 -27
  62. package/template/apps/api/src/routes/uploads.ts +0 -154
  63. package/template/apps/api/src/routes/users.ts +0 -114
  64. package/template/apps/api/src/routes/verification.ts +0 -90
  65. package/template/apps/api/src/server.ts +0 -34
  66. package/template/apps/api/src/services/accounts.service.ts +0 -125
  67. package/template/apps/api/src/services/authorization.service.ts +0 -162
  68. package/template/apps/api/src/services/email.service.ts +0 -170
  69. package/template/apps/api/src/services/file-storage.service.ts +0 -267
  70. package/template/apps/api/src/services/metrics.service.ts +0 -175
  71. package/template/apps/api/src/services/password.service.ts +0 -56
  72. package/template/apps/api/src/services/sessions.service.spec.ts +0 -134
  73. package/template/apps/api/src/services/sessions.service.ts +0 -276
  74. package/template/apps/api/src/services/stats.service.ts +0 -273
  75. package/template/apps/api/src/services/uploads.service.ts +0 -163
  76. package/template/apps/api/src/services/users.service.spec.ts +0 -249
  77. package/template/apps/api/src/services/users.service.ts +0 -198
  78. package/template/apps/api/src/utils/file-validation.ts +0 -108
  79. package/template/apps/api/start.sh +0 -33
  80. package/template/apps/api/test/helpers/fastify-app.ts +0 -24
  81. package/template/apps/api/test/helpers/mock-authorization.ts +0 -16
  82. package/template/apps/api/test/helpers/mock-logger.ts +0 -28
  83. package/template/apps/api/test/helpers/mock-prisma.ts +0 -30
  84. package/template/apps/api/test/helpers/test-db.ts +0 -125
  85. package/template/apps/api/test/integration/auth-flow.integration.spec.ts +0 -449
  86. package/template/apps/api/test/integration/password.integration.spec.ts +0 -427
  87. package/template/apps/api/test/integration/rate-limit.integration.spec.ts +0 -51
  88. package/template/apps/api/test/integration/sessions.integration.spec.ts +0 -445
  89. package/template/apps/api/test/integration/users.integration.spec.ts +0 -211
  90. package/template/apps/api/test/setup.ts +0 -31
  91. package/template/apps/api/tsconfig.json +0 -26
  92. package/template/apps/api/vitest.config.ts +0 -35
  93. package/template/apps/web/.env.local.example +0 -11
  94. package/template/apps/web/components.json +0 -24
  95. package/template/apps/web/next.config.ts +0 -22
  96. package/template/apps/web/package.json +0 -56
  97. package/template/apps/web/postcss.config.js +0 -5
  98. package/template/apps/web/public/apple-icon.png +0 -0
  99. package/template/apps/web/public/icon.png +0 -0
  100. package/template/apps/web/public/robots.txt +0 -3
  101. package/template/apps/web/src/app/(admin)/admin/layout.tsx +0 -222
  102. package/template/apps/web/src/app/(admin)/admin/page.tsx +0 -157
  103. package/template/apps/web/src/app/(admin)/admin/sessions/page.tsx +0 -18
  104. package/template/apps/web/src/app/(admin)/admin/users/page.tsx +0 -20
  105. package/template/apps/web/src/app/(auth)/forgot-password/page.tsx +0 -177
  106. package/template/apps/web/src/app/(auth)/login/page.tsx +0 -159
  107. package/template/apps/web/src/app/(auth)/reset-password/page.tsx +0 -245
  108. package/template/apps/web/src/app/(auth)/signup/page.tsx +0 -153
  109. package/template/apps/web/src/app/dashboard/change-password/page.tsx +0 -255
  110. package/template/apps/web/src/app/dashboard/page.tsx +0 -296
  111. package/template/apps/web/src/app/error.tsx +0 -32
  112. package/template/apps/web/src/app/examples/file-upload/page.tsx +0 -200
  113. package/template/apps/web/src/app/favicon.ico +0 -0
  114. package/template/apps/web/src/app/global-error.tsx +0 -96
  115. package/template/apps/web/src/app/globals.css +0 -22
  116. package/template/apps/web/src/app/icon.png +0 -0
  117. package/template/apps/web/src/app/layout.tsx +0 -34
  118. package/template/apps/web/src/app/not-found.tsx +0 -28
  119. package/template/apps/web/src/app/page.tsx +0 -192
  120. package/template/apps/web/src/components/admin/activity-feed.tsx +0 -101
  121. package/template/apps/web/src/components/admin/charts/auth-breakdown-chart.tsx +0 -114
  122. package/template/apps/web/src/components/admin/charts/chart-tooltip.tsx +0 -124
  123. package/template/apps/web/src/components/admin/charts/realtime-metrics-chart.tsx +0 -511
  124. package/template/apps/web/src/components/admin/charts/role-distribution-chart.tsx +0 -102
  125. package/template/apps/web/src/components/admin/charts/session-activity-chart.tsx +0 -90
  126. package/template/apps/web/src/components/admin/charts/user-growth-chart.tsx +0 -108
  127. package/template/apps/web/src/components/admin/health-indicator.tsx +0 -175
  128. package/template/apps/web/src/components/admin/refresh-control.tsx +0 -90
  129. package/template/apps/web/src/components/admin/session-revoke-all-dialog.tsx +0 -79
  130. package/template/apps/web/src/components/admin/session-revoke-dialog.tsx +0 -74
  131. package/template/apps/web/src/components/admin/sessions-management-table.tsx +0 -372
  132. package/template/apps/web/src/components/admin/stat-card.tsx +0 -137
  133. package/template/apps/web/src/components/admin/user-create-dialog.tsx +0 -152
  134. package/template/apps/web/src/components/admin/user-delete-dialog.tsx +0 -73
  135. package/template/apps/web/src/components/admin/user-edit-dialog.tsx +0 -170
  136. package/template/apps/web/src/components/admin/users-management-table.tsx +0 -285
  137. package/template/apps/web/src/components/auth/email-verification-banner.tsx +0 -85
  138. package/template/apps/web/src/components/auth/github-button.tsx +0 -40
  139. package/template/apps/web/src/components/auth/google-button.tsx +0 -54
  140. package/template/apps/web/src/components/auth/protected-route.tsx +0 -66
  141. package/template/apps/web/src/components/auth/redirect-if-authenticated.tsx +0 -31
  142. package/template/apps/web/src/components/auth/with-auth.tsx +0 -30
  143. package/template/apps/web/src/components/error/error-card.tsx +0 -47
  144. package/template/apps/web/src/components/error/forbidden.tsx +0 -25
  145. package/template/apps/web/src/components/landing/command-block.tsx +0 -64
  146. package/template/apps/web/src/components/landing/feature-card.tsx +0 -60
  147. package/template/apps/web/src/components/landing/included-feature-card.tsx +0 -63
  148. package/template/apps/web/src/components/landing/logo.tsx +0 -41
  149. package/template/apps/web/src/components/landing/tech-badge.tsx +0 -11
  150. package/template/apps/web/src/components/layout/auth-nav.tsx +0 -58
  151. package/template/apps/web/src/components/layout/footer.tsx +0 -3
  152. package/template/apps/web/src/config/landing-data.ts +0 -152
  153. package/template/apps/web/src/config/site.ts +0 -5
  154. package/template/apps/web/src/hooks/api/__tests__/use-users.test.tsx +0 -181
  155. package/template/apps/web/src/hooks/api/use-admin-sessions.ts +0 -75
  156. package/template/apps/web/src/hooks/api/use-admin-stats.ts +0 -33
  157. package/template/apps/web/src/hooks/api/use-sessions.ts +0 -52
  158. package/template/apps/web/src/hooks/api/use-uploads.ts +0 -156
  159. package/template/apps/web/src/hooks/api/use-users.ts +0 -149
  160. package/template/apps/web/src/hooks/use-mobile.ts +0 -21
  161. package/template/apps/web/src/hooks/use-realtime-metrics.ts +0 -120
  162. package/template/apps/web/src/lib/__tests__/utils.test.ts +0 -29
  163. package/template/apps/web/src/lib/api.ts +0 -151
  164. package/template/apps/web/src/lib/auth.ts +0 -13
  165. package/template/apps/web/src/lib/env.ts +0 -52
  166. package/template/apps/web/src/lib/form-utils.ts +0 -11
  167. package/template/apps/web/src/lib/utils.ts +0 -1
  168. package/template/apps/web/src/providers.tsx +0 -34
  169. package/template/apps/web/src/store/atoms.ts +0 -15
  170. package/template/apps/web/src/test/helpers/test-utils.tsx +0 -44
  171. package/template/apps/web/src/test/setup.ts +0 -8
  172. package/template/apps/web/tailwind.config.ts +0 -5
  173. package/template/apps/web/tsconfig.json +0 -26
  174. package/template/apps/web/vitest.config.ts +0 -32
  175. package/template/assets/logo-512.png +0 -0
  176. package/template/assets/logo.svg +0 -4
  177. package/template/docker-compose.prod.yml +0 -66
  178. package/template/docker-compose.yml +0 -36
  179. package/template/eslint.config.ts +0 -119
  180. package/template/package.json +0 -77
  181. package/template/packages/tailwind-config/package.json +0 -9
  182. package/template/packages/tailwind-config/theme.css +0 -179
  183. package/template/packages/types/package.json +0 -29
  184. package/template/packages/types/src/__tests__/schemas.test.ts +0 -255
  185. package/template/packages/types/src/api-response.ts +0 -53
  186. package/template/packages/types/src/health-check.ts +0 -11
  187. package/template/packages/types/src/pagination.ts +0 -41
  188. package/template/packages/types/src/role.ts +0 -5
  189. package/template/packages/types/src/session.ts +0 -48
  190. package/template/packages/types/src/stats.ts +0 -113
  191. package/template/packages/types/src/upload.ts +0 -51
  192. package/template/packages/types/src/user.ts +0 -36
  193. package/template/packages/types/tsconfig.json +0 -5
  194. package/template/packages/types/vitest.config.ts +0 -21
  195. package/template/packages/ui/components.json +0 -21
  196. package/template/packages/ui/package.json +0 -108
  197. package/template/packages/ui/src/__tests__/button.test.tsx +0 -70
  198. package/template/packages/ui/src/alert-dialog.tsx +0 -141
  199. package/template/packages/ui/src/alert.tsx +0 -66
  200. package/template/packages/ui/src/animated-theme-toggler.tsx +0 -167
  201. package/template/packages/ui/src/avatar.tsx +0 -53
  202. package/template/packages/ui/src/badge.tsx +0 -36
  203. package/template/packages/ui/src/button.tsx +0 -84
  204. package/template/packages/ui/src/card.tsx +0 -92
  205. package/template/packages/ui/src/checkbox.tsx +0 -32
  206. package/template/packages/ui/src/data-table/data-table-column-header.tsx +0 -68
  207. package/template/packages/ui/src/data-table/data-table-pagination.tsx +0 -99
  208. package/template/packages/ui/src/data-table/data-table-toolbar.tsx +0 -55
  209. package/template/packages/ui/src/data-table/data-table-view-options.tsx +0 -63
  210. package/template/packages/ui/src/data-table/data-table.tsx +0 -167
  211. package/template/packages/ui/src/dialog.tsx +0 -143
  212. package/template/packages/ui/src/dropdown-menu.tsx +0 -257
  213. package/template/packages/ui/src/empty-state.tsx +0 -52
  214. package/template/packages/ui/src/file-upload-input.tsx +0 -202
  215. package/template/packages/ui/src/form.tsx +0 -168
  216. package/template/packages/ui/src/hooks/use-mobile.ts +0 -19
  217. package/template/packages/ui/src/icons/brand-icons.tsx +0 -16
  218. package/template/packages/ui/src/input.tsx +0 -21
  219. package/template/packages/ui/src/label.tsx +0 -24
  220. package/template/packages/ui/src/lib/utils.ts +0 -6
  221. package/template/packages/ui/src/password-input.tsx +0 -102
  222. package/template/packages/ui/src/popover.tsx +0 -48
  223. package/template/packages/ui/src/radio-group.tsx +0 -45
  224. package/template/packages/ui/src/scroll-area.tsx +0 -58
  225. package/template/packages/ui/src/select.tsx +0 -187
  226. package/template/packages/ui/src/separator.tsx +0 -28
  227. package/template/packages/ui/src/sheet.tsx +0 -139
  228. package/template/packages/ui/src/sidebar.tsx +0 -726
  229. package/template/packages/ui/src/skeleton-variants.tsx +0 -87
  230. package/template/packages/ui/src/skeleton.tsx +0 -13
  231. package/template/packages/ui/src/slider.tsx +0 -63
  232. package/template/packages/ui/src/sonner.tsx +0 -25
  233. package/template/packages/ui/src/spinner.tsx +0 -16
  234. package/template/packages/ui/src/switch.tsx +0 -31
  235. package/template/packages/ui/src/table.tsx +0 -116
  236. package/template/packages/ui/src/tabs.tsx +0 -66
  237. package/template/packages/ui/src/textarea.tsx +0 -18
  238. package/template/packages/ui/src/tooltip.tsx +0 -61
  239. package/template/packages/ui/src/user-avatar.tsx +0 -97
  240. package/template/packages/ui/test-config.js +0 -3
  241. package/template/packages/ui/tsconfig.json +0 -12
  242. package/template/packages/ui/turbo.json +0 -18
  243. package/template/packages/ui/vitest.config.ts +0 -17
  244. package/template/packages/ui/vitest.setup.ts +0 -1
  245. package/template/packages/utils/package.json +0 -23
  246. package/template/packages/utils/src/__tests__/utils.test.ts +0 -223
  247. package/template/packages/utils/src/array.ts +0 -18
  248. package/template/packages/utils/src/async.ts +0 -3
  249. package/template/packages/utils/src/date.ts +0 -77
  250. package/template/packages/utils/src/errors.ts +0 -73
  251. package/template/packages/utils/src/number.ts +0 -11
  252. package/template/packages/utils/src/string.ts +0 -13
  253. package/template/packages/utils/tsconfig.json +0 -5
  254. package/template/packages/utils/vitest.config.ts +0 -21
  255. package/template/pnpm-workspace.yaml +0 -4
  256. package/template/tsconfig.base.json +0 -32
  257. package/template/turbo.json +0 -133
  258. package/template/vitest.shared.ts +0 -26
  259. package/template/vitest.workspace.ts +0 -9
@@ -1,223 +0,0 @@
1
- import { groupBy, unique } from '@repo/packages-utils/array';
2
- import { sleep } from '@repo/packages-utils/async';
3
- import {
4
- formatDate,
5
- formatDateTime,
6
- getRelativeTime,
7
- } from '@repo/packages-utils/date';
8
- import { clamp, formatBytes } from '@repo/packages-utils/number';
9
- import { slugify, truncate } from '@repo/packages-utils/string';
10
- import { beforeEach, describe, expect, it, vi } from 'vitest';
11
-
12
- describe('slugify', () => {
13
- it('should convert text to slug format', () => {
14
- expect(slugify('Hello World')).toBe('hello-world');
15
- expect(slugify('TypeScript is Great!')).toBe('typescript-is-great');
16
- });
17
-
18
- it('should handle special characters', () => {
19
- expect(slugify('React & Vue.js')).toBe('react-vuejs');
20
- expect(slugify('C++ Programming')).toBe('c-programming');
21
- });
22
-
23
- it('should handle multiple spaces and trim', () => {
24
- expect(slugify(' too many spaces ')).toBe('too-many-spaces');
25
- });
26
-
27
- it('should handle underscores', () => {
28
- expect(slugify('snake_case_text')).toBe('snake-case-text');
29
- });
30
- });
31
-
32
- describe('truncate', () => {
33
- it('should truncate text longer than maxLength', () => {
34
- expect(truncate('This is a long text', 10)).toBe('This is...');
35
- });
36
-
37
- it('should not truncate text shorter than maxLength', () => {
38
- expect(truncate('Short', 10)).toBe('Short');
39
- });
40
-
41
- it('should return text as-is when exactly maxLength', () => {
42
- expect(truncate('Exactly10!', 10)).toBe('Exactly10!');
43
- });
44
- });
45
-
46
- describe('formatDate', () => {
47
- it('should format Date object to YYYY-MM-DD', () => {
48
- const date = new Date('2024-01-15T10:30:00Z');
49
- expect(formatDate(date)).toBe('2024-01-15');
50
- });
51
-
52
- it('should format ISO string to YYYY-MM-DD', () => {
53
- expect(formatDate('2024-01-15T10:30:00Z')).toBe('2024-01-15');
54
- });
55
- });
56
-
57
- describe('formatDateTime', () => {
58
- it('should format Date object to localized string', () => {
59
- const date = new Date('2024-01-15T10:30:00Z');
60
- const formatted = formatDateTime(date);
61
- expect(formatted).toContain('Jan');
62
- expect(formatted).toContain('15');
63
- expect(formatted).toContain('2024');
64
- });
65
-
66
- it('should format ISO string to localized string', () => {
67
- const formatted = formatDateTime('2024-01-15T10:30:00Z');
68
- expect(formatted).toContain('Jan');
69
- expect(formatted).toContain('15');
70
- expect(formatted).toContain('2024');
71
- });
72
- });
73
-
74
- describe('getRelativeTime', () => {
75
- beforeEach(() => {
76
- vi.useFakeTimers();
77
- });
78
-
79
- it('should return "just now" for recent dates', () => {
80
- const now = new Date();
81
- vi.setSystemTime(now);
82
- const recentDate = new Date(now.getTime() - 30000);
83
- expect(getRelativeTime(recentDate)).toBe('just now');
84
- });
85
-
86
- it('should return minutes ago', () => {
87
- const now = new Date();
88
- vi.setSystemTime(now);
89
- const pastDate = new Date(now.getTime() - 5 * 60 * 1000);
90
- expect(getRelativeTime(pastDate)).toBe('5 minutes ago');
91
- });
92
-
93
- it('should return hours ago', () => {
94
- const now = new Date();
95
- vi.setSystemTime(now);
96
- const pastDate = new Date(now.getTime() - 3 * 60 * 60 * 1000);
97
- expect(getRelativeTime(pastDate)).toBe('3 hours ago');
98
- });
99
-
100
- it('should return days ago', () => {
101
- const now = new Date();
102
- vi.setSystemTime(now);
103
- const pastDate = new Date(now.getTime() - 2 * 24 * 60 * 60 * 1000);
104
- expect(getRelativeTime(pastDate)).toBe('2 days ago');
105
- });
106
-
107
- it('should return formatted date for old dates', () => {
108
- const now = new Date();
109
- vi.setSystemTime(now);
110
- const oldDate = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
111
- const result = getRelativeTime(oldDate);
112
- expect(result).toMatch(/\d{4}-\d{2}-\d{2}/);
113
- });
114
- });
115
-
116
- describe('sleep', () => {
117
- it('should resolve after specified milliseconds', async () => {
118
- vi.useFakeTimers();
119
- const promise = sleep(1000);
120
- vi.advanceTimersByTime(1000);
121
- await expect(promise).resolves.toBeUndefined();
122
- vi.useRealTimers();
123
- });
124
- });
125
-
126
- describe('formatBytes', () => {
127
- it('should format zero bytes', () => {
128
- expect(formatBytes(0)).toBe('0 Bytes');
129
- });
130
-
131
- it('should format bytes', () => {
132
- expect(formatBytes(500)).toBe('500 Bytes');
133
- });
134
-
135
- it('should format kilobytes', () => {
136
- expect(formatBytes(1024)).toBe('1 KB');
137
- expect(formatBytes(1536)).toBe('1.5 KB');
138
- });
139
-
140
- it('should format megabytes', () => {
141
- expect(formatBytes(1048576)).toBe('1 MB');
142
- expect(formatBytes(5242880)).toBe('5 MB');
143
- });
144
-
145
- it('should format gigabytes', () => {
146
- expect(formatBytes(1073741824)).toBe('1 GB');
147
- });
148
-
149
- it('should respect decimals parameter', () => {
150
- expect(formatBytes(1536, 0)).toBe('2 KB');
151
- expect(formatBytes(1536, 3)).toBe('1.5 KB');
152
- });
153
- });
154
-
155
- describe('clamp', () => {
156
- it('should clamp value within range', () => {
157
- expect(clamp(5, 0, 10)).toBe(5);
158
- });
159
-
160
- it('should clamp to minimum', () => {
161
- expect(clamp(-5, 0, 10)).toBe(0);
162
- });
163
-
164
- it('should clamp to maximum', () => {
165
- expect(clamp(15, 0, 10)).toBe(10);
166
- });
167
-
168
- it('should handle negative ranges', () => {
169
- expect(clamp(-15, -10, -5)).toBe(-10);
170
- expect(clamp(-3, -10, -5)).toBe(-5);
171
- });
172
- });
173
-
174
- describe('unique', () => {
175
- it('should remove duplicates from array', () => {
176
- expect(unique([1, 2, 2, 3, 3, 3, 4])).toEqual([1, 2, 3, 4]);
177
- });
178
-
179
- it('should work with strings', () => {
180
- expect(unique(['a', 'b', 'a', 'c', 'b'])).toEqual(['a', 'b', 'c']);
181
- });
182
-
183
- it('should return empty array for empty input', () => {
184
- expect(unique([])).toEqual([]);
185
- });
186
-
187
- it('should preserve single item arrays', () => {
188
- expect(unique([1])).toEqual([1]);
189
- });
190
- });
191
-
192
- describe('groupBy', () => {
193
- it('should group objects by key function', () => {
194
- const items = [
195
- { name: 'Alice', age: 25 },
196
- { name: 'Bob', age: 30 },
197
- { name: 'Charlie', age: 25 },
198
- ];
199
-
200
- const grouped = groupBy(items, (item) => item.age);
201
- expect(grouped[25]).toHaveLength(2);
202
- expect(grouped[30]).toHaveLength(1);
203
- expect(grouped[25][0].name).toBe('Alice');
204
- expect(grouped[25][1].name).toBe('Charlie');
205
- });
206
-
207
- it('should work with string keys', () => {
208
- const items = [
209
- { type: 'fruit', name: 'apple' },
210
- { type: 'vegetable', name: 'carrot' },
211
- { type: 'fruit', name: 'banana' },
212
- ];
213
-
214
- const grouped = groupBy(items, (item) => item.type);
215
- expect(grouped.fruit).toHaveLength(2);
216
- expect(grouped.vegetable).toHaveLength(1);
217
- });
218
-
219
- it('should handle empty array', () => {
220
- const grouped = groupBy([], (item: { id: number }) => item.id);
221
- expect(grouped).toEqual({});
222
- });
223
- });
@@ -1,18 +0,0 @@
1
- export function unique<T>(array: T[]): T[] {
2
- return [...new Set(array)];
3
- }
4
-
5
- export function groupBy<T, K extends string | number | symbol>(
6
- array: T[],
7
- keyFn: (item: T) => K
8
- ): Record<K, T[]> {
9
- return array.reduce(
10
- (acc, item) => {
11
- const key = keyFn(item);
12
- if (!acc[key]) acc[key] = [];
13
- acc[key].push(item);
14
- return acc;
15
- },
16
- {} as Record<K, T[]>
17
- );
18
- }
@@ -1,3 +0,0 @@
1
- export function sleep(ms: number): Promise<void> {
2
- return new Promise((resolve) => setTimeout(resolve, ms));
3
- }
@@ -1,77 +0,0 @@
1
- export function formatDate(date: Date | string): string {
2
- const d = typeof date === 'string' ? new Date(date) : date;
3
- return d.toISOString().split('T')[0];
4
- }
5
-
6
- export function formatDateTime(date: Date | string): string {
7
- const d = typeof date === 'string' ? new Date(date) : date;
8
- return d.toLocaleString('en-US', {
9
- year: 'numeric',
10
- month: 'short',
11
- day: 'numeric',
12
- hour: '2-digit',
13
- minute: '2-digit',
14
- });
15
- }
16
-
17
- export function formatShortDate(
18
- date: Date | string,
19
- includeYear = false
20
- ): string {
21
- const d = typeof date === 'string' ? new Date(date) : date;
22
- return d.toLocaleString('en-US', {
23
- month: 'short',
24
- day: 'numeric',
25
- ...(includeYear && { year: 'numeric' }),
26
- });
27
- }
28
-
29
- export function getRelativeTime(
30
- date: Date | string,
31
- options: { short?: boolean; alwaysShowTime?: boolean } = {}
32
- ): string {
33
- const { short = false, alwaysShowTime = false } = options;
34
- const d = typeof date === 'string' ? new Date(date) : date;
35
- const now = new Date();
36
- const seconds = Math.floor((now.getTime() - d.getTime()) / 1000);
37
-
38
- if (seconds < 60) {
39
- return alwaysShowTime ? (short ? '1m ago' : '1 minute ago') : 'just now';
40
- }
41
-
42
- const minutes = Math.floor(seconds / 60);
43
- if (minutes < 60) {
44
- return short ? `${minutes}m ago` : `${minutes} minutes ago`;
45
- }
46
-
47
- const hours = Math.floor(seconds / 3600);
48
- if (hours < 24) {
49
- return short ? `${hours}h ago` : `${hours} hours ago`;
50
- }
51
-
52
- const days = Math.floor(seconds / 86400);
53
- if (days < 7) {
54
- return short ? `${days}d ago` : `${days} days ago`;
55
- }
56
-
57
- const weeks = Math.floor(days / 7);
58
- if (weeks < 4) {
59
- return short ? `${weeks}w ago` : `${weeks} weeks ago`;
60
- }
61
-
62
- return formatDate(d);
63
- }
64
-
65
- export function formatTime(date: Date | string | number): string {
66
- const d =
67
- typeof date === 'number'
68
- ? new Date(date)
69
- : typeof date === 'string'
70
- ? new Date(date)
71
- : date;
72
- return d.toLocaleTimeString(undefined, {
73
- hour: '2-digit',
74
- minute: '2-digit',
75
- second: '2-digit',
76
- });
77
- }
@@ -1,73 +0,0 @@
1
- export class AppError extends Error {
2
- readonly statusCode: number;
3
- readonly code: string;
4
- readonly details?: Record<string, unknown>;
5
-
6
- constructor(
7
- message: string,
8
- statusCode: number,
9
- code: string,
10
- details?: Record<string, unknown>
11
- ) {
12
- super(message);
13
- this.statusCode = statusCode;
14
- this.code = code;
15
- this.details = details;
16
- this.name = this.constructor.name;
17
- Error.captureStackTrace(this, this.constructor);
18
- }
19
- }
20
-
21
- export class ValidationError extends AppError {
22
- constructor(message: string, details?: Record<string, unknown>) {
23
- super(message, 400, 'VALIDATION_ERROR', details);
24
- }
25
- }
26
-
27
- export class UnauthorizedError extends AppError {
28
- constructor(
29
- message: string = 'Authentication required',
30
- details?: Record<string, unknown>
31
- ) {
32
- super(message, 401, 'UNAUTHORIZED', details);
33
- }
34
- }
35
-
36
- export class ForbiddenError extends AppError {
37
- constructor(
38
- message: string = 'Insufficient permissions',
39
- details?: Record<string, unknown>
40
- ) {
41
- super(message, 403, 'FORBIDDEN', details);
42
- }
43
- }
44
-
45
- export class NotFoundError extends AppError {
46
- constructor(message: string, details?: Record<string, unknown>) {
47
- super(message, 404, 'NOT_FOUND', details);
48
- }
49
- }
50
-
51
- export class ConflictError extends AppError {
52
- constructor(message: string, details?: Record<string, unknown>) {
53
- super(message, 409, 'CONFLICT', details);
54
- }
55
- }
56
-
57
- export class RateLimitError extends AppError {
58
- constructor(
59
- message: string = 'Rate limit exceeded',
60
- details?: Record<string, unknown>
61
- ) {
62
- super(message, 429, 'RATE_LIMIT_EXCEEDED', details);
63
- }
64
- }
65
-
66
- export class InternalServerError extends AppError {
67
- constructor(
68
- message: string = 'Internal server error',
69
- details?: Record<string, unknown>
70
- ) {
71
- super(message, 500, 'INTERNAL_ERROR', details);
72
- }
73
- }
@@ -1,11 +0,0 @@
1
- export function formatBytes(bytes: number, decimals = 2): string {
2
- if (bytes === 0) return '0 Bytes';
3
- const k = 1024;
4
- const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
5
- const i = Math.floor(Math.log(bytes) / Math.log(k));
6
- return `${parseFloat((bytes / Math.pow(k, i)).toFixed(decimals))} ${sizes[i]}`;
7
- }
8
-
9
- export function clamp(value: number, min: number, max: number): number {
10
- return Math.min(Math.max(value, min), max);
11
- }
@@ -1,13 +0,0 @@
1
- export function slugify(text: string): string {
2
- return text
3
- .toLowerCase()
4
- .trim()
5
- .replace(/[^\w\s-]/g, '')
6
- .replace(/[\s_-]+/g, '-')
7
- .replace(/^-+|-+$/g, '');
8
- }
9
-
10
- export function truncate(text: string, maxLength: number): string {
11
- if (text.length <= maxLength) return text;
12
- return text.slice(0, maxLength - 3) + '...';
13
- }
@@ -1,5 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.base.json",
3
- "include": ["src/**/*"],
4
- "exclude": ["node_modules"]
5
- }
@@ -1,21 +0,0 @@
1
- import { defineConfig, mergeConfig } from 'vitest/config';
2
-
3
- import { sharedConfig } from '../../vitest.shared';
4
-
5
- export default mergeConfig(
6
- sharedConfig,
7
- defineConfig({
8
- test: {
9
- name: '@repo/packages-utils',
10
- include: ['src/**/*.{test,spec}.{ts,tsx}'],
11
- coverage: {
12
- thresholds: {
13
- lines: 70,
14
- functions: 70,
15
- branches: 60,
16
- statements: 70,
17
- },
18
- },
19
- },
20
- })
21
- );
@@ -1,4 +0,0 @@
1
- packages:
2
- - 'apps/*'
3
- - 'packages/*'
4
- - 'create-blitzpack'
@@ -1,32 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "ESNext",
5
- "lib": ["ES2022"],
6
- "moduleResolution": "bundler",
7
- "resolveJsonModule": true,
8
- "allowJs": true,
9
- "strict": true,
10
- "esModuleInterop": true,
11
- "skipLibCheck": true,
12
- "forceConsistentCasingInFileNames": true,
13
- "declaration": true,
14
- "declarationMap": true,
15
- "sourceMap": true,
16
- "baseUrl": ".",
17
- "paths": {
18
- "@repo/packages-types": ["packages/types/src/index.ts"],
19
- "@repo/packages-types/*": ["packages/types/src/*"],
20
- "@repo/packages-utils": ["packages/utils/src/index.ts"],
21
- "@repo/packages-utils/*": ["packages/utils/src/*"]
22
- }
23
- },
24
- "exclude": [
25
- "node_modules",
26
- "dist",
27
- "build",
28
- ".next",
29
- "out",
30
- "**/node_modules"
31
- ]
32
- }
@@ -1,133 +0,0 @@
1
- {
2
- "$schema": "https://turbo.build/schema.json",
3
- "ui": "stream",
4
- "globalDependencies": ["tsconfig.json", "turbo.json", "eslint.config.ts"],
5
- "tasks": {
6
- "build": {
7
- "dependsOn": ["build:components"],
8
- "outputs": ["dist/**", ".next/**", "build/**"],
9
- "inputs": [
10
- "src/**",
11
- "prisma/**",
12
- "package.json",
13
- "tsconfig.json",
14
- "next.config.ts",
15
- "tailwind.config.ts"
16
- ]
17
- },
18
- "build:components": {
19
- "outputs": ["dist/**"],
20
- "inputs": ["src/**/*.tsx", "src/**/*.ts", "tsconfig.json"]
21
- },
22
- "typecheck": {
23
- "dependsOn": ["db:generate"],
24
- "cache": true,
25
- "inputs": ["src/**", "prisma/**", "tsconfig.json", "package.json"]
26
- },
27
- "dev": {
28
- "dependsOn": ["db:generate"],
29
- "cache": false,
30
- "persistent": true,
31
- "with": ["dev:components"]
32
- },
33
- "dev:components": {
34
- "cache": false,
35
- "persistent": true,
36
- "outputs": ["dist/**"]
37
- },
38
- "db:generate": {
39
- "cache": false
40
- },
41
- "lint": {
42
- "dependsOn": [],
43
- "inputs": ["src/**", "eslint.config.*", "package.json"],
44
- "outputs": []
45
- },
46
- "lint:fix": {
47
- "dependsOn": [],
48
- "cache": false,
49
- "inputs": ["src/**", "eslint.config.*", "package.json"]
50
- },
51
- "test": {
52
- "dependsOn": [],
53
- "inputs": [
54
- "src/**",
55
- "**/*.test.ts",
56
- "**/*.test.tsx",
57
- "vitest.config.ts",
58
- "package.json"
59
- ],
60
- "outputs": ["coverage/**"]
61
- },
62
- "test:unit": {
63
- "dependsOn": [],
64
- "inputs": [
65
- "src/**",
66
- "**/*.test.ts",
67
- "**/*.test.tsx",
68
- "vitest.config.ts",
69
- "package.json"
70
- ],
71
- "outputs": ["coverage/**"]
72
- },
73
- "test:integration": {
74
- "dependsOn": [],
75
- "inputs": [
76
- "src/**",
77
- "test/**",
78
- "**/*.integration.spec.ts",
79
- "vitest.config.ts",
80
- "package.json"
81
- ],
82
- "outputs": ["coverage/**"]
83
- },
84
- "test:watch": {
85
- "dependsOn": [],
86
- "cache": false,
87
- "persistent": true
88
- },
89
- "test:coverage": {
90
- "dependsOn": [],
91
- "outputs": ["coverage/**"],
92
- "inputs": [
93
- "src/**",
94
- "**/*.test.ts",
95
- "**/*.test.tsx",
96
- "vitest.config.ts",
97
- "package.json"
98
- ]
99
- },
100
- "clean": {
101
- "cache": false,
102
- "outputs": []
103
- },
104
- "format": {
105
- "cache": false,
106
- "outputs": []
107
- },
108
- "format:check": {
109
- "cache": true,
110
- "outputs": []
111
- },
112
- "db:migrate": {
113
- "cache": false,
114
- "outputs": []
115
- },
116
- "db:push": {
117
- "cache": false,
118
- "outputs": []
119
- },
120
- "db:studio": {
121
- "cache": false,
122
- "persistent": true
123
- },
124
- "db:seed": {
125
- "cache": false,
126
- "outputs": []
127
- },
128
- "email:dev": {
129
- "cache": false,
130
- "persistent": true
131
- }
132
- }
133
- }
@@ -1,26 +0,0 @@
1
- import { defineConfig } from 'vitest/config';
2
-
3
- export const sharedConfig = defineConfig({
4
- test: {
5
- globals: true,
6
- passWithNoTests: true,
7
- coverage: {
8
- provider: 'v8',
9
- reporter: ['text', 'json', 'html', 'lcov'],
10
- exclude: [
11
- 'node_modules/**',
12
- 'dist/**',
13
- 'build/**',
14
- '.next/**',
15
- '**/*.config.{js,ts,mjs,mts}',
16
- '**/*.d.ts',
17
- '**/index.ts',
18
- '**/__tests__/**',
19
- '**/test/**',
20
- '**/*.spec.{ts,tsx}',
21
- '**/*.test.{ts,tsx}',
22
- ],
23
- },
24
- setupFiles: [],
25
- },
26
- });
@@ -1,9 +0,0 @@
1
- import { defineWorkspace } from 'vitest/config';
2
-
3
- export default defineWorkspace([
4
- 'shared/types/vitest.config.ts',
5
- 'shared/utils/vitest.config.ts',
6
- 'shared/config/vitest.config.ts',
7
- 'apps/api/vitest.config.ts',
8
- 'apps/web/vitest.config.ts',
9
- ]);