featuredrop 1.1.0 → 1.3.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 (84) hide show
  1. package/README.md +547 -4
  2. package/dist/admin.cjs +212 -0
  3. package/dist/admin.cjs.map +1 -0
  4. package/dist/admin.d.cts +176 -0
  5. package/dist/admin.d.ts +176 -0
  6. package/dist/admin.js +207 -0
  7. package/dist/admin.js.map +1 -0
  8. package/dist/angular.cjs +296 -0
  9. package/dist/angular.cjs.map +1 -0
  10. package/dist/angular.d.cts +233 -0
  11. package/dist/angular.d.ts +233 -0
  12. package/dist/angular.js +293 -0
  13. package/dist/angular.js.map +1 -0
  14. package/dist/bridges.cjs +401 -0
  15. package/dist/bridges.cjs.map +1 -0
  16. package/dist/bridges.d.cts +194 -0
  17. package/dist/bridges.d.ts +194 -0
  18. package/dist/bridges.js +394 -0
  19. package/dist/bridges.js.map +1 -0
  20. package/dist/ci.cjs +328 -0
  21. package/dist/ci.cjs.map +1 -0
  22. package/dist/ci.d.cts +176 -0
  23. package/dist/ci.d.ts +176 -0
  24. package/dist/ci.js +324 -0
  25. package/dist/ci.js.map +1 -0
  26. package/dist/featuredrop.cjs +1377 -0
  27. package/dist/featuredrop.cjs.map +1 -0
  28. package/dist/flags.cjs +51 -0
  29. package/dist/flags.cjs.map +1 -0
  30. package/dist/flags.d.cts +48 -0
  31. package/dist/flags.d.ts +48 -0
  32. package/dist/flags.js +47 -0
  33. package/dist/flags.js.map +1 -0
  34. package/dist/index.cjs +4734 -70
  35. package/dist/index.cjs.map +1 -1
  36. package/dist/index.d.cts +1516 -9
  37. package/dist/index.d.ts +1516 -9
  38. package/dist/index.js +4660 -71
  39. package/dist/index.js.map +1 -1
  40. package/dist/preact.cjs +7790 -0
  41. package/dist/preact.cjs.map +1 -0
  42. package/dist/preact.d.cts +1213 -0
  43. package/dist/preact.d.ts +1213 -0
  44. package/dist/preact.js +7760 -0
  45. package/dist/preact.js.map +1 -0
  46. package/dist/react.cjs +6678 -159
  47. package/dist/react.cjs.map +1 -1
  48. package/dist/react.d.cts +852 -112
  49. package/dist/react.d.ts +852 -112
  50. package/dist/react.js +6657 -156
  51. package/dist/react.js.map +1 -1
  52. package/dist/schema.cjs +292 -0
  53. package/dist/schema.cjs.map +1 -0
  54. package/dist/schema.d.cts +345 -0
  55. package/dist/schema.d.ts +345 -0
  56. package/dist/schema.js +286 -0
  57. package/dist/schema.js.map +1 -0
  58. package/dist/solid.cjs +383 -0
  59. package/dist/solid.cjs.map +1 -0
  60. package/dist/solid.d.cts +246 -0
  61. package/dist/solid.d.ts +246 -0
  62. package/dist/solid.js +376 -0
  63. package/dist/solid.js.map +1 -0
  64. package/dist/svelte.cjs +339 -0
  65. package/dist/svelte.cjs.map +1 -0
  66. package/dist/svelte.js +334 -0
  67. package/dist/svelte.js.map +1 -0
  68. package/dist/testing.cjs +1543 -0
  69. package/dist/testing.cjs.map +1 -0
  70. package/dist/testing.d.cts +361 -0
  71. package/dist/testing.d.ts +361 -0
  72. package/dist/testing.js +1536 -0
  73. package/dist/testing.js.map +1 -0
  74. package/dist/vue.cjs +1094 -0
  75. package/dist/vue.cjs.map +1 -0
  76. package/dist/vue.js +1082 -0
  77. package/dist/vue.js.map +1 -0
  78. package/dist/web-components.cjs +493 -0
  79. package/dist/web-components.cjs.map +1 -0
  80. package/dist/web-components.d.cts +215 -0
  81. package/dist/web-components.d.ts +215 -0
  82. package/dist/web-components.js +487 -0
  83. package/dist/web-components.js.map +1 -0
  84. package/package.json +184 -3
package/dist/index.d.cts CHANGED
@@ -1,7 +1,13 @@
1
+ import { z } from 'zod';
2
+ import * as react_jsx_runtime from 'react/jsx-runtime';
3
+ import { ReactNode } from 'react';
4
+
1
5
  /** Entry type label — determines default icon/color in UI */
2
6
  type FeatureType = "feature" | "improvement" | "fix" | "breaking";
3
7
  /** Priority level for announcements */
4
8
  type FeaturePriority = "critical" | "normal" | "low";
9
+ /** Motion preset for built-in component transitions */
10
+ type FeatureDropAnimationPreset = "none" | "subtle" | "normal" | "playful";
5
11
  /** Call-to-action for a feature entry */
6
12
  interface FeatureCTA {
7
13
  /** Button/link label */
@@ -9,6 +15,106 @@ interface FeatureCTA {
9
15
  /** URL to navigate to */
10
16
  url: string;
11
17
  }
18
+ /** Variant-level overrides for A/B announcement testing */
19
+ interface FeatureVariant {
20
+ /** Optional variant-specific label override */
21
+ label?: string;
22
+ /** Optional variant-specific description override */
23
+ description?: string;
24
+ /** Optional variant-specific image override */
25
+ image?: string;
26
+ /** Optional variant-specific CTA override */
27
+ cta?: FeatureCTA;
28
+ /** Optional variant-specific metadata overrides */
29
+ meta?: Record<string, unknown>;
30
+ }
31
+ /** Audience targeting rule — determines which user segments see a feature */
32
+ interface AudienceRule {
33
+ /** Plans that should see this feature (e.g. ["pro", "enterprise"]) */
34
+ plan?: string[];
35
+ /** Roles that should see this feature (e.g. ["admin", "editor"]) */
36
+ role?: string[];
37
+ /** Regions that should see this feature (e.g. ["us", "eu"]) */
38
+ region?: string[];
39
+ /** Arbitrary key-value pairs for custom matching logic */
40
+ custom?: Record<string, unknown>;
41
+ }
42
+ /** User context for audience targeting */
43
+ interface UserContext {
44
+ /** Current user's plan (e.g. "pro", "free") */
45
+ plan?: string;
46
+ /** Current user's role (e.g. "admin", "viewer") */
47
+ role?: string;
48
+ /** Current user's region (e.g. "us", "eu") */
49
+ region?: string;
50
+ /** Arbitrary traits for custom matching logic */
51
+ traits?: Record<string, unknown>;
52
+ }
53
+ /** Custom audience matcher function */
54
+ type AudienceMatchFn = (audience: AudienceRule, userContext: UserContext) => boolean;
55
+ /** Feature flag resolver interface for gating announcement visibility */
56
+ interface FeatureFlagBridge {
57
+ isEnabled: (flagKey: string, userContext?: UserContext) => boolean;
58
+ }
59
+ /** Dependency gates for progressive feature discovery */
60
+ interface FeatureDependencies {
61
+ /** Features the user must have seen before this one can surface */
62
+ seen?: string[];
63
+ /** Features the user must have clicked before this one can surface */
64
+ clicked?: string[];
65
+ /** Features the user must have dismissed before this one can surface */
66
+ dismissed?: string[];
67
+ }
68
+ /** Runtime interaction state used to resolve dependency chains */
69
+ interface FeatureDependencyState {
70
+ /** IDs marked as seen */
71
+ seenIds?: ReadonlySet<string>;
72
+ /** IDs marked as clicked */
73
+ clickedIds?: ReadonlySet<string>;
74
+ /** IDs marked as dismissed */
75
+ dismissedIds?: ReadonlySet<string>;
76
+ }
77
+ /** Runtime context used by trigger evaluation */
78
+ interface TriggerContext {
79
+ /** Current app route/path */
80
+ path?: string;
81
+ /** Named events observed in this session */
82
+ events?: ReadonlySet<string>;
83
+ /** Named milestone flags reached in this session */
84
+ milestones?: ReadonlySet<string>;
85
+ /** Usage counters keyed by event/pattern name */
86
+ usage?: Record<string, number>;
87
+ /** Session elapsed time in milliseconds */
88
+ elapsedMs?: number;
89
+ /** Scroll completion percentage (0-100) */
90
+ scrollPercent?: number;
91
+ /** Optional additional trigger context */
92
+ metadata?: Record<string, unknown>;
93
+ }
94
+ type FeatureTrigger = {
95
+ type: "page";
96
+ match: string | RegExp;
97
+ } | {
98
+ type: "usage";
99
+ event: string;
100
+ minActions?: number;
101
+ } | {
102
+ type: "time";
103
+ minSeconds: number;
104
+ } | {
105
+ type: "milestone";
106
+ event: string;
107
+ } | {
108
+ type: "frustration";
109
+ pattern: string;
110
+ threshold?: number;
111
+ } | {
112
+ type: "scroll";
113
+ minPercent?: number;
114
+ } | {
115
+ type: "custom";
116
+ evaluate: (context: TriggerContext) => boolean;
117
+ };
12
118
  /** A single feature entry in the manifest */
13
119
  interface FeatureEntry {
14
120
  /** Unique identifier for the feature */
@@ -17,6 +123,20 @@ interface FeatureEntry {
17
123
  label: string;
18
124
  /** Optional longer description (supports markdown in UI components) */
19
125
  description?: string;
126
+ /**
127
+ * Semantic version targeting.
128
+ * If provided as an object, requires `appVersion` to be supplied to the provider/helpers.
129
+ * - introduced: earliest app version that includes this feature
130
+ * - showNewUntil: stop showing "new" once appVersion reaches this
131
+ * - deprecatedAt: hide feature for app versions at or above this (optional safety)
132
+ * - showIn: range string, e.g. ">=2.5.0 <3.0.0"
133
+ */
134
+ version?: string | {
135
+ introduced?: string;
136
+ showNewUntil?: string;
137
+ deprecatedAt?: string;
138
+ showIn?: string;
139
+ };
20
140
  /** ISO date when this feature was released */
21
141
  releasedAt: string;
22
142
  /** ISO date after which the "new" badge should stop showing */
@@ -25,10 +145,12 @@ interface FeatureEntry {
25
145
  sidebarKey?: string;
26
146
  /** Optional grouping category (e.g. "ai", "billing", "core") */
27
147
  category?: string;
148
+ /** Optional product scope (`"*"`, `"askverdict"`, etc.) for multi-product manifests */
149
+ product?: string;
28
150
  /** Optional URL to link to (e.g. docs page, changelog entry) */
29
151
  url?: string;
30
- /** Optional version string when this feature shipped */
31
- version?: string;
152
+ /** Optional feature flag key; requires a flag bridge to evaluate */
153
+ flagKey?: string;
32
154
  /** Entry type — determines default icon/color in UI components */
33
155
  type?: FeatureType;
34
156
  /** Priority level — critical entries get special treatment in UI */
@@ -41,6 +163,16 @@ interface FeatureEntry {
41
163
  publishAt?: string;
42
164
  /** Optional arbitrary metadata */
43
165
  meta?: Record<string, unknown>;
166
+ /** A/B variants keyed by variant name (e.g. control, treatment_a) */
167
+ variants?: Record<string, FeatureVariant>;
168
+ /** Percentage split per variant (same order as variants object keys) */
169
+ variantSplit?: number[];
170
+ /** Audience targeting — if set, only matching users see this feature */
171
+ audience?: AudienceRule;
172
+ /** Dependency requirements (progressive disclosure sequencing) */
173
+ dependsOn?: FeatureDependencies;
174
+ /** Contextual trigger rule */
175
+ trigger?: FeatureTrigger;
44
176
  }
45
177
  /** The full feature manifest — an array of feature entries */
46
178
  type FeatureManifest = readonly FeatureEntry[];
@@ -61,6 +193,34 @@ interface StorageAdapter {
61
193
  /** Dismiss all features — sets watermark to `now` and clears dismissals */
62
194
  dismissAll(now: Date): Promise<void>;
63
195
  }
196
+ /** Extended server-side dismissal state */
197
+ interface DismissalState {
198
+ /** Server-side watermark */
199
+ watermark: string | null;
200
+ /** Dismissed feature IDs */
201
+ dismissedIds: string[];
202
+ /** ISO timestamp of last interaction */
203
+ lastSeen: string;
204
+ /** Estimated device count contributing to this state */
205
+ deviceCount: number;
206
+ }
207
+ /** Server-capable storage adapter */
208
+ interface ServerStorageAdapter extends StorageAdapter {
209
+ /** Current user for this adapter instance */
210
+ userId: string;
211
+ /** Pull latest state from the server/database */
212
+ sync(): Promise<void>;
213
+ /** Dismiss multiple features at once */
214
+ dismissBatch(ids: string[]): Promise<void>;
215
+ /** Reset state for a target user */
216
+ resetUser(userId: string): Promise<void>;
217
+ /** Fetch multiple users' state */
218
+ getBulkState(userIds: string[]): Promise<Map<string, DismissalState>>;
219
+ /** Health check */
220
+ isHealthy(): Promise<boolean>;
221
+ /** Cleanup resources */
222
+ destroy(): Promise<void>;
223
+ }
64
224
  /** Analytics event callbacks — pipe to your analytics provider */
65
225
  interface AnalyticsCallbacks {
66
226
  /** Fired when a feature badge becomes visible to the user */
@@ -77,6 +237,15 @@ interface AnalyticsCallbacks {
77
237
  onAllDismissed?: () => void;
78
238
  }
79
239
 
240
+ /**
241
+ * Default audience matching logic.
242
+ *
243
+ * For each specified field (plan, role, region), checks if the user's
244
+ * value is included in the allowed list. Fields use AND logic between them,
245
+ * OR logic within each field's array. The `custom` field is ignored by
246
+ * the default matcher — use a custom `AudienceMatchFn` for that.
247
+ */
248
+ declare function matchesAudience(audience: AudienceRule, userContext: UserContext): boolean;
80
249
  /**
81
250
  * Check if a single feature should show as "new".
82
251
  *
@@ -85,24 +254,27 @@ interface AnalyticsCallbacks {
85
254
  * 2. Feature was released after the watermark (or no watermark exists)
86
255
  * 3. Feature has not been individually dismissed
87
256
  * 4. If `publishAt` is set, current time must be after it (scheduled publishing)
257
+ * 5. If `audience` is set, user must match the targeting rules
258
+ * 6. If `flagKey` is set, the flag bridge must resolve it as enabled
259
+ * 7. If `product` is set, it must match the current product scope
88
260
  */
89
- declare function isNew(feature: FeatureEntry, watermark: string | null, dismissedIds: ReadonlySet<string>, now?: Date): boolean;
261
+ declare function isNew(feature: FeatureEntry, watermark: string | null, dismissedIds: ReadonlySet<string>, now?: Date, userContext?: UserContext, matchAudience?: AudienceMatchFn, appVersion?: string, dependencyState?: FeatureDependencyState, triggerContext?: TriggerContext, flagBridge?: FeatureFlagBridge, product?: string): boolean;
90
262
  /**
91
263
  * Get all features that are currently "new" for this user.
92
264
  */
93
- declare function getNewFeatures(manifest: FeatureManifest, storage: StorageAdapter, now?: Date): FeatureEntry[];
265
+ declare function getNewFeatures(manifest: FeatureManifest, storage: StorageAdapter, now?: Date, userContext?: UserContext, matchAudience?: AudienceMatchFn, appVersion?: string, dependencyState?: FeatureDependencyState, triggerContext?: TriggerContext, flagBridge?: FeatureFlagBridge, product?: string): FeatureEntry[];
94
266
  /**
95
267
  * Get the count of new features.
96
268
  */
97
- declare function getNewFeatureCount(manifest: FeatureManifest, storage: StorageAdapter, now?: Date): number;
269
+ declare function getNewFeatureCount(manifest: FeatureManifest, storage: StorageAdapter, now?: Date, userContext?: UserContext, matchAudience?: AudienceMatchFn, appVersion?: string, dependencyState?: FeatureDependencyState, triggerContext?: TriggerContext, flagBridge?: FeatureFlagBridge, product?: string): number;
98
270
  /**
99
271
  * Check if a specific sidebar key has a new feature.
100
272
  */
101
- declare function hasNewFeature(manifest: FeatureManifest, sidebarKey: string, storage: StorageAdapter, now?: Date): boolean;
273
+ declare function hasNewFeature(manifest: FeatureManifest, sidebarKey: string, storage: StorageAdapter, now?: Date, userContext?: UserContext, matchAudience?: AudienceMatchFn, appVersion?: string, dependencyState?: FeatureDependencyState, triggerContext?: TriggerContext, flagBridge?: FeatureFlagBridge, product?: string): boolean;
102
274
  /**
103
275
  * Get all features sorted by priority (critical first) then by release date (newest first).
104
276
  */
105
- declare function getNewFeaturesSorted(manifest: FeatureManifest, storage: StorageAdapter, now?: Date): FeatureEntry[];
277
+ declare function getNewFeaturesSorted(manifest: FeatureManifest, storage: StorageAdapter, now?: Date, userContext?: UserContext, matchAudience?: AudienceMatchFn, appVersion?: string, dependencyState?: FeatureDependencyState, triggerContext?: TriggerContext, flagBridge?: FeatureFlagBridge, product?: string): FeatureEntry[];
106
278
 
107
279
  /**
108
280
  * Create a frozen feature manifest from an array of entries.
@@ -117,7 +289,895 @@ declare function getFeatureById(manifest: FeatureManifest, id: string): FeatureE
117
289
  /**
118
290
  * Get all new features in a specific category.
119
291
  */
120
- declare function getNewFeaturesByCategory(manifest: FeatureManifest, category: string, storage: StorageAdapter, now?: Date): FeatureEntry[];
292
+ declare function getNewFeaturesByCategory(manifest: FeatureManifest, category: string, storage: StorageAdapter, now?: Date, userContext?: UserContext, matchAudience?: AudienceMatchFn, appVersion?: string): FeatureEntry[];
293
+
294
+ /**
295
+ * Parse a feature description from markdown into sanitized HTML.
296
+ * - Uses `marked` when installed (optional peer dep)
297
+ * - Falls back to a tiny built-in parser when `marked` is absent
298
+ * - Strips script tags, event handlers, and javascript:/data: URLs
299
+ */
300
+ declare function parseDescription(markdown: string): string;
301
+
302
+ interface ChangelogRendererOptions {
303
+ manifest: FeatureManifest;
304
+ storage: StorageAdapter;
305
+ userContext?: UserContext;
306
+ matchAudience?: AudienceMatchFn;
307
+ appVersion?: string;
308
+ flagBridge?: FeatureFlagBridge;
309
+ product?: string;
310
+ now?: () => Date;
311
+ }
312
+ interface ChangelogRendererState {
313
+ manifest: FeatureManifest;
314
+ newFeatures: FeatureEntry[];
315
+ newFeaturesSorted: FeatureEntry[];
316
+ newCount: number;
317
+ watermark: string | null;
318
+ dismissedIds: ReadonlySet<string>;
319
+ }
320
+ interface ChangelogRendererActions {
321
+ refresh: () => void;
322
+ dismiss: (id: string) => void;
323
+ dismissAll: () => Promise<void>;
324
+ setManifest: (manifest: FeatureManifest) => void;
325
+ setUserContext: (userContext?: UserContext) => void;
326
+ setAppVersion: (appVersion?: string) => void;
327
+ setAudienceMatcher: (matchAudience?: AudienceMatchFn) => void;
328
+ setFlagBridge: (flagBridge?: FeatureFlagBridge) => void;
329
+ setProduct: (product?: string) => void;
330
+ }
331
+ interface ChangelogRendererComputed {
332
+ isNew: (sidebarKey: string) => boolean;
333
+ getFeature: (sidebarKey: string) => FeatureEntry | undefined;
334
+ getFeatureById: (id: string) => FeatureEntry | undefined;
335
+ getFeaturesByCategory: (category: string) => FeatureEntry[];
336
+ }
337
+ interface ChangelogRenderer {
338
+ readonly state: ChangelogRendererState;
339
+ readonly actions: ChangelogRendererActions;
340
+ readonly computed: ChangelogRendererComputed;
341
+ subscribe: (listener: (state: ChangelogRendererState) => void) => () => void;
342
+ }
343
+ declare function createChangelogRenderer({ manifest: initialManifest, storage, userContext: initialUserContext, matchAudience: initialMatchAudience, appVersion: initialAppVersion, flagBridge: initialFlagBridge, product: initialProduct, now, }: ChangelogRendererOptions): ChangelogRenderer;
344
+
345
+ interface SlackBridgeOptions {
346
+ webhookUrl: string;
347
+ username?: string;
348
+ iconEmoji?: string;
349
+ channel?: string;
350
+ formatter?: (feature: FeatureEntry) => Record<string, unknown>;
351
+ }
352
+ declare const SlackBridge: {
353
+ notify(feature: FeatureEntry, options: SlackBridgeOptions): Promise<void>;
354
+ };
355
+ interface DiscordBridgeOptions {
356
+ webhookUrl: string;
357
+ username?: string;
358
+ avatarUrl?: string;
359
+ formatter?: (feature: FeatureEntry) => Record<string, unknown>;
360
+ }
361
+ declare const DiscordBridge: {
362
+ notify(feature: FeatureEntry, options: DiscordBridgeOptions): Promise<void>;
363
+ };
364
+ interface WebhookBridgeOptions {
365
+ url: string;
366
+ headers?: Record<string, string>;
367
+ event?: string;
368
+ body?: Record<string, unknown>;
369
+ }
370
+ declare const WebhookBridge: {
371
+ post(feature: FeatureEntry, options: WebhookBridgeOptions): Promise<void>;
372
+ };
373
+ interface EmailDigestGeneratorOptions {
374
+ title?: string;
375
+ intro?: string;
376
+ template?: "default" | "minimal";
377
+ productName?: string;
378
+ }
379
+ declare const EmailDigestGenerator: {
380
+ generate(features: readonly FeatureEntry[], options?: EmailDigestGeneratorOptions): string;
381
+ };
382
+ interface RSSFeedGeneratorOptions {
383
+ title?: string;
384
+ link?: string;
385
+ description?: string;
386
+ }
387
+ declare const RSSFeedGenerator: {
388
+ generate(manifest: FeatureManifest, options?: RSSFeedGeneratorOptions): string;
389
+ };
390
+
391
+ interface ValidationIssue {
392
+ path: string;
393
+ message: string;
394
+ code: "invalid_type" | "missing_required" | "invalid_value" | "invalid_date" | "duplicate_id" | "circular_dependency";
395
+ }
396
+ interface ValidationResult {
397
+ valid: boolean;
398
+ errors: ValidationIssue[];
399
+ }
400
+ declare const featureEntryJsonSchema: {
401
+ readonly type: "object";
402
+ readonly required: readonly ["id", "label", "releasedAt", "showNewUntil"];
403
+ readonly properties: {
404
+ readonly id: {
405
+ readonly type: "string";
406
+ };
407
+ readonly label: {
408
+ readonly type: "string";
409
+ };
410
+ readonly description: {
411
+ readonly type: "string";
412
+ };
413
+ readonly releasedAt: {
414
+ readonly type: "string";
415
+ readonly format: "date-time";
416
+ };
417
+ readonly showNewUntil: {
418
+ readonly type: "string";
419
+ readonly format: "date-time";
420
+ };
421
+ readonly flagKey: {
422
+ readonly type: "string";
423
+ };
424
+ readonly product: {
425
+ readonly type: "string";
426
+ };
427
+ readonly url: {
428
+ readonly type: "string";
429
+ };
430
+ readonly image: {
431
+ readonly type: "string";
432
+ };
433
+ readonly type: {
434
+ readonly enum: readonly ["feature", "improvement", "fix", "breaking"];
435
+ };
436
+ readonly priority: {
437
+ readonly enum: readonly ["critical", "normal", "low"];
438
+ };
439
+ readonly cta: {
440
+ readonly type: "object";
441
+ readonly properties: {
442
+ readonly label: {
443
+ readonly type: "string";
444
+ };
445
+ readonly url: {
446
+ readonly type: "string";
447
+ };
448
+ };
449
+ };
450
+ readonly meta: {
451
+ readonly type: "object";
452
+ };
453
+ };
454
+ };
455
+ declare const featureManifestJsonSchema: {
456
+ readonly type: "array";
457
+ readonly items: {
458
+ readonly type: "object";
459
+ readonly required: readonly ["id", "label", "releasedAt", "showNewUntil"];
460
+ readonly properties: {
461
+ readonly id: {
462
+ readonly type: "string";
463
+ };
464
+ readonly label: {
465
+ readonly type: "string";
466
+ };
467
+ readonly description: {
468
+ readonly type: "string";
469
+ };
470
+ readonly releasedAt: {
471
+ readonly type: "string";
472
+ readonly format: "date-time";
473
+ };
474
+ readonly showNewUntil: {
475
+ readonly type: "string";
476
+ readonly format: "date-time";
477
+ };
478
+ readonly flagKey: {
479
+ readonly type: "string";
480
+ };
481
+ readonly product: {
482
+ readonly type: "string";
483
+ };
484
+ readonly url: {
485
+ readonly type: "string";
486
+ };
487
+ readonly image: {
488
+ readonly type: "string";
489
+ };
490
+ readonly type: {
491
+ readonly enum: readonly ["feature", "improvement", "fix", "breaking"];
492
+ };
493
+ readonly priority: {
494
+ readonly enum: readonly ["critical", "normal", "low"];
495
+ };
496
+ readonly cta: {
497
+ readonly type: "object";
498
+ readonly properties: {
499
+ readonly label: {
500
+ readonly type: "string";
501
+ };
502
+ readonly url: {
503
+ readonly type: "string";
504
+ };
505
+ };
506
+ };
507
+ readonly meta: {
508
+ readonly type: "object";
509
+ };
510
+ };
511
+ };
512
+ };
513
+ declare const featureEntrySchema: z.ZodObject<{
514
+ id: z.ZodString;
515
+ label: z.ZodString;
516
+ releasedAt: z.ZodEffects<z.ZodString, string, string>;
517
+ showNewUntil: z.ZodEffects<z.ZodString, string, string>;
518
+ description: z.ZodOptional<z.ZodString>;
519
+ flagKey: z.ZodOptional<z.ZodString>;
520
+ product: z.ZodOptional<z.ZodString>;
521
+ url: z.ZodOptional<z.ZodString>;
522
+ image: z.ZodOptional<z.ZodString>;
523
+ type: z.ZodOptional<z.ZodEnum<["feature", "improvement", "fix", "breaking"]>>;
524
+ priority: z.ZodOptional<z.ZodEnum<["critical", "normal", "low"]>>;
525
+ cta: z.ZodOptional<z.ZodObject<{
526
+ label: z.ZodString;
527
+ url: z.ZodString;
528
+ }, "strip", z.ZodTypeAny, {
529
+ label: string;
530
+ url: string;
531
+ }, {
532
+ label: string;
533
+ url: string;
534
+ }>>;
535
+ meta: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
536
+ dependsOn: z.ZodOptional<z.ZodObject<{
537
+ seen: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
538
+ clicked: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
539
+ dismissed: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
540
+ }, "strip", z.ZodTypeAny, {
541
+ seen?: string[] | undefined;
542
+ clicked?: string[] | undefined;
543
+ dismissed?: string[] | undefined;
544
+ }, {
545
+ seen?: string[] | undefined;
546
+ clicked?: string[] | undefined;
547
+ dismissed?: string[] | undefined;
548
+ }>>;
549
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
550
+ id: z.ZodString;
551
+ label: z.ZodString;
552
+ releasedAt: z.ZodEffects<z.ZodString, string, string>;
553
+ showNewUntil: z.ZodEffects<z.ZodString, string, string>;
554
+ description: z.ZodOptional<z.ZodString>;
555
+ flagKey: z.ZodOptional<z.ZodString>;
556
+ product: z.ZodOptional<z.ZodString>;
557
+ url: z.ZodOptional<z.ZodString>;
558
+ image: z.ZodOptional<z.ZodString>;
559
+ type: z.ZodOptional<z.ZodEnum<["feature", "improvement", "fix", "breaking"]>>;
560
+ priority: z.ZodOptional<z.ZodEnum<["critical", "normal", "low"]>>;
561
+ cta: z.ZodOptional<z.ZodObject<{
562
+ label: z.ZodString;
563
+ url: z.ZodString;
564
+ }, "strip", z.ZodTypeAny, {
565
+ label: string;
566
+ url: string;
567
+ }, {
568
+ label: string;
569
+ url: string;
570
+ }>>;
571
+ meta: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
572
+ dependsOn: z.ZodOptional<z.ZodObject<{
573
+ seen: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
574
+ clicked: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
575
+ dismissed: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
576
+ }, "strip", z.ZodTypeAny, {
577
+ seen?: string[] | undefined;
578
+ clicked?: string[] | undefined;
579
+ dismissed?: string[] | undefined;
580
+ }, {
581
+ seen?: string[] | undefined;
582
+ clicked?: string[] | undefined;
583
+ dismissed?: string[] | undefined;
584
+ }>>;
585
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
586
+ id: z.ZodString;
587
+ label: z.ZodString;
588
+ releasedAt: z.ZodEffects<z.ZodString, string, string>;
589
+ showNewUntil: z.ZodEffects<z.ZodString, string, string>;
590
+ description: z.ZodOptional<z.ZodString>;
591
+ flagKey: z.ZodOptional<z.ZodString>;
592
+ product: z.ZodOptional<z.ZodString>;
593
+ url: z.ZodOptional<z.ZodString>;
594
+ image: z.ZodOptional<z.ZodString>;
595
+ type: z.ZodOptional<z.ZodEnum<["feature", "improvement", "fix", "breaking"]>>;
596
+ priority: z.ZodOptional<z.ZodEnum<["critical", "normal", "low"]>>;
597
+ cta: z.ZodOptional<z.ZodObject<{
598
+ label: z.ZodString;
599
+ url: z.ZodString;
600
+ }, "strip", z.ZodTypeAny, {
601
+ label: string;
602
+ url: string;
603
+ }, {
604
+ label: string;
605
+ url: string;
606
+ }>>;
607
+ meta: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
608
+ dependsOn: z.ZodOptional<z.ZodObject<{
609
+ seen: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
610
+ clicked: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
611
+ dismissed: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
612
+ }, "strip", z.ZodTypeAny, {
613
+ seen?: string[] | undefined;
614
+ clicked?: string[] | undefined;
615
+ dismissed?: string[] | undefined;
616
+ }, {
617
+ seen?: string[] | undefined;
618
+ clicked?: string[] | undefined;
619
+ dismissed?: string[] | undefined;
620
+ }>>;
621
+ }, z.ZodTypeAny, "passthrough">>;
622
+ declare const featureManifestSchema: z.ZodArray<z.ZodObject<{
623
+ id: z.ZodString;
624
+ label: z.ZodString;
625
+ releasedAt: z.ZodEffects<z.ZodString, string, string>;
626
+ showNewUntil: z.ZodEffects<z.ZodString, string, string>;
627
+ description: z.ZodOptional<z.ZodString>;
628
+ flagKey: z.ZodOptional<z.ZodString>;
629
+ product: z.ZodOptional<z.ZodString>;
630
+ url: z.ZodOptional<z.ZodString>;
631
+ image: z.ZodOptional<z.ZodString>;
632
+ type: z.ZodOptional<z.ZodEnum<["feature", "improvement", "fix", "breaking"]>>;
633
+ priority: z.ZodOptional<z.ZodEnum<["critical", "normal", "low"]>>;
634
+ cta: z.ZodOptional<z.ZodObject<{
635
+ label: z.ZodString;
636
+ url: z.ZodString;
637
+ }, "strip", z.ZodTypeAny, {
638
+ label: string;
639
+ url: string;
640
+ }, {
641
+ label: string;
642
+ url: string;
643
+ }>>;
644
+ meta: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
645
+ dependsOn: z.ZodOptional<z.ZodObject<{
646
+ seen: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
647
+ clicked: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
648
+ dismissed: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
649
+ }, "strip", z.ZodTypeAny, {
650
+ seen?: string[] | undefined;
651
+ clicked?: string[] | undefined;
652
+ dismissed?: string[] | undefined;
653
+ }, {
654
+ seen?: string[] | undefined;
655
+ clicked?: string[] | undefined;
656
+ dismissed?: string[] | undefined;
657
+ }>>;
658
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
659
+ id: z.ZodString;
660
+ label: z.ZodString;
661
+ releasedAt: z.ZodEffects<z.ZodString, string, string>;
662
+ showNewUntil: z.ZodEffects<z.ZodString, string, string>;
663
+ description: z.ZodOptional<z.ZodString>;
664
+ flagKey: z.ZodOptional<z.ZodString>;
665
+ product: z.ZodOptional<z.ZodString>;
666
+ url: z.ZodOptional<z.ZodString>;
667
+ image: z.ZodOptional<z.ZodString>;
668
+ type: z.ZodOptional<z.ZodEnum<["feature", "improvement", "fix", "breaking"]>>;
669
+ priority: z.ZodOptional<z.ZodEnum<["critical", "normal", "low"]>>;
670
+ cta: z.ZodOptional<z.ZodObject<{
671
+ label: z.ZodString;
672
+ url: z.ZodString;
673
+ }, "strip", z.ZodTypeAny, {
674
+ label: string;
675
+ url: string;
676
+ }, {
677
+ label: string;
678
+ url: string;
679
+ }>>;
680
+ meta: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
681
+ dependsOn: z.ZodOptional<z.ZodObject<{
682
+ seen: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
683
+ clicked: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
684
+ dismissed: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
685
+ }, "strip", z.ZodTypeAny, {
686
+ seen?: string[] | undefined;
687
+ clicked?: string[] | undefined;
688
+ dismissed?: string[] | undefined;
689
+ }, {
690
+ seen?: string[] | undefined;
691
+ clicked?: string[] | undefined;
692
+ dismissed?: string[] | undefined;
693
+ }>>;
694
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
695
+ id: z.ZodString;
696
+ label: z.ZodString;
697
+ releasedAt: z.ZodEffects<z.ZodString, string, string>;
698
+ showNewUntil: z.ZodEffects<z.ZodString, string, string>;
699
+ description: z.ZodOptional<z.ZodString>;
700
+ flagKey: z.ZodOptional<z.ZodString>;
701
+ product: z.ZodOptional<z.ZodString>;
702
+ url: z.ZodOptional<z.ZodString>;
703
+ image: z.ZodOptional<z.ZodString>;
704
+ type: z.ZodOptional<z.ZodEnum<["feature", "improvement", "fix", "breaking"]>>;
705
+ priority: z.ZodOptional<z.ZodEnum<["critical", "normal", "low"]>>;
706
+ cta: z.ZodOptional<z.ZodObject<{
707
+ label: z.ZodString;
708
+ url: z.ZodString;
709
+ }, "strip", z.ZodTypeAny, {
710
+ label: string;
711
+ url: string;
712
+ }, {
713
+ label: string;
714
+ url: string;
715
+ }>>;
716
+ meta: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
717
+ dependsOn: z.ZodOptional<z.ZodObject<{
718
+ seen: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
719
+ clicked: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
720
+ dismissed: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
721
+ }, "strip", z.ZodTypeAny, {
722
+ seen?: string[] | undefined;
723
+ clicked?: string[] | undefined;
724
+ dismissed?: string[] | undefined;
725
+ }, {
726
+ seen?: string[] | undefined;
727
+ clicked?: string[] | undefined;
728
+ dismissed?: string[] | undefined;
729
+ }>>;
730
+ }, z.ZodTypeAny, "passthrough">>, "many">;
731
+ declare function validateManifest(data: unknown): ValidationResult;
732
+
733
+ interface ChangedFeature {
734
+ id: string;
735
+ before: FeatureEntry;
736
+ after: FeatureEntry;
737
+ changedFields: string[];
738
+ }
739
+ interface ManifestDiff {
740
+ added: FeatureEntry[];
741
+ removed: FeatureEntry[];
742
+ changed: ChangedFeature[];
743
+ }
744
+ declare function diffManifest(before: FeatureManifest, after: FeatureManifest): ManifestDiff;
745
+ interface ChangelogDiffOptions {
746
+ includeFieldChanges?: boolean;
747
+ }
748
+ declare function generateChangelogDiff(diff: ManifestDiff, options?: ChangelogDiffOptions): string;
749
+ declare function validateManifestForCI(manifest: FeatureManifest): ValidationResult;
750
+
751
+ interface CreateFlagBridgeOptions {
752
+ isEnabled: (flagKey: string, userContext?: UserContext) => boolean;
753
+ }
754
+ declare function createFlagBridge(options: CreateFlagBridgeOptions): FeatureFlagBridge;
755
+ interface LaunchDarklyClientLike {
756
+ variation: (flagKey: string, user: Record<string, unknown>, defaultValue: boolean) => boolean;
757
+ }
758
+ interface LaunchDarklyBridgeOptions {
759
+ userResolver?: (userContext?: UserContext) => Record<string, unknown>;
760
+ defaultValue?: boolean;
761
+ }
762
+ declare class LaunchDarklyBridge implements FeatureFlagBridge {
763
+ private readonly client;
764
+ private readonly options;
765
+ constructor(client: LaunchDarklyClientLike, options?: LaunchDarklyBridgeOptions);
766
+ isEnabled(flagKey: string, userContext?: UserContext): boolean;
767
+ }
768
+ interface PostHogClientLike {
769
+ isFeatureEnabled: (flagKey: string, distinctId?: string, groups?: Record<string, string>, personProperties?: Record<string, unknown>) => boolean;
770
+ }
771
+ interface PostHogBridgeOptions {
772
+ distinctIdResolver?: (userContext?: UserContext) => string | undefined;
773
+ groupsResolver?: (userContext?: UserContext) => Record<string, string> | undefined;
774
+ }
775
+ declare class PostHogBridge implements FeatureFlagBridge {
776
+ private readonly client;
777
+ private readonly options;
778
+ constructor(client: PostHogClientLike, options?: PostHogBridgeOptions);
779
+ isEnabled(flagKey: string, userContext?: UserContext): boolean;
780
+ }
781
+
782
+ interface ManifestEditorProps {
783
+ features: readonly FeatureEntry[];
784
+ onSave: (updated: FeatureEntry[]) => Promise<void> | void;
785
+ readOnly?: boolean;
786
+ children?: ReactNode;
787
+ }
788
+ declare function ManifestEditor({ features, onSave, readOnly, children, }: ManifestEditorProps): react_jsx_runtime.JSX.Element;
789
+ interface ScheduleCalendarProps {
790
+ features: readonly FeatureEntry[];
791
+ onSchedule: (featureId: string, publishAt: string) => Promise<void> | void;
792
+ }
793
+ declare function ScheduleCalendar({ features, onSchedule }: ScheduleCalendarProps): react_jsx_runtime.JSX.Element;
794
+ interface PreviewPanelProps {
795
+ feature?: FeatureEntry | null;
796
+ components?: Array<"badge" | "changelog" | "spotlight" | "banner" | "toast">;
797
+ }
798
+ declare function PreviewPanel({ feature, components }: PreviewPanelProps): react_jsx_runtime.JSX.Element;
799
+ interface AudienceBuilderProps {
800
+ segments?: string[];
801
+ roles?: string[];
802
+ regions?: string[];
803
+ value?: AudienceRule;
804
+ onChange?: (audience: AudienceRule) => void;
805
+ onSave?: (audience: AudienceRule) => Promise<void> | void;
806
+ }
807
+ declare function AudienceBuilder({ segments, roles, regions, value, onChange, onSave, }: AudienceBuilderProps): react_jsx_runtime.JSX.Element;
808
+
809
+ type CMSFieldResolver = string | ((record: unknown) => unknown);
810
+ interface CMSFieldMapping {
811
+ id?: CMSFieldResolver;
812
+ label?: CMSFieldResolver;
813
+ description?: CMSFieldResolver;
814
+ releasedAt?: CMSFieldResolver;
815
+ showNewUntil?: CMSFieldResolver;
816
+ sidebarKey?: CMSFieldResolver;
817
+ category?: CMSFieldResolver;
818
+ product?: CMSFieldResolver;
819
+ flagKey?: CMSFieldResolver;
820
+ url?: CMSFieldResolver;
821
+ image?: CMSFieldResolver;
822
+ publishAt?: CMSFieldResolver;
823
+ type?: CMSFieldResolver;
824
+ priority?: CMSFieldResolver;
825
+ ctaLabel?: CMSFieldResolver;
826
+ ctaUrl?: CMSFieldResolver;
827
+ }
828
+ interface CMSAdapter {
829
+ load: () => Promise<FeatureEntry[]>;
830
+ }
831
+ interface CMSAdapterBaseOptions {
832
+ fieldMapping?: CMSFieldMapping;
833
+ /** Throw when mapped entries are invalid. Default false (invalid entries are dropped). */
834
+ strictValidation?: boolean;
835
+ }
836
+ interface ContentfulAdapterOptions extends CMSAdapterBaseOptions {
837
+ spaceId: string;
838
+ accessToken: string;
839
+ contentType: string;
840
+ environment?: string;
841
+ locale?: string;
842
+ limit?: number;
843
+ }
844
+ interface SanityAdapterOptions extends CMSAdapterBaseOptions {
845
+ projectId: string;
846
+ dataset: string;
847
+ query: string;
848
+ token?: string;
849
+ apiVersion?: string;
850
+ }
851
+ interface StrapiAdapterOptions extends CMSAdapterBaseOptions {
852
+ baseUrl: string;
853
+ endpoint?: string;
854
+ token?: string;
855
+ query?: string;
856
+ }
857
+ interface NotionAdapterOptions extends CMSAdapterBaseOptions {
858
+ databaseId: string;
859
+ token: string;
860
+ notionVersion?: string;
861
+ filter?: Record<string, unknown>;
862
+ sorts?: Array<Record<string, unknown>>;
863
+ }
864
+ interface MarkdownAdapterFile {
865
+ source?: string;
866
+ markdown: string;
867
+ }
868
+ interface MarkdownAdapterOptions extends CMSAdapterBaseOptions {
869
+ pattern?: string;
870
+ cwd?: string;
871
+ entries?: MarkdownAdapterFile[];
872
+ }
873
+ declare class ContentfulAdapter implements CMSAdapter {
874
+ private readonly options;
875
+ constructor(options: ContentfulAdapterOptions);
876
+ load(): Promise<FeatureEntry[]>;
877
+ }
878
+ declare class SanityAdapter implements CMSAdapter {
879
+ private readonly options;
880
+ constructor(options: SanityAdapterOptions);
881
+ load(): Promise<FeatureEntry[]>;
882
+ }
883
+ declare class StrapiAdapter implements CMSAdapter {
884
+ private readonly options;
885
+ constructor(options: StrapiAdapterOptions);
886
+ load(): Promise<FeatureEntry[]>;
887
+ }
888
+ declare class NotionAdapter implements CMSAdapter {
889
+ private readonly options;
890
+ constructor(options: NotionAdapterOptions);
891
+ load(): Promise<FeatureEntry[]>;
892
+ }
893
+ declare class MarkdownAdapter implements CMSAdapter {
894
+ private readonly options;
895
+ constructor(options?: MarkdownAdapterOptions);
896
+ load(): Promise<FeatureEntry[]>;
897
+ }
898
+
899
+ interface FeatureDropTheme {
900
+ colors: {
901
+ primary: string;
902
+ background: string;
903
+ text: string;
904
+ textMuted: string;
905
+ border: string;
906
+ success: string;
907
+ warning: string;
908
+ error: string;
909
+ };
910
+ fonts: {
911
+ family: string;
912
+ sizeBase: string;
913
+ sizeSm: string;
914
+ sizeLg: string;
915
+ };
916
+ spacing: {
917
+ xs: string;
918
+ sm: string;
919
+ md: string;
920
+ lg: string;
921
+ xl: string;
922
+ };
923
+ radii: {
924
+ sm: string;
925
+ md: string;
926
+ lg: string;
927
+ full: string;
928
+ };
929
+ shadows: {
930
+ sm: string;
931
+ md: string;
932
+ lg: string;
933
+ };
934
+ zIndex: {
935
+ base: number;
936
+ tooltip: number;
937
+ modal: number;
938
+ overlay: number;
939
+ };
940
+ }
941
+ type FeatureDropThemePreset = "light" | "dark" | "auto" | "minimal" | "vibrant";
942
+ type FeatureDropThemeOverrides = {
943
+ [K in keyof FeatureDropTheme]?: Partial<FeatureDropTheme[K]>;
944
+ };
945
+ type FeatureDropThemeInput = FeatureDropThemePreset | FeatureDropThemeOverrides | FeatureDropTheme;
946
+ declare const FEATUREDROP_THEMES: {
947
+ readonly light: FeatureDropTheme;
948
+ readonly dark: FeatureDropTheme;
949
+ readonly minimal: FeatureDropTheme;
950
+ readonly vibrant: FeatureDropTheme;
951
+ };
952
+ declare function createTheme(overrides: FeatureDropThemeOverrides, base?: FeatureDropTheme): FeatureDropTheme;
953
+ declare function resolveTheme(input?: FeatureDropThemeInput, options?: {
954
+ prefersDark?: boolean;
955
+ }): FeatureDropTheme;
956
+ declare function themeToCSSVariables(theme: FeatureDropTheme): Record<string, string | number>;
957
+
958
+ interface FeatureDropTranslations {
959
+ newBadge: string;
960
+ whatsNewTitle: string;
961
+ markAllRead: string;
962
+ allCaughtUp: string;
963
+ close: string;
964
+ changelogTitle: string;
965
+ searchPlaceholder: string;
966
+ allCategories: string;
967
+ noUpdatesYet: string;
968
+ loadMore: string;
969
+ share: string;
970
+ skipToEntries: string;
971
+ newFeatureCount: (count: number) => string;
972
+ stepOf: (current: number, total: number) => string;
973
+ back: string;
974
+ next: string;
975
+ skip: string;
976
+ finish: string;
977
+ gotIt: string;
978
+ announcement: string;
979
+ feedbackTitle: string;
980
+ feedbackTrigger: string;
981
+ feedbackSubmitted: string;
982
+ submit: string;
983
+ cancel: string;
984
+ askLater: string;
985
+ }
986
+ declare function resolveLocale(locale?: string): string;
987
+ declare function getLocaleDirection(locale?: string): "ltr" | "rtl";
988
+ declare function formatDateForLocale(value: string | number | Date, locale?: string, options?: Intl.DateTimeFormatOptions): string;
989
+ declare function formatRelativeTimeForLocale(value: string | number | Date, locale?: string, options?: {
990
+ now?: string | number | Date;
991
+ numeric?: Intl.RelativeTimeFormatNumeric;
992
+ style?: Intl.RelativeTimeFormatStyle;
993
+ }): string;
994
+ declare function resolveTranslations(locale?: string, overrides?: Partial<FeatureDropTranslations>): FeatureDropTranslations;
995
+ declare const FEATUREDROP_TRANSLATIONS: {
996
+ readonly en: FeatureDropTranslations;
997
+ };
998
+
999
+ declare const FEATUREDROP_ANIMATION_PRESETS: readonly ["none", "subtle", "normal", "playful"];
1000
+ declare function resolveAnimationPreset(preset?: FeatureDropAnimationPreset, options?: {
1001
+ reducedMotion?: boolean;
1002
+ }): FeatureDropAnimationPreset;
1003
+ type AnimationSurface = "toast" | "panel" | "modal" | "popover";
1004
+ type AnimationPhase = "enter" | "exit";
1005
+ type PulseSurface = "dot" | "beacon";
1006
+ declare function getEnterAnimation(preset: FeatureDropAnimationPreset, surface: AnimationSurface): string | undefined;
1007
+ declare function getExitAnimation(preset: FeatureDropAnimationPreset, surface: AnimationSurface): string | undefined;
1008
+ declare function getPulseAnimation(preset: FeatureDropAnimationPreset, surface?: PulseSurface): string | undefined;
1009
+ declare function getAnimationDurationMs(preset: FeatureDropAnimationPreset, surface: AnimationSurface, phase: AnimationPhase): number;
1010
+
1011
+ /**
1012
+ * Generate a simple RSS 2.0 feed from a feature manifest.
1013
+ * Titles and descriptions are sanitized via `parseDescription`.
1014
+ */
1015
+ declare function generateRSS(manifest: FeatureManifest, options?: {
1016
+ title?: string;
1017
+ link?: string;
1018
+ description?: string;
1019
+ }): string;
1020
+
1021
+ interface ThrottleOptions {
1022
+ maxSimultaneousBadges?: number;
1023
+ maxSimultaneousSpotlights?: number;
1024
+ maxToastsPerSession?: number;
1025
+ minTimeBetweenModals?: number;
1026
+ minTimeBetweenTours?: number;
1027
+ sessionCooldown?: number;
1028
+ respectDoNotDisturb?: boolean;
1029
+ }
1030
+ interface ThrottleRuntimeState {
1031
+ sessionStartedAt: number;
1032
+ quietMode?: boolean;
1033
+ }
1034
+ interface ThrottleResult {
1035
+ visible: FeatureEntry[];
1036
+ queued: FeatureEntry[];
1037
+ }
1038
+ declare function applyAnnouncementThrottle(features: FeatureEntry[], options: ThrottleOptions | undefined, state: ThrottleRuntimeState, now?: number): ThrottleResult;
1039
+
1040
+ type AdoptionEventType = "feature_seen" | "feature_clicked" | "feature_dismissed" | "tour_started" | "tour_completed" | "tour_skipped" | "checklist_task_completed" | "checklist_completed" | "survey_submitted" | "feedback_submitted" | "announcement_shown" | "cta_clicked";
1041
+ interface AdoptionEvent {
1042
+ type: AdoptionEventType;
1043
+ featureId?: string;
1044
+ tourId?: string;
1045
+ variant?: string;
1046
+ timestamp: string;
1047
+ sessionId?: string;
1048
+ userId?: string;
1049
+ metadata?: Record<string, unknown>;
1050
+ }
1051
+ type AdoptionEventInput = Omit<AdoptionEvent, "timestamp"> & {
1052
+ timestamp?: string;
1053
+ };
1054
+ interface AnalyticsAdapter {
1055
+ track: (event: AdoptionEvent) => void | Promise<void>;
1056
+ trackBatch?: (events: AdoptionEvent[]) => void | Promise<void>;
1057
+ }
1058
+ interface AnalyticsCollectorOptions {
1059
+ adapter: AnalyticsAdapter;
1060
+ batchSize?: number;
1061
+ flushInterval?: number;
1062
+ sampleRate?: number;
1063
+ enabled?: boolean;
1064
+ sessionId?: string;
1065
+ userId?: string;
1066
+ now?: () => Date;
1067
+ random?: () => number;
1068
+ }
1069
+ declare class AnalyticsCollector {
1070
+ private adapter;
1071
+ private queue;
1072
+ private batchSize;
1073
+ private flushInterval;
1074
+ private sampleRate;
1075
+ private enabled;
1076
+ private now;
1077
+ private random;
1078
+ private sessionId?;
1079
+ private userId?;
1080
+ private timer;
1081
+ private flushing;
1082
+ constructor(options: AnalyticsCollectorOptions);
1083
+ setEnabled(enabled: boolean): void;
1084
+ setContext(context: {
1085
+ sessionId?: string;
1086
+ userId?: string;
1087
+ }): void;
1088
+ getQueueSize(): number;
1089
+ track(event: AdoptionEventInput): void;
1090
+ flush(): Promise<void>;
1091
+ destroy(): Promise<void>;
1092
+ private startTimer;
1093
+ }
1094
+ declare class PostHogAdapter implements AnalyticsAdapter {
1095
+ private readonly client;
1096
+ constructor(client: {
1097
+ capture: (event: string, properties?: Record<string, unknown>) => void;
1098
+ });
1099
+ track(event: AdoptionEvent): void;
1100
+ }
1101
+ declare class AmplitudeAdapter implements AnalyticsAdapter {
1102
+ private readonly client;
1103
+ constructor(client: {
1104
+ track: (event: string, properties?: Record<string, unknown>) => void;
1105
+ });
1106
+ track(event: AdoptionEvent): void;
1107
+ }
1108
+ declare class MixpanelAdapter implements AnalyticsAdapter {
1109
+ private readonly client;
1110
+ constructor(client: {
1111
+ track: (event: string, properties?: Record<string, unknown>) => void;
1112
+ });
1113
+ track(event: AdoptionEvent): void;
1114
+ }
1115
+ declare class SegmentAdapter implements AnalyticsAdapter {
1116
+ private readonly client;
1117
+ constructor(client: {
1118
+ track: (event: string, properties?: Record<string, unknown>) => void;
1119
+ });
1120
+ track(event: AdoptionEvent): void;
1121
+ }
1122
+ declare class CustomAdapter implements AnalyticsAdapter {
1123
+ private readonly handler;
1124
+ constructor(handler: (event: AdoptionEvent) => void | Promise<void>);
1125
+ track(event: AdoptionEvent): void | Promise<void>;
1126
+ }
1127
+ interface FeatureEngagementMetrics {
1128
+ seen: number;
1129
+ clicked: number;
1130
+ dismissed: number;
1131
+ }
1132
+ interface AdoptionMetrics {
1133
+ getAdoptionRate: (featureId: string) => number;
1134
+ getTourCompletionRate: (tourId: string) => number;
1135
+ getChecklistCompletionRate: (checklistId: string) => number;
1136
+ getFeatureEngagement: (featureId: string) => FeatureEngagementMetrics;
1137
+ getVariantPerformance: (featureId: string) => Record<string, number>;
1138
+ }
1139
+ declare function createAdoptionMetrics(events: AdoptionEvent[]): AdoptionMetrics;
1140
+
1141
+ declare function resolveDependencyOrder(manifest: FeatureManifest): string[];
1142
+ declare function hasDependencyCycle(manifest: FeatureManifest): boolean;
1143
+ declare function sortFeaturesByDependencies(features: FeatureEntry[]): FeatureEntry[];
1144
+
1145
+ declare function isTriggerMatch(trigger: FeatureTrigger | undefined, context?: TriggerContext): boolean;
1146
+ declare class TriggerEngine {
1147
+ private context;
1148
+ constructor(initial?: TriggerContext);
1149
+ setPath(path: string): void;
1150
+ trackEvent(event: string): void;
1151
+ trackUsage(event: string, delta?: number): void;
1152
+ trackMilestone(event: string): void;
1153
+ setElapsedMs(elapsedMs: number): void;
1154
+ setScrollPercent(scrollPercent: number): void;
1155
+ setMetadata(next: Record<string, unknown>): void;
1156
+ getContext(): TriggerContext;
1157
+ evaluate(trigger: FeatureTrigger | undefined): boolean;
1158
+ evaluateFeature(feature: Pick<FeatureEntry, "trigger">): boolean;
1159
+ }
1160
+
1161
+ declare function getFeatureVariantName(feature: FeatureEntry): string | undefined;
1162
+ declare function applyFeatureVariant(feature: FeatureEntry, variantKey: string): FeatureEntry;
1163
+ declare function applyFeatureVariants(manifest: FeatureManifest, variantKey: string): FeatureEntry[];
1164
+ declare function getOrCreateVariantKey(explicitKey?: string): string;
1165
+
1166
+ interface ManifestStats {
1167
+ total: number;
1168
+ byType: Record<string, number>;
1169
+ byCategory: Record<string, number>;
1170
+ newestRelease: string | null;
1171
+ oldestRelease: string | null;
1172
+ }
1173
+ declare function computeManifestStats(entries: FeatureEntry[]): ManifestStats;
1174
+ declare function generateMarkdownChangelog(entries: FeatureEntry[]): string;
1175
+ interface DoctorReport {
1176
+ checks: string[];
1177
+ warnings: string[];
1178
+ errors: string[];
1179
+ }
1180
+ declare function runDoctor(entries: FeatureEntry[], now?: Date): DoctorReport;
121
1181
 
122
1182
  interface LocalStorageAdapterOptions {
123
1183
  /** Key prefix for localStorage entries. Default: "featuredrop" */
@@ -149,6 +1209,65 @@ declare class LocalStorageAdapter implements StorageAdapter {
149
1209
  dismissAll(now: Date): Promise<void>;
150
1210
  }
151
1211
 
1212
+ interface IndexedDBAdapterOptions {
1213
+ prefix?: string;
1214
+ watermark?: string | null;
1215
+ dbName?: string;
1216
+ storeName?: string;
1217
+ onDismissAll?: (now: Date) => Promise<void>;
1218
+ /** Optional remote state fetch for offline-first sync reconciliation. */
1219
+ onSyncState?: () => Promise<{
1220
+ watermark?: string | null;
1221
+ dismissedIds?: string[];
1222
+ }>;
1223
+ /** Optional remote flush for queued single-dismiss operations. */
1224
+ onFlushDismissBatch?: (ids: string[]) => Promise<void>;
1225
+ /** Optional remote flush for queued dismiss-all operations. */
1226
+ onFlushDismissAll?: (watermark: string) => Promise<void>;
1227
+ /** Delay before queued operations are flushed. Default: 500ms. */
1228
+ flushDebounceMs?: number;
1229
+ /** Attach online/visibility listeners to trigger sync+flush. Default: true in browser. */
1230
+ autoSyncOnOnline?: boolean;
1231
+ }
1232
+ declare class IndexedDBAdapter implements StorageAdapter {
1233
+ private readonly prefix;
1234
+ private readonly dbName;
1235
+ private readonly storeName;
1236
+ private readonly onDismissAllCallback?;
1237
+ private readonly onSyncStateCallback?;
1238
+ private readonly onFlushDismissBatchCallback?;
1239
+ private readonly onFlushDismissAllCallback?;
1240
+ private readonly flushDebounceMs;
1241
+ private readonly autoSyncOnOnline;
1242
+ private watermark;
1243
+ private dismissed;
1244
+ private queue;
1245
+ private readonly hydratePromise;
1246
+ private flushTimer;
1247
+ private flushing;
1248
+ private readonly boundOnlineHandler;
1249
+ private readonly boundVisibilityHandler;
1250
+ constructor(options?: IndexedDBAdapterOptions);
1251
+ getWatermark(): string | null;
1252
+ getDismissedIds(): ReadonlySet<string>;
1253
+ dismiss(id: string): void;
1254
+ dismissAll(now: Date): Promise<void>;
1255
+ /** Flush queued dismiss operations to optional remote callbacks. */
1256
+ flushQueue(): Promise<void>;
1257
+ /** Merge local state with optional remote source, then flush queued writes. */
1258
+ syncFromRemote(): Promise<void>;
1259
+ /** Cleanup optional browser listeners. */
1260
+ destroy(): void;
1261
+ private persist;
1262
+ private hydrateFromIndexedDB;
1263
+ private readIndexedDBState;
1264
+ private writeIndexedDBState;
1265
+ private openDb;
1266
+ private scheduleFlush;
1267
+ private getLastDismissAll;
1268
+ private collectDismissBatch;
1269
+ }
1270
+
152
1271
  /**
153
1272
  * In-memory storage adapter.
154
1273
  *
@@ -169,4 +1288,392 @@ declare class MemoryAdapter implements StorageAdapter {
169
1288
  dismissAll(now: Date): Promise<void>;
170
1289
  }
171
1290
 
172
- export { type AnalyticsCallbacks, type FeatureCTA, type FeatureEntry, type FeatureManifest, type FeaturePriority, type FeatureType, LocalStorageAdapter, type LocalStorageAdapterOptions, MemoryAdapter, type StorageAdapter, createManifest, getFeatureById, getNewFeatureCount, getNewFeatures, getNewFeaturesByCategory, getNewFeaturesSorted, hasNewFeature, isNew };
1291
+ interface RemoteAdapterOptions {
1292
+ /** Base URL for the feature API (e.g. https://api.example.com/api/features) */
1293
+ url: string;
1294
+ /** Optional headers applied to all requests */
1295
+ headers?: Record<string, string>;
1296
+ /** Polling interval in ms for stale-while-revalidate (default: 5 minutes) */
1297
+ fetchInterval?: number;
1298
+ /** Data format, currently supports 'rest' (default) */
1299
+ format?: "rest";
1300
+ /** Optional user identifier to pass to state endpoint */
1301
+ userId?: string;
1302
+ /** Number of retries after the initial request (default: 3) */
1303
+ retryAttempts?: number;
1304
+ /** Base backoff delay used between retries (default: 250ms) */
1305
+ retryBaseDelayMs?: number;
1306
+ /** Consecutive failed operations before opening the circuit (default: 5) */
1307
+ circuitBreakerThreshold?: number;
1308
+ /** Cooldown period while the circuit is open (default: 60s) */
1309
+ circuitBreakerCooldownMs?: number;
1310
+ /** Optional sleep function override for test environments */
1311
+ sleep?: (delayMs: number) => Promise<void>;
1312
+ /** Optional timestamp function override for test environments */
1313
+ now?: () => number;
1314
+ }
1315
+ declare class RemoteAdapter implements StorageAdapter {
1316
+ private readonly baseUrl;
1317
+ private readonly headers;
1318
+ private readonly fetchInterval;
1319
+ private readonly userId?;
1320
+ private dismissedIds;
1321
+ private watermark;
1322
+ private lastManifest;
1323
+ private lastFetchTs;
1324
+ private readonly retryAttempts;
1325
+ private readonly retryBaseDelayMs;
1326
+ private readonly circuitBreakerThreshold;
1327
+ private readonly circuitBreakerCooldownMs;
1328
+ private readonly sleep;
1329
+ private readonly now;
1330
+ private consecutiveFailures;
1331
+ private circuitOpenUntil;
1332
+ constructor(options: RemoteAdapterOptions);
1333
+ /** Fetch manifest with stale-while-revalidate */
1334
+ fetchManifest(force?: boolean): Promise<FeatureManifest>;
1335
+ /** Fetch state (watermark + dismissed IDs) */
1336
+ syncState(): Promise<void>;
1337
+ getWatermark(): string | null;
1338
+ getDismissedIds(): ReadonlySet<string>;
1339
+ dismiss(id: string): void;
1340
+ dismissAll(now: Date): Promise<void>;
1341
+ /** Returns current adapter health; false while circuit breaker is open. */
1342
+ isHealthy(): Promise<boolean>;
1343
+ private flushDismiss;
1344
+ private flushDismissAll;
1345
+ private isCircuitOpen;
1346
+ private markFailure;
1347
+ private markSuccess;
1348
+ private withRetry;
1349
+ }
1350
+
1351
+ interface QueryResultRow {
1352
+ watermark?: string | null;
1353
+ dismissed_ids?: string[] | null;
1354
+ dismissedIds?: string[] | null;
1355
+ last_seen?: string | null;
1356
+ lastSeen?: string | null;
1357
+ }
1358
+ interface PostgresQueryResult<T = QueryResultRow> {
1359
+ rows: T[];
1360
+ rowCount?: number | null;
1361
+ }
1362
+ type PostgresQueryFn = <T = QueryResultRow>(sql: string, params?: unknown[]) => Promise<PostgresQueryResult<T>>;
1363
+ interface PostgresAdapterOptions {
1364
+ userId: string;
1365
+ query: PostgresQueryFn;
1366
+ tableName?: string;
1367
+ autoMigrate?: boolean;
1368
+ }
1369
+ /**
1370
+ * Postgres-backed storage adapter.
1371
+ *
1372
+ * This adapter is dependency-free by design and accepts a user-provided
1373
+ * query function, allowing integration with pg, drizzle, prisma, etc.
1374
+ */
1375
+ declare class PostgresAdapter implements ServerStorageAdapter {
1376
+ readonly userId: string;
1377
+ private readonly query;
1378
+ private readonly tableName;
1379
+ private readonly autoMigrate;
1380
+ private watermark;
1381
+ private dismissedIds;
1382
+ private initialized;
1383
+ constructor(options: PostgresAdapterOptions);
1384
+ getWatermark(): string | null;
1385
+ getDismissedIds(): ReadonlySet<string>;
1386
+ dismiss(id: string): void;
1387
+ dismissAll(now: Date): Promise<void>;
1388
+ sync(): Promise<void>;
1389
+ dismissBatch(ids: string[]): Promise<void>;
1390
+ resetUser(userId: string): Promise<void>;
1391
+ getBulkState(userIds: string[]): Promise<Map<string, DismissalState>>;
1392
+ isHealthy(): Promise<boolean>;
1393
+ destroy(): Promise<void>;
1394
+ private ensureReady;
1395
+ }
1396
+
1397
+ interface RedisLikePipeline {
1398
+ set(key: string, value: string): RedisLikePipeline;
1399
+ del(key: string): RedisLikePipeline;
1400
+ sadd(key: string, ...members: string[]): RedisLikePipeline;
1401
+ exec(): Promise<unknown>;
1402
+ }
1403
+ interface RedisLikeClient {
1404
+ get(key: string): Promise<string | null>;
1405
+ set(key: string, value: string): Promise<unknown>;
1406
+ del(key: string): Promise<unknown>;
1407
+ smembers(key: string): Promise<string[]>;
1408
+ sadd(key: string, ...members: string[]): Promise<unknown>;
1409
+ ping(): Promise<string>;
1410
+ multi(): RedisLikePipeline;
1411
+ quit?(): Promise<unknown>;
1412
+ disconnect?(): void;
1413
+ }
1414
+ interface RedisAdapterOptions {
1415
+ userId: string;
1416
+ client: RedisLikeClient;
1417
+ keyPrefix?: string;
1418
+ }
1419
+ /**
1420
+ * Redis-backed storage adapter.
1421
+ * Uses simple key + set structures to keep operations predictable.
1422
+ */
1423
+ declare class RedisAdapter implements ServerStorageAdapter {
1424
+ readonly userId: string;
1425
+ private readonly client;
1426
+ private readonly keyPrefix;
1427
+ private watermark;
1428
+ private dismissedIds;
1429
+ constructor(options: RedisAdapterOptions);
1430
+ getWatermark(): string | null;
1431
+ getDismissedIds(): ReadonlySet<string>;
1432
+ dismiss(id: string): void;
1433
+ dismissAll(now: Date): Promise<void>;
1434
+ sync(): Promise<void>;
1435
+ dismissBatch(ids: string[]): Promise<void>;
1436
+ resetUser(userId: string): Promise<void>;
1437
+ getBulkState(userIds: string[]): Promise<Map<string, DismissalState>>;
1438
+ isHealthy(): Promise<boolean>;
1439
+ destroy(): Promise<void>;
1440
+ private watermarkKey;
1441
+ private dismissedKey;
1442
+ private lastSeenKey;
1443
+ }
1444
+
1445
+ interface HybridAdapterOptions {
1446
+ local: StorageAdapter;
1447
+ remote: ServerStorageAdapter;
1448
+ /** If true, sync from remote before writes. Default false */
1449
+ syncBeforeWrite?: boolean;
1450
+ /** Batch window for queued dismiss writes. Default: 500ms */
1451
+ dismissBatchWindowMs?: number;
1452
+ /** Optional periodic sync interval in ms. Default: disabled (0) */
1453
+ syncIntervalMs?: number;
1454
+ /** Sync on browser visibility return. Default: true */
1455
+ syncOnVisibilityChange?: boolean;
1456
+ /** Sync on browser online event. Default: true */
1457
+ syncOnOnline?: boolean;
1458
+ }
1459
+ /**
1460
+ * Hybrid adapter that combines local immediacy with remote persistence.
1461
+ */
1462
+ declare class HybridAdapter implements ServerStorageAdapter {
1463
+ readonly userId: string;
1464
+ private readonly local;
1465
+ private readonly remote;
1466
+ private readonly syncBeforeWrite;
1467
+ private readonly dismissBatchWindowMs;
1468
+ private readonly syncIntervalMs;
1469
+ private readonly syncOnVisibilityChange;
1470
+ private readonly syncOnOnline;
1471
+ private pendingDismissIds;
1472
+ private dismissTimer;
1473
+ private syncTimer;
1474
+ private readonly boundVisibilityHandler;
1475
+ private readonly boundOnlineHandler;
1476
+ constructor(options: HybridAdapterOptions);
1477
+ getWatermark(): string | null;
1478
+ getDismissedIds(): ReadonlySet<string>;
1479
+ dismiss(id: string): void;
1480
+ dismissAll(now: Date): Promise<void>;
1481
+ sync(): Promise<void>;
1482
+ dismissBatch(ids: string[]): Promise<void>;
1483
+ resetUser(userId: string): Promise<void>;
1484
+ getBulkState(userIds: string[]): Promise<Map<string, DismissalState>>;
1485
+ isHealthy(): Promise<boolean>;
1486
+ destroy(): Promise<void>;
1487
+ /** Manually flush queued dismiss operations to the remote adapter. */
1488
+ flushPendingDismisses(): Promise<void>;
1489
+ private scheduleDismissFlush;
1490
+ }
1491
+
1492
+ interface MySQLRow {
1493
+ user_id?: string;
1494
+ watermark?: string | null;
1495
+ dismissed_ids?: string[] | string | null;
1496
+ dismissedIds?: string[] | string | null;
1497
+ last_seen?: string | null;
1498
+ lastSeen?: string | null;
1499
+ }
1500
+ interface MySQLQueryResult<T = MySQLRow> {
1501
+ rows: T[];
1502
+ }
1503
+ type MySQLQueryFn = <T = MySQLRow>(sql: string, params?: unknown[]) => Promise<MySQLQueryResult<T>>;
1504
+ interface MySQLAdapterOptions {
1505
+ userId: string;
1506
+ query: MySQLQueryFn;
1507
+ tableName?: string;
1508
+ autoMigrate?: boolean;
1509
+ }
1510
+ declare class MySQLAdapter implements ServerStorageAdapter {
1511
+ readonly userId: string;
1512
+ private readonly query;
1513
+ private readonly tableName;
1514
+ private readonly autoMigrate;
1515
+ private watermark;
1516
+ private dismissedIds;
1517
+ private initialized;
1518
+ constructor(options: MySQLAdapterOptions);
1519
+ getWatermark(): string | null;
1520
+ getDismissedIds(): ReadonlySet<string>;
1521
+ dismiss(id: string): void;
1522
+ dismissAll(now: Date): Promise<void>;
1523
+ sync(): Promise<void>;
1524
+ dismissBatch(ids: string[]): Promise<void>;
1525
+ resetUser(userId: string): Promise<void>;
1526
+ getBulkState(userIds: string[]): Promise<Map<string, DismissalState>>;
1527
+ isHealthy(): Promise<boolean>;
1528
+ destroy(): Promise<void>;
1529
+ private ensureReady;
1530
+ }
1531
+
1532
+ interface MongoStateDoc {
1533
+ userId: string;
1534
+ watermark?: string | null;
1535
+ dismissedIds?: string[];
1536
+ lastSeen?: string;
1537
+ }
1538
+ interface MongoFindCursor<T> {
1539
+ toArray: () => Promise<T[]>;
1540
+ }
1541
+ interface MongoLikeCollection {
1542
+ findOne: (filter: Record<string, unknown>) => Promise<MongoStateDoc | null>;
1543
+ updateOne: (filter: Record<string, unknown>, update: Record<string, unknown>, options?: Record<string, unknown>) => Promise<unknown>;
1544
+ deleteOne: (filter: Record<string, unknown>) => Promise<unknown>;
1545
+ find?: (filter: Record<string, unknown>) => MongoFindCursor<MongoStateDoc>;
1546
+ }
1547
+ interface MongoAdapterOptions {
1548
+ userId: string;
1549
+ collection: MongoLikeCollection;
1550
+ }
1551
+ declare class MongoAdapter implements ServerStorageAdapter {
1552
+ readonly userId: string;
1553
+ private readonly collection;
1554
+ private watermark;
1555
+ private dismissedIds;
1556
+ constructor(options: MongoAdapterOptions);
1557
+ getWatermark(): string | null;
1558
+ getDismissedIds(): ReadonlySet<string>;
1559
+ dismiss(id: string): void;
1560
+ dismissAll(now: Date): Promise<void>;
1561
+ sync(): Promise<void>;
1562
+ dismissBatch(ids: string[]): Promise<void>;
1563
+ resetUser(userId: string): Promise<void>;
1564
+ getBulkState(userIds: string[]): Promise<Map<string, DismissalState>>;
1565
+ isHealthy(): Promise<boolean>;
1566
+ destroy(): Promise<void>;
1567
+ }
1568
+
1569
+ interface SQLiteRow {
1570
+ user_id?: string;
1571
+ watermark?: string | null;
1572
+ dismissed_ids?: string[] | string | null;
1573
+ dismissedIds?: string[] | string | null;
1574
+ last_seen?: string | null;
1575
+ lastSeen?: string | null;
1576
+ }
1577
+ interface SQLiteQueryResult<T = SQLiteRow> {
1578
+ rows: T[];
1579
+ }
1580
+ type SQLiteQueryFn = <T = SQLiteRow>(sql: string, params?: unknown[]) => Promise<SQLiteQueryResult<T>>;
1581
+ interface SQLiteAdapterOptions {
1582
+ userId: string;
1583
+ query: SQLiteQueryFn;
1584
+ tableName?: string;
1585
+ autoMigrate?: boolean;
1586
+ }
1587
+ declare class SQLiteAdapter implements ServerStorageAdapter {
1588
+ readonly userId: string;
1589
+ private readonly query;
1590
+ private readonly tableName;
1591
+ private readonly autoMigrate;
1592
+ private watermark;
1593
+ private dismissedIds;
1594
+ private initialized;
1595
+ constructor(options: SQLiteAdapterOptions);
1596
+ getWatermark(): string | null;
1597
+ getDismissedIds(): ReadonlySet<string>;
1598
+ dismiss(id: string): void;
1599
+ dismissAll(now: Date): Promise<void>;
1600
+ sync(): Promise<void>;
1601
+ dismissBatch(ids: string[]): Promise<void>;
1602
+ resetUser(userId: string): Promise<void>;
1603
+ getBulkState(userIds: string[]): Promise<Map<string, DismissalState>>;
1604
+ isHealthy(): Promise<boolean>;
1605
+ destroy(): Promise<void>;
1606
+ private ensureReady;
1607
+ }
1608
+
1609
+ interface SupabaseErrorLike {
1610
+ message?: string;
1611
+ }
1612
+ interface SupabaseMaybeSingleResult<T> {
1613
+ data: T | null;
1614
+ error: SupabaseErrorLike | null;
1615
+ }
1616
+ interface SupabaseMutationResult {
1617
+ error: SupabaseErrorLike | null;
1618
+ }
1619
+ interface SupabaseSelectQuery<T> {
1620
+ eq: (column: string, value: unknown) => SupabaseSelectQuery<T>;
1621
+ maybeSingle: () => Promise<SupabaseMaybeSingleResult<T>>;
1622
+ }
1623
+ interface SupabaseMutationQuery {
1624
+ eq: (column: string, value: unknown) => Promise<SupabaseMutationResult>;
1625
+ }
1626
+ interface SupabaseTableClient<T> {
1627
+ select: (columns: string) => SupabaseSelectQuery<T>;
1628
+ upsert: (value: Record<string, unknown>) => Promise<SupabaseMutationResult>;
1629
+ update: (value: Record<string, unknown>) => SupabaseMutationQuery;
1630
+ delete: () => SupabaseMutationQuery;
1631
+ }
1632
+ interface SupabaseRealtimeChannelLike {
1633
+ on: (event: "postgres_changes", filter: Record<string, unknown>, callback: () => void) => SupabaseRealtimeChannelLike;
1634
+ subscribe: (callback?: (status: string) => void) => SupabaseRealtimeChannelLike;
1635
+ }
1636
+ interface SupabaseClientLike {
1637
+ from: <T = SupabaseStateRow>(table: string) => SupabaseTableClient<T>;
1638
+ channel?: (name: string) => SupabaseRealtimeChannelLike;
1639
+ removeChannel?: (channel: SupabaseRealtimeChannelLike) => void | Promise<void>;
1640
+ }
1641
+ interface SupabaseStateRow {
1642
+ [key: string]: unknown;
1643
+ user_id: string;
1644
+ watermark?: string | null;
1645
+ dismissed_ids?: string[] | null;
1646
+ last_seen?: string | null;
1647
+ }
1648
+ interface SupabaseAdapterOptions {
1649
+ userId: string;
1650
+ client: SupabaseClientLike;
1651
+ tableName?: string;
1652
+ realtime?: boolean;
1653
+ }
1654
+ declare class SupabaseAdapter implements ServerStorageAdapter {
1655
+ readonly userId: string;
1656
+ private readonly client;
1657
+ private readonly tableName;
1658
+ private readonly realtime;
1659
+ private watermark;
1660
+ private dismissedIds;
1661
+ private realtimeChannel;
1662
+ private syncing;
1663
+ constructor(options: SupabaseAdapterOptions);
1664
+ getWatermark(): string | null;
1665
+ getDismissedIds(): ReadonlySet<string>;
1666
+ dismiss(id: string): void;
1667
+ dismissAll(now: Date): Promise<void>;
1668
+ sync(): Promise<void>;
1669
+ dismissBatch(ids: string[]): Promise<void>;
1670
+ resetUser(userId: string): Promise<void>;
1671
+ getBulkState(userIds: string[]): Promise<Map<string, DismissalState>>;
1672
+ isHealthy(): Promise<boolean>;
1673
+ destroy(): Promise<void>;
1674
+ private fetchState;
1675
+ private upsertState;
1676
+ private setupRealtime;
1677
+ }
1678
+
1679
+ export { type AdoptionEvent, type AdoptionEventInput, type AdoptionEventType, type AdoptionMetrics, AmplitudeAdapter, type AnalyticsAdapter, type AnalyticsCallbacks, AnalyticsCollector, type AnalyticsCollectorOptions, AudienceBuilder, type AudienceBuilderProps, type AudienceMatchFn, type AudienceRule, type CMSAdapter, type CMSFieldMapping, type ChangedFeature, type ChangelogDiffOptions, type ChangelogRenderer, type ChangelogRendererActions, type ChangelogRendererComputed, type ChangelogRendererOptions, type ChangelogRendererState, ContentfulAdapter, type CreateFlagBridgeOptions, CustomAdapter, DiscordBridge, type DiscordBridgeOptions, type DismissalState, EmailDigestGenerator, type EmailDigestGeneratorOptions, FEATUREDROP_ANIMATION_PRESETS, FEATUREDROP_THEMES, FEATUREDROP_TRANSLATIONS, type FeatureCTA, type FeatureDependencies, type FeatureDependencyState, type FeatureDropAnimationPreset, type FeatureDropTheme, type FeatureDropThemeInput, type FeatureDropThemeOverrides, type FeatureDropThemePreset, type FeatureDropTranslations, type FeatureEngagementMetrics, type FeatureEntry, type FeatureFlagBridge, type FeatureManifest, type FeaturePriority, type FeatureTrigger, type FeatureType, type FeatureVariant, HybridAdapter, type HybridAdapterOptions, IndexedDBAdapter, type IndexedDBAdapterOptions, LaunchDarklyBridge, type LaunchDarklyBridgeOptions, type LaunchDarklyClientLike, LocalStorageAdapter, type LocalStorageAdapterOptions, type ManifestDiff, ManifestEditor, type ManifestEditorProps, MarkdownAdapter, MemoryAdapter, MixpanelAdapter, MongoAdapter, type MongoAdapterOptions, type MongoLikeCollection, MySQLAdapter, type MySQLAdapterOptions, type MySQLQueryFn, type MySQLQueryResult, NotionAdapter, PostHogAdapter, PostHogBridge, type PostHogBridgeOptions, type PostHogClientLike, PostgresAdapter, type PostgresAdapterOptions, type PostgresQueryFn, type PostgresQueryResult, PreviewPanel, type PreviewPanelProps, RSSFeedGenerator, type RSSFeedGeneratorOptions, RedisAdapter, type RedisAdapterOptions, type RedisLikeClient, type RedisLikePipeline, RemoteAdapter, type RemoteAdapterOptions, SQLiteAdapter, type SQLiteAdapterOptions, type SQLiteQueryFn, type SQLiteQueryResult, SanityAdapter, ScheduleCalendar, type ScheduleCalendarProps, SegmentAdapter, type ServerStorageAdapter, SlackBridge, type SlackBridgeOptions, type StorageAdapter, StrapiAdapter, SupabaseAdapter, type SupabaseAdapterOptions, type SupabaseClientLike, type SupabaseRealtimeChannelLike, type ThrottleOptions, type ThrottleResult, type ThrottleRuntimeState, type TriggerContext, TriggerEngine, type UserContext, type ValidationIssue, type ValidationResult, WebhookBridge, type WebhookBridgeOptions, applyAnnouncementThrottle, applyFeatureVariant, applyFeatureVariants, computeManifestStats, createAdoptionMetrics, createChangelogRenderer, createFlagBridge, createManifest, createTheme, diffManifest, featureEntryJsonSchema, featureEntrySchema, featureManifestJsonSchema, featureManifestSchema, formatDateForLocale, formatRelativeTimeForLocale, generateChangelogDiff, generateMarkdownChangelog, generateRSS, getAnimationDurationMs, getEnterAnimation, getExitAnimation, getFeatureById, getFeatureVariantName, getLocaleDirection, getNewFeatureCount, getNewFeatures, getNewFeaturesByCategory, getNewFeaturesSorted, getOrCreateVariantKey, getPulseAnimation, hasDependencyCycle, hasNewFeature, isNew, isTriggerMatch, matchesAudience, parseDescription, resolveAnimationPreset, resolveDependencyOrder, resolveLocale, resolveTheme, resolveTranslations, runDoctor, sortFeaturesByDependencies, themeToCSSVariables, validateManifest, validateManifestForCI };