featuredrop 2.6.1 → 2.7.1

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.
@@ -252,6 +252,94 @@ interface AnalyticsCallbacks {
252
252
  /** Fired when all features are dismissed at once */
253
253
  onAllDismissed?: () => void;
254
254
  }
255
+ /** Display format hint from the engine */
256
+ type DisplayFormat = "badge" | "toast" | "modal" | "banner" | "inline" | "spotlight";
257
+ /** Interaction type tracked by the engine */
258
+ type InteractionType = "seen" | "dismissed" | "clicked" | "completed" | "snoozed" | "hovered" | "expanded";
259
+ /** Timing decision returned by the engine */
260
+ interface TimingDecision {
261
+ /** Whether to show the announcement now */
262
+ show: boolean;
263
+ /** Reason for the decision */
264
+ reason: string;
265
+ /** Suggested delay in ms if not showing now */
266
+ delayMs?: number;
267
+ /** Confidence level (0-1) */
268
+ confidence: number;
269
+ }
270
+ /** Format recommendation from the engine */
271
+ interface FormatRecommendation {
272
+ /** Recommended display format */
273
+ primary: DisplayFormat;
274
+ /** Fallback format if primary component isn't used */
275
+ fallback: DisplayFormat;
276
+ /** Reason for the recommendation */
277
+ reason: string;
278
+ }
279
+ /** Adoption score breakdown */
280
+ interface AdoptionScore {
281
+ /** Overall score (0-100) */
282
+ score: number;
283
+ /** Letter grade */
284
+ grade: "A" | "B" | "C" | "D" | "F";
285
+ /** Score breakdown */
286
+ breakdown: {
287
+ /** % of features the user has explored */
288
+ featuresExplored: number;
289
+ /** Rate of dismissals (lower is better) */
290
+ dismissRate: number;
291
+ /** Rate of tour/checklist completion */
292
+ completionRate: number;
293
+ /** Whether engagement is rising, stable, or declining */
294
+ engagementTrend: "rising" | "stable" | "declining";
295
+ };
296
+ /** Actionable recommendations */
297
+ recommendations: string[];
298
+ }
299
+ /** Per-feature adoption status */
300
+ interface FeatureAdoptionStatus {
301
+ featureId: string;
302
+ status: "unseen" | "seen" | "explored" | "adopted" | "dismissed";
303
+ firstSeen?: string;
304
+ lastInteraction?: string;
305
+ interactionCount: number;
306
+ }
307
+ /** Delivery context passed to the engine for timing decisions */
308
+ interface DeliveryContext {
309
+ /** Current route/path */
310
+ currentPath: string;
311
+ /** Seconds since session start */
312
+ sessionAge: number;
313
+ /** Dismissals in last 5 minutes */
314
+ recentDismissals: number;
315
+ /** Feature priority */
316
+ featurePriority: FeaturePriority;
317
+ }
318
+ /**
319
+ * Plugin interface for the FeatureDrop engine.
320
+ *
321
+ * The open-source library defines this interface.
322
+ * The proprietary @featuredrop/engine implements it.
323
+ * Users can also build their own engine implementation.
324
+ *
325
+ * The free library works perfectly without any engine.
326
+ */
327
+ interface FeatureDropEngine {
328
+ /** Decide whether to show a feature announcement now */
329
+ shouldShow(featureId: string, context: DeliveryContext): TimingDecision;
330
+ /** Recommend the best display format for a feature */
331
+ recommendFormat(featureId: string): FormatRecommendation;
332
+ /** Get the user's overall adoption score */
333
+ getAdoptionScore(): AdoptionScore;
334
+ /** Track a user interaction with a feature */
335
+ trackInteraction(featureId: string, type: InteractionType): void;
336
+ /** Get adoption status for a specific feature */
337
+ getFeatureAdoption(featureId: string): FeatureAdoptionStatus;
338
+ /** Initialize the engine (called by provider on mount) */
339
+ initialize?(): void;
340
+ /** Cleanup resources (called by provider on unmount) */
341
+ destroy?(): void;
342
+ }
255
343
 
256
344
  interface ThrottleOptions {
257
345
  maxSimultaneousBadges?: number;
@@ -326,6 +414,8 @@ interface FeatureDropProviderProps {
326
414
  animation?: FeatureDropAnimationPreset;
327
415
  /** Custom translation overrides for built-in component strings */
328
416
  translations?: Partial<FeatureDropTranslations>;
417
+ /** Optional engine for AI-powered delivery intelligence (@featuredrop/engine) */
418
+ engine?: FeatureDropEngine;
329
419
  children: ReactNode;
330
420
  }
331
421
 
package/dist/testing.d.ts CHANGED
@@ -252,6 +252,94 @@ interface AnalyticsCallbacks {
252
252
  /** Fired when all features are dismissed at once */
253
253
  onAllDismissed?: () => void;
254
254
  }
255
+ /** Display format hint from the engine */
256
+ type DisplayFormat = "badge" | "toast" | "modal" | "banner" | "inline" | "spotlight";
257
+ /** Interaction type tracked by the engine */
258
+ type InteractionType = "seen" | "dismissed" | "clicked" | "completed" | "snoozed" | "hovered" | "expanded";
259
+ /** Timing decision returned by the engine */
260
+ interface TimingDecision {
261
+ /** Whether to show the announcement now */
262
+ show: boolean;
263
+ /** Reason for the decision */
264
+ reason: string;
265
+ /** Suggested delay in ms if not showing now */
266
+ delayMs?: number;
267
+ /** Confidence level (0-1) */
268
+ confidence: number;
269
+ }
270
+ /** Format recommendation from the engine */
271
+ interface FormatRecommendation {
272
+ /** Recommended display format */
273
+ primary: DisplayFormat;
274
+ /** Fallback format if primary component isn't used */
275
+ fallback: DisplayFormat;
276
+ /** Reason for the recommendation */
277
+ reason: string;
278
+ }
279
+ /** Adoption score breakdown */
280
+ interface AdoptionScore {
281
+ /** Overall score (0-100) */
282
+ score: number;
283
+ /** Letter grade */
284
+ grade: "A" | "B" | "C" | "D" | "F";
285
+ /** Score breakdown */
286
+ breakdown: {
287
+ /** % of features the user has explored */
288
+ featuresExplored: number;
289
+ /** Rate of dismissals (lower is better) */
290
+ dismissRate: number;
291
+ /** Rate of tour/checklist completion */
292
+ completionRate: number;
293
+ /** Whether engagement is rising, stable, or declining */
294
+ engagementTrend: "rising" | "stable" | "declining";
295
+ };
296
+ /** Actionable recommendations */
297
+ recommendations: string[];
298
+ }
299
+ /** Per-feature adoption status */
300
+ interface FeatureAdoptionStatus {
301
+ featureId: string;
302
+ status: "unseen" | "seen" | "explored" | "adopted" | "dismissed";
303
+ firstSeen?: string;
304
+ lastInteraction?: string;
305
+ interactionCount: number;
306
+ }
307
+ /** Delivery context passed to the engine for timing decisions */
308
+ interface DeliveryContext {
309
+ /** Current route/path */
310
+ currentPath: string;
311
+ /** Seconds since session start */
312
+ sessionAge: number;
313
+ /** Dismissals in last 5 minutes */
314
+ recentDismissals: number;
315
+ /** Feature priority */
316
+ featurePriority: FeaturePriority;
317
+ }
318
+ /**
319
+ * Plugin interface for the FeatureDrop engine.
320
+ *
321
+ * The open-source library defines this interface.
322
+ * The proprietary @featuredrop/engine implements it.
323
+ * Users can also build their own engine implementation.
324
+ *
325
+ * The free library works perfectly without any engine.
326
+ */
327
+ interface FeatureDropEngine {
328
+ /** Decide whether to show a feature announcement now */
329
+ shouldShow(featureId: string, context: DeliveryContext): TimingDecision;
330
+ /** Recommend the best display format for a feature */
331
+ recommendFormat(featureId: string): FormatRecommendation;
332
+ /** Get the user's overall adoption score */
333
+ getAdoptionScore(): AdoptionScore;
334
+ /** Track a user interaction with a feature */
335
+ trackInteraction(featureId: string, type: InteractionType): void;
336
+ /** Get adoption status for a specific feature */
337
+ getFeatureAdoption(featureId: string): FeatureAdoptionStatus;
338
+ /** Initialize the engine (called by provider on mount) */
339
+ initialize?(): void;
340
+ /** Cleanup resources (called by provider on unmount) */
341
+ destroy?(): void;
342
+ }
255
343
 
256
344
  interface ThrottleOptions {
257
345
  maxSimultaneousBadges?: number;
@@ -326,6 +414,8 @@ interface FeatureDropProviderProps {
326
414
  animation?: FeatureDropAnimationPreset;
327
415
  /** Custom translation overrides for built-in component strings */
328
416
  translations?: Partial<FeatureDropTranslations>;
417
+ /** Optional engine for AI-powered delivery intelligence (@featuredrop/engine) */
418
+ engine?: FeatureDropEngine;
329
419
  children: ReactNode;
330
420
  }
331
421
 
package/dist/testing.js CHANGED
@@ -982,6 +982,7 @@ function FeatureDropProvider({
982
982
  locale = "en",
983
983
  animation = "normal",
984
984
  translations: translationOverrides,
985
+ engine,
985
986
  children
986
987
  }) {
987
988
  const analyticsRef = useRef(analytics);
@@ -1038,20 +1039,20 @@ function FeatureDropProvider({
1038
1039
  seenFeatureIds: readIdSet(SEEN_FEATURES_STORAGE_KEY),
1039
1040
  clickedFeatureIds: readIdSet(CLICKED_FEATURES_STORAGE_KEY),
1040
1041
  triggerContext: (() => {
1041
- const engine = triggerEngineRef.current;
1042
- if (!engine) return void 0;
1043
- engine.setElapsedMs(Date.now() - sessionStartedAtRef.current);
1044
- return engine.getContext();
1042
+ const engine2 = triggerEngineRef.current;
1043
+ if (!engine2) return void 0;
1044
+ engine2.setElapsedMs(Date.now() - sessionStartedAtRef.current);
1045
+ return engine2.getContext();
1045
1046
  })(),
1046
1047
  flagBridge
1047
1048
  })
1048
1049
  );
1049
1050
  const recompute = useCallback(() => {
1050
- const engine = triggerEngineRef.current;
1051
+ const engine2 = triggerEngineRef.current;
1051
1052
  let triggerContext;
1052
- if (engine) {
1053
- engine.setElapsedMs(Date.now() - sessionStartedAtRef.current);
1054
- triggerContext = engine.getContext();
1053
+ if (engine2) {
1054
+ engine2.setElapsedMs(Date.now() - sessionStartedAtRef.current);
1055
+ triggerContext = engine2.getContext();
1055
1056
  }
1056
1057
  setFeatureState(
1057
1058
  computeFeatureState({
@@ -1088,6 +1089,12 @@ function FeatureDropProvider({
1088
1089
  useEffect(() => {
1089
1090
  recompute();
1090
1091
  }, [recompute]);
1092
+ useEffect(() => {
1093
+ engine?.initialize?.();
1094
+ return () => {
1095
+ engine?.destroy?.();
1096
+ };
1097
+ }, [engine]);
1091
1098
  const hasTimeTriggers = useMemo(
1092
1099
  () => resolvedManifest.some((feature) => feature.trigger?.type === "time"),
1093
1100
  [resolvedManifest]
@@ -1370,7 +1377,8 @@ function FeatureDropProvider({
1370
1377
  trackUsageEvent,
1371
1378
  trackTriggerEvent,
1372
1379
  trackMilestone,
1373
- setTriggerPath
1380
+ setTriggerPath,
1381
+ engine: engine ?? null
1374
1382
  }),
1375
1383
  [
1376
1384
  resolvedManifest,
@@ -1404,7 +1412,8 @@ function FeatureDropProvider({
1404
1412
  trackUsageEvent,
1405
1413
  trackTriggerEvent,
1406
1414
  trackMilestone,
1407
- setTriggerPath
1415
+ setTriggerPath,
1416
+ engine
1408
1417
  ]
1409
1418
  );
1410
1419
  return /* @__PURE__ */ jsx(FeatureDropContext.Provider, { value, children });