openship 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/.env.example +22 -0
  2. package/.nvmrc +1 -0
  3. package/.prettierrc +7 -0
  4. package/CONTRIBUTING.md +40 -0
  5. package/README.md +164 -0
  6. package/apps/api/.env.example +30 -0
  7. package/apps/api/Dockerfile +24 -0
  8. package/apps/api/package.json +37 -0
  9. package/apps/api/src/app.ts +26 -0
  10. package/apps/api/src/config/env.ts +39 -0
  11. package/apps/api/src/config/index.ts +1 -0
  12. package/apps/api/src/index.ts +8 -0
  13. package/apps/api/src/middleware/auth.ts +24 -0
  14. package/apps/api/src/middleware/error-handler.ts +21 -0
  15. package/apps/api/src/middleware/index.ts +3 -0
  16. package/apps/api/src/middleware/rate-limiter.ts +26 -0
  17. package/apps/api/src/modules/auth/auth.controller.ts +26 -0
  18. package/apps/api/src/modules/auth/auth.routes.ts +10 -0
  19. package/apps/api/src/modules/auth/auth.schema.ts +12 -0
  20. package/apps/api/src/modules/auth/auth.service.ts +23 -0
  21. package/apps/api/src/modules/billing/billing.controller.ts +60 -0
  22. package/apps/api/src/modules/billing/billing.routes.ts +26 -0
  23. package/apps/api/src/modules/billing/billing.schema.ts +9 -0
  24. package/apps/api/src/modules/billing/billing.service.ts +33 -0
  25. package/apps/api/src/modules/deployments/deployment.controller.ts +35 -0
  26. package/apps/api/src/modules/deployments/deployment.routes.ts +11 -0
  27. package/apps/api/src/modules/deployments/deployment.schema.ts +7 -0
  28. package/apps/api/src/modules/deployments/deployment.service.ts +23 -0
  29. package/apps/api/src/modules/domains/domain.controller.ts +21 -0
  30. package/apps/api/src/modules/domains/domain.routes.ts +9 -0
  31. package/apps/api/src/modules/domains/domain.service.ts +19 -0
  32. package/apps/api/src/modules/health/health.routes.ts +10 -0
  33. package/apps/api/src/modules/projects/project.controller.ts +29 -0
  34. package/apps/api/src/modules/projects/project.routes.ts +10 -0
  35. package/apps/api/src/modules/projects/project.schema.ts +9 -0
  36. package/apps/api/src/modules/projects/project.service.ts +23 -0
  37. package/apps/api/src/modules/webhooks/webhook.controller.ts +22 -0
  38. package/apps/api/src/modules/webhooks/webhook.routes.ts +12 -0
  39. package/apps/api/src/modules/webhooks/webhook.service.ts +11 -0
  40. package/apps/api/tsconfig.json +12 -0
  41. package/apps/cli/package.json +28 -0
  42. package/apps/cli/src/commands/deploy.ts +11 -0
  43. package/apps/cli/src/commands/init.ts +8 -0
  44. package/apps/cli/src/commands/login.ts +8 -0
  45. package/apps/cli/src/commands/logs.ts +9 -0
  46. package/apps/cli/src/index.ts +21 -0
  47. package/apps/cli/src/lib/api-client.ts +35 -0
  48. package/apps/cli/tsconfig.json +12 -0
  49. package/apps/dashboard/Dockerfile +24 -0
  50. package/apps/dashboard/next.config.mjs +6 -0
  51. package/apps/dashboard/package.json +28 -0
  52. package/apps/dashboard/src/app/(auth)/layout.tsx +6 -0
  53. package/apps/dashboard/src/app/(auth)/login/page.tsx +11 -0
  54. package/apps/dashboard/src/app/(auth)/register/page.tsx +10 -0
  55. package/apps/dashboard/src/app/(dashboard)/billing/page.tsx +10 -0
  56. package/apps/dashboard/src/app/(dashboard)/deployments/page.tsx +8 -0
  57. package/apps/dashboard/src/app/(dashboard)/domains/page.tsx +8 -0
  58. package/apps/dashboard/src/app/(dashboard)/layout.tsx +37 -0
  59. package/apps/dashboard/src/app/(dashboard)/monitoring/page.tsx +8 -0
  60. package/apps/dashboard/src/app/(dashboard)/projects/page.tsx +8 -0
  61. package/apps/dashboard/src/app/(dashboard)/settings/page.tsx +8 -0
  62. package/apps/dashboard/src/app/globals.css +3 -0
  63. package/apps/dashboard/src/app/layout.tsx +15 -0
  64. package/apps/dashboard/src/app/page.tsx +6 -0
  65. package/apps/dashboard/tsconfig.json +11 -0
  66. package/apps/web/Dockerfile +24 -0
  67. package/apps/web/next.config.mjs +6 -0
  68. package/apps/web/package.json +26 -0
  69. package/apps/web/src/app/(marketing)/docs/page.tsx +10 -0
  70. package/apps/web/src/app/(marketing)/layout.tsx +24 -0
  71. package/apps/web/src/app/(marketing)/page.tsx +2 -0
  72. package/apps/web/src/app/(marketing)/pricing/page.tsx +10 -0
  73. package/apps/web/src/app/globals.css +3 -0
  74. package/apps/web/src/app/layout.tsx +16 -0
  75. package/apps/web/src/app/page.tsx +10 -0
  76. package/apps/web/tsconfig.json +11 -0
  77. package/docker-compose.yml +87 -0
  78. package/package.json +31 -0
  79. package/packages/adapters/package.json +27 -0
  80. package/packages/adapters/src/base-adapter.ts +49 -0
  81. package/packages/adapters/src/docker-adapter.ts +43 -0
  82. package/packages/adapters/src/index.ts +4 -0
  83. package/packages/adapters/src/oblien-adapter.ts +46 -0
  84. package/packages/adapters/src/registry.ts +22 -0
  85. package/packages/adapters/tsconfig.json +9 -0
  86. package/packages/core/package.json +27 -0
  87. package/packages/core/src/constants.ts +28 -0
  88. package/packages/core/src/errors.ts +49 -0
  89. package/packages/core/src/index.ts +4 -0
  90. package/packages/core/src/types.ts +47 -0
  91. package/packages/core/src/utils.ts +35 -0
  92. package/packages/core/tsconfig.json +9 -0
  93. package/packages/db/package.json +32 -0
  94. package/packages/db/prisma/schema.prisma +160 -0
  95. package/packages/db/src/index.ts +11 -0
  96. package/packages/db/tsconfig.json +9 -0
  97. package/packages/ui/package.json +38 -0
  98. package/packages/ui/src/components/badge.tsx +29 -0
  99. package/packages/ui/src/components/button.tsx +44 -0
  100. package/packages/ui/src/components/card.tsx +26 -0
  101. package/packages/ui/src/components/status-dot.tsx +25 -0
  102. package/packages/ui/src/globals.css +18 -0
  103. package/packages/ui/src/index.tsx +8 -0
  104. package/packages/ui/src/lib/cn.ts +7 -0
  105. package/packages/ui/tsconfig.json +9 -0
  106. package/pnpm-workspace.yaml +4 -0
  107. package/tooling/tsconfig/base.json +19 -0
  108. package/tooling/tsconfig/nextjs.json +10 -0
  109. package/tooling/tsconfig/node.json +10 -0
  110. package/tooling/tsconfig/package.json +7 -0
  111. package/turbo.json +32 -0
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Shared TypeScript types used across apps and packages.
3
+ */
4
+
5
+ /* ---------- Deployment ---------- */
6
+
7
+ export type DeploymentStatus =
8
+ | "queued"
9
+ | "building"
10
+ | "deploying"
11
+ | "ready"
12
+ | "failed"
13
+ | "cancelled";
14
+
15
+ export type Environment = "production" | "preview" | "development";
16
+
17
+ export type Framework = "nextjs" | "node" | "static" | "docker";
18
+
19
+ export type AdapterType = "docker" | "oblien";
20
+
21
+ /* ---------- Billing ---------- */
22
+
23
+ export type PlanId = "free" | "pro" | "team";
24
+
25
+ export type SubscriptionStatus = "active" | "canceled" | "past_due";
26
+
27
+ export type UsageMetric = "build_minutes" | "bandwidth_gb" | "deployments";
28
+
29
+ /* ---------- Auth ---------- */
30
+
31
+ export type UserRole = "user" | "admin";
32
+
33
+ export type TeamRole = "owner" | "admin" | "member";
34
+
35
+ /* ---------- API Responses ---------- */
36
+
37
+ export interface ApiResponse<T = unknown> {
38
+ data?: T;
39
+ error?: string;
40
+ message?: string;
41
+ }
42
+
43
+ export interface PaginatedResponse<T> extends ApiResponse<T[]> {
44
+ total: number;
45
+ page: number;
46
+ perPage: number;
47
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Shared utility functions.
3
+ */
4
+
5
+ /** Generate a URL-safe slug from a string */
6
+ export function slugify(text: string): string {
7
+ return text
8
+ .toLowerCase()
9
+ .replace(/[^\w\s-]/g, "")
10
+ .replace(/[\s_]+/g, "-")
11
+ .replace(/^-+|-+$/g, "")
12
+ .slice(0, 100);
13
+ }
14
+
15
+ /** Format bytes to human-readable string */
16
+ export function formatBytes(bytes: number, decimals = 2): string {
17
+ if (bytes === 0) return "0 B";
18
+ const k = 1024;
19
+ const sizes = ["B", "KB", "MB", "GB", "TB"];
20
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
21
+ return `${parseFloat((bytes / Math.pow(k, i)).toFixed(decimals))} ${sizes[i]}`;
22
+ }
23
+
24
+ /** Format duration in seconds to human-readable string */
25
+ export function formatDuration(seconds: number): string {
26
+ if (seconds < 60) return `${seconds}s`;
27
+ const m = Math.floor(seconds / 60);
28
+ const s = seconds % 60;
29
+ return s > 0 ? `${m}m ${s}s` : `${m}m`;
30
+ }
31
+
32
+ /** Sleep for a given number of milliseconds */
33
+ export function sleep(ms: number): Promise<void> {
34
+ return new Promise((resolve) => setTimeout(resolve, ms));
35
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "openship-tsconfig/node.json",
3
+ "compilerOptions": {
4
+ "baseUrl": ".",
5
+ "outDir": "dist"
6
+ },
7
+ "include": ["src/**/*.ts"],
8
+ "exclude": ["node_modules", "dist"]
9
+ }
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "openship-db",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "import": "./dist/index.js",
10
+ "types": "./dist/index.d.ts"
11
+ }
12
+ },
13
+ "scripts": {
14
+ "build": "tsup src/index.ts --format esm --dts --clean",
15
+ "dev": "tsup src/index.ts --format esm --dts --watch",
16
+ "lint": "tsc --noEmit",
17
+ "db:generate": "prisma generate",
18
+ "db:push": "prisma db push",
19
+ "db:migrate": "prisma migrate dev",
20
+ "db:studio": "prisma studio",
21
+ "clean": "rm -rf dist .turbo node_modules"
22
+ },
23
+ "dependencies": {
24
+ "@prisma/client": "^5.14.0"
25
+ },
26
+ "devDependencies": {
27
+ "openship-tsconfig": "workspace:*",
28
+ "prisma": "^5.14.0",
29
+ "tsup": "^8.1.0",
30
+ "typescript": "^5.4.0"
31
+ }
32
+ }
@@ -0,0 +1,160 @@
1
+ // Prisma Schema for Openship
2
+ // SQLite for local/self-hosted · Postgres for cloud
3
+ // Change the datasource provider as needed.
4
+
5
+ generator client {
6
+ provider = "prisma-client-js"
7
+ }
8
+
9
+ datasource db {
10
+ provider = "sqlite" // Switch to "postgresql" for cloud
11
+ url = env("DATABASE_URL")
12
+ }
13
+
14
+ // ─── Auth ────────────────────────────────────────────────────────────────────
15
+
16
+ model User {
17
+ id String @id @default(cuid())
18
+ email String @unique
19
+ name String?
20
+ passwordHash String
21
+ avatarUrl String?
22
+ role String @default("user") // user | admin
23
+ createdAt DateTime @default(now())
24
+ updatedAt DateTime @updatedAt
25
+
26
+ projects Project[]
27
+ teams TeamMember[]
28
+ subscription Subscription?
29
+ apiTokens ApiToken[]
30
+ }
31
+
32
+ model ApiToken {
33
+ id String @id @default(cuid())
34
+ name String
35
+ token String @unique
36
+ userId String
37
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
38
+ expiresAt DateTime?
39
+ createdAt DateTime @default(now())
40
+ }
41
+
42
+ // ─── Teams ───────────────────────────────────────────────────────────────────
43
+
44
+ model Team {
45
+ id String @id @default(cuid())
46
+ name String
47
+ slug String @unique
48
+ createdAt DateTime @default(now())
49
+ updatedAt DateTime @updatedAt
50
+
51
+ members TeamMember[]
52
+ projects Project[]
53
+ }
54
+
55
+ model TeamMember {
56
+ id String @id @default(cuid())
57
+ role String @default("member") // owner | admin | member
58
+ userId String
59
+ teamId String
60
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
61
+ team Team @relation(fields: [teamId], references: [id], onDelete: Cascade)
62
+
63
+ @@unique([userId, teamId])
64
+ }
65
+
66
+ // ─── Projects ────────────────────────────────────────────────────────────────
67
+
68
+ model Project {
69
+ id String @id @default(cuid())
70
+ name String
71
+ slug String
72
+ repoUrl String?
73
+ framework String? // nextjs | node | static | docker
74
+ rootDir String @default("/")
75
+ buildCmd String?
76
+ outputDir String?
77
+ createdAt DateTime @default(now())
78
+ updatedAt DateTime @updatedAt
79
+
80
+ userId String?
81
+ user User? @relation(fields: [userId], references: [id])
82
+ teamId String?
83
+ team Team? @relation(fields: [teamId], references: [id])
84
+
85
+ deployments Deployment[]
86
+ domains Domain[]
87
+ envVars EnvVar[]
88
+
89
+ @@unique([slug, teamId])
90
+ }
91
+
92
+ model EnvVar {
93
+ id String @id @default(cuid())
94
+ key String
95
+ value String
96
+ target String @default("production") // production | preview | development
97
+ projectId String
98
+ project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
99
+
100
+ @@unique([key, target, projectId])
101
+ }
102
+
103
+ // ─── Deployments ─────────────────────────────────────────────────────────────
104
+
105
+ model Deployment {
106
+ id String @id @default(cuid())
107
+ status String @default("queued") // queued | building | deploying | ready | failed | cancelled
108
+ environment String @default("production") // production | preview
109
+ branch String @default("main")
110
+ commitSha String?
111
+ commitMsg String?
112
+ url String?
113
+ buildLogs String? // stored as text; could be moved to object storage
114
+ duration Int? // build duration in seconds
115
+ createdAt DateTime @default(now())
116
+ updatedAt DateTime @updatedAt
117
+
118
+ projectId String
119
+ project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
120
+ }
121
+
122
+ // ─── Domains ─────────────────────────────────────────────────────────────────
123
+
124
+ model Domain {
125
+ id String @id @default(cuid())
126
+ domain String @unique
127
+ verified Boolean @default(false)
128
+ sslStatus String @default("pending") // pending | active | failed
129
+ createdAt DateTime @default(now())
130
+
131
+ projectId String
132
+ project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
133
+ }
134
+
135
+ // ─── Billing (Cloud only) ────────────────────────────────────────────────────
136
+
137
+ model Subscription {
138
+ id String @id @default(cuid())
139
+ plan String @default("free") // free | pro | team
140
+ status String @default("active") // active | canceled | past_due
141
+ stripeCustomerId String?
142
+ stripeSubId String?
143
+ currentPeriodStart DateTime?
144
+ currentPeriodEnd DateTime?
145
+ createdAt DateTime @default(now())
146
+ updatedAt DateTime @updatedAt
147
+
148
+ userId String @unique
149
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
150
+ }
151
+
152
+ model UsageRecord {
153
+ id String @id @default(cuid())
154
+ metric String // build_minutes | bandwidth_gb | deployments
155
+ quantity Float
156
+ period String // "2026-03" (year-month)
157
+ createdAt DateTime @default(now())
158
+
159
+ userId String
160
+ }
@@ -0,0 +1,11 @@
1
+ import { PrismaClient } from "@prisma/client";
2
+
3
+ const globalForPrisma = globalThis as unknown as { prisma: PrismaClient | undefined };
4
+
5
+ export const db = globalForPrisma.prisma ?? new PrismaClient();
6
+
7
+ if (process.env.NODE_ENV !== "production") {
8
+ globalForPrisma.prisma = db;
9
+ }
10
+
11
+ export * from "@prisma/client";
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "openship-tsconfig/node.json",
3
+ "compilerOptions": {
4
+ "baseUrl": ".",
5
+ "outDir": "dist"
6
+ },
7
+ "include": ["src/**/*.ts"],
8
+ "exclude": ["node_modules", "dist"]
9
+ }
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "openship-ui",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "import": "./dist/index.js",
10
+ "types": "./dist/index.d.ts"
11
+ },
12
+ "./globals.css": "./src/globals.css"
13
+ },
14
+ "scripts": {
15
+ "build": "tsup src/index.tsx --format esm --dts --clean --external react --external react-dom",
16
+ "dev": "tsup src/index.tsx --format esm --dts --watch --external react --external react-dom",
17
+ "lint": "tsc --noEmit",
18
+ "clean": "rm -rf dist .turbo node_modules"
19
+ },
20
+ "dependencies": {
21
+ "class-variance-authority": "^0.7.0",
22
+ "clsx": "^2.1.0",
23
+ "tailwind-merge": "^2.3.0"
24
+ },
25
+ "peerDependencies": {
26
+ "react": "^18.0.0",
27
+ "react-dom": "^18.0.0"
28
+ },
29
+ "devDependencies": {
30
+ "openship-tsconfig": "workspace:*",
31
+ "@types/react": "^18.3.0",
32
+ "@types/react-dom": "^18.3.0",
33
+ "react": "^18.3.0",
34
+ "react-dom": "^18.3.0",
35
+ "tsup": "^8.1.0",
36
+ "typescript": "^5.4.0"
37
+ }
38
+ }
@@ -0,0 +1,29 @@
1
+ import * as React from "react";
2
+ import { cva, type VariantProps } from "class-variance-authority";
3
+ import { cn } from "../lib/cn";
4
+
5
+ const badgeVariants = cva(
6
+ "inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium",
7
+ {
8
+ variants: {
9
+ variant: {
10
+ default: "bg-primary/10 text-primary",
11
+ success: "bg-green-100 text-green-800",
12
+ warning: "bg-yellow-100 text-yellow-800",
13
+ error: "bg-red-100 text-red-800",
14
+ muted: "bg-muted text-muted-foreground",
15
+ },
16
+ },
17
+ defaultVariants: {
18
+ variant: "default",
19
+ },
20
+ },
21
+ );
22
+
23
+ export interface BadgeProps
24
+ extends React.HTMLAttributes<HTMLSpanElement>,
25
+ VariantProps<typeof badgeVariants> {}
26
+
27
+ export function Badge({ className, variant, ...props }: BadgeProps) {
28
+ return <span className={cn(badgeVariants({ variant }), className)} {...props} />;
29
+ }
@@ -0,0 +1,44 @@
1
+ import * as React from "react";
2
+ import { cva, type VariantProps } from "class-variance-authority";
3
+ import { cn } from "../lib/cn";
4
+
5
+ const buttonVariants = cva(
6
+ "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
7
+ {
8
+ variants: {
9
+ variant: {
10
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
11
+ secondary: "bg-muted text-foreground hover:bg-muted/80",
12
+ outline: "border border-border bg-transparent hover:bg-muted",
13
+ ghost: "hover:bg-muted",
14
+ destructive: "bg-red-600 text-white hover:bg-red-700",
15
+ },
16
+ size: {
17
+ sm: "h-8 px-3 text-xs",
18
+ md: "h-10 px-4",
19
+ lg: "h-12 px-6 text-base",
20
+ },
21
+ },
22
+ defaultVariants: {
23
+ variant: "default",
24
+ size: "md",
25
+ },
26
+ },
27
+ );
28
+
29
+ export interface ButtonProps
30
+ extends React.ButtonHTMLAttributes<HTMLButtonElement>,
31
+ VariantProps<typeof buttonVariants> {}
32
+
33
+ export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
34
+ ({ className, variant, size, ...props }, ref) => {
35
+ return (
36
+ <button
37
+ className={cn(buttonVariants({ variant, size }), className)}
38
+ ref={ref}
39
+ {...props}
40
+ />
41
+ );
42
+ },
43
+ );
44
+ Button.displayName = "Button";
@@ -0,0 +1,26 @@
1
+ import * as React from "react";
2
+ import { cn } from "../lib/cn";
3
+
4
+ export function Card({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
5
+ return <div className={cn("rounded-lg border bg-white shadow-sm", className)} {...props} />;
6
+ }
7
+
8
+ export function CardHeader({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
9
+ return <div className={cn("flex flex-col space-y-1.5 p-6", className)} {...props} />;
10
+ }
11
+
12
+ export function CardTitle({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) {
13
+ return <h3 className={cn("text-lg font-semibold", className)} {...props} />;
14
+ }
15
+
16
+ export function CardDescription({ className, ...props }: React.HTMLAttributes<HTMLParagraphElement>) {
17
+ return <p className={cn("text-sm text-muted-foreground", className)} {...props} />;
18
+ }
19
+
20
+ export function CardContent({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
21
+ return <div className={cn("p-6 pt-0", className)} {...props} />;
22
+ }
23
+
24
+ export function CardFooter({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
25
+ return <div className={cn("flex items-center p-6 pt-0", className)} {...props} />;
26
+ }
@@ -0,0 +1,25 @@
1
+ import { cn } from "../lib/cn";
2
+ import type { DeploymentStatus } from "openship-core";
3
+
4
+ const statusColors: Record<string, string> = {
5
+ queued: "bg-gray-400",
6
+ building: "bg-yellow-400 animate-pulse",
7
+ deploying: "bg-blue-400 animate-pulse",
8
+ ready: "bg-green-500",
9
+ failed: "bg-red-500",
10
+ cancelled: "bg-gray-400",
11
+ };
12
+
13
+ interface StatusDotProps {
14
+ status: DeploymentStatus;
15
+ className?: string;
16
+ }
17
+
18
+ export function StatusDot({ status, className }: StatusDotProps) {
19
+ return (
20
+ <span
21
+ className={cn("inline-block h-2.5 w-2.5 rounded-full", statusColors[status], className)}
22
+ title={status}
23
+ />
24
+ );
25
+ }
@@ -0,0 +1,18 @@
1
+ /* Shared design tokens and base Tailwind styles */
2
+ @tailwind base;
3
+ @tailwind components;
4
+ @tailwind utilities;
5
+
6
+ @layer base {
7
+ :root {
8
+ --background: 0 0% 100%;
9
+ --foreground: 222.2 84% 4.9%;
10
+ --primary: 222.2 47.4% 11.2%;
11
+ --primary-foreground: 210 40% 98%;
12
+ --muted: 210 40% 96.1%;
13
+ --muted-foreground: 215.4 16.3% 46.9%;
14
+ --border: 214.3 31.8% 91.4%;
15
+ --ring: 222.2 84% 4.9%;
16
+ --radius: 0.5rem;
17
+ }
18
+ }
@@ -0,0 +1,8 @@
1
+ /* ---------- Components ---------- */
2
+ export { Button } from "./components/button";
3
+ export { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from "./components/card";
4
+ export { Badge } from "./components/badge";
5
+ export { StatusDot } from "./components/status-dot";
6
+
7
+ /* ---------- Utils ---------- */
8
+ export { cn } from "./lib/cn";
@@ -0,0 +1,7 @@
1
+ import { clsx, type ClassValue } from "clsx";
2
+ import { twMerge } from "tailwind-merge";
3
+
4
+ /** Merge Tailwind classes without conflicts */
5
+ export function cn(...inputs: ClassValue[]) {
6
+ return twMerge(clsx(inputs));
7
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "openship-tsconfig/nextjs.json",
3
+ "compilerOptions": {
4
+ "baseUrl": ".",
5
+ "outDir": "dist"
6
+ },
7
+ "include": ["src/**/*.ts", "src/**/*.tsx"],
8
+ "exclude": ["node_modules", "dist"]
9
+ }
@@ -0,0 +1,4 @@
1
+ packages:
2
+ - "apps/*"
3
+ - "packages/*"
4
+ - "tooling/*"
@@ -0,0 +1,19 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/tsconfig",
3
+ "compilerOptions": {
4
+ "strict": true,
5
+ "esModuleInterop": true,
6
+ "forceConsistentCasingInFileNames": true,
7
+ "skipLibCheck": true,
8
+ "target": "ES2022",
9
+ "module": "ESNext",
10
+ "moduleResolution": "bundler",
11
+ "resolveJsonModule": true,
12
+ "isolatedModules": true,
13
+ "incremental": true,
14
+ "declaration": true,
15
+ "declarationMap": true,
16
+ "sourceMap": true
17
+ },
18
+ "exclude": ["node_modules", "dist"]
19
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/tsconfig",
3
+ "extends": "./base.json",
4
+ "compilerOptions": {
5
+ "lib": ["DOM", "DOM.Iterable", "ES2022"],
6
+ "jsx": "preserve",
7
+ "noEmit": true,
8
+ "plugins": [{ "name": "next" }]
9
+ }
10
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/tsconfig",
3
+ "extends": "./base.json",
4
+ "compilerOptions": {
5
+ "lib": ["ES2022"],
6
+ "module": "ESNext",
7
+ "outDir": "dist",
8
+ "noEmit": false
9
+ }
10
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "name": "openship-tsconfig",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "license": "MIT",
6
+ "files": ["base.json", "nextjs.json", "node.json"]
7
+ }
package/turbo.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "$schema": "https://turbo.build/schema.json",
3
+ "globalDependencies": ["**/.env.*local"],
4
+ "tasks": {
5
+ "build": {
6
+ "dependsOn": ["^build"],
7
+ "outputs": ["dist/**", ".next/**", "!.next/cache/**"]
8
+ },
9
+ "dev": {
10
+ "cache": false,
11
+ "persistent": true
12
+ },
13
+ "lint": {
14
+ "dependsOn": ["^build"]
15
+ },
16
+ "test": {
17
+ "dependsOn": ["^build"]
18
+ },
19
+ "clean": {
20
+ "cache": false
21
+ },
22
+ "db:generate": {
23
+ "cache": false
24
+ },
25
+ "db:push": {
26
+ "cache": false
27
+ },
28
+ "db:migrate": {
29
+ "cache": false
30
+ }
31
+ }
32
+ }