Jedna definicja.Trzynaście platform.
next-vibe zamienia pojedynczą definicję TypeScript w trzynaście platform naraz - dedykowany interfejs web, polecenie CLI, narzędzie MCP, ekran mobilny, cron job, WebSocket, panel admina i więcej. Pełne bezpieczeństwo typów, zero dryfu, zero powtórzeń.
typed endpoints
runtime type errors
platforms per endpoint
files required
Budowałeś to samo trzynaście razy.
Każdy feature potrzebuje własnego interfejsu web, polecenia CLI, narzędzia MCP, ekranu mobilnego, cron joba, handlera WebSocket, panelu admina i więcej. Ta sama walidacja, ten sam i18n, ta sama obsługa błędów - tylko inaczej ubrana. Za każdym razem.
next-vibe buduje wszystkie trzynaście z jednego pliku.
Dwa pliki wymagane. Każda platforma.
Każdy feature żyje w folderze. Tylko definition.ts i route.ts są wymagane - wszystko inne jest opcjonalne.
definition.ts - kontrakt
Zadeklaruj pola, schematy Zod, etykiety, typy błędów i przykłady raz. Ten plik jest jedynym źródłem prawdy - framework odczytuje go w czasie wykonania na każdej platformie.
1const { POST } = createEndpoint({
2 scopedTranslation,
3 method: Methods.POST,
4 path: ["explain", "to-my-boss"],
5 title: "post.title" as const,
6 description: "post.description" as const,
7 icon: "sparkles",
8 category: "endpointCategories.ai",
9 allowedRoles: [UserRole.CUSTOMER, UserRole.ADMIN] as const,
10 fields: customWidgetObject({
11 render: ExplainContainer,
12 usage: { request: "data", response: true } as const,
13 children: {
14 decision: requestField(scopedTranslation, {
15 type: WidgetType.FORM_FIELD,
16 fieldType: FieldDataType.TEXTAREA,
17 label: "post.decision.label" as const,
18 columns: 12,
19 schema: z.string().min(10),
20 }),
21 justification: responseField(scopedTranslation, {
22 type: WidgetType.TEXT,
23 content: "post.justification.content" as const,
24 schema: z.string(),
25 }),
26 },
27 }),
28 errorTypes: {
29 [EndpointErrorTypes.UNAUTHORIZED]: { title: "post.errors.unauthorized.title" as const, description: "post.errors.unauthorized.description" as const },
30 [EndpointErrorTypes.SERVER_ERROR]: { title: "post.errors.serverError.title" as const, description: "post.errors.serverError.description" as const },
31 // ... other error types
32 },
33 successTypes: { title: "post.success.title" as const, description: "post.success.description" as const },
34 examples: { requests: { default: { decision: "Przepisz wszystko na TypeScript" } }, responses: { default: { justification: "..." } } },
35});
36
37export type ExplainResponseOutput = typeof POST.types.ResponseOutput;
38const definitions = { POST } as const;
39export default definitions;repository.ts - logika
Logika biznesowa żyje tu - zapytania DB, sprawdzanie uprawnień, obsługa błędów z success()/fail(). Plik route.ts to tylko cienki delegator; walidacja, logowanie i rejestracja platform działają automatycznie.
1import "server-only";
2
3export class ExplainRepository {
4 static async explainToMyBoss(
5 data: ExplainRequestOutput,
6 user: JwtPayloadType,
7 logger: EndpointLogger,
8 t: ExplainT,
9 ): Promise<ResponseType<ExplainResponseOutput>> {
10 if (!user.id) {
11 return fail({ message: t("post.errors.unauthorized.title"), errorType: ErrorResponseTypes.UNAUTHORIZED });
12 }
13 try {
14 const [saved] = await db
15 .insert(explainResults)
16 .values({ userId: user.id, decision: data.decision })
17 .returning();
18 logger.info("Saved decision", { userId: user.id, id: saved.id });
19 return success({ justification: saved.justification ?? "" });
20 } catch (error) {
21 logger.error("Failed to save decision", parseError(error));
22 return fail({ message: t("post.errors.serverError.title"), errorType: ErrorResponseTypes.INTERNAL_ERROR });
23 }
24 }
25}widget.tsx - UI (opcjonalny)
Bez widgetu framework automatycznie renderuje pola wszędzie. Dodaj widget.tsx, żeby dostarczyć w pełni customowy interaktywny interfejs - ten sam komponent renderuje w panelach admina, osadzonych widgetach i ekranach mobilnych.
1"use client";
2
3interface CustomWidgetProps {
4 field: {
5 value: ExplainResponseOutput | null | undefined;
6 } & (typeof definition.POST)["fields"];
7}
8
9export function ExplainContainer({ field }: CustomWidgetProps): JSX.Element {
10 const children = field.children;
11 const emptyField = useMemo(() => ({}), []);
12
13 return (
14 <Div className="flex flex-col gap-4 p-4">
15 <TextareaFieldWidget fieldName="decision" field={children.decision} />
16 <SubmitButtonWidget<typeof definition.POST>
17 field={{
18 text: "Explain to my boss",
19 loadingText: "Explaining...",
20 icon: "sparkles",
21 variant: "primary",
22 }}
23 />
24 <FormAlertWidget field={emptyField} />
25 <AlertWidget
26 fieldName="justification"
27 field={withValue(children.justification, field.value?.justification, null)}
28 />
29 </Div>
30 );
31}Usuń folder. Feature znika z każdej platformy naraz.
Jedna definicja. Trzynaście platform.
Gdy dodajesz feature do next-vibe, nie staje się tylko endpointem API. Działa wszędzie naraz.
Endpoint REST, w pełni walidowany i typowany
Dedykowany interaktywny interfejs - bez JSX
Każdy endpoint to polecenie z automatycznie generowanymi flagami
Schemat function calling generowany automatycznie
Połącz Claude Desktop, Cursor, dowolny klient MCP
Ekrany iOS i Android z tej samej definicji
Zaplanuj dowolny endpoint na wyrażeniu cron
Wypchnij aktualizacje do podłączonych klientów po zakończeniu
Natywna aplikacja desktopowa przez te same kontrakty endpoint
Automatycznie generowany panel admina - bez dedykowanego kodu
Osadzalny widget iframe dla dowolnej strony
Wywoływalny przez agentów AI jako strukturalny skill
Węzeł w grafie przepływu danych na żywo - ten sam endpoint
Żadnego any. Żadnego unknown. Żadnego throw.
Typy muszą być całkowicie zgodne - bez wyjątków. To nie jest preferencja stylistyczna. To reguła strukturalna wymuszana w czasie budowania przez vibe check.
Zastąp prawdziwym typowanym interfejsem. Jeśli sięgasz po any, twoja architektura ma dziurę.
Ta sama reguła. unknown to tylko any z dodatkowymi krokami. Zdefiniuj typ.
Gołe object jest bez znaczenia. Napisz kształt, którego naprawdę oczekujesz.
Asercje typów to kłamstwa dla kompilatora. Napraw architekturę zamiast tego.
Użyj ResponseType<T> z success(data) lub fail({message, errorType}). Błędy to dane.
Każdy string potrzebuje klucza tłumaczenia. Checker wykrywa nie przetłumaczone literały.
vibe check uruchamia Oxlint (Rust), ESLint i sprawdzanie typów TypeScript równolegle. Zero błędów wymagane przed wysyłką.
Sforkuj, zapytaj, wdróż.
Od pierwszego dnia na poziomie unbottled.ai.
Sforkuj na GitHub, potem sklonuj swój fork lokalnie.
1git clone https://github.com/YOUR_USERNAME/next-vibe
2cd next-vibe && bun installDo lokalnego dewelopmentu vibe dev uruchamia PostgreSQL w Dockerze, migracje, seeduje dane i daje hot reload. Na produkcji vibe build && vibe start robi pierwszy deploy. Potem używasz vibe rebuild, żeby aktualizować produkcję bez przestojów.
1# development
2vibe dev
3
4# production - pierwsze uruchomienie
5vibe build && vibe start
6
7# production - po zmianach
8vibe rebuildOtwórz aplikację i kliknij "Zaloguj jako admin" - kreator konfiguracji przeprowadzi przez klucze API i hasło admina.
Otwórz czat unbottled.ai lub Claude Code i opisz feature, który chcesz. AI napisze wszystkie pliki - definicję, route, widget, i18n - i automatycznie uruchomi vibe check.
1# W czacie unbottled.ai lub Claude Code:
2"Zbuduj mi feature, który tłumaczy decyzje mojemu szefowi"
3# AI pisze wszystkie pliki i automatycznie uruchamia vibe checkBudujesz coś dużego?
Pomagamy zespołom przy konfiguracji, niestandardowych integracjach, przeglądzie architektury i bieżącym wsparciu deweloperskim. Ta sama baza kodu, twój deployment.