@primstack/cli 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 (210) hide show
  1. package/dist/generators/crud/templates/drizzle-table.ts.template +12 -0
  2. package/dist/generators/crud/templates/handlers.ts.template +136 -0
  3. package/dist/generators/crud/templates/routes.ts.template +21 -0
  4. package/dist/generators/crud/templates/schema.ts.template +20 -0
  5. package/dist/index.js +9618 -0
  6. package/dist/integrations/analytics/providers/amplitude/templates/src/analytics/index.ts.template +79 -0
  7. package/dist/integrations/analytics/providers/amplitude/templates/src/analytics/types.ts.template +12 -0
  8. package/dist/integrations/analytics/providers/mixpanel/templates/src/analytics/index.ts.template +62 -0
  9. package/dist/integrations/analytics/providers/mixpanel/templates/src/analytics/types.ts.template +12 -0
  10. package/dist/integrations/analytics/providers/posthog/templates/src/analytics/index.ts.template +67 -0
  11. package/dist/integrations/analytics/providers/posthog/templates/src/analytics/types.ts.template +12 -0
  12. package/dist/integrations/auth/providers/authjoy/templates/api/src/middleware/auth.ts.template +89 -0
  13. package/dist/integrations/auth/providers/authjoy/templates/api/src/routes/auth.ts.template +27 -0
  14. package/dist/integrations/auth/providers/authjoy/templates/web/src/components/AuthProvider.tsx.template +40 -0
  15. package/dist/integrations/auth/providers/authjoy/templates/web/src/hooks/use-auth.ts.template +71 -0
  16. package/dist/integrations/auth/providers/authjoy/templates/web/src/lib/auth.ts.template +59 -0
  17. package/dist/integrations/auth/providers/authjoy/templates/web/src/pages/account.tsx.template +84 -0
  18. package/dist/integrations/auth/providers/authjoy/templates/web/src/pages/login.tsx.template +73 -0
  19. package/dist/integrations/cache/providers/memory/templates/src/cache/index.ts.template +43 -0
  20. package/dist/integrations/cache/providers/memory/templates/src/cache/types.ts.template +3 -0
  21. package/dist/integrations/cache/providers/redis/templates/src/cache/index.ts.template +37 -0
  22. package/dist/integrations/cache/providers/redis/templates/src/cache/types.ts.template +3 -0
  23. package/dist/integrations/cache/providers/valkey/templates/src/cache/index.ts.template +38 -0
  24. package/dist/integrations/cache/providers/valkey/templates/src/cache/types.ts.template +3 -0
  25. package/dist/integrations/db/providers/postgres/templates/drizzle.config.ts.template +10 -0
  26. package/dist/integrations/db/providers/postgres/templates/src/db/index.ts.template +13 -0
  27. package/dist/integrations/db/providers/postgres/templates/src/db/migrate.ts.template +19 -0
  28. package/dist/integrations/db/providers/postgres/templates/src/db/schema/index.ts.template +1 -0
  29. package/dist/integrations/db/providers/postgres/templates/src/db/schema/users.ts.template +12 -0
  30. package/dist/integrations/db/providers/postgres/templates/src/db/seed.ts.template +28 -0
  31. package/dist/integrations/db/providers/sqlite/templates/drizzle.config.ts.template +10 -0
  32. package/dist/integrations/db/providers/sqlite/templates/src/db/index.ts.template +10 -0
  33. package/dist/integrations/db/providers/sqlite/templates/src/db/schema/index.ts.template +1 -0
  34. package/dist/integrations/db/providers/sqlite/templates/src/db/schema/users.ts.template +12 -0
  35. package/dist/integrations/db/providers/sqlite/templates/src/db/seed.ts.template +28 -0
  36. package/dist/integrations/db/providers/supabase/templates/drizzle.config.ts.template +10 -0
  37. package/dist/integrations/db/providers/supabase/templates/src/db/index.ts.template +13 -0
  38. package/dist/integrations/db/providers/supabase/templates/src/db/migrate.ts.template +19 -0
  39. package/dist/integrations/db/providers/supabase/templates/src/db/schema/index.ts.template +1 -0
  40. package/dist/integrations/db/providers/supabase/templates/src/db/schema/users.ts.template +12 -0
  41. package/dist/integrations/db/providers/supabase/templates/src/db/seed.ts.template +28 -0
  42. package/dist/integrations/db/providers/turso/templates/drizzle.config.ts.template +11 -0
  43. package/dist/integrations/db/providers/turso/templates/src/db/index.ts.template +14 -0
  44. package/dist/integrations/db/providers/turso/templates/src/db/schema/index.ts.template +1 -0
  45. package/dist/integrations/db/providers/turso/templates/src/db/schema/users.ts.template +12 -0
  46. package/dist/integrations/db/providers/turso/templates/src/db/seed.ts.template +28 -0
  47. package/dist/integrations/email/providers/nodemailer/templates/src/email/index.ts.template +24 -0
  48. package/dist/integrations/email/providers/nodemailer/templates/src/email/templates/index.ts.template +1 -0
  49. package/dist/integrations/email/providers/nodemailer/templates/src/email/templates/welcome.ts.template +7 -0
  50. package/dist/integrations/email/providers/nodemailer/templates/src/email/types.ts.template +7 -0
  51. package/dist/integrations/email/providers/resend/templates/src/email/index.ts.template +18 -0
  52. package/dist/integrations/email/providers/resend/templates/src/email/templates/index.ts.template +1 -0
  53. package/dist/integrations/email/providers/resend/templates/src/email/templates/welcome.ts.template +7 -0
  54. package/dist/integrations/email/providers/resend/templates/src/email/types.ts.template +7 -0
  55. package/dist/integrations/email/providers/sendgrid/templates/src/email/index.ts.template +16 -0
  56. package/dist/integrations/email/providers/sendgrid/templates/src/email/templates/index.ts.template +1 -0
  57. package/dist/integrations/email/providers/sendgrid/templates/src/email/templates/welcome.ts.template +7 -0
  58. package/dist/integrations/email/providers/sendgrid/templates/src/email/types.ts.template +7 -0
  59. package/dist/integrations/flags/providers/local/templates/api/src/lib/flags.ts.template +97 -0
  60. package/dist/integrations/flags/providers/local/templates/api/src/routes/flags.ts.template +36 -0
  61. package/dist/integrations/flags/providers/local/templates/flags.json.template +8 -0
  62. package/dist/integrations/flags/providers/local/templates/web/src/hooks/use-flag.ts.template +60 -0
  63. package/dist/integrations/logging/providers/axiom/templates/src/logging/index.ts.template +56 -0
  64. package/dist/integrations/logging/providers/axiom/templates/src/logging/types.ts.template +5 -0
  65. package/dist/integrations/logging/providers/pino/templates/src/logging/index.ts.template +21 -0
  66. package/dist/integrations/logging/providers/pino/templates/src/logging/types.ts.template +5 -0
  67. package/dist/integrations/logging/providers/winston/templates/src/logging/index.ts.template +30 -0
  68. package/dist/integrations/logging/providers/winston/templates/src/logging/types.ts.template +5 -0
  69. package/dist/integrations/monitor/providers/datadog/templates/src/monitor/index.ts.template +78 -0
  70. package/dist/integrations/monitor/providers/datadog/templates/src/monitor/types.ts.template +12 -0
  71. package/dist/integrations/monitor/providers/newrelic/templates/src/monitor/index.ts.template +60 -0
  72. package/dist/integrations/monitor/providers/newrelic/templates/src/monitor/types.ts.template +12 -0
  73. package/dist/integrations/monitor/providers/sentry/templates/src/monitor/index.ts.template +70 -0
  74. package/dist/integrations/monitor/providers/sentry/templates/src/monitor/types.ts.template +12 -0
  75. package/dist/integrations/queue/providers/bullmq/templates/src/queue/index.ts.template +56 -0
  76. package/dist/integrations/queue/providers/bullmq/templates/src/queue/types.ts.template +10 -0
  77. package/dist/integrations/queue/providers/memory/templates/src/queue/index.ts.template +73 -0
  78. package/dist/integrations/queue/providers/memory/templates/src/queue/types.ts.template +10 -0
  79. package/dist/integrations/queue/providers/pgboss/templates/src/queue/index.ts.template +34 -0
  80. package/dist/integrations/queue/providers/pgboss/templates/src/queue/types.ts.template +10 -0
  81. package/dist/integrations/ratelimit/providers/memory/templates/src/ratelimit/index.ts.template +95 -0
  82. package/dist/integrations/ratelimit/providers/memory/templates/src/ratelimit/types.ts.template +12 -0
  83. package/dist/integrations/ratelimit/providers/rate-limiter-flexible/templates/src/ratelimit/index.ts.template +80 -0
  84. package/dist/integrations/ratelimit/providers/rate-limiter-flexible/templates/src/ratelimit/types.ts.template +12 -0
  85. package/dist/integrations/ratelimit/providers/upstash/templates/src/ratelimit/index.ts.template +67 -0
  86. package/dist/integrations/ratelimit/providers/upstash/templates/src/ratelimit/types.ts.template +12 -0
  87. package/dist/integrations/schedule/providers/bullmq/templates/src/schedule/index.ts.template +81 -0
  88. package/dist/integrations/schedule/providers/bullmq/templates/src/schedule/types.ts.template +10 -0
  89. package/dist/integrations/schedule/providers/croner/templates/src/schedule/index.ts.template +47 -0
  90. package/dist/integrations/schedule/providers/croner/templates/src/schedule/types.ts.template +10 -0
  91. package/dist/integrations/schedule/providers/node-cron/templates/src/schedule/index.ts.template +45 -0
  92. package/dist/integrations/schedule/providers/node-cron/templates/src/schedule/types.ts.template +10 -0
  93. package/dist/integrations/search/providers/algolia/templates/src/search/index.ts.template +52 -0
  94. package/dist/integrations/search/providers/algolia/templates/src/search/types.ts.template +18 -0
  95. package/dist/integrations/search/providers/meilisearch/templates/src/search/index.ts.template +49 -0
  96. package/dist/integrations/search/providers/meilisearch/templates/src/search/types.ts.template +18 -0
  97. package/dist/integrations/search/providers/typesense/templates/src/search/index.ts.template +71 -0
  98. package/dist/integrations/search/providers/typesense/templates/src/search/types.ts.template +35 -0
  99. package/dist/integrations/storage/providers/local/templates/src/storage/index.ts.template +69 -0
  100. package/dist/integrations/storage/providers/local/templates/src/storage/types.ts.template +12 -0
  101. package/dist/integrations/storage/providers/r2/templates/src/storage/index.ts.template +80 -0
  102. package/dist/integrations/storage/providers/r2/templates/src/storage/types.ts.template +12 -0
  103. package/dist/integrations/storage/providers/s3/templates/src/storage/index.ts.template +78 -0
  104. package/dist/integrations/storage/providers/s3/templates/src/storage/types.ts.template +12 -0
  105. package/dist/integrations/stripe/templates/api/src/lib/stripe.ts.template +259 -0
  106. package/dist/integrations/stripe/templates/api/src/routes/stripe-webhooks.ts.template +284 -0
  107. package/dist/integrations/stripe/templates/api/stripe.config.ts.template +178 -0
  108. package/dist/integrations/stripe/templates/shared/src/pricing.ts.template +117 -0
  109. package/dist/integrations/stripe/templates/shared/src/stripe-types.ts.template +133 -0
  110. package/dist/integrations/stripe/templates/web/src/components/billing-settings.tsx.template +123 -0
  111. package/dist/integrations/stripe/templates/web/src/components/pricing-cards.tsx.template +115 -0
  112. package/dist/integrations/stripe/templates/web/src/pages/pricing.tsx.template +95 -0
  113. package/dist/templates/api/fastify/.env.example.template +7 -0
  114. package/dist/templates/api/fastify/.gitignore.template +24 -0
  115. package/dist/templates/api/fastify/package.json.template +23 -0
  116. package/dist/templates/api/fastify/src/index.ts.template +52 -0
  117. package/dist/templates/api/fastify/src/lib/env.ts.template +20 -0
  118. package/dist/templates/api/fastify/src/routes/health.ts.template +12 -0
  119. package/dist/templates/api/fastify/tsconfig.json.template +18 -0
  120. package/dist/templates/api/fastify-postgres/.env.example.template +10 -0
  121. package/dist/templates/api/fastify-postgres/drizzle.config.ts.template +10 -0
  122. package/dist/templates/api/fastify-postgres/package.json.template +16 -0
  123. package/dist/templates/api/fastify-postgres/src/db/index.ts.template +9 -0
  124. package/dist/templates/api/fastify-postgres/src/db/schema/users.ts.template +12 -0
  125. package/dist/templates/api/fastify-sqlite/.env.example.template +10 -0
  126. package/dist/templates/api/fastify-sqlite/drizzle.config.ts.template +10 -0
  127. package/dist/templates/api/fastify-sqlite/package.json.template +16 -0
  128. package/dist/templates/api/fastify-sqlite/src/db/index.ts.template +6 -0
  129. package/dist/templates/api/fastify-sqlite/src/db/schema/users.ts.template +12 -0
  130. package/dist/templates/api/fastify-supabase/.env.example.template +10 -0
  131. package/dist/templates/api/fastify-supabase/drizzle.config.ts.template +10 -0
  132. package/dist/templates/api/fastify-supabase/package.json.template +15 -0
  133. package/dist/templates/api/fastify-supabase/src/db/index.ts.template +9 -0
  134. package/dist/templates/api/fastify-supabase/src/db/schema/users.ts.template +12 -0
  135. package/dist/templates/api/fastify-turso/.env.example.template +11 -0
  136. package/dist/templates/api/fastify-turso/drizzle.config.ts.template +11 -0
  137. package/dist/templates/api/fastify-turso/package.json.template +15 -0
  138. package/dist/templates/api/fastify-turso/src/db/index.ts.template +10 -0
  139. package/dist/templates/api/fastify-turso/src/db/schema/users.ts.template +12 -0
  140. package/dist/templates/fullstack/api/.env.example.template +10 -0
  141. package/dist/templates/fullstack/api/.gitignore.template +4 -0
  142. package/dist/templates/fullstack/api/drizzle.config.ts.template +14 -0
  143. package/dist/templates/fullstack/api/package.json.template +33 -0
  144. package/dist/templates/fullstack/api/src/db/index.ts.template +13 -0
  145. package/dist/templates/fullstack/api/src/db/schema/api-keys.ts.template +19 -0
  146. package/dist/templates/fullstack/api/src/db/schema/audit-logs.ts.template +23 -0
  147. package/dist/templates/fullstack/api/src/db/schema/index.ts.template +8 -0
  148. package/dist/templates/fullstack/api/src/db/schema/invites.ts.template +19 -0
  149. package/dist/templates/fullstack/api/src/db/schema/memberships.ts.template +16 -0
  150. package/dist/templates/fullstack/api/src/db/schema/organizations.ts.template +13 -0
  151. package/dist/templates/fullstack/api/src/db/schema/plans.ts.template +29 -0
  152. package/dist/templates/fullstack/api/src/db/schema/subscriptions.ts.template +38 -0
  153. package/dist/templates/fullstack/api/src/db/schema/users.ts.template +14 -0
  154. package/dist/templates/fullstack/api/src/index.ts.template +54 -0
  155. package/dist/templates/fullstack/api/src/lib/env.ts.template +22 -0
  156. package/dist/templates/fullstack/api/src/routes/health.ts.template +14 -0
  157. package/dist/templates/fullstack/api/tsconfig.json.template +15 -0
  158. package/dist/templates/fullstack/root/.gitignore.template +26 -0
  159. package/dist/templates/fullstack/root/package.json.template +15 -0
  160. package/dist/templates/fullstack/root/pnpm-workspace.yaml.template +3 -0
  161. package/dist/templates/fullstack/root/turbo.json.template +17 -0
  162. package/dist/templates/fullstack/shared/package.json.template +36 -0
  163. package/dist/templates/fullstack/shared/src/index.ts.template +8 -0
  164. package/dist/templates/fullstack/shared/src/schemas/api-key.ts.template +28 -0
  165. package/dist/templates/fullstack/shared/src/schemas/audit-log.ts.template +41 -0
  166. package/dist/templates/fullstack/shared/src/schemas/index.ts.template +8 -0
  167. package/dist/templates/fullstack/shared/src/schemas/invite.ts.template +25 -0
  168. package/dist/templates/fullstack/shared/src/schemas/membership.ts.template +20 -0
  169. package/dist/templates/fullstack/shared/src/schemas/organization.ts.template +18 -0
  170. package/dist/templates/fullstack/shared/src/schemas/plan.ts.template +38 -0
  171. package/dist/templates/fullstack/shared/src/schemas/subscription.ts.template +56 -0
  172. package/dist/templates/fullstack/shared/src/schemas/user.ts.template +21 -0
  173. package/dist/templates/fullstack/shared/src/types/index.ts.template +75 -0
  174. package/dist/templates/fullstack/shared/src/validators/index.ts.template +53 -0
  175. package/dist/templates/fullstack/shared/tsconfig.json.template +17 -0
  176. package/dist/templates/fullstack/web/.gitignore.template +3 -0
  177. package/dist/templates/fullstack/web/index.html.template +13 -0
  178. package/dist/templates/fullstack/web/package.json.template +23 -0
  179. package/dist/templates/fullstack/web/src/App.tsx.template +47 -0
  180. package/dist/templates/fullstack/web/src/index.css.template +54 -0
  181. package/dist/templates/fullstack/web/src/main.tsx.template +10 -0
  182. package/dist/templates/fullstack/web/src/vite-env.d.ts.template +1 -0
  183. package/dist/templates/fullstack/web/tsconfig.json.template +21 -0
  184. package/dist/templates/fullstack/web/tsconfig.node.json.template +11 -0
  185. package/dist/templates/fullstack/web/vite.config.ts.template +15 -0
  186. package/dist/templates/hosted/root/.env.local.template +13 -0
  187. package/dist/templates/hosted/root/.gitignore.template +32 -0
  188. package/dist/templates/hosted/root/CLAUDE.md.template +139 -0
  189. package/dist/templates/hosted/root/drizzle.config.ts.template +10 -0
  190. package/dist/templates/hosted/root/next.config.ts.template +15 -0
  191. package/dist/templates/hosted/root/package.json.template +40 -0
  192. package/dist/templates/hosted/root/postcss.config.mjs.template +9 -0
  193. package/dist/templates/hosted/root/primstack.config.json.template +5 -0
  194. package/dist/templates/hosted/root/tailwind.config.ts.template +14 -0
  195. package/dist/templates/hosted/root/tsconfig.json.template +25 -0
  196. package/dist/templates/hosted/root/wrangler.toml.template +9 -0
  197. package/dist/templates/hosted/src/app/actions/example.ts.template +50 -0
  198. package/dist/templates/hosted/src/app/api/health/route.ts.template +5 -0
  199. package/dist/templates/hosted/src/app/auth/login/page.tsx.template +32 -0
  200. package/dist/templates/hosted/src/app/globals.css.template +59 -0
  201. package/dist/templates/hosted/src/app/layout.tsx.template +24 -0
  202. package/dist/templates/hosted/src/app/page.tsx.template +34 -0
  203. package/dist/templates/hosted/src/db/migrations/0000_initial.sql.template +43 -0
  204. package/dist/templates/hosted/src/db/schema.ts.template +52 -0
  205. package/dist/templates/hosted/src/env.d.ts.template +10 -0
  206. package/dist/templates/hosted/src/instrumentation.ts.template +6 -0
  207. package/dist/templates/hosted/src/lib/auth.ts.template +35 -0
  208. package/dist/templates/hosted/src/lib/db.ts.template +17 -0
  209. package/dist/templates/hosted/src/middleware.ts.template +6 -0
  210. package/package.json +46 -0
@@ -0,0 +1,43 @@
1
+ import { LRUCache } from 'lru-cache';
2
+ import type { CacheOptions } from './types.js';
3
+
4
+ const maxSize = parseInt(process.env.CACHE_MAX_SIZE || '1000', 10);
5
+ const defaultTtl = parseInt(process.env.CACHE_TTL || '3600', 10) * 1000; // ms
6
+
7
+ export const cache = new LRUCache<string, string>({
8
+ max: maxSize,
9
+ ttl: defaultTtl,
10
+ });
11
+
12
+ export async function cacheGet<T = string>(key: string): Promise<T | null> {
13
+ const value = cache.get(key);
14
+ if (value === undefined) return null;
15
+ try {
16
+ return JSON.parse(value) as T;
17
+ } catch {
18
+ return value as unknown as T;
19
+ }
20
+ }
21
+
22
+ export async function cacheSet(key: string, value: unknown, options?: CacheOptions): Promise<void> {
23
+ const serialized = typeof value === 'string' ? value : JSON.stringify(value);
24
+ if (options?.ttl) {
25
+ cache.set(key, serialized, { ttl: options.ttl * 1000 });
26
+ } else {
27
+ cache.set(key, serialized);
28
+ }
29
+ }
30
+
31
+ export async function cacheDel(key: string): Promise<void> {
32
+ cache.delete(key);
33
+ }
34
+
35
+ export async function cacheHas(key: string): Promise<boolean> {
36
+ return cache.has(key);
37
+ }
38
+
39
+ export async function cacheDisconnect(): Promise<void> {
40
+ cache.clear();
41
+ }
42
+
43
+ export type { CacheOptions } from './types.js';
@@ -0,0 +1,3 @@
1
+ export interface CacheOptions {
2
+ ttl?: number; // seconds
3
+ }
@@ -0,0 +1,37 @@
1
+ import Redis from 'ioredis';
2
+ import type { CacheOptions } from './types.js';
3
+
4
+ export const cache = new Redis(process.env.REDIS_URL || 'redis://localhost:6379');
5
+
6
+ export async function cacheGet<T = string>(key: string): Promise<T | null> {
7
+ const value = await cache.get(key);
8
+ if (value === null) return null;
9
+ try {
10
+ return JSON.parse(value) as T;
11
+ } catch {
12
+ return value as unknown as T;
13
+ }
14
+ }
15
+
16
+ export async function cacheSet(key: string, value: unknown, options?: CacheOptions): Promise<void> {
17
+ const serialized = typeof value === 'string' ? value : JSON.stringify(value);
18
+ if (options?.ttl) {
19
+ await cache.set(key, serialized, 'EX', options.ttl);
20
+ } else {
21
+ await cache.set(key, serialized);
22
+ }
23
+ }
24
+
25
+ export async function cacheDel(key: string): Promise<void> {
26
+ await cache.del(key);
27
+ }
28
+
29
+ export async function cacheHas(key: string): Promise<boolean> {
30
+ return (await cache.exists(key)) === 1;
31
+ }
32
+
33
+ export async function cacheDisconnect(): Promise<void> {
34
+ await cache.quit();
35
+ }
36
+
37
+ export type { CacheOptions } from './types.js';
@@ -0,0 +1,3 @@
1
+ export interface CacheOptions {
2
+ ttl?: number; // seconds
3
+ }
@@ -0,0 +1,38 @@
1
+ import Redis from 'ioredis';
2
+ import type { CacheOptions } from './types.js';
3
+
4
+ // Valkey is wire-compatible with Redis — ioredis works out of the box
5
+ export const cache = new Redis(process.env.VALKEY_URL || 'redis://localhost:6379');
6
+
7
+ export async function cacheGet<T = string>(key: string): Promise<T | null> {
8
+ const value = await cache.get(key);
9
+ if (value === null) return null;
10
+ try {
11
+ return JSON.parse(value) as T;
12
+ } catch {
13
+ return value as unknown as T;
14
+ }
15
+ }
16
+
17
+ export async function cacheSet(key: string, value: unknown, options?: CacheOptions): Promise<void> {
18
+ const serialized = typeof value === 'string' ? value : JSON.stringify(value);
19
+ if (options?.ttl) {
20
+ await cache.set(key, serialized, 'EX', options.ttl);
21
+ } else {
22
+ await cache.set(key, serialized);
23
+ }
24
+ }
25
+
26
+ export async function cacheDel(key: string): Promise<void> {
27
+ await cache.del(key);
28
+ }
29
+
30
+ export async function cacheHas(key: string): Promise<boolean> {
31
+ return (await cache.exists(key)) === 1;
32
+ }
33
+
34
+ export async function cacheDisconnect(): Promise<void> {
35
+ await cache.quit();
36
+ }
37
+
38
+ export type { CacheOptions } from './types.js';
@@ -0,0 +1,3 @@
1
+ export interface CacheOptions {
2
+ ttl?: number; // seconds
3
+ }
@@ -0,0 +1,10 @@
1
+ import { defineConfig } from 'drizzle-kit';
2
+
3
+ export default defineConfig({
4
+ schema: './src/db/schema',
5
+ out: './src/db/migrations',
6
+ dialect: 'postgresql',
7
+ dbCredentials: {
8
+ url: process.env.DATABASE_URL!,
9
+ },
10
+ });
@@ -0,0 +1,13 @@
1
+ import { drizzle } from 'drizzle-orm/node-postgres';
2
+ import pg from 'pg';
3
+ import * as schema from './schema/index.js';
4
+
5
+ const pool = new pg.Pool({
6
+ connectionString: process.env.DATABASE_URL,
7
+ });
8
+
9
+ export const db = drizzle(pool, { schema });
10
+
11
+ export async function closeDatabase(): Promise<void> {
12
+ await pool.end();
13
+ }
@@ -0,0 +1,19 @@
1
+ import { migrate } from 'drizzle-orm/node-postgres/migrator';
2
+ import { db, closeDatabase } from './index.js';
3
+
4
+ async function runMigrations() {
5
+ console.log('Running migrations...');
6
+
7
+ await migrate(db, { migrationsFolder: './src/db/migrations' });
8
+
9
+ console.log('Migrations complete!');
10
+ }
11
+
12
+ runMigrations()
13
+ .catch((error) => {
14
+ console.error('Migration failed:', error);
15
+ process.exit(1);
16
+ })
17
+ .finally(async () => {
18
+ await closeDatabase();
19
+ });
@@ -0,0 +1,12 @@
1
+ import { pgTable, uuid, varchar, timestamp } from 'drizzle-orm/pg-core';
2
+
3
+ export const users = pgTable('users', {
4
+ id: uuid('id').primaryKey().defaultRandom(),
5
+ email: varchar('email', { length: 255 }).notNull().unique(),
6
+ name: varchar('name', { length: 255 }),
7
+ createdAt: timestamp('created_at').defaultNow().notNull(),
8
+ updatedAt: timestamp('updated_at').defaultNow().notNull(),
9
+ });
10
+
11
+ export type User = typeof users.$inferSelect;
12
+ export type NewUser = typeof users.$inferInsert;
@@ -0,0 +1,28 @@
1
+ import { db, closeDatabase } from './index.js';
2
+ import { users } from './schema/index.js';
3
+
4
+ async function seed() {
5
+ console.log('Seeding database...');
6
+
7
+ await db.insert(users).values([
8
+ {
9
+ email: 'alice@example.com',
10
+ name: 'Alice Johnson',
11
+ },
12
+ {
13
+ email: 'bob@example.com',
14
+ name: 'Bob Smith',
15
+ },
16
+ ]);
17
+
18
+ console.log('Seeding complete!');
19
+ }
20
+
21
+ seed()
22
+ .catch((error) => {
23
+ console.error('Seeding failed:', error);
24
+ process.exit(1);
25
+ })
26
+ .finally(async () => {
27
+ await closeDatabase();
28
+ });
@@ -0,0 +1,10 @@
1
+ import { defineConfig } from 'drizzle-kit';
2
+
3
+ export default defineConfig({
4
+ schema: './src/db/schema',
5
+ out: './src/db/migrations',
6
+ dialect: 'sqlite',
7
+ dbCredentials: {
8
+ url: process.env.DATABASE_URL || 'sqlite.db',
9
+ },
10
+ });
@@ -0,0 +1,10 @@
1
+ import { drizzle } from 'drizzle-orm/better-sqlite3';
2
+ import Database from 'better-sqlite3';
3
+ import * as schema from './schema/index.js';
4
+
5
+ const sqlite = new Database(process.env.DATABASE_URL || 'sqlite.db');
6
+ export const db = drizzle(sqlite, { schema });
7
+
8
+ export function closeDatabase(): void {
9
+ sqlite.close();
10
+ }
@@ -0,0 +1,12 @@
1
+ import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core';
2
+
3
+ export const users = sqliteTable('users', {
4
+ id: integer('id').primaryKey({ autoIncrement: true }),
5
+ email: text('email').notNull().unique(),
6
+ name: text('name'),
7
+ createdAt: text('created_at').notNull().default('CURRENT_TIMESTAMP'),
8
+ updatedAt: text('updated_at').notNull().default('CURRENT_TIMESTAMP'),
9
+ });
10
+
11
+ export type User = typeof users.$inferSelect;
12
+ export type NewUser = typeof users.$inferInsert;
@@ -0,0 +1,28 @@
1
+ import { db, closeDatabase } from './index.js';
2
+ import { users } from './schema/index.js';
3
+
4
+ function seed() {
5
+ console.log('Seeding database...');
6
+
7
+ db.insert(users).values([
8
+ {
9
+ email: 'alice@example.com',
10
+ name: 'Alice Johnson',
11
+ },
12
+ {
13
+ email: 'bob@example.com',
14
+ name: 'Bob Smith',
15
+ },
16
+ ]).run();
17
+
18
+ console.log('Seeding complete!');
19
+ }
20
+
21
+ try {
22
+ seed();
23
+ } catch (error) {
24
+ console.error('Seeding failed:', error);
25
+ process.exit(1);
26
+ } finally {
27
+ closeDatabase();
28
+ }
@@ -0,0 +1,10 @@
1
+ import { defineConfig } from 'drizzle-kit';
2
+
3
+ export default defineConfig({
4
+ schema: './src/db/schema',
5
+ out: './src/db/migrations',
6
+ dialect: 'postgresql',
7
+ dbCredentials: {
8
+ url: process.env.SUPABASE_DATABASE_URL!,
9
+ },
10
+ });
@@ -0,0 +1,13 @@
1
+ import { drizzle } from 'drizzle-orm/postgres-js';
2
+ import postgres from 'postgres';
3
+ import * as schema from './schema/index.js';
4
+
5
+ const client = postgres(process.env.SUPABASE_DATABASE_URL!, {
6
+ prepare: false, // Required for Supabase connection pooler (Supavisor)
7
+ });
8
+
9
+ export const db = drizzle(client, { schema });
10
+
11
+ export async function closeDatabase(): Promise<void> {
12
+ await client.end();
13
+ }
@@ -0,0 +1,19 @@
1
+ import { migrate } from 'drizzle-orm/postgres-js/migrator';
2
+ import { db, closeDatabase } from './index.js';
3
+
4
+ async function runMigrations() {
5
+ console.log('Running migrations...');
6
+
7
+ await migrate(db, { migrationsFolder: './src/db/migrations' });
8
+
9
+ console.log('Migrations complete!');
10
+ }
11
+
12
+ runMigrations()
13
+ .catch((error) => {
14
+ console.error('Migration failed:', error);
15
+ process.exit(1);
16
+ })
17
+ .finally(async () => {
18
+ await closeDatabase();
19
+ });
@@ -0,0 +1,12 @@
1
+ import { pgTable, uuid, varchar, timestamp } from 'drizzle-orm/pg-core';
2
+
3
+ export const users = pgTable('users', {
4
+ id: uuid('id').primaryKey().defaultRandom(),
5
+ email: varchar('email', { length: 255 }).notNull().unique(),
6
+ name: varchar('name', { length: 255 }),
7
+ createdAt: timestamp('created_at').defaultNow().notNull(),
8
+ updatedAt: timestamp('updated_at').defaultNow().notNull(),
9
+ });
10
+
11
+ export type User = typeof users.$inferSelect;
12
+ export type NewUser = typeof users.$inferInsert;
@@ -0,0 +1,28 @@
1
+ import { db, closeDatabase } from './index.js';
2
+ import { users } from './schema/index.js';
3
+
4
+ async function seed() {
5
+ console.log('Seeding database...');
6
+
7
+ await db.insert(users).values([
8
+ {
9
+ email: 'alice@example.com',
10
+ name: 'Alice Johnson',
11
+ },
12
+ {
13
+ email: 'bob@example.com',
14
+ name: 'Bob Smith',
15
+ },
16
+ ]);
17
+
18
+ console.log('Seeding complete!');
19
+ }
20
+
21
+ seed()
22
+ .catch((error) => {
23
+ console.error('Seeding failed:', error);
24
+ process.exit(1);
25
+ })
26
+ .finally(async () => {
27
+ await closeDatabase();
28
+ });
@@ -0,0 +1,11 @@
1
+ import { defineConfig } from 'drizzle-kit';
2
+
3
+ export default defineConfig({
4
+ schema: './src/db/schema',
5
+ out: './src/db/migrations',
6
+ dialect: 'turso',
7
+ dbCredentials: {
8
+ url: process.env.TURSO_DATABASE_URL!,
9
+ authToken: process.env.TURSO_AUTH_TOKEN,
10
+ },
11
+ });
@@ -0,0 +1,14 @@
1
+ import { drizzle } from 'drizzle-orm/libsql';
2
+ import { createClient } from '@libsql/client';
3
+ import * as schema from './schema/index.js';
4
+
5
+ const client = createClient({
6
+ url: process.env.TURSO_DATABASE_URL!,
7
+ authToken: process.env.TURSO_AUTH_TOKEN!,
8
+ });
9
+
10
+ export const db = drizzle(client, { schema });
11
+
12
+ export function closeDatabase(): void {
13
+ client.close();
14
+ }
@@ -0,0 +1,12 @@
1
+ import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core';
2
+
3
+ export const users = sqliteTable('users', {
4
+ id: integer('id').primaryKey({ autoIncrement: true }),
5
+ email: text('email').notNull().unique(),
6
+ name: text('name'),
7
+ createdAt: text('created_at').notNull().default('CURRENT_TIMESTAMP'),
8
+ updatedAt: text('updated_at').notNull().default('CURRENT_TIMESTAMP'),
9
+ });
10
+
11
+ export type User = typeof users.$inferSelect;
12
+ export type NewUser = typeof users.$inferInsert;
@@ -0,0 +1,28 @@
1
+ import { db, closeDatabase } from './index.js';
2
+ import { users } from './schema/index.js';
3
+
4
+ async function seed() {
5
+ console.log('Seeding database...');
6
+
7
+ await db.insert(users).values([
8
+ {
9
+ email: 'alice@example.com',
10
+ name: 'Alice Johnson',
11
+ },
12
+ {
13
+ email: 'bob@example.com',
14
+ name: 'Bob Smith',
15
+ },
16
+ ]);
17
+
18
+ console.log('Seeding complete!');
19
+ }
20
+
21
+ seed()
22
+ .catch((error) => {
23
+ console.error('Seeding failed:', error);
24
+ process.exit(1);
25
+ })
26
+ .finally(() => {
27
+ closeDatabase();
28
+ });
@@ -0,0 +1,24 @@
1
+ import nodemailer from 'nodemailer';
2
+ import type { EmailOptions } from './types.js';
3
+
4
+ const transporter = nodemailer.createTransport({
5
+ host: process.env.SMTP_HOST!,
6
+ port: Number(process.env.SMTP_PORT || 587),
7
+ secure: process.env.SMTP_SECURE === 'true',
8
+ auth: {
9
+ user: process.env.SMTP_USER!,
10
+ pass: process.env.SMTP_PASS!,
11
+ },
12
+ });
13
+
14
+ export async function sendEmail(options: EmailOptions): Promise<void> {
15
+ await transporter.sendMail({
16
+ from: options.from || process.env.EMAIL_FROM!,
17
+ to: options.to,
18
+ subject: options.subject,
19
+ html: options.html,
20
+ text: options.text,
21
+ });
22
+ }
23
+
24
+ export type { EmailOptions } from './types.js';
@@ -0,0 +1 @@
1
+ export { welcomeEmail } from './welcome.js';
@@ -0,0 +1,7 @@
1
+ export function welcomeEmail(name: string): { subject: string; html: string; text: string } {
2
+ return {
3
+ subject: `Welcome, ${name}!`,
4
+ html: `<h1>Welcome, ${name}!</h1><p>Thanks for signing up.</p>`,
5
+ text: `Welcome, ${name}! Thanks for signing up.`,
6
+ };
7
+ }
@@ -0,0 +1,7 @@
1
+ export interface EmailOptions {
2
+ to: string | string[];
3
+ subject: string;
4
+ html: string;
5
+ text?: string;
6
+ from?: string;
7
+ }
@@ -0,0 +1,18 @@
1
+ import { Resend } from 'resend';
2
+ import type { EmailOptions } from './types.js';
3
+
4
+ const resend = new Resend(process.env.RESEND_API_KEY!);
5
+
6
+ export async function sendEmail(options: EmailOptions): Promise<{ id: string }> {
7
+ const { data, error } = await resend.emails.send({
8
+ from: options.from || process.env.EMAIL_FROM!,
9
+ to: options.to,
10
+ subject: options.subject,
11
+ html: options.html,
12
+ text: options.text,
13
+ });
14
+ if (error) throw new Error(error.message);
15
+ return { id: data!.id };
16
+ }
17
+
18
+ export type { EmailOptions } from './types.js';
@@ -0,0 +1 @@
1
+ export { welcomeEmail } from './welcome.js';
@@ -0,0 +1,7 @@
1
+ export function welcomeEmail(name: string): { subject: string; html: string; text: string } {
2
+ return {
3
+ subject: `Welcome, ${name}!`,
4
+ html: `<h1>Welcome, ${name}!</h1><p>Thanks for signing up.</p>`,
5
+ text: `Welcome, ${name}! Thanks for signing up.`,
6
+ };
7
+ }
@@ -0,0 +1,7 @@
1
+ export interface EmailOptions {
2
+ to: string | string[];
3
+ subject: string;
4
+ html: string;
5
+ text?: string;
6
+ from?: string;
7
+ }
@@ -0,0 +1,16 @@
1
+ import sgMail from '@sendgrid/mail';
2
+ import type { EmailOptions } from './types.js';
3
+
4
+ sgMail.setApiKey(process.env.SENDGRID_API_KEY!);
5
+
6
+ export async function sendEmail(options: EmailOptions): Promise<void> {
7
+ await sgMail.send({
8
+ from: options.from || process.env.EMAIL_FROM!,
9
+ to: options.to,
10
+ subject: options.subject,
11
+ html: options.html,
12
+ text: options.text,
13
+ });
14
+ }
15
+
16
+ export type { EmailOptions } from './types.js';
@@ -0,0 +1 @@
1
+ export { welcomeEmail } from './welcome.js';
@@ -0,0 +1,7 @@
1
+ export function welcomeEmail(name: string): { subject: string; html: string; text: string } {
2
+ return {
3
+ subject: `Welcome, ${name}!`,
4
+ html: `<h1>Welcome, ${name}!</h1><p>Thanks for signing up.</p>`,
5
+ text: `Welcome, ${name}! Thanks for signing up.`,
6
+ };
7
+ }
@@ -0,0 +1,7 @@
1
+ export interface EmailOptions {
2
+ to: string | string[];
3
+ subject: string;
4
+ html: string;
5
+ text?: string;
6
+ from?: string;
7
+ }