@smooai/config 2.1.3 → 2.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -80,6 +80,26 @@ smooai-config = { git = "https://github.com/SmooAI/config", package = "smooai-co
80
80
  go get github.com/SmooAI/config/go/config
81
81
  ```
82
82
 
83
+ ### TypeScript Setup
84
+
85
+ Add `.smooai-config/**/*.ts` to your `tsconfig.json` so TypeScript processes your configuration files:
86
+
87
+ ```json
88
+ {
89
+ "compilerOptions": { ... },
90
+ "include": ["src/**/*", ".smooai-config/**/*.ts"]
91
+ }
92
+ ```
93
+
94
+ Or if you prefer a separate tsconfig for the config directory, create `.smooai-config/tsconfig.json`:
95
+
96
+ ```json
97
+ {
98
+ "extends": "../tsconfig.json",
99
+ "include": ["./**/*.ts"]
100
+ }
101
+ ```
102
+
83
103
  ## Usage
84
104
 
85
105
  ### TypeScript - Define Configuration Schemas
@@ -0,0 +1,30 @@
1
+ import {
2
+ ConfigContext
3
+ } from "./chunk-N6Z4AZZA.mjs";
4
+ import {
5
+ ConfigClient
6
+ } from "./chunk-3B5SRHJ2.mjs";
7
+
8
+ // src/nextjs/SmooConfigProvider.tsx
9
+ import { useEffect, useMemo } from "react";
10
+ import { jsx } from "react/jsx-runtime";
11
+ function SmooConfigProvider({ initialValues, children, ...options }) {
12
+ const client = useMemo(() => {
13
+ const c = new ConfigClient(options);
14
+ if (initialValues) {
15
+ c.seedCacheFromMap(initialValues, options.environment);
16
+ }
17
+ return c;
18
+ }, [options.baseUrl, options.apiKey, options.orgId, options.environment]);
19
+ useEffect(() => {
20
+ if (initialValues) {
21
+ client.seedCacheFromMap(initialValues, options.environment);
22
+ }
23
+ }, [initialValues, client, options.environment]);
24
+ return /* @__PURE__ */ jsx(ConfigContext.Provider, { value: client, children });
25
+ }
26
+
27
+ export {
28
+ SmooConfigProvider
29
+ };
30
+ //# sourceMappingURL=chunk-22SLF4JR.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/nextjs/SmooConfigProvider.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect, useMemo, type ReactNode } from 'react';\nimport { ConfigClient, type ConfigClientOptions } from '../platform/client';\nimport { ConfigContext } from '../react/ConfigProvider';\n\nexport interface SmooConfigProviderProps extends ConfigClientOptions {\n /** Initial config values fetched on the server (from `getConfig()`). Pre-seeds the client cache. */\n initialValues?: Record<string, unknown>;\n children: ReactNode;\n}\n\n/**\n * Client Component that provides a pre-seeded ConfigClient, eliminating loading\n * flashes on first render when used with server-fetched `initialValues`.\n *\n * ```tsx\n * // layout.tsx (Server Component)\n * const config = await getConfig({ environment: 'production' });\n *\n * // Client boundary\n * <SmooConfigProvider initialValues={config} environment=\"production\" {...clientOptions}>\n * <App />\n * </SmooConfigProvider>\n * ```\n */\nexport function SmooConfigProvider({ initialValues, children, ...options }: SmooConfigProviderProps) {\n const client = useMemo(() => {\n const c = new ConfigClient(options);\n if (initialValues) {\n c.seedCacheFromMap(initialValues, options.environment);\n }\n return c;\n }, [options.baseUrl, options.apiKey, options.orgId, options.environment]);\n\n // Re-seed if initialValues change (e.g., after revalidation)\n useEffect(() => {\n if (initialValues) {\n client.seedCacheFromMap(initialValues, options.environment);\n }\n }, [initialValues, client, options.environment]);\n\n return <ConfigContext.Provider value={client}>{children}</ConfigContext.Provider>;\n}\n"],"mappings":";;;;;;;;AAEA,SAAS,WAAW,eAA+B;AAwCxC;AAhBJ,SAAS,mBAAmB,EAAE,eAAe,UAAU,GAAG,QAAQ,GAA4B;AACjG,QAAM,SAAS,QAAQ,MAAM;AACzB,UAAM,IAAI,IAAI,aAAa,OAAO;AAClC,QAAI,eAAe;AACf,QAAE,iBAAiB,eAAe,QAAQ,WAAW;AAAA,IACzD;AACA,WAAO;AAAA,EACX,GAAG,CAAC,QAAQ,SAAS,QAAQ,QAAQ,QAAQ,OAAO,QAAQ,WAAW,CAAC;AAGxE,YAAU,MAAM;AACZ,QAAI,eAAe;AACf,aAAO,iBAAiB,eAAe,QAAQ,WAAW;AAAA,IAC9D;AAAA,EACJ,GAAG,CAAC,eAAe,QAAQ,QAAQ,WAAW,CAAC;AAE/C,SAAO,oBAAC,cAAc,UAAd,EAAuB,OAAO,QAAS,UAAS;AAC5D;","names":[]}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Client-safe feature flag utilities.
3
+ *
4
+ * These functions read feature flags from build-time environment variables
5
+ * (NEXT_PUBLIC_FEATURE_FLAG_* or VITE_FEATURE_FLAG_*) and can be used
6
+ * in both server and client components.
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * // In your .smooai-config/config.ts:
11
+ * const config = defineConfig({
12
+ * featureFlagSchema: {
13
+ * aboutPage: BooleanSchema,
14
+ * contactPage: BooleanSchema,
15
+ * },
16
+ * });
17
+ *
18
+ * // In your next.config.js, inject flags as env vars:
19
+ * // NEXT_PUBLIC_FEATURE_FLAG_ABOUT_PAGE=true
20
+ *
21
+ * // In any component (server or client):
22
+ * import { getClientFeatureFlag } from '@smooai/config/feature-flags';
23
+ *
24
+ * if (getClientFeatureFlag('aboutPage')) {
25
+ * // show the feature
26
+ * }
27
+ * ```
28
+ */
29
+ /**
30
+ * Get a feature flag value from build-time environment variables.
31
+ *
32
+ * Checks for:
33
+ * 1. NEXT_PUBLIC_FEATURE_FLAG_{KEY} (Next.js)
34
+ * 2. VITE_FEATURE_FLAG_{KEY} (Vite)
35
+ *
36
+ * The key is converted from camelCase to UPPER_SNAKE_CASE.
37
+ * e.g., "aboutPage" checks NEXT_PUBLIC_FEATURE_FLAG_ABOUT_PAGE
38
+ *
39
+ * @param key - The camelCase feature flag key
40
+ * @returns true if the flag is explicitly set to "true", false otherwise
41
+ */
42
+ declare function getClientFeatureFlag(key: string): boolean;
43
+ /**
44
+ * Create a typed feature flag checker from a config's FeatureFlagKeys.
45
+ *
46
+ * @example
47
+ * ```tsx
48
+ * import config, { FeatureFlagKeys } from '../.smooai-config/config';
49
+ * import { createFeatureFlagChecker } from '@smooai/config/feature-flags';
50
+ *
51
+ * export const getFeatureFlag = createFeatureFlagChecker<typeof FeatureFlagKeys>();
52
+ *
53
+ * // Usage:
54
+ * getFeatureFlag('aboutPage') // typed to valid keys
55
+ * ```
56
+ */
57
+ declare function createFeatureFlagChecker<T extends Record<string, string>>(): (key: T[keyof T]) => boolean;
58
+
59
+ export { createFeatureFlagChecker, getClientFeatureFlag };
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Client-safe feature flag utilities.
3
+ *
4
+ * These functions read feature flags from build-time environment variables
5
+ * (NEXT_PUBLIC_FEATURE_FLAG_* or VITE_FEATURE_FLAG_*) and can be used
6
+ * in both server and client components.
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * // In your .smooai-config/config.ts:
11
+ * const config = defineConfig({
12
+ * featureFlagSchema: {
13
+ * aboutPage: BooleanSchema,
14
+ * contactPage: BooleanSchema,
15
+ * },
16
+ * });
17
+ *
18
+ * // In your next.config.js, inject flags as env vars:
19
+ * // NEXT_PUBLIC_FEATURE_FLAG_ABOUT_PAGE=true
20
+ *
21
+ * // In any component (server or client):
22
+ * import { getClientFeatureFlag } from '@smooai/config/feature-flags';
23
+ *
24
+ * if (getClientFeatureFlag('aboutPage')) {
25
+ * // show the feature
26
+ * }
27
+ * ```
28
+ */
29
+ /**
30
+ * Get a feature flag value from build-time environment variables.
31
+ *
32
+ * Checks for:
33
+ * 1. NEXT_PUBLIC_FEATURE_FLAG_{KEY} (Next.js)
34
+ * 2. VITE_FEATURE_FLAG_{KEY} (Vite)
35
+ *
36
+ * The key is converted from camelCase to UPPER_SNAKE_CASE.
37
+ * e.g., "aboutPage" checks NEXT_PUBLIC_FEATURE_FLAG_ABOUT_PAGE
38
+ *
39
+ * @param key - The camelCase feature flag key
40
+ * @returns true if the flag is explicitly set to "true", false otherwise
41
+ */
42
+ declare function getClientFeatureFlag(key: string): boolean;
43
+ /**
44
+ * Create a typed feature flag checker from a config's FeatureFlagKeys.
45
+ *
46
+ * @example
47
+ * ```tsx
48
+ * import config, { FeatureFlagKeys } from '../.smooai-config/config';
49
+ * import { createFeatureFlagChecker } from '@smooai/config/feature-flags';
50
+ *
51
+ * export const getFeatureFlag = createFeatureFlagChecker<typeof FeatureFlagKeys>();
52
+ *
53
+ * // Usage:
54
+ * getFeatureFlag('aboutPage') // typed to valid keys
55
+ * ```
56
+ */
57
+ declare function createFeatureFlagChecker<T extends Record<string, string>>(): (key: T[keyof T]) => boolean;
58
+
59
+ export { createFeatureFlagChecker, getClientFeatureFlag };
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/feature-flags/index.ts
21
+ var feature_flags_exports = {};
22
+ __export(feature_flags_exports, {
23
+ createFeatureFlagChecker: () => createFeatureFlagChecker,
24
+ getClientFeatureFlag: () => getClientFeatureFlag
25
+ });
26
+ module.exports = __toCommonJS(feature_flags_exports);
27
+ function toUpperSnakeCase(key) {
28
+ return key.replace(/([A-Z])/g, "_$1").toUpperCase();
29
+ }
30
+ function getClientFeatureFlag(key) {
31
+ const envKey = toUpperSnakeCase(key);
32
+ const nextValue = typeof process !== "undefined" ? process.env?.[`NEXT_PUBLIC_FEATURE_FLAG_${envKey}`] : void 0;
33
+ if (nextValue !== void 0) {
34
+ return nextValue === "true" || nextValue === "1";
35
+ }
36
+ try {
37
+ const viteEnv = globalThis.__VITE_ENV__;
38
+ const viteValue = viteEnv?.[`VITE_FEATURE_FLAG_${envKey}`];
39
+ if (viteValue !== void 0) {
40
+ return viteValue === "true" || viteValue === "1";
41
+ }
42
+ } catch {
43
+ }
44
+ return false;
45
+ }
46
+ function createFeatureFlagChecker() {
47
+ return (key) => getClientFeatureFlag(key);
48
+ }
49
+ // Annotate the CommonJS export names for ESM import in node:
50
+ 0 && (module.exports = {
51
+ createFeatureFlagChecker,
52
+ getClientFeatureFlag
53
+ });
54
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/feature-flags/index.ts"],"sourcesContent":["/**\n * Client-safe feature flag utilities.\n *\n * These functions read feature flags from build-time environment variables\n * (NEXT_PUBLIC_FEATURE_FLAG_* or VITE_FEATURE_FLAG_*) and can be used\n * in both server and client components.\n *\n * @example\n * ```tsx\n * // In your .smooai-config/config.ts:\n * const config = defineConfig({\n * featureFlagSchema: {\n * aboutPage: BooleanSchema,\n * contactPage: BooleanSchema,\n * },\n * });\n *\n * // In your next.config.js, inject flags as env vars:\n * // NEXT_PUBLIC_FEATURE_FLAG_ABOUT_PAGE=true\n *\n * // In any component (server or client):\n * import { getClientFeatureFlag } from '@smooai/config/feature-flags';\n *\n * if (getClientFeatureFlag('aboutPage')) {\n * // show the feature\n * }\n * ```\n */\n\n/**\n * Convert a camelCase feature flag key to UPPER_SNAKE_CASE.\n * e.g., \"aboutPage\" → \"ABOUT_PAGE\"\n */\nfunction toUpperSnakeCase(key: string): string {\n return key.replace(/([A-Z])/g, '_$1').toUpperCase();\n}\n\n/**\n * Get a feature flag value from build-time environment variables.\n *\n * Checks for:\n * 1. NEXT_PUBLIC_FEATURE_FLAG_{KEY} (Next.js)\n * 2. VITE_FEATURE_FLAG_{KEY} (Vite)\n *\n * The key is converted from camelCase to UPPER_SNAKE_CASE.\n * e.g., \"aboutPage\" checks NEXT_PUBLIC_FEATURE_FLAG_ABOUT_PAGE\n *\n * @param key - The camelCase feature flag key\n * @returns true if the flag is explicitly set to \"true\", false otherwise\n */\nexport function getClientFeatureFlag(key: string): boolean {\n const envKey = toUpperSnakeCase(key);\n\n // Check Next.js env var\n const nextValue = typeof process !== 'undefined' ? process.env?.[`NEXT_PUBLIC_FEATURE_FLAG_${envKey}`] : undefined;\n if (nextValue !== undefined) {\n return nextValue === 'true' || nextValue === '1';\n }\n\n // Check Vite env var (available via import.meta.env in Vite)\n // At build time, Vite replaces import.meta.env.VITE_* with literal values\n // We use a dynamic approach for runtime compatibility\n try {\n const viteEnv = (globalThis as Record<string, unknown>).__VITE_ENV__ as Record<string, string> | undefined;\n const viteValue = viteEnv?.[`VITE_FEATURE_FLAG_${envKey}`];\n if (viteValue !== undefined) {\n return viteValue === 'true' || viteValue === '1';\n }\n } catch {\n // Vite env not available\n }\n\n return false;\n}\n\n/**\n * Create a typed feature flag checker from a config's FeatureFlagKeys.\n *\n * @example\n * ```tsx\n * import config, { FeatureFlagKeys } from '../.smooai-config/config';\n * import { createFeatureFlagChecker } from '@smooai/config/feature-flags';\n *\n * export const getFeatureFlag = createFeatureFlagChecker<typeof FeatureFlagKeys>();\n *\n * // Usage:\n * getFeatureFlag('aboutPage') // typed to valid keys\n * ```\n */\nexport function createFeatureFlagChecker<T extends Record<string, string>>(): (key: T[keyof T]) => boolean {\n return (key: T[keyof T]) => getClientFeatureFlag(key as string);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAiCA,SAAS,iBAAiB,KAAqB;AAC3C,SAAO,IAAI,QAAQ,YAAY,KAAK,EAAE,YAAY;AACtD;AAeO,SAAS,qBAAqB,KAAsB;AACvD,QAAM,SAAS,iBAAiB,GAAG;AAGnC,QAAM,YAAY,OAAO,YAAY,cAAc,QAAQ,MAAM,4BAA4B,MAAM,EAAE,IAAI;AACzG,MAAI,cAAc,QAAW;AACzB,WAAO,cAAc,UAAU,cAAc;AAAA,EACjD;AAKA,MAAI;AACA,UAAM,UAAW,WAAuC;AACxD,UAAM,YAAY,UAAU,qBAAqB,MAAM,EAAE;AACzD,QAAI,cAAc,QAAW;AACzB,aAAO,cAAc,UAAU,cAAc;AAAA,IACjD;AAAA,EACJ,QAAQ;AAAA,EAER;AAEA,SAAO;AACX;AAgBO,SAAS,2BAA2F;AACvG,SAAO,CAAC,QAAoB,qBAAqB,GAAa;AAClE;","names":[]}
@@ -0,0 +1,30 @@
1
+ import "../chunk-J5LGTIGS.mjs";
2
+
3
+ // src/feature-flags/index.ts
4
+ function toUpperSnakeCase(key) {
5
+ return key.replace(/([A-Z])/g, "_$1").toUpperCase();
6
+ }
7
+ function getClientFeatureFlag(key) {
8
+ const envKey = toUpperSnakeCase(key);
9
+ const nextValue = typeof process !== "undefined" ? process.env?.[`NEXT_PUBLIC_FEATURE_FLAG_${envKey}`] : void 0;
10
+ if (nextValue !== void 0) {
11
+ return nextValue === "true" || nextValue === "1";
12
+ }
13
+ try {
14
+ const viteEnv = globalThis.__VITE_ENV__;
15
+ const viteValue = viteEnv?.[`VITE_FEATURE_FLAG_${envKey}`];
16
+ if (viteValue !== void 0) {
17
+ return viteValue === "true" || viteValue === "1";
18
+ }
19
+ } catch {
20
+ }
21
+ return false;
22
+ }
23
+ function createFeatureFlagChecker() {
24
+ return (key) => getClientFeatureFlag(key);
25
+ }
26
+ export {
27
+ createFeatureFlagChecker,
28
+ getClientFeatureFlag
29
+ };
30
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/feature-flags/index.ts"],"sourcesContent":["/**\n * Client-safe feature flag utilities.\n *\n * These functions read feature flags from build-time environment variables\n * (NEXT_PUBLIC_FEATURE_FLAG_* or VITE_FEATURE_FLAG_*) and can be used\n * in both server and client components.\n *\n * @example\n * ```tsx\n * // In your .smooai-config/config.ts:\n * const config = defineConfig({\n * featureFlagSchema: {\n * aboutPage: BooleanSchema,\n * contactPage: BooleanSchema,\n * },\n * });\n *\n * // In your next.config.js, inject flags as env vars:\n * // NEXT_PUBLIC_FEATURE_FLAG_ABOUT_PAGE=true\n *\n * // In any component (server or client):\n * import { getClientFeatureFlag } from '@smooai/config/feature-flags';\n *\n * if (getClientFeatureFlag('aboutPage')) {\n * // show the feature\n * }\n * ```\n */\n\n/**\n * Convert a camelCase feature flag key to UPPER_SNAKE_CASE.\n * e.g., \"aboutPage\" → \"ABOUT_PAGE\"\n */\nfunction toUpperSnakeCase(key: string): string {\n return key.replace(/([A-Z])/g, '_$1').toUpperCase();\n}\n\n/**\n * Get a feature flag value from build-time environment variables.\n *\n * Checks for:\n * 1. NEXT_PUBLIC_FEATURE_FLAG_{KEY} (Next.js)\n * 2. VITE_FEATURE_FLAG_{KEY} (Vite)\n *\n * The key is converted from camelCase to UPPER_SNAKE_CASE.\n * e.g., \"aboutPage\" checks NEXT_PUBLIC_FEATURE_FLAG_ABOUT_PAGE\n *\n * @param key - The camelCase feature flag key\n * @returns true if the flag is explicitly set to \"true\", false otherwise\n */\nexport function getClientFeatureFlag(key: string): boolean {\n const envKey = toUpperSnakeCase(key);\n\n // Check Next.js env var\n const nextValue = typeof process !== 'undefined' ? process.env?.[`NEXT_PUBLIC_FEATURE_FLAG_${envKey}`] : undefined;\n if (nextValue !== undefined) {\n return nextValue === 'true' || nextValue === '1';\n }\n\n // Check Vite env var (available via import.meta.env in Vite)\n // At build time, Vite replaces import.meta.env.VITE_* with literal values\n // We use a dynamic approach for runtime compatibility\n try {\n const viteEnv = (globalThis as Record<string, unknown>).__VITE_ENV__ as Record<string, string> | undefined;\n const viteValue = viteEnv?.[`VITE_FEATURE_FLAG_${envKey}`];\n if (viteValue !== undefined) {\n return viteValue === 'true' || viteValue === '1';\n }\n } catch {\n // Vite env not available\n }\n\n return false;\n}\n\n/**\n * Create a typed feature flag checker from a config's FeatureFlagKeys.\n *\n * @example\n * ```tsx\n * import config, { FeatureFlagKeys } from '../.smooai-config/config';\n * import { createFeatureFlagChecker } from '@smooai/config/feature-flags';\n *\n * export const getFeatureFlag = createFeatureFlagChecker<typeof FeatureFlagKeys>();\n *\n * // Usage:\n * getFeatureFlag('aboutPage') // typed to valid keys\n * ```\n */\nexport function createFeatureFlagChecker<T extends Record<string, string>>(): (key: T[keyof T]) => boolean {\n return (key: T[keyof T]) => getClientFeatureFlag(key as string);\n}\n"],"mappings":";;;AAiCA,SAAS,iBAAiB,KAAqB;AAC3C,SAAO,IAAI,QAAQ,YAAY,KAAK,EAAE,YAAY;AACtD;AAeO,SAAS,qBAAqB,KAAsB;AACvD,QAAM,SAAS,iBAAiB,GAAG;AAGnC,QAAM,YAAY,OAAO,YAAY,cAAc,QAAQ,MAAM,4BAA4B,MAAM,EAAE,IAAI;AACzG,MAAI,cAAc,QAAW;AACzB,WAAO,cAAc,UAAU,cAAc;AAAA,EACjD;AAKA,MAAI;AACA,UAAM,UAAW,WAAuC;AACxD,UAAM,YAAY,UAAU,qBAAqB,MAAM,EAAE;AACzD,QAAI,cAAc,QAAW;AACzB,aAAO,cAAc,UAAU,cAAc;AAAA,IACjD;AAAA,EACJ,QAAQ;AAAA,EAER;AAEA,SAAO;AACX;AAgBO,SAAS,2BAA2F;AACvG,SAAO,CAAC,QAAoB,qBAAqB,GAAa;AAClE;","names":[]}
@@ -0,0 +1,27 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode } from 'react';
3
+ import { ConfigClientOptions } from '../platform/client.mjs';
4
+ export { useFeatureFlag, usePublicConfig } from './hooks.mjs';
5
+
6
+ interface SmooConfigProviderProps extends ConfigClientOptions {
7
+ /** Initial config values fetched on the server (from `getConfig()`). Pre-seeds the client cache. */
8
+ initialValues?: Record<string, unknown>;
9
+ children: ReactNode;
10
+ }
11
+ /**
12
+ * Client Component that provides a pre-seeded ConfigClient, eliminating loading
13
+ * flashes on first render when used with server-fetched `initialValues`.
14
+ *
15
+ * ```tsx
16
+ * // layout.tsx (Server Component)
17
+ * const config = await getConfig({ environment: 'production' });
18
+ *
19
+ * // Client boundary
20
+ * <SmooConfigProvider initialValues={config} environment="production" {...clientOptions}>
21
+ * <App />
22
+ * </SmooConfigProvider>
23
+ * ```
24
+ */
25
+ declare function SmooConfigProvider({ initialValues, children, ...options }: SmooConfigProviderProps): react_jsx_runtime.JSX.Element;
26
+
27
+ export { SmooConfigProvider, type SmooConfigProviderProps };
@@ -0,0 +1,27 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode } from 'react';
3
+ import { ConfigClientOptions } from '../platform/client.js';
4
+ export { useFeatureFlag, usePublicConfig } from './hooks.js';
5
+
6
+ interface SmooConfigProviderProps extends ConfigClientOptions {
7
+ /** Initial config values fetched on the server (from `getConfig()`). Pre-seeds the client cache. */
8
+ initialValues?: Record<string, unknown>;
9
+ children: ReactNode;
10
+ }
11
+ /**
12
+ * Client Component that provides a pre-seeded ConfigClient, eliminating loading
13
+ * flashes on first render when used with server-fetched `initialValues`.
14
+ *
15
+ * ```tsx
16
+ * // layout.tsx (Server Component)
17
+ * const config = await getConfig({ environment: 'production' });
18
+ *
19
+ * // Client boundary
20
+ * <SmooConfigProvider initialValues={config} environment="production" {...clientOptions}>
21
+ * <App />
22
+ * </SmooConfigProvider>
23
+ * ```
24
+ */
25
+ declare function SmooConfigProvider({ initialValues, children, ...options }: SmooConfigProviderProps): react_jsx_runtime.JSX.Element;
26
+
27
+ export { SmooConfigProvider, type SmooConfigProviderProps };
@@ -0,0 +1,238 @@
1
+ "use strict";
2
+ "use client";
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/nextjs/client.ts
22
+ var client_exports = {};
23
+ __export(client_exports, {
24
+ SmooConfigProvider: () => SmooConfigProvider,
25
+ useFeatureFlag: () => useFeatureFlag,
26
+ usePublicConfig: () => usePublicConfig
27
+ });
28
+ module.exports = __toCommonJS(client_exports);
29
+
30
+ // src/nextjs/SmooConfigProvider.tsx
31
+ var import_react2 = require("react");
32
+
33
+ // src/platform/client.ts
34
+ function getEnv(key) {
35
+ if (typeof process !== "undefined" && process.env) {
36
+ return process.env[key];
37
+ }
38
+ return void 0;
39
+ }
40
+ var ConfigClient = class {
41
+ baseUrl;
42
+ orgId;
43
+ apiKey;
44
+ defaultEnvironment;
45
+ cacheTtlMs;
46
+ cache = /* @__PURE__ */ new Map();
47
+ constructor(options = {}) {
48
+ const baseUrl = options.baseUrl ?? getEnv("SMOOAI_CONFIG_API_URL");
49
+ const apiKey = options.apiKey ?? getEnv("SMOOAI_CONFIG_API_KEY");
50
+ const orgId = options.orgId ?? getEnv("SMOOAI_CONFIG_ORG_ID");
51
+ if (!baseUrl) throw new Error("@smooai/config: baseUrl is required (or set SMOOAI_CONFIG_API_URL)");
52
+ if (!apiKey) throw new Error("@smooai/config: apiKey is required (or set SMOOAI_CONFIG_API_KEY)");
53
+ if (!orgId) throw new Error("@smooai/config: orgId is required (or set SMOOAI_CONFIG_ORG_ID)");
54
+ this.baseUrl = baseUrl.replace(/\/+$/, "");
55
+ this.apiKey = apiKey;
56
+ this.orgId = orgId;
57
+ this.defaultEnvironment = options.environment ?? getEnv("SMOOAI_CONFIG_ENV") ?? "development";
58
+ this.cacheTtlMs = options.cacheTtlMs ?? 0;
59
+ }
60
+ computeExpiresAt() {
61
+ return this.cacheTtlMs > 0 ? Date.now() + this.cacheTtlMs : 0;
62
+ }
63
+ getCached(cacheKey) {
64
+ const entry = this.cache.get(cacheKey);
65
+ if (!entry) return void 0;
66
+ if (entry.expiresAt > 0 && Date.now() > entry.expiresAt) {
67
+ this.cache.delete(cacheKey);
68
+ return void 0;
69
+ }
70
+ return entry.value;
71
+ }
72
+ async fetchJson(path, fetchOptions) {
73
+ const response = await fetch(`${this.baseUrl}${path}`, {
74
+ ...fetchOptions,
75
+ headers: { Authorization: `Bearer ${this.apiKey}`, ...fetchOptions?.headers }
76
+ });
77
+ if (!response.ok) {
78
+ throw new Error(`Config API error: HTTP ${response.status} ${response.statusText}`);
79
+ }
80
+ return response.json();
81
+ }
82
+ /**
83
+ * Get a single config value by key.
84
+ * Results are cached locally after the first fetch.
85
+ */
86
+ async getValue(key, environment) {
87
+ const env = environment ?? this.defaultEnvironment;
88
+ const cacheKey = `${env}:${key}`;
89
+ const cached = this.getCached(cacheKey);
90
+ if (cached !== void 0) {
91
+ return cached;
92
+ }
93
+ const result = await this.fetchJson(
94
+ `/organizations/${this.orgId}/config/values/${encodeURIComponent(key)}?environment=${encodeURIComponent(env)}`
95
+ );
96
+ this.cache.set(cacheKey, { value: result.value, expiresAt: this.computeExpiresAt() });
97
+ return result.value;
98
+ }
99
+ /**
100
+ * Get all config values for an environment.
101
+ * All returned values are cached locally.
102
+ * @param environment - Environment name (defaults to constructor option or SMOOAI_CONFIG_ENV)
103
+ * @param fetchOptions - Optional fetch options (e.g., Next.js `{ next: { revalidate: 60 } }`)
104
+ */
105
+ async getAllValues(environment, fetchOptions) {
106
+ const env = environment ?? this.defaultEnvironment;
107
+ const result = await this.fetchJson(
108
+ `/organizations/${this.orgId}/config/values?environment=${encodeURIComponent(env)}`,
109
+ fetchOptions
110
+ );
111
+ const expiresAt = this.computeExpiresAt();
112
+ for (const [key, value] of Object.entries(result.values)) {
113
+ this.cache.set(`${env}:${key}`, { value, expiresAt });
114
+ }
115
+ return result.values;
116
+ }
117
+ /**
118
+ * Pre-populate a single cache entry (e.g., from SSR).
119
+ * Does not make a network request.
120
+ */
121
+ seedCache(key, value, environment) {
122
+ const env = environment ?? this.defaultEnvironment;
123
+ this.cache.set(`${env}:${key}`, { value, expiresAt: this.computeExpiresAt() });
124
+ }
125
+ /**
126
+ * Pre-populate multiple cache entries from a key-value map (e.g., from SSR).
127
+ * Does not make a network request.
128
+ */
129
+ seedCacheFromMap(values, environment) {
130
+ const env = environment ?? this.defaultEnvironment;
131
+ const expiresAt = this.computeExpiresAt();
132
+ for (const [key, value] of Object.entries(values)) {
133
+ this.cache.set(`${env}:${key}`, { value, expiresAt });
134
+ }
135
+ }
136
+ /**
137
+ * Synchronously read a value from the local cache without making a network request.
138
+ * Returns `undefined` if the key is not cached or has expired.
139
+ */
140
+ getCachedValue(key, environment) {
141
+ const env = environment ?? this.defaultEnvironment;
142
+ return this.getCached(`${env}:${key}`);
143
+ }
144
+ /** Clear the entire local cache. */
145
+ invalidateCache() {
146
+ this.cache.clear();
147
+ }
148
+ /** Clear cached values for a specific environment. */
149
+ invalidateCacheForEnvironment(environment) {
150
+ const prefix = `${environment}:`;
151
+ for (const key of this.cache.keys()) {
152
+ if (key.startsWith(prefix)) {
153
+ this.cache.delete(key);
154
+ }
155
+ }
156
+ }
157
+ };
158
+
159
+ // src/react/ConfigProvider.tsx
160
+ var import_react = require("react");
161
+ var import_jsx_runtime = require("react/jsx-runtime");
162
+ var ConfigContext = (0, import_react.createContext)(null);
163
+ function useConfigClient() {
164
+ const client = (0, import_react.useContext)(ConfigContext);
165
+ if (!client) {
166
+ throw new Error("useConfigClient must be used within a <ConfigProvider>");
167
+ }
168
+ return client;
169
+ }
170
+
171
+ // src/nextjs/SmooConfigProvider.tsx
172
+ var import_jsx_runtime2 = require("react/jsx-runtime");
173
+ function SmooConfigProvider({ initialValues, children, ...options }) {
174
+ const client = (0, import_react2.useMemo)(() => {
175
+ const c = new ConfigClient(options);
176
+ if (initialValues) {
177
+ c.seedCacheFromMap(initialValues, options.environment);
178
+ }
179
+ return c;
180
+ }, [options.baseUrl, options.apiKey, options.orgId, options.environment]);
181
+ (0, import_react2.useEffect)(() => {
182
+ if (initialValues) {
183
+ client.seedCacheFromMap(initialValues, options.environment);
184
+ }
185
+ }, [initialValues, client, options.environment]);
186
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ConfigContext.Provider, { value: client, children });
187
+ }
188
+
189
+ // src/nextjs/hooks.ts
190
+ var import_react3 = require("react");
191
+ function useConfigValue(key, environment) {
192
+ const client = useConfigClient();
193
+ const [value, setValue] = (0, import_react3.useState)(() => client.getCachedValue(key, environment));
194
+ const [isLoading, setIsLoading] = (0, import_react3.useState)(value === void 0);
195
+ const [error, setError] = (0, import_react3.useState)(null);
196
+ const [fetchCount, setFetchCount] = (0, import_react3.useState)(0);
197
+ const refetch = (0, import_react3.useCallback)(() => {
198
+ client.invalidateCache();
199
+ setFetchCount((c) => c + 1);
200
+ }, [client]);
201
+ (0, import_react3.useEffect)(() => {
202
+ if (value !== void 0 && fetchCount === 0) {
203
+ setIsLoading(false);
204
+ return;
205
+ }
206
+ let cancelled = false;
207
+ setIsLoading(true);
208
+ setError(null);
209
+ client.getValue(key, environment).then((result) => {
210
+ if (!cancelled) {
211
+ setValue(result);
212
+ setIsLoading(false);
213
+ }
214
+ }).catch((err) => {
215
+ if (!cancelled) {
216
+ setError(err instanceof Error ? err : new Error(String(err)));
217
+ setIsLoading(false);
218
+ }
219
+ });
220
+ return () => {
221
+ cancelled = true;
222
+ };
223
+ }, [client, key, environment, fetchCount]);
224
+ return { value, isLoading, error, refetch };
225
+ }
226
+ function usePublicConfig(key, environment) {
227
+ return useConfigValue(key, environment);
228
+ }
229
+ function useFeatureFlag(key, environment) {
230
+ return useConfigValue(key, environment);
231
+ }
232
+ // Annotate the CommonJS export names for ESM import in node:
233
+ 0 && (module.exports = {
234
+ SmooConfigProvider,
235
+ useFeatureFlag,
236
+ usePublicConfig
237
+ });
238
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/nextjs/client.ts","../../src/nextjs/SmooConfigProvider.tsx","../../src/platform/client.ts","../../src/react/ConfigProvider.tsx","../../src/nextjs/hooks.ts"],"sourcesContent":["'use client';\n\nexport { SmooConfigProvider } from './SmooConfigProvider';\nexport type { SmooConfigProviderProps } from './SmooConfigProvider';\nexport { usePublicConfig, useFeatureFlag } from './hooks';\n","'use client';\n\nimport { useEffect, useMemo, type ReactNode } from 'react';\nimport { ConfigClient, type ConfigClientOptions } from '../platform/client';\nimport { ConfigContext } from '../react/ConfigProvider';\n\nexport interface SmooConfigProviderProps extends ConfigClientOptions {\n /** Initial config values fetched on the server (from `getConfig()`). Pre-seeds the client cache. */\n initialValues?: Record<string, unknown>;\n children: ReactNode;\n}\n\n/**\n * Client Component that provides a pre-seeded ConfigClient, eliminating loading\n * flashes on first render when used with server-fetched `initialValues`.\n *\n * ```tsx\n * // layout.tsx (Server Component)\n * const config = await getConfig({ environment: 'production' });\n *\n * // Client boundary\n * <SmooConfigProvider initialValues={config} environment=\"production\" {...clientOptions}>\n * <App />\n * </SmooConfigProvider>\n * ```\n */\nexport function SmooConfigProvider({ initialValues, children, ...options }: SmooConfigProviderProps) {\n const client = useMemo(() => {\n const c = new ConfigClient(options);\n if (initialValues) {\n c.seedCacheFromMap(initialValues, options.environment);\n }\n return c;\n }, [options.baseUrl, options.apiKey, options.orgId, options.environment]);\n\n // Re-seed if initialValues change (e.g., after revalidation)\n useEffect(() => {\n if (initialValues) {\n client.seedCacheFromMap(initialValues, options.environment);\n }\n }, [initialValues, client, options.environment]);\n\n return <ConfigContext.Provider value={client}>{children}</ConfigContext.Provider>;\n}\n","/**\n * Browser/universal HTTP client for fetching config values from the Smoo AI config server.\n *\n * Environment variables (optional — used as defaults when constructor args are omitted):\n * SMOOAI_CONFIG_API_URL — Base URL of the config API\n * SMOOAI_CONFIG_API_KEY — Bearer token for authentication\n * SMOOAI_CONFIG_ORG_ID — Organization ID\n * SMOOAI_CONFIG_ENV — Default environment name (e.g. \"production\")\n */\n\nexport interface ConfigClientOptions {\n /** Base URL of the config API server. Falls back to SMOOAI_CONFIG_API_URL env var. */\n baseUrl?: string;\n /** API key (M2M / client credentials token). Falls back to SMOOAI_CONFIG_API_KEY env var. */\n apiKey?: string;\n /** Organization ID. Falls back to SMOOAI_CONFIG_ORG_ID env var. */\n orgId?: string;\n /** Default environment name. Falls back to SMOOAI_CONFIG_ENV env var, then \"development\". */\n environment?: string;\n /** Cache TTL in milliseconds. 0 or undefined means cache never expires (manual invalidation only). */\n cacheTtlMs?: number;\n}\n\ninterface CacheEntry {\n value: unknown;\n expiresAt: number; // 0 = never expires\n}\n\nfunction getEnv(key: string): string | undefined {\n if (typeof process !== 'undefined' && process.env) {\n return process.env[key];\n }\n return undefined;\n}\n\nexport class ConfigClient {\n private baseUrl: string;\n private orgId: string;\n private apiKey: string;\n private defaultEnvironment: string;\n private cacheTtlMs: number;\n private cache: Map<string, CacheEntry> = new Map();\n\n constructor(options: ConfigClientOptions = {}) {\n const baseUrl = options.baseUrl ?? getEnv('SMOOAI_CONFIG_API_URL');\n const apiKey = options.apiKey ?? getEnv('SMOOAI_CONFIG_API_KEY');\n const orgId = options.orgId ?? getEnv('SMOOAI_CONFIG_ORG_ID');\n\n if (!baseUrl) throw new Error('@smooai/config: baseUrl is required (or set SMOOAI_CONFIG_API_URL)');\n if (!apiKey) throw new Error('@smooai/config: apiKey is required (or set SMOOAI_CONFIG_API_KEY)');\n if (!orgId) throw new Error('@smooai/config: orgId is required (or set SMOOAI_CONFIG_ORG_ID)');\n\n this.baseUrl = baseUrl.replace(/\\/+$/, '');\n this.apiKey = apiKey;\n this.orgId = orgId;\n this.defaultEnvironment = options.environment ?? getEnv('SMOOAI_CONFIG_ENV') ?? 'development';\n this.cacheTtlMs = options.cacheTtlMs ?? 0;\n }\n\n private computeExpiresAt(): number {\n return this.cacheTtlMs > 0 ? Date.now() + this.cacheTtlMs : 0;\n }\n\n private getCached(cacheKey: string): unknown | undefined {\n const entry = this.cache.get(cacheKey);\n if (!entry) return undefined;\n if (entry.expiresAt > 0 && Date.now() > entry.expiresAt) {\n this.cache.delete(cacheKey);\n return undefined;\n }\n return entry.value;\n }\n\n private async fetchJson<T>(path: string, fetchOptions?: RequestInit): Promise<T> {\n const response = await fetch(`${this.baseUrl}${path}`, {\n ...fetchOptions,\n headers: { Authorization: `Bearer ${this.apiKey}`, ...fetchOptions?.headers },\n });\n if (!response.ok) {\n throw new Error(`Config API error: HTTP ${response.status} ${response.statusText}`);\n }\n return response.json() as Promise<T>;\n }\n\n /**\n * Get a single config value by key.\n * Results are cached locally after the first fetch.\n */\n async getValue(key: string, environment?: string): Promise<unknown> {\n const env = environment ?? this.defaultEnvironment;\n const cacheKey = `${env}:${key}`;\n\n const cached = this.getCached(cacheKey);\n if (cached !== undefined) {\n return cached;\n }\n\n const result = await this.fetchJson<{ value: unknown }>(\n `/organizations/${this.orgId}/config/values/${encodeURIComponent(key)}?environment=${encodeURIComponent(env)}`,\n );\n this.cache.set(cacheKey, { value: result.value, expiresAt: this.computeExpiresAt() });\n return result.value;\n }\n\n /**\n * Get all config values for an environment.\n * All returned values are cached locally.\n * @param environment - Environment name (defaults to constructor option or SMOOAI_CONFIG_ENV)\n * @param fetchOptions - Optional fetch options (e.g., Next.js `{ next: { revalidate: 60 } }`)\n */\n async getAllValues(environment?: string, fetchOptions?: RequestInit): Promise<Record<string, unknown>> {\n const env = environment ?? this.defaultEnvironment;\n\n const result = await this.fetchJson<{ values: Record<string, unknown> }>(\n `/organizations/${this.orgId}/config/values?environment=${encodeURIComponent(env)}`,\n fetchOptions,\n );\n\n const expiresAt = this.computeExpiresAt();\n for (const [key, value] of Object.entries(result.values)) {\n this.cache.set(`${env}:${key}`, { value, expiresAt });\n }\n\n return result.values;\n }\n\n /**\n * Pre-populate a single cache entry (e.g., from SSR).\n * Does not make a network request.\n */\n seedCache(key: string, value: unknown, environment?: string): void {\n const env = environment ?? this.defaultEnvironment;\n this.cache.set(`${env}:${key}`, { value, expiresAt: this.computeExpiresAt() });\n }\n\n /**\n * Pre-populate multiple cache entries from a key-value map (e.g., from SSR).\n * Does not make a network request.\n */\n seedCacheFromMap(values: Record<string, unknown>, environment?: string): void {\n const env = environment ?? this.defaultEnvironment;\n const expiresAt = this.computeExpiresAt();\n for (const [key, value] of Object.entries(values)) {\n this.cache.set(`${env}:${key}`, { value, expiresAt });\n }\n }\n\n /**\n * Synchronously read a value from the local cache without making a network request.\n * Returns `undefined` if the key is not cached or has expired.\n */\n getCachedValue(key: string, environment?: string): unknown | undefined {\n const env = environment ?? this.defaultEnvironment;\n return this.getCached(`${env}:${key}`);\n }\n\n /** Clear the entire local cache. */\n invalidateCache(): void {\n this.cache.clear();\n }\n\n /** Clear cached values for a specific environment. */\n invalidateCacheForEnvironment(environment: string): void {\n const prefix = `${environment}:`;\n for (const key of this.cache.keys()) {\n if (key.startsWith(prefix)) {\n this.cache.delete(key);\n }\n }\n }\n}\n","'use client';\n\nimport { createContext, useContext, useMemo, type ReactNode } from 'react';\nimport { ConfigClient, type ConfigClientOptions } from '../platform/client';\n\nexport const ConfigContext = createContext<ConfigClient | null>(null);\n\nexport interface ConfigProviderProps extends ConfigClientOptions {\n children: ReactNode;\n}\n\n/**\n * Provides a ConfigClient instance to all descendant components.\n *\n * ```tsx\n * <ConfigProvider baseUrl=\"https://api.smooai.dev\" apiKey=\"...\" orgId=\"...\" environment=\"production\">\n * <App />\n * </ConfigProvider>\n * ```\n *\n * All props are optional if the corresponding environment variables are set:\n * SMOOAI_CONFIG_API_URL, SMOOAI_CONFIG_API_KEY, SMOOAI_CONFIG_ORG_ID, SMOOAI_CONFIG_ENV\n */\nexport function ConfigProvider({ children, ...options }: ConfigProviderProps) {\n const client = useMemo(() => new ConfigClient(options), [options.baseUrl, options.apiKey, options.orgId, options.environment]);\n\n return <ConfigContext.Provider value={client}>{children}</ConfigContext.Provider>;\n}\n\n/**\n * Access the ConfigClient instance from the nearest ConfigProvider.\n * Throws if used outside a ConfigProvider.\n */\nexport function useConfigClient(): ConfigClient {\n const client = useContext(ConfigContext);\n if (!client) {\n throw new Error('useConfigClient must be used within a <ConfigProvider>');\n }\n return client;\n}\n","'use client';\n\nimport { useCallback, useEffect, useState } from 'react';\nimport { useConfigClient } from '../react/ConfigProvider';\n\ninterface UseConfigResult<T = unknown> {\n /** The resolved config value, or undefined while loading (synchronous when pre-seeded). */\n value: T | undefined;\n /** True while the initial fetch is in progress. False immediately if value was pre-seeded. */\n isLoading: boolean;\n /** The error if the fetch failed. */\n error: Error | null;\n /** Re-fetch the value (bypasses cache). */\n refetch: () => void;\n}\n\nfunction useConfigValue(key: string, environment?: string): UseConfigResult {\n const client = useConfigClient();\n\n // Attempt synchronous read from pre-seeded cache (zero loading flash)\n const [value, setValue] = useState<unknown>(() => client.getCachedValue(key, environment));\n const [isLoading, setIsLoading] = useState(value === undefined);\n const [error, setError] = useState<Error | null>(null);\n const [fetchCount, setFetchCount] = useState(0);\n\n const refetch = useCallback(() => {\n client.invalidateCache();\n setFetchCount((c) => c + 1);\n }, [client]);\n\n useEffect(() => {\n // If we already have a cached value and this is the initial mount, skip fetch\n if (value !== undefined && fetchCount === 0) {\n setIsLoading(false);\n return;\n }\n\n let cancelled = false;\n setIsLoading(true);\n setError(null);\n\n client\n .getValue(key, environment)\n .then((result) => {\n if (!cancelled) {\n setValue(result);\n setIsLoading(false);\n }\n })\n .catch((err: unknown) => {\n if (!cancelled) {\n setError(err instanceof Error ? err : new Error(String(err)));\n setIsLoading(false);\n }\n });\n\n return () => {\n cancelled = true;\n };\n }, [client, key, environment, fetchCount]);\n\n return { value, isLoading, error, refetch };\n}\n\n/**\n * SSR-aware hook for fetching a public config value.\n * Returns the value synchronously when pre-seeded via `SmooConfigProvider`.\n */\nexport function usePublicConfig<T = unknown>(key: string, environment?: string): UseConfigResult<T> {\n return useConfigValue(key, environment) as UseConfigResult<T>;\n}\n\n/**\n * SSR-aware hook for fetching a feature flag value.\n * Returns the value synchronously when pre-seeded via `SmooConfigProvider`.\n */\nexport function useFeatureFlag<T = unknown>(key: string, environment?: string): UseConfigResult<T> {\n return useConfigValue(key, environment) as UseConfigResult<T>;\n}\n"],"mappings":"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","names":["import_react","import_jsx_runtime","import_react"]}
@@ -0,0 +1,17 @@
1
+ "use client";
2
+ import {
3
+ SmooConfigProvider
4
+ } from "../chunk-22SLF4JR.mjs";
5
+ import {
6
+ useFeatureFlag,
7
+ usePublicConfig
8
+ } from "../chunk-GP2JUWOD.mjs";
9
+ import "../chunk-N6Z4AZZA.mjs";
10
+ import "../chunk-3B5SRHJ2.mjs";
11
+ import "../chunk-J5LGTIGS.mjs";
12
+ export {
13
+ SmooConfigProvider,
14
+ useFeatureFlag,
15
+ usePublicConfig
16
+ };
17
+ //# sourceMappingURL=client.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -1,28 +1,6 @@
1
1
  export { GetConfigOptions, getConfig } from './getConfig.mjs';
2
- import * as react_jsx_runtime from 'react/jsx-runtime';
3
- import { ReactNode } from 'react';
4
- import { ConfigClientOptions } from '../platform/client.mjs';
2
+ export { SmooConfigProvider, SmooConfigProviderProps } from './client.mjs';
5
3
  export { useFeatureFlag, usePublicConfig } from './hooks.mjs';
6
-
7
- interface SmooConfigProviderProps extends ConfigClientOptions {
8
- /** Initial config values fetched on the server (from `getConfig()`). Pre-seeds the client cache. */
9
- initialValues?: Record<string, unknown>;
10
- children: ReactNode;
11
- }
12
- /**
13
- * Client Component that provides a pre-seeded ConfigClient, eliminating loading
14
- * flashes on first render when used with server-fetched `initialValues`.
15
- *
16
- * ```tsx
17
- * // layout.tsx (Server Component)
18
- * const config = await getConfig({ environment: 'production' });
19
- *
20
- * // Client boundary
21
- * <SmooConfigProvider initialValues={config} environment="production" {...clientOptions}>
22
- * <App />
23
- * </SmooConfigProvider>
24
- * ```
25
- */
26
- declare function SmooConfigProvider({ initialValues, children, ...options }: SmooConfigProviderProps): react_jsx_runtime.JSX.Element;
27
-
28
- export { SmooConfigProvider, type SmooConfigProviderProps };
4
+ import '../platform/client.mjs';
5
+ import 'react/jsx-runtime';
6
+ import 'react';
@@ -1,28 +1,6 @@
1
1
  export { GetConfigOptions, getConfig } from './getConfig.js';
2
- import * as react_jsx_runtime from 'react/jsx-runtime';
3
- import { ReactNode } from 'react';
4
- import { ConfigClientOptions } from '../platform/client.js';
2
+ export { SmooConfigProvider, SmooConfigProviderProps } from './client.js';
5
3
  export { useFeatureFlag, usePublicConfig } from './hooks.js';
6
-
7
- interface SmooConfigProviderProps extends ConfigClientOptions {
8
- /** Initial config values fetched on the server (from `getConfig()`). Pre-seeds the client cache. */
9
- initialValues?: Record<string, unknown>;
10
- children: ReactNode;
11
- }
12
- /**
13
- * Client Component that provides a pre-seeded ConfigClient, eliminating loading
14
- * flashes on first render when used with server-fetched `initialValues`.
15
- *
16
- * ```tsx
17
- * // layout.tsx (Server Component)
18
- * const config = await getConfig({ environment: 'production' });
19
- *
20
- * // Client boundary
21
- * <SmooConfigProvider initialValues={config} environment="production" {...clientOptions}>
22
- * <App />
23
- * </SmooConfigProvider>
24
- * ```
25
- */
26
- declare function SmooConfigProvider({ initialValues, children, ...options }: SmooConfigProviderProps): react_jsx_runtime.JSX.Element;
27
-
28
- export { SmooConfigProvider, type SmooConfigProviderProps };
4
+ import '../platform/client.js';
5
+ import 'react/jsx-runtime';
6
+ import 'react';
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/nextjs/index.ts","../../src/platform/client.ts","../../src/nextjs/getConfig.ts","../../src/nextjs/SmooConfigProvider.tsx","../../src/react/ConfigProvider.tsx","../../src/nextjs/hooks.ts"],"sourcesContent":["export { getConfig } from './getConfig';\nexport type { GetConfigOptions } from './getConfig';\nexport { SmooConfigProvider } from './SmooConfigProvider';\nexport type { SmooConfigProviderProps } from './SmooConfigProvider';\nexport { usePublicConfig, useFeatureFlag } from './hooks';\n","/**\n * Browser/universal HTTP client for fetching config values from the Smoo AI config server.\n *\n * Environment variables (optional — used as defaults when constructor args are omitted):\n * SMOOAI_CONFIG_API_URL — Base URL of the config API\n * SMOOAI_CONFIG_API_KEY — Bearer token for authentication\n * SMOOAI_CONFIG_ORG_ID — Organization ID\n * SMOOAI_CONFIG_ENV — Default environment name (e.g. \"production\")\n */\n\nexport interface ConfigClientOptions {\n /** Base URL of the config API server. Falls back to SMOOAI_CONFIG_API_URL env var. */\n baseUrl?: string;\n /** API key (M2M / client credentials token). Falls back to SMOOAI_CONFIG_API_KEY env var. */\n apiKey?: string;\n /** Organization ID. Falls back to SMOOAI_CONFIG_ORG_ID env var. */\n orgId?: string;\n /** Default environment name. Falls back to SMOOAI_CONFIG_ENV env var, then \"development\". */\n environment?: string;\n /** Cache TTL in milliseconds. 0 or undefined means cache never expires (manual invalidation only). */\n cacheTtlMs?: number;\n}\n\ninterface CacheEntry {\n value: unknown;\n expiresAt: number; // 0 = never expires\n}\n\nfunction getEnv(key: string): string | undefined {\n if (typeof process !== 'undefined' && process.env) {\n return process.env[key];\n }\n return undefined;\n}\n\nexport class ConfigClient {\n private baseUrl: string;\n private orgId: string;\n private apiKey: string;\n private defaultEnvironment: string;\n private cacheTtlMs: number;\n private cache: Map<string, CacheEntry> = new Map();\n\n constructor(options: ConfigClientOptions = {}) {\n const baseUrl = options.baseUrl ?? getEnv('SMOOAI_CONFIG_API_URL');\n const apiKey = options.apiKey ?? getEnv('SMOOAI_CONFIG_API_KEY');\n const orgId = options.orgId ?? getEnv('SMOOAI_CONFIG_ORG_ID');\n\n if (!baseUrl) throw new Error('@smooai/config: baseUrl is required (or set SMOOAI_CONFIG_API_URL)');\n if (!apiKey) throw new Error('@smooai/config: apiKey is required (or set SMOOAI_CONFIG_API_KEY)');\n if (!orgId) throw new Error('@smooai/config: orgId is required (or set SMOOAI_CONFIG_ORG_ID)');\n\n this.baseUrl = baseUrl.replace(/\\/+$/, '');\n this.apiKey = apiKey;\n this.orgId = orgId;\n this.defaultEnvironment = options.environment ?? getEnv('SMOOAI_CONFIG_ENV') ?? 'development';\n this.cacheTtlMs = options.cacheTtlMs ?? 0;\n }\n\n private computeExpiresAt(): number {\n return this.cacheTtlMs > 0 ? Date.now() + this.cacheTtlMs : 0;\n }\n\n private getCached(cacheKey: string): unknown | undefined {\n const entry = this.cache.get(cacheKey);\n if (!entry) return undefined;\n if (entry.expiresAt > 0 && Date.now() > entry.expiresAt) {\n this.cache.delete(cacheKey);\n return undefined;\n }\n return entry.value;\n }\n\n private async fetchJson<T>(path: string, fetchOptions?: RequestInit): Promise<T> {\n const response = await fetch(`${this.baseUrl}${path}`, {\n ...fetchOptions,\n headers: { Authorization: `Bearer ${this.apiKey}`, ...fetchOptions?.headers },\n });\n if (!response.ok) {\n throw new Error(`Config API error: HTTP ${response.status} ${response.statusText}`);\n }\n return response.json() as Promise<T>;\n }\n\n /**\n * Get a single config value by key.\n * Results are cached locally after the first fetch.\n */\n async getValue(key: string, environment?: string): Promise<unknown> {\n const env = environment ?? this.defaultEnvironment;\n const cacheKey = `${env}:${key}`;\n\n const cached = this.getCached(cacheKey);\n if (cached !== undefined) {\n return cached;\n }\n\n const result = await this.fetchJson<{ value: unknown }>(\n `/organizations/${this.orgId}/config/values/${encodeURIComponent(key)}?environment=${encodeURIComponent(env)}`,\n );\n this.cache.set(cacheKey, { value: result.value, expiresAt: this.computeExpiresAt() });\n return result.value;\n }\n\n /**\n * Get all config values for an environment.\n * All returned values are cached locally.\n * @param environment - Environment name (defaults to constructor option or SMOOAI_CONFIG_ENV)\n * @param fetchOptions - Optional fetch options (e.g., Next.js `{ next: { revalidate: 60 } }`)\n */\n async getAllValues(environment?: string, fetchOptions?: RequestInit): Promise<Record<string, unknown>> {\n const env = environment ?? this.defaultEnvironment;\n\n const result = await this.fetchJson<{ values: Record<string, unknown> }>(\n `/organizations/${this.orgId}/config/values?environment=${encodeURIComponent(env)}`,\n fetchOptions,\n );\n\n const expiresAt = this.computeExpiresAt();\n for (const [key, value] of Object.entries(result.values)) {\n this.cache.set(`${env}:${key}`, { value, expiresAt });\n }\n\n return result.values;\n }\n\n /**\n * Pre-populate a single cache entry (e.g., from SSR).\n * Does not make a network request.\n */\n seedCache(key: string, value: unknown, environment?: string): void {\n const env = environment ?? this.defaultEnvironment;\n this.cache.set(`${env}:${key}`, { value, expiresAt: this.computeExpiresAt() });\n }\n\n /**\n * Pre-populate multiple cache entries from a key-value map (e.g., from SSR).\n * Does not make a network request.\n */\n seedCacheFromMap(values: Record<string, unknown>, environment?: string): void {\n const env = environment ?? this.defaultEnvironment;\n const expiresAt = this.computeExpiresAt();\n for (const [key, value] of Object.entries(values)) {\n this.cache.set(`${env}:${key}`, { value, expiresAt });\n }\n }\n\n /**\n * Synchronously read a value from the local cache without making a network request.\n * Returns `undefined` if the key is not cached or has expired.\n */\n getCachedValue(key: string, environment?: string): unknown | undefined {\n const env = environment ?? this.defaultEnvironment;\n return this.getCached(`${env}:${key}`);\n }\n\n /** Clear the entire local cache. */\n invalidateCache(): void {\n this.cache.clear();\n }\n\n /** Clear cached values for a specific environment. */\n invalidateCacheForEnvironment(environment: string): void {\n const prefix = `${environment}:`;\n for (const key of this.cache.keys()) {\n if (key.startsWith(prefix)) {\n this.cache.delete(key);\n }\n }\n }\n}\n","import { ConfigClient, type ConfigClientOptions } from '../platform/client';\n\nexport interface GetConfigOptions extends ConfigClientOptions {\n /** Config keys to fetch. If omitted, fetches all values for the environment. */\n keys?: string[];\n /** Additional fetch options passed to the underlying HTTP call (e.g., Next.js `{ next: { revalidate: 60 } }`). */\n fetchOptions?: RequestInit;\n}\n\n/**\n * Server-side helper for fetching config values in Next.js Server Components or `getServerSideProps`.\n *\n * ```tsx\n * // app/layout.tsx (Server Component)\n * import { getConfig } from '@smooai/config/nextjs';\n *\n * export default async function RootLayout({ children }) {\n * const config = await getConfig({\n * environment: 'production',\n * fetchOptions: { next: { revalidate: 60 } },\n * });\n * return (\n * <SmooConfigProvider initialValues={config}>\n * {children}\n * </SmooConfigProvider>\n * );\n * }\n * ```\n */\nexport async function getConfig(options: GetConfigOptions = {}): Promise<Record<string, unknown>> {\n const { keys, fetchOptions, ...clientOptions } = options;\n const client = new ConfigClient(clientOptions);\n\n if (keys && keys.length > 0) {\n const result: Record<string, unknown> = {};\n await Promise.all(\n keys.map(async (key) => {\n result[key] = await client.getValue(key, clientOptions.environment);\n }),\n );\n return result;\n }\n\n return client.getAllValues(clientOptions.environment, fetchOptions);\n}\n","'use client';\n\nimport { useEffect, useMemo, type ReactNode } from 'react';\nimport { ConfigClient, type ConfigClientOptions } from '../platform/client';\nimport { ConfigContext } from '../react/ConfigProvider';\n\nexport interface SmooConfigProviderProps extends ConfigClientOptions {\n /** Initial config values fetched on the server (from `getConfig()`). Pre-seeds the client cache. */\n initialValues?: Record<string, unknown>;\n children: ReactNode;\n}\n\n/**\n * Client Component that provides a pre-seeded ConfigClient, eliminating loading\n * flashes on first render when used with server-fetched `initialValues`.\n *\n * ```tsx\n * // layout.tsx (Server Component)\n * const config = await getConfig({ environment: 'production' });\n *\n * // Client boundary\n * <SmooConfigProvider initialValues={config} environment=\"production\" {...clientOptions}>\n * <App />\n * </SmooConfigProvider>\n * ```\n */\nexport function SmooConfigProvider({ initialValues, children, ...options }: SmooConfigProviderProps) {\n const client = useMemo(() => {\n const c = new ConfigClient(options);\n if (initialValues) {\n c.seedCacheFromMap(initialValues, options.environment);\n }\n return c;\n }, [options.baseUrl, options.apiKey, options.orgId, options.environment]);\n\n // Re-seed if initialValues change (e.g., after revalidation)\n useEffect(() => {\n if (initialValues) {\n client.seedCacheFromMap(initialValues, options.environment);\n }\n }, [initialValues, client, options.environment]);\n\n return <ConfigContext.Provider value={client}>{children}</ConfigContext.Provider>;\n}\n","'use client';\n\nimport { createContext, useContext, useMemo, type ReactNode } from 'react';\nimport { ConfigClient, type ConfigClientOptions } from '../platform/client';\n\nexport const ConfigContext = createContext<ConfigClient | null>(null);\n\nexport interface ConfigProviderProps extends ConfigClientOptions {\n children: ReactNode;\n}\n\n/**\n * Provides a ConfigClient instance to all descendant components.\n *\n * ```tsx\n * <ConfigProvider baseUrl=\"https://api.smooai.dev\" apiKey=\"...\" orgId=\"...\" environment=\"production\">\n * <App />\n * </ConfigProvider>\n * ```\n *\n * All props are optional if the corresponding environment variables are set:\n * SMOOAI_CONFIG_API_URL, SMOOAI_CONFIG_API_KEY, SMOOAI_CONFIG_ORG_ID, SMOOAI_CONFIG_ENV\n */\nexport function ConfigProvider({ children, ...options }: ConfigProviderProps) {\n const client = useMemo(() => new ConfigClient(options), [options.baseUrl, options.apiKey, options.orgId, options.environment]);\n\n return <ConfigContext.Provider value={client}>{children}</ConfigContext.Provider>;\n}\n\n/**\n * Access the ConfigClient instance from the nearest ConfigProvider.\n * Throws if used outside a ConfigProvider.\n */\nexport function useConfigClient(): ConfigClient {\n const client = useContext(ConfigContext);\n if (!client) {\n throw new Error('useConfigClient must be used within a <ConfigProvider>');\n }\n return client;\n}\n","'use client';\n\nimport { useCallback, useEffect, useState } from 'react';\nimport { useConfigClient } from '../react/ConfigProvider';\n\ninterface UseConfigResult<T = unknown> {\n /** The resolved config value, or undefined while loading (synchronous when pre-seeded). */\n value: T | undefined;\n /** True while the initial fetch is in progress. False immediately if value was pre-seeded. */\n isLoading: boolean;\n /** The error if the fetch failed. */\n error: Error | null;\n /** Re-fetch the value (bypasses cache). */\n refetch: () => void;\n}\n\nfunction useConfigValue(key: string, environment?: string): UseConfigResult {\n const client = useConfigClient();\n\n // Attempt synchronous read from pre-seeded cache (zero loading flash)\n const [value, setValue] = useState<unknown>(() => client.getCachedValue(key, environment));\n const [isLoading, setIsLoading] = useState(value === undefined);\n const [error, setError] = useState<Error | null>(null);\n const [fetchCount, setFetchCount] = useState(0);\n\n const refetch = useCallback(() => {\n client.invalidateCache();\n setFetchCount((c) => c + 1);\n }, [client]);\n\n useEffect(() => {\n // If we already have a cached value and this is the initial mount, skip fetch\n if (value !== undefined && fetchCount === 0) {\n setIsLoading(false);\n return;\n }\n\n let cancelled = false;\n setIsLoading(true);\n setError(null);\n\n client\n .getValue(key, environment)\n .then((result) => {\n if (!cancelled) {\n setValue(result);\n setIsLoading(false);\n }\n })\n .catch((err: unknown) => {\n if (!cancelled) {\n setError(err instanceof Error ? err : new Error(String(err)));\n setIsLoading(false);\n }\n });\n\n return () => {\n cancelled = true;\n };\n }, [client, key, environment, fetchCount]);\n\n return { value, isLoading, error, refetch };\n}\n\n/**\n * SSR-aware hook for fetching a public config value.\n * Returns the value synchronously when pre-seeded via `SmooConfigProvider`.\n */\nexport function usePublicConfig<T = unknown>(key: string, environment?: string): UseConfigResult<T> {\n return useConfigValue(key, environment) as UseConfigResult<T>;\n}\n\n/**\n * SSR-aware hook for fetching a feature flag value.\n * Returns the value synchronously when pre-seeded via `SmooConfigProvider`.\n */\nexport function useFeatureFlag<T = unknown>(key: string, environment?: string): UseConfigResult<T> {\n return useConfigValue(key, environment) as UseConfigResult<T>;\n}\n"],"mappings":"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","names":["import_react","import_jsx_runtime","import_react"]}
1
+ {"version":3,"sources":["../../src/nextjs/index.ts","../../src/platform/client.ts","../../src/nextjs/getConfig.ts","../../src/nextjs/SmooConfigProvider.tsx","../../src/react/ConfigProvider.tsx","../../src/nextjs/hooks.ts"],"sourcesContent":["export { getConfig } from './getConfig';\nexport type { GetConfigOptions } from './getConfig';\n\n// Client components re-exported for backwards compatibility.\n// Prefer importing from '@smooai/config/nextjs/client' to avoid\n// pulling React (createContext) into server bundles.\nexport { SmooConfigProvider } from './SmooConfigProvider';\nexport type { SmooConfigProviderProps } from './SmooConfigProvider';\nexport { usePublicConfig, useFeatureFlag } from './hooks';\n","/**\n * Browser/universal HTTP client for fetching config values from the Smoo AI config server.\n *\n * Environment variables (optional — used as defaults when constructor args are omitted):\n * SMOOAI_CONFIG_API_URL — Base URL of the config API\n * SMOOAI_CONFIG_API_KEY — Bearer token for authentication\n * SMOOAI_CONFIG_ORG_ID — Organization ID\n * SMOOAI_CONFIG_ENV — Default environment name (e.g. \"production\")\n */\n\nexport interface ConfigClientOptions {\n /** Base URL of the config API server. Falls back to SMOOAI_CONFIG_API_URL env var. */\n baseUrl?: string;\n /** API key (M2M / client credentials token). Falls back to SMOOAI_CONFIG_API_KEY env var. */\n apiKey?: string;\n /** Organization ID. Falls back to SMOOAI_CONFIG_ORG_ID env var. */\n orgId?: string;\n /** Default environment name. Falls back to SMOOAI_CONFIG_ENV env var, then \"development\". */\n environment?: string;\n /** Cache TTL in milliseconds. 0 or undefined means cache never expires (manual invalidation only). */\n cacheTtlMs?: number;\n}\n\ninterface CacheEntry {\n value: unknown;\n expiresAt: number; // 0 = never expires\n}\n\nfunction getEnv(key: string): string | undefined {\n if (typeof process !== 'undefined' && process.env) {\n return process.env[key];\n }\n return undefined;\n}\n\nexport class ConfigClient {\n private baseUrl: string;\n private orgId: string;\n private apiKey: string;\n private defaultEnvironment: string;\n private cacheTtlMs: number;\n private cache: Map<string, CacheEntry> = new Map();\n\n constructor(options: ConfigClientOptions = {}) {\n const baseUrl = options.baseUrl ?? getEnv('SMOOAI_CONFIG_API_URL');\n const apiKey = options.apiKey ?? getEnv('SMOOAI_CONFIG_API_KEY');\n const orgId = options.orgId ?? getEnv('SMOOAI_CONFIG_ORG_ID');\n\n if (!baseUrl) throw new Error('@smooai/config: baseUrl is required (or set SMOOAI_CONFIG_API_URL)');\n if (!apiKey) throw new Error('@smooai/config: apiKey is required (or set SMOOAI_CONFIG_API_KEY)');\n if (!orgId) throw new Error('@smooai/config: orgId is required (or set SMOOAI_CONFIG_ORG_ID)');\n\n this.baseUrl = baseUrl.replace(/\\/+$/, '');\n this.apiKey = apiKey;\n this.orgId = orgId;\n this.defaultEnvironment = options.environment ?? getEnv('SMOOAI_CONFIG_ENV') ?? 'development';\n this.cacheTtlMs = options.cacheTtlMs ?? 0;\n }\n\n private computeExpiresAt(): number {\n return this.cacheTtlMs > 0 ? Date.now() + this.cacheTtlMs : 0;\n }\n\n private getCached(cacheKey: string): unknown | undefined {\n const entry = this.cache.get(cacheKey);\n if (!entry) return undefined;\n if (entry.expiresAt > 0 && Date.now() > entry.expiresAt) {\n this.cache.delete(cacheKey);\n return undefined;\n }\n return entry.value;\n }\n\n private async fetchJson<T>(path: string, fetchOptions?: RequestInit): Promise<T> {\n const response = await fetch(`${this.baseUrl}${path}`, {\n ...fetchOptions,\n headers: { Authorization: `Bearer ${this.apiKey}`, ...fetchOptions?.headers },\n });\n if (!response.ok) {\n throw new Error(`Config API error: HTTP ${response.status} ${response.statusText}`);\n }\n return response.json() as Promise<T>;\n }\n\n /**\n * Get a single config value by key.\n * Results are cached locally after the first fetch.\n */\n async getValue(key: string, environment?: string): Promise<unknown> {\n const env = environment ?? this.defaultEnvironment;\n const cacheKey = `${env}:${key}`;\n\n const cached = this.getCached(cacheKey);\n if (cached !== undefined) {\n return cached;\n }\n\n const result = await this.fetchJson<{ value: unknown }>(\n `/organizations/${this.orgId}/config/values/${encodeURIComponent(key)}?environment=${encodeURIComponent(env)}`,\n );\n this.cache.set(cacheKey, { value: result.value, expiresAt: this.computeExpiresAt() });\n return result.value;\n }\n\n /**\n * Get all config values for an environment.\n * All returned values are cached locally.\n * @param environment - Environment name (defaults to constructor option or SMOOAI_CONFIG_ENV)\n * @param fetchOptions - Optional fetch options (e.g., Next.js `{ next: { revalidate: 60 } }`)\n */\n async getAllValues(environment?: string, fetchOptions?: RequestInit): Promise<Record<string, unknown>> {\n const env = environment ?? this.defaultEnvironment;\n\n const result = await this.fetchJson<{ values: Record<string, unknown> }>(\n `/organizations/${this.orgId}/config/values?environment=${encodeURIComponent(env)}`,\n fetchOptions,\n );\n\n const expiresAt = this.computeExpiresAt();\n for (const [key, value] of Object.entries(result.values)) {\n this.cache.set(`${env}:${key}`, { value, expiresAt });\n }\n\n return result.values;\n }\n\n /**\n * Pre-populate a single cache entry (e.g., from SSR).\n * Does not make a network request.\n */\n seedCache(key: string, value: unknown, environment?: string): void {\n const env = environment ?? this.defaultEnvironment;\n this.cache.set(`${env}:${key}`, { value, expiresAt: this.computeExpiresAt() });\n }\n\n /**\n * Pre-populate multiple cache entries from a key-value map (e.g., from SSR).\n * Does not make a network request.\n */\n seedCacheFromMap(values: Record<string, unknown>, environment?: string): void {\n const env = environment ?? this.defaultEnvironment;\n const expiresAt = this.computeExpiresAt();\n for (const [key, value] of Object.entries(values)) {\n this.cache.set(`${env}:${key}`, { value, expiresAt });\n }\n }\n\n /**\n * Synchronously read a value from the local cache without making a network request.\n * Returns `undefined` if the key is not cached or has expired.\n */\n getCachedValue(key: string, environment?: string): unknown | undefined {\n const env = environment ?? this.defaultEnvironment;\n return this.getCached(`${env}:${key}`);\n }\n\n /** Clear the entire local cache. */\n invalidateCache(): void {\n this.cache.clear();\n }\n\n /** Clear cached values for a specific environment. */\n invalidateCacheForEnvironment(environment: string): void {\n const prefix = `${environment}:`;\n for (const key of this.cache.keys()) {\n if (key.startsWith(prefix)) {\n this.cache.delete(key);\n }\n }\n }\n}\n","import { ConfigClient, type ConfigClientOptions } from '../platform/client';\n\nexport interface GetConfigOptions extends ConfigClientOptions {\n /** Config keys to fetch. If omitted, fetches all values for the environment. */\n keys?: string[];\n /** Additional fetch options passed to the underlying HTTP call (e.g., Next.js `{ next: { revalidate: 60 } }`). */\n fetchOptions?: RequestInit;\n}\n\n/**\n * Server-side helper for fetching config values in Next.js Server Components or `getServerSideProps`.\n *\n * ```tsx\n * // app/layout.tsx (Server Component)\n * import { getConfig } from '@smooai/config/nextjs';\n *\n * export default async function RootLayout({ children }) {\n * const config = await getConfig({\n * environment: 'production',\n * fetchOptions: { next: { revalidate: 60 } },\n * });\n * return (\n * <SmooConfigProvider initialValues={config}>\n * {children}\n * </SmooConfigProvider>\n * );\n * }\n * ```\n */\nexport async function getConfig(options: GetConfigOptions = {}): Promise<Record<string, unknown>> {\n const { keys, fetchOptions, ...clientOptions } = options;\n const client = new ConfigClient(clientOptions);\n\n if (keys && keys.length > 0) {\n const result: Record<string, unknown> = {};\n await Promise.all(\n keys.map(async (key) => {\n result[key] = await client.getValue(key, clientOptions.environment);\n }),\n );\n return result;\n }\n\n return client.getAllValues(clientOptions.environment, fetchOptions);\n}\n","'use client';\n\nimport { useEffect, useMemo, type ReactNode } from 'react';\nimport { ConfigClient, type ConfigClientOptions } from '../platform/client';\nimport { ConfigContext } from '../react/ConfigProvider';\n\nexport interface SmooConfigProviderProps extends ConfigClientOptions {\n /** Initial config values fetched on the server (from `getConfig()`). Pre-seeds the client cache. */\n initialValues?: Record<string, unknown>;\n children: ReactNode;\n}\n\n/**\n * Client Component that provides a pre-seeded ConfigClient, eliminating loading\n * flashes on first render when used with server-fetched `initialValues`.\n *\n * ```tsx\n * // layout.tsx (Server Component)\n * const config = await getConfig({ environment: 'production' });\n *\n * // Client boundary\n * <SmooConfigProvider initialValues={config} environment=\"production\" {...clientOptions}>\n * <App />\n * </SmooConfigProvider>\n * ```\n */\nexport function SmooConfigProvider({ initialValues, children, ...options }: SmooConfigProviderProps) {\n const client = useMemo(() => {\n const c = new ConfigClient(options);\n if (initialValues) {\n c.seedCacheFromMap(initialValues, options.environment);\n }\n return c;\n }, [options.baseUrl, options.apiKey, options.orgId, options.environment]);\n\n // Re-seed if initialValues change (e.g., after revalidation)\n useEffect(() => {\n if (initialValues) {\n client.seedCacheFromMap(initialValues, options.environment);\n }\n }, [initialValues, client, options.environment]);\n\n return <ConfigContext.Provider value={client}>{children}</ConfigContext.Provider>;\n}\n","'use client';\n\nimport { createContext, useContext, useMemo, type ReactNode } from 'react';\nimport { ConfigClient, type ConfigClientOptions } from '../platform/client';\n\nexport const ConfigContext = createContext<ConfigClient | null>(null);\n\nexport interface ConfigProviderProps extends ConfigClientOptions {\n children: ReactNode;\n}\n\n/**\n * Provides a ConfigClient instance to all descendant components.\n *\n * ```tsx\n * <ConfigProvider baseUrl=\"https://api.smooai.dev\" apiKey=\"...\" orgId=\"...\" environment=\"production\">\n * <App />\n * </ConfigProvider>\n * ```\n *\n * All props are optional if the corresponding environment variables are set:\n * SMOOAI_CONFIG_API_URL, SMOOAI_CONFIG_API_KEY, SMOOAI_CONFIG_ORG_ID, SMOOAI_CONFIG_ENV\n */\nexport function ConfigProvider({ children, ...options }: ConfigProviderProps) {\n const client = useMemo(() => new ConfigClient(options), [options.baseUrl, options.apiKey, options.orgId, options.environment]);\n\n return <ConfigContext.Provider value={client}>{children}</ConfigContext.Provider>;\n}\n\n/**\n * Access the ConfigClient instance from the nearest ConfigProvider.\n * Throws if used outside a ConfigProvider.\n */\nexport function useConfigClient(): ConfigClient {\n const client = useContext(ConfigContext);\n if (!client) {\n throw new Error('useConfigClient must be used within a <ConfigProvider>');\n }\n return client;\n}\n","'use client';\n\nimport { useCallback, useEffect, useState } from 'react';\nimport { useConfigClient } from '../react/ConfigProvider';\n\ninterface UseConfigResult<T = unknown> {\n /** The resolved config value, or undefined while loading (synchronous when pre-seeded). */\n value: T | undefined;\n /** True while the initial fetch is in progress. False immediately if value was pre-seeded. */\n isLoading: boolean;\n /** The error if the fetch failed. */\n error: Error | null;\n /** Re-fetch the value (bypasses cache). */\n refetch: () => void;\n}\n\nfunction useConfigValue(key: string, environment?: string): UseConfigResult {\n const client = useConfigClient();\n\n // Attempt synchronous read from pre-seeded cache (zero loading flash)\n const [value, setValue] = useState<unknown>(() => client.getCachedValue(key, environment));\n const [isLoading, setIsLoading] = useState(value === undefined);\n const [error, setError] = useState<Error | null>(null);\n const [fetchCount, setFetchCount] = useState(0);\n\n const refetch = useCallback(() => {\n client.invalidateCache();\n setFetchCount((c) => c + 1);\n }, [client]);\n\n useEffect(() => {\n // If we already have a cached value and this is the initial mount, skip fetch\n if (value !== undefined && fetchCount === 0) {\n setIsLoading(false);\n return;\n }\n\n let cancelled = false;\n setIsLoading(true);\n setError(null);\n\n client\n .getValue(key, environment)\n .then((result) => {\n if (!cancelled) {\n setValue(result);\n setIsLoading(false);\n }\n })\n .catch((err: unknown) => {\n if (!cancelled) {\n setError(err instanceof Error ? err : new Error(String(err)));\n setIsLoading(false);\n }\n });\n\n return () => {\n cancelled = true;\n };\n }, [client, key, environment, fetchCount]);\n\n return { value, isLoading, error, refetch };\n}\n\n/**\n * SSR-aware hook for fetching a public config value.\n * Returns the value synchronously when pre-seeded via `SmooConfigProvider`.\n */\nexport function usePublicConfig<T = unknown>(key: string, environment?: string): UseConfigResult<T> {\n return useConfigValue(key, environment) as UseConfigResult<T>;\n}\n\n/**\n * SSR-aware hook for fetching a feature flag value.\n * Returns the value synchronously when pre-seeded via `SmooConfigProvider`.\n */\nexport function useFeatureFlag<T = unknown>(key: string, environment?: string): UseConfigResult<T> {\n return useConfigValue(key, environment) as UseConfigResult<T>;\n}\n"],"mappings":"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","names":["import_react","import_jsx_runtime","import_react"]}
@@ -1,36 +1,16 @@
1
1
  import {
2
- useFeatureFlag,
3
- usePublicConfig
4
- } from "../chunk-GP2JUWOD.mjs";
5
- import {
6
- ConfigContext
7
- } from "../chunk-N6Z4AZZA.mjs";
2
+ SmooConfigProvider
3
+ } from "../chunk-22SLF4JR.mjs";
8
4
  import {
9
5
  getConfig
10
6
  } from "../chunk-FAF45VZA.mjs";
11
7
  import {
12
- ConfigClient
13
- } from "../chunk-3B5SRHJ2.mjs";
8
+ useFeatureFlag,
9
+ usePublicConfig
10
+ } from "../chunk-GP2JUWOD.mjs";
11
+ import "../chunk-N6Z4AZZA.mjs";
12
+ import "../chunk-3B5SRHJ2.mjs";
14
13
  import "../chunk-J5LGTIGS.mjs";
15
-
16
- // src/nextjs/SmooConfigProvider.tsx
17
- import { useEffect, useMemo } from "react";
18
- import { jsx } from "react/jsx-runtime";
19
- function SmooConfigProvider({ initialValues, children, ...options }) {
20
- const client = useMemo(() => {
21
- const c = new ConfigClient(options);
22
- if (initialValues) {
23
- c.seedCacheFromMap(initialValues, options.environment);
24
- }
25
- return c;
26
- }, [options.baseUrl, options.apiKey, options.orgId, options.environment]);
27
- useEffect(() => {
28
- if (initialValues) {
29
- client.seedCacheFromMap(initialValues, options.environment);
30
- }
31
- }, [initialValues, client, options.environment]);
32
- return /* @__PURE__ */ jsx(ConfigContext.Provider, { value: client, children });
33
- }
34
14
  export {
35
15
  SmooConfigProvider,
36
16
  getConfig,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/nextjs/SmooConfigProvider.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect, useMemo, type ReactNode } from 'react';\nimport { ConfigClient, type ConfigClientOptions } from '../platform/client';\nimport { ConfigContext } from '../react/ConfigProvider';\n\nexport interface SmooConfigProviderProps extends ConfigClientOptions {\n /** Initial config values fetched on the server (from `getConfig()`). Pre-seeds the client cache. */\n initialValues?: Record<string, unknown>;\n children: ReactNode;\n}\n\n/**\n * Client Component that provides a pre-seeded ConfigClient, eliminating loading\n * flashes on first render when used with server-fetched `initialValues`.\n *\n * ```tsx\n * // layout.tsx (Server Component)\n * const config = await getConfig({ environment: 'production' });\n *\n * // Client boundary\n * <SmooConfigProvider initialValues={config} environment=\"production\" {...clientOptions}>\n * <App />\n * </SmooConfigProvider>\n * ```\n */\nexport function SmooConfigProvider({ initialValues, children, ...options }: SmooConfigProviderProps) {\n const client = useMemo(() => {\n const c = new ConfigClient(options);\n if (initialValues) {\n c.seedCacheFromMap(initialValues, options.environment);\n }\n return c;\n }, [options.baseUrl, options.apiKey, options.orgId, options.environment]);\n\n // Re-seed if initialValues change (e.g., after revalidation)\n useEffect(() => {\n if (initialValues) {\n client.seedCacheFromMap(initialValues, options.environment);\n }\n }, [initialValues, client, options.environment]);\n\n return <ConfigContext.Provider value={client}>{children}</ConfigContext.Provider>;\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAEA,SAAS,WAAW,eAA+B;AAwCxC;AAhBJ,SAAS,mBAAmB,EAAE,eAAe,UAAU,GAAG,QAAQ,GAA4B;AACjG,QAAM,SAAS,QAAQ,MAAM;AACzB,UAAM,IAAI,IAAI,aAAa,OAAO;AAClC,QAAI,eAAe;AACf,QAAE,iBAAiB,eAAe,QAAQ,WAAW;AAAA,IACzD;AACA,WAAO;AAAA,EACX,GAAG,CAAC,QAAQ,SAAS,QAAQ,QAAQ,QAAQ,OAAO,QAAQ,WAAW,CAAC;AAGxE,YAAU,MAAM;AACZ,QAAI,eAAe;AACf,aAAO,iBAAiB,eAAe,QAAQ,WAAW;AAAA,IAC9D;AAAA,EACJ,GAAG,CAAC,eAAe,QAAQ,QAAQ,WAAW,CAAC;AAE/C,SAAO,oBAAC,cAAc,UAAd,EAAuB,OAAO,QAAS,UAAS;AAC5D;","names":[]}
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smooai/config",
3
- "version": "2.1.3",
3
+ "version": "2.1.5",
4
4
  "description": "Type-safe multi-language configuration management with schema validation, three-tier config (public, secret, feature flags), and runtime client support for TypeScript, Python, Rust, and Go.",
5
5
  "homepage": "https://github.com/SmooAI/config#readme",
6
6
  "bugs": {
@@ -37,6 +37,21 @@
37
37
  "import": "./dist/nextjs/index.mjs",
38
38
  "require": "./dist/nextjs/index.js"
39
39
  },
40
+ "./nextjs/client": {
41
+ "types": "./dist/nextjs/client.d.ts",
42
+ "import": "./dist/nextjs/client.mjs",
43
+ "require": "./dist/nextjs/client.js"
44
+ },
45
+ "./nextjs/getConfig": {
46
+ "types": "./dist/nextjs/getConfig.d.ts",
47
+ "import": "./dist/nextjs/getConfig.mjs",
48
+ "require": "./dist/nextjs/getConfig.js"
49
+ },
50
+ "./feature-flags": {
51
+ "types": "./dist/feature-flags/index.d.ts",
52
+ "import": "./dist/feature-flags/index.mjs",
53
+ "require": "./dist/feature-flags/index.js"
54
+ },
40
55
  "./react": {
41
56
  "types": "./dist/react/index.d.ts",
42
57
  "import": "./dist/react/index.mjs",