chub-dev 0.1.0 → 0.1.2-beta.0

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.
Files changed (139) hide show
  1. package/README.md +55 -0
  2. package/bin/chub-mcp +2 -0
  3. package/dist/airtable/docs/database/javascript/DOC.md +1437 -0
  4. package/dist/airtable/docs/database/python/DOC.md +1735 -0
  5. package/dist/amplitude/docs/analytics/javascript/DOC.md +1282 -0
  6. package/dist/amplitude/docs/analytics/python/DOC.md +1199 -0
  7. package/dist/anthropic/docs/claude-api/javascript/DOC.md +503 -0
  8. package/dist/anthropic/docs/claude-api/python/DOC.md +389 -0
  9. package/dist/asana/docs/tasks/DOC.md +1396 -0
  10. package/dist/assemblyai/docs/transcription/DOC.md +1043 -0
  11. package/dist/atlassian/docs/confluence/javascript/DOC.md +1347 -0
  12. package/dist/atlassian/docs/confluence/python/DOC.md +1604 -0
  13. package/dist/auth0/docs/identity/javascript/DOC.md +968 -0
  14. package/dist/auth0/docs/identity/python/DOC.md +1199 -0
  15. package/dist/aws/docs/s3/javascript/DOC.md +1773 -0
  16. package/dist/aws/docs/s3/python/DOC.md +1807 -0
  17. package/dist/binance/docs/trading/javascript/DOC.md +1315 -0
  18. package/dist/binance/docs/trading/python/DOC.md +1454 -0
  19. package/dist/braintree/docs/gateway/javascript/DOC.md +1278 -0
  20. package/dist/braintree/docs/gateway/python/DOC.md +1179 -0
  21. package/dist/chromadb/docs/embeddings-db/javascript/DOC.md +1263 -0
  22. package/dist/chromadb/docs/embeddings-db/python/DOC.md +1707 -0
  23. package/dist/clerk/docs/auth/javascript/DOC.md +1220 -0
  24. package/dist/clerk/docs/auth/python/DOC.md +274 -0
  25. package/dist/cloudflare/docs/workers/javascript/DOC.md +918 -0
  26. package/dist/cloudflare/docs/workers/python/DOC.md +994 -0
  27. package/dist/cockroachdb/docs/distributed-db/DOC.md +1500 -0
  28. package/dist/cohere/docs/llm/DOC.md +1335 -0
  29. package/dist/datadog/docs/monitoring/javascript/DOC.md +1740 -0
  30. package/dist/datadog/docs/monitoring/python/DOC.md +1815 -0
  31. package/dist/deepgram/docs/speech/javascript/DOC.md +885 -0
  32. package/dist/deepgram/docs/speech/python/DOC.md +685 -0
  33. package/dist/deepl/docs/translation/javascript/DOC.md +887 -0
  34. package/dist/deepl/docs/translation/python/DOC.md +944 -0
  35. package/dist/deepseek/docs/llm/DOC.md +1220 -0
  36. package/dist/directus/docs/headless-cms/javascript/DOC.md +1128 -0
  37. package/dist/directus/docs/headless-cms/python/DOC.md +1276 -0
  38. package/dist/discord/docs/bot/javascript/DOC.md +1090 -0
  39. package/dist/discord/docs/bot/python/DOC.md +1130 -0
  40. package/dist/elasticsearch/docs/search/DOC.md +1634 -0
  41. package/dist/elevenlabs/docs/text-to-speech/javascript/DOC.md +336 -0
  42. package/dist/elevenlabs/docs/text-to-speech/python/DOC.md +552 -0
  43. package/dist/firebase/docs/auth/DOC.md +1015 -0
  44. package/dist/gemini/docs/genai/javascript/DOC.md +691 -0
  45. package/dist/gemini/docs/genai/python/DOC.md +555 -0
  46. package/dist/github/docs/octokit/DOC.md +1560 -0
  47. package/dist/google/docs/bigquery/javascript/DOC.md +1688 -0
  48. package/dist/google/docs/bigquery/python/DOC.md +1503 -0
  49. package/dist/hubspot/docs/crm/javascript/DOC.md +1805 -0
  50. package/dist/hubspot/docs/crm/python/DOC.md +2033 -0
  51. package/dist/huggingface/docs/transformers/DOC.md +948 -0
  52. package/dist/intercom/docs/messaging/javascript/DOC.md +1844 -0
  53. package/dist/intercom/docs/messaging/python/DOC.md +1797 -0
  54. package/dist/jira/docs/issues/javascript/DOC.md +1420 -0
  55. package/dist/jira/docs/issues/python/DOC.md +1492 -0
  56. package/dist/kafka/docs/streaming/javascript/DOC.md +1671 -0
  57. package/dist/kafka/docs/streaming/python/DOC.md +1464 -0
  58. package/dist/landingai-ade/docs/api/DOC.md +620 -0
  59. package/dist/landingai-ade/docs/sdk/python/DOC.md +489 -0
  60. package/dist/landingai-ade/docs/sdk/typescript/DOC.md +542 -0
  61. package/dist/landingai-ade/skills/SKILL.md +489 -0
  62. package/dist/launchdarkly/docs/feature-flags/javascript/DOC.md +1191 -0
  63. package/dist/launchdarkly/docs/feature-flags/python/DOC.md +1671 -0
  64. package/dist/linear/docs/tracker/DOC.md +1554 -0
  65. package/dist/livekit/docs/realtime/javascript/DOC.md +303 -0
  66. package/dist/livekit/docs/realtime/python/DOC.md +163 -0
  67. package/dist/mailchimp/docs/marketing/DOC.md +1420 -0
  68. package/dist/meilisearch/docs/search/DOC.md +1241 -0
  69. package/dist/microsoft/docs/onedrive/javascript/DOC.md +1421 -0
  70. package/dist/microsoft/docs/onedrive/python/DOC.md +1549 -0
  71. package/dist/mongodb/docs/atlas/DOC.md +2041 -0
  72. package/dist/notion/docs/workspace-api/javascript/DOC.md +1435 -0
  73. package/dist/notion/docs/workspace-api/python/DOC.md +1400 -0
  74. package/dist/okta/docs/identity/javascript/DOC.md +1171 -0
  75. package/dist/okta/docs/identity/python/DOC.md +1401 -0
  76. package/dist/openai/docs/chat/javascript/DOC.md +407 -0
  77. package/dist/openai/docs/chat/python/DOC.md +568 -0
  78. package/dist/paypal/docs/checkout/DOC.md +278 -0
  79. package/dist/pinecone/docs/sdk/javascript/DOC.md +984 -0
  80. package/dist/pinecone/docs/sdk/python/DOC.md +1395 -0
  81. package/dist/plaid/docs/banking/javascript/DOC.md +1163 -0
  82. package/dist/plaid/docs/banking/python/DOC.md +1203 -0
  83. package/dist/playwright-community/skills/login-flows/SKILL.md +108 -0
  84. package/dist/postmark/docs/transactional-email/DOC.md +1168 -0
  85. package/dist/prisma/docs/orm/javascript/DOC.md +1419 -0
  86. package/dist/prisma/docs/orm/python/DOC.md +1317 -0
  87. package/dist/qdrant/docs/vector-search/javascript/DOC.md +1221 -0
  88. package/dist/qdrant/docs/vector-search/python/DOC.md +1653 -0
  89. package/dist/rabbitmq/docs/message-queue/javascript/DOC.md +1193 -0
  90. package/dist/rabbitmq/docs/message-queue/python/DOC.md +1243 -0
  91. package/dist/razorpay/docs/payments/javascript/DOC.md +1219 -0
  92. package/dist/razorpay/docs/payments/python/DOC.md +1330 -0
  93. package/dist/redis/docs/key-value/javascript/DOC.md +1851 -0
  94. package/dist/redis/docs/key-value/python/DOC.md +2054 -0
  95. package/dist/registry.json +2817 -0
  96. package/dist/replicate/docs/model-hosting/DOC.md +1318 -0
  97. package/dist/resend/docs/email/DOC.md +1271 -0
  98. package/dist/salesforce/docs/crm/javascript/DOC.md +1241 -0
  99. package/dist/salesforce/docs/crm/python/DOC.md +1183 -0
  100. package/dist/search-index.json +1 -0
  101. package/dist/sendgrid/docs/email-api/javascript/DOC.md +371 -0
  102. package/dist/sendgrid/docs/email-api/python/DOC.md +656 -0
  103. package/dist/sentry/docs/error-tracking/javascript/DOC.md +1073 -0
  104. package/dist/sentry/docs/error-tracking/python/DOC.md +1309 -0
  105. package/dist/shopify/docs/storefront/DOC.md +457 -0
  106. package/dist/slack/docs/workspace/javascript/DOC.md +933 -0
  107. package/dist/slack/docs/workspace/python/DOC.md +271 -0
  108. package/dist/square/docs/payments/javascript/DOC.md +1855 -0
  109. package/dist/square/docs/payments/python/DOC.md +1728 -0
  110. package/dist/stripe/docs/api/DOC.md +1727 -0
  111. package/dist/stripe/docs/payments/DOC.md +1726 -0
  112. package/dist/stytch/docs/auth/javascript/DOC.md +1813 -0
  113. package/dist/stytch/docs/auth/python/DOC.md +1962 -0
  114. package/dist/supabase/docs/client/DOC.md +1606 -0
  115. package/dist/twilio/docs/messaging/python/DOC.md +469 -0
  116. package/dist/twilio/docs/messaging/typescript/DOC.md +946 -0
  117. package/dist/vercel/docs/platform/DOC.md +1940 -0
  118. package/dist/weaviate/docs/vector-db/javascript/DOC.md +1268 -0
  119. package/dist/weaviate/docs/vector-db/python/DOC.md +1388 -0
  120. package/dist/zendesk/docs/support/javascript/DOC.md +2150 -0
  121. package/dist/zendesk/docs/support/python/DOC.md +2297 -0
  122. package/package.json +22 -6
  123. package/skills/get-api-docs/SKILL.md +84 -0
  124. package/src/commands/annotate.js +83 -0
  125. package/src/commands/build.js +12 -1
  126. package/src/commands/feedback.js +150 -0
  127. package/src/commands/get.js +83 -42
  128. package/src/commands/search.js +7 -0
  129. package/src/index.js +43 -17
  130. package/src/lib/analytics.js +90 -0
  131. package/src/lib/annotations.js +57 -0
  132. package/src/lib/bm25.js +170 -0
  133. package/src/lib/cache.js +69 -6
  134. package/src/lib/config.js +8 -3
  135. package/src/lib/identity.js +99 -0
  136. package/src/lib/registry.js +103 -20
  137. package/src/lib/telemetry.js +86 -0
  138. package/src/mcp/server.js +177 -0
  139. package/src/mcp/tools.js +251 -0
@@ -0,0 +1,1191 @@
1
+ ---
2
+ name: feature-flags
3
+ description: "LaunchDarkly Node.js Server SDK for feature flag management and experimentation"
4
+ metadata:
5
+ languages: "javascript"
6
+ versions: "9.10.2"
7
+ updated-on: "2026-03-02"
8
+ source: maintainer
9
+ tags: "launchdarkly,feature-flags,toggles,experimentation,rollout"
10
+ ---
11
+
12
+ # LaunchDarkly Node.js Server SDK
13
+
14
+ ## Golden Rule
15
+
16
+ **Always use `@launchdarkly/node-server-sdk` for server-side Node.js applications.**
17
+
18
+ ```bash
19
+ npm install @launchdarkly/node-server-sdk
20
+ ```
21
+
22
+ Do NOT use:
23
+ - `launchdarkly-node-server-sdk` (deprecated, last updated 2021)
24
+ - `launchdarkly-node-client-sdk` (client-side only, different use case)
25
+ - Any unofficial or third-party LaunchDarkly packages
26
+
27
+ The official package is `@launchdarkly/node-server-sdk` maintained by LaunchDarkly.
28
+
29
+ ---
30
+
31
+ ## Installation
32
+
33
+ ```bash
34
+ npm install @launchdarkly/node-server-sdk
35
+ ```
36
+
37
+ Optional observability plugin (requires v9.10+):
38
+
39
+ ```bash
40
+ npm install @launchdarkly/observability-node
41
+ ```
42
+
43
+ ---
44
+
45
+ ## Environment Variables
46
+
47
+ Set your SDK key as an environment variable:
48
+
49
+ ```bash
50
+ export LAUNCHDARKLY_SDK_KEY="sdk-key-123abc"
51
+ ```
52
+
53
+ ---
54
+
55
+ ## Initialization
56
+
57
+ ### Basic Initialization
58
+
59
+ ```javascript
60
+ import { init } from '@launchdarkly/node-server-sdk';
61
+
62
+ const client = init(process.env.LAUNCHDARKLY_SDK_KEY);
63
+
64
+ // Wait for initialization before evaluating flags
65
+ await client.waitForInitialization({ timeout: 10 });
66
+ ```
67
+
68
+ ### With Configuration Options
69
+
70
+ ```javascript
71
+ import { init } from '@launchdarkly/node-server-sdk';
72
+
73
+ const options = {
74
+ timeout: 10,
75
+ capacity: 1000,
76
+ flushInterval: 30,
77
+ stream: true,
78
+ allAttributesPrivate: false,
79
+ privateAttributes: ['email', 'ssn'],
80
+ offline: false,
81
+ diagnosticOptOut: false,
82
+ wrapperName: 'my-wrapper',
83
+ wrapperVersion: '1.0.0'
84
+ };
85
+
86
+ const client = init(process.env.LAUNCHDARKLY_SDK_KEY, options);
87
+ await client.waitForInitialization({ timeout: 10 });
88
+ ```
89
+
90
+ ### With Observability Plugin
91
+
92
+ ```javascript
93
+ import { init } from '@launchdarkly/node-server-sdk';
94
+ import { Observability } from '@launchdarkly/observability-node';
95
+
96
+ const client = init(process.env.LAUNCHDARKLY_SDK_KEY, {
97
+ plugins: [new Observability()]
98
+ });
99
+
100
+ await client.waitForInitialization({ timeout: 10 });
101
+ ```
102
+
103
+ ### Singleton Pattern
104
+
105
+ **Critical:** Make the LDClient a singleton. Do NOT create multiple instances per request.
106
+
107
+ ```javascript
108
+ // app.js or server initialization
109
+ import { init } from '@launchdarkly/node-server-sdk';
110
+
111
+ let ldClient;
112
+
113
+ async function initializeLaunchDarkly() {
114
+ ldClient = init(process.env.LAUNCHDARKLY_SDK_KEY);
115
+ await ldClient.waitForInitialization({ timeout: 10 });
116
+ return ldClient;
117
+ }
118
+
119
+ export function getLDClient() {
120
+ if (!ldClient) {
121
+ throw new Error('LaunchDarkly client not initialized');
122
+ }
123
+ return ldClient;
124
+ }
125
+
126
+ // Initialize once at app startup
127
+ await initializeLaunchDarkly();
128
+ ```
129
+
130
+ ### Error Handling During Initialization
131
+
132
+ ```javascript
133
+ import { init } from '@launchdarkly/node-server-sdk';
134
+
135
+ const client = init(process.env.LAUNCHDARKLY_SDK_KEY);
136
+
137
+ client.once('ready', () => {
138
+ console.log('LaunchDarkly client initialized successfully');
139
+ });
140
+
141
+ client.once('failed', () => {
142
+ console.error('LaunchDarkly client failed to initialize');
143
+ });
144
+
145
+ try {
146
+ await client.waitForInitialization({ timeout: 10 });
147
+ console.log('Client ready');
148
+ } catch (error) {
149
+ console.error('Initialization timeout:', error);
150
+ // Client will still work but may return default values
151
+ }
152
+ ```
153
+
154
+ ---
155
+
156
+ ## Contexts
157
+
158
+ ### Simple User Context
159
+
160
+ ```javascript
161
+ const context = {
162
+ kind: 'user',
163
+ key: 'user-key-123abc',
164
+ name: 'Sandy Smith',
165
+ email: 'sandy@example.com'
166
+ };
167
+ ```
168
+
169
+ ### Context with Custom Attributes
170
+
171
+ ```javascript
172
+ const context = {
173
+ kind: 'user',
174
+ key: 'user-key-123abc',
175
+ name: 'Sandy Smith',
176
+ email: 'sandy@example.com',
177
+ plan: 'premium',
178
+ betaTester: true,
179
+ customAttribute: 'custom-value'
180
+ };
181
+ ```
182
+
183
+ ### Anonymous Context
184
+
185
+ ```javascript
186
+ const context = {
187
+ kind: 'user',
188
+ key: 'anonymous-user-123',
189
+ anonymous: true
190
+ };
191
+ ```
192
+
193
+ ### Multi-Context (Multiple Kinds)
194
+
195
+ ```javascript
196
+ const multiContext = {
197
+ kind: 'multi',
198
+ user: {
199
+ key: 'user-key-123',
200
+ name: 'Sandy'
201
+ },
202
+ organization: {
203
+ key: 'org-key-456',
204
+ name: 'Acme Corp'
205
+ },
206
+ device: {
207
+ key: 'device-key-789',
208
+ platform: 'iOS'
209
+ }
210
+ };
211
+ ```
212
+
213
+ ### Private Attributes
214
+
215
+ Mark specific attributes as private (not sent to LaunchDarkly):
216
+
217
+ ```javascript
218
+ const context = {
219
+ kind: 'user',
220
+ key: 'user-key-123abc',
221
+ name: 'Sandy Smith',
222
+ email: 'sandy@example.com',
223
+ ssn: '123-45-6789',
224
+ _meta: {
225
+ privateAttributes: ['email', 'ssn']
226
+ }
227
+ };
228
+ ```
229
+
230
+ ---
231
+
232
+ ## Flag Evaluation
233
+
234
+ ### Boolean Flag
235
+
236
+ ```javascript
237
+ const showFeature = await client.variation('flag-key-123abc', context, false);
238
+
239
+ if (showFeature) {
240
+ // Feature enabled
241
+ } else {
242
+ // Feature disabled
243
+ }
244
+ ```
245
+
246
+ ### String Flag
247
+
248
+ ```javascript
249
+ const theme = await client.variation('theme-flag', context, 'light');
250
+
251
+ console.log(`User theme: ${theme}`); // 'dark' or 'light'
252
+ ```
253
+
254
+ ### Number Flag
255
+
256
+ ```javascript
257
+ const maxItems = await client.variation('max-items', context, 10);
258
+
259
+ console.log(`Max items: ${maxItems}`); // e.g., 50
260
+ ```
261
+
262
+ ### JSON Flag
263
+
264
+ ```javascript
265
+ const config = await client.variation('config-flag', context, {
266
+ timeout: 30,
267
+ retries: 3
268
+ });
269
+
270
+ console.log(`Timeout: ${config.timeout}, Retries: ${config.retries}`);
271
+ ```
272
+
273
+ ### Callback Style (Legacy)
274
+
275
+ ```javascript
276
+ client.variation('flag-key-123abc', context, false, (err, showFeature) => {
277
+ if (err) {
278
+ console.error('Error evaluating flag:', err);
279
+ return;
280
+ }
281
+
282
+ if (showFeature) {
283
+ // Feature enabled
284
+ }
285
+ });
286
+ ```
287
+
288
+ ### Synchronous Variation (After Initialization)
289
+
290
+ ```javascript
291
+ // Only use after client is fully initialized
292
+ const showFeature = client.variation('flag-key-123abc', context, false);
293
+ ```
294
+
295
+ ---
296
+
297
+ ## Flag Evaluation with Details
298
+
299
+ ### Get Evaluation Reason
300
+
301
+ ```javascript
302
+ const result = await client.variationDetail('flag-key-123abc', context, false);
303
+
304
+ console.log('Value:', result.value);
305
+ console.log('Variation Index:', result.variationIndex);
306
+ console.log('Reason:', result.reason);
307
+ ```
308
+
309
+ ### Reason Object Structure
310
+
311
+ ```javascript
312
+ const result = await client.variationDetail('flag-key-123abc', context, false);
313
+
314
+ // result.reason can be:
315
+ // { kind: 'OFF' }
316
+ // { kind: 'FALLTHROUGH' }
317
+ // { kind: 'TARGET_MATCH' }
318
+ // { kind: 'RULE_MATCH', ruleIndex: 0, ruleId: 'rule-id' }
319
+ // { kind: 'PREREQUISITE_FAILED', prerequisiteKey: 'other-flag' }
320
+ // { kind: 'ERROR', errorKind: 'MALFORMED_FLAG' }
321
+ ```
322
+
323
+ ### Using Evaluation Details for Debugging
324
+
325
+ ```javascript
326
+ const result = await client.variationDetail('experiment-flag', context, 'control');
327
+
328
+ if (result.reason.kind === 'ERROR') {
329
+ console.error('Flag evaluation error:', result.reason.errorKind);
330
+ } else if (result.reason.kind === 'RULE_MATCH') {
331
+ console.log('Matched rule:', result.reason.ruleIndex);
332
+ } else if (result.reason.kind === 'FALLTHROUGH') {
333
+ console.log('Using fallthrough variation');
334
+ }
335
+
336
+ console.log('Serving variation:', result.value);
337
+ ```
338
+
339
+ ---
340
+
341
+ ## All Flags State
342
+
343
+ ### Get All Flags for a Context
344
+
345
+ ```javascript
346
+ const state = await client.allFlagsState(context);
347
+
348
+ const allFlags = state.allValues();
349
+ console.log('All flags:', allFlags);
350
+ // { 'flag-1': true, 'flag-2': 'value', 'flag-3': 42 }
351
+ ```
352
+
353
+ ### For Client-Side Bootstrapping
354
+
355
+ ```javascript
356
+ const state = await client.allFlagsState(context, {
357
+ clientSideOnly: true,
358
+ withReasons: true,
359
+ detailsOnlyForTrackedFlags: false
360
+ });
361
+
362
+ const bootstrapData = state.toJSON();
363
+
364
+ // Send to client-side
365
+ res.json({
366
+ flags: bootstrapData
367
+ });
368
+ ```
369
+
370
+ ### Check If State Is Valid
371
+
372
+ ```javascript
373
+ const state = await client.allFlagsState(context);
374
+
375
+ if (state.valid) {
376
+ console.log('Successfully retrieved all flags');
377
+ const flags = state.allValues();
378
+ } else {
379
+ console.error('Failed to retrieve flags');
380
+ }
381
+ ```
382
+
383
+ ---
384
+
385
+ ## Event Tracking
386
+
387
+ ### Track Custom Event
388
+
389
+ ```javascript
390
+ client.track('button-clicked', context);
391
+ ```
392
+
393
+ ### Track Event with Data
394
+
395
+ ```javascript
396
+ client.track('purchase-completed', context, {
397
+ itemId: 'item-123',
398
+ price: 29.99,
399
+ currency: 'USD'
400
+ });
401
+ ```
402
+
403
+ ### Track Event with Numeric Metric
404
+
405
+ ```javascript
406
+ client.track('purchase-completed', context, {
407
+ itemId: 'item-123'
408
+ }, 29.99);
409
+ ```
410
+
411
+ ### Identify Context
412
+
413
+ Send context attributes to LaunchDarkly for targeting:
414
+
415
+ ```javascript
416
+ client.identify(context);
417
+ ```
418
+
419
+ ### Flush Events
420
+
421
+ Force immediate delivery of pending events:
422
+
423
+ ```javascript
424
+ await client.flush();
425
+ ```
426
+
427
+ ### Auto-Flush on Shutdown
428
+
429
+ ```javascript
430
+ process.on('SIGTERM', async () => {
431
+ await client.flush();
432
+ await client.close();
433
+ process.exit(0);
434
+ });
435
+ ```
436
+
437
+ ---
438
+
439
+ ## Data Modes
440
+
441
+ ### Streaming Mode (Default)
442
+
443
+ ```javascript
444
+ const client = init(process.env.LAUNCHDARKLY_SDK_KEY, {
445
+ stream: true
446
+ });
447
+ ```
448
+
449
+ ### Polling Mode
450
+
451
+ ```javascript
452
+ const client = init(process.env.LAUNCHDARKLY_SDK_KEY, {
453
+ stream: false,
454
+ pollInterval: 60 // seconds
455
+ });
456
+ ```
457
+
458
+ ### Offline Mode
459
+
460
+ ```javascript
461
+ const client = init(process.env.LAUNCHDARKLY_SDK_KEY, {
462
+ offline: true
463
+ });
464
+
465
+ // All flags return default values
466
+ const result = await client.variation('flag-key', context, false);
467
+ // Will always return false (the default)
468
+ ```
469
+
470
+ ---
471
+
472
+ ## Relay Proxy
473
+
474
+ ### Using LaunchDarkly Relay Proxy
475
+
476
+ ```javascript
477
+ const client = init(process.env.LAUNCHDARKLY_SDK_KEY, {
478
+ baseUri: 'http://relay-proxy.example.com',
479
+ streamUri: 'http://relay-proxy.example.com',
480
+ eventsUri: 'http://relay-proxy.example.com'
481
+ });
482
+ ```
483
+
484
+ ### Daemon Mode
485
+
486
+ ```javascript
487
+ const client = init(process.env.LAUNCHDARKLY_SDK_KEY, {
488
+ useLdd: true,
489
+ featureStore: myFeatureStore
490
+ });
491
+ ```
492
+
493
+ ---
494
+
495
+ ## Feature Stores
496
+
497
+ ### Redis Feature Store
498
+
499
+ ```javascript
500
+ import { init } from '@launchdarkly/node-server-sdk';
501
+ import { RedisFeatureStore } from '@launchdarkly/node-server-sdk-redis';
502
+
503
+ const redisStore = RedisFeatureStore({
504
+ redisOpts: {
505
+ host: 'localhost',
506
+ port: 6379
507
+ },
508
+ prefix: 'ld',
509
+ cacheTTL: 30
510
+ });
511
+
512
+ const client = init(process.env.LAUNCHDARKLY_SDK_KEY, {
513
+ featureStore: redisStore
514
+ });
515
+ ```
516
+
517
+ ### DynamoDB Feature Store
518
+
519
+ ```javascript
520
+ import { DynamoDBFeatureStore } from '@launchdarkly/node-server-sdk-dynamodb';
521
+
522
+ const dynamoStore = DynamoDBFeatureStore('feature-flags-table', {
523
+ cacheTTL: 30
524
+ });
525
+
526
+ const client = init(process.env.LAUNCHDARKLY_SDK_KEY, {
527
+ featureStore: dynamoStore
528
+ });
529
+ ```
530
+
531
+ ---
532
+
533
+ ## Bootstrapping
534
+
535
+ ### File-Based Data Source
536
+
537
+ ```javascript
538
+ import { init } from '@launchdarkly/node-server-sdk';
539
+ import { FileDataSource } from '@launchdarkly/node-server-sdk-file';
540
+
541
+ const fileSource = FileDataSource({
542
+ paths: ['./flags.json']
543
+ });
544
+
545
+ const client = init(process.env.LAUNCHDARKLY_SDK_KEY, {
546
+ updateProcessor: fileSource
547
+ });
548
+ ```
549
+
550
+ ### flags.json Format
551
+
552
+ ```json
553
+ {
554
+ "flags": {
555
+ "flag-key-123abc": {
556
+ "key": "flag-key-123abc",
557
+ "on": true,
558
+ "variations": [true, false],
559
+ "fallthrough": {
560
+ "variation": 0
561
+ }
562
+ }
563
+ },
564
+ "segments": {}
565
+ }
566
+ ```
567
+
568
+ ---
569
+
570
+ ## Subscribing to Flag Changes
571
+
572
+ ### Listen for Specific Flag Changes
573
+
574
+ ```javascript
575
+ client.on('update:flag-key-123abc', (newValue, oldValue) => {
576
+ console.log(`Flag changed from ${oldValue} to ${newValue}`);
577
+ });
578
+ ```
579
+
580
+ ### Listen for All Flag Updates
581
+
582
+ ```javascript
583
+ client.on('update', (settings) => {
584
+ console.log('Flags updated');
585
+ });
586
+ ```
587
+
588
+ ---
589
+
590
+ ## Big Segments
591
+
592
+ ### Configure Big Segments Store
593
+
594
+ ```javascript
595
+ import { init } from '@launchdarkly/node-server-sdk';
596
+ import { RedisBigSegmentStore } from '@launchdarkly/node-server-sdk-redis';
597
+
598
+ const bigSegmentStore = RedisBigSegmentStore({
599
+ redisOpts: {
600
+ host: 'localhost',
601
+ port: 6379
602
+ },
603
+ prefix: 'big-segments'
604
+ });
605
+
606
+ const client = init(process.env.LAUNCHDARKLY_SDK_KEY, {
607
+ bigSegments: {
608
+ store: bigSegmentStore,
609
+ contextCacheSize: 1000,
610
+ contextCacheTime: 5,
611
+ statusPollInterval: 5,
612
+ staleAfter: 120
613
+ }
614
+ });
615
+ ```
616
+
617
+ ### Check Big Segments Status
618
+
619
+ ```javascript
620
+ const result = await client.variationDetail('segment-flag', context, false);
621
+
622
+ if (result.reason.bigSegmentsStatus === 'STALE') {
623
+ console.warn('Big segments data is stale');
624
+ }
625
+ ```
626
+
627
+ ---
628
+
629
+ ## Hooks
630
+
631
+ ### Create a Hook
632
+
633
+ ```javascript
634
+ class LoggingHook {
635
+ getMetadata() {
636
+ return { name: 'logging-hook' };
637
+ }
638
+
639
+ beforeEvaluation(hookContext, data) {
640
+ console.log('Evaluating flag:', hookContext.flagKey);
641
+ return data;
642
+ }
643
+
644
+ afterEvaluation(hookContext, data, detail) {
645
+ console.log('Flag result:', detail.value);
646
+ return data;
647
+ }
648
+ }
649
+ ```
650
+
651
+ ### Register Hook via Configuration
652
+
653
+ ```javascript
654
+ const client = init(process.env.LAUNCHDARKLY_SDK_KEY, {
655
+ hooks: [new LoggingHook()]
656
+ });
657
+ ```
658
+
659
+ ### Add Hook at Runtime
660
+
661
+ ```javascript
662
+ const hook = new LoggingHook();
663
+ client.addHook(hook);
664
+ ```
665
+
666
+ ---
667
+
668
+ ## Migrations
669
+
670
+ ### Create Migration
671
+
672
+ ```javascript
673
+ import { createMigration } from '@launchdarkly/node-server-sdk';
674
+
675
+ const migration = createMigration(client, {
676
+ execution: 'parallel',
677
+ latencyTracking: true,
678
+ errorTracking: true,
679
+
680
+ readOld: async (payload) => {
681
+ return await oldDatabase.read(payload.id);
682
+ },
683
+
684
+ readNew: async (payload) => {
685
+ return await newDatabase.read(payload.id);
686
+ },
687
+
688
+ writeOld: async (payload) => {
689
+ await oldDatabase.write(payload.id, payload.data);
690
+ },
691
+
692
+ writeNew: async (payload) => {
693
+ await newDatabase.write(payload.id, payload.data);
694
+ },
695
+
696
+ check: (oldValue, newValue) => {
697
+ return JSON.stringify(oldValue) === JSON.stringify(newValue);
698
+ }
699
+ });
700
+ ```
701
+
702
+ ### Execute Migration Read
703
+
704
+ ```javascript
705
+ const stage = await client.variation('migration-flag', context, 'off');
706
+
707
+ const result = await migration.read('migration-flag', context, stage, {
708
+ id: 'record-123'
709
+ });
710
+
711
+ if (result.isSuccessful()) {
712
+ console.log('Data:', result.getValue());
713
+ } else {
714
+ console.error('Migration read failed');
715
+ }
716
+ ```
717
+
718
+ ### Execute Migration Write
719
+
720
+ ```javascript
721
+ const stage = await client.variation('migration-flag', context, 'off');
722
+
723
+ const result = await migration.write('migration-flag', context, stage, {
724
+ id: 'record-123',
725
+ data: { name: 'Example' }
726
+ });
727
+
728
+ if (result.isSuccessful()) {
729
+ console.log('Write completed');
730
+ }
731
+ ```
732
+
733
+ ### Migration Stages
734
+
735
+ - `'off'` - Use old implementation only
736
+ - `'dualwrite'` - Write to both, read from old
737
+ - `'shadow'` - Read from both, write to both, use old for responses
738
+ - `'live'` - Read from both, write to both, use new for responses
739
+ - `'rampdown'` - Write to both, read from new
740
+ - `'complete'` - Use new implementation only
741
+
742
+ ---
743
+
744
+ ## HTTP Configuration
745
+
746
+ ### Configure Proxy
747
+
748
+ ```javascript
749
+ const client = init(process.env.LAUNCHDARKLY_SDK_KEY, {
750
+ proxyOptions: {
751
+ host: 'proxy.example.com',
752
+ port: 8080,
753
+ auth: 'username:password'
754
+ }
755
+ });
756
+ ```
757
+
758
+ ### Custom TLS Options
759
+
760
+ ```javascript
761
+ import * as fs from 'fs';
762
+
763
+ const client = init(process.env.LAUNCHDARKLY_SDK_KEY, {
764
+ tlsOptions: {
765
+ ca: fs.readFileSync('./ca-cert.pem'),
766
+ cert: fs.readFileSync('./client-cert.pem'),
767
+ key: fs.readFileSync('./client-key.pem')
768
+ }
769
+ });
770
+ ```
771
+
772
+ ### Custom HTTP Headers
773
+
774
+ ```javascript
775
+ const client = init(process.env.LAUNCHDARKLY_SDK_KEY, {
776
+ requestHeaderTransform: (headers) => {
777
+ headers['X-Custom-Header'] = 'custom-value';
778
+ return headers;
779
+ }
780
+ });
781
+ ```
782
+
783
+ ---
784
+
785
+ ## Application Metadata
786
+
787
+ ### Set Application Info
788
+
789
+ ```javascript
790
+ const client = init(process.env.LAUNCHDARKLY_SDK_KEY, {
791
+ application: {
792
+ id: 'my-app',
793
+ version: '1.2.3',
794
+ name: 'My Application',
795
+ versionName: 'v1.2.3-beta'
796
+ }
797
+ });
798
+ ```
799
+
800
+ ---
801
+
802
+ ## Secure Mode
803
+
804
+ ### Generate Secure Mode Hash
805
+
806
+ ```javascript
807
+ import { createHmac } from 'crypto';
808
+
809
+ function generateSecureModeHash(sdkKey, contextKey) {
810
+ return createHmac('sha256', sdkKey)
811
+ .update(contextKey)
812
+ .digest('hex');
813
+ }
814
+
815
+ const hash = generateSecureModeHash(
816
+ process.env.LAUNCHDARKLY_SDK_KEY,
817
+ context.key
818
+ );
819
+
820
+ // Send hash to client-side
821
+ res.json({ hash });
822
+ ```
823
+
824
+ ---
825
+
826
+ ## Testing
827
+
828
+ ### Using Test Data Source
829
+
830
+ ```javascript
831
+ import { init, TestData } from '@launchdarkly/node-server-sdk';
832
+
833
+ const td = TestData();
834
+
835
+ td.update(td.flag('flag-key-123abc')
836
+ .variations(true, false)
837
+ .variationForAll(true)
838
+ );
839
+
840
+ const client = init('sdk-key', {
841
+ updateProcessor: td.getFactory()
842
+ });
843
+
844
+ // Update flag during test
845
+ td.update(td.flag('flag-key-123abc').variationForAll(false));
846
+ ```
847
+
848
+ ### Mock Client for Unit Tests
849
+
850
+ ```javascript
851
+ const mockClient = {
852
+ variation: jest.fn().mockResolvedValue(true),
853
+ variationDetail: jest.fn().mockResolvedValue({
854
+ value: true,
855
+ variationIndex: 0,
856
+ reason: { kind: 'OFF' }
857
+ }),
858
+ track: jest.fn(),
859
+ identify: jest.fn(),
860
+ flush: jest.fn().mockResolvedValue(undefined),
861
+ close: jest.fn().mockResolvedValue(undefined)
862
+ };
863
+ ```
864
+
865
+ ---
866
+
867
+ ## Error Handling
868
+
869
+ ### Handle Initialization Errors
870
+
871
+ ```javascript
872
+ const client = init(process.env.LAUNCHDARKLY_SDK_KEY);
873
+
874
+ client.on('error', (error) => {
875
+ console.error('LaunchDarkly error:', error);
876
+ });
877
+
878
+ try {
879
+ await client.waitForInitialization({ timeout: 10 });
880
+ } catch (error) {
881
+ console.error('Initialization timeout');
882
+ // Client will still work with default values
883
+ }
884
+ ```
885
+
886
+ ### Handle Evaluation Errors
887
+
888
+ ```javascript
889
+ const result = await client.variationDetail('flag-key', context, false);
890
+
891
+ if (result.reason.kind === 'ERROR') {
892
+ switch (result.reason.errorKind) {
893
+ case 'MALFORMED_FLAG':
894
+ console.error('Flag configuration is invalid');
895
+ break;
896
+ case 'FLAG_NOT_FOUND':
897
+ console.error('Flag does not exist');
898
+ break;
899
+ case 'USER_NOT_SPECIFIED':
900
+ console.error('Context is invalid');
901
+ break;
902
+ case 'WRONG_TYPE':
903
+ console.error('Flag type mismatch');
904
+ break;
905
+ default:
906
+ console.error('Unknown error:', result.reason.errorKind);
907
+ }
908
+ }
909
+ ```
910
+
911
+ ---
912
+
913
+ ## Logging
914
+
915
+ ### Custom Logger
916
+
917
+ ```javascript
918
+ import { BasicLogger } from '@launchdarkly/node-server-sdk';
919
+
920
+ const logger = BasicLogger.get();
921
+
922
+ const client = init(process.env.LAUNCHDARKLY_SDK_KEY, {
923
+ logger: logger
924
+ });
925
+ ```
926
+
927
+ ### Set Log Level
928
+
929
+ ```javascript
930
+ import { BasicLogger } from '@launchdarkly/node-server-sdk';
931
+
932
+ const logger = BasicLogger.get();
933
+ logger.setLevel('debug'); // 'debug', 'info', 'warn', 'error'
934
+
935
+ const client = init(process.env.LAUNCHDARKLY_SDK_KEY, {
936
+ logger: logger
937
+ });
938
+ ```
939
+
940
+ ### Custom Logger Implementation
941
+
942
+ ```javascript
943
+ class CustomLogger {
944
+ debug(message) {
945
+ console.log('[DEBUG]', message);
946
+ }
947
+
948
+ info(message) {
949
+ console.log('[INFO]', message);
950
+ }
951
+
952
+ warn(message) {
953
+ console.warn('[WARN]', message);
954
+ }
955
+
956
+ error(message) {
957
+ console.error('[ERROR]', message);
958
+ }
959
+ }
960
+
961
+ const client = init(process.env.LAUNCHDARKLY_SDK_KEY, {
962
+ logger: new CustomLogger()
963
+ });
964
+ ```
965
+
966
+ ---
967
+
968
+ ## Complete Express.js Example
969
+
970
+ ```javascript
971
+ import express from 'express';
972
+ import { init } from '@launchdarkly/node-server-sdk';
973
+
974
+ const app = express();
975
+ let ldClient;
976
+
977
+ // Initialize LaunchDarkly client
978
+ async function initLaunchDarkly() {
979
+ ldClient = init(process.env.LAUNCHDARKLY_SDK_KEY, {
980
+ timeout: 10,
981
+ stream: true
982
+ });
983
+
984
+ await ldClient.waitForInitialization({ timeout: 10 });
985
+ console.log('LaunchDarkly initialized');
986
+ }
987
+
988
+ // Middleware to add LD client to request
989
+ app.use((req, res, next) => {
990
+ req.ldClient = ldClient;
991
+ next();
992
+ });
993
+
994
+ // Route using feature flag
995
+ app.get('/feature', async (req, res) => {
996
+ const context = {
997
+ kind: 'user',
998
+ key: req.headers['x-user-id'] || 'anonymous',
999
+ email: req.headers['x-user-email']
1000
+ };
1001
+
1002
+ const showNewFeature = await ldClient.variation(
1003
+ 'new-feature-flag',
1004
+ context,
1005
+ false
1006
+ );
1007
+
1008
+ if (showNewFeature) {
1009
+ res.json({ feature: 'new', message: 'Welcome to the new feature!' });
1010
+ } else {
1011
+ res.json({ feature: 'old', message: 'Using legacy feature' });
1012
+ }
1013
+ });
1014
+
1015
+ // Track custom event
1016
+ app.post('/track-event', async (req, res) => {
1017
+ const context = {
1018
+ kind: 'user',
1019
+ key: req.body.userId
1020
+ };
1021
+
1022
+ ldClient.track('button-clicked', context, {
1023
+ buttonId: req.body.buttonId
1024
+ });
1025
+
1026
+ res.json({ success: true });
1027
+ });
1028
+
1029
+ // Graceful shutdown
1030
+ process.on('SIGTERM', async () => {
1031
+ console.log('Shutting down...');
1032
+ await ldClient.flush();
1033
+ await ldClient.close();
1034
+ process.exit(0);
1035
+ });
1036
+
1037
+ // Start server
1038
+ const PORT = process.env.PORT || 3000;
1039
+
1040
+ initLaunchDarkly()
1041
+ .then(() => {
1042
+ app.listen(PORT, () => {
1043
+ console.log(`Server running on port ${PORT}`);
1044
+ });
1045
+ })
1046
+ .catch((error) => {
1047
+ console.error('Failed to initialize LaunchDarkly:', error);
1048
+ process.exit(1);
1049
+ });
1050
+ ```
1051
+
1052
+ ---
1053
+
1054
+ ## TypeScript Support
1055
+
1056
+ ### Type Definitions
1057
+
1058
+ ```typescript
1059
+ import {
1060
+ init,
1061
+ LDClient,
1062
+ LDContext,
1063
+ LDOptions,
1064
+ LDFlagValue,
1065
+ LDEvaluationDetail
1066
+ } from '@launchdarkly/node-server-sdk';
1067
+
1068
+ const options: LDOptions = {
1069
+ timeout: 10,
1070
+ stream: true
1071
+ };
1072
+
1073
+ const client: LDClient = init(process.env.LAUNCHDARKLY_SDK_KEY!, options);
1074
+
1075
+ const context: LDContext = {
1076
+ kind: 'user',
1077
+ key: 'user-123',
1078
+ name: 'Sandy'
1079
+ };
1080
+
1081
+ const value: LDFlagValue = await client.variation('flag-key', context, false);
1082
+ ```
1083
+
1084
+ ### Typed Flag Values
1085
+
1086
+ ```typescript
1087
+ interface FeatureConfig {
1088
+ maxItems: number;
1089
+ enabled: boolean;
1090
+ theme: string;
1091
+ }
1092
+
1093
+ const config = await client.variation<FeatureConfig>(
1094
+ 'config-flag',
1095
+ context,
1096
+ { maxItems: 10, enabled: false, theme: 'light' }
1097
+ );
1098
+
1099
+ console.log(config.maxItems); // Type-safe
1100
+ ```
1101
+
1102
+ ---
1103
+
1104
+ ## OpenTelemetry Integration
1105
+
1106
+ ### Setup OpenTelemetry with LaunchDarkly
1107
+
1108
+ ```javascript
1109
+ import { init } from '@launchdarkly/node-server-sdk';
1110
+ import { Observability } from '@launchdarkly/observability-node';
1111
+ import { NodeSDK } from '@opentelemetry/sdk-node';
1112
+ import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
1113
+ import { PrometheusExporter } from '@opentelemetry/exporter-prometheus';
1114
+
1115
+ const sdk = new NodeSDK({
1116
+ metricReader: new PrometheusExporter({
1117
+ port: 9464
1118
+ }),
1119
+ instrumentations: [getNodeAutoInstrumentations()]
1120
+ });
1121
+
1122
+ sdk.start();
1123
+
1124
+ const client = init(process.env.LAUNCHDARKLY_SDK_KEY, {
1125
+ plugins: [new Observability()]
1126
+ });
1127
+ ```
1128
+
1129
+ ---
1130
+
1131
+ ## Private Attributes Configuration
1132
+
1133
+ ### Global Private Attributes
1134
+
1135
+ ```javascript
1136
+ const client = init(process.env.LAUNCHDARKLY_SDK_KEY, {
1137
+ privateAttributes: ['email', 'ssn', 'address'],
1138
+ allAttributesPrivate: false
1139
+ });
1140
+ ```
1141
+
1142
+ ### All Attributes Private
1143
+
1144
+ ```javascript
1145
+ const client = init(process.env.LAUNCHDARKLY_SDK_KEY, {
1146
+ allAttributesPrivate: true
1147
+ });
1148
+ ```
1149
+
1150
+ ---
1151
+
1152
+ ## Diagnostics
1153
+
1154
+ ### Disable Diagnostic Events
1155
+
1156
+ ```javascript
1157
+ const client = init(process.env.LAUNCHDARKLY_SDK_KEY, {
1158
+ diagnosticOptOut: true
1159
+ });
1160
+ ```
1161
+
1162
+ ---
1163
+
1164
+ ## Shutdown and Cleanup
1165
+
1166
+ ### Graceful Shutdown
1167
+
1168
+ ```javascript
1169
+ async function shutdown() {
1170
+ console.log('Flushing events...');
1171
+ await client.flush();
1172
+
1173
+ console.log('Closing client...');
1174
+ await client.close();
1175
+
1176
+ console.log('Shutdown complete');
1177
+ }
1178
+
1179
+ process.on('SIGINT', shutdown);
1180
+ process.on('SIGTERM', shutdown);
1181
+ ```
1182
+
1183
+ ### Check Initialization Status
1184
+
1185
+ ```javascript
1186
+ if (client.initialized()) {
1187
+ console.log('Client is ready');
1188
+ } else {
1189
+ console.log('Client is not initialized yet');
1190
+ }
1191
+ ```