@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.
- package/dist/generators/crud/templates/drizzle-table.ts.template +12 -0
- package/dist/generators/crud/templates/handlers.ts.template +136 -0
- package/dist/generators/crud/templates/routes.ts.template +21 -0
- package/dist/generators/crud/templates/schema.ts.template +20 -0
- package/dist/index.js +9618 -0
- package/dist/integrations/analytics/providers/amplitude/templates/src/analytics/index.ts.template +79 -0
- package/dist/integrations/analytics/providers/amplitude/templates/src/analytics/types.ts.template +12 -0
- package/dist/integrations/analytics/providers/mixpanel/templates/src/analytics/index.ts.template +62 -0
- package/dist/integrations/analytics/providers/mixpanel/templates/src/analytics/types.ts.template +12 -0
- package/dist/integrations/analytics/providers/posthog/templates/src/analytics/index.ts.template +67 -0
- package/dist/integrations/analytics/providers/posthog/templates/src/analytics/types.ts.template +12 -0
- package/dist/integrations/auth/providers/authjoy/templates/api/src/middleware/auth.ts.template +89 -0
- package/dist/integrations/auth/providers/authjoy/templates/api/src/routes/auth.ts.template +27 -0
- package/dist/integrations/auth/providers/authjoy/templates/web/src/components/AuthProvider.tsx.template +40 -0
- package/dist/integrations/auth/providers/authjoy/templates/web/src/hooks/use-auth.ts.template +71 -0
- package/dist/integrations/auth/providers/authjoy/templates/web/src/lib/auth.ts.template +59 -0
- package/dist/integrations/auth/providers/authjoy/templates/web/src/pages/account.tsx.template +84 -0
- package/dist/integrations/auth/providers/authjoy/templates/web/src/pages/login.tsx.template +73 -0
- package/dist/integrations/cache/providers/memory/templates/src/cache/index.ts.template +43 -0
- package/dist/integrations/cache/providers/memory/templates/src/cache/types.ts.template +3 -0
- package/dist/integrations/cache/providers/redis/templates/src/cache/index.ts.template +37 -0
- package/dist/integrations/cache/providers/redis/templates/src/cache/types.ts.template +3 -0
- package/dist/integrations/cache/providers/valkey/templates/src/cache/index.ts.template +38 -0
- package/dist/integrations/cache/providers/valkey/templates/src/cache/types.ts.template +3 -0
- package/dist/integrations/db/providers/postgres/templates/drizzle.config.ts.template +10 -0
- package/dist/integrations/db/providers/postgres/templates/src/db/index.ts.template +13 -0
- package/dist/integrations/db/providers/postgres/templates/src/db/migrate.ts.template +19 -0
- package/dist/integrations/db/providers/postgres/templates/src/db/schema/index.ts.template +1 -0
- package/dist/integrations/db/providers/postgres/templates/src/db/schema/users.ts.template +12 -0
- package/dist/integrations/db/providers/postgres/templates/src/db/seed.ts.template +28 -0
- package/dist/integrations/db/providers/sqlite/templates/drizzle.config.ts.template +10 -0
- package/dist/integrations/db/providers/sqlite/templates/src/db/index.ts.template +10 -0
- package/dist/integrations/db/providers/sqlite/templates/src/db/schema/index.ts.template +1 -0
- package/dist/integrations/db/providers/sqlite/templates/src/db/schema/users.ts.template +12 -0
- package/dist/integrations/db/providers/sqlite/templates/src/db/seed.ts.template +28 -0
- package/dist/integrations/db/providers/supabase/templates/drizzle.config.ts.template +10 -0
- package/dist/integrations/db/providers/supabase/templates/src/db/index.ts.template +13 -0
- package/dist/integrations/db/providers/supabase/templates/src/db/migrate.ts.template +19 -0
- package/dist/integrations/db/providers/supabase/templates/src/db/schema/index.ts.template +1 -0
- package/dist/integrations/db/providers/supabase/templates/src/db/schema/users.ts.template +12 -0
- package/dist/integrations/db/providers/supabase/templates/src/db/seed.ts.template +28 -0
- package/dist/integrations/db/providers/turso/templates/drizzle.config.ts.template +11 -0
- package/dist/integrations/db/providers/turso/templates/src/db/index.ts.template +14 -0
- package/dist/integrations/db/providers/turso/templates/src/db/schema/index.ts.template +1 -0
- package/dist/integrations/db/providers/turso/templates/src/db/schema/users.ts.template +12 -0
- package/dist/integrations/db/providers/turso/templates/src/db/seed.ts.template +28 -0
- package/dist/integrations/email/providers/nodemailer/templates/src/email/index.ts.template +24 -0
- package/dist/integrations/email/providers/nodemailer/templates/src/email/templates/index.ts.template +1 -0
- package/dist/integrations/email/providers/nodemailer/templates/src/email/templates/welcome.ts.template +7 -0
- package/dist/integrations/email/providers/nodemailer/templates/src/email/types.ts.template +7 -0
- package/dist/integrations/email/providers/resend/templates/src/email/index.ts.template +18 -0
- package/dist/integrations/email/providers/resend/templates/src/email/templates/index.ts.template +1 -0
- package/dist/integrations/email/providers/resend/templates/src/email/templates/welcome.ts.template +7 -0
- package/dist/integrations/email/providers/resend/templates/src/email/types.ts.template +7 -0
- package/dist/integrations/email/providers/sendgrid/templates/src/email/index.ts.template +16 -0
- package/dist/integrations/email/providers/sendgrid/templates/src/email/templates/index.ts.template +1 -0
- package/dist/integrations/email/providers/sendgrid/templates/src/email/templates/welcome.ts.template +7 -0
- package/dist/integrations/email/providers/sendgrid/templates/src/email/types.ts.template +7 -0
- package/dist/integrations/flags/providers/local/templates/api/src/lib/flags.ts.template +97 -0
- package/dist/integrations/flags/providers/local/templates/api/src/routes/flags.ts.template +36 -0
- package/dist/integrations/flags/providers/local/templates/flags.json.template +8 -0
- package/dist/integrations/flags/providers/local/templates/web/src/hooks/use-flag.ts.template +60 -0
- package/dist/integrations/logging/providers/axiom/templates/src/logging/index.ts.template +56 -0
- package/dist/integrations/logging/providers/axiom/templates/src/logging/types.ts.template +5 -0
- package/dist/integrations/logging/providers/pino/templates/src/logging/index.ts.template +21 -0
- package/dist/integrations/logging/providers/pino/templates/src/logging/types.ts.template +5 -0
- package/dist/integrations/logging/providers/winston/templates/src/logging/index.ts.template +30 -0
- package/dist/integrations/logging/providers/winston/templates/src/logging/types.ts.template +5 -0
- package/dist/integrations/monitor/providers/datadog/templates/src/monitor/index.ts.template +78 -0
- package/dist/integrations/monitor/providers/datadog/templates/src/monitor/types.ts.template +12 -0
- package/dist/integrations/monitor/providers/newrelic/templates/src/monitor/index.ts.template +60 -0
- package/dist/integrations/monitor/providers/newrelic/templates/src/monitor/types.ts.template +12 -0
- package/dist/integrations/monitor/providers/sentry/templates/src/monitor/index.ts.template +70 -0
- package/dist/integrations/monitor/providers/sentry/templates/src/monitor/types.ts.template +12 -0
- package/dist/integrations/queue/providers/bullmq/templates/src/queue/index.ts.template +56 -0
- package/dist/integrations/queue/providers/bullmq/templates/src/queue/types.ts.template +10 -0
- package/dist/integrations/queue/providers/memory/templates/src/queue/index.ts.template +73 -0
- package/dist/integrations/queue/providers/memory/templates/src/queue/types.ts.template +10 -0
- package/dist/integrations/queue/providers/pgboss/templates/src/queue/index.ts.template +34 -0
- package/dist/integrations/queue/providers/pgboss/templates/src/queue/types.ts.template +10 -0
- package/dist/integrations/ratelimit/providers/memory/templates/src/ratelimit/index.ts.template +95 -0
- package/dist/integrations/ratelimit/providers/memory/templates/src/ratelimit/types.ts.template +12 -0
- package/dist/integrations/ratelimit/providers/rate-limiter-flexible/templates/src/ratelimit/index.ts.template +80 -0
- package/dist/integrations/ratelimit/providers/rate-limiter-flexible/templates/src/ratelimit/types.ts.template +12 -0
- package/dist/integrations/ratelimit/providers/upstash/templates/src/ratelimit/index.ts.template +67 -0
- package/dist/integrations/ratelimit/providers/upstash/templates/src/ratelimit/types.ts.template +12 -0
- package/dist/integrations/schedule/providers/bullmq/templates/src/schedule/index.ts.template +81 -0
- package/dist/integrations/schedule/providers/bullmq/templates/src/schedule/types.ts.template +10 -0
- package/dist/integrations/schedule/providers/croner/templates/src/schedule/index.ts.template +47 -0
- package/dist/integrations/schedule/providers/croner/templates/src/schedule/types.ts.template +10 -0
- package/dist/integrations/schedule/providers/node-cron/templates/src/schedule/index.ts.template +45 -0
- package/dist/integrations/schedule/providers/node-cron/templates/src/schedule/types.ts.template +10 -0
- package/dist/integrations/search/providers/algolia/templates/src/search/index.ts.template +52 -0
- package/dist/integrations/search/providers/algolia/templates/src/search/types.ts.template +18 -0
- package/dist/integrations/search/providers/meilisearch/templates/src/search/index.ts.template +49 -0
- package/dist/integrations/search/providers/meilisearch/templates/src/search/types.ts.template +18 -0
- package/dist/integrations/search/providers/typesense/templates/src/search/index.ts.template +71 -0
- package/dist/integrations/search/providers/typesense/templates/src/search/types.ts.template +35 -0
- package/dist/integrations/storage/providers/local/templates/src/storage/index.ts.template +69 -0
- package/dist/integrations/storage/providers/local/templates/src/storage/types.ts.template +12 -0
- package/dist/integrations/storage/providers/r2/templates/src/storage/index.ts.template +80 -0
- package/dist/integrations/storage/providers/r2/templates/src/storage/types.ts.template +12 -0
- package/dist/integrations/storage/providers/s3/templates/src/storage/index.ts.template +78 -0
- package/dist/integrations/storage/providers/s3/templates/src/storage/types.ts.template +12 -0
- package/dist/integrations/stripe/templates/api/src/lib/stripe.ts.template +259 -0
- package/dist/integrations/stripe/templates/api/src/routes/stripe-webhooks.ts.template +284 -0
- package/dist/integrations/stripe/templates/api/stripe.config.ts.template +178 -0
- package/dist/integrations/stripe/templates/shared/src/pricing.ts.template +117 -0
- package/dist/integrations/stripe/templates/shared/src/stripe-types.ts.template +133 -0
- package/dist/integrations/stripe/templates/web/src/components/billing-settings.tsx.template +123 -0
- package/dist/integrations/stripe/templates/web/src/components/pricing-cards.tsx.template +115 -0
- package/dist/integrations/stripe/templates/web/src/pages/pricing.tsx.template +95 -0
- package/dist/templates/api/fastify/.env.example.template +7 -0
- package/dist/templates/api/fastify/.gitignore.template +24 -0
- package/dist/templates/api/fastify/package.json.template +23 -0
- package/dist/templates/api/fastify/src/index.ts.template +52 -0
- package/dist/templates/api/fastify/src/lib/env.ts.template +20 -0
- package/dist/templates/api/fastify/src/routes/health.ts.template +12 -0
- package/dist/templates/api/fastify/tsconfig.json.template +18 -0
- package/dist/templates/api/fastify-postgres/.env.example.template +10 -0
- package/dist/templates/api/fastify-postgres/drizzle.config.ts.template +10 -0
- package/dist/templates/api/fastify-postgres/package.json.template +16 -0
- package/dist/templates/api/fastify-postgres/src/db/index.ts.template +9 -0
- package/dist/templates/api/fastify-postgres/src/db/schema/users.ts.template +12 -0
- package/dist/templates/api/fastify-sqlite/.env.example.template +10 -0
- package/dist/templates/api/fastify-sqlite/drizzle.config.ts.template +10 -0
- package/dist/templates/api/fastify-sqlite/package.json.template +16 -0
- package/dist/templates/api/fastify-sqlite/src/db/index.ts.template +6 -0
- package/dist/templates/api/fastify-sqlite/src/db/schema/users.ts.template +12 -0
- package/dist/templates/api/fastify-supabase/.env.example.template +10 -0
- package/dist/templates/api/fastify-supabase/drizzle.config.ts.template +10 -0
- package/dist/templates/api/fastify-supabase/package.json.template +15 -0
- package/dist/templates/api/fastify-supabase/src/db/index.ts.template +9 -0
- package/dist/templates/api/fastify-supabase/src/db/schema/users.ts.template +12 -0
- package/dist/templates/api/fastify-turso/.env.example.template +11 -0
- package/dist/templates/api/fastify-turso/drizzle.config.ts.template +11 -0
- package/dist/templates/api/fastify-turso/package.json.template +15 -0
- package/dist/templates/api/fastify-turso/src/db/index.ts.template +10 -0
- package/dist/templates/api/fastify-turso/src/db/schema/users.ts.template +12 -0
- package/dist/templates/fullstack/api/.env.example.template +10 -0
- package/dist/templates/fullstack/api/.gitignore.template +4 -0
- package/dist/templates/fullstack/api/drizzle.config.ts.template +14 -0
- package/dist/templates/fullstack/api/package.json.template +33 -0
- package/dist/templates/fullstack/api/src/db/index.ts.template +13 -0
- package/dist/templates/fullstack/api/src/db/schema/api-keys.ts.template +19 -0
- package/dist/templates/fullstack/api/src/db/schema/audit-logs.ts.template +23 -0
- package/dist/templates/fullstack/api/src/db/schema/index.ts.template +8 -0
- package/dist/templates/fullstack/api/src/db/schema/invites.ts.template +19 -0
- package/dist/templates/fullstack/api/src/db/schema/memberships.ts.template +16 -0
- package/dist/templates/fullstack/api/src/db/schema/organizations.ts.template +13 -0
- package/dist/templates/fullstack/api/src/db/schema/plans.ts.template +29 -0
- package/dist/templates/fullstack/api/src/db/schema/subscriptions.ts.template +38 -0
- package/dist/templates/fullstack/api/src/db/schema/users.ts.template +14 -0
- package/dist/templates/fullstack/api/src/index.ts.template +54 -0
- package/dist/templates/fullstack/api/src/lib/env.ts.template +22 -0
- package/dist/templates/fullstack/api/src/routes/health.ts.template +14 -0
- package/dist/templates/fullstack/api/tsconfig.json.template +15 -0
- package/dist/templates/fullstack/root/.gitignore.template +26 -0
- package/dist/templates/fullstack/root/package.json.template +15 -0
- package/dist/templates/fullstack/root/pnpm-workspace.yaml.template +3 -0
- package/dist/templates/fullstack/root/turbo.json.template +17 -0
- package/dist/templates/fullstack/shared/package.json.template +36 -0
- package/dist/templates/fullstack/shared/src/index.ts.template +8 -0
- package/dist/templates/fullstack/shared/src/schemas/api-key.ts.template +28 -0
- package/dist/templates/fullstack/shared/src/schemas/audit-log.ts.template +41 -0
- package/dist/templates/fullstack/shared/src/schemas/index.ts.template +8 -0
- package/dist/templates/fullstack/shared/src/schemas/invite.ts.template +25 -0
- package/dist/templates/fullstack/shared/src/schemas/membership.ts.template +20 -0
- package/dist/templates/fullstack/shared/src/schemas/organization.ts.template +18 -0
- package/dist/templates/fullstack/shared/src/schemas/plan.ts.template +38 -0
- package/dist/templates/fullstack/shared/src/schemas/subscription.ts.template +56 -0
- package/dist/templates/fullstack/shared/src/schemas/user.ts.template +21 -0
- package/dist/templates/fullstack/shared/src/types/index.ts.template +75 -0
- package/dist/templates/fullstack/shared/src/validators/index.ts.template +53 -0
- package/dist/templates/fullstack/shared/tsconfig.json.template +17 -0
- package/dist/templates/fullstack/web/.gitignore.template +3 -0
- package/dist/templates/fullstack/web/index.html.template +13 -0
- package/dist/templates/fullstack/web/package.json.template +23 -0
- package/dist/templates/fullstack/web/src/App.tsx.template +47 -0
- package/dist/templates/fullstack/web/src/index.css.template +54 -0
- package/dist/templates/fullstack/web/src/main.tsx.template +10 -0
- package/dist/templates/fullstack/web/src/vite-env.d.ts.template +1 -0
- package/dist/templates/fullstack/web/tsconfig.json.template +21 -0
- package/dist/templates/fullstack/web/tsconfig.node.json.template +11 -0
- package/dist/templates/fullstack/web/vite.config.ts.template +15 -0
- package/dist/templates/hosted/root/.env.local.template +13 -0
- package/dist/templates/hosted/root/.gitignore.template +32 -0
- package/dist/templates/hosted/root/CLAUDE.md.template +139 -0
- package/dist/templates/hosted/root/drizzle.config.ts.template +10 -0
- package/dist/templates/hosted/root/next.config.ts.template +15 -0
- package/dist/templates/hosted/root/package.json.template +40 -0
- package/dist/templates/hosted/root/postcss.config.mjs.template +9 -0
- package/dist/templates/hosted/root/primstack.config.json.template +5 -0
- package/dist/templates/hosted/root/tailwind.config.ts.template +14 -0
- package/dist/templates/hosted/root/tsconfig.json.template +25 -0
- package/dist/templates/hosted/root/wrangler.toml.template +9 -0
- package/dist/templates/hosted/src/app/actions/example.ts.template +50 -0
- package/dist/templates/hosted/src/app/api/health/route.ts.template +5 -0
- package/dist/templates/hosted/src/app/auth/login/page.tsx.template +32 -0
- package/dist/templates/hosted/src/app/globals.css.template +59 -0
- package/dist/templates/hosted/src/app/layout.tsx.template +24 -0
- package/dist/templates/hosted/src/app/page.tsx.template +34 -0
- package/dist/templates/hosted/src/db/migrations/0000_initial.sql.template +43 -0
- package/dist/templates/hosted/src/db/schema.ts.template +52 -0
- package/dist/templates/hosted/src/env.d.ts.template +10 -0
- package/dist/templates/hosted/src/instrumentation.ts.template +6 -0
- package/dist/templates/hosted/src/lib/auth.ts.template +35 -0
- package/dist/templates/hosted/src/lib/db.ts.template +17 -0
- package/dist/templates/hosted/src/middleware.ts.template +6 -0
- package/package.json +46 -0
|
@@ -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,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 @@
|
|
|
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,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
|