Skip to content

JavaScript / TypeScript SDK

JavaScript/TypeScript SDK для можно. — клиентская библиотека для Node.js и браузерных приложений. Полная поддержка TypeScript, типы включены в пакет.

Установка

bash
npm install @mozhno/client-js
bash
yarn add @mozhno/client-js
bash
pnpm add @mozhno/client-js

Системные требования

СредаМинимальная версия
Node.js18+
БраузерыПоследние 2 версии Chrome, Firefox, Safari, Edge
TypeScript5.0+ (опционально, типы включены в пакет)

Быстрый старт

typescript
import { MozhnoClient } from '@mozhno/client-js';

const client = new MozhnoClient({
  url: 'https://flags.example.com',
  apiKey: 'env-abc123',
  appName: 'my-app',
});
await client.start();

const on = client.isEnabled('new-checkout', { userId: '42' });

if (on) {
  // новый код
} else {
  // старый код
}

Конфигурация

Клиент создаётся через конструктор MozhnoClient, который принимает объект настроек MozhnoConfig:

typescript
import { MozhnoClient } from '@mozhno/client-js';

const client = new MozhnoClient({
  url: 'https://flags.example.com',
  apiKey: 'env-abc123',
  appName: 'my-app',
  refreshInterval: 15,
  metricsInterval: 60,
  disableMetrics: false,
  stickyAnonId: true,
  environment: 'production',
});
await client.start();
ОпцияТипОбязательноПо умолчаниюОписание
urlstringДаURL сервера можно.
appNamestringДаИдентификатор приложения
apiKeystringНетAPI-ключ окружения
clientKeystringНетКлиентский ключ (для mode: 'client')
instanceIdstringНетUUIDУникальный идентификатор экземпляра
mode'server' | 'client'Нет'server'Режим работы
refreshIntervalnumberНет15 секИнтервал поллинга правил
metricsIntervalnumberНет60 секИнтервал отправки метрик
disableMetricsbooleanНетfalseОтключить отправку метрик
stickyAnonIdbooleanНетtrueАвто-ID для анонимных пользователей
bootstrapFeatureFlag[]НетПредзагруженные правила
storageProviderStorageProviderНетКастомное хранилище
fetchtypeof fetchНетglobalThis.fetchПереопределение HTTP-клиента
environmentstringНет'default'Имя окружения
contextMozhnoContextНетГлобальный контекст по умолчанию

Жизненный цикл

typescript
const client = new MozhnoClient({ url: '...', apiKey: '...', appName: 'my-app' });
await client.start();  // запускает поллинг
// ... работа с флагами ...
client.stop();          // останавливает поллинг и освобождает ресурсы

Клиент наследует EventEmitter и генерирует события: 'ready', 'update', 'error', 'initialized', 'sent', 'warn'.

Контекст (MozhnoContext)

MozhnoContext — это простой объект с опциональными полями для передачи атрибутов в момент оценки флага:

typescript
interface MozhnoContext {
  userId?: string;
  sessionId?: string;
  [key: string]: string | undefined;
}

Создание и передача

typescript
const ctx = {
  userId: 'user-123',
  country: 'RU',
  plan: 'premium',
  appVersion: '2.4.1',
};

const enabled = client.isEnabled('new-checkout', ctx);

Если не передан userId или sessionId, SDK автоматически использует stickyAnonId (по умолчанию true) для детерминированного процентного роллаута. В браузере анонимный ID генерируется один раз и сохраняется в localStorage, поэтому пользователь стабильно попадает в одну группу между сессиями. В режиме mode: 'client' анонимный ID передаётся на сервер в поле anonymousId контекста и используется там для бакетинга.

Глобальный контекст

Глобальный контекст задаётся при создании клиента:

typescript
const client = new MozhnoClient({
  url: '...',
  apiKey: '...',
  appName: 'my-app',
  context: { userId: 'service-account' },
});

При каждом вызове isEnabled локальный контекст объединяется с глобальным.

Интеграция с React

SDK не содержит встроенных React-хуков, но легко оборачивается вручную:

typescript
// mozhnoContext.tsx
import React, { createContext, useContext, useEffect, useState } from 'react';
import { MozhnoClient, type MozhnoContext } from '@mozhno/client-js';

const MozhnoCtx = createContext<MozhnoClient | null>(null);

export function MozhnoProvider({
  client,
  children,
}: {
  client: MozhnoClient;
  children: React.ReactNode;
}) {
  useEffect(() => {
    client.start();
    return () => { client.stop(); };
  }, [client]);
  return <MozhnoCtx.Provider value={client}>{children}</MozhnoCtx.Provider>;
}

export function useFlag(flagKey: string, ctx?: MozhnoContext): boolean {
  const client = useContext(MozhnoCtx);
  if (!client) return false;
  return client.isEnabled(flagKey, ctx);
}
tsx
// App.tsx
const client = new MozhnoClient({
  url: import.meta.env.VITE_MOZHNO_URL,
  apiKey: import.meta.env.VITE_MOZHNO_API_KEY,
  appName: 'web-app',
});

function App() {
  return (
    <MozhnoProvider client={client}>
      <CheckoutPage />
    </MozhnoProvider>
  );
}

function CheckoutPage() {
  const showNewCheckout = useFlag('new-checkout', { userId: '42' });
  return showNewCheckout ? <NewCheckout /> : <OldCheckout />;
}

Интеграция с Node.js (Express)

typescript
import express from 'express';
import { MozhnoClient } from '@mozhno/client-js';

const client = new MozhnoClient({
  url: process.env.MOZHNO_URL || 'http://localhost:8080',
  apiKey: process.env.MOZHNO_API_KEY,
  appName: 'api-server',
});

await client.start();

const app = express();

app.get('/checkout', (req, res) => {
  const ctx = {
    userId: req.headers['x-user-id'] as string,
    country: req.headers['x-country'] as string,
  };

  if (client.isEnabled('new-checkout', ctx)) {
    res.json({ flow: 'new' });
  } else {
    res.json({ flow: 'old' });
  }
});

Что дальше?

Released under the BSL 1.1 License.