UNPKG

@flags-sdk/bucket

Version:

Bucket.co provider for the Flags SDK

1 lines 5.97 kB
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":";AACA;AAAA,EACE;AAAA,OAIK;AAaP,IAAI;AAEJ,SAAS,UAAU,MAAsB;AACvC,QAAM,QAAQ,QAAQ,IAAI,IAAI;AAC9B,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,8BAA8B,IAAI,uBAAuB;AAAA,EAC3E;AACA,SAAO;AACT;AAEO,SAAS,oBACd,eACiB;AACjB,MAAI;AAEJ,iBAAe,aAAa;AAC1B,QAAI,CAAC,cAAc;AACjB,UAAI;AACF,uBAAe,IAAI,aAAa,aAAa;AAAA,MAC/C,SAAS,KAAK;AAEZ,gBAAQ,MAAM,kDAAkD,GAAG;AACnE,cAAM;AAAA,MACR;AAAA,IACF;AAGA,WAAO,aAAa,WAAW;AAAA,EACjC;AAEA,WAAS,iBACP,SAC2B;AAC3B,WAAO;AAAA,MACL,MAAM,OAAO,EAAE,KAAK,SAAS,GAAqB;AAChD,cAAM,WAAW;AAEjB,eAAO,aAAa,WAAW,EAAE,GAAG,SAAS,GAAG,SAAS,GAAG,GAAG,EAC5D;AAAA,MACL;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA,cAAc,YAAY;AACxB,YAAM,WAAW;AACjB,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAEA,SAAS,4BAA4B;AACnC,MAAI,CAAC,sBAAsB;AACzB,UAAM,YAAY,UAAU,mBAAmB;AAE/C,2BAAuB,oBAAoB,EAAE,UAAU,CAAC;AAAA,EAC1D;AAEA,SAAO;AACT;AAuBO,IAAM,gBAAiC;AAAA,EAC5C,kBAAkB,IAAI,SACpB,0BAA0B,EAAE,iBAAiB,GAAG,IAAI;AAAA,EACtD,cAAc,YAAY;AACxB,WAAO,0BAA0B,EAAE,aAAa;AAAA,EAClD;AACF;AAuBA,eAAsB,gBAAgB;AAAA,EACpC;AACF,IAKI,CAAC,GAA0B;AAC7B,MAAI,CAAC,cAAc;AACjB,mBAAe,MAAM,0BAA0B,EAAE,aAAa;AAAA,EAChE;AAEA,QAAM,WAAW,MAAM,aAAa,sBAAsB;AAE1D,SAAO;AAAA,IACL,aAAa,SAAS,OAA4B,CAAC,KAAK,SAAS;AAC/D,UAAI,KAAK,GAAG,IAAI;AAAA,QACd,SAAS;AAAA,UACP,EAAE,OAAO,YAAY,OAAO,MAAM;AAAA,UAClC,EAAE,OAAO,WAAW,OAAO,KAAK;AAAA,QAClC;AAAA,QACA,aAAa,KAAK,eAAe;AAAA,MACnC;AACA,aAAO;AAAA,IACT,GAAG,CAAC,CAAC;AAAA,IACL,OAAO,CAAC;AAAA,EACV;AACF","sourcesContent":["import type { Adapter, FlagDefinitionsType } from 'flags';\nimport {\n BucketClient,\n ClientOptions,\n Context,\n ContextWithTracking,\n} from '@bucketco/node-sdk';\nimport { ProviderData } from 'flags';\n\nexport type { Context };\n\ntype AdapterOptions = Pick<ContextWithTracking, 'enableTracking' | 'meta'>;\n\ntype AdapterResponse = {\n featureIsEnabled: (options?: AdapterOptions) => Adapter<boolean, Context>;\n /** The Bucket client instance used by the adapter. */\n bucketClient: () => Promise<BucketClient>;\n};\n\nlet defaultBucketAdapter: ReturnType<typeof createBucketAdapter> | undefined;\n\nfunction assertEnv(name: string): string {\n const value = process.env[name];\n if (!value) {\n throw new Error(`@flags-sdk/bucket: Missing ${name} environment variable`);\n }\n return value;\n}\n\nexport function createBucketAdapter(\n clientOptions: ClientOptions,\n): AdapterResponse {\n let bucketClient: BucketClient;\n\n async function initialize() {\n if (!bucketClient) {\n try {\n bucketClient = new BucketClient(clientOptions);\n } catch (err) {\n // explicitly log out the error, otherwise it's swallowed\n console.error('@flags-sdk/bucket: Error creating bucketClient', err);\n throw err;\n }\n }\n\n // this can be called multiple times. Same promise is returned.\n return bucketClient.initialize();\n }\n\n function featureIsEnabled(\n options?: AdapterOptions,\n ): Adapter<boolean, Context> {\n return {\n async decide({ key, entities }): Promise<boolean> {\n await initialize();\n\n return bucketClient.getFeature({ ...options, ...entities }, key)\n .isEnabled;\n },\n };\n }\n\n return {\n featureIsEnabled,\n bucketClient: async () => {\n await initialize();\n return bucketClient;\n },\n };\n}\n\nfunction getOrCreateDefaultAdapter() {\n if (!defaultBucketAdapter) {\n const secretKey = assertEnv('BUCKET_SECRET_KEY');\n\n defaultBucketAdapter = createBucketAdapter({ secretKey });\n }\n\n return defaultBucketAdapter;\n}\n\n/**\n * The default Bucket adapter.\n *\n * This is a convenience object that pre-initializes the Bucket SDK and provides\n * the adapter function for usage with the Flags SDK.\n *\n * This is the recommended way to use the Bucket adapter.\n *\n * ```ts\n * // flags.ts\n * import { flag } from 'flags/next';\n * import { bucketAdapter, type Context } from '@flags-sdk/bucket';\n *\n * const flag = flag<boolean, Context>({\n * key: 'my-flag',\n * defaultValue: false,\n * identify: () => ({ key: \"user-123\" }),\n * adapter: bucketAdapter.featureIsEnabled(),\n * });\n * ```\n */\nexport const bucketAdapter: AdapterResponse = {\n featureIsEnabled: (...args) =>\n getOrCreateDefaultAdapter().featureIsEnabled(...args),\n bucketClient: async () => {\n return getOrCreateDefaultAdapter().bucketClient();\n },\n};\n\n/**\n * Get the provider data for the Bucket adapter.\n *\n * This function is used the the [Flags API endpoint](https://vercel.com/docs/workflow-collaboration/feature-flags/implement-flags-in-toolbar#creating-the-flags-api-endpoint) to load and emit your Bucket data.\n *\n * ```ts\n * // .well-known/vercel/flags/route.ts\n * import { NextResponse, type NextRequest } from 'next/server';\n * import { verifyAccess, type ApiData } from 'flags';\n * import { bucketAdapter, getProviderData } from '@flags-sdk/bucket';\n *\n * export async function GET(request: NextRequest) {\n * const access = await verifyAccess(request.headers.get('Authorization'));\n * if (!access) return NextResponse.json(null, { status: 401 });\n *\n * return NextResponse.json<ApiData>(\n * await getProviderData({ bucketClient: await bucketAdapter.bucketClient() }),\n * );\n * }\n * ```\n */\nexport async function getProviderData({\n bucketClient,\n}: {\n /**\n * The BucketClient instance.\n */\n bucketClient?: BucketClient;\n} = {}): Promise<ProviderData> {\n if (!bucketClient) {\n bucketClient = await getOrCreateDefaultAdapter().bucketClient();\n }\n\n const features = await bucketClient.getFeatureDefinitions();\n\n return {\n definitions: features.reduce<FlagDefinitionsType>((acc, item) => {\n acc[item.key] = {\n options: [\n { label: 'Disabled', value: false },\n { label: 'Enabled', value: true },\n ],\n description: item.description ?? undefined,\n };\n return acc;\n }, {}),\n hints: [],\n };\n}\n"]}