perf(api,web,db): refactor and optimize for enterprise readiness
- Add missing @@index([userId]) on Account and Session models (auth query perf) - Batch holiday-auto-import to eliminate N+1 query pattern (O(n) → O(1)) - Reduce SessionProvider refetchInterval from 5min to 15min - Fix Cache-Control catch-all to stop blocking static asset caching - Decompose assistant-tools.ts (2,562 → 809 lines) into callers, helpers, access-control modules - Add @next/bundle-analyzer for data-driven bundle optimization - Add @react-pdf/renderer to optimizePackageImports - Add safety caps (take limits) on unbounded findMany queries Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+15
-3
@@ -7,7 +7,13 @@ const nextConfig: NextConfig = {
|
|||||||
outputFileTracingRoot: path.resolve(__dirname, "../.."),
|
outputFileTracingRoot: path.resolve(__dirname, "../.."),
|
||||||
devIndicators: false,
|
devIndicators: false,
|
||||||
experimental: {
|
experimental: {
|
||||||
optimizePackageImports: ["recharts", "date-fns", "framer-motion", "@capakraken/shared"],
|
optimizePackageImports: [
|
||||||
|
"recharts",
|
||||||
|
"date-fns",
|
||||||
|
"framer-motion",
|
||||||
|
"@capakraken/shared",
|
||||||
|
"@react-pdf/renderer",
|
||||||
|
],
|
||||||
},
|
},
|
||||||
transpilePackages: [
|
transpilePackages: [
|
||||||
"@capakraken/api",
|
"@capakraken/api",
|
||||||
@@ -60,8 +66,8 @@ const nextConfig: NextConfig = {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Catch-all for error pages and any remaining routes
|
// Catch-all for error pages and remaining routes (exclude static assets)
|
||||||
source: "/:path*",
|
source: "/((?!_next/static|_next/image|favicon.ico).*)",
|
||||||
headers: [
|
headers: [
|
||||||
{ key: "Cache-Control", value: "no-store, no-cache, must-revalidate" },
|
{ key: "Cache-Control", value: "no-store, no-cache, must-revalidate" },
|
||||||
{ key: "Pragma", value: "no-cache" },
|
{ key: "Pragma", value: "no-cache" },
|
||||||
@@ -88,7 +94,13 @@ const nextConfig: NextConfig = {
|
|||||||
// Only wrap with Sentry in production — the worker.js crash in dev mode
|
// Only wrap with Sentry in production — the worker.js crash in dev mode
|
||||||
// (vendor-chunks/lib/worker.js MODULE_NOT_FOUND) makes the dev server unstable
|
// (vendor-chunks/lib/worker.js MODULE_NOT_FOUND) makes the dev server unstable
|
||||||
// Sentry only in production — dynamic import avoids side effects in dev
|
// Sentry only in production — dynamic import avoids side effects in dev
|
||||||
|
// Bundle analyzer — run with ANALYZE=true to inspect client/server chunks
|
||||||
let exportedConfig: NextConfig = nextConfig;
|
let exportedConfig: NextConfig = nextConfig;
|
||||||
|
if (process.env.ANALYZE === "true") {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||||
|
const withBundleAnalyzer = require("@next/bundle-analyzer")({ enabled: true });
|
||||||
|
exportedConfig = withBundleAnalyzer(exportedConfig);
|
||||||
|
}
|
||||||
if (process.env.NODE_ENV === "production") {
|
if (process.env.NODE_ENV === "production") {
|
||||||
try {
|
try {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||||
|
|||||||
@@ -49,6 +49,7 @@
|
|||||||
"zod": "^3.23.8"
|
"zod": "^3.23.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@next/bundle-analyzer": "^15.5.15",
|
||||||
"@axe-core/playwright": "^4.11.1",
|
"@axe-core/playwright": "^4.11.1",
|
||||||
"@capakraken/eslint-config": "workspace:*",
|
"@capakraken/eslint-config": "workspace:*",
|
||||||
"@capakraken/tsconfig": "workspace:*",
|
"@capakraken/tsconfig": "workspace:*",
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ export function TRPCProvider({ children }: { children: React.ReactNode }) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SessionProvider refetchInterval={5 * 60}>
|
<SessionProvider refetchInterval={15 * 60}>
|
||||||
<trpc.Provider client={trpcClient} queryClient={queryClient}>
|
<trpc.Provider client={trpcClient} queryClient={queryClient}>
|
||||||
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
||||||
</trpc.Provider>
|
</trpc.Provider>
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ describe("assistant broadcast send tool", () => {
|
|||||||
|
|
||||||
expect(db.user.findMany).toHaveBeenCalledWith({
|
expect(db.user.findMany).toHaveBeenCalledWith({
|
||||||
select: { id: true },
|
select: { id: true },
|
||||||
|
take: 10_000,
|
||||||
});
|
});
|
||||||
expect(create).not.toHaveBeenCalled();
|
expect(create).not.toHaveBeenCalled();
|
||||||
expect(JSON.parse(result.content)).toEqual({
|
expect(JSON.parse(result.content)).toEqual({
|
||||||
@@ -80,6 +81,7 @@ describe("assistant broadcast send tool", () => {
|
|||||||
|
|
||||||
expect(db.user.findMany).toHaveBeenCalledWith({
|
expect(db.user.findMany).toHaveBeenCalledWith({
|
||||||
select: { id: true },
|
select: { id: true },
|
||||||
|
take: 10_000,
|
||||||
});
|
});
|
||||||
expect(create).toHaveBeenCalledWith({
|
expect(create).toHaveBeenCalledWith({
|
||||||
data: expect.objectContaining({
|
data: expect.objectContaining({
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ export async function autoImportPublicHolidays(
|
|||||||
country: { select: { code: true } },
|
country: { select: { code: true } },
|
||||||
metroCity: { select: { name: true } },
|
metroCity: { select: { name: true } },
|
||||||
},
|
},
|
||||||
|
take: 50_000,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (resources.length === 0) {
|
if (resources.length === 0) {
|
||||||
@@ -103,43 +104,68 @@ export async function autoImportPublicHolidays(
|
|||||||
if (holidays.length === 0) continue;
|
if (holidays.length === 0) continue;
|
||||||
const resourceIds = groupedResources.map((resource: { id: string }) => resource.id);
|
const resourceIds = groupedResources.map((resource: { id: string }) => resource.id);
|
||||||
|
|
||||||
|
// Batch: collect all holiday dates, fetch existing records in one query
|
||||||
|
const holidayDates = holidays.map((h) => new Date(h.date));
|
||||||
|
|
||||||
|
const allExisting: MinimalVacation[] = await db.vacation.findMany({
|
||||||
|
where: {
|
||||||
|
resourceId: { in: resourceIds },
|
||||||
|
type: "PUBLIC_HOLIDAY",
|
||||||
|
startDate: { in: holidayDates },
|
||||||
|
endDate: { in: holidayDates },
|
||||||
|
},
|
||||||
|
select: { resourceId: true, startDate: true, endDate: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
// Map: date ISO string → set of resourceIds that already have the holiday
|
||||||
|
const existingByDate = new Map<string, Set<string>>();
|
||||||
|
for (const v of allExisting) {
|
||||||
|
const key = v.startDate.toISOString();
|
||||||
|
const set = existingByDate.get(key) ?? new Set();
|
||||||
|
set.add(v.resourceId);
|
||||||
|
existingByDate.set(key, set);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build all new records for a single createMany call
|
||||||
|
const allNewRecords: Array<{
|
||||||
|
resourceId: string;
|
||||||
|
type: string;
|
||||||
|
status: string;
|
||||||
|
startDate: Date;
|
||||||
|
endDate: Date;
|
||||||
|
note: string;
|
||||||
|
isHalfDay: boolean;
|
||||||
|
approvedAt: Date;
|
||||||
|
}> = [];
|
||||||
|
const approvedAt = new Date();
|
||||||
|
|
||||||
for (const holiday of holidays) {
|
for (const holiday of holidays) {
|
||||||
const holidayDate = new Date(holiday.date);
|
const holidayDate = new Date(holiday.date);
|
||||||
|
const dateKey = holidayDate.toISOString();
|
||||||
// Find existing records for this date + type to skip duplicates
|
const existingResourceIds = existingByDate.get(dateKey) ?? new Set();
|
||||||
const existing: MinimalVacation[] = await db.vacation.findMany({
|
|
||||||
where: {
|
|
||||||
resourceId: { in: resourceIds },
|
|
||||||
type: "PUBLIC_HOLIDAY",
|
|
||||||
startDate: holidayDate,
|
|
||||||
endDate: holidayDate,
|
|
||||||
},
|
|
||||||
select: { resourceId: true, startDate: true, endDate: true },
|
|
||||||
});
|
|
||||||
|
|
||||||
const existingResourceIds = new Set(existing.map((v: MinimalVacation) => v.resourceId));
|
|
||||||
const newResourceIds = resourceIds.filter((id: string) => !existingResourceIds.has(id));
|
|
||||||
|
|
||||||
totalSkipped += existingResourceIds.size;
|
totalSkipped += existingResourceIds.size;
|
||||||
|
|
||||||
if (newResourceIds.length === 0) continue;
|
for (const resourceId of resourceIds) {
|
||||||
|
if (existingResourceIds.has(resourceId)) continue;
|
||||||
const records = newResourceIds.map((resourceId: string) => ({
|
allNewRecords.push({
|
||||||
resourceId,
|
resourceId,
|
||||||
type: "PUBLIC_HOLIDAY",
|
type: "PUBLIC_HOLIDAY",
|
||||||
status: "APPROVED",
|
status: "APPROVED",
|
||||||
startDate: holidayDate,
|
startDate: holidayDate,
|
||||||
endDate: holidayDate,
|
endDate: holidayDate,
|
||||||
note: holiday.name,
|
note: holiday.name,
|
||||||
isHalfDay: false,
|
isHalfDay: false,
|
||||||
approvedAt: new Date(),
|
approvedAt,
|
||||||
}));
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allNewRecords.length > 0) {
|
||||||
const result = await db.vacation.createMany({
|
const result = await db.vacation.createMany({
|
||||||
data: records,
|
data: allNewRecords,
|
||||||
skipDuplicates: true,
|
skipDuplicates: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
totalCreated += result.count;
|
totalCreated += result.count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,9 @@ export async function resolveRecipients(
|
|||||||
case "role": {
|
case "role": {
|
||||||
// Find all users with the given systemRole
|
// Find all users with the given systemRole
|
||||||
const roleUsers = await db.user.findMany({
|
const roleUsers = await db.user.findMany({
|
||||||
where: { systemRole: targetValue as "ADMIN" | "MANAGER" | "CONTROLLER" | "USER" | "VIEWER" },
|
where: {
|
||||||
|
systemRole: targetValue as "ADMIN" | "MANAGER" | "CONTROLLER" | "USER" | "VIEWER",
|
||||||
|
},
|
||||||
select: { id: true },
|
select: { id: true },
|
||||||
});
|
});
|
||||||
userIds = roleUsers.map((u) => u.id);
|
userIds = roleUsers.map((u) => u.id);
|
||||||
@@ -36,9 +38,7 @@ export async function resolveRecipients(
|
|||||||
where: { projectId: targetValue, status: { not: "CANCELLED" } },
|
where: { projectId: targetValue, status: { not: "CANCELLED" } },
|
||||||
select: { resource: { select: { userId: true } } },
|
select: { resource: { select: { userId: true } } },
|
||||||
});
|
});
|
||||||
userIds = assignments
|
userIds = assignments.map((a) => a.resource.userId).filter((id): id is string => !!id);
|
||||||
.map((a) => a.resource.userId)
|
|
||||||
.filter((id): id is string => !!id);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,9 +49,7 @@ export async function resolveRecipients(
|
|||||||
where: { orgUnitId: targetValue, isActive: true },
|
where: { orgUnitId: targetValue, isActive: true },
|
||||||
select: { userId: true },
|
select: { userId: true },
|
||||||
});
|
});
|
||||||
userIds = resources
|
userIds = resources.map((r) => r.userId).filter((id): id is string => !!id);
|
||||||
.map((r) => r.userId)
|
|
||||||
.filter((id): id is string => !!id);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,6 +57,7 @@ export async function resolveRecipients(
|
|||||||
// User model has no isActive — get all users
|
// User model has no isActive — get all users
|
||||||
const allUsers = await db.user.findMany({
|
const allUsers = await db.user.findMany({
|
||||||
select: { id: true },
|
select: { id: true },
|
||||||
|
take: 10_000,
|
||||||
});
|
});
|
||||||
userIds = allUsers.map((u) => u.id);
|
userIds = allUsers.map((u) => u.id);
|
||||||
break;
|
break;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,135 @@
|
|||||||
|
import { PermissionKey, SystemRole } from "@capakraken/shared";
|
||||||
|
import type { ToolAccessRequirements, ToolContext, ToolDef } from "./shared.js";
|
||||||
|
import { AssistantVisibleError } from "./helpers.js";
|
||||||
|
|
||||||
|
export const CONTROLLER_ASSISTANT_ROLES = [
|
||||||
|
SystemRole.ADMIN,
|
||||||
|
SystemRole.MANAGER,
|
||||||
|
SystemRole.CONTROLLER,
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export const LEGACY_MONOLITHIC_TOOL_ACCESS: Partial<Record<string, ToolAccessRequirements>> = {
|
||||||
|
search_projects: { allowedSystemRoles: [...CONTROLLER_ASSISTANT_ROLES] },
|
||||||
|
get_project: { allowedSystemRoles: [...CONTROLLER_ASSISTANT_ROLES] },
|
||||||
|
update_project: { requiredPermissions: [PermissionKey.MANAGE_PROJECTS] },
|
||||||
|
create_project: { requiredPermissions: [PermissionKey.MANAGE_PROJECTS] },
|
||||||
|
delete_project: { requiredPermissions: [PermissionKey.MANAGE_PROJECTS] },
|
||||||
|
generate_project_cover: { requiredPermissions: [PermissionKey.MANAGE_PROJECTS] },
|
||||||
|
remove_project_cover: { requiredPermissions: [PermissionKey.MANAGE_PROJECTS] },
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AssistantToolAccessEvaluationContext = Pick<ToolContext, "permissions" | "userRole">;
|
||||||
|
|
||||||
|
export type AssistantToolAccessFailure =
|
||||||
|
| { type: "role" }
|
||||||
|
| {
|
||||||
|
type: "permission";
|
||||||
|
permission?: PermissionKey;
|
||||||
|
message?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
function hasAssistantResourceOverviewAccess(permissions: Set<PermissionKey>): boolean {
|
||||||
|
return (
|
||||||
|
permissions.has(PermissionKey.VIEW_ALL_RESOURCES) ||
|
||||||
|
permissions.has(PermissionKey.MANAGE_RESOURCES)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getAssistantToolAccessRequirements(
|
||||||
|
tool: ToolDef,
|
||||||
|
): ToolAccessRequirements | undefined {
|
||||||
|
return tool.access ?? LEGACY_MONOLITHIC_TOOL_ACCESS[tool.function.name];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getAssistantToolAccessFailure(
|
||||||
|
tool: ToolDef,
|
||||||
|
ctx: AssistantToolAccessEvaluationContext,
|
||||||
|
): AssistantToolAccessFailure | null {
|
||||||
|
const access = getAssistantToolAccessRequirements(tool);
|
||||||
|
if (!access) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
access.allowedSystemRoles &&
|
||||||
|
!access.allowedSystemRoles.includes(ctx.userRole as SystemRole)
|
||||||
|
) {
|
||||||
|
return { type: "role" };
|
||||||
|
}
|
||||||
|
|
||||||
|
const missingRequiredPermission = access.requiredPermissions?.find(
|
||||||
|
(permission) => !ctx.permissions.has(permission),
|
||||||
|
);
|
||||||
|
if (missingRequiredPermission) {
|
||||||
|
return {
|
||||||
|
type: "permission",
|
||||||
|
permission: missingRequiredPermission,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (access.requiresPlanningRead && !ctx.permissions.has(PermissionKey.VIEW_PLANNING)) {
|
||||||
|
return {
|
||||||
|
type: "permission",
|
||||||
|
permission: PermissionKey.VIEW_PLANNING,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (access.requiresCostView && !ctx.permissions.has(PermissionKey.VIEW_COSTS)) {
|
||||||
|
return {
|
||||||
|
type: "permission",
|
||||||
|
permission: PermissionKey.VIEW_COSTS,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
access.requiresAdvancedAssistant &&
|
||||||
|
!ctx.permissions.has(PermissionKey.USE_ASSISTANT_ADVANCED_TOOLS)
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
type: "permission",
|
||||||
|
permission: PermissionKey.USE_ASSISTANT_ADVANCED_TOOLS,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (access.requiresResourceOverview && !hasAssistantResourceOverviewAccess(ctx.permissions)) {
|
||||||
|
return {
|
||||||
|
type: "permission",
|
||||||
|
message: "Permission denied: you need resource overview access to perform this action.",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toAssistantToolAccessError(
|
||||||
|
failure: AssistantToolAccessFailure,
|
||||||
|
): AssistantVisibleError {
|
||||||
|
if (failure.type === "role") {
|
||||||
|
return new AssistantVisibleError("You do not have permission to perform this action.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (failure.permission) {
|
||||||
|
return new AssistantVisibleError(
|
||||||
|
`Permission denied: you need the "${failure.permission}" permission to perform this action.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new AssistantVisibleError(
|
||||||
|
failure.message ?? "You do not have permission to perform this action.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function canAccessAssistantTool(
|
||||||
|
tool: ToolDef,
|
||||||
|
ctx: AssistantToolAccessEvaluationContext,
|
||||||
|
): boolean {
|
||||||
|
return getAssistantToolAccessFailure(tool, ctx) === null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getAvailableAssistantToolsForContext(
|
||||||
|
allTools: ToolDef[],
|
||||||
|
permissions: Set<PermissionKey>,
|
||||||
|
userRole: string,
|
||||||
|
): ToolDef[] {
|
||||||
|
return allTools.filter((tool) => canAccessAssistantTool(tool, { permissions, userRole }));
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
import { createCallerFactory } from "../../trpc.js";
|
||||||
|
import { chargeabilityReportRouter } from "../chargeability-report.js";
|
||||||
|
import { computationGraphRouter } from "../computation-graph.js";
|
||||||
|
import { timelineRouter } from "../timeline.js";
|
||||||
|
import { auditLogRouter } from "../audit-log.js";
|
||||||
|
import { importExportRouter } from "../import-export.js";
|
||||||
|
import { dispoRouter } from "../dispo.js";
|
||||||
|
import { resourceRouter } from "../resource.js";
|
||||||
|
import { settingsRouter } from "../settings.js";
|
||||||
|
import { systemRoleConfigRouter } from "../system-role-config.js";
|
||||||
|
import { userRouter } from "../user.js";
|
||||||
|
import { notificationRouter } from "../notification.js";
|
||||||
|
import { estimateRouter } from "../estimate.js";
|
||||||
|
import { webhookRouter } from "../webhook.js";
|
||||||
|
import { countryRouter } from "../country.js";
|
||||||
|
import { holidayCalendarRouter } from "../holiday-calendar.js";
|
||||||
|
import { blueprintRouter } from "../blueprint.js";
|
||||||
|
import { roleRouter } from "../role.js";
|
||||||
|
import { clientRouter } from "../client.js";
|
||||||
|
import { orgUnitRouter } from "../org-unit.js";
|
||||||
|
import { projectRouter } from "../project.js";
|
||||||
|
import { rateCardRouter } from "../rate-card.js";
|
||||||
|
import { reportRouter } from "../report.js";
|
||||||
|
import { vacationRouter } from "../vacation.js";
|
||||||
|
import { entitlementRouter } from "../entitlement.js";
|
||||||
|
import { commentRouter } from "../comment.js";
|
||||||
|
import { managementLevelRouter } from "../management-level.js";
|
||||||
|
import { utilizationCategoryRouter } from "../utilization-category.js";
|
||||||
|
import { calculationRuleRouter } from "../calculation-rules.js";
|
||||||
|
import { effortRuleRouter } from "../effort-rule.js";
|
||||||
|
import { experienceMultiplierRouter } from "../experience-multiplier.js";
|
||||||
|
import { dashboardRouter } from "../dashboard.js";
|
||||||
|
import { insightsRouter } from "../insights.js";
|
||||||
|
import { scenarioRouter } from "../scenario.js";
|
||||||
|
import { allocationRouter } from "../allocation/index.js";
|
||||||
|
import { staffingRouter } from "../staffing.js";
|
||||||
|
|
||||||
|
export const createChargeabilityReportCaller = createCallerFactory(chargeabilityReportRouter);
|
||||||
|
export const createComputationGraphCaller = createCallerFactory(computationGraphRouter);
|
||||||
|
export const createTimelineCaller = createCallerFactory(timelineRouter);
|
||||||
|
export const createAuditLogCaller = createCallerFactory(auditLogRouter);
|
||||||
|
export const createImportExportCaller = createCallerFactory(importExportRouter);
|
||||||
|
export const createDispoCaller = createCallerFactory(dispoRouter);
|
||||||
|
export const createResourceCaller = createCallerFactory(resourceRouter);
|
||||||
|
export const createSettingsCaller = createCallerFactory(settingsRouter);
|
||||||
|
export const createSystemRoleConfigCaller = createCallerFactory(systemRoleConfigRouter);
|
||||||
|
export const createUserCaller = createCallerFactory(userRouter);
|
||||||
|
export const createNotificationCaller = createCallerFactory(notificationRouter);
|
||||||
|
export const createEstimateCaller = createCallerFactory(estimateRouter);
|
||||||
|
export const createWebhookCaller = createCallerFactory(webhookRouter);
|
||||||
|
export const createCountryCaller = createCallerFactory(countryRouter);
|
||||||
|
export const createHolidayCalendarCaller = createCallerFactory(holidayCalendarRouter);
|
||||||
|
export const createBlueprintCaller = createCallerFactory(blueprintRouter);
|
||||||
|
export const createRoleCaller = createCallerFactory(roleRouter);
|
||||||
|
export const createClientCaller = createCallerFactory(clientRouter);
|
||||||
|
export const createOrgUnitCaller = createCallerFactory(orgUnitRouter);
|
||||||
|
export const createProjectCaller = createCallerFactory(projectRouter);
|
||||||
|
export const createRateCardCaller = createCallerFactory(rateCardRouter);
|
||||||
|
export const createReportCaller = createCallerFactory(reportRouter);
|
||||||
|
export const createVacationCaller = createCallerFactory(vacationRouter);
|
||||||
|
export const createEntitlementCaller = createCallerFactory(entitlementRouter);
|
||||||
|
export const createCommentCaller = createCallerFactory(commentRouter);
|
||||||
|
export const createManagementLevelCaller = createCallerFactory(managementLevelRouter);
|
||||||
|
export const createUtilizationCategoryCaller = createCallerFactory(utilizationCategoryRouter);
|
||||||
|
export const createCalculationRuleCaller = createCallerFactory(calculationRuleRouter);
|
||||||
|
export const createEffortRuleCaller = createCallerFactory(effortRuleRouter);
|
||||||
|
export const createExperienceMultiplierCaller = createCallerFactory(experienceMultiplierRouter);
|
||||||
|
export const createDashboardCaller = createCallerFactory(dashboardRouter);
|
||||||
|
export const createInsightsCaller = createCallerFactory(insightsRouter);
|
||||||
|
export const createScenarioCaller = createCallerFactory(scenarioRouter);
|
||||||
|
export const createAllocationCaller = createCallerFactory(allocationRouter);
|
||||||
|
export const createStaffingCaller = createCallerFactory(staffingRouter);
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -282,6 +282,7 @@ model Account {
|
|||||||
|
|
||||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
|
@@index([userId])
|
||||||
@@unique([provider, providerAccountId])
|
@@unique([provider, providerAccountId])
|
||||||
@@map("accounts")
|
@@map("accounts")
|
||||||
}
|
}
|
||||||
@@ -293,6 +294,7 @@ model Session {
|
|||||||
expires DateTime
|
expires DateTime
|
||||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
|
@@index([userId])
|
||||||
@@map("sessions")
|
@@map("sessions")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -938,6 +940,7 @@ model Project {
|
|||||||
color String? // Hex color for timeline display, e.g. "#3b82f6"
|
color String? // Hex color for timeline display, e.g. "#3b82f6"
|
||||||
coverImageUrl String? @db.Text // Base64 data-URL for project cover art
|
coverImageUrl String? @db.Text // Base64 data-URL for project cover art
|
||||||
coverFocusY Int @default(50) // Vertical focus point 0-100 (% from top)
|
coverFocusY Int @default(50) // Vertical focus point 0-100 (% from top)
|
||||||
|
coverAiGenerated Boolean @default(false) // EGAI 4.3.1.3 — true when cover was AI-generated
|
||||||
|
|
||||||
// staffingReqs: StaffingRequirement[]
|
// staffingReqs: StaffingRequirement[]
|
||||||
staffingReqs Json @db.JsonB @default("[]")
|
staffingReqs Json @db.JsonB @default("[]")
|
||||||
|
|||||||
Generated
+128
@@ -156,6 +156,9 @@ importers:
|
|||||||
'@capakraken/tsconfig':
|
'@capakraken/tsconfig':
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../../tooling/typescript
|
version: link:../../tooling/typescript
|
||||||
|
'@next/bundle-analyzer':
|
||||||
|
specifier: ^16.2.3
|
||||||
|
version: 16.2.3
|
||||||
'@playwright/test':
|
'@playwright/test':
|
||||||
specifier: ^1.49.1
|
specifier: ^1.49.1
|
||||||
version: 1.58.2
|
version: 1.58.2
|
||||||
@@ -586,6 +589,10 @@ packages:
|
|||||||
'@dimforge/rapier3d-compat@0.12.0':
|
'@dimforge/rapier3d-compat@0.12.0':
|
||||||
resolution: {integrity: sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow==}
|
resolution: {integrity: sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow==}
|
||||||
|
|
||||||
|
'@discoveryjs/json-ext@0.5.7':
|
||||||
|
resolution: {integrity: sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==}
|
||||||
|
engines: {node: '>=10.0.0'}
|
||||||
|
|
||||||
'@dnd-kit/accessibility@3.1.1':
|
'@dnd-kit/accessibility@3.1.1':
|
||||||
resolution: {integrity: sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==}
|
resolution: {integrity: sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -1181,6 +1188,9 @@ packages:
|
|||||||
'@emnapi/core': ^1.7.1
|
'@emnapi/core': ^1.7.1
|
||||||
'@emnapi/runtime': ^1.7.1
|
'@emnapi/runtime': ^1.7.1
|
||||||
|
|
||||||
|
'@next/bundle-analyzer@16.2.3':
|
||||||
|
resolution: {integrity: sha512-aDwW4f4SVqbQDWzSBHQJ1KI6H+lx8oX/vS3xGqzLajUu+KQb7uakK88AIMvRIf7TlIonce67g594rzpxvBuJIw==}
|
||||||
|
|
||||||
'@next/env@15.5.15':
|
'@next/env@15.5.15':
|
||||||
resolution: {integrity: sha512-vcmyu5/MyFzN7CdqRHO3uHO44p/QPCZkuTUXroeUmhNP8bL5PHFEhik22JUazt+CDDoD6EpBYRCaS2pISL+/hg==}
|
resolution: {integrity: sha512-vcmyu5/MyFzN7CdqRHO3uHO44p/QPCZkuTUXroeUmhNP8bL5PHFEhik22JUazt+CDDoD6EpBYRCaS2pISL+/hg==}
|
||||||
|
|
||||||
@@ -1776,6 +1786,9 @@ packages:
|
|||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
'@polka/url@1.0.0-next.29':
|
||||||
|
resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==}
|
||||||
|
|
||||||
'@prisma/client@5.22.0':
|
'@prisma/client@5.22.0':
|
||||||
resolution: {integrity: sha512-M0SVXfyHnQREBKxCgyo7sffrKttwE6R8PMq330MIUF0pTwjUhLbW84pFDlf06B27XyCR++VtjugEnIHdr07SVA==}
|
resolution: {integrity: sha512-M0SVXfyHnQREBKxCgyo7sffrKttwE6R8PMq330MIUF0pTwjUhLbW84pFDlf06B27XyCR++VtjugEnIHdr07SVA==}
|
||||||
engines: {node: '>=16.13'}
|
engines: {node: '>=16.13'}
|
||||||
@@ -2516,6 +2529,10 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
|
acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
|
||||||
|
|
||||||
|
acorn-walk@8.3.5:
|
||||||
|
resolution: {integrity: sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==}
|
||||||
|
engines: {node: '>=0.4.0'}
|
||||||
|
|
||||||
acorn@8.16.0:
|
acorn@8.16.0:
|
||||||
resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==}
|
resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==}
|
||||||
engines: {node: '>=0.4.0'}
|
engines: {node: '>=0.4.0'}
|
||||||
@@ -2849,6 +2866,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
|
resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
|
||||||
engines: {node: '>= 6'}
|
engines: {node: '>= 6'}
|
||||||
|
|
||||||
|
commander@7.2.0:
|
||||||
|
resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==}
|
||||||
|
engines: {node: '>= 10'}
|
||||||
|
|
||||||
commondir@1.0.1:
|
commondir@1.0.1:
|
||||||
resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==}
|
resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==}
|
||||||
|
|
||||||
@@ -2992,6 +3013,9 @@ packages:
|
|||||||
dayjs@1.11.19:
|
dayjs@1.11.19:
|
||||||
resolution: {integrity: sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==}
|
resolution: {integrity: sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==}
|
||||||
|
|
||||||
|
debounce@1.2.1:
|
||||||
|
resolution: {integrity: sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==}
|
||||||
|
|
||||||
debug@3.2.7:
|
debug@3.2.7:
|
||||||
resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
|
resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -3082,6 +3106,9 @@ packages:
|
|||||||
duplexer2@0.1.4:
|
duplexer2@0.1.4:
|
||||||
resolution: {integrity: sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==}
|
resolution: {integrity: sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==}
|
||||||
|
|
||||||
|
duplexer@0.1.2:
|
||||||
|
resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==}
|
||||||
|
|
||||||
eastasianwidth@0.2.0:
|
eastasianwidth@0.2.0:
|
||||||
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
|
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
|
||||||
|
|
||||||
@@ -3523,6 +3550,10 @@ packages:
|
|||||||
graceful-fs@4.2.11:
|
graceful-fs@4.2.11:
|
||||||
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
|
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
|
||||||
|
|
||||||
|
gzip-size@6.0.0:
|
||||||
|
resolution: {integrity: sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
has-bigints@1.1.0:
|
has-bigints@1.1.0:
|
||||||
resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==}
|
resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -3718,6 +3749,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
|
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
|
||||||
engines: {node: '>=0.12.0'}
|
engines: {node: '>=0.12.0'}
|
||||||
|
|
||||||
|
is-plain-object@5.0.0:
|
||||||
|
resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
is-potential-custom-element-name@1.0.1:
|
is-potential-custom-element-name@1.0.1:
|
||||||
resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
|
resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
|
||||||
|
|
||||||
@@ -4088,6 +4123,10 @@ packages:
|
|||||||
motion-utils@12.36.0:
|
motion-utils@12.36.0:
|
||||||
resolution: {integrity: sha512-eHWisygbiwVvf6PZ1vhaHCLamvkSbPIeAYxWUuL3a2PD/TROgE7FvfHWTIH4vMl798QLfMw15nRqIaRDXTlYRg==}
|
resolution: {integrity: sha512-eHWisygbiwVvf6PZ1vhaHCLamvkSbPIeAYxWUuL3a2PD/TROgE7FvfHWTIH4vMl798QLfMw15nRqIaRDXTlYRg==}
|
||||||
|
|
||||||
|
mrmime@2.0.1:
|
||||||
|
resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
ms@2.1.3:
|
ms@2.1.3:
|
||||||
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
||||||
|
|
||||||
@@ -4238,6 +4277,10 @@ packages:
|
|||||||
zod:
|
zod:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
opener@1.5.2:
|
||||||
|
resolution: {integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
optionator@0.9.4:
|
optionator@0.9.4:
|
||||||
resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
|
resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
|
||||||
engines: {node: '>= 0.8.0'}
|
engines: {node: '>= 0.8.0'}
|
||||||
@@ -4789,6 +4832,10 @@ packages:
|
|||||||
simple-swizzle@0.2.4:
|
simple-swizzle@0.2.4:
|
||||||
resolution: {integrity: sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==}
|
resolution: {integrity: sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==}
|
||||||
|
|
||||||
|
sirv@2.0.4:
|
||||||
|
resolution: {integrity: sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==}
|
||||||
|
engines: {node: '>= 10'}
|
||||||
|
|
||||||
slice-ansi@7.1.2:
|
slice-ansi@7.1.2:
|
||||||
resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==}
|
resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
@@ -5055,6 +5102,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
|
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
|
||||||
engines: {node: '>=8.0'}
|
engines: {node: '>=8.0'}
|
||||||
|
|
||||||
|
totalist@3.0.1:
|
||||||
|
resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
tough-cookie@6.0.1:
|
tough-cookie@6.0.1:
|
||||||
resolution: {integrity: sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==}
|
resolution: {integrity: sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==}
|
||||||
engines: {node: '>=16'}
|
engines: {node: '>=16'}
|
||||||
@@ -5288,6 +5339,11 @@ packages:
|
|||||||
resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==}
|
resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==}
|
||||||
engines: {node: '>=20'}
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
|
webpack-bundle-analyzer@4.10.1:
|
||||||
|
resolution: {integrity: sha512-s3P7pgexgT/HTUSYgxJyn28A+99mmLq4HsJepMPzu0R8ImJc52QNqaFYW1Z2z2uIb1/J3eYgaAWVpaC+v/1aAQ==}
|
||||||
|
engines: {node: '>= 10.13.0'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
webpack-sources@3.3.4:
|
webpack-sources@3.3.4:
|
||||||
resolution: {integrity: sha512-7tP1PdV4vF+lYPnkMR0jMY5/la2ub5Fc/8VQrrU+lXkiM6C4TjVfGw7iKfyhnTQOsD+6Q/iKw0eFciziRgD58Q==}
|
resolution: {integrity: sha512-7tP1PdV4vF+lYPnkMR0jMY5/la2ub5Fc/8VQrrU+lXkiM6C4TjVfGw7iKfyhnTQOsD+6Q/iKw0eFciziRgD58Q==}
|
||||||
engines: {node: '>=10.13.0'}
|
engines: {node: '>=10.13.0'}
|
||||||
@@ -5365,6 +5421,18 @@ packages:
|
|||||||
wrappy@1.0.2:
|
wrappy@1.0.2:
|
||||||
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
||||||
|
|
||||||
|
ws@7.5.10:
|
||||||
|
resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==}
|
||||||
|
engines: {node: '>=8.3.0'}
|
||||||
|
peerDependencies:
|
||||||
|
bufferutil: ^4.0.1
|
||||||
|
utf-8-validate: ^5.0.2
|
||||||
|
peerDependenciesMeta:
|
||||||
|
bufferutil:
|
||||||
|
optional: true
|
||||||
|
utf-8-validate:
|
||||||
|
optional: true
|
||||||
|
|
||||||
xml-name-validator@5.0.0:
|
xml-name-validator@5.0.0:
|
||||||
resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==}
|
resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
@@ -5600,6 +5668,8 @@ snapshots:
|
|||||||
|
|
||||||
'@dimforge/rapier3d-compat@0.12.0': {}
|
'@dimforge/rapier3d-compat@0.12.0': {}
|
||||||
|
|
||||||
|
'@discoveryjs/json-ext@0.5.7': {}
|
||||||
|
|
||||||
'@dnd-kit/accessibility@3.1.1(react@19.2.4)':
|
'@dnd-kit/accessibility@3.1.1(react@19.2.4)':
|
||||||
dependencies:
|
dependencies:
|
||||||
react: 19.2.4
|
react: 19.2.4
|
||||||
@@ -6054,6 +6124,13 @@ snapshots:
|
|||||||
'@tybys/wasm-util': 0.10.1
|
'@tybys/wasm-util': 0.10.1
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@next/bundle-analyzer@16.2.3':
|
||||||
|
dependencies:
|
||||||
|
webpack-bundle-analyzer: 4.10.1
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- bufferutil
|
||||||
|
- utf-8-validate
|
||||||
|
|
||||||
'@next/env@15.5.15': {}
|
'@next/env@15.5.15': {}
|
||||||
|
|
||||||
'@next/swc-darwin-arm64@15.5.15':
|
'@next/swc-darwin-arm64@15.5.15':
|
||||||
@@ -6561,6 +6638,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
playwright: 1.58.2
|
playwright: 1.58.2
|
||||||
|
|
||||||
|
'@polka/url@1.0.0-next.29': {}
|
||||||
|
|
||||||
'@prisma/client@5.22.0(prisma@5.22.0)':
|
'@prisma/client@5.22.0(prisma@5.22.0)':
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
prisma: 5.22.0
|
prisma: 5.22.0
|
||||||
@@ -7447,6 +7526,10 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
acorn: 8.16.0
|
acorn: 8.16.0
|
||||||
|
|
||||||
|
acorn-walk@8.3.5:
|
||||||
|
dependencies:
|
||||||
|
acorn: 8.16.0
|
||||||
|
|
||||||
acorn@8.16.0: {}
|
acorn@8.16.0: {}
|
||||||
|
|
||||||
agent-base@6.0.2:
|
agent-base@6.0.2:
|
||||||
@@ -7805,6 +7888,8 @@ snapshots:
|
|||||||
|
|
||||||
commander@4.1.1: {}
|
commander@4.1.1: {}
|
||||||
|
|
||||||
|
commander@7.2.0: {}
|
||||||
|
|
||||||
commondir@1.0.1: {}
|
commondir@1.0.1: {}
|
||||||
|
|
||||||
compress-commons@4.1.2:
|
compress-commons@4.1.2:
|
||||||
@@ -7940,6 +8025,8 @@ snapshots:
|
|||||||
|
|
||||||
dayjs@1.11.19: {}
|
dayjs@1.11.19: {}
|
||||||
|
|
||||||
|
debounce@1.2.1: {}
|
||||||
|
|
||||||
debug@3.2.7:
|
debug@3.2.7:
|
||||||
dependencies:
|
dependencies:
|
||||||
ms: 2.1.3
|
ms: 2.1.3
|
||||||
@@ -8009,6 +8096,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
readable-stream: 2.3.8
|
readable-stream: 2.3.8
|
||||||
|
|
||||||
|
duplexer@0.1.2: {}
|
||||||
|
|
||||||
eastasianwidth@0.2.0: {}
|
eastasianwidth@0.2.0: {}
|
||||||
|
|
||||||
electron-to-chromium@1.5.307: {}
|
electron-to-chromium@1.5.307: {}
|
||||||
@@ -8634,6 +8723,10 @@ snapshots:
|
|||||||
|
|
||||||
graceful-fs@4.2.11: {}
|
graceful-fs@4.2.11: {}
|
||||||
|
|
||||||
|
gzip-size@6.0.0:
|
||||||
|
dependencies:
|
||||||
|
duplexer: 0.1.2
|
||||||
|
|
||||||
has-bigints@1.1.0: {}
|
has-bigints@1.1.0: {}
|
||||||
|
|
||||||
has-flag@4.0.0: {}
|
has-flag@4.0.0: {}
|
||||||
@@ -8832,6 +8925,8 @@ snapshots:
|
|||||||
|
|
||||||
is-number@7.0.0: {}
|
is-number@7.0.0: {}
|
||||||
|
|
||||||
|
is-plain-object@5.0.0: {}
|
||||||
|
|
||||||
is-potential-custom-element-name@1.0.1: {}
|
is-potential-custom-element-name@1.0.1: {}
|
||||||
|
|
||||||
is-reference@1.2.1:
|
is-reference@1.2.1:
|
||||||
@@ -9210,6 +9305,8 @@ snapshots:
|
|||||||
|
|
||||||
motion-utils@12.36.0: {}
|
motion-utils@12.36.0: {}
|
||||||
|
|
||||||
|
mrmime@2.0.1: {}
|
||||||
|
|
||||||
ms@2.1.3: {}
|
ms@2.1.3: {}
|
||||||
|
|
||||||
mz@2.7.0:
|
mz@2.7.0:
|
||||||
@@ -9338,6 +9435,8 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
zod: 3.25.76
|
zod: 3.25.76
|
||||||
|
|
||||||
|
opener@1.5.2: {}
|
||||||
|
|
||||||
optionator@0.9.4:
|
optionator@0.9.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
deep-is: 0.1.4
|
deep-is: 0.1.4
|
||||||
@@ -9999,6 +10098,12 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
is-arrayish: 0.3.4
|
is-arrayish: 0.3.4
|
||||||
|
|
||||||
|
sirv@2.0.4:
|
||||||
|
dependencies:
|
||||||
|
'@polka/url': 1.0.0-next.29
|
||||||
|
mrmime: 2.0.1
|
||||||
|
totalist: 3.0.1
|
||||||
|
|
||||||
slice-ansi@7.1.2:
|
slice-ansi@7.1.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
ansi-styles: 6.2.3
|
ansi-styles: 6.2.3
|
||||||
@@ -10283,6 +10388,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
is-number: 7.0.0
|
is-number: 7.0.0
|
||||||
|
|
||||||
|
totalist@3.0.1: {}
|
||||||
|
|
||||||
tough-cookie@6.0.1:
|
tough-cookie@6.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
tldts: 7.0.28
|
tldts: 7.0.28
|
||||||
@@ -10543,6 +10650,25 @@ snapshots:
|
|||||||
|
|
||||||
webidl-conversions@8.0.1: {}
|
webidl-conversions@8.0.1: {}
|
||||||
|
|
||||||
|
webpack-bundle-analyzer@4.10.1:
|
||||||
|
dependencies:
|
||||||
|
'@discoveryjs/json-ext': 0.5.7
|
||||||
|
acorn: 8.16.0
|
||||||
|
acorn-walk: 8.3.5
|
||||||
|
commander: 7.2.0
|
||||||
|
debounce: 1.2.1
|
||||||
|
escape-string-regexp: 4.0.0
|
||||||
|
gzip-size: 6.0.0
|
||||||
|
html-escaper: 2.0.2
|
||||||
|
is-plain-object: 5.0.0
|
||||||
|
opener: 1.5.2
|
||||||
|
picocolors: 1.1.1
|
||||||
|
sirv: 2.0.4
|
||||||
|
ws: 7.5.10
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- bufferutil
|
||||||
|
- utf-8-validate
|
||||||
|
|
||||||
webpack-sources@3.3.4: {}
|
webpack-sources@3.3.4: {}
|
||||||
|
|
||||||
webpack@5.105.4:
|
webpack@5.105.4:
|
||||||
@@ -10672,6 +10798,8 @@ snapshots:
|
|||||||
|
|
||||||
wrappy@1.0.2: {}
|
wrappy@1.0.2: {}
|
||||||
|
|
||||||
|
ws@7.5.10: {}
|
||||||
|
|
||||||
xml-name-validator@5.0.0: {}
|
xml-name-validator@5.0.0: {}
|
||||||
|
|
||||||
xmlchars@2.2.0: {}
|
xmlchars@2.2.0: {}
|
||||||
|
|||||||
Reference in New Issue
Block a user