featuredrop 1.0.1 → 1.2.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 (60) hide show
  1. package/README.md +626 -186
  2. package/dist/angular.cjs +286 -0
  3. package/dist/angular.cjs.map +1 -0
  4. package/dist/angular.d.cts +229 -0
  5. package/dist/angular.d.ts +229 -0
  6. package/dist/angular.js +283 -0
  7. package/dist/angular.js.map +1 -0
  8. package/dist/featuredrop.cjs +1256 -0
  9. package/dist/featuredrop.cjs.map +1 -0
  10. package/dist/index.cjs +2769 -9
  11. package/dist/index.cjs.map +1 -1
  12. package/dist/index.d.cts +1020 -9
  13. package/dist/index.d.ts +1020 -9
  14. package/dist/index.js +2726 -10
  15. package/dist/index.js.map +1 -1
  16. package/dist/preact.cjs +7289 -0
  17. package/dist/preact.cjs.map +1 -0
  18. package/dist/preact.d.cts +1266 -0
  19. package/dist/preact.d.ts +1266 -0
  20. package/dist/preact.js +7259 -0
  21. package/dist/preact.js.map +1 -0
  22. package/dist/react.cjs +7142 -49
  23. package/dist/react.cjs.map +1 -1
  24. package/dist/react.d.cts +1119 -7
  25. package/dist/react.d.ts +1119 -7
  26. package/dist/react.js +7122 -52
  27. package/dist/react.js.map +1 -1
  28. package/dist/schema.cjs +215 -0
  29. package/dist/schema.cjs.map +1 -0
  30. package/dist/schema.d.cts +203 -0
  31. package/dist/schema.d.ts +203 -0
  32. package/dist/schema.js +209 -0
  33. package/dist/schema.js.map +1 -0
  34. package/dist/solid.cjs +373 -0
  35. package/dist/solid.cjs.map +1 -0
  36. package/dist/solid.d.cts +242 -0
  37. package/dist/solid.d.ts +242 -0
  38. package/dist/solid.js +366 -0
  39. package/dist/solid.js.map +1 -0
  40. package/dist/svelte.cjs +329 -0
  41. package/dist/svelte.cjs.map +1 -0
  42. package/dist/svelte.js +324 -0
  43. package/dist/svelte.js.map +1 -0
  44. package/dist/testing.cjs +1422 -0
  45. package/dist/testing.cjs.map +1 -0
  46. package/dist/testing.d.cts +339 -0
  47. package/dist/testing.d.ts +339 -0
  48. package/dist/testing.js +1415 -0
  49. package/dist/testing.js.map +1 -0
  50. package/dist/vue.cjs +1084 -0
  51. package/dist/vue.cjs.map +1 -0
  52. package/dist/vue.js +1072 -0
  53. package/dist/vue.js.map +1 -0
  54. package/dist/web-components.cjs +483 -0
  55. package/dist/web-components.cjs.map +1 -0
  56. package/dist/web-components.d.cts +211 -0
  57. package/dist/web-components.d.ts +211 -0
  58. package/dist/web-components.js +477 -0
  59. package/dist/web-components.js.map +1 -0
  60. package/package.json +126 -3
@@ -0,0 +1,339 @@
1
+ import { ReactNode, ReactElement } from 'react';
2
+
3
+ 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";
4
+ interface AdoptionEvent {
5
+ type: AdoptionEventType;
6
+ featureId?: string;
7
+ tourId?: string;
8
+ variant?: string;
9
+ timestamp: string;
10
+ sessionId?: string;
11
+ userId?: string;
12
+ metadata?: Record<string, unknown>;
13
+ }
14
+ type AdoptionEventInput = Omit<AdoptionEvent, "timestamp"> & {
15
+ timestamp?: string;
16
+ };
17
+ interface AnalyticsAdapter {
18
+ track: (event: AdoptionEvent) => void | Promise<void>;
19
+ trackBatch?: (events: AdoptionEvent[]) => void | Promise<void>;
20
+ }
21
+ interface AnalyticsCollectorOptions {
22
+ adapter: AnalyticsAdapter;
23
+ batchSize?: number;
24
+ flushInterval?: number;
25
+ sampleRate?: number;
26
+ enabled?: boolean;
27
+ sessionId?: string;
28
+ userId?: string;
29
+ now?: () => Date;
30
+ random?: () => number;
31
+ }
32
+ declare class AnalyticsCollector {
33
+ private adapter;
34
+ private queue;
35
+ private batchSize;
36
+ private flushInterval;
37
+ private sampleRate;
38
+ private enabled;
39
+ private now;
40
+ private random;
41
+ private sessionId?;
42
+ private userId?;
43
+ private timer;
44
+ private flushing;
45
+ constructor(options: AnalyticsCollectorOptions);
46
+ setEnabled(enabled: boolean): void;
47
+ setContext(context: {
48
+ sessionId?: string;
49
+ userId?: string;
50
+ }): void;
51
+ getQueueSize(): number;
52
+ track(event: AdoptionEventInput): void;
53
+ flush(): Promise<void>;
54
+ destroy(): Promise<void>;
55
+ private startTimer;
56
+ }
57
+
58
+ /** Entry type label — determines default icon/color in UI */
59
+ type FeatureType = "feature" | "improvement" | "fix" | "breaking";
60
+ /** Priority level for announcements */
61
+ type FeaturePriority = "critical" | "normal" | "low";
62
+ /** Call-to-action for a feature entry */
63
+ interface FeatureCTA {
64
+ /** Button/link label */
65
+ label: string;
66
+ /** URL to navigate to */
67
+ url: string;
68
+ }
69
+ /** Variant-level overrides for A/B announcement testing */
70
+ interface FeatureVariant {
71
+ /** Optional variant-specific label override */
72
+ label?: string;
73
+ /** Optional variant-specific description override */
74
+ description?: string;
75
+ /** Optional variant-specific image override */
76
+ image?: string;
77
+ /** Optional variant-specific CTA override */
78
+ cta?: FeatureCTA;
79
+ /** Optional variant-specific metadata overrides */
80
+ meta?: Record<string, unknown>;
81
+ }
82
+ /** Audience targeting rule — determines which user segments see a feature */
83
+ interface AudienceRule {
84
+ /** Plans that should see this feature (e.g. ["pro", "enterprise"]) */
85
+ plan?: string[];
86
+ /** Roles that should see this feature (e.g. ["admin", "editor"]) */
87
+ role?: string[];
88
+ /** Regions that should see this feature (e.g. ["us", "eu"]) */
89
+ region?: string[];
90
+ /** Arbitrary key-value pairs for custom matching logic */
91
+ custom?: Record<string, unknown>;
92
+ }
93
+ /** User context for audience targeting */
94
+ interface UserContext {
95
+ /** Current user's plan (e.g. "pro", "free") */
96
+ plan?: string;
97
+ /** Current user's role (e.g. "admin", "viewer") */
98
+ role?: string;
99
+ /** Current user's region (e.g. "us", "eu") */
100
+ region?: string;
101
+ /** Arbitrary traits for custom matching logic */
102
+ traits?: Record<string, unknown>;
103
+ }
104
+ /** Custom audience matcher function */
105
+ type AudienceMatchFn = (audience: AudienceRule, userContext: UserContext) => boolean;
106
+ /** Dependency gates for progressive feature discovery */
107
+ interface FeatureDependencies {
108
+ /** Features the user must have seen before this one can surface */
109
+ seen?: string[];
110
+ /** Features the user must have clicked before this one can surface */
111
+ clicked?: string[];
112
+ /** Features the user must have dismissed before this one can surface */
113
+ dismissed?: string[];
114
+ }
115
+ /** Runtime context used by trigger evaluation */
116
+ interface TriggerContext {
117
+ /** Current app route/path */
118
+ path?: string;
119
+ /** Named events observed in this session */
120
+ events?: ReadonlySet<string>;
121
+ /** Named milestone flags reached in this session */
122
+ milestones?: ReadonlySet<string>;
123
+ /** Usage counters keyed by event/pattern name */
124
+ usage?: Record<string, number>;
125
+ /** Session elapsed time in milliseconds */
126
+ elapsedMs?: number;
127
+ /** Scroll completion percentage (0-100) */
128
+ scrollPercent?: number;
129
+ /** Optional additional trigger context */
130
+ metadata?: Record<string, unknown>;
131
+ }
132
+ type FeatureTrigger = {
133
+ type: "page";
134
+ match: string | RegExp;
135
+ } | {
136
+ type: "usage";
137
+ event: string;
138
+ minActions?: number;
139
+ } | {
140
+ type: "time";
141
+ minSeconds: number;
142
+ } | {
143
+ type: "milestone";
144
+ event: string;
145
+ } | {
146
+ type: "frustration";
147
+ pattern: string;
148
+ threshold?: number;
149
+ } | {
150
+ type: "scroll";
151
+ minPercent?: number;
152
+ } | {
153
+ type: "custom";
154
+ evaluate: (context: TriggerContext) => boolean;
155
+ };
156
+ /** A single feature entry in the manifest */
157
+ interface FeatureEntry {
158
+ /** Unique identifier for the feature */
159
+ id: string;
160
+ /** Human-readable label (e.g. "Decision Journal") */
161
+ label: string;
162
+ /** Optional longer description (supports markdown in UI components) */
163
+ description?: string;
164
+ /**
165
+ * Semantic version targeting.
166
+ * If provided as an object, requires `appVersion` to be supplied to the provider/helpers.
167
+ * - introduced: earliest app version that includes this feature
168
+ * - showNewUntil: stop showing "new" once appVersion reaches this
169
+ * - deprecatedAt: hide feature for app versions at or above this (optional safety)
170
+ * - showIn: range string, e.g. ">=2.5.0 <3.0.0"
171
+ */
172
+ version?: string | {
173
+ introduced?: string;
174
+ showNewUntil?: string;
175
+ deprecatedAt?: string;
176
+ showIn?: string;
177
+ };
178
+ /** ISO date when this feature was released */
179
+ releasedAt: string;
180
+ /** ISO date after which the "new" badge should stop showing */
181
+ showNewUntil: string;
182
+ /** Optional key to match navigation items (e.g. "/journal", "settings") */
183
+ sidebarKey?: string;
184
+ /** Optional grouping category (e.g. "ai", "billing", "core") */
185
+ category?: string;
186
+ /** Optional URL to link to (e.g. docs page, changelog entry) */
187
+ url?: string;
188
+ /** Entry type — determines default icon/color in UI components */
189
+ type?: FeatureType;
190
+ /** Priority level — critical entries get special treatment in UI */
191
+ priority?: FeaturePriority;
192
+ /** Optional image/screenshot URL */
193
+ image?: string;
194
+ /** Optional call-to-action button */
195
+ cta?: FeatureCTA;
196
+ /** ISO date — entry is hidden until this date (scheduled publishing) */
197
+ publishAt?: string;
198
+ /** Optional arbitrary metadata */
199
+ meta?: Record<string, unknown>;
200
+ /** A/B variants keyed by variant name (e.g. control, treatment_a) */
201
+ variants?: Record<string, FeatureVariant>;
202
+ /** Percentage split per variant (same order as variants object keys) */
203
+ variantSplit?: number[];
204
+ /** Audience targeting — if set, only matching users see this feature */
205
+ audience?: AudienceRule;
206
+ /** Dependency requirements (progressive disclosure sequencing) */
207
+ dependsOn?: FeatureDependencies;
208
+ /** Contextual trigger rule */
209
+ trigger?: FeatureTrigger;
210
+ }
211
+ /** The full feature manifest — an array of feature entries */
212
+ type FeatureManifest = readonly FeatureEntry[];
213
+ /**
214
+ * Storage adapter interface — implement for your persistence layer.
215
+ *
216
+ * The adapter bridges two data sources:
217
+ * - **Watermark**: a server-side timestamp ("features seen at")
218
+ * - **Dismissed IDs**: client-side per-feature dismissals
219
+ */
220
+ interface StorageAdapter {
221
+ /** Get the user's "features seen at" watermark (ISO string or null) */
222
+ getWatermark(): string | null;
223
+ /** Get the set of individually dismissed feature IDs */
224
+ getDismissedIds(): ReadonlySet<string>;
225
+ /** Dismiss a single feature by ID */
226
+ dismiss(id: string): void;
227
+ /** Dismiss all features — sets watermark to `now` and clears dismissals */
228
+ dismissAll(now: Date): Promise<void>;
229
+ }
230
+ /** Analytics event callbacks — pipe to your analytics provider */
231
+ interface AnalyticsCallbacks {
232
+ /** Fired when a feature badge becomes visible to the user */
233
+ onFeatureSeen?: (feature: FeatureEntry) => void;
234
+ /** Fired when a user dismisses a single feature */
235
+ onFeatureDismissed?: (feature: FeatureEntry) => void;
236
+ /** Fired when a user clicks a feature link or CTA */
237
+ onFeatureClicked?: (feature: FeatureEntry) => void;
238
+ /** Fired when the changelog widget is opened */
239
+ onWidgetOpened?: () => void;
240
+ /** Fired when the changelog widget is closed */
241
+ onWidgetClosed?: () => void;
242
+ /** Fired when all features are dismissed at once */
243
+ onAllDismissed?: () => void;
244
+ }
245
+
246
+ interface ThrottleOptions {
247
+ maxSimultaneousBadges?: number;
248
+ maxSimultaneousSpotlights?: number;
249
+ maxToastsPerSession?: number;
250
+ minTimeBetweenModals?: number;
251
+ minTimeBetweenTours?: number;
252
+ sessionCooldown?: number;
253
+ respectDoNotDisturb?: boolean;
254
+ }
255
+
256
+ interface FeatureDropTranslations {
257
+ newBadge: string;
258
+ whatsNewTitle: string;
259
+ markAllRead: string;
260
+ allCaughtUp: string;
261
+ close: string;
262
+ changelogTitle: string;
263
+ searchPlaceholder: string;
264
+ allCategories: string;
265
+ noUpdatesYet: string;
266
+ loadMore: string;
267
+ share: string;
268
+ skipToEntries: string;
269
+ stepOf: (current: number, total: number) => string;
270
+ back: string;
271
+ next: string;
272
+ skip: string;
273
+ finish: string;
274
+ gotIt: string;
275
+ announcement: string;
276
+ feedbackTitle: string;
277
+ feedbackTrigger: string;
278
+ feedbackSubmitted: string;
279
+ submit: string;
280
+ cancel: string;
281
+ askLater: string;
282
+ }
283
+
284
+ interface FeatureDropProviderProps {
285
+ /** The feature manifest — typically a frozen array of FeatureEntry objects */
286
+ manifest: FeatureManifest;
287
+ /** Storage adapter instance (e.g. LocalStorageAdapter, MemoryAdapter) */
288
+ storage: StorageAdapter;
289
+ /** Optional analytics callbacks — pipe to your analytics provider */
290
+ analytics?: AnalyticsCallbacks;
291
+ /** User context for audience targeting (plan, role, region, traits) */
292
+ userContext?: UserContext;
293
+ /** Custom audience matcher — overrides default AND/OR matching logic */
294
+ matchAudience?: AudienceMatchFn;
295
+ /** Current app version (semver) for version-pinned features */
296
+ appVersion?: string;
297
+ /** Announcement throttling and session cooldown controls */
298
+ throttle?: ThrottleOptions;
299
+ /** Stable identifier for A/B variant assignment (e.g. userId) */
300
+ variantKey?: string;
301
+ /** Optional adoption analytics collector */
302
+ collector?: AnalyticsCollector;
303
+ /** Locale code for built-in component translations (e.g. "en", "fr", "es") */
304
+ locale?: string;
305
+ /** Custom translation overrides for built-in component strings */
306
+ translations?: Partial<FeatureDropTranslations>;
307
+ children: ReactNode;
308
+ }
309
+
310
+ type MockFeatureInput = Partial<FeatureEntry> & {
311
+ id?: string;
312
+ label?: string;
313
+ releasedAt?: string;
314
+ showNewUntil?: string;
315
+ type?: FeatureType;
316
+ };
317
+ declare function createMockManifest(entries: MockFeatureInput[], now?: Date): FeatureManifest;
318
+ interface MockStorage extends StorageAdapter {
319
+ setWatermark: (watermark: string | null) => void;
320
+ setDismissedIds: (ids: string[]) => void;
321
+ reset: () => void;
322
+ }
323
+ declare function createMockStorage(initial?: {
324
+ watermark?: string | null;
325
+ dismissedIds?: string[];
326
+ }): MockStorage;
327
+ declare function createTestProvider(props: Omit<FeatureDropProviderProps, "children">): ({ children }: {
328
+ children?: ReactNode;
329
+ }) => ReactElement;
330
+ declare function advanceTime(ms: number): void;
331
+ declare function setMockTime(now: number | Date): void;
332
+ type MockAnalyticsCollectorOptions = Omit<AnalyticsCollectorOptions, "adapter">;
333
+ declare class MockAnalyticsCollector extends AnalyticsCollector {
334
+ readonly events: AdoptionEvent[];
335
+ constructor(options?: MockAnalyticsCollectorOptions);
336
+ clear(): void;
337
+ }
338
+
339
+ export { MockAnalyticsCollector, type MockAnalyticsCollectorOptions, type MockFeatureInput, type MockStorage, advanceTime, createMockManifest, createMockStorage, createTestProvider, setMockTime };