create-ej-app 17.0.0 → 19.0.0

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 (116) hide show
  1. package/README.md +89 -13
  2. package/cli.js +9 -25
  3. package/cli.ts +10 -34
  4. package/package.json +2 -3
  5. package/template/api/.env.example +3 -0
  6. package/template/api/.github/copilot-instructions.md +129 -0
  7. package/template/api/.vscode/extensions.json +9 -0
  8. package/template/api/.vscode/settings.json +3 -0
  9. package/template/api/Dockerfile +23 -0
  10. package/template/api/package.json +31 -0
  11. package/template/api/prettier.config.mjs +11 -0
  12. package/template/api/src/data-access/todo.ts +142 -0
  13. package/template/api/src/db/fields.ts +46 -0
  14. package/template/api/src/db/index.ts +21 -0
  15. package/template/api/src/db/schema.ts +19 -0
  16. package/template/api/src/env.ts +24 -0
  17. package/template/api/src/index.ts +39 -0
  18. package/template/api/src/lib/util.ts +55 -0
  19. package/template/api/src/routes/index.ts +18 -0
  20. package/template/api/src/routes/todo/index.ts +241 -0
  21. package/template/api/src/scripts/seed.ts +25 -0
  22. package/template/api/tsconfig.json +33 -0
  23. package/template/nextjs/.vscode/extensions.json +11 -0
  24. package/template/nextjs/.vscode/settings.json +3 -0
  25. package/template/{base → nextjs}/components.json +4 -1
  26. package/template/nextjs/drizzle.config.ts +10 -0
  27. package/template/{base → nextjs}/package.json +25 -27
  28. package/template/{base → nextjs}/src/app/(admin)/layout.tsx +2 -2
  29. package/template/nextjs/src/app/(app)/app/page.tsx +3 -0
  30. package/template/{base → nextjs}/src/app/(app)/layout.tsx +1 -1
  31. package/template/{base → nextjs}/src/app/(auth)/login/email-login-form.tsx +33 -34
  32. package/template/{base → nextjs}/src/app/globals.css +127 -15
  33. package/template/{base → nextjs}/src/app/layout.tsx +8 -4
  34. package/template/{base → nextjs}/src/app/not-found.tsx +1 -1
  35. package/template/nextjs/src/components/theme-provider.tsx +67 -0
  36. package/template/nextjs/src/components/ui/button.tsx +67 -0
  37. package/template/{base → nextjs}/src/components/ui/card.tsx +17 -6
  38. package/template/nextjs/src/components/ui/field.tsx +238 -0
  39. package/template/nextjs/src/components/ui/input.tsx +19 -0
  40. package/template/{base → nextjs}/src/components/ui/label.tsx +1 -1
  41. package/template/{base → nextjs}/src/components/ui/separator.tsx +2 -2
  42. package/template/{base → nextjs}/src/components/ui/sonner.tsx +5 -0
  43. package/template/nextjs/src/db/common.ts +31 -0
  44. package/template/{base → nextjs}/src/db/schema.ts +2 -28
  45. package/template/nextjs/src/hooks/use-mobile.ts +19 -0
  46. package/template/{base → nextjs}/src/lib/auth.ts +2 -6
  47. package/template/nextjs/src/lib/format-currency.ts +27 -0
  48. package/template/nextjs/src/lib/format-date.ts +12 -0
  49. package/template/nextjs/src/scripts/seed.ts +11 -0
  50. package/template/{base → nextjs}/src/scripts/test-mail.ts +11 -5
  51. package/template/{base → nextjs}/tsconfig.json +2 -0
  52. package/template/base/src/app/api/[[...slugs]]/route.ts +0 -25
  53. package/template/base/src/components/ui/button.tsx +0 -60
  54. package/template/base/src/components/ui/form.tsx +0 -167
  55. package/template/base/src/components/ui/input.tsx +0 -21
  56. package/template/base/src/lib/eden.ts +0 -7
  57. /package/template/{base → api}/drizzle.config.ts +0 -0
  58. /package/template/{base → nextjs}/.dockerignore +0 -0
  59. /package/template/{base → nextjs}/.env.example +0 -0
  60. /package/template/{base → nextjs}/Dockerfile +0 -0
  61. /package/template/{base → nextjs}/eslint.config.mjs +0 -0
  62. /package/template/{base → nextjs}/next.config.ts +0 -0
  63. /package/template/{base → nextjs}/postcss.config.mjs +0 -0
  64. /package/template/{base → nextjs}/prettier.config.mjs +0 -0
  65. /package/template/{base → nextjs}/public/placeholder.svg +0 -0
  66. /package/template/{base → nextjs}/src/app/(admin)/admin/page.tsx +0 -0
  67. /package/template/{base → nextjs}/src/app/(auth)/login/page.tsx +0 -0
  68. /package/template/{base → nextjs}/src/app/(auth)/login/social-login-buttons.tsx +0 -0
  69. /package/template/{base → nextjs}/src/app/(marketing)/layout.tsx +0 -0
  70. /package/template/{base → nextjs}/src/app/(marketing)/page.tsx +0 -0
  71. /package/template/{base → nextjs}/src/app/(marketing)/privacy/page.tsx +0 -0
  72. /package/template/{base → nextjs}/src/app/(marketing)/terms/page.tsx +0 -0
  73. /package/template/{base → nextjs}/src/app/api/auth/[...all]/route.ts +0 -0
  74. /package/template/{base → nextjs}/src/app/favicon.ico +0 -0
  75. /package/template/{base → nextjs}/src/components/loader.tsx +0 -0
  76. /package/template/{base → nextjs}/src/components/react-query-provider.tsx +0 -0
  77. /package/template/{base → nextjs}/src/components/screen-size.tsx +0 -0
  78. /package/template/{base → nextjs}/src/components/site-footer.tsx +0 -0
  79. /package/template/{base → nextjs}/src/components/theme-toggle.tsx +0 -0
  80. /package/template/{base → nextjs}/src/db/index.ts +0 -0
  81. /package/template/{base → nextjs}/src/emails/components/email-layout.tsx +0 -0
  82. /package/template/{base → nextjs}/src/emails/magic-link.tsx +0 -0
  83. /package/template/{base → nextjs}/src/emails/test.tsx +0 -0
  84. /package/template/{base → nextjs}/src/emails/welcome.tsx +0 -0
  85. /package/template/{base → nextjs}/src/env.ts +0 -0
  86. /package/template/{base → nextjs}/src/instrumentation.ts +0 -0
  87. /package/template/{base → nextjs}/src/lib/auth-client.ts +0 -0
  88. /package/template/{base → nextjs}/src/lib/cn.ts +0 -0
  89. /package/template/{base → nextjs}/src/lib/delay.ts +0 -0
  90. /package/template/{base → nextjs}/src/lib/email-service.ts +0 -0
  91. /package/template/{base → nextjs}/src/lib/nodemailer.ts +0 -0
  92. /package/template/{base → nextjs}/src/lib/otel.ts +0 -0
  93. /package/template/{base → nextjs}/src/lib/safe-action-client.ts +0 -0
  94. /package/template/{base → nextjs}/src/lib/safe-action.ts +0 -0
  95. /package/template/{base → nextjs}/src/lib/siteConfig.ts +0 -0
  96. /package/template/{base → nextjs}/src/lib/toast-message.ts +0 -0
  97. /package/template/{base → nextjs}/terraform/artifact-registry.tf +0 -0
  98. /package/template/{base → nextjs}/terraform/backend-service.tf +0 -0
  99. /package/template/{base → nextjs}/terraform/cleanup.sh +0 -0
  100. /package/template/{base → nextjs}/terraform/cloudbuild.tf +0 -0
  101. /package/template/{base → nextjs}/terraform/cloudrun.tf +0 -0
  102. /package/template/{base → nextjs}/terraform/database.tf +0 -0
  103. /package/template/{base → nextjs}/terraform/forwarding-rule.tf +0 -0
  104. /package/template/{base → nextjs}/terraform/ip-address.tf +0 -0
  105. /package/template/{base → nextjs}/terraform/network-endpoint-group.tf +0 -0
  106. /package/template/{base → nextjs}/terraform/network.tf +0 -0
  107. /package/template/{base → nextjs}/terraform/pre.sh +0 -0
  108. /package/template/{base → nextjs}/terraform/providers.tf +0 -0
  109. /package/template/{base → nextjs}/terraform/secret.txt +0 -0
  110. /package/template/{base → nextjs}/terraform/setup.sh +0 -0
  111. /package/template/{base → nextjs}/terraform/ssl-certs.tf +0 -0
  112. /package/template/{base → nextjs}/terraform/target-https-proxy copy.tf +0 -0
  113. /package/template/{base → nextjs}/terraform/target-https-proxy.tf +0 -0
  114. /package/template/{base → nextjs}/terraform/terraform.tfvars +0 -0
  115. /package/template/{base → nextjs}/terraform/url-map.tf +0 -0
  116. /package/template/{base → nextjs}/terraform/variables.tf +0 -0
@@ -0,0 +1,238 @@
1
+ 'use client'
2
+
3
+ import { cva, type VariantProps } from 'class-variance-authority'
4
+ import { useMemo } from 'react'
5
+
6
+ import { Label } from '~/components/ui/label'
7
+ import { Separator } from '~/components/ui/separator'
8
+ import { cn } from '~/lib/cn'
9
+
10
+ function FieldSet({ className, ...props }: React.ComponentProps<'fieldset'>) {
11
+ return (
12
+ <fieldset
13
+ data-slot="field-set"
14
+ className={cn(
15
+ 'flex flex-col gap-6 has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3',
16
+ className
17
+ )}
18
+ {...props}
19
+ />
20
+ )
21
+ }
22
+
23
+ function FieldLegend({
24
+ className,
25
+ variant = 'legend',
26
+ ...props
27
+ }: React.ComponentProps<'legend'> & { variant?: 'legend' | 'label' }) {
28
+ return (
29
+ <legend
30
+ data-slot="field-legend"
31
+ data-variant={variant}
32
+ className={cn(
33
+ 'mb-3 font-medium data-[variant=label]:text-sm data-[variant=legend]:text-base',
34
+ className
35
+ )}
36
+ {...props}
37
+ />
38
+ )
39
+ }
40
+
41
+ function FieldGroup({ className, ...props }: React.ComponentProps<'div'>) {
42
+ return (
43
+ <div
44
+ data-slot="field-group"
45
+ className={cn(
46
+ 'group/field-group @container/field-group flex w-full flex-col gap-7 data-[slot=checkbox-group]:gap-3 *:data-[slot=field-group]:gap-4',
47
+ className
48
+ )}
49
+ {...props}
50
+ />
51
+ )
52
+ }
53
+
54
+ const fieldVariants = cva(
55
+ 'group/field flex w-full gap-3 data-[invalid=true]:text-destructive',
56
+ {
57
+ variants: {
58
+ orientation: {
59
+ vertical: 'flex-col *:w-full [&>.sr-only]:w-auto',
60
+ horizontal:
61
+ 'flex-row items-center has-[>[data-slot=field-content]]:items-start *:data-[slot=field-label]:flex-auto has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px',
62
+ responsive:
63
+ 'flex-col *:w-full @md/field-group:flex-row @md/field-group:items-center @md/field-group:*:w-auto @md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:*:data-[slot=field-label]:flex-auto [&>.sr-only]:w-auto @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px'
64
+ }
65
+ },
66
+ defaultVariants: {
67
+ orientation: 'vertical'
68
+ }
69
+ }
70
+ )
71
+
72
+ function Field({
73
+ className,
74
+ orientation = 'vertical',
75
+ ...props
76
+ }: React.ComponentProps<'div'> & VariantProps<typeof fieldVariants>) {
77
+ return (
78
+ <div
79
+ role="group"
80
+ data-slot="field"
81
+ data-orientation={orientation}
82
+ className={cn(fieldVariants({ orientation }), className)}
83
+ {...props}
84
+ />
85
+ )
86
+ }
87
+
88
+ function FieldContent({ className, ...props }: React.ComponentProps<'div'>) {
89
+ return (
90
+ <div
91
+ data-slot="field-content"
92
+ className={cn(
93
+ 'group/field-content flex flex-1 flex-col gap-1 leading-snug',
94
+ className
95
+ )}
96
+ {...props}
97
+ />
98
+ )
99
+ }
100
+
101
+ function FieldLabel({
102
+ className,
103
+ ...props
104
+ }: React.ComponentProps<typeof Label>) {
105
+ return (
106
+ <Label
107
+ data-slot="field-label"
108
+ className={cn(
109
+ 'group/field-label peer/field-label has-data-checked:border-primary/30 has-data-checked:bg-primary/5 dark:has-data-checked:border-primary/20 dark:has-data-checked:bg-primary/10 flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50 has-[>[data-slot=field]]:rounded-md has-[>[data-slot=field]]:border *:data-[slot=field]:p-3',
110
+ 'has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col',
111
+ className
112
+ )}
113
+ {...props}
114
+ />
115
+ )
116
+ }
117
+
118
+ function FieldTitle({ className, ...props }: React.ComponentProps<'div'>) {
119
+ return (
120
+ <div
121
+ data-slot="field-label"
122
+ className={cn(
123
+ 'flex w-fit items-center gap-2 text-sm leading-snug font-medium group-data-[disabled=true]/field:opacity-50',
124
+ className
125
+ )}
126
+ {...props}
127
+ />
128
+ )
129
+ }
130
+
131
+ function FieldDescription({ className, ...props }: React.ComponentProps<'p'>) {
132
+ return (
133
+ <p
134
+ data-slot="field-description"
135
+ className={cn(
136
+ 'text-muted-foreground text-left text-sm leading-normal font-normal group-has-data-horizontal/field:text-balance [[data-variant=legend]+&]:-mt-1.5',
137
+ 'last:mt-0 nth-last-2:-mt-1',
138
+ '[&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4',
139
+ className
140
+ )}
141
+ {...props}
142
+ />
143
+ )
144
+ }
145
+
146
+ function FieldSeparator({
147
+ children,
148
+ className,
149
+ ...props
150
+ }: React.ComponentProps<'div'> & {
151
+ children?: React.ReactNode
152
+ }) {
153
+ return (
154
+ <div
155
+ data-slot="field-separator"
156
+ data-content={!!children}
157
+ className={cn(
158
+ 'relative -my-2 h-5 text-sm group-data-[variant=outline]/field-group:-mb-2',
159
+ className
160
+ )}
161
+ {...props}
162
+ >
163
+ <Separator className="absolute inset-0 top-1/2" />
164
+ {children && (
165
+ <span
166
+ className="bg-background text-muted-foreground relative mx-auto block w-fit px-2"
167
+ data-slot="field-separator-content"
168
+ >
169
+ {children}
170
+ </span>
171
+ )}
172
+ </div>
173
+ )
174
+ }
175
+
176
+ function FieldError({
177
+ className,
178
+ children,
179
+ errors,
180
+ ...props
181
+ }: React.ComponentProps<'div'> & {
182
+ errors?: Array<{ message?: string } | undefined>
183
+ }) {
184
+ const content = useMemo(() => {
185
+ if (children) {
186
+ return children
187
+ }
188
+
189
+ if (!errors?.length) {
190
+ return null
191
+ }
192
+
193
+ const uniqueErrors = [
194
+ ...new Map(errors.map(error => [error?.message, error])).values()
195
+ ]
196
+
197
+ if (uniqueErrors?.length == 1) {
198
+ return uniqueErrors[0]?.message
199
+ }
200
+
201
+ return (
202
+ <ul className="ml-4 flex list-disc flex-col gap-1">
203
+ {uniqueErrors.map(
204
+ (error, index) =>
205
+ error?.message && <li key={index}>{error.message}</li>
206
+ )}
207
+ </ul>
208
+ )
209
+ }, [children, errors])
210
+
211
+ if (!content) {
212
+ return null
213
+ }
214
+
215
+ return (
216
+ <div
217
+ role="alert"
218
+ data-slot="field-error"
219
+ className={cn('text-destructive text-sm font-normal', className)}
220
+ {...props}
221
+ >
222
+ {content}
223
+ </div>
224
+ )
225
+ }
226
+
227
+ export {
228
+ Field,
229
+ FieldContent,
230
+ FieldDescription,
231
+ FieldError,
232
+ FieldGroup,
233
+ FieldLabel,
234
+ FieldLegend,
235
+ FieldSeparator,
236
+ FieldSet,
237
+ FieldTitle
238
+ }
@@ -0,0 +1,19 @@
1
+ import * as React from 'react'
2
+
3
+ import { cn } from '~/lib/cn'
4
+
5
+ function Input({ className, type, ...props }: React.ComponentProps<'input'>) {
6
+ return (
7
+ <input
8
+ type={type}
9
+ data-slot="input"
10
+ className={cn(
11
+ 'border-input file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 h-9 w-full min-w-0 rounded-md border bg-transparent px-2.5 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:ring-3 disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:ring-3 md:text-sm',
12
+ className
13
+ )}
14
+ {...props}
15
+ />
16
+ )
17
+ }
18
+
19
+ export { Input }
@@ -1,6 +1,6 @@
1
1
  'use client'
2
2
 
3
- import * as LabelPrimitive from '@radix-ui/react-label'
3
+ import { Label as LabelPrimitive } from 'radix-ui'
4
4
  import * as React from 'react'
5
5
 
6
6
  import { cn } from '~/lib/cn'
@@ -1,6 +1,6 @@
1
1
  'use client'
2
2
 
3
- import * as SeparatorPrimitive from '@radix-ui/react-separator'
3
+ import { Separator as SeparatorPrimitive } from 'radix-ui'
4
4
  import * as React from 'react'
5
5
 
6
6
  import { cn } from '~/lib/cn'
@@ -17,7 +17,7 @@ function Separator({
17
17
  decorative={decorative}
18
18
  orientation={orientation}
19
19
  className={cn(
20
- 'bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px',
20
+ 'bg-border shrink-0 data-horizontal:h-px data-horizontal:w-full data-vertical:w-px data-vertical:self-stretch',
21
21
  className
22
22
  )}
23
23
  {...props}
@@ -32,6 +32,11 @@ const Toaster = ({ ...props }: ToasterProps) => {
32
32
  '--border-radius': 'var(--radius)'
33
33
  } as React.CSSProperties
34
34
  }
35
+ toastOptions={{
36
+ classNames: {
37
+ toast: 'cn-toast'
38
+ }
39
+ }}
35
40
  {...props}
36
41
  />
37
42
  )
@@ -0,0 +1,31 @@
1
+ import { createId } from '@paralleldrive/cuid2'
2
+ import { sql } from 'drizzle-orm'
3
+ import { boolean, timestamp, varchar } from 'drizzle-orm/pg-core'
4
+
5
+ export const commonFieldDefs = {
6
+ id: (prefix: string) =>
7
+ varchar('id')
8
+ .primaryKey()
9
+ .$defaultFn(() => prefix + '_' + createId()),
10
+ date: (name: string) =>
11
+ timestamp(name, {
12
+ mode: 'date',
13
+ withTimezone: true
14
+ }),
15
+ dates: {
16
+ createdAt: timestamp('created_at', {
17
+ mode: 'date',
18
+ withTimezone: true
19
+ })
20
+ .notNull()
21
+ .defaultNow(),
22
+ updatedAt: timestamp('updated_at', {
23
+ mode: 'date',
24
+ withTimezone: true
25
+ })
26
+ .notNull()
27
+ .$onUpdate(() => sql`NOW()`)
28
+ .defaultNow()
29
+ },
30
+ isActive: boolean('is_active').notNull().default(true)
31
+ }
@@ -1,31 +1,5 @@
1
- import { createId } from '@paralleldrive/cuid2'
2
- import { boolean, pgTable, text, timestamp, varchar } from 'drizzle-orm/pg-core'
3
-
4
- const commonFieldDefs = {
5
- id: (prefix: string) =>
6
- varchar('id')
7
- .primaryKey()
8
- .$defaultFn(() => prefix + '_' + createId()),
9
- date: (name: string) =>
10
- timestamp(name, {
11
- mode: 'date',
12
- withTimezone: true
13
- }),
14
- dates: {
15
- createdAt: timestamp('created_at', {
16
- mode: 'date',
17
- withTimezone: true
18
- })
19
- .notNull()
20
- .defaultNow(),
21
- updatedAt: timestamp('updated_at', {
22
- mode: 'date',
23
- withTimezone: true
24
- })
25
- .notNull()
26
- .defaultNow()
27
- }
28
- }
1
+ import { boolean, pgTable, text } from 'drizzle-orm/pg-core'
2
+ import { commonFieldDefs } from './common'
29
3
 
30
4
  export const usersTable = pgTable('users', {
31
5
  id: commonFieldDefs.id('user'),
@@ -0,0 +1,19 @@
1
+ import * as React from 'react'
2
+
3
+ const MOBILE_BREAKPOINT = 768
4
+
5
+ export function useIsMobile() {
6
+ const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined)
7
+
8
+ React.useEffect(() => {
9
+ const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`)
10
+ const onChange = () => {
11
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
12
+ }
13
+ mql.addEventListener('change', onChange)
14
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
15
+ return () => mql.removeEventListener('change', onChange)
16
+ }, [])
17
+
18
+ return !!isMobile
19
+ }
@@ -2,12 +2,8 @@ import { validateEmail } from '@ejekanshjain/simple-email-validator'
2
2
  import { createId } from '@paralleldrive/cuid2'
3
3
  import { APIError, betterAuth } from 'better-auth'
4
4
  import { drizzleAdapter } from 'better-auth/adapters/drizzle'
5
- import {
6
- admin,
7
- createAuthMiddleware,
8
- lastLoginMethod,
9
- magicLink
10
- } from 'better-auth/plugins'
5
+ import { createAuthMiddleware } from 'better-auth/api'
6
+ import { admin, lastLoginMethod, magicLink } from 'better-auth/plugins'
11
7
  import { headers } from 'next/headers'
12
8
  import { cache } from 'react'
13
9
  import { start } from 'workflow/api'
@@ -0,0 +1,27 @@
1
+ export const formatCurrency = (
2
+ currencyCode: string,
3
+ num: number | string,
4
+ precision = 2
5
+ ) => {
6
+ const numCopy = Number(num)
7
+
8
+ switch (currencyCode) {
9
+ case 'INR':
10
+ return new Intl.NumberFormat('en-IN', {
11
+ style: 'currency',
12
+ currency: 'INR',
13
+ signDisplay: 'negative'
14
+ }).format(numCopy)
15
+ case 'USD':
16
+ return new Intl.NumberFormat('en-US', {
17
+ style: 'currency',
18
+ currency: 'USD',
19
+ signDisplay: 'negative'
20
+ }).format(numCopy)
21
+ default:
22
+ console.warn(
23
+ `Unsupported currency: ${currencyCode}. Defaulting to fixed format.`
24
+ )
25
+ return numCopy.toFixed(precision)
26
+ }
27
+ }
@@ -0,0 +1,12 @@
1
+ export const formatDate = (
2
+ date: Date | string | number | null | undefined,
3
+ options?: { short?: boolean; time?: boolean; noYear?: boolean }
4
+ ): string => {
5
+ if (date == null) return '—'
6
+ return new Date(date).toLocaleDateString('en-US', {
7
+ ...(options?.noYear ? {} : { year: 'numeric' }),
8
+ month: options?.short ? 'short' : 'long',
9
+ day: 'numeric',
10
+ ...(options?.time ? { hour: '2-digit', minute: '2-digit' } : {})
11
+ })
12
+ }
@@ -0,0 +1,11 @@
1
+ const seedData = async () => {}
2
+
3
+ if (import.meta.main) {
4
+ try {
5
+ await seedData()
6
+ } catch (err) {
7
+ console.error('Error seeding data:', err)
8
+ } finally {
9
+ process.exit(0)
10
+ }
11
+ }
@@ -2,10 +2,10 @@ import { render, toPlainText } from '@react-email/render'
2
2
  import TestEmail from '~/emails/test'
3
3
  import { sendEmail } from '~/lib/nodemailer'
4
4
 
5
- async function main() {
5
+ async function sendTestMail() {
6
6
  const html = await render(
7
7
  TestEmail({
8
- name: 'Ekansh Jain'
8
+ name: 'John Doe'
9
9
  }),
10
10
  {
11
11
  pretty: true
@@ -22,8 +22,14 @@ async function main() {
22
22
  })
23
23
 
24
24
  console.info('Test email sent successfully', result)
25
-
26
- process.exit()
27
25
  }
28
26
 
29
- main()
27
+ if (import.meta.main) {
28
+ try {
29
+ await sendTestMail()
30
+ } catch (err) {
31
+ console.error('Error sending test mail:', err)
32
+ } finally {
33
+ process.exit(0)
34
+ }
35
+ }
@@ -15,6 +15,8 @@
15
15
  "noUnusedParameters": true,
16
16
  "noImplicitReturns": true,
17
17
  "noFallthroughCasesInSwitch": true,
18
+ "allowUnreachableCode": false,
19
+ "noUncheckedSideEffectImports": false,
18
20
  "forceConsistentCasingInFileNames": true,
19
21
  "noEmit": true,
20
22
  "esModuleInterop": true,
@@ -1,25 +0,0 @@
1
- import { Elysia } from 'elysia'
2
- import z from 'zod'
3
-
4
- export const elysiaApi = new Elysia({ prefix: '/api' })
5
- .get('/', 'Hello from Elysia')
6
- .post(
7
- '/',
8
- ({ body }) => {
9
- return `Hello, ${body.name}!`
10
- },
11
- {
12
- body: z.object({
13
- name: z.string().trim().min(1).describe('Name of the user')
14
- }),
15
- detail: {
16
- description: 'Greet a user by name'
17
- }
18
- }
19
- )
20
-
21
- export const GET = elysiaApi.fetch
22
- export const POST = elysiaApi.fetch
23
- export const PUT = elysiaApi.fetch
24
- export const PATCH = elysiaApi.fetch
25
- export const DELETE = elysiaApi.fetch
@@ -1,60 +0,0 @@
1
- import { Slot } from '@radix-ui/react-slot'
2
- import { cva, type VariantProps } from 'class-variance-authority'
3
- import * as React from 'react'
4
-
5
- import { cn } from '~/lib/cn'
6
-
7
- const buttonVariants = cva(
8
- "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
9
- {
10
- variants: {
11
- variant: {
12
- default: 'bg-primary text-primary-foreground hover:bg-primary/90',
13
- destructive:
14
- 'bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60',
15
- outline:
16
- 'border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50',
17
- secondary:
18
- 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
19
- ghost:
20
- 'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50',
21
- link: 'text-primary underline-offset-4 hover:underline'
22
- },
23
- size: {
24
- default: 'h-9 px-4 py-2 has-[>svg]:px-3',
25
- sm: 'h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5',
26
- lg: 'h-10 rounded-md px-6 has-[>svg]:px-4',
27
- icon: 'size-9',
28
- 'icon-sm': 'size-8',
29
- 'icon-lg': 'size-10'
30
- }
31
- },
32
- defaultVariants: {
33
- variant: 'default',
34
- size: 'default'
35
- }
36
- }
37
- )
38
-
39
- function Button({
40
- className,
41
- variant,
42
- size,
43
- asChild = false,
44
- ...props
45
- }: React.ComponentProps<'button'> &
46
- VariantProps<typeof buttonVariants> & {
47
- asChild?: boolean
48
- }) {
49
- const Comp = asChild ? Slot : 'button'
50
-
51
- return (
52
- <Comp
53
- data-slot="button"
54
- className={cn(buttonVariants({ variant, size, className }))}
55
- {...props}
56
- />
57
- )
58
- }
59
-
60
- export { Button, buttonVariants }