diff --git a/LICENSE b/LICENSE deleted file mode 100644 index fe603e1..0000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2024 Lovart AI - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/README.md b/README.md index f3433cd..0dbd122 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,16 @@ -# statsig +# @lovart-open/flags -[![CI](https://github.com/lovartai/flags/actions/workflows/ci.yml/badge.svg)](https://github.com/lovartai/flags/actions/workflows/ci.yml) -[![npm version](https://img.shields.io/npm/v/statsig.svg)](https://www.npmjs.com/package/statsig) +[![CI](https://github.com/lovartai/statsig/actions/workflows/ci.yml/badge.svg)](https://github.com/lovartai/statsig/actions/workflows/ci.yml) +[![npm version](https://img.shields.io/npm/v/@lovart-open/flags.svg)](https://www.npmjs.com/package/@lovart-open/flags) Type-safe Feature Flag and Parameter Store library built on Statsig, with a fully synchronous architecture. ## Installation ```bash -npm install statsig +npm install @lovart-open/flags # or -pnpm add statsig +pnpm add @lovart-open/flags ``` ## Initialize Statsig @@ -18,7 +18,7 @@ pnpm add statsig Initialize the Statsig client at your app entry point: ```ts -import { initStatsigClient } from 'statsig'; +import { initStatsigClient } from '@lovart-open/flags/statsig'; initStatsigClient('your-statsig-client-key', { userID: 'user-123' }, { environment: { tier: 'production' }, @@ -38,7 +38,7 @@ initStatsigClient('your-statsig-client-key', { userID: 'user-123' }, { ## Define and Create ```ts -import { createFlagStore, type FlagDefinition } from 'statsig'; +import { createFlagStore, type FlagDefinition } from '@lovart-open/flags/statsig'; // 1. Define your flags const MY_FLAGS = { @@ -120,7 +120,7 @@ import { createParamStore, defineParam, type ParamStoreDefinition -} from 'statsig'; +} from '@lovart-open/flags/statsig'; // 1. Define your param stores const MY_PARAMS = { @@ -217,9 +217,15 @@ store.get('currency'); // 'USD' | 'CNY' | 'EUR' ## Custom Logger ```ts +import { initStatsigClient, setLogger } from '@lovart-open/flags/statsig'; + +// Option 1: Pass during init initStatsigClient('client-xxx', { userID: 'user-123' }, { - logger: (message, data) => myLogger.info(message, data), + logger: (message) => myLogger.info(message), }); + +// Option 2: Set globally +setLogger((message) => myLogger.info(message)); ``` ## E2E Test Support @@ -237,6 +243,16 @@ await page.addInitScript(() => { }); ``` +## Server-Side Bootstrap (Zero-Network Init) + +```ts +initStatsigClient('client-xxx', { userID: 'user-123' }, { + bootstrap: { + data: bootstrapDataFromServer, // Pre-fetched from BFF + }, +}); +``` + ## FlagDefinition Options | Property | Type | Description | diff --git a/package.json b/package.json index f893f71..f635fdd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "name": "@lovart-open/statsig", - "version": "1.0.0", + "name": "@lovart-open/flags", + "version": "0.0.1", "description": "Feature flag management with multi-layer priority system (client-side)", "main": "src/index.ts", "types": "src/index.ts", @@ -8,6 +8,10 @@ ".": { "types": "./src/index.ts", "default": "./src/index.ts" + }, + "./statsig": { + "types": "./src/statsig/index.ts", + "default": "./src/statsig/index.ts" } }, "files": [ @@ -27,7 +31,6 @@ "dependencies": { "@statsig/js-client": "^3.30.0", "@statsig/react-bindings": "^3.30.2", - "@statsig/web-analytics": "^3.30.0", "lodash": "^4.17.21", "zod": "^3.23.0" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 25034f5..4cf4a4f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,9 +14,6 @@ importers: '@statsig/react-bindings': specifier: ^3.30.2 version: 3.31.2(react@19.2.4) - '@statsig/web-analytics': - specifier: ^3.30.0 - version: 3.31.2 lodash: specifier: ^4.17.21 version: 4.17.23 @@ -389,9 +386,6 @@ packages: peerDependencies: react: ^16.6.3 || ^17.0.0 || ^18.0.0 || ^19.0.0 - '@statsig/web-analytics@3.31.2': - resolution: {integrity: sha512-9Y1I9s+7IK6YYK4E7hfSH/82zk2na3TDAKB3E6P6o9Z/E96vCRbfMETKyJKofPweEg35Asec/qcT9rpbDuuurw==} - '@testing-library/dom@10.4.1': resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} engines: {node: '>=18'} @@ -915,9 +909,6 @@ packages: resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} engines: {node: '>=18'} - web-vitals@5.0.3: - resolution: {integrity: sha512-4KmOFYxj7qT6RAdCH0SWwq8eKeXNhAFXR4PmgF6nrWFmrJ41n7lq3UCA6UK0GebQ4uu+XP8e8zGjaDO3wZlqTg==} - webidl-conversions@7.0.0: resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} engines: {node: '>=12'} @@ -1175,12 +1166,6 @@ snapshots: '@statsig/js-client': 3.31.2 react: 19.2.4 - '@statsig/web-analytics@3.31.2': - dependencies: - '@statsig/client-core': 3.31.2 - '@statsig/js-client': 3.31.2 - web-vitals: 5.0.3 - '@testing-library/dom@10.4.1': dependencies: '@babel/code-frame': 7.29.0 @@ -1733,8 +1718,6 @@ snapshots: dependencies: xml-name-validator: 5.0.0 - web-vitals@5.0.3: {} - webidl-conversions@7.0.0: {} whatwg-encoding@3.1.1: diff --git a/src/__tests__/flag-store.test.ts b/src/__tests__/flag-store.test.ts index cd3f555..4a59d15 100644 --- a/src/__tests__/flag-store.test.ts +++ b/src/__tests__/flag-store.test.ts @@ -1,7 +1,7 @@ // Mock external dependencies const mockGetFeatureGate = vi.fn(); const mockIsTestEnv = vi.fn(() => false); -vi.mock('../statsig', () => ({ +vi.mock('../statsig/client', () => ({ getStatsigClientSync: vi.fn(() => ({ getFeatureGate: mockGetFeatureGate, })), @@ -10,7 +10,7 @@ vi.mock('../statsig', () => ({ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; -import { createFlagStore, type FlagDefinition } from '../flags'; +import { createFlagStore, type FlagDefinition } from '../statsig'; describe('FlagStore', () => { const mockFlags = { diff --git a/src/__tests__/param-store.test.ts b/src/__tests__/param-store.test.ts index 3961eda..4e5e45e 100644 --- a/src/__tests__/param-store.test.ts +++ b/src/__tests__/param-store.test.ts @@ -3,15 +3,14 @@ import { z } from 'zod'; // Mock statsig const mockIsTestEnv = vi.fn(() => false); -vi.mock('../statsig', () => ({ +vi.mock('../statsig/client', () => ({ getStatsigClientSync: vi.fn(() => ({ getParameterStore: vi.fn(), })), isTestEnv: () => mockIsTestEnv(), })); -import { createParamStore, defineParam, type ParamStoreDefinition } from '../params'; -import { getStatsigClientSync } from '../statsig'; +import { createParamStore, defineParam, type ParamStoreDefinition, getStatsigClientSync } from '../statsig'; describe('ParamStore', () => { const testStores = { diff --git a/src/index.ts b/src/index.ts index 0e3b108..c69afb0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,54 +1,12 @@ /** - * statsig + * @lovart-open/flags * - * Type-safe Feature Flag and Parameter Store management library. + * This package uses subpath exports. Import from: + * - @lovart-open/flags/statsig - Statsig-based feature flags and parameter stores * - * Feature flag priority: URL > testOverride > override > remote > fallback(false) - * Parameter Store priority: URL > testOverride > override > remote > fallback + * @example + * import { createFlagStore, createParamStore } from '@lovart-open/flags/statsig'; */ -// Flag factory and types -export { createFlagStore, FlagStore, parseUrlOverrides, type ResolveOptions } from './flags'; - -// Param factory and types -export { - createParamStore, - defineParam, - PARAM_SCHEMA_MISMATCH_EVENT, - type ParamSchemaMismatchDetail, - parseParamStoreUrlOverrides, - ParamStore, - type ParamDefinition, - type ParamResolveOptions, - type ParamState, - type ParamStoreDefinition, - type ParamStoreHandle, - type ParamStoreValue, -} from './params'; - -// Logger -export { getLogger, type Logger } from './logger'; - -// Statsig integration -export { - getStatsigClientSync, - initStatsigClient, - isTestEnv, - type InitOptions, - type StatsigBootstrap, -} from './statsig'; - -// Core types -export { - type EvaluationOptions, - type FeatureGate, - type FlagDefinition, - type FlagKeyOf, - type FlagPriority, - type FlagSnapshot, - type FlagState, - type RemoteFlagValues, -} from './types'; - -// Statsig React bindings (re-export) -export * from '@statsig/react-bindings'; +// No exports from root path - use subpath imports +export {}; diff --git a/src/statsig/client.ts b/src/statsig/client.ts index 74dfedd..6bcbb05 100644 --- a/src/statsig/client.ts +++ b/src/statsig/client.ts @@ -5,7 +5,7 @@ import { StatsigClient, type StatsigOptions, type StatsigUser } from '@statsig/js-client'; -import { getLogger, type Logger, setLogger } from '../logger'; +import { getLogger, type Logger, setLogger } from './logger'; let client: StatsigClient | null = null; let testEnvDetector: (() => boolean) | null = null; diff --git a/src/flags.ts b/src/statsig/flags.ts similarity index 98% rename from src/flags.ts rename to src/statsig/flags.ts index a7dd921..b76bfbb 100644 --- a/src/flags.ts +++ b/src/statsig/flags.ts @@ -5,7 +5,7 @@ import { useFeatureGate } from '@statsig/react-bindings'; -import { getStatsigClientSync, isTestEnv } from './statsig'; +import { getStatsigClientSync, isTestEnv } from './client'; import type { EvaluationOptions, FeatureGate, FlagDefinition, FlagSnapshot, FlagState } from './types'; export interface ResolveOptions extends EvaluationOptions { diff --git a/src/statsig/index.ts b/src/statsig/index.ts index d2e17b7..8bf2072 100644 --- a/src/statsig/index.ts +++ b/src/statsig/index.ts @@ -1,7 +1,35 @@ /** - * Statsig module exports. + * @lovart-open/flags/statsig + * + * Type-safe Feature Flag and Parameter Store management library. + * + * Feature flag priority: URL > testOverride > override > remote > fallback(false) + * Parameter Store priority: URL > testOverride > override > remote > fallback */ +// Flag factory and types +export { createFlagStore, FlagStore, parseUrlOverrides, type ResolveOptions } from './flags'; + +// Param factory and types +export { + createParamStore, + defineParam, + PARAM_SCHEMA_MISMATCH_EVENT, + type ParamSchemaMismatchDetail, + parseParamStoreUrlOverrides, + ParamStore, + type ParamDefinition, + type ParamResolveOptions, + type ParamState, + type ParamStoreDefinition, + type ParamStoreHandle, + type ParamStoreValue, +} from './params'; + +// Logger +export { getLogger, setLogger, type Logger } from './logger'; + +// Statsig client export { getStatsigClientSync, initStatsigClient, @@ -9,3 +37,18 @@ export { type InitOptions, type StatsigBootstrap, } from './client'; + +// Core types +export { + type EvaluationOptions, + type FeatureGate, + type FlagDefinition, + type FlagKeyOf, + type FlagPriority, + type FlagSnapshot, + type FlagState, + type RemoteFlagValues, +} from './types'; + +// Statsig React bindings (re-export) +export * from '@statsig/react-bindings'; diff --git a/src/logger.ts b/src/statsig/logger.ts similarity index 100% rename from src/logger.ts rename to src/statsig/logger.ts diff --git a/src/params.ts b/src/statsig/params.ts similarity index 99% rename from src/params.ts rename to src/statsig/params.ts index 9152545..19752e5 100644 --- a/src/params.ts +++ b/src/statsig/params.ts @@ -7,7 +7,7 @@ import { merge, set } from 'lodash'; import { useParameterStore } from '@statsig/react-bindings'; import z from 'zod'; -import { getStatsigClientSync, isTestEnv } from './statsig'; +import { getStatsigClientSync, isTestEnv } from './client'; /** Parameter definition */ export interface ParamDefinition { diff --git a/src/types.ts b/src/statsig/types.ts similarity index 100% rename from src/types.ts rename to src/statsig/types.ts