@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,8 @@
1
+ export * from './user.js';
2
+ export * from './organization.js';
3
+ export * from './membership.js';
4
+ export * from './plan.js';
5
+ export * from './subscription.js';
6
+ export * from './invite.js';
7
+ export * from './api-key.js';
8
+ export * from './audit-log.js';
@@ -0,0 +1,25 @@
1
+ import { z } from 'zod';
2
+
3
+ export const inviteRoleSchema = z.enum(['admin', 'member']);
4
+
5
+ export const inviteSchema = z.object({
6
+ id: z.string().uuid(),
7
+ email: z.string().email(),
8
+ organizationId: z.string().uuid(),
9
+ role: inviteRoleSchema,
10
+ token: z.string().min(32),
11
+ expiresAt: z.coerce.date(),
12
+ acceptedAt: z.coerce.date().nullable(),
13
+ createdAt: z.coerce.date(),
14
+ });
15
+
16
+ export const createInviteSchema = inviteSchema.omit({
17
+ id: true,
18
+ token: true,
19
+ acceptedAt: true,
20
+ createdAt: true,
21
+ });
22
+
23
+ export const acceptInviteSchema = z.object({
24
+ token: z.string(),
25
+ });
@@ -0,0 +1,20 @@
1
+ import { z } from 'zod';
2
+
3
+ export const memberRoleSchema = z.enum(['owner', 'admin', 'member']);
4
+
5
+ export const membershipSchema = z.object({
6
+ id: z.string().uuid(),
7
+ userId: z.string().uuid(),
8
+ organizationId: z.string().uuid(),
9
+ role: memberRoleSchema,
10
+ createdAt: z.coerce.date(),
11
+ });
12
+
13
+ export const createMembershipSchema = membershipSchema.omit({
14
+ id: true,
15
+ createdAt: true,
16
+ });
17
+
18
+ export const updateMembershipSchema = z.object({
19
+ role: memberRoleSchema,
20
+ });
@@ -0,0 +1,18 @@
1
+ import { z } from 'zod';
2
+
3
+ export const organizationSchema = z.object({
4
+ id: z.string().uuid(),
5
+ name: z.string().min(1).max(255),
6
+ slug: z.string().regex(/^[a-z0-9-]+$/, 'Slug must be lowercase alphanumeric with hyphens'),
7
+ logoUrl: z.string().url().nullable(),
8
+ createdAt: z.coerce.date(),
9
+ updatedAt: z.coerce.date(),
10
+ });
11
+
12
+ export const createOrganizationSchema = organizationSchema.omit({
13
+ id: true,
14
+ createdAt: true,
15
+ updatedAt: true,
16
+ });
17
+
18
+ export const updateOrganizationSchema = createOrganizationSchema.partial();
@@ -0,0 +1,38 @@
1
+ import { z } from 'zod';
2
+
3
+ export const planLimitsSchema = z.object({
4
+ seats: z.number().int().nullable(),
5
+ storage: z.number().int().nullable(), // bytes
6
+ apiCalls: z.number().int().nullable(),
7
+ });
8
+
9
+ export const planSchema = z.object({
10
+ id: z.string().uuid(),
11
+ name: z.string().min(1),
12
+ slug: z.string().regex(/^[a-z0-9-]+$/),
13
+ description: z.string().nullable(),
14
+ priceMonthly: z.number().int(), // cents
15
+ priceYearly: z.number().int(), // cents
16
+ features: z.array(z.string()),
17
+ limits: planLimitsSchema,
18
+ isActive: z.boolean().default(true),
19
+
20
+ // Stripe integration fields
21
+ stripeProductIdTest: z.string().nullable().optional(),
22
+ stripeProductIdLive: z.string().nullable().optional(),
23
+ currentPriceLookupKeyMonthly: z.string().nullable().optional(), // e.g., 'pro_monthly_v2'
24
+ currentPriceLookupKeyYearly: z.string().nullable().optional(),
25
+
26
+ createdAt: z.coerce.date(),
27
+ updatedAt: z.coerce.date(),
28
+ });
29
+
30
+ export const createPlanSchema = planSchema.omit({
31
+ id: true,
32
+ createdAt: true,
33
+ updatedAt: true,
34
+ }).extend({
35
+ isActive: z.boolean().optional(),
36
+ });
37
+
38
+ export const updatePlanSchema = createPlanSchema.partial();
@@ -0,0 +1,56 @@
1
+ import { z } from 'zod';
2
+
3
+ export const subscriptionStatusSchema = z.enum(['active', 'canceled', 'past_due', 'trialing', 'incomplete']);
4
+ export const billingIntervalSchema = z.enum(['monthly', 'yearly']);
5
+
6
+ export const subscriptionSchema = z.object({
7
+ id: z.string().uuid(),
8
+ organizationId: z.string().uuid(),
9
+ planId: z.string().uuid(),
10
+ status: subscriptionStatusSchema,
11
+ currentPeriodStart: z.coerce.date(),
12
+ currentPeriodEnd: z.coerce.date(),
13
+ cancelAtPeriodEnd: z.boolean().default(false),
14
+
15
+ // Stripe integration fields
16
+ stripeSubscriptionId: z.string().nullable().optional(),
17
+ stripeCustomerId: z.string().nullable().optional(),
18
+
19
+ // Price versioning (locks in the price at subscription time)
20
+ billingInterval: billingIntervalSchema.nullable().optional(),
21
+ priceLookupKey: z.string().nullable().optional(), // e.g., 'pro_monthly_v1'
22
+ priceVersion: z.number().int().nullable().optional(),
23
+ priceAmountCents: z.number().int().nullable().optional(), // Locked-in price
24
+
25
+ // Trial support
26
+ trialStart: z.coerce.date().nullable().optional(),
27
+ trialEnd: z.coerce.date().nullable().optional(),
28
+
29
+ canceledAt: z.coerce.date().nullable().optional(),
30
+ createdAt: z.coerce.date(),
31
+ updatedAt: z.coerce.date(),
32
+ });
33
+
34
+ export const createSubscriptionSchema = subscriptionSchema.omit({
35
+ id: true,
36
+ createdAt: true,
37
+ updatedAt: true,
38
+ }).extend({
39
+ cancelAtPeriodEnd: z.boolean().optional(),
40
+ });
41
+
42
+ export const updateSubscriptionSchema = z.object({
43
+ status: subscriptionStatusSchema.optional(),
44
+ currentPeriodStart: z.coerce.date().optional(),
45
+ currentPeriodEnd: z.coerce.date().optional(),
46
+ cancelAtPeriodEnd: z.boolean().optional(),
47
+ stripeSubscriptionId: z.string().nullable().optional(),
48
+ stripeCustomerId: z.string().nullable().optional(),
49
+ billingInterval: billingIntervalSchema.nullable().optional(),
50
+ priceLookupKey: z.string().nullable().optional(),
51
+ priceVersion: z.number().int().nullable().optional(),
52
+ priceAmountCents: z.number().int().nullable().optional(),
53
+ trialStart: z.coerce.date().nullable().optional(),
54
+ trialEnd: z.coerce.date().nullable().optional(),
55
+ canceledAt: z.coerce.date().nullable().optional(),
56
+ });
@@ -0,0 +1,21 @@
1
+ import { z } from 'zod';
2
+
3
+ export const userSchema = z.object({
4
+ id: z.string().uuid(),
5
+ email: z.string().email(),
6
+ name: z.string().nullable(),
7
+ avatarUrl: z.string().url().nullable(),
8
+ emailVerified: z.boolean().default(false),
9
+ createdAt: z.coerce.date(),
10
+ updatedAt: z.coerce.date(),
11
+ });
12
+
13
+ export const createUserSchema = userSchema.omit({
14
+ id: true,
15
+ createdAt: true,
16
+ updatedAt: true,
17
+ }).extend({
18
+ emailVerified: z.boolean().optional(),
19
+ });
20
+
21
+ export const updateUserSchema = createUserSchema.partial();
@@ -0,0 +1,75 @@
1
+ import { z } from 'zod';
2
+ import {
3
+ userSchema,
4
+ createUserSchema,
5
+ updateUserSchema,
6
+ organizationSchema,
7
+ createOrganizationSchema,
8
+ updateOrganizationSchema,
9
+ membershipSchema,
10
+ createMembershipSchema,
11
+ updateMembershipSchema,
12
+ memberRoleSchema,
13
+ planSchema,
14
+ createPlanSchema,
15
+ updatePlanSchema,
16
+ planLimitsSchema,
17
+ subscriptionSchema,
18
+ createSubscriptionSchema,
19
+ updateSubscriptionSchema,
20
+ subscriptionStatusSchema,
21
+ inviteSchema,
22
+ createInviteSchema,
23
+ acceptInviteSchema,
24
+ inviteRoleSchema,
25
+ apiKeySchema,
26
+ createApiKeySchema,
27
+ apiKeyWithSecretSchema,
28
+ updateApiKeySchema,
29
+ auditLogSchema,
30
+ createAuditLogSchema,
31
+ } from '../schemas/index.js';
32
+
33
+ // User types
34
+ export type User = z.infer<typeof userSchema>;
35
+ export type CreateUser = z.infer<typeof createUserSchema>;
36
+ export type UpdateUser = z.infer<typeof updateUserSchema>;
37
+
38
+ // Organization types
39
+ export type Organization = z.infer<typeof organizationSchema>;
40
+ export type CreateOrganization = z.infer<typeof createOrganizationSchema>;
41
+ export type UpdateOrganization = z.infer<typeof updateOrganizationSchema>;
42
+
43
+ // Membership types
44
+ export type Membership = z.infer<typeof membershipSchema>;
45
+ export type CreateMembership = z.infer<typeof createMembershipSchema>;
46
+ export type UpdateMembership = z.infer<typeof updateMembershipSchema>;
47
+ export type MemberRole = z.infer<typeof memberRoleSchema>;
48
+
49
+ // Plan types
50
+ export type Plan = z.infer<typeof planSchema>;
51
+ export type CreatePlan = z.infer<typeof createPlanSchema>;
52
+ export type UpdatePlan = z.infer<typeof updatePlanSchema>;
53
+ export type PlanLimits = z.infer<typeof planLimitsSchema>;
54
+
55
+ // Subscription types
56
+ export type Subscription = z.infer<typeof subscriptionSchema>;
57
+ export type CreateSubscription = z.infer<typeof createSubscriptionSchema>;
58
+ export type UpdateSubscription = z.infer<typeof updateSubscriptionSchema>;
59
+ export type SubscriptionStatus = z.infer<typeof subscriptionStatusSchema>;
60
+
61
+ // Invite types
62
+ export type Invite = z.infer<typeof inviteSchema>;
63
+ export type CreateInvite = z.infer<typeof createInviteSchema>;
64
+ export type AcceptInvite = z.infer<typeof acceptInviteSchema>;
65
+ export type InviteRole = z.infer<typeof inviteRoleSchema>;
66
+
67
+ // ApiKey types
68
+ export type ApiKey = z.infer<typeof apiKeySchema>;
69
+ export type CreateApiKey = z.infer<typeof createApiKeySchema>;
70
+ export type ApiKeyWithSecret = z.infer<typeof apiKeyWithSecretSchema>;
71
+ export type UpdateApiKey = z.infer<typeof updateApiKeySchema>;
72
+
73
+ // AuditLog types
74
+ export type AuditLog = z.infer<typeof auditLogSchema>;
75
+ export type CreateAuditLog = z.infer<typeof createAuditLogSchema>;
@@ -0,0 +1,53 @@
1
+ import { z } from 'zod';
2
+
3
+ // Email validation
4
+ export function isValidEmail(email: string): boolean {
5
+ return z.string().email().safeParse(email).success;
6
+ }
7
+
8
+ // UUID validation
9
+ export function isValidUuid(id: string): boolean {
10
+ return z.string().uuid().safeParse(id).success;
11
+ }
12
+
13
+ // Slug validation (lowercase alphanumeric with hyphens)
14
+ export function isValidSlug(slug: string): boolean {
15
+ return /^[a-z0-9-]+$/.test(slug);
16
+ }
17
+
18
+ // URL validation
19
+ export function isValidUrl(url: string): boolean {
20
+ return z.string().url().safeParse(url).success;
21
+ }
22
+
23
+ // Parse and validate with a schema, returns typed result or throws
24
+ export function parseOrThrow<T extends z.ZodSchema>(
25
+ schema: T,
26
+ data: unknown
27
+ ): z.infer<T> {
28
+ return schema.parse(data);
29
+ }
30
+
31
+ // Safe parse with a schema, returns { success, data, error }
32
+ export function safeParse<T extends z.ZodSchema>(
33
+ schema: T,
34
+ data: unknown
35
+ ): z.SafeParseReturnType<unknown, z.infer<T>> {
36
+ return schema.safeParse(data);
37
+ }
38
+
39
+ // Validate pagination params
40
+ export const paginationSchema = z.object({
41
+ page: z.coerce.number().int().min(1).default(1),
42
+ limit: z.coerce.number().int().min(1).max(100).default(20),
43
+ });
44
+
45
+ export type Pagination = z.infer<typeof paginationSchema>;
46
+
47
+ // Validate sort params
48
+ export function createSortSchema<T extends string>(allowedFields: readonly T[]) {
49
+ return z.object({
50
+ sortBy: z.enum(allowedFields as unknown as [T, ...T[]]).optional(),
51
+ sortOrder: z.enum(['asc', 'desc']).default('asc'),
52
+ });
53
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "lib": ["ES2022"],
7
+ "strict": true,
8
+ "esModuleInterop": true,
9
+ "skipLibCheck": true,
10
+ "declaration": true,
11
+ "declarationMap": true,
12
+ "outDir": "./dist",
13
+ "rootDir": "./src"
14
+ },
15
+ "include": ["src/**/*"],
16
+ "exclude": ["node_modules", "dist"]
17
+ }
@@ -0,0 +1,3 @@
1
+ node_modules/
2
+ dist/
3
+ *.log
@@ -0,0 +1,13 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>{{PROJECT_NAME}}</title>
8
+ </head>
9
+ <body>
10
+ <div id="root"></div>
11
+ <script type="module" src="/src/main.tsx"></script>
12
+ </body>
13
+ </html>
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "@{{PROJECT_NAME}}/web",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "scripts": {
6
+ "dev": "vite",
7
+ "build": "tsc && vite build",
8
+ "preview": "vite preview",
9
+ "typecheck": "tsc --noEmit"
10
+ },
11
+ "dependencies": {
12
+ "@{{PROJECT_NAME}}/shared": "workspace:*",
13
+ "react": "^18.3.0",
14
+ "react-dom": "^18.3.0"
15
+ },
16
+ "devDependencies": {
17
+ "@types/react": "^18.3.0",
18
+ "@types/react-dom": "^18.3.0",
19
+ "@vitejs/plugin-react": "^4.3.0",
20
+ "typescript": "^5.7.0",
21
+ "vite": "^5.4.0"
22
+ }
23
+ }
@@ -0,0 +1,47 @@
1
+ import { useState, useEffect } from 'react';
2
+ import type { User } from '@{{PROJECT_NAME}}/shared';
3
+
4
+ function App() {
5
+ const [health, setHealth] = useState<{ status: string; timestamp: string } | null>(null);
6
+
7
+ useEffect(() => {
8
+ fetch('/api/health')
9
+ .then((res) => res.json())
10
+ .then(setHealth)
11
+ .catch(console.error);
12
+ }, []);
13
+
14
+ // Example: Type-safe user display (types from shared package)
15
+ const exampleUser: Partial<User> = {
16
+ email: 'user@example.com',
17
+ name: 'Example User',
18
+ };
19
+
20
+ return (
21
+ <div className="app">
22
+ <header>
23
+ <h1>{{PROJECT_NAME}}</h1>
24
+ <p>Fullstack app with shared types</p>
25
+ </header>
26
+
27
+ <main>
28
+ <section>
29
+ <h2>API Health</h2>
30
+ {health ? (
31
+ <pre>{JSON.stringify(health, null, 2)}</pre>
32
+ ) : (
33
+ <p>Loading...</p>
34
+ )}
35
+ </section>
36
+
37
+ <section>
38
+ <h2>Type-Safe User Example</h2>
39
+ <p>Types imported from @{{PROJECT_NAME}}/shared</p>
40
+ <pre>{JSON.stringify(exampleUser, null, 2)}</pre>
41
+ </section>
42
+ </main>
43
+ </div>
44
+ );
45
+ }
46
+
47
+ export default App;
@@ -0,0 +1,54 @@
1
+ * {
2
+ box-sizing: border-box;
3
+ margin: 0;
4
+ padding: 0;
5
+ }
6
+
7
+ body {
8
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
9
+ Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
10
+ line-height: 1.6;
11
+ color: #333;
12
+ background: #f5f5f5;
13
+ }
14
+
15
+ .app {
16
+ max-width: 800px;
17
+ margin: 0 auto;
18
+ padding: 2rem;
19
+ }
20
+
21
+ header {
22
+ margin-bottom: 2rem;
23
+ }
24
+
25
+ header h1 {
26
+ font-size: 2rem;
27
+ margin-bottom: 0.5rem;
28
+ }
29
+
30
+ header p {
31
+ color: #666;
32
+ }
33
+
34
+ section {
35
+ background: white;
36
+ border-radius: 8px;
37
+ padding: 1.5rem;
38
+ margin-bottom: 1.5rem;
39
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
40
+ }
41
+
42
+ section h2 {
43
+ font-size: 1.25rem;
44
+ margin-bottom: 1rem;
45
+ color: #444;
46
+ }
47
+
48
+ pre {
49
+ background: #f8f8f8;
50
+ padding: 1rem;
51
+ border-radius: 4px;
52
+ overflow-x: auto;
53
+ font-size: 0.875rem;
54
+ }
@@ -0,0 +1,10 @@
1
+ import { StrictMode } from 'react';
2
+ import { createRoot } from 'react-dom/client';
3
+ import App from './App';
4
+ import './index.css';
5
+
6
+ createRoot(document.getElementById('root')!).render(
7
+ <StrictMode>
8
+ <App />
9
+ </StrictMode>
10
+ );
@@ -0,0 +1 @@
1
+ /// <reference types="vite/client" />
@@ -0,0 +1,21 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "useDefineForClassFields": true,
5
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
6
+ "module": "ESNext",
7
+ "skipLibCheck": true,
8
+ "moduleResolution": "bundler",
9
+ "allowImportingTsExtensions": true,
10
+ "resolveJsonModule": true,
11
+ "isolatedModules": true,
12
+ "noEmit": true,
13
+ "jsx": "react-jsx",
14
+ "strict": true,
15
+ "noUnusedLocals": true,
16
+ "noUnusedParameters": true,
17
+ "noFallthroughCasesInSwitch": true
18
+ },
19
+ "include": ["src"],
20
+ "references": [{ "path": "./tsconfig.node.json" }]
21
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "compilerOptions": {
3
+ "composite": true,
4
+ "skipLibCheck": true,
5
+ "module": "ESNext",
6
+ "moduleResolution": "bundler",
7
+ "allowSyntheticDefaultImports": true,
8
+ "strict": true
9
+ },
10
+ "include": ["vite.config.ts"]
11
+ }
@@ -0,0 +1,15 @@
1
+ import { defineConfig } from 'vite';
2
+ import react from '@vitejs/plugin-react';
3
+
4
+ export default defineConfig({
5
+ plugins: [react()],
6
+ server: {
7
+ port: 5173,
8
+ proxy: {
9
+ '/api': {
10
+ target: 'http://localhost:{{API_PORT}}',
11
+ changeOrigin: true,
12
+ },
13
+ },
14
+ },
15
+ });
@@ -0,0 +1,13 @@
1
+ # Auth.js (auto-generated secret — replace if needed)
2
+ AUTH_SECRET={{AUTH_SECRET}}
3
+ AUTH_URL=http://localhost:3000
4
+
5
+ # GitHub OAuth (create app at https://github.com/settings/developers)
6
+ # Set callback URL to: http://localhost:3000/api/auth/callback/github
7
+ GITHUB_CLIENT_ID=
8
+ GITHUB_CLIENT_SECRET=
9
+
10
+ # Stripe (optional — add via `prim integrations add stripe`)
11
+ # STRIPE_SECRET_KEY=sk_test_...
12
+ # STRIPE_WEBHOOK_SECRET=whsec_...
13
+ # NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...
@@ -0,0 +1,32 @@
1
+ # dependencies
2
+ node_modules/
3
+ .pnp
4
+ .pnp.js
5
+
6
+ # next.js
7
+ .next/
8
+ out/
9
+
10
+ # production
11
+ build/
12
+ dist/
13
+
14
+ # env files
15
+ .env
16
+ .env.local
17
+ .env.*.local
18
+
19
+ # misc
20
+ .DS_Store
21
+ *.pem
22
+ *.log
23
+
24
+ # local db
25
+ local.db
26
+ local.db-journal
27
+
28
+ # turbo
29
+ .turbo
30
+
31
+ # vercel
32
+ .vercel