Merge pull request #475 from buster-so/nate/hot-fix-server-build

Server build update
This commit is contained in:
Nate Kelley 2025-07-10 12:50:47 -06:00 committed by GitHub
commit 252a892cae
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
62 changed files with 296 additions and 986 deletions

View File

@ -1,37 +0,0 @@
# Custom Base Image - Published base with tools + dependencies
# ================================================================
# This image contains:
# - Node 22 + npm
# - pnpm + bun
# - All workspace dependencies pre-installed
#
# Build and publish this multi-platform image:
# docker buildx build --platform linux/amd64,linux/arm64 \
# -f apps/server/Dockerfile.custom-base \
# -t ghcr.io/buster-so/server-base:latest \
# --push .
FROM node:22-alpine
WORKDIR /app
# Install tools: pnpm + bun
RUN echo "=== Installing build tools: $(date) ===" && \
apk add --no-cache curl bash git && \
npm install -g pnpm@9.15.0 turbo && \
curl -fsSL https://bun.sh/install | bash && \
echo "=== Tools installed: $(date) ==="
ENV PATH="/root/.bun/bin:$PATH"
# Copy package configuration files
COPY package.json pnpm-lock.yaml* turbo.json* pnpm-workspace.yaml* ./
COPY packages/ ./packages/
COPY apps/server/package.json ./apps/server/
# Install ALL dependencies (this is the heavy lifting)
RUN echo "=== Installing all dependencies: $(date) ===" && \
time pnpm install --ignore-scripts && \
echo "=== Dependencies installed: $(date) ===" && \
echo "Base image ready with $(du -sh node_modules | cut -f1) of dependencies"
# This base image is now ready to be published and reused!

View File

@ -1,51 +0,0 @@
# Buster Server Docker Workflow
Two-step build process for maximum efficiency
## 🔐 GitHub Container Registry Setup
First, authenticate with GitHub Container Registry:
- The password to login must be a github access token (made with token classic) with write and write package permissions
```bash
docker login ghcr.io
```
## 🏗️ Step 1: Build & Publish Base Image (Do this occasionally)
The base image contains all heavy dependencies and build tools.
```bash
# Build the base image with all dependencies
docker build -f apps/server/Dockerfile.custom-base -t ghcr.io/buster-so/server-base:latest .
# Publish to GitHub Container Registry
docker push ghcr.io/buster-so/server-base:latest
```
**When to rebuild the base:**
- Major dependency updates
- New packages added to workspace
- Tool version updates (pnpm, bun)
- Weekly/monthly maintenance
## ⚡ Step 2: Ultra-Fast App Builds (CI/CD)
Use the published base for lightning-fast builds:
```bash
# Pull the latest base (in CI/CD)
docker pull ghcr.io/buster-so/buster-server-base:latest
# Build your app (super fast!)
docker build -f apps/server/Dockerfile.ultra-fast -t buster-server:latest .
```
### Update Base Image (periodically):
```bash
# Rebuild and push new base
docker buildx build --platform linux/amd64,linux/arm64 \
-f apps/server/Dockerfile.custom-base \
-t ghcr.io/buster-so/server-base:latest \
--push .

View File

@ -7,11 +7,10 @@
}
},
"scripts": {
"prebuild": "bun run scripts/validate-env.js",
"build": "bun build src/index.ts --outdir ./dist --target bun --external pino-pretty",
"dev": "bun --max-old-space-size=512 run --hot src/index.ts",
"prebuild": "bun run scripts/validate-env.js && pnpm run typecheck",
"build": "tsup",
"dev": "tsup --watch",
"lint": "biome check",
"prod": "pnpm run build:vercel && pnpm run start:vercel",
"start": "bun dist/index.js",
"test": "vitest run",
"test:coverage": "vitest --coverage",
@ -31,13 +30,10 @@
"@trigger.dev/sdk": "catalog:",
"drizzle-orm": "catalog:",
"hono": "catalog:",
"hono-pino": "^0.8.0",
"hono-pino": "^0.9.1",
"pino": "^9.7.0",
"pino-pretty": "^13.0.0",
"tsup": "catalog:",
"zod": "catalog:"
},
"devDependencies": {
"@vercel/node": "^5.3.3",
"tsup": "^8.5.0"
}
}

View File

@ -16,15 +16,10 @@ import type {
ChatMessageResponseMessage,
ChatWithMessages,
} from '@buster/server-shared/chats';
import {
ChatError,
ChatErrorCode,
ReasoningMessageSchema,
ResponseMessageSchema,
} from '@buster/server-shared/chats';
import { ChatError, ChatErrorCode } from '@buster/server-shared/chats';
import { PostProcessingMessageSchema } from '@buster/server-shared/message';
import { and, eq, gte, isNull } from 'drizzle-orm';
import type { z } from 'zod/v4';
import type { z } from 'zod';
/**
* Validates a nullable JSONB field against a Zod schema

View File

@ -1,6 +1,10 @@
import { randomUUID } from 'node:crypto';
import { db, organizations, users, usersToOrganizations } from '@buster/database';
import type { Organization, User } from '@buster/database';
import type { User } from '@buster/database';
import type { UserOrganizationRole } from '@buster/server-shared/user';
import type { InferInsertModel, InferSelectModel } from 'drizzle-orm';
type Organization = InferSelectModel<typeof organizations>;
import { and, eq, isNull } from 'drizzle-orm';
export async function createTestUserInDb(userData: Partial<User> = {}): Promise<User> {
@ -48,26 +52,28 @@ export async function createTestOrganizationInDb(
const id = randomUUID();
// Use a unique domain for each organization to avoid conflicts with the trigger
const uniqueDomain = `test-${id.substring(0, 8)}.com`;
const org = {
const org: Organization = {
id,
name: `Test Organization ${id}`,
domains: orgData.domains !== undefined ? orgData.domains : [uniqueDomain],
restrictNewUserInvitations: false,
defaultRole: 'restricted_querier',
defaultRole: 'restricted_querier' as const,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
domain: null,
paymentRequired: true,
...orgData,
};
await db.insert(organizations).values(org);
return org as Organization;
return org;
}
export async function createTestOrgMemberInDb(
userId: string,
organizationId: string,
role = 'querier'
role: UserOrganizationRole = 'querier'
): Promise<void> {
// First check if there's already a membership
const existing = await db
@ -80,7 +86,7 @@ export async function createTestOrgMemberInDb(
await db.delete(usersToOrganizations).where(eq(usersToOrganizations.userId, userId));
}
const member = {
const member: InferInsertModel<typeof usersToOrganizations> = {
userId,
organizationId,
role,

View File

@ -1,4 +1,7 @@
import type { Organization, User } from '@buster/database';
import type { User, organizations } from '@buster/database';
import type { InferSelectModel } from 'drizzle-orm';
type Organization = InferSelectModel<typeof organizations>;
export function createTestUser(overrides?: Partial<User>): User {
const id = `test-user-${Math.random().toString(36).substring(7)}`;

View File

@ -111,8 +111,8 @@ async function updateOrganizationSettings(
restrictNewUserInvitations: updateData.restrictNewUserInvitations,
}),
...(updateData.defaultRole !== undefined &&
updateData.defaultRole !== 'none' && {
defaultRole: updateData.defaultRole as Exclude<OrganizationRole, 'none'>,
updateData.defaultRole && {
defaultRole: updateData.defaultRole,
}),
};

View File

@ -130,10 +130,10 @@ export class SlackHandler {
* Handle OAuth callback from Slack
*/
async handleOAuthCallback(c: Context): Promise<Response> {
try {
// Get base URL from environment
const baseUrl = process.env.BUSTER_URL || '';
// Get base URL from environment
const baseUrl = process.env.BUSTER_URL || '';
try {
// Get service instance (lazy initialization)
const slackOAuthService = this.getSlackOAuthService();

View File

@ -10,8 +10,8 @@ const SlackEnvSchema = z.object({
SERVER_URL: z.string().url(),
SLACK_INTEGRATION_ENABLED: z
.string()
.transform((val) => val === 'true')
.default('false'),
.default('false')
.transform((val) => val === 'true'),
});
// OAuth metadata schema

View File

@ -32,7 +32,13 @@ app.onError((err, c) => {
}
if (err instanceof z.ZodError) {
return c.text(err.errors.map((e) => e.message).join(', '), 400);
return c.json(
{
error: 'Validation Error',
message: err.issues.map((issue) => issue.message).join(', '),
},
400
);
}
return c.json(

View File

@ -0,0 +1,35 @@
import { defineConfig } from 'tsup';
export default defineConfig({
entry: ['src/index.ts'],
format: ['esm'],
target: 'node22', // Bun is compatible with recent Node.js versions
platform: 'node',
outDir: 'dist',
clean: true,
dts: false, // Disable for now due to TypeScript file list errors
sourcemap: true,
minify: false, // Don't minify for bun runtime
splitting: false,
shims: false, // Bun doesn't need shims
external: [
// Mark all workspace packages as external to avoid bundling them
'@buster/access-controls',
'@buster/database',
'@buster/server-shared',
'@buster/slack',
'@buster/test-utils',
'@buster/typescript-config',
'@buster/vitest-config',
],
noExternal: [
// Bundle these specific packages if needed
],
esbuildOptions(options) {
// Additional esbuild options for bun compatibility
options.keepNames = true; // Preserve function names for better debugging
},
onSuccess: async () => {
console.log('Build completed successfully!');
},
});

View File

@ -27,13 +27,13 @@
"@buster/typescript-config": "workspace:*",
"@buster/vitest-config": "workspace:*",
"@mastra/core": "catalog:",
"@trigger.dev/sdk": "4.0.0-v4-beta.22",
"@trigger.dev/sdk": "catalog:",
"ai": "catalog:",
"braintrust": "^0.0.206",
"braintrust": "catalog:",
"vitest": "catalog:",
"zod": "catalog:"
},
"devDependencies": {
"@trigger.dev/build": "4.0.0-v4-beta.22"
"@trigger.dev/build": "catalog:"
}
}

View File

@ -41,7 +41,7 @@
"@mastra/core": "catalog:",
"@mastra/loggers": "^0.10.3",
"ai": "catalog:",
"braintrust": "^0.0.206",
"braintrust": "catalog:",
"drizzle-orm": "catalog:",
"glob": "^11.0.3",
"minimatch": "^10.0.3",

View File

@ -66,7 +66,7 @@
"@buster/vitest-config": "workspace:*",
"@buster/typescript-config": "workspace:*",
"@buster/database": "workspace:*",
"zod": "catalog:"
"zod": "^3.0.0"
},
"devDependencies": {
"vitest": "catalog:"

View File

@ -1,4 +1,4 @@
import { z } from 'zod/v4';
import { z } from 'zod';
/**
* Error codes for chat operations

View File

@ -1,4 +1,4 @@
import { z } from 'zod/v4';
import { z } from 'zod';
import { PostProcessingMessageSchema } from '../message';
// Message role for chat messages

View File

@ -1,4 +1,4 @@
import { z } from 'zod/v4';
import { z } from 'zod';
import { ChatMessageSchema } from './chat-message.types';
const AssetType = z.enum(['metric_file', 'dashboard_file']);

View File

@ -1,4 +1,4 @@
import { z } from 'zod/v4';
import { z } from 'zod';
export const CurrencySchema = z.object({
code: z.string(),

View File

@ -1,4 +1,4 @@
import { z } from 'zod/v4';
import { z } from 'zod';
export const AssumptionClassificationSchema = z.enum([
'fieldMapping',

View File

@ -1,4 +1,4 @@
import { z } from 'zod/v4';
import { z } from 'zod';
import { getDefaults } from '../defaultHelpers';
// Goal line is a line that is drawn on the chart to represent a goal.

View File

@ -1,4 +1,4 @@
import { z } from 'zod/v4';
import { z } from 'zod';
export const BarAndLineAxisSchema = z
.object({

View File

@ -1,4 +1,4 @@
import { z } from 'zod/v4';
import { z } from 'zod';
import { BarAndLineAxisSchema } from './axisInterfaces';
import { BarSortBySchema } from './etcInterfaces';

View File

@ -1,4 +1,4 @@
import { z } from 'zod/v4';
import { z } from 'zod';
import { getDefaults } from '../defaultHelpers';
import { GoalLineSchema, TrendlineSchema } from './annotationInterfaces';
import { BarChartPropsSchema } from './barChartProps';

View File

@ -1,4 +1,4 @@
import { z } from 'zod/v4';
import { z } from 'zod';
import { getDefaults } from '../defaultHelpers';
export const LineColumnSettingsSchema = z.object({

View File

@ -1,4 +1,4 @@
import { z } from 'zod/v4';
import { z } from 'zod';
import { getDefaults } from '../defaultHelpers';
export const ColumnLabelFormatSchema = z.object({
@ -14,10 +14,8 @@ export const ColumnLabelFormatSchema = z.object({
.optional(
z
.number()
.check(
z.gte(0, 'Minimum fraction digits must be at least 0'),
z.lte(20, 'Minimum fraction digits must be at most 20')
)
.min(0, 'Minimum fraction digits must be at least 0')
.max(20, 'Minimum fraction digits must be at most 20')
)
.default(0),
// OPTIONAL: default is 2. This is essentially used to set a maximum number of decimal places. This will only apply if the format is set to 'number'.
@ -25,10 +23,8 @@ export const ColumnLabelFormatSchema = z.object({
.optional(
z
.number()
.check(
z.gte(0, 'Maximum fraction digits must be at least 0'),
z.lte(20, 'Maximum fraction digits must be at most 20')
)
.min(0, 'Maximum fraction digits must be at least 0')
.max(20, 'Maximum fraction digits must be at most 20')
)
.default(2),
// OPTIONAL: default is 1. This will only apply if the format is set to 'number', 'currency', or 'percent'.
@ -36,10 +32,8 @@ export const ColumnLabelFormatSchema = z.object({
.optional(
z
.number()
.check(
z.gte(0.001, 'Multiplier must be at least 0.001'),
z.lte(1000000, 'Multiplier must be at most 1,000,000')
)
.min(0.001, 'Multiplier must be at least 0.001')
.max(1000000, 'Multiplier must be at most 1,000,000')
)
.default(1),
// OPTIONAL: default is ''. This sets a prefix to go in front of each value found within the column. This will only apply if the format is set to 'number' or 'percent'.

View File

@ -1,4 +1,4 @@
import { z } from 'zod/v4';
import { z } from 'zod';
import { ComboChartAxisSchema } from './axisInterfaces';
export const ComboChartPropsSchema = z.object({

View File

@ -1,4 +1,4 @@
import { z } from 'zod/v4';
import { z } from 'zod';
export const ChartTypeSchema = z
.enum(['line', 'bar', 'scatter', 'pie', 'metric', 'table', 'combo'])

View File

@ -1,4 +1,4 @@
import { z } from 'zod/v4';
import { z } from 'zod';
// OPTIONAL: default is no sorting (none). The first item in the array will be the primary sort. The second item will be the secondary sort. This will only apply if the X axis type is not a date.
export const BarSortBySchema = z.array(z.enum(['asc', 'desc', 'none'])).default([]);

View File

@ -1,4 +1,4 @@
import { z } from 'zod/v4';
import { z } from 'zod';
export const LineChartPropsSchema = z.object({
// OPTIONAL: default is null. This will only apply if the columnVisualization is set to 'line'. If this is set to stack it will stack the lines on top of each other. The UI has this labeled as "Show as %"

View File

@ -1,4 +1,4 @@
import { z } from 'zod/v4';
import { z } from 'zod';
export const DerivedMetricTitleSchema = z.object({
// which column to use.

View File

@ -1,4 +1,4 @@
import { z } from 'zod/v4';
import { z } from 'zod';
import { PieChartAxisSchema } from './axisInterfaces';
import { PieSortBySchema } from './etcInterfaces';

View File

@ -1,4 +1,4 @@
import { z } from 'zod/v4';
import { z } from 'zod';
import { ScatterAxisSchema } from './axisInterfaces';
export const ScatterChartPropsSchema = z.object({

View File

@ -1,4 +1,4 @@
import { z } from 'zod/v4';
import { z } from 'zod';
export const TableChartPropsSchema = z.object({
tableColumnOrder: z.nullable(z.array(z.string())).default(null),

View File

@ -1,4 +1,4 @@
import { z } from 'zod/v4';
import { z } from 'zod';
/**
* Configuration options for the Y-axis of a chart.

View File

@ -1,5 +1,5 @@
import { describe, expect, it } from 'vitest';
import { z } from 'zod/v4';
import { z } from 'zod';
import { getDefaults, getDefaultsPartial } from './defaultHelpers';
describe('getDefaults', () => {

View File

@ -1,4 +1,4 @@
import type { z } from 'zod/v4';
import type { z } from 'zod';
/**
* Extracts all default values from a Zod schema.

View File

@ -1,4 +1,4 @@
import { z } from 'zod/v4';
import { z } from 'zod';
export const ColumnMetaDataSchema = z.object({
name: z.string(),

View File

@ -1,4 +1,4 @@
import { z } from 'zod/v4';
import { z } from 'zod';
import { ShareConfigSchema, VerificationStatusSchema } from '../share';
import { ChartConfigPropsSchema } from './charts';
import { DEFAULT_CHART_CONFIG } from './charts/chartConfigProps';

View File

@ -1,4 +1,4 @@
import { z } from 'zod/v4';
import { z } from 'zod';
import { VerificationStatusSchema } from '../share';
export const MetricListItemSchema = z.object({

View File

@ -1,4 +1,4 @@
import { z } from 'zod/v4';
import { z } from 'zod';
import { ShareRoleSchema, VerificationStatusSchema } from '../share';
import { ChartConfigPropsSchema } from './charts';

View File

@ -1,4 +1,4 @@
import { z } from 'zod/v4';
import { z } from 'zod';
import { DataMetadataSchema, DataResultSchema } from './metadata.type';
import { MetricSchema } from './metric.types';
import { MetricListItemSchema } from './metrics-list.types';

View File

@ -1,4 +1,4 @@
import { z } from 'zod/v4';
import { z } from 'zod';
import { OrganizationRoleSchema } from './roles.types';
export const OrganizationSchema = z.object({

View File

@ -1,5 +1,5 @@
import type { userOrganizationRoleEnum } from '@buster/database'; //we import as type to avoid postgres dependency in the frontend ☹️
import { z } from 'zod/v4';
import { z } from 'zod';
type OrganizationRoleBase = (typeof userOrganizationRoleEnum.enumValues)[number];
@ -14,6 +14,8 @@ export const OrganizationRoleEnum: Record<OrganizationRoleBase, OrganizationRole
restricted_querier: 'restricted_querier',
});
export const OrganizationRoleSchema = z.enum(Object.values(OrganizationRoleEnum));
export const OrganizationRoleSchema = z.enum(
Object.values(OrganizationRoleEnum) as [OrganizationRoleBase, ...OrganizationRoleBase[]]
);
export type OrganizationRole = z.infer<typeof OrganizationRoleSchema>;

View File

@ -1,4 +1,4 @@
import { z } from 'zod/v4';
import { z } from 'zod';
import { OrganizationRoleSchema } from './roles.types';
export const LineageUserItemTypeSchema = z.enum(['user', 'datasets', 'permissionGroups']);

View File

@ -1,4 +1,4 @@
import { z } from 'zod/v4';
import { z } from 'zod';
import { OrganizationRoleSchema } from '../organization';
export const UpdateInviteLinkRequestSchema = z.object({
@ -43,7 +43,7 @@ export const UpdateWorkspaceSettingsRequestSchema = z.object({
restrict_new_user_invitations: z.boolean().optional(),
default_role: OrganizationRoleSchema.optional(),
// this can either be a uuid or "all"
default_datasets_ids: z.array(z.union([z.uuid(), z.literal('all')])).optional(),
default_datasets_ids: z.array(z.union([z.string(), z.literal('all')])).optional(),
});
export type UpdateWorkspaceSettingsRequest = z.infer<typeof UpdateWorkspaceSettingsRequestSchema>;

View File

@ -1,4 +1,4 @@
import { z } from 'zod/v4';
import { z } from 'zod';
import { OrganizationRoleSchema } from '../organization';
export const GetInviteLinkResponseSchema = z.object({

View File

@ -1,4 +1,4 @@
import { z } from 'zod/v4';
import { z } from 'zod';
export const ShareRoleSchema = z.enum([
'owner', //owner of the asset

View File

@ -1,4 +1,4 @@
import { z } from 'zod/v4';
import { z } from 'zod';
export const VerificationStatusSchema = z.enum([
'notRequested',

View File

@ -1,4 +1,4 @@
import { z } from 'zod/v4';
import { z } from 'zod';
// POST /api/v2/slack/auth/init
export const InitiateOAuthSchema = z

View File

@ -1,4 +1,4 @@
import { z } from 'zod/v4';
import { z } from 'zod';
export const CreateTeamRequestSchema = z.object({
name: z.string(),

View File

@ -1,4 +1,4 @@
import { z } from 'zod/v4';
import { z } from 'zod';
import { TeamSchema } from './teams.types';
export const TeamListResponseSchema = z.array(TeamSchema);

View File

@ -1,5 +1,5 @@
import type { teamRoleEnum } from '@buster/database'; //we import as type to avoid postgres dependency in the frontend ☹️
import { z } from 'zod/v4';
import { z } from 'zod';
import { SharingSettingSchema } from '../user/sharing-setting.types';
type TeamRoleBase = (typeof teamRoleEnum.enumValues)[number] | 'none';
@ -8,7 +8,9 @@ const TeamRoleEnums: Record<TeamRoleBase, TeamRoleBase> = Object.freeze({
manager: 'manager',
member: 'member',
});
export const TeamRoleSchema = z.enum(Object.values(TeamRoleEnums));
export const TeamRoleSchema = z.enum(
Object.values(TeamRoleEnums) as [TeamRoleBase, ...TeamRoleBase[]]
);
export type TeamRole = z.infer<typeof TeamRoleSchema>;

View File

@ -1,4 +1,4 @@
import { z } from 'zod/v4';
import { z } from 'zod';
import { ShareAssetTypeSchema } from '../share';
export const UserFavoriteSchema = z.object({

View File

@ -1,4 +1,4 @@
import { z } from 'zod/v4';
import { z } from 'zod';
import { OrganizationRoleSchema } from '../organization/roles.types';
import { ShareAssetTypeSchema } from '../share';

View File

@ -1,4 +1,4 @@
import { z } from 'zod/v4';
import { z } from 'zod';
import { OrganizationSchema } from '../organization/organization.types';
import { OrganizationRoleSchema } from '../organization/roles.types';
import { TeamSchema } from '../teams/teams.types';

View File

@ -1,11 +1,10 @@
import type { userOrganizationRoleEnum } from '@buster/database'; //we import as type to avoid postgres dependency in the frontend ☹️
import { z } from 'zod/v4';
import { z } from 'zod';
type UserOrganizationRoleBase = (typeof userOrganizationRoleEnum.enumValues)[number] | 'none';
type UserOrganizationRoleBase = (typeof userOrganizationRoleEnum.enumValues)[number];
const UserOrganizationRoleEnums: Record<UserOrganizationRoleBase, UserOrganizationRoleBase> =
Object.freeze({
none: 'none',
viewer: 'viewer',
workspace_admin: 'workspace_admin',
data_admin: 'data_admin',
@ -13,6 +12,11 @@ const UserOrganizationRoleEnums: Record<UserOrganizationRoleBase, UserOrganizati
restricted_querier: 'restricted_querier',
});
export const UserOrganizationRoleSchema = z.enum(Object.values(UserOrganizationRoleEnums));
export const UserOrganizationRoleSchema = z.enum(
Object.values(UserOrganizationRoleEnums) as [
UserOrganizationRoleBase,
...UserOrganizationRoleBase[],
]
);
export type UserOrganizationRole = z.infer<typeof UserOrganizationRoleSchema>;

View File

@ -1,5 +1,5 @@
import type { sharingSettingEnum } from '@buster/database'; //we import as type to avoid postgres dependency in the frontend ☹️
import { z } from 'zod/v4';
import { z } from 'zod';
type SharingSettingBase = (typeof sharingSettingEnum.enumValues)[number] | 'none';
@ -9,6 +9,9 @@ const SharingSettingEnums: Record<SharingSettingBase, SharingSettingBase> = Obje
team: 'team',
organization: 'organization',
});
export const SharingSettingSchema = z.enum(Object.values(SharingSettingEnums));
const test = Object.values(SharingSettingEnums) as [SharingSettingBase, ...SharingSettingBase[]];
export const SharingSettingSchema = z.enum(test);
export type SharingSetting = z.infer<typeof SharingSettingSchema>;

View File

@ -1,4 +1,4 @@
import { z } from 'zod/v4';
import { z } from 'zod';
import type { UserFavorite } from './favorites.types';
import type { UserOrganizationRole } from './roles.types';

View File

@ -35,6 +35,6 @@
"@buster/typescript-config": "workspace:*",
"@buster/vitest-config": "workspace:*",
"ai": "catalog:",
"zod": "catalog:"
"zod": "^3.0.0"
}
}

View File

@ -2,21 +2,22 @@
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
"allowImportingTsExtensions": false,
"allowSyntheticDefaultImports": true,
"composite": true,
"declaration": true,
"declarationMap": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"incremental": true,
"inlineSources": false,
"isolatedModules": true,
"lib": ["ESNext"],
"module": "ESNext",
"moduleResolution": "bundler",
"noEmit": false,
"preserveWatchOutput": true,
"skipLibCheck": true,
"target": "ES2022",
"lib": ["ESNext"]
"strict": true,
"target": "ES2022"
},
"exclude": ["node_modules"],
"extends": "./type-checking.json"

File diff suppressed because it is too large Load Diff

View File

@ -10,17 +10,19 @@ packages:
- "apps/api"
catalog:
"@mastra/core": "^0.10.8"
"@supabase/supabase-js": "^2.50.0"
"@trigger.dev/build": "^4.0.0-v4-beta.22"
"@trigger.dev/sdk": "^4.0.0-v4-beta.22"
ai: "^4.0.0"
axios: "^1.10.0"
"vite-tsconfig-paths": "^5.1.4"
"braintrust": "^0.0.209"
drizzle-orm: "^0.44.2"
hono: "^4.8.0"
"@mastra/core": "^0.10.8"
pg: "^8.16.2"
tsup: "^8.5.0"
uuid: "^11.0.0"
vitest: "3.2.4"
vite: "6.3.5"
zod: "^3.0.0"
"vite-tsconfig-paths": "^5.1.4"
vitest: "3.2.4"
zod: "^3.25.0"