rps-flagforge 1.1.2 → 1.1.3

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.
File without changes
@@ -1 +1 @@
1
- {"version":3,"sources":["../../bin/mergeSchema.ts"],"sourcesContent":["#!/usr/bin/env node\r\nimport fs from \"fs\";\r\nimport path from \"path\";\r\nimport chalk from \"chalk\";\r\nimport prompts from \"prompts\";\r\n\r\n// -----------------------------\r\n// CLI Flags\r\n// -----------------------------\r\nconst args = process.argv.slice(2);\r\nconst isDryRun = args.includes(\"--dry-run\");\r\nconst isForce = args.includes(\"--force\");\r\n\r\n// -----------------------------\r\n// Paths\r\n// -----------------------------\r\nconst appSchema = path.resolve(process.cwd(), \"prisma/schema.prisma\");\r\nconst packageSchema = path.resolve(__dirname, \"../../prisma/schema.prisma\");\r\n\r\n// -----------------------------\r\n// Helpers\r\n// -----------------------------\r\nfunction extractBlocks(schema: string, type: \"model\" | \"enum\"): string[] {\r\n const regex = new RegExp(`${type}\\\\s+\\\\w+\\\\s+{[\\\\s\\\\S]*?}\\\\n?`, \"g\");\r\n return schema.match(regex) || [];\r\n}\r\n\r\nfunction parseBlockFields(block: string) {\r\n return block\r\n .split(\"\\n\")\r\n .map(l => l.trim())\r\n .filter(\r\n l =>\r\n l &&\r\n !l.startsWith(\"//\") &&\r\n !l.startsWith(\"model\") &&\r\n !l.startsWith(\"enum\") &&\r\n !l.startsWith(\"{\") &&\r\n !l.startsWith(\"}\")\r\n );\r\n}\r\n\r\nfunction mergeModelFields(existing: string, incoming: string) {\r\n const existingFields = parseBlockFields(existing);\r\n const incomingFields = parseBlockFields(incoming);\r\n\r\n const mergedFields: string[] = [...existingFields];\r\n const diff: string[] = [];\r\n\r\n const map = new Map(existingFields.map(f => [f.split(\" \")[0], f]));\r\n\r\n for (const field of incomingFields) {\r\n const name = field.split(\" \")[0];\r\n if (!map.has(name)) {\r\n mergedFields.push(field);\r\n diff.push(chalk.green(`+ ${field}`));\r\n } else if (map.get(name) !== field) {\r\n const idx = mergedFields.findIndex(f => f.split(\" \")[0] === name);\r\n mergedFields[idx] = field;\r\n diff.push(chalk.yellow(`~ ${field}`));\r\n }\r\n }\r\n\r\n const mergedBlock = existing.replace(/\\{[\\s\\S]*\\}/, `{ \\n ${mergedFields.join(\"\\n \")} \\n}`);\r\n return { mergedBlock, diff };\r\n}\r\n\r\nfunction mergeEnumValues(existing: string, incoming: string) {\r\n const existingValues = parseBlockFields(existing);\r\n const incomingValues = parseBlockFields(incoming);\r\n\r\n const mergedValues = [...existingValues];\r\n const diff: string[] = [];\r\n\r\n for (const v of incomingValues) {\r\n if (!existingValues.includes(v)) {\r\n mergedValues.push(v);\r\n diff.push(chalk.green(`+ ${v}`));\r\n }\r\n }\r\n\r\n const mergedBlock = existing.replace(/\\{[\\s\\S]*\\}/, `{ \\n ${mergedValues.join(\"\\n \")} \\n}`);\r\n return { mergedBlock, diff };\r\n}\r\n\r\n// Replace blocks in schema (update existing, append only new)\r\nfunction applyMergedBlocks(original: string, blocks: string[], type: \"model\" | \"enum\") {\r\n let content = original;\r\n for (const block of blocks) {\r\n const match = block.match(new RegExp(`${type}\\\\s+(\\\\w+)\\\\s+{`));\r\n if (!match) continue;\r\n const name = match[1];\r\n\r\n const regex = new RegExp(`${type}\\\\s+${name}\\\\s+{[\\\\s\\\\S]*?}`, \"g\");\r\n if (regex.test(content)) {\r\n content = content.replace(regex, block); // update existing\r\n } else {\r\n content += `\\n\\n${block}`; // append new\r\n }\r\n }\r\n return content;\r\n}\r\n\r\n// -----------------------------\r\n// Main\r\n// -----------------------------\r\nasync function main() {\r\n console.log(chalk.blueBright.bold(\"\\n⚔ FlagsForge Prisma Schema Smart Merger\\n\"));\r\n\r\n if (isDryRun) console.log(chalk.cyan(\"šŸ” Dry run: preview all changes\\n\"));\r\n if (isForce) console.log(chalk.yellow(\"āš ļø Force mode enabled (skipping backup prompts)\\n\"));\r\n\r\n if (!fs.existsSync(appSchema)) {\r\n console.error(chalk.red(\"āŒ App schema not found at prisma/schema.prisma\"));\r\n process.exit(1);\r\n }\r\n if (!fs.existsSync(packageSchema)) {\r\n console.error(chalk.red(\"āŒ Package Prisma models not found\"));\r\n process.exit(1);\r\n }\r\n\r\n const appContent = fs.readFileSync(appSchema, \"utf-8\");\r\n const packageRaw = fs.readFileSync(packageSchema, \"utf-8\");\r\n const packageContent = packageRaw.replace(/generator\\s+\\w+\\s+{[\\s\\S]*?}\\n?/g, \"\").replace(/datasource\\s+\\w+\\s+{[\\s\\S]*?}\\n?/g, \"\");\r\n\r\n const appModels = extractBlocks(appContent, \"model\");\r\n const appEnums = extractBlocks(appContent, \"enum\");\r\n const packageModels = extractBlocks(packageContent, \"model\");\r\n const packageEnums = extractBlocks(packageContent, \"enum\");\r\n\r\n // Merge models\r\n const mergedModels: string[] = [];\r\n const modelDiffs: string[] = [];\r\n for (const pModel of packageModels) {\r\n const match = pModel.match(/model\\s+(\\w+)\\s+{/);\r\n if (!match) continue;\r\n const name = match[1];\r\n\r\n const existing = appModels.find(m => m.match(new RegExp(`model\\\\s+${name}\\\\s+{`)));\r\n if (existing) {\r\n const { mergedBlock, diff } = mergeModelFields(existing, pModel);\r\n mergedModels.push(mergedBlock);\r\n modelDiffs.push(...diff);\r\n } else {\r\n mergedModels.push(pModel);\r\n modelDiffs.push(chalk.green(`+ New model: ${name}`));\r\n }\r\n }\r\n\r\n // Merge enums\r\n const mergedEnums: string[] = [];\r\n const enumDiffs: string[] = [];\r\n for (const pEnum of packageEnums) {\r\n const match = pEnum.match(/enum\\s+(\\w+)\\s+{/);\r\n if (!match) continue;\r\n const name = match[1];\r\n\r\n const existing = appEnums.find(e => e.match(new RegExp(`enum\\\\s+${name}\\\\s+{`)));\r\n if (existing) {\r\n const { mergedBlock, diff } = mergeEnumValues(existing, pEnum);\r\n mergedEnums.push(mergedBlock);\r\n enumDiffs.push(...diff);\r\n } else {\r\n mergedEnums.push(pEnum);\r\n enumDiffs.push(chalk.green(`+ New enum: ${name}`));\r\n }\r\n }\r\n\r\n // ------------------------------\r\n // Prepend the FlagsForge Schema comment\r\n // ------------------------------\r\n const extensionComment = \"\\n\\n// ------------------------------\\n\" +\r\n \"// FlagsForge Schema Extensions\\n\" +\r\n \"// ------------------------------\\n\\n\";\r\n\r\n let mergedContent = appContent.trim() + extensionComment;\r\n mergedContent = applyMergedBlocks(mergedContent, mergedModels, \"model\");\r\n mergedContent = applyMergedBlocks(mergedContent, mergedEnums, \"enum\");\r\n\r\n // Dry-run preview\r\n if (isDryRun) {\r\n console.log(chalk.gray(\"----- FULL MERGE DIFF -----\\n\"));\r\n [...enumDiffs, ...modelDiffs].forEach(line => console.log(line));\r\n console.log(chalk.gray(\"\\n------------------------------\"));\r\n console.log(chalk.cyan(\"✨ Dry run complete. No changes were made.\\n\"));\r\n process.exit(0);\r\n }\r\n\r\n // Backup prompt\r\n let shouldBackup = true;\r\n if (!isForce) {\r\n const response = await prompts({\r\n type: \"confirm\",\r\n name: \"backup\",\r\n message: \"Create a backup of your current schema?\",\r\n initial: true,\r\n });\r\n shouldBackup = response.backup;\r\n }\r\n\r\n if (shouldBackup) {\r\n const timestamp = new Date().toISOString().replace(/[:.]/g, \"-\");\r\n const backupPath = `${appSchema}.bak-${timestamp}`;\r\n fs.copyFileSync(appSchema, backupPath);\r\n console.log(chalk.green(`āœ… Backup created: ${backupPath}\\n`));\r\n }\r\n\r\n fs.writeFileSync(appSchema, mergedContent);\r\n console.log(chalk.green.bold(\"āœ… Prisma schema merged successfully!\"));\r\n console.log(chalk.yellow(\"Run `npx prisma generate` to update your client.\\n\"));\r\n}\r\n\r\nmain().catch(err => {\r\n console.error(chalk.red(\"āŒ Error merging schemas:\"), err);\r\n process.exit(1);\r\n});\r\n"],"mappings":"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","names":["path","chalk","fs","prompts"]}
1
+ {"version":3,"sources":["../../bin/mergeSchema.ts"],"sourcesContent":["#!/usr/bin/env node\nimport fs from \"fs\";\nimport path from \"path\";\nimport chalk from \"chalk\";\nimport prompts from \"prompts\";\n\n// -----------------------------\n// CLI Flags\n// -----------------------------\nconst args = process.argv.slice(2);\nconst isDryRun = args.includes(\"--dry-run\");\nconst isForce = args.includes(\"--force\");\n\n// -----------------------------\n// Paths\n// -----------------------------\nconst appSchema = path.resolve(process.cwd(), \"prisma/schema.prisma\");\nconst packageSchema = path.resolve(__dirname, \"../../prisma/schema.prisma\");\n\n// -----------------------------\n// Helpers\n// -----------------------------\nfunction extractBlocks(schema: string, type: \"model\" | \"enum\"): string[] {\n const regex = new RegExp(`${type}\\\\s+\\\\w+\\\\s+{[\\\\s\\\\S]*?}\\\\n?`, \"g\");\n return schema.match(regex) || [];\n}\n\nfunction parseBlockFields(block: string) {\n return block\n .split(\"\\n\")\n .map(l => l.trim())\n .filter(\n l =>\n l &&\n !l.startsWith(\"//\") &&\n !l.startsWith(\"model\") &&\n !l.startsWith(\"enum\") &&\n !l.startsWith(\"{\") &&\n !l.startsWith(\"}\")\n );\n}\n\nfunction mergeModelFields(existing: string, incoming: string) {\n const existingFields = parseBlockFields(existing);\n const incomingFields = parseBlockFields(incoming);\n\n const mergedFields: string[] = [...existingFields];\n const diff: string[] = [];\n\n const map = new Map(existingFields.map(f => [f.split(\" \")[0], f]));\n\n for (const field of incomingFields) {\n const name = field.split(\" \")[0];\n if (!map.has(name)) {\n mergedFields.push(field);\n diff.push(chalk.green(`+ ${field}`));\n } else if (map.get(name) !== field) {\n const idx = mergedFields.findIndex(f => f.split(\" \")[0] === name);\n mergedFields[idx] = field;\n diff.push(chalk.yellow(`~ ${field}`));\n }\n }\n\n const mergedBlock = existing.replace(/\\{[\\s\\S]*\\}/, `{ \\n ${mergedFields.join(\"\\n \")} \\n}`);\n return { mergedBlock, diff };\n}\n\nfunction mergeEnumValues(existing: string, incoming: string) {\n const existingValues = parseBlockFields(existing);\n const incomingValues = parseBlockFields(incoming);\n\n const mergedValues = [...existingValues];\n const diff: string[] = [];\n\n for (const v of incomingValues) {\n if (!existingValues.includes(v)) {\n mergedValues.push(v);\n diff.push(chalk.green(`+ ${v}`));\n }\n }\n\n const mergedBlock = existing.replace(/\\{[\\s\\S]*\\}/, `{ \\n ${mergedValues.join(\"\\n \")} \\n}`);\n return { mergedBlock, diff };\n}\n\n// Replace blocks in schema (update existing, append only new)\nfunction applyMergedBlocks(original: string, blocks: string[], type: \"model\" | \"enum\") {\n let content = original;\n for (const block of blocks) {\n const match = block.match(new RegExp(`${type}\\\\s+(\\\\w+)\\\\s+{`));\n if (!match) continue;\n const name = match[1];\n\n const regex = new RegExp(`${type}\\\\s+${name}\\\\s+{[\\\\s\\\\S]*?}`, \"g\");\n if (regex.test(content)) {\n content = content.replace(regex, block); // update existing\n } else {\n content += `\\n\\n${block}`; // append new\n }\n }\n return content;\n}\n\n// -----------------------------\n// Main\n// -----------------------------\nasync function main() {\n console.log(chalk.blueBright.bold(\"\\n⚔ FlagsForge Prisma Schema Smart Merger\\n\"));\n\n if (isDryRun) console.log(chalk.cyan(\"šŸ” Dry run: preview all changes\\n\"));\n if (isForce) console.log(chalk.yellow(\"āš ļø Force mode enabled (skipping backup prompts)\\n\"));\n\n if (!fs.existsSync(appSchema)) {\n console.error(chalk.red(\"āŒ App schema not found at prisma/schema.prisma\"));\n process.exit(1);\n }\n if (!fs.existsSync(packageSchema)) {\n console.error(chalk.red(\"āŒ Package Prisma models not found\"));\n process.exit(1);\n }\n\n const appContent = fs.readFileSync(appSchema, \"utf-8\");\n const packageRaw = fs.readFileSync(packageSchema, \"utf-8\");\n const packageContent = packageRaw.replace(/generator\\s+\\w+\\s+{[\\s\\S]*?}\\n?/g, \"\").replace(/datasource\\s+\\w+\\s+{[\\s\\S]*?}\\n?/g, \"\");\n\n const appModels = extractBlocks(appContent, \"model\");\n const appEnums = extractBlocks(appContent, \"enum\");\n const packageModels = extractBlocks(packageContent, \"model\");\n const packageEnums = extractBlocks(packageContent, \"enum\");\n\n // Merge models\n const mergedModels: string[] = [];\n const modelDiffs: string[] = [];\n for (const pModel of packageModels) {\n const match = pModel.match(/model\\s+(\\w+)\\s+{/);\n if (!match) continue;\n const name = match[1];\n\n const existing = appModels.find(m => m.match(new RegExp(`model\\\\s+${name}\\\\s+{`)));\n if (existing) {\n const { mergedBlock, diff } = mergeModelFields(existing, pModel);\n mergedModels.push(mergedBlock);\n modelDiffs.push(...diff);\n } else {\n mergedModels.push(pModel);\n modelDiffs.push(chalk.green(`+ New model: ${name}`));\n }\n }\n\n // Merge enums\n const mergedEnums: string[] = [];\n const enumDiffs: string[] = [];\n for (const pEnum of packageEnums) {\n const match = pEnum.match(/enum\\s+(\\w+)\\s+{/);\n if (!match) continue;\n const name = match[1];\n\n const existing = appEnums.find(e => e.match(new RegExp(`enum\\\\s+${name}\\\\s+{`)));\n if (existing) {\n const { mergedBlock, diff } = mergeEnumValues(existing, pEnum);\n mergedEnums.push(mergedBlock);\n enumDiffs.push(...diff);\n } else {\n mergedEnums.push(pEnum);\n enumDiffs.push(chalk.green(`+ New enum: ${name}`));\n }\n }\n\n // ------------------------------\n // Prepend the FlagsForge Schema comment\n // ------------------------------\n const extensionComment = \"\\n\\n// ------------------------------\\n\" +\n \"// FlagsForge Schema Extensions\\n\" +\n \"// ------------------------------\\n\\n\";\n\n let mergedContent = appContent.trim() + extensionComment;\n mergedContent = applyMergedBlocks(mergedContent, mergedModels, \"model\");\n mergedContent = applyMergedBlocks(mergedContent, mergedEnums, \"enum\");\n\n // Dry-run preview\n if (isDryRun) {\n console.log(chalk.gray(\"----- FULL MERGE DIFF -----\\n\"));\n [...enumDiffs, ...modelDiffs].forEach(line => console.log(line));\n console.log(chalk.gray(\"\\n------------------------------\"));\n console.log(chalk.cyan(\"✨ Dry run complete. No changes were made.\\n\"));\n process.exit(0);\n }\n\n // Backup prompt\n let shouldBackup = true;\n if (!isForce) {\n const response = await prompts({\n type: \"confirm\",\n name: \"backup\",\n message: \"Create a backup of your current schema?\",\n initial: true,\n });\n shouldBackup = response.backup;\n }\n\n if (shouldBackup) {\n const timestamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n const backupPath = `${appSchema}.bak-${timestamp}`;\n fs.copyFileSync(appSchema, backupPath);\n console.log(chalk.green(`āœ… Backup created: ${backupPath}\\n`));\n }\n\n fs.writeFileSync(appSchema, mergedContent);\n console.log(chalk.green.bold(\"āœ… Prisma schema merged successfully!\"));\n console.log(chalk.yellow(\"Run `npx prisma generate` to update your client.\\n\"));\n}\n\nmain().catch(err => {\n console.error(chalk.red(\"āŒ Error merging schemas:\"), err);\n process.exit(1);\n});\n"],"mappings":"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","names":["path","chalk","fs","prompts"]}
package/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { PrismaClient } from '@custom-prisma/client';
2
- import * as react_jsx_runtime from 'react/jsx-runtime';
3
2
  import React, { ReactNode } from 'react';
3
+ import * as react_jsx_runtime from 'react/jsx-runtime';
4
4
 
5
5
  type Operator = "equals" | "not_equals" | "gt" | "lt" | "contains" | "in";
6
6
  interface Condition {
@@ -71,6 +71,17 @@ declare class MemoryFlagAdapter implements FlagAdapter {
71
71
  clear(): void;
72
72
  }
73
73
 
74
+ interface FeatureFlagManagerProps {
75
+ engine: FlagEngine;
76
+ isOpen: boolean;
77
+ onClose: () => void;
78
+ fetchFlags: () => Promise<FeatureFlag[]>;
79
+ fetchUsers?: () => Promise<any[]>;
80
+ fetchGroups?: () => Promise<any[]>;
81
+ updateFlag?: (flag: FeatureFlag) => Promise<void>;
82
+ }
83
+ declare const FeatureFlagManager: React.FC<FeatureFlagManagerProps>;
84
+
74
85
  interface FlagProviderValue {
75
86
  engine: FlagEngine;
76
87
  context: FlagContext;
@@ -103,4 +114,4 @@ declare function requireFlag({ key, engine }: RequireFlagOptions): (context: Fla
103
114
  user?: any;
104
115
  }, next: () => void | Promise<void>, handleDenied?: () => void) => Promise<void>;
105
116
 
106
- export { type Condition, type FeatureFlag, type FlagAdapter, type FlagContext, FlagContextReact, FlagEngine, FlagProvider, type FlagProviderValue, type FlagRule, type MemoryAdapterOptions, MemoryFlagAdapter, type Operator, PrismaFlagAdapter, type RequireFlagOptions, type RuleType, requireFlag, useFlag, withFlag };
117
+ export { type Condition, type FeatureFlag, FeatureFlagManager, type FlagAdapter, type FlagContext, FlagContextReact, FlagEngine, FlagProvider, type FlagProviderValue, type FlagRule, type MemoryAdapterOptions, MemoryFlagAdapter, type Operator, PrismaFlagAdapter, type RequireFlagOptions, type RuleType, requireFlag, useFlag, withFlag };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { PrismaClient } from '@custom-prisma/client';
2
- import * as react_jsx_runtime from 'react/jsx-runtime';
3
2
  import React, { ReactNode } from 'react';
3
+ import * as react_jsx_runtime from 'react/jsx-runtime';
4
4
 
5
5
  type Operator = "equals" | "not_equals" | "gt" | "lt" | "contains" | "in";
6
6
  interface Condition {
@@ -71,6 +71,17 @@ declare class MemoryFlagAdapter implements FlagAdapter {
71
71
  clear(): void;
72
72
  }
73
73
 
74
+ interface FeatureFlagManagerProps {
75
+ engine: FlagEngine;
76
+ isOpen: boolean;
77
+ onClose: () => void;
78
+ fetchFlags: () => Promise<FeatureFlag[]>;
79
+ fetchUsers?: () => Promise<any[]>;
80
+ fetchGroups?: () => Promise<any[]>;
81
+ updateFlag?: (flag: FeatureFlag) => Promise<void>;
82
+ }
83
+ declare const FeatureFlagManager: React.FC<FeatureFlagManagerProps>;
84
+
74
85
  interface FlagProviderValue {
75
86
  engine: FlagEngine;
76
87
  context: FlagContext;
@@ -103,4 +114,4 @@ declare function requireFlag({ key, engine }: RequireFlagOptions): (context: Fla
103
114
  user?: any;
104
115
  }, next: () => void | Promise<void>, handleDenied?: () => void) => Promise<void>;
105
116
 
106
- export { type Condition, type FeatureFlag, type FlagAdapter, type FlagContext, FlagContextReact, FlagEngine, FlagProvider, type FlagProviderValue, type FlagRule, type MemoryAdapterOptions, MemoryFlagAdapter, type Operator, PrismaFlagAdapter, type RequireFlagOptions, type RuleType, requireFlag, useFlag, withFlag };
117
+ export { type Condition, type FeatureFlag, FeatureFlagManager, type FlagAdapter, type FlagContext, FlagContextReact, FlagEngine, FlagProvider, type FlagProviderValue, type FlagRule, type MemoryAdapterOptions, MemoryFlagAdapter, type Operator, PrismaFlagAdapter, type RequireFlagOptions, type RuleType, requireFlag, useFlag, withFlag };
package/dist/index.js CHANGED
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,11 +17,20 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // src/index.ts
21
31
  var index_exports = {};
22
32
  __export(index_exports, {
33
+ FeatureFlagManager: () => FeatureFlagManager,
23
34
  FlagContextReact: () => FlagContextReact,
24
35
  FlagEngine: () => FlagEngine,
25
36
  FlagProvider: () => FlagProvider,
@@ -147,28 +158,292 @@ var MemoryFlagAdapter = class {
147
158
  }
148
159
  };
149
160
 
150
- // src/react/FlagProvider.tsx
161
+ // src/react/FeatureFlagsDialog.tsx
151
162
  var import_react = require("react");
163
+ var import_framer_motion2 = require("framer-motion");
164
+ var import_clsx2 = __toESM(require("clsx"));
165
+
166
+ // src/react/Tabs/FlagList.tsx
167
+ var import_framer_motion = require("framer-motion");
168
+ var import_clsx = __toESM(require("clsx"));
152
169
  var import_jsx_runtime = require("react/jsx-runtime");
153
- var FlagContextReact = (0, import_react.createContext)(null);
170
+ var FlagList = ({ flags, selectedFlag, setSelectedFlag, updateFlag }) => {
171
+ const toggleFlag = async (flag) => {
172
+ flag.enabled = !flag.enabled;
173
+ if (updateFlag) await updateFlag(flag);
174
+ };
175
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "space-y-4", children: flags.map((flag) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
176
+ import_framer_motion.motion.div,
177
+ {
178
+ className: "flex justify-between items-center border rounded p-2 hover:bg-gray-50 cursor-pointer",
179
+ onClick: () => setSelectedFlag(flag),
180
+ whileHover: { scale: 1.02 },
181
+ children: [
182
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
183
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "font-semibold", children: flag.key }),
184
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "text-gray-500 text-sm", children: [
185
+ "Enabled: ",
186
+ flag.enabled ? "Yes" : "No"
187
+ ] })
188
+ ] }),
189
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
190
+ "button",
191
+ {
192
+ onClick: (e) => {
193
+ e.stopPropagation();
194
+ toggleFlag(flag);
195
+ },
196
+ className: (0, import_clsx.default)(
197
+ "px-3 py-1 rounded text-white",
198
+ flag.enabled ? "bg-red-500 hover:bg-red-600" : "bg-green-500 hover:bg-green-600"
199
+ ),
200
+ children: flag.enabled ? "Disable" : "Enable"
201
+ }
202
+ )
203
+ ]
204
+ },
205
+ flag.key
206
+ )) });
207
+ };
208
+
209
+ // src/react/Tabs/RulesEditor.tsx
210
+ var import_uuid = require("uuid");
211
+ var import_jsx_runtime2 = require("react/jsx-runtime");
212
+ var ruleTypes = [
213
+ "USER_ID",
214
+ "USER_ATTRIBUTE",
215
+ "PERMISSION",
216
+ "GROUP",
217
+ "PERCENTAGE",
218
+ "CUSTOM"
219
+ ];
220
+ var RulesEditor = ({ selectedFlag, updateFlag }) => {
221
+ const addRule = () => {
222
+ selectedFlag.rules.push({ id: (0, import_uuid.v4)(), type: "USER_ID", priority: selectedFlag.rules.length, conditions: [] });
223
+ updateFlag?.(selectedFlag);
224
+ };
225
+ const addCondition = (rule) => {
226
+ const conditions = Array.isArray(rule.conditions) ? rule.conditions : [];
227
+ conditions.push({ field: "", operator: "equals", value: "" });
228
+ rule.conditions = conditions;
229
+ updateFlag?.(selectedFlag);
230
+ };
231
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mt-4 border-t pt-4 space-y-2", children: [
232
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("h3", { className: "font-bold mb-2", children: [
233
+ "Rules for ",
234
+ selectedFlag.key
235
+ ] }),
236
+ selectedFlag.rules.map((rule) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-col gap-2 border p-2 rounded", children: [
237
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex gap-2 items-center", children: [
238
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
239
+ "select",
240
+ {
241
+ value: rule.type,
242
+ onChange: (e) => {
243
+ rule.type = e.target.value;
244
+ updateFlag?.(selectedFlag);
245
+ },
246
+ className: "border p-1 rounded",
247
+ children: ruleTypes.map((rt) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: rt, children: rt }, rt))
248
+ }
249
+ ),
250
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
251
+ "input",
252
+ {
253
+ type: "number",
254
+ value: rule.priority,
255
+ onChange: (e) => {
256
+ rule.priority = Number(e.target.value);
257
+ updateFlag?.(selectedFlag);
258
+ },
259
+ className: "border p-1 rounded w-20"
260
+ }
261
+ ),
262
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { onClick: () => addCondition(rule), className: "bg-blue-500 text-white px-2 py-1 rounded hover:bg-blue-600", children: "+ Condition" })
263
+ ] }),
264
+ (Array.isArray(rule.conditions) ? rule.conditions : []).map((c, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex gap-2 items-center ml-4", children: [
265
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { type: "text", value: c.field, onChange: (e) => {
266
+ c.field = e.target.value;
267
+ updateFlag?.(selectedFlag);
268
+ }, className: "border p-1 rounded w-24" }),
269
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { type: "text", value: c.operator, onChange: (e) => {
270
+ c.operator = e.target.value;
271
+ updateFlag?.(selectedFlag);
272
+ }, className: "border p-1 rounded w-24" }),
273
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { type: "text", value: c.value, onChange: (e) => {
274
+ c.value = e.target.value;
275
+ updateFlag?.(selectedFlag);
276
+ }, className: "border p-1 rounded w-24" }),
277
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { onClick: () => {
278
+ rule.conditions.splice(i, 1);
279
+ updateFlag?.(selectedFlag);
280
+ }, className: "bg-red-500 text-white px-2 py-1 rounded hover:bg-red-600", children: "\u2715" })
281
+ ] }, i))
282
+ ] }, rule.id)),
283
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { onClick: addRule, className: "bg-blue-500 text-white px-3 py-1 rounded hover:bg-blue-600 mt-2", children: "Add Rule" })
284
+ ] });
285
+ };
286
+
287
+ // src/react/Tabs/GrantEditor.tsx
288
+ var import_uuid2 = require("uuid");
289
+ var import_jsx_runtime3 = require("react/jsx-runtime");
290
+ var GrantEditor = ({ selectedFlag, users, groups, updateFlag }) => {
291
+ const grantCondition = (rule, cond) => {
292
+ const conditions = Array.isArray(rule.conditions) ? rule.conditions : [];
293
+ conditions.push(cond);
294
+ rule.conditions = conditions;
295
+ selectedFlag.rules.push(rule);
296
+ updateFlag?.(selectedFlag);
297
+ };
298
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "space-y-4", children: [
299
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("h3", { className: "font-bold mb-2", children: [
300
+ "Grant Access for ",
301
+ selectedFlag.key
302
+ ] }),
303
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "space-y-2", children: [
304
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h4", { className: "font-semibold", children: "Users" }),
305
+ users.map((u) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex justify-between items-center border p-2 rounded hover:bg-gray-50", children: [
306
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { children: u.username || u.id }),
307
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { onClick: () => grantCondition({ id: (0, import_uuid2.v4)(), type: "USER_ID", priority: selectedFlag.rules.length, conditions: [] }, { field: "userId", operator: "equals", value: u.id }), className: "bg-green-500 text-white px-3 py-1 rounded hover:bg-green-600", children: "Grant" })
308
+ ] }, u.id))
309
+ ] }),
310
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "space-y-2", children: [
311
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h4", { className: "font-semibold", children: "Groups" }),
312
+ groups.map((g) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex justify-between items-center border p-2 rounded hover:bg-gray-50", children: [
313
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { children: g.name }),
314
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { onClick: () => grantCondition({ id: (0, import_uuid2.v4)(), type: "GROUP", priority: selectedFlag.rules.length, conditions: [] }, { field: "groupId", operator: "equals", value: g.id }), className: "bg-purple-500 text-white px-3 py-1 rounded hover:bg-purple-600", children: "Grant" })
315
+ ] }, g.id))
316
+ ] })
317
+ ] });
318
+ };
319
+
320
+ // src/react/Tabs/AuditView.tsx
321
+ var import_jsx_runtime4 = require("react/jsx-runtime");
322
+ var AuditView = ({ flags }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
323
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h3", { className: "font-bold mb-2", children: "Audit / Access Management" }),
324
+ flags.map((flag) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "border p-2 rounded mb-2", children: [
325
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "font-semibold", children: flag.key }),
326
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ul", { className: "ml-4 text-sm", children: flag.rules.map((r) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("li", { children: [
327
+ r.type,
328
+ " - ",
329
+ JSON.stringify(r.conditions)
330
+ ] }, r.id)) })
331
+ ] }, flag.key))
332
+ ] });
333
+
334
+ // src/react/FeatureFlagsDialog.tsx
335
+ var import_jsx_runtime5 = require("react/jsx-runtime");
336
+ var FeatureFlagManager = ({
337
+ engine,
338
+ isOpen,
339
+ onClose,
340
+ fetchFlags,
341
+ fetchUsers,
342
+ fetchGroups,
343
+ updateFlag
344
+ }) => {
345
+ const [flags, setFlags] = (0, import_react.useState)([]);
346
+ const [selectedFlag, setSelectedFlag] = (0, import_react.useState)(null);
347
+ const [tab, setTab] = (0, import_react.useState)("flags");
348
+ const [users, setUsers] = (0, import_react.useState)([]);
349
+ const [groups, setGroups] = (0, import_react.useState)([]);
350
+ (0, import_react.useEffect)(() => {
351
+ if (!isOpen) return;
352
+ const loadFlags = async () => setFlags(await fetchFlags());
353
+ loadFlags();
354
+ }, [isOpen]);
355
+ (0, import_react.useEffect)(() => {
356
+ if (!isOpen || tab !== "grant") return;
357
+ const loadData = async () => {
358
+ if (fetchUsers) setUsers(await fetchUsers());
359
+ if (fetchGroups) setGroups(await fetchGroups());
360
+ };
361
+ loadData();
362
+ }, [isOpen, tab, fetchUsers, fetchGroups]);
363
+ if (!isOpen) return null;
364
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_framer_motion2.AnimatePresence, { children: isOpen && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
365
+ import_framer_motion2.motion.div,
366
+ {
367
+ className: "fixed inset-0 flex items-center justify-center z-50 bg-black/50",
368
+ initial: { opacity: 0 },
369
+ animate: { opacity: 1 },
370
+ exit: { opacity: 0 },
371
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
372
+ import_framer_motion2.motion.div,
373
+ {
374
+ className: "bg-white rounded-lg shadow-xl w-11/12 max-w-6xl p-6",
375
+ initial: { scale: 0.8, opacity: 0 },
376
+ animate: { scale: 1, opacity: 1 },
377
+ exit: { scale: 0.8, opacity: 0 },
378
+ transition: { type: "spring", stiffness: 300, damping: 25 },
379
+ children: [
380
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex justify-between items-center mb-4", children: [
381
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h2", { className: "text-xl font-bold", children: "Feature Flags Manager" }),
382
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { onClick: onClose, className: "text-gray-500 hover:text-gray-800", children: "\u2715" })
383
+ ] }),
384
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "flex gap-2 mb-4 border-b", children: ["flags", "grant", "audit"].map((t) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
385
+ "button",
386
+ {
387
+ className: (0, import_clsx2.default)(
388
+ "px-4 py-2 rounded-t font-semibold",
389
+ tab === t ? "bg-white border-t border-x border-gray-300" : "bg-gray-200 hover:bg-gray-300"
390
+ ),
391
+ onClick: () => setTab(t),
392
+ children: t === "flags" ? "Feature Flags" : t === "grant" ? "Grant Access" : "Audit"
393
+ },
394
+ t
395
+ )) }),
396
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "max-h-[60vh] overflow-y-auto", children: [
397
+ tab === "flags" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
398
+ FlagList,
399
+ {
400
+ flags,
401
+ selectedFlag,
402
+ setSelectedFlag,
403
+ updateFlag
404
+ }
405
+ ),
406
+ tab === "grant" && selectedFlag && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
407
+ GrantEditor,
408
+ {
409
+ selectedFlag,
410
+ users,
411
+ groups,
412
+ updateFlag
413
+ }
414
+ ),
415
+ tab === "audit" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(AuditView, { flags }),
416
+ selectedFlag && tab === "flags" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(RulesEditor, { selectedFlag, updateFlag })
417
+ ] })
418
+ ]
419
+ }
420
+ )
421
+ }
422
+ ) });
423
+ };
424
+
425
+ // src/react/FlagProvider.tsx
426
+ var import_react2 = require("react");
427
+ var import_jsx_runtime6 = require("react/jsx-runtime");
428
+ var FlagContextReact = (0, import_react2.createContext)(null);
154
429
  function FlagProvider({
155
430
  engine,
156
431
  context,
157
432
  children
158
433
  }) {
159
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FlagContextReact.Provider, { value: { engine, context }, children });
434
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(FlagContextReact.Provider, { value: { engine, context }, children });
160
435
  }
161
436
 
162
437
  // src/react/useFlag.ts
163
- var import_react2 = require("react");
438
+ var import_react3 = require("react");
164
439
  function useFlag(key) {
165
- const ctx = (0, import_react2.useContext)(FlagContextReact);
440
+ const ctx = (0, import_react3.useContext)(FlagContextReact);
166
441
  if (!ctx) {
167
442
  throw new Error("useFlag must be used inside FlagProvider");
168
443
  }
169
444
  const { engine, context } = ctx;
170
- const [enabled, setEnabled] = (0, import_react2.useState)(false);
171
- (0, import_react2.useEffect)(() => {
445
+ const [enabled, setEnabled] = (0, import_react3.useState)(false);
446
+ (0, import_react3.useEffect)(() => {
172
447
  let cancelled = false;
173
448
  engine.isEnabled(key, context).then((result) => {
174
449
  if (!cancelled) {
@@ -183,15 +458,15 @@ function useFlag(key) {
183
458
  }
184
459
 
185
460
  // src/react/withFlag.tsx
186
- var import_jsx_runtime2 = require("react/jsx-runtime");
461
+ var import_jsx_runtime7 = require("react/jsx-runtime");
187
462
  function withFlag(flagKey, options) {
188
463
  return function(WrappedComponent) {
189
464
  const ComponentWithFlag = (props) => {
190
465
  const enabled = useFlag(flagKey);
191
466
  if (!enabled) {
192
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: options?.fallback ?? null });
467
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_jsx_runtime7.Fragment, { children: options?.fallback ?? null });
193
468
  }
194
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(WrappedComponent, { ...props });
469
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(WrappedComponent, { ...props });
195
470
  };
196
471
  ComponentWithFlag.displayName = `withFlag(${WrappedComponent.displayName || WrappedComponent.name || "Component"})`;
197
472
  return ComponentWithFlag;
@@ -216,6 +491,7 @@ function requireFlag({ key, engine }) {
216
491
  }
217
492
  // Annotate the CommonJS export names for ESM import in node:
218
493
  0 && (module.exports = {
494
+ FeatureFlagManager,
219
495
  FlagContextReact,
220
496
  FlagEngine,
221
497
  FlagProvider,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/core/engine.ts","../src/adapters/prisma.ts","../src/adapters/memory.ts","../src/react/FlagProvider.tsx","../src/react/useFlag.ts","../src/react/withFlag.tsx","../src/node/middleware.ts"],"sourcesContent":["// Types\r\nexport * from \"./core/types\" // exports both types & values (if any)\r\nexport type { FlagAdapter, FeatureFlag, FlagContext, FlagRule, RuleType, Condition, Operator } from \"./core/types\"\r\n\r\n// Engine\r\nexport { FlagEngine } from \"./core/engine\"\r\n\r\n// Adapters\r\nexport { PrismaFlagAdapter } from \"./adapters/prisma\"\r\nexport { MemoryFlagAdapter, MemoryAdapterOptions } from \"./adapters/memory\"\r\n\r\n// React\r\nexport { FlagProvider, FlagProviderValue, FlagContextReact } from \"./react/FlagProvider\"\r\nexport { useFlag } from \"./react/useFlag\"\r\nexport { withFlag } from \"./react/withFlag\"\r\n\r\n// Node\r\nexport { requireFlag, RequireFlagOptions } from \"./node/middleware\"\r\n","import { FlagAdapter, FlagContext } from \"./types\"\r\n\r\nexport class FlagEngine {\r\n constructor(private adapter: FlagAdapter) {}\r\n\r\n async isEnabled(key: string, context: FlagContext): Promise<boolean> {\r\n const flag = await this.adapter.getFlag(key)\r\n if (!flag || !flag.enabled) return false\r\n\r\n const sortedRules = flag.rules.sort((a: any, b: any) => b.priority - a.priority)\r\n\r\n for (const rule of sortedRules) {\r\n const result = await this.evaluateRule(rule, context)\r\n if (result !== null) return result\r\n }\r\n\r\n return false\r\n }\r\n\r\n private async evaluateRule(rule: any, context: FlagContext): Promise<boolean | null> {\r\n switch (rule.type) {\r\n case \"USER_ID\":\r\n return rule.conditions.includes(context.userId)\r\n\r\n case \"PERMISSION\":\r\n return context.permissions?.includes(rule.conditions.permission) ?? false\r\n\r\n case \"USER_ATTRIBUTE\":\r\n const { field, operator, value } = rule.conditions\r\n const userValue = context.attributes?.[field]\r\n\r\n switch (operator) {\r\n case \"equals\":\r\n return userValue === value\r\n case \"gt\":\r\n return userValue > value\r\n case \"lt\":\r\n return userValue < value\r\n case \"contains\":\r\n return userValue?.includes(value)\r\n }\r\n return false\r\n\r\n case \"GROUP_MEMBERSHIP\":\r\n return context.groups?.includes(rule.conditions.groupId) ?? false\r\n\r\n case \"PERCENTAGE\":\r\n return this.percentageRollout(context.userId!, rule.percentage)\r\n\r\n default:\r\n return null\r\n }\r\n }\r\n\r\n private percentageRollout(userId: string, percentage: number) {\r\n const hash = this.hash(userId)\r\n return (hash % 100) < percentage\r\n }\r\n\r\n private hash(str: string) {\r\n let hash = 0\r\n for (let i = 0; i < str.length; i++) {\r\n hash = (hash << 5) - hash + str.charCodeAt(i)\r\n hash |= 0\r\n }\r\n return Math.abs(hash)\r\n }\r\n}\r\n","import { PrismaClient } from \"@custom-prisma/client\" // ← match schema generator\r\nimport { FlagAdapter, FeatureFlag } from \"../core/types\"\r\n\r\nexport class PrismaFlagAdapter implements FlagAdapter {\r\n constructor(private prisma: PrismaClient) {}\r\n\r\n async getFlag(key: string): Promise<FeatureFlag | null> {\r\n const flag = await this.prisma.featureFlag.findUnique({\r\n where: { key },\r\n include: { rules: true } // make sure 'rules' relation exists\r\n })\r\n\r\n if (!flag) return null\r\n\r\n return {\r\n key: flag.key,\r\n enabled: flag.enabled,\r\n rules: flag.rules as any\r\n }\r\n }\r\n}\r\n","import type {\r\n FlagAdapter,\r\n FeatureFlag\r\n} from \"../core/types\"\r\n\r\nexport interface MemoryAdapterOptions {\r\n initialFlags?: FeatureFlag[]\r\n}\r\n\r\nexport class MemoryFlagAdapter implements FlagAdapter {\r\n private flags = new Map<string, FeatureFlag>()\r\n\r\n constructor(options?: MemoryAdapterOptions) {\r\n if (options?.initialFlags) {\r\n for (const flag of options.initialFlags) {\r\n this.flags.set(flag.key, flag)\r\n }\r\n }\r\n }\r\n\r\n async getFlag(key: string): Promise<FeatureFlag | null> {\r\n return this.flags.get(key) ?? null\r\n }\r\n\r\n /**\r\n * Add or update a flag\r\n */\r\n setFlag(flag: FeatureFlag): void {\r\n this.flags.set(flag.key, flag)\r\n }\r\n\r\n /**\r\n * Remove a flag\r\n */\r\n removeFlag(key: string): void {\r\n this.flags.delete(key)\r\n }\r\n\r\n /**\r\n * Get all flags (useful for debugging)\r\n */\r\n getAllFlags(): FeatureFlag[] {\r\n return Array.from(this.flags.values())\r\n }\r\n\r\n /**\r\n * Clear all flags\r\n */\r\n clear(): void {\r\n this.flags.clear()\r\n }\r\n}\r\n","import React, { createContext, ReactNode } from \"react\"\r\nimport type { FlagEngine } from \"../core/engine\"\r\nimport type { FlagContext } from \"../core/types\"\r\n\r\nexport interface FlagProviderValue {\r\n engine: FlagEngine\r\n context: FlagContext\r\n}\r\n\r\nexport const FlagContextReact =\r\n createContext<FlagProviderValue | null>(null)\r\n\r\ninterface FlagProviderProps {\r\n engine: FlagEngine\r\n context: FlagContext\r\n children: ReactNode\r\n}\r\n\r\nexport function FlagProvider({\r\n engine,\r\n context,\r\n children\r\n}: FlagProviderProps) {\r\n return (\r\n <FlagContextReact.Provider value={{ engine, context }}>\r\n {children}\r\n </FlagContextReact.Provider>\r\n )\r\n}\r\n","import { useContext, useEffect, useState } from \"react\"\r\nimport { FlagContextReact } from \"./FlagProvider\"\r\n\r\nexport function useFlag(key: string): boolean {\r\n const ctx = useContext(FlagContextReact)\r\n\r\n if (!ctx) {\r\n throw new Error(\"useFlag must be used inside FlagProvider\")\r\n }\r\n\r\n const { engine, context } = ctx\r\n const [enabled, setEnabled] = useState(false)\r\n\r\n useEffect(() => {\r\n let cancelled = false\r\n\r\n engine.isEnabled(key, context).then(result => {\r\n if (!cancelled) {\r\n setEnabled(result)\r\n }\r\n })\r\n\r\n return () => {\r\n cancelled = true\r\n }\r\n }, [engine, key, context])\r\n\r\n return enabled\r\n}\r\n","import React from \"react\"\r\nimport { useFlag } from \"./useFlag\"\r\n\r\ninterface WithFlagOptions {\r\n fallback?: React.ReactNode\r\n}\r\n\r\nexport function withFlag(\r\n flagKey: string,\r\n options?: WithFlagOptions\r\n) {\r\n return function <P extends object>(\r\n WrappedComponent: React.ComponentType<P>\r\n ) {\r\n const ComponentWithFlag: React.FC<P> = (props) => {\r\n const enabled = useFlag(flagKey)\r\n\r\n if (!enabled) {\r\n return <>{options?.fallback ?? null}</>\r\n }\r\n\r\n return <WrappedComponent {...props} />\r\n }\r\n\r\n ComponentWithFlag.displayName = `withFlag(${\r\n WrappedComponent.displayName ||\r\n WrappedComponent.name ||\r\n \"Component\"\r\n })`\r\n\r\n return ComponentWithFlag\r\n }\r\n}\r\n","import { FlagEngine } from \"../core/engine\"\r\nimport type { FlagContext } from \"../core/types\"\r\n\r\nexport interface RequireFlagOptions {\r\n key: string\r\n engine: FlagEngine\r\n}\r\n\r\n/**\r\n * Universal middleware/check function for any Node.js system\r\n * @param options.key - Feature flag key\r\n * @param options.engine - FlagEngine instance\r\n */\r\nexport function requireFlag({ key, engine }: RequireFlagOptions) {\r\n /**\r\n * context: object containing user info or any relevant attributes\r\n * next: function to call if feature is enabled\r\n * handleDenied: optional function to handle denial (default throws)\r\n */\r\n return async (\r\n context: FlagContext & { user?: any },\r\n next: () => void | Promise<void>,\r\n handleDenied?: () => void\r\n ) => {\r\n const allowed = await engine.isEnabled(key, {\r\n userId: context.user?.id,\r\n attributes: context.user,\r\n permissions: context.user?.permissions,\r\n groups: context.user?.groups ?? context.groups\r\n })\r\n\r\n if (!allowed) {\r\n if (handleDenied) return handleDenied()\r\n throw new Error(`Feature \"${key}\" is disabled for this user`)\r\n }\r\n\r\n return next()\r\n }\r\n}\r\n"],"mappings":"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","names":["import_react","import_jsx_runtime"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/core/engine.ts","../src/adapters/prisma.ts","../src/adapters/memory.ts","../src/react/FeatureFlagsDialog.tsx","../src/react/Tabs/FlagList.tsx","../src/react/Tabs/RulesEditor.tsx","../src/react/Tabs/GrantEditor.tsx","../src/react/Tabs/AuditView.tsx","../src/react/FlagProvider.tsx","../src/react/useFlag.ts","../src/react/withFlag.tsx","../src/node/middleware.ts"],"sourcesContent":["// Types\nexport * from \"./core/types\" // exports both types & values (if any)\nexport type { FlagAdapter, FeatureFlag, FlagContext, FlagRule, RuleType, Condition, Operator } from \"./core/types\"\n\n// Engine\nexport { FlagEngine } from \"./core/engine\"\n\n// Adapters\nexport { PrismaFlagAdapter } from \"./adapters/prisma\"\nexport { MemoryFlagAdapter, MemoryAdapterOptions } from \"./adapters/memory\"\n\n// React\nexport { FeatureFlagManager } from \"./react/FeatureFlagsDialog\"\nexport { FlagProvider, FlagProviderValue, FlagContextReact } from \"./react/FlagProvider\"\nexport { useFlag } from \"./react/useFlag\"\nexport { withFlag } from \"./react/withFlag\"\n\n// Node\nexport { requireFlag, RequireFlagOptions } from \"./node/middleware\"\n","import { FlagAdapter, FlagContext } from \"./types\"\n\nexport class FlagEngine {\n constructor(private adapter: FlagAdapter) {}\n\n async isEnabled(key: string, context: FlagContext): Promise<boolean> {\n const flag = await this.adapter.getFlag(key)\n if (!flag || !flag.enabled) return false\n\n const sortedRules = flag.rules.sort((a: any, b: any) => b.priority - a.priority)\n\n for (const rule of sortedRules) {\n const result = await this.evaluateRule(rule, context)\n if (result !== null) return result\n }\n\n return false\n }\n\n private async evaluateRule(rule: any, context: FlagContext): Promise<boolean | null> {\n switch (rule.type) {\n case \"USER_ID\":\n return rule.conditions.includes(context.userId)\n\n case \"PERMISSION\":\n return context.permissions?.includes(rule.conditions.permission) ?? false\n\n case \"USER_ATTRIBUTE\":\n const { field, operator, value } = rule.conditions\n const userValue = context.attributes?.[field]\n\n switch (operator) {\n case \"equals\":\n return userValue === value\n case \"gt\":\n return userValue > value\n case \"lt\":\n return userValue < value\n case \"contains\":\n return userValue?.includes(value)\n }\n return false\n\n case \"GROUP_MEMBERSHIP\":\n return context.groups?.includes(rule.conditions.groupId) ?? false\n\n case \"PERCENTAGE\":\n return this.percentageRollout(context.userId!, rule.percentage)\n\n default:\n return null\n }\n }\n\n private percentageRollout(userId: string, percentage: number) {\n const hash = this.hash(userId)\n return (hash % 100) < percentage\n }\n\n private hash(str: string) {\n let hash = 0\n for (let i = 0; i < str.length; i++) {\n hash = (hash << 5) - hash + str.charCodeAt(i)\n hash |= 0\n }\n return Math.abs(hash)\n }\n}\n","import { PrismaClient } from \"@custom-prisma/client\" // ← match schema generator\nimport { FlagAdapter, FeatureFlag } from \"../core/types\"\n\nexport class PrismaFlagAdapter implements FlagAdapter {\n constructor(private prisma: PrismaClient) {}\n\n async getFlag(key: string): Promise<FeatureFlag | null> {\n const flag = await this.prisma.featureFlag.findUnique({\n where: { key },\n include: { rules: true } // make sure 'rules' relation exists\n })\n\n if (!flag) return null\n\n return {\n key: flag.key,\n enabled: flag.enabled,\n rules: flag.rules as any\n }\n }\n}\n","import type {\n FlagAdapter,\n FeatureFlag\n} from \"../core/types\"\n\nexport interface MemoryAdapterOptions {\n initialFlags?: FeatureFlag[]\n}\n\nexport class MemoryFlagAdapter implements FlagAdapter {\n private flags = new Map<string, FeatureFlag>()\n\n constructor(options?: MemoryAdapterOptions) {\n if (options?.initialFlags) {\n for (const flag of options.initialFlags) {\n this.flags.set(flag.key, flag)\n }\n }\n }\n\n async getFlag(key: string): Promise<FeatureFlag | null> {\n return this.flags.get(key) ?? null\n }\n\n /**\n * Add or update a flag\n */\n setFlag(flag: FeatureFlag): void {\n this.flags.set(flag.key, flag)\n }\n\n /**\n * Remove a flag\n */\n removeFlag(key: string): void {\n this.flags.delete(key)\n }\n\n /**\n * Get all flags (useful for debugging)\n */\n getAllFlags(): FeatureFlag[] {\n return Array.from(this.flags.values())\n }\n\n /**\n * Clear all flags\n */\n clear(): void {\n this.flags.clear()\n }\n}\n","import React, { useState, useEffect } from \"react\"\nimport { motion, AnimatePresence } from \"framer-motion\"\nimport clsx from \"clsx\"\n\nimport type { FeatureFlag, FlagRule, RuleType } from \"../core/types\"\nimport { FlagEngine } from \"../core/engine\"\n\nimport { FlagList } from \"./Tabs/FlagList\";\nimport { RulesEditor } from \"./Tabs/RulesEditor\";\nimport { GrantEditor } from \"./Tabs/GrantEditor\";\nimport { AuditView } from \"./Tabs/AuditView\";\n\ntype Tab = \"flags\" | \"grant\" | \"audit\"\n\ninterface FeatureFlagManagerProps {\n engine: FlagEngine\n isOpen: boolean\n onClose: () => void\n fetchFlags: () => Promise<FeatureFlag[]>\n fetchUsers?: () => Promise<any[]>\n fetchGroups?: () => Promise<any[]>\n updateFlag?: (flag: FeatureFlag) => Promise<void>\n}\n\nexport const FeatureFlagManager: React.FC<FeatureFlagManagerProps> = ({\n engine,\n isOpen,\n onClose,\n fetchFlags,\n fetchUsers,\n fetchGroups,\n updateFlag\n}) => {\n const [flags, setFlags] = useState<FeatureFlag[]>([])\n const [selectedFlag, setSelectedFlag] = useState<FeatureFlag | null>(null)\n const [tab, setTab] = useState<Tab>(\"flags\")\n const [users, setUsers] = useState<any[]>([])\n const [groups, setGroups] = useState<any[]>([])\n\n useEffect(() => {\n if (!isOpen) return\n const loadFlags = async () => setFlags(await fetchFlags())\n loadFlags()\n }, [isOpen])\n\n useEffect(() => {\n if (!isOpen || tab !== \"grant\") return\n const loadData = async () => {\n if (fetchUsers) setUsers(await fetchUsers())\n if (fetchGroups) setGroups(await fetchGroups())\n }\n loadData()\n }, [isOpen, tab, fetchUsers, fetchGroups])\n\n if (!isOpen) return null\n\n return (\n <AnimatePresence>\n {isOpen && (\n <motion.div\n className=\"fixed inset-0 flex items-center justify-center z-50 bg-black/50\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n >\n <motion.div\n className=\"bg-white rounded-lg shadow-xl w-11/12 max-w-6xl p-6\"\n initial={{ scale: 0.8, opacity: 0 }}\n animate={{ scale: 1, opacity: 1 }}\n exit={{ scale: 0.8, opacity: 0 }}\n transition={{ type: \"spring\", stiffness: 300, damping: 25 }}\n >\n {/* Header */}\n <div className=\"flex justify-between items-center mb-4\">\n <h2 className=\"text-xl font-bold\">Feature Flags Manager</h2>\n <button onClick={onClose} className=\"text-gray-500 hover:text-gray-800\">āœ•</button>\n </div>\n\n {/* Tabs */}\n <div className=\"flex gap-2 mb-4 border-b\">\n {([\"flags\", \"grant\", \"audit\"] as Tab[]).map(t => (\n <button\n key={t}\n className={clsx(\n \"px-4 py-2 rounded-t font-semibold\",\n tab === t ? \"bg-white border-t border-x border-gray-300\" : \"bg-gray-200 hover:bg-gray-300\"\n )}\n onClick={() => setTab(t)}\n >\n {t === \"flags\" ? \"Feature Flags\" : t === \"grant\" ? \"Grant Access\" : \"Audit\"}\n </button>\n ))}\n </div>\n\n {/* Tab content */}\n <div className=\"max-h-[60vh] overflow-y-auto\">\n {tab === \"flags\" && (\n <FlagList\n flags={flags}\n selectedFlag={selectedFlag}\n setSelectedFlag={setSelectedFlag}\n updateFlag={updateFlag}\n />\n )}\n {tab === \"grant\" && selectedFlag && (\n <GrantEditor\n selectedFlag={selectedFlag}\n users={users}\n groups={groups}\n updateFlag={updateFlag}\n />\n )}\n {tab === \"audit\" && <AuditView flags={flags} />}\n {selectedFlag && tab === \"flags\" && (\n <RulesEditor selectedFlag={selectedFlag} updateFlag={updateFlag} />\n )}\n </div>\n </motion.div>\n </motion.div>\n )}\n </AnimatePresence>\n )\n}\n","import React from \"react\"\nimport { motion } from \"framer-motion\"\nimport clsx from \"clsx\"\nimport type { FeatureFlag } from \"../../core/types\"\n\ninterface FlagListProps {\n flags: FeatureFlag[]\n selectedFlag: FeatureFlag | null\n setSelectedFlag: (flag: FeatureFlag) => void\n updateFlag?: (flag: FeatureFlag) => Promise<void>\n}\n\nexport const FlagList: React.FC<FlagListProps> = ({ flags, selectedFlag, setSelectedFlag, updateFlag }) => {\n const toggleFlag = async (flag: FeatureFlag) => {\n flag.enabled = !flag.enabled\n if (updateFlag) await updateFlag(flag)\n }\n\n return (\n <div className=\"space-y-4\">\n {flags.map(flag => (\n <motion.div\n key={flag.key}\n className=\"flex justify-between items-center border rounded p-2 hover:bg-gray-50 cursor-pointer\"\n onClick={() => setSelectedFlag(flag)}\n whileHover={{ scale: 1.02 }}\n >\n <div>\n <p className=\"font-semibold\">{flag.key}</p>\n <p className=\"text-gray-500 text-sm\">\n Enabled: {flag.enabled ? \"Yes\" : \"No\"}\n </p>\n </div>\n <button\n onClick={e => {\n e.stopPropagation()\n toggleFlag(flag)\n }}\n className={clsx(\n \"px-3 py-1 rounded text-white\",\n flag.enabled ? \"bg-red-500 hover:bg-red-600\" : \"bg-green-500 hover:bg-green-600\"\n )}\n >\n {flag.enabled ? \"Disable\" : \"Enable\"}\n </button>\n </motion.div>\n ))}\n </div>\n )\n}\n","import React from \"react\"\nimport { v4 as uuidv4 } from \"uuid\"\nimport type { FeatureFlag, FlagRule, RuleType, Condition } from \"../../core/types\"\n\ninterface RulesEditorProps {\n selectedFlag: FeatureFlag\n updateFlag?: (flag: FeatureFlag) => Promise<void>\n}\n\nconst ruleTypes: RuleType[] = [\n \"USER_ID\",\n \"USER_ATTRIBUTE\",\n \"PERMISSION\",\n \"GROUP\",\n \"PERCENTAGE\",\n \"CUSTOM\"\n]\n\nexport const RulesEditor: React.FC<RulesEditorProps> = ({ selectedFlag, updateFlag }) => {\n\n const addRule = () => {\n selectedFlag.rules.push({ id: uuidv4(), type: \"USER_ID\", priority: selectedFlag.rules.length, conditions: [] })\n updateFlag?.(selectedFlag)\n }\n\n const addCondition = (rule: FlagRule) => {\n const conditions = Array.isArray(rule.conditions) ? rule.conditions : []\n conditions.push({ field: \"\", operator: \"equals\", value: \"\" })\n rule.conditions = conditions\n updateFlag?.(selectedFlag)\n }\n\n return (\n <div className=\"mt-4 border-t pt-4 space-y-2\">\n <h3 className=\"font-bold mb-2\">Rules for {selectedFlag.key}</h3>\n {selectedFlag.rules.map(rule => (\n <div key={rule.id} className=\"flex flex-col gap-2 border p-2 rounded\">\n <div className=\"flex gap-2 items-center\">\n <select\n value={rule.type}\n onChange={e => {\n rule.type = e.target.value as RuleType\n updateFlag?.(selectedFlag)\n }}\n className=\"border p-1 rounded\"\n >\n {ruleTypes.map(rt => (\n <option key={rt} value={rt}>{rt}</option>\n ))}\n </select>\n <input\n type=\"number\"\n value={rule.priority}\n onChange={e => {\n rule.priority = Number(e.target.value)\n updateFlag?.(selectedFlag)\n }}\n className=\"border p-1 rounded w-20\"\n />\n <button onClick={() => addCondition(rule)} className=\"bg-blue-500 text-white px-2 py-1 rounded hover:bg-blue-600\">\n + Condition\n </button>\n </div>\n {/* Render conditions */}\n {(Array.isArray(rule.conditions) ? rule.conditions : []).map((c, i) => (\n <div key={i} className=\"flex gap-2 items-center ml-4\">\n <input type=\"text\" value={c.field} onChange={e => { c.field = e.target.value; updateFlag?.(selectedFlag) }} className=\"border p-1 rounded w-24\" />\n <input type=\"text\" value={c.operator} onChange={e => { c.operator = e.target.value as any; updateFlag?.(selectedFlag) }} className=\"border p-1 rounded w-24\" />\n <input type=\"text\" value={c.value} onChange={e => { c.value = e.target.value; updateFlag?.(selectedFlag) }} className=\"border p-1 rounded w-24\" />\n <button onClick={() => { (rule.conditions as Condition[]).splice(i, 1); updateFlag?.(selectedFlag) }} className=\"bg-red-500 text-white px-2 py-1 rounded hover:bg-red-600\">āœ•</button>\n </div>\n ))}\n </div>\n ))}\n <button onClick={addRule} className=\"bg-blue-500 text-white px-3 py-1 rounded hover:bg-blue-600 mt-2\">Add Rule</button>\n </div>\n )\n}\n","import React from \"react\"\nimport { v4 as uuidv4 } from \"uuid\"\nimport type { FeatureFlag, FlagRule, RuleType, Condition } from \"../../core/types\"\n\ninterface GrantEditorProps {\n selectedFlag: FeatureFlag\n users: any[]\n groups: any[]\n updateFlag?: (flag: FeatureFlag) => Promise<void>\n}\n\nexport const GrantEditor: React.FC<GrantEditorProps> = ({ selectedFlag, users, groups, updateFlag }) => {\n\n const grantCondition = (rule: FlagRule, cond: Condition) => {\n const conditions = Array.isArray(rule.conditions) ? rule.conditions : []\n conditions.push(cond)\n rule.conditions = conditions\n selectedFlag.rules.push(rule)\n updateFlag?.(selectedFlag)\n }\n\n return (\n <div className=\"space-y-4\">\n <h3 className=\"font-bold mb-2\">Grant Access for {selectedFlag.key}</h3>\n\n {/* Users */}\n <div className=\"space-y-2\">\n <h4 className=\"font-semibold\">Users</h4>\n {users.map(u => (\n <div key={u.id} className=\"flex justify-between items-center border p-2 rounded hover:bg-gray-50\">\n <p>{u.username || u.id}</p>\n <button onClick={() => grantCondition({ id: uuidv4(), type: \"USER_ID\", priority: selectedFlag.rules.length, conditions: [] }, { field: \"userId\", operator: \"equals\", value: u.id })} className=\"bg-green-500 text-white px-3 py-1 rounded hover:bg-green-600\">Grant</button>\n </div>\n ))}\n </div>\n\n {/* Groups */}\n <div className=\"space-y-2\">\n <h4 className=\"font-semibold\">Groups</h4>\n {groups.map(g => (\n <div key={g.id} className=\"flex justify-between items-center border p-2 rounded hover:bg-gray-50\">\n <p>{g.name}</p>\n <button onClick={() => grantCondition({ id: uuidv4(), type: \"GROUP\", priority: selectedFlag.rules.length, conditions: [] }, { field: \"groupId\", operator: \"equals\", value: g.id })} className=\"bg-purple-500 text-white px-3 py-1 rounded hover:bg-purple-600\">Grant</button>\n </div>\n ))}\n </div>\n </div>\n )\n}\n","import React from \"react\"\nimport type { FeatureFlag } from \"../../core/types\"\n\ninterface AuditViewProps {\n flags: FeatureFlag[]\n}\n\nexport const AuditView: React.FC<AuditViewProps> = ({ flags }) => (\n <div>\n <h3 className=\"font-bold mb-2\">Audit / Access Management</h3>\n {flags.map(flag => (\n <div key={flag.key} className=\"border p-2 rounded mb-2\">\n <p className=\"font-semibold\">{flag.key}</p>\n <ul className=\"ml-4 text-sm\">\n {flag.rules.map(r => (\n <li key={r.id}>{r.type} - {JSON.stringify(r.conditions)}</li>\n ))}\n </ul>\n </div>\n ))}\n </div>\n)\n","import React, { createContext, ReactNode } from \"react\"\nimport type { FlagEngine } from \"../core/engine\"\nimport type { FlagContext } from \"../core/types\"\n\nexport interface FlagProviderValue {\n engine: FlagEngine\n context: FlagContext\n}\n\nexport const FlagContextReact =\n createContext<FlagProviderValue | null>(null)\n\ninterface FlagProviderProps {\n engine: FlagEngine\n context: FlagContext\n children: ReactNode\n}\n\nexport function FlagProvider({\n engine,\n context,\n children\n}: FlagProviderProps) {\n return (\n <FlagContextReact.Provider value={{ engine, context }}>\n {children}\n </FlagContextReact.Provider>\n )\n}\n","import { useContext, useEffect, useState } from \"react\"\nimport { FlagContextReact } from \"./FlagProvider\"\n\nexport function useFlag(key: string): boolean {\n const ctx = useContext(FlagContextReact)\n\n if (!ctx) {\n throw new Error(\"useFlag must be used inside FlagProvider\")\n }\n\n const { engine, context } = ctx\n const [enabled, setEnabled] = useState(false)\n\n useEffect(() => {\n let cancelled = false\n\n engine.isEnabled(key, context).then(result => {\n if (!cancelled) {\n setEnabled(result)\n }\n })\n\n return () => {\n cancelled = true\n }\n }, [engine, key, context])\n\n return enabled\n}\n","import React from \"react\"\nimport { useFlag } from \"./useFlag\"\n\ninterface WithFlagOptions {\n fallback?: React.ReactNode\n}\n\nexport function withFlag(\n flagKey: string,\n options?: WithFlagOptions\n) {\n return function <P extends object>(\n WrappedComponent: React.ComponentType<P>\n ) {\n const ComponentWithFlag: React.FC<P> = (props) => {\n const enabled = useFlag(flagKey)\n\n if (!enabled) {\n return <>{options?.fallback ?? null}</>\n }\n\n return <WrappedComponent {...props} />\n }\n\n ComponentWithFlag.displayName = `withFlag(${\n WrappedComponent.displayName ||\n WrappedComponent.name ||\n \"Component\"\n })`\n\n return ComponentWithFlag\n }\n}\n","import { FlagEngine } from \"../core/engine\"\nimport type { FlagContext } from \"../core/types\"\n\nexport interface RequireFlagOptions {\n key: string\n engine: FlagEngine\n}\n\n/**\n * Universal middleware/check function for any Node.js system\n * @param options.key - Feature flag key\n * @param options.engine - FlagEngine instance\n */\nexport function requireFlag({ key, engine }: RequireFlagOptions) {\n /**\n * context: object containing user info or any relevant attributes\n * next: function to call if feature is enabled\n * handleDenied: optional function to handle denial (default throws)\n */\n return async (\n context: FlagContext & { user?: any },\n next: () => void | Promise<void>,\n handleDenied?: () => void\n ) => {\n const allowed = await engine.isEnabled(key, {\n userId: context.user?.id,\n attributes: context.user,\n permissions: context.user?.permissions,\n groups: context.user?.groups ?? context.groups\n })\n\n if (!allowed) {\n if (handleDenied) return handleDenied()\n throw new Error(`Feature \"${key}\" is disabled for this user`)\n }\n\n return next()\n }\n}\n"],"mappings":"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","names":["import_framer_motion","import_clsx","clsx","import_jsx_runtime","uuidv4","import_uuid","import_jsx_runtime","uuidv4","import_jsx_runtime","import_jsx_runtime","clsx","import_react","import_jsx_runtime","import_react","import_jsx_runtime"]}
package/dist/index.mjs CHANGED
@@ -114,28 +114,292 @@ var MemoryFlagAdapter = class {
114
114
  }
115
115
  };
116
116
 
117
+ // src/react/FeatureFlagsDialog.tsx
118
+ import { useState, useEffect } from "react";
119
+ import { motion as motion2, AnimatePresence } from "framer-motion";
120
+ import clsx2 from "clsx";
121
+
122
+ // src/react/Tabs/FlagList.tsx
123
+ import { motion } from "framer-motion";
124
+ import clsx from "clsx";
125
+ import { jsx, jsxs } from "react/jsx-runtime";
126
+ var FlagList = ({ flags, selectedFlag, setSelectedFlag, updateFlag }) => {
127
+ const toggleFlag = async (flag) => {
128
+ flag.enabled = !flag.enabled;
129
+ if (updateFlag) await updateFlag(flag);
130
+ };
131
+ return /* @__PURE__ */ jsx("div", { className: "space-y-4", children: flags.map((flag) => /* @__PURE__ */ jsxs(
132
+ motion.div,
133
+ {
134
+ className: "flex justify-between items-center border rounded p-2 hover:bg-gray-50 cursor-pointer",
135
+ onClick: () => setSelectedFlag(flag),
136
+ whileHover: { scale: 1.02 },
137
+ children: [
138
+ /* @__PURE__ */ jsxs("div", { children: [
139
+ /* @__PURE__ */ jsx("p", { className: "font-semibold", children: flag.key }),
140
+ /* @__PURE__ */ jsxs("p", { className: "text-gray-500 text-sm", children: [
141
+ "Enabled: ",
142
+ flag.enabled ? "Yes" : "No"
143
+ ] })
144
+ ] }),
145
+ /* @__PURE__ */ jsx(
146
+ "button",
147
+ {
148
+ onClick: (e) => {
149
+ e.stopPropagation();
150
+ toggleFlag(flag);
151
+ },
152
+ className: clsx(
153
+ "px-3 py-1 rounded text-white",
154
+ flag.enabled ? "bg-red-500 hover:bg-red-600" : "bg-green-500 hover:bg-green-600"
155
+ ),
156
+ children: flag.enabled ? "Disable" : "Enable"
157
+ }
158
+ )
159
+ ]
160
+ },
161
+ flag.key
162
+ )) });
163
+ };
164
+
165
+ // src/react/Tabs/RulesEditor.tsx
166
+ import { v4 as uuidv4 } from "uuid";
167
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
168
+ var ruleTypes = [
169
+ "USER_ID",
170
+ "USER_ATTRIBUTE",
171
+ "PERMISSION",
172
+ "GROUP",
173
+ "PERCENTAGE",
174
+ "CUSTOM"
175
+ ];
176
+ var RulesEditor = ({ selectedFlag, updateFlag }) => {
177
+ const addRule = () => {
178
+ selectedFlag.rules.push({ id: uuidv4(), type: "USER_ID", priority: selectedFlag.rules.length, conditions: [] });
179
+ updateFlag?.(selectedFlag);
180
+ };
181
+ const addCondition = (rule) => {
182
+ const conditions = Array.isArray(rule.conditions) ? rule.conditions : [];
183
+ conditions.push({ field: "", operator: "equals", value: "" });
184
+ rule.conditions = conditions;
185
+ updateFlag?.(selectedFlag);
186
+ };
187
+ return /* @__PURE__ */ jsxs2("div", { className: "mt-4 border-t pt-4 space-y-2", children: [
188
+ /* @__PURE__ */ jsxs2("h3", { className: "font-bold mb-2", children: [
189
+ "Rules for ",
190
+ selectedFlag.key
191
+ ] }),
192
+ selectedFlag.rules.map((rule) => /* @__PURE__ */ jsxs2("div", { className: "flex flex-col gap-2 border p-2 rounded", children: [
193
+ /* @__PURE__ */ jsxs2("div", { className: "flex gap-2 items-center", children: [
194
+ /* @__PURE__ */ jsx2(
195
+ "select",
196
+ {
197
+ value: rule.type,
198
+ onChange: (e) => {
199
+ rule.type = e.target.value;
200
+ updateFlag?.(selectedFlag);
201
+ },
202
+ className: "border p-1 rounded",
203
+ children: ruleTypes.map((rt) => /* @__PURE__ */ jsx2("option", { value: rt, children: rt }, rt))
204
+ }
205
+ ),
206
+ /* @__PURE__ */ jsx2(
207
+ "input",
208
+ {
209
+ type: "number",
210
+ value: rule.priority,
211
+ onChange: (e) => {
212
+ rule.priority = Number(e.target.value);
213
+ updateFlag?.(selectedFlag);
214
+ },
215
+ className: "border p-1 rounded w-20"
216
+ }
217
+ ),
218
+ /* @__PURE__ */ jsx2("button", { onClick: () => addCondition(rule), className: "bg-blue-500 text-white px-2 py-1 rounded hover:bg-blue-600", children: "+ Condition" })
219
+ ] }),
220
+ (Array.isArray(rule.conditions) ? rule.conditions : []).map((c, i) => /* @__PURE__ */ jsxs2("div", { className: "flex gap-2 items-center ml-4", children: [
221
+ /* @__PURE__ */ jsx2("input", { type: "text", value: c.field, onChange: (e) => {
222
+ c.field = e.target.value;
223
+ updateFlag?.(selectedFlag);
224
+ }, className: "border p-1 rounded w-24" }),
225
+ /* @__PURE__ */ jsx2("input", { type: "text", value: c.operator, onChange: (e) => {
226
+ c.operator = e.target.value;
227
+ updateFlag?.(selectedFlag);
228
+ }, className: "border p-1 rounded w-24" }),
229
+ /* @__PURE__ */ jsx2("input", { type: "text", value: c.value, onChange: (e) => {
230
+ c.value = e.target.value;
231
+ updateFlag?.(selectedFlag);
232
+ }, className: "border p-1 rounded w-24" }),
233
+ /* @__PURE__ */ jsx2("button", { onClick: () => {
234
+ rule.conditions.splice(i, 1);
235
+ updateFlag?.(selectedFlag);
236
+ }, className: "bg-red-500 text-white px-2 py-1 rounded hover:bg-red-600", children: "\u2715" })
237
+ ] }, i))
238
+ ] }, rule.id)),
239
+ /* @__PURE__ */ jsx2("button", { onClick: addRule, className: "bg-blue-500 text-white px-3 py-1 rounded hover:bg-blue-600 mt-2", children: "Add Rule" })
240
+ ] });
241
+ };
242
+
243
+ // src/react/Tabs/GrantEditor.tsx
244
+ import { v4 as uuidv42 } from "uuid";
245
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
246
+ var GrantEditor = ({ selectedFlag, users, groups, updateFlag }) => {
247
+ const grantCondition = (rule, cond) => {
248
+ const conditions = Array.isArray(rule.conditions) ? rule.conditions : [];
249
+ conditions.push(cond);
250
+ rule.conditions = conditions;
251
+ selectedFlag.rules.push(rule);
252
+ updateFlag?.(selectedFlag);
253
+ };
254
+ return /* @__PURE__ */ jsxs3("div", { className: "space-y-4", children: [
255
+ /* @__PURE__ */ jsxs3("h3", { className: "font-bold mb-2", children: [
256
+ "Grant Access for ",
257
+ selectedFlag.key
258
+ ] }),
259
+ /* @__PURE__ */ jsxs3("div", { className: "space-y-2", children: [
260
+ /* @__PURE__ */ jsx3("h4", { className: "font-semibold", children: "Users" }),
261
+ users.map((u) => /* @__PURE__ */ jsxs3("div", { className: "flex justify-between items-center border p-2 rounded hover:bg-gray-50", children: [
262
+ /* @__PURE__ */ jsx3("p", { children: u.username || u.id }),
263
+ /* @__PURE__ */ jsx3("button", { onClick: () => grantCondition({ id: uuidv42(), type: "USER_ID", priority: selectedFlag.rules.length, conditions: [] }, { field: "userId", operator: "equals", value: u.id }), className: "bg-green-500 text-white px-3 py-1 rounded hover:bg-green-600", children: "Grant" })
264
+ ] }, u.id))
265
+ ] }),
266
+ /* @__PURE__ */ jsxs3("div", { className: "space-y-2", children: [
267
+ /* @__PURE__ */ jsx3("h4", { className: "font-semibold", children: "Groups" }),
268
+ groups.map((g) => /* @__PURE__ */ jsxs3("div", { className: "flex justify-between items-center border p-2 rounded hover:bg-gray-50", children: [
269
+ /* @__PURE__ */ jsx3("p", { children: g.name }),
270
+ /* @__PURE__ */ jsx3("button", { onClick: () => grantCondition({ id: uuidv42(), type: "GROUP", priority: selectedFlag.rules.length, conditions: [] }, { field: "groupId", operator: "equals", value: g.id }), className: "bg-purple-500 text-white px-3 py-1 rounded hover:bg-purple-600", children: "Grant" })
271
+ ] }, g.id))
272
+ ] })
273
+ ] });
274
+ };
275
+
276
+ // src/react/Tabs/AuditView.tsx
277
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
278
+ var AuditView = ({ flags }) => /* @__PURE__ */ jsxs4("div", { children: [
279
+ /* @__PURE__ */ jsx4("h3", { className: "font-bold mb-2", children: "Audit / Access Management" }),
280
+ flags.map((flag) => /* @__PURE__ */ jsxs4("div", { className: "border p-2 rounded mb-2", children: [
281
+ /* @__PURE__ */ jsx4("p", { className: "font-semibold", children: flag.key }),
282
+ /* @__PURE__ */ jsx4("ul", { className: "ml-4 text-sm", children: flag.rules.map((r) => /* @__PURE__ */ jsxs4("li", { children: [
283
+ r.type,
284
+ " - ",
285
+ JSON.stringify(r.conditions)
286
+ ] }, r.id)) })
287
+ ] }, flag.key))
288
+ ] });
289
+
290
+ // src/react/FeatureFlagsDialog.tsx
291
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
292
+ var FeatureFlagManager = ({
293
+ engine,
294
+ isOpen,
295
+ onClose,
296
+ fetchFlags,
297
+ fetchUsers,
298
+ fetchGroups,
299
+ updateFlag
300
+ }) => {
301
+ const [flags, setFlags] = useState([]);
302
+ const [selectedFlag, setSelectedFlag] = useState(null);
303
+ const [tab, setTab] = useState("flags");
304
+ const [users, setUsers] = useState([]);
305
+ const [groups, setGroups] = useState([]);
306
+ useEffect(() => {
307
+ if (!isOpen) return;
308
+ const loadFlags = async () => setFlags(await fetchFlags());
309
+ loadFlags();
310
+ }, [isOpen]);
311
+ useEffect(() => {
312
+ if (!isOpen || tab !== "grant") return;
313
+ const loadData = async () => {
314
+ if (fetchUsers) setUsers(await fetchUsers());
315
+ if (fetchGroups) setGroups(await fetchGroups());
316
+ };
317
+ loadData();
318
+ }, [isOpen, tab, fetchUsers, fetchGroups]);
319
+ if (!isOpen) return null;
320
+ return /* @__PURE__ */ jsx5(AnimatePresence, { children: isOpen && /* @__PURE__ */ jsx5(
321
+ motion2.div,
322
+ {
323
+ className: "fixed inset-0 flex items-center justify-center z-50 bg-black/50",
324
+ initial: { opacity: 0 },
325
+ animate: { opacity: 1 },
326
+ exit: { opacity: 0 },
327
+ children: /* @__PURE__ */ jsxs5(
328
+ motion2.div,
329
+ {
330
+ className: "bg-white rounded-lg shadow-xl w-11/12 max-w-6xl p-6",
331
+ initial: { scale: 0.8, opacity: 0 },
332
+ animate: { scale: 1, opacity: 1 },
333
+ exit: { scale: 0.8, opacity: 0 },
334
+ transition: { type: "spring", stiffness: 300, damping: 25 },
335
+ children: [
336
+ /* @__PURE__ */ jsxs5("div", { className: "flex justify-between items-center mb-4", children: [
337
+ /* @__PURE__ */ jsx5("h2", { className: "text-xl font-bold", children: "Feature Flags Manager" }),
338
+ /* @__PURE__ */ jsx5("button", { onClick: onClose, className: "text-gray-500 hover:text-gray-800", children: "\u2715" })
339
+ ] }),
340
+ /* @__PURE__ */ jsx5("div", { className: "flex gap-2 mb-4 border-b", children: ["flags", "grant", "audit"].map((t) => /* @__PURE__ */ jsx5(
341
+ "button",
342
+ {
343
+ className: clsx2(
344
+ "px-4 py-2 rounded-t font-semibold",
345
+ tab === t ? "bg-white border-t border-x border-gray-300" : "bg-gray-200 hover:bg-gray-300"
346
+ ),
347
+ onClick: () => setTab(t),
348
+ children: t === "flags" ? "Feature Flags" : t === "grant" ? "Grant Access" : "Audit"
349
+ },
350
+ t
351
+ )) }),
352
+ /* @__PURE__ */ jsxs5("div", { className: "max-h-[60vh] overflow-y-auto", children: [
353
+ tab === "flags" && /* @__PURE__ */ jsx5(
354
+ FlagList,
355
+ {
356
+ flags,
357
+ selectedFlag,
358
+ setSelectedFlag,
359
+ updateFlag
360
+ }
361
+ ),
362
+ tab === "grant" && selectedFlag && /* @__PURE__ */ jsx5(
363
+ GrantEditor,
364
+ {
365
+ selectedFlag,
366
+ users,
367
+ groups,
368
+ updateFlag
369
+ }
370
+ ),
371
+ tab === "audit" && /* @__PURE__ */ jsx5(AuditView, { flags }),
372
+ selectedFlag && tab === "flags" && /* @__PURE__ */ jsx5(RulesEditor, { selectedFlag, updateFlag })
373
+ ] })
374
+ ]
375
+ }
376
+ )
377
+ }
378
+ ) });
379
+ };
380
+
117
381
  // src/react/FlagProvider.tsx
118
382
  import { createContext } from "react";
119
- import { jsx } from "react/jsx-runtime";
383
+ import { jsx as jsx6 } from "react/jsx-runtime";
120
384
  var FlagContextReact = createContext(null);
121
385
  function FlagProvider({
122
386
  engine,
123
387
  context,
124
388
  children
125
389
  }) {
126
- return /* @__PURE__ */ jsx(FlagContextReact.Provider, { value: { engine, context }, children });
390
+ return /* @__PURE__ */ jsx6(FlagContextReact.Provider, { value: { engine, context }, children });
127
391
  }
128
392
 
129
393
  // src/react/useFlag.ts
130
- import { useContext, useEffect, useState } from "react";
394
+ import { useContext, useEffect as useEffect2, useState as useState2 } from "react";
131
395
  function useFlag(key) {
132
396
  const ctx = useContext(FlagContextReact);
133
397
  if (!ctx) {
134
398
  throw new Error("useFlag must be used inside FlagProvider");
135
399
  }
136
400
  const { engine, context } = ctx;
137
- const [enabled, setEnabled] = useState(false);
138
- useEffect(() => {
401
+ const [enabled, setEnabled] = useState2(false);
402
+ useEffect2(() => {
139
403
  let cancelled = false;
140
404
  engine.isEnabled(key, context).then((result) => {
141
405
  if (!cancelled) {
@@ -150,15 +414,15 @@ function useFlag(key) {
150
414
  }
151
415
 
152
416
  // src/react/withFlag.tsx
153
- import { Fragment, jsx as jsx2 } from "react/jsx-runtime";
417
+ import { Fragment, jsx as jsx7 } from "react/jsx-runtime";
154
418
  function withFlag(flagKey, options) {
155
419
  return function(WrappedComponent) {
156
420
  const ComponentWithFlag = (props) => {
157
421
  const enabled = useFlag(flagKey);
158
422
  if (!enabled) {
159
- return /* @__PURE__ */ jsx2(Fragment, { children: options?.fallback ?? null });
423
+ return /* @__PURE__ */ jsx7(Fragment, { children: options?.fallback ?? null });
160
424
  }
161
- return /* @__PURE__ */ jsx2(WrappedComponent, { ...props });
425
+ return /* @__PURE__ */ jsx7(WrappedComponent, { ...props });
162
426
  };
163
427
  ComponentWithFlag.displayName = `withFlag(${WrappedComponent.displayName || WrappedComponent.name || "Component"})`;
164
428
  return ComponentWithFlag;
@@ -182,6 +446,7 @@ function requireFlag({ key, engine }) {
182
446
  };
183
447
  }
184
448
  export {
449
+ FeatureFlagManager,
185
450
  FlagContextReact,
186
451
  FlagEngine,
187
452
  FlagProvider,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core/engine.ts","../src/adapters/prisma.ts","../src/adapters/memory.ts","../src/react/FlagProvider.tsx","../src/react/useFlag.ts","../src/react/withFlag.tsx","../src/node/middleware.ts"],"sourcesContent":["import { FlagAdapter, FlagContext } from \"./types\"\r\n\r\nexport class FlagEngine {\r\n constructor(private adapter: FlagAdapter) {}\r\n\r\n async isEnabled(key: string, context: FlagContext): Promise<boolean> {\r\n const flag = await this.adapter.getFlag(key)\r\n if (!flag || !flag.enabled) return false\r\n\r\n const sortedRules = flag.rules.sort((a: any, b: any) => b.priority - a.priority)\r\n\r\n for (const rule of sortedRules) {\r\n const result = await this.evaluateRule(rule, context)\r\n if (result !== null) return result\r\n }\r\n\r\n return false\r\n }\r\n\r\n private async evaluateRule(rule: any, context: FlagContext): Promise<boolean | null> {\r\n switch (rule.type) {\r\n case \"USER_ID\":\r\n return rule.conditions.includes(context.userId)\r\n\r\n case \"PERMISSION\":\r\n return context.permissions?.includes(rule.conditions.permission) ?? false\r\n\r\n case \"USER_ATTRIBUTE\":\r\n const { field, operator, value } = rule.conditions\r\n const userValue = context.attributes?.[field]\r\n\r\n switch (operator) {\r\n case \"equals\":\r\n return userValue === value\r\n case \"gt\":\r\n return userValue > value\r\n case \"lt\":\r\n return userValue < value\r\n case \"contains\":\r\n return userValue?.includes(value)\r\n }\r\n return false\r\n\r\n case \"GROUP_MEMBERSHIP\":\r\n return context.groups?.includes(rule.conditions.groupId) ?? false\r\n\r\n case \"PERCENTAGE\":\r\n return this.percentageRollout(context.userId!, rule.percentage)\r\n\r\n default:\r\n return null\r\n }\r\n }\r\n\r\n private percentageRollout(userId: string, percentage: number) {\r\n const hash = this.hash(userId)\r\n return (hash % 100) < percentage\r\n }\r\n\r\n private hash(str: string) {\r\n let hash = 0\r\n for (let i = 0; i < str.length; i++) {\r\n hash = (hash << 5) - hash + str.charCodeAt(i)\r\n hash |= 0\r\n }\r\n return Math.abs(hash)\r\n }\r\n}\r\n","import { PrismaClient } from \"@custom-prisma/client\" // ← match schema generator\r\nimport { FlagAdapter, FeatureFlag } from \"../core/types\"\r\n\r\nexport class PrismaFlagAdapter implements FlagAdapter {\r\n constructor(private prisma: PrismaClient) {}\r\n\r\n async getFlag(key: string): Promise<FeatureFlag | null> {\r\n const flag = await this.prisma.featureFlag.findUnique({\r\n where: { key },\r\n include: { rules: true } // make sure 'rules' relation exists\r\n })\r\n\r\n if (!flag) return null\r\n\r\n return {\r\n key: flag.key,\r\n enabled: flag.enabled,\r\n rules: flag.rules as any\r\n }\r\n }\r\n}\r\n","import type {\r\n FlagAdapter,\r\n FeatureFlag\r\n} from \"../core/types\"\r\n\r\nexport interface MemoryAdapterOptions {\r\n initialFlags?: FeatureFlag[]\r\n}\r\n\r\nexport class MemoryFlagAdapter implements FlagAdapter {\r\n private flags = new Map<string, FeatureFlag>()\r\n\r\n constructor(options?: MemoryAdapterOptions) {\r\n if (options?.initialFlags) {\r\n for (const flag of options.initialFlags) {\r\n this.flags.set(flag.key, flag)\r\n }\r\n }\r\n }\r\n\r\n async getFlag(key: string): Promise<FeatureFlag | null> {\r\n return this.flags.get(key) ?? null\r\n }\r\n\r\n /**\r\n * Add or update a flag\r\n */\r\n setFlag(flag: FeatureFlag): void {\r\n this.flags.set(flag.key, flag)\r\n }\r\n\r\n /**\r\n * Remove a flag\r\n */\r\n removeFlag(key: string): void {\r\n this.flags.delete(key)\r\n }\r\n\r\n /**\r\n * Get all flags (useful for debugging)\r\n */\r\n getAllFlags(): FeatureFlag[] {\r\n return Array.from(this.flags.values())\r\n }\r\n\r\n /**\r\n * Clear all flags\r\n */\r\n clear(): void {\r\n this.flags.clear()\r\n }\r\n}\r\n","import React, { createContext, ReactNode } from \"react\"\r\nimport type { FlagEngine } from \"../core/engine\"\r\nimport type { FlagContext } from \"../core/types\"\r\n\r\nexport interface FlagProviderValue {\r\n engine: FlagEngine\r\n context: FlagContext\r\n}\r\n\r\nexport const FlagContextReact =\r\n createContext<FlagProviderValue | null>(null)\r\n\r\ninterface FlagProviderProps {\r\n engine: FlagEngine\r\n context: FlagContext\r\n children: ReactNode\r\n}\r\n\r\nexport function FlagProvider({\r\n engine,\r\n context,\r\n children\r\n}: FlagProviderProps) {\r\n return (\r\n <FlagContextReact.Provider value={{ engine, context }}>\r\n {children}\r\n </FlagContextReact.Provider>\r\n )\r\n}\r\n","import { useContext, useEffect, useState } from \"react\"\r\nimport { FlagContextReact } from \"./FlagProvider\"\r\n\r\nexport function useFlag(key: string): boolean {\r\n const ctx = useContext(FlagContextReact)\r\n\r\n if (!ctx) {\r\n throw new Error(\"useFlag must be used inside FlagProvider\")\r\n }\r\n\r\n const { engine, context } = ctx\r\n const [enabled, setEnabled] = useState(false)\r\n\r\n useEffect(() => {\r\n let cancelled = false\r\n\r\n engine.isEnabled(key, context).then(result => {\r\n if (!cancelled) {\r\n setEnabled(result)\r\n }\r\n })\r\n\r\n return () => {\r\n cancelled = true\r\n }\r\n }, [engine, key, context])\r\n\r\n return enabled\r\n}\r\n","import React from \"react\"\r\nimport { useFlag } from \"./useFlag\"\r\n\r\ninterface WithFlagOptions {\r\n fallback?: React.ReactNode\r\n}\r\n\r\nexport function withFlag(\r\n flagKey: string,\r\n options?: WithFlagOptions\r\n) {\r\n return function <P extends object>(\r\n WrappedComponent: React.ComponentType<P>\r\n ) {\r\n const ComponentWithFlag: React.FC<P> = (props) => {\r\n const enabled = useFlag(flagKey)\r\n\r\n if (!enabled) {\r\n return <>{options?.fallback ?? null}</>\r\n }\r\n\r\n return <WrappedComponent {...props} />\r\n }\r\n\r\n ComponentWithFlag.displayName = `withFlag(${\r\n WrappedComponent.displayName ||\r\n WrappedComponent.name ||\r\n \"Component\"\r\n })`\r\n\r\n return ComponentWithFlag\r\n }\r\n}\r\n","import { FlagEngine } from \"../core/engine\"\r\nimport type { FlagContext } from \"../core/types\"\r\n\r\nexport interface RequireFlagOptions {\r\n key: string\r\n engine: FlagEngine\r\n}\r\n\r\n/**\r\n * Universal middleware/check function for any Node.js system\r\n * @param options.key - Feature flag key\r\n * @param options.engine - FlagEngine instance\r\n */\r\nexport function requireFlag({ key, engine }: RequireFlagOptions) {\r\n /**\r\n * context: object containing user info or any relevant attributes\r\n * next: function to call if feature is enabled\r\n * handleDenied: optional function to handle denial (default throws)\r\n */\r\n return async (\r\n context: FlagContext & { user?: any },\r\n next: () => void | Promise<void>,\r\n handleDenied?: () => void\r\n ) => {\r\n const allowed = await engine.isEnabled(key, {\r\n userId: context.user?.id,\r\n attributes: context.user,\r\n permissions: context.user?.permissions,\r\n groups: context.user?.groups ?? context.groups\r\n })\r\n\r\n if (!allowed) {\r\n if (handleDenied) return handleDenied()\r\n throw new Error(`Feature \"${key}\" is disabled for this user`)\r\n }\r\n\r\n return next()\r\n }\r\n}\r\n"],"mappings":"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","names":["jsx"]}
1
+ {"version":3,"sources":["../src/core/engine.ts","../src/adapters/prisma.ts","../src/adapters/memory.ts","../src/react/FeatureFlagsDialog.tsx","../src/react/Tabs/FlagList.tsx","../src/react/Tabs/RulesEditor.tsx","../src/react/Tabs/GrantEditor.tsx","../src/react/Tabs/AuditView.tsx","../src/react/FlagProvider.tsx","../src/react/useFlag.ts","../src/react/withFlag.tsx","../src/node/middleware.ts"],"sourcesContent":["import { FlagAdapter, FlagContext } from \"./types\"\n\nexport class FlagEngine {\n constructor(private adapter: FlagAdapter) {}\n\n async isEnabled(key: string, context: FlagContext): Promise<boolean> {\n const flag = await this.adapter.getFlag(key)\n if (!flag || !flag.enabled) return false\n\n const sortedRules = flag.rules.sort((a: any, b: any) => b.priority - a.priority)\n\n for (const rule of sortedRules) {\n const result = await this.evaluateRule(rule, context)\n if (result !== null) return result\n }\n\n return false\n }\n\n private async evaluateRule(rule: any, context: FlagContext): Promise<boolean | null> {\n switch (rule.type) {\n case \"USER_ID\":\n return rule.conditions.includes(context.userId)\n\n case \"PERMISSION\":\n return context.permissions?.includes(rule.conditions.permission) ?? false\n\n case \"USER_ATTRIBUTE\":\n const { field, operator, value } = rule.conditions\n const userValue = context.attributes?.[field]\n\n switch (operator) {\n case \"equals\":\n return userValue === value\n case \"gt\":\n return userValue > value\n case \"lt\":\n return userValue < value\n case \"contains\":\n return userValue?.includes(value)\n }\n return false\n\n case \"GROUP_MEMBERSHIP\":\n return context.groups?.includes(rule.conditions.groupId) ?? false\n\n case \"PERCENTAGE\":\n return this.percentageRollout(context.userId!, rule.percentage)\n\n default:\n return null\n }\n }\n\n private percentageRollout(userId: string, percentage: number) {\n const hash = this.hash(userId)\n return (hash % 100) < percentage\n }\n\n private hash(str: string) {\n let hash = 0\n for (let i = 0; i < str.length; i++) {\n hash = (hash << 5) - hash + str.charCodeAt(i)\n hash |= 0\n }\n return Math.abs(hash)\n }\n}\n","import { PrismaClient } from \"@custom-prisma/client\" // ← match schema generator\nimport { FlagAdapter, FeatureFlag } from \"../core/types\"\n\nexport class PrismaFlagAdapter implements FlagAdapter {\n constructor(private prisma: PrismaClient) {}\n\n async getFlag(key: string): Promise<FeatureFlag | null> {\n const flag = await this.prisma.featureFlag.findUnique({\n where: { key },\n include: { rules: true } // make sure 'rules' relation exists\n })\n\n if (!flag) return null\n\n return {\n key: flag.key,\n enabled: flag.enabled,\n rules: flag.rules as any\n }\n }\n}\n","import type {\n FlagAdapter,\n FeatureFlag\n} from \"../core/types\"\n\nexport interface MemoryAdapterOptions {\n initialFlags?: FeatureFlag[]\n}\n\nexport class MemoryFlagAdapter implements FlagAdapter {\n private flags = new Map<string, FeatureFlag>()\n\n constructor(options?: MemoryAdapterOptions) {\n if (options?.initialFlags) {\n for (const flag of options.initialFlags) {\n this.flags.set(flag.key, flag)\n }\n }\n }\n\n async getFlag(key: string): Promise<FeatureFlag | null> {\n return this.flags.get(key) ?? null\n }\n\n /**\n * Add or update a flag\n */\n setFlag(flag: FeatureFlag): void {\n this.flags.set(flag.key, flag)\n }\n\n /**\n * Remove a flag\n */\n removeFlag(key: string): void {\n this.flags.delete(key)\n }\n\n /**\n * Get all flags (useful for debugging)\n */\n getAllFlags(): FeatureFlag[] {\n return Array.from(this.flags.values())\n }\n\n /**\n * Clear all flags\n */\n clear(): void {\n this.flags.clear()\n }\n}\n","import React, { useState, useEffect } from \"react\"\nimport { motion, AnimatePresence } from \"framer-motion\"\nimport clsx from \"clsx\"\n\nimport type { FeatureFlag, FlagRule, RuleType } from \"../core/types\"\nimport { FlagEngine } from \"../core/engine\"\n\nimport { FlagList } from \"./Tabs/FlagList\";\nimport { RulesEditor } from \"./Tabs/RulesEditor\";\nimport { GrantEditor } from \"./Tabs/GrantEditor\";\nimport { AuditView } from \"./Tabs/AuditView\";\n\ntype Tab = \"flags\" | \"grant\" | \"audit\"\n\ninterface FeatureFlagManagerProps {\n engine: FlagEngine\n isOpen: boolean\n onClose: () => void\n fetchFlags: () => Promise<FeatureFlag[]>\n fetchUsers?: () => Promise<any[]>\n fetchGroups?: () => Promise<any[]>\n updateFlag?: (flag: FeatureFlag) => Promise<void>\n}\n\nexport const FeatureFlagManager: React.FC<FeatureFlagManagerProps> = ({\n engine,\n isOpen,\n onClose,\n fetchFlags,\n fetchUsers,\n fetchGroups,\n updateFlag\n}) => {\n const [flags, setFlags] = useState<FeatureFlag[]>([])\n const [selectedFlag, setSelectedFlag] = useState<FeatureFlag | null>(null)\n const [tab, setTab] = useState<Tab>(\"flags\")\n const [users, setUsers] = useState<any[]>([])\n const [groups, setGroups] = useState<any[]>([])\n\n useEffect(() => {\n if (!isOpen) return\n const loadFlags = async () => setFlags(await fetchFlags())\n loadFlags()\n }, [isOpen])\n\n useEffect(() => {\n if (!isOpen || tab !== \"grant\") return\n const loadData = async () => {\n if (fetchUsers) setUsers(await fetchUsers())\n if (fetchGroups) setGroups(await fetchGroups())\n }\n loadData()\n }, [isOpen, tab, fetchUsers, fetchGroups])\n\n if (!isOpen) return null\n\n return (\n <AnimatePresence>\n {isOpen && (\n <motion.div\n className=\"fixed inset-0 flex items-center justify-center z-50 bg-black/50\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n >\n <motion.div\n className=\"bg-white rounded-lg shadow-xl w-11/12 max-w-6xl p-6\"\n initial={{ scale: 0.8, opacity: 0 }}\n animate={{ scale: 1, opacity: 1 }}\n exit={{ scale: 0.8, opacity: 0 }}\n transition={{ type: \"spring\", stiffness: 300, damping: 25 }}\n >\n {/* Header */}\n <div className=\"flex justify-between items-center mb-4\">\n <h2 className=\"text-xl font-bold\">Feature Flags Manager</h2>\n <button onClick={onClose} className=\"text-gray-500 hover:text-gray-800\">āœ•</button>\n </div>\n\n {/* Tabs */}\n <div className=\"flex gap-2 mb-4 border-b\">\n {([\"flags\", \"grant\", \"audit\"] as Tab[]).map(t => (\n <button\n key={t}\n className={clsx(\n \"px-4 py-2 rounded-t font-semibold\",\n tab === t ? \"bg-white border-t border-x border-gray-300\" : \"bg-gray-200 hover:bg-gray-300\"\n )}\n onClick={() => setTab(t)}\n >\n {t === \"flags\" ? \"Feature Flags\" : t === \"grant\" ? \"Grant Access\" : \"Audit\"}\n </button>\n ))}\n </div>\n\n {/* Tab content */}\n <div className=\"max-h-[60vh] overflow-y-auto\">\n {tab === \"flags\" && (\n <FlagList\n flags={flags}\n selectedFlag={selectedFlag}\n setSelectedFlag={setSelectedFlag}\n updateFlag={updateFlag}\n />\n )}\n {tab === \"grant\" && selectedFlag && (\n <GrantEditor\n selectedFlag={selectedFlag}\n users={users}\n groups={groups}\n updateFlag={updateFlag}\n />\n )}\n {tab === \"audit\" && <AuditView flags={flags} />}\n {selectedFlag && tab === \"flags\" && (\n <RulesEditor selectedFlag={selectedFlag} updateFlag={updateFlag} />\n )}\n </div>\n </motion.div>\n </motion.div>\n )}\n </AnimatePresence>\n )\n}\n","import React from \"react\"\nimport { motion } from \"framer-motion\"\nimport clsx from \"clsx\"\nimport type { FeatureFlag } from \"../../core/types\"\n\ninterface FlagListProps {\n flags: FeatureFlag[]\n selectedFlag: FeatureFlag | null\n setSelectedFlag: (flag: FeatureFlag) => void\n updateFlag?: (flag: FeatureFlag) => Promise<void>\n}\n\nexport const FlagList: React.FC<FlagListProps> = ({ flags, selectedFlag, setSelectedFlag, updateFlag }) => {\n const toggleFlag = async (flag: FeatureFlag) => {\n flag.enabled = !flag.enabled\n if (updateFlag) await updateFlag(flag)\n }\n\n return (\n <div className=\"space-y-4\">\n {flags.map(flag => (\n <motion.div\n key={flag.key}\n className=\"flex justify-between items-center border rounded p-2 hover:bg-gray-50 cursor-pointer\"\n onClick={() => setSelectedFlag(flag)}\n whileHover={{ scale: 1.02 }}\n >\n <div>\n <p className=\"font-semibold\">{flag.key}</p>\n <p className=\"text-gray-500 text-sm\">\n Enabled: {flag.enabled ? \"Yes\" : \"No\"}\n </p>\n </div>\n <button\n onClick={e => {\n e.stopPropagation()\n toggleFlag(flag)\n }}\n className={clsx(\n \"px-3 py-1 rounded text-white\",\n flag.enabled ? \"bg-red-500 hover:bg-red-600\" : \"bg-green-500 hover:bg-green-600\"\n )}\n >\n {flag.enabled ? \"Disable\" : \"Enable\"}\n </button>\n </motion.div>\n ))}\n </div>\n )\n}\n","import React from \"react\"\nimport { v4 as uuidv4 } from \"uuid\"\nimport type { FeatureFlag, FlagRule, RuleType, Condition } from \"../../core/types\"\n\ninterface RulesEditorProps {\n selectedFlag: FeatureFlag\n updateFlag?: (flag: FeatureFlag) => Promise<void>\n}\n\nconst ruleTypes: RuleType[] = [\n \"USER_ID\",\n \"USER_ATTRIBUTE\",\n \"PERMISSION\",\n \"GROUP\",\n \"PERCENTAGE\",\n \"CUSTOM\"\n]\n\nexport const RulesEditor: React.FC<RulesEditorProps> = ({ selectedFlag, updateFlag }) => {\n\n const addRule = () => {\n selectedFlag.rules.push({ id: uuidv4(), type: \"USER_ID\", priority: selectedFlag.rules.length, conditions: [] })\n updateFlag?.(selectedFlag)\n }\n\n const addCondition = (rule: FlagRule) => {\n const conditions = Array.isArray(rule.conditions) ? rule.conditions : []\n conditions.push({ field: \"\", operator: \"equals\", value: \"\" })\n rule.conditions = conditions\n updateFlag?.(selectedFlag)\n }\n\n return (\n <div className=\"mt-4 border-t pt-4 space-y-2\">\n <h3 className=\"font-bold mb-2\">Rules for {selectedFlag.key}</h3>\n {selectedFlag.rules.map(rule => (\n <div key={rule.id} className=\"flex flex-col gap-2 border p-2 rounded\">\n <div className=\"flex gap-2 items-center\">\n <select\n value={rule.type}\n onChange={e => {\n rule.type = e.target.value as RuleType\n updateFlag?.(selectedFlag)\n }}\n className=\"border p-1 rounded\"\n >\n {ruleTypes.map(rt => (\n <option key={rt} value={rt}>{rt}</option>\n ))}\n </select>\n <input\n type=\"number\"\n value={rule.priority}\n onChange={e => {\n rule.priority = Number(e.target.value)\n updateFlag?.(selectedFlag)\n }}\n className=\"border p-1 rounded w-20\"\n />\n <button onClick={() => addCondition(rule)} className=\"bg-blue-500 text-white px-2 py-1 rounded hover:bg-blue-600\">\n + Condition\n </button>\n </div>\n {/* Render conditions */}\n {(Array.isArray(rule.conditions) ? rule.conditions : []).map((c, i) => (\n <div key={i} className=\"flex gap-2 items-center ml-4\">\n <input type=\"text\" value={c.field} onChange={e => { c.field = e.target.value; updateFlag?.(selectedFlag) }} className=\"border p-1 rounded w-24\" />\n <input type=\"text\" value={c.operator} onChange={e => { c.operator = e.target.value as any; updateFlag?.(selectedFlag) }} className=\"border p-1 rounded w-24\" />\n <input type=\"text\" value={c.value} onChange={e => { c.value = e.target.value; updateFlag?.(selectedFlag) }} className=\"border p-1 rounded w-24\" />\n <button onClick={() => { (rule.conditions as Condition[]).splice(i, 1); updateFlag?.(selectedFlag) }} className=\"bg-red-500 text-white px-2 py-1 rounded hover:bg-red-600\">āœ•</button>\n </div>\n ))}\n </div>\n ))}\n <button onClick={addRule} className=\"bg-blue-500 text-white px-3 py-1 rounded hover:bg-blue-600 mt-2\">Add Rule</button>\n </div>\n )\n}\n","import React from \"react\"\nimport { v4 as uuidv4 } from \"uuid\"\nimport type { FeatureFlag, FlagRule, RuleType, Condition } from \"../../core/types\"\n\ninterface GrantEditorProps {\n selectedFlag: FeatureFlag\n users: any[]\n groups: any[]\n updateFlag?: (flag: FeatureFlag) => Promise<void>\n}\n\nexport const GrantEditor: React.FC<GrantEditorProps> = ({ selectedFlag, users, groups, updateFlag }) => {\n\n const grantCondition = (rule: FlagRule, cond: Condition) => {\n const conditions = Array.isArray(rule.conditions) ? rule.conditions : []\n conditions.push(cond)\n rule.conditions = conditions\n selectedFlag.rules.push(rule)\n updateFlag?.(selectedFlag)\n }\n\n return (\n <div className=\"space-y-4\">\n <h3 className=\"font-bold mb-2\">Grant Access for {selectedFlag.key}</h3>\n\n {/* Users */}\n <div className=\"space-y-2\">\n <h4 className=\"font-semibold\">Users</h4>\n {users.map(u => (\n <div key={u.id} className=\"flex justify-between items-center border p-2 rounded hover:bg-gray-50\">\n <p>{u.username || u.id}</p>\n <button onClick={() => grantCondition({ id: uuidv4(), type: \"USER_ID\", priority: selectedFlag.rules.length, conditions: [] }, { field: \"userId\", operator: \"equals\", value: u.id })} className=\"bg-green-500 text-white px-3 py-1 rounded hover:bg-green-600\">Grant</button>\n </div>\n ))}\n </div>\n\n {/* Groups */}\n <div className=\"space-y-2\">\n <h4 className=\"font-semibold\">Groups</h4>\n {groups.map(g => (\n <div key={g.id} className=\"flex justify-between items-center border p-2 rounded hover:bg-gray-50\">\n <p>{g.name}</p>\n <button onClick={() => grantCondition({ id: uuidv4(), type: \"GROUP\", priority: selectedFlag.rules.length, conditions: [] }, { field: \"groupId\", operator: \"equals\", value: g.id })} className=\"bg-purple-500 text-white px-3 py-1 rounded hover:bg-purple-600\">Grant</button>\n </div>\n ))}\n </div>\n </div>\n )\n}\n","import React from \"react\"\nimport type { FeatureFlag } from \"../../core/types\"\n\ninterface AuditViewProps {\n flags: FeatureFlag[]\n}\n\nexport const AuditView: React.FC<AuditViewProps> = ({ flags }) => (\n <div>\n <h3 className=\"font-bold mb-2\">Audit / Access Management</h3>\n {flags.map(flag => (\n <div key={flag.key} className=\"border p-2 rounded mb-2\">\n <p className=\"font-semibold\">{flag.key}</p>\n <ul className=\"ml-4 text-sm\">\n {flag.rules.map(r => (\n <li key={r.id}>{r.type} - {JSON.stringify(r.conditions)}</li>\n ))}\n </ul>\n </div>\n ))}\n </div>\n)\n","import React, { createContext, ReactNode } from \"react\"\nimport type { FlagEngine } from \"../core/engine\"\nimport type { FlagContext } from \"../core/types\"\n\nexport interface FlagProviderValue {\n engine: FlagEngine\n context: FlagContext\n}\n\nexport const FlagContextReact =\n createContext<FlagProviderValue | null>(null)\n\ninterface FlagProviderProps {\n engine: FlagEngine\n context: FlagContext\n children: ReactNode\n}\n\nexport function FlagProvider({\n engine,\n context,\n children\n}: FlagProviderProps) {\n return (\n <FlagContextReact.Provider value={{ engine, context }}>\n {children}\n </FlagContextReact.Provider>\n )\n}\n","import { useContext, useEffect, useState } from \"react\"\nimport { FlagContextReact } from \"./FlagProvider\"\n\nexport function useFlag(key: string): boolean {\n const ctx = useContext(FlagContextReact)\n\n if (!ctx) {\n throw new Error(\"useFlag must be used inside FlagProvider\")\n }\n\n const { engine, context } = ctx\n const [enabled, setEnabled] = useState(false)\n\n useEffect(() => {\n let cancelled = false\n\n engine.isEnabled(key, context).then(result => {\n if (!cancelled) {\n setEnabled(result)\n }\n })\n\n return () => {\n cancelled = true\n }\n }, [engine, key, context])\n\n return enabled\n}\n","import React from \"react\"\nimport { useFlag } from \"./useFlag\"\n\ninterface WithFlagOptions {\n fallback?: React.ReactNode\n}\n\nexport function withFlag(\n flagKey: string,\n options?: WithFlagOptions\n) {\n return function <P extends object>(\n WrappedComponent: React.ComponentType<P>\n ) {\n const ComponentWithFlag: React.FC<P> = (props) => {\n const enabled = useFlag(flagKey)\n\n if (!enabled) {\n return <>{options?.fallback ?? null}</>\n }\n\n return <WrappedComponent {...props} />\n }\n\n ComponentWithFlag.displayName = `withFlag(${\n WrappedComponent.displayName ||\n WrappedComponent.name ||\n \"Component\"\n })`\n\n return ComponentWithFlag\n }\n}\n","import { FlagEngine } from \"../core/engine\"\nimport type { FlagContext } from \"../core/types\"\n\nexport interface RequireFlagOptions {\n key: string\n engine: FlagEngine\n}\n\n/**\n * Universal middleware/check function for any Node.js system\n * @param options.key - Feature flag key\n * @param options.engine - FlagEngine instance\n */\nexport function requireFlag({ key, engine }: RequireFlagOptions) {\n /**\n * context: object containing user info or any relevant attributes\n * next: function to call if feature is enabled\n * handleDenied: optional function to handle denial (default throws)\n */\n return async (\n context: FlagContext & { user?: any },\n next: () => void | Promise<void>,\n handleDenied?: () => void\n ) => {\n const allowed = await engine.isEnabled(key, {\n userId: context.user?.id,\n attributes: context.user,\n permissions: context.user?.permissions,\n groups: context.user?.groups ?? context.groups\n })\n\n if (!allowed) {\n if (handleDenied) return handleDenied()\n throw new Error(`Feature \"${key}\" is disabled for this user`)\n }\n\n return next()\n }\n}\n"],"mappings":"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","names":["motion","clsx","jsx","jsxs","uuidv4","jsx","jsxs","jsx","jsxs","jsx","jsxs","motion","clsx","jsx","useEffect","useState","useState","useEffect","jsx"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rps-flagforge",
3
- "version": "1.1.2",
3
+ "version": "1.1.3",
4
4
  "description": "A Multi-Core Feature Flag Package with support for React & NodeJS",
5
5
  "main": "dist/index.cjs.js",
6
6
  "module": "dist/index.esm.js",