featuredrop 2.7.2 → 3.0.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.
Files changed (52) hide show
  1. package/README.md +32 -1
  2. package/dist/astro.cjs +333 -0
  3. package/dist/astro.cjs.map +1 -0
  4. package/dist/astro.d.cts +242 -0
  5. package/dist/astro.d.ts +242 -0
  6. package/dist/astro.js +329 -0
  7. package/dist/astro.js.map +1 -0
  8. package/dist/engine.cjs +552 -0
  9. package/dist/engine.cjs.map +1 -0
  10. package/dist/engine.d.cts +422 -0
  11. package/dist/engine.d.ts +422 -0
  12. package/dist/engine.js +545 -0
  13. package/dist/engine.js.map +1 -0
  14. package/dist/featuredrop.cjs +208 -1
  15. package/dist/featuredrop.cjs.map +1 -1
  16. package/dist/next.cjs +336 -0
  17. package/dist/next.cjs.map +1 -0
  18. package/dist/next.d.cts +243 -0
  19. package/dist/next.d.ts +243 -0
  20. package/dist/next.js +332 -0
  21. package/dist/next.js.map +1 -0
  22. package/dist/nuxt.cjs +352 -0
  23. package/dist/nuxt.cjs.map +1 -0
  24. package/dist/nuxt.d.cts +282 -0
  25. package/dist/nuxt.d.ts +282 -0
  26. package/dist/nuxt.js +347 -0
  27. package/dist/nuxt.js.map +1 -0
  28. package/dist/preact.cjs +354 -0
  29. package/dist/preact.cjs.map +1 -1
  30. package/dist/preact.d.cts +170 -1
  31. package/dist/preact.d.ts +170 -1
  32. package/dist/preact.js +350 -1
  33. package/dist/preact.js.map +1 -1
  34. package/dist/react-hooks.cjs +82 -0
  35. package/dist/react-hooks.cjs.map +1 -1
  36. package/dist/react-hooks.d.cts +117 -1
  37. package/dist/react-hooks.d.ts +117 -1
  38. package/dist/react-hooks.js +80 -1
  39. package/dist/react-hooks.js.map +1 -1
  40. package/dist/react.cjs +354 -0
  41. package/dist/react.cjs.map +1 -1
  42. package/dist/react.d.cts +170 -1
  43. package/dist/react.d.ts +170 -1
  44. package/dist/react.js +350 -1
  45. package/dist/react.js.map +1 -1
  46. package/dist/remix.cjs +331 -0
  47. package/dist/remix.cjs.map +1 -0
  48. package/dist/remix.d.cts +305 -0
  49. package/dist/remix.d.ts +305 -0
  50. package/dist/remix.js +327 -0
  51. package/dist/remix.js.map +1 -0
  52. package/package.json +70 -2
@@ -0,0 +1,305 @@
1
+ /** Entry type label — determines default icon/color in UI */
2
+ type FeatureType = "feature" | "improvement" | "fix" | "breaking";
3
+ /** Priority level for announcements */
4
+ type FeaturePriority = "critical" | "normal" | "low";
5
+ /** Call-to-action for a feature entry */
6
+ interface FeatureCTA {
7
+ /** Button/link label */
8
+ label: string;
9
+ /** URL to navigate to */
10
+ url: string;
11
+ }
12
+ /** Variant-level overrides for A/B announcement testing */
13
+ interface FeatureVariant {
14
+ /** Optional variant-specific label override */
15
+ label?: string;
16
+ /** Optional variant-specific description override */
17
+ description?: string;
18
+ /** Optional variant-specific image override */
19
+ image?: string;
20
+ /** Optional variant-specific CTA override */
21
+ cta?: FeatureCTA;
22
+ /** Optional variant-specific metadata overrides */
23
+ meta?: Record<string, unknown>;
24
+ }
25
+ /** Audience targeting rule — determines which user segments see a feature */
26
+ interface AudienceRule {
27
+ /** Plans that should see this feature (e.g. ["pro", "enterprise"]) */
28
+ plan?: string[];
29
+ /** Roles that should see this feature (e.g. ["admin", "editor"]) */
30
+ role?: string[];
31
+ /** Regions that should see this feature (e.g. ["us", "eu"]) */
32
+ region?: string[];
33
+ /** Arbitrary key-value pairs for custom matching logic */
34
+ custom?: Record<string, unknown>;
35
+ }
36
+ /** User context for audience targeting */
37
+ interface UserContext {
38
+ /** Current user's plan (e.g. "pro", "free") */
39
+ plan?: string;
40
+ /** Current user's role (e.g. "admin", "viewer") */
41
+ role?: string;
42
+ /** Current user's region (e.g. "us", "eu") */
43
+ region?: string;
44
+ /** Arbitrary traits for custom matching logic */
45
+ traits?: Record<string, unknown>;
46
+ }
47
+ /** Custom audience matcher function */
48
+ type AudienceMatchFn = (audience: AudienceRule, userContext: UserContext) => boolean;
49
+ /** Feature flag resolver interface for gating announcement visibility */
50
+ interface FeatureFlagBridge {
51
+ isEnabled: (flagKey: string, userContext?: UserContext) => boolean;
52
+ }
53
+ /** Dependency gates for progressive feature discovery */
54
+ interface FeatureDependencies {
55
+ /** Features the user must have seen before this one can surface */
56
+ seen?: string[];
57
+ /** Features the user must have clicked before this one can surface */
58
+ clicked?: string[];
59
+ /** Features the user must have dismissed before this one can surface */
60
+ dismissed?: string[];
61
+ }
62
+ /** Runtime interaction state used to resolve dependency chains */
63
+ interface FeatureDependencyState {
64
+ /** IDs marked as seen */
65
+ seenIds?: ReadonlySet<string>;
66
+ /** IDs marked as clicked */
67
+ clickedIds?: ReadonlySet<string>;
68
+ /** IDs marked as dismissed */
69
+ dismissedIds?: ReadonlySet<string>;
70
+ }
71
+ /** Runtime context used by trigger evaluation */
72
+ interface TriggerContext {
73
+ /** Current app route/path */
74
+ path?: string;
75
+ /** Named events observed in this session */
76
+ events?: ReadonlySet<string>;
77
+ /** Named milestone flags reached in this session */
78
+ milestones?: ReadonlySet<string>;
79
+ /** Usage counters keyed by event/pattern name */
80
+ usage?: Record<string, number>;
81
+ /** Session elapsed time in milliseconds */
82
+ elapsedMs?: number;
83
+ /** Scroll completion percentage (0-100) */
84
+ scrollPercent?: number;
85
+ /** Optional additional trigger context */
86
+ metadata?: Record<string, unknown>;
87
+ }
88
+ type FeatureTrigger = {
89
+ type: "page";
90
+ match: string | RegExp;
91
+ } | {
92
+ type: "usage";
93
+ event: string;
94
+ minActions?: number;
95
+ } | {
96
+ type: "time";
97
+ minSeconds: number;
98
+ } | {
99
+ type: "milestone";
100
+ event: string;
101
+ } | {
102
+ type: "frustration";
103
+ pattern: string;
104
+ threshold?: number;
105
+ } | {
106
+ type: "scroll";
107
+ minPercent?: number;
108
+ } | {
109
+ type: "custom";
110
+ evaluate: (context: TriggerContext) => boolean;
111
+ };
112
+ /** A single feature entry in the manifest */
113
+ interface FeatureEntry {
114
+ /** Unique identifier for the feature */
115
+ id: string;
116
+ /** Human-readable label (e.g. "Decision Journal") */
117
+ label: string;
118
+ /** Optional longer description (supports markdown in UI components) */
119
+ description?: string;
120
+ /**
121
+ * Semantic version targeting.
122
+ * If provided as an object, requires `appVersion` to be supplied to the provider/helpers.
123
+ * - introduced: earliest app version that includes this feature
124
+ * - showNewUntil: stop showing "new" once appVersion reaches this
125
+ * - deprecatedAt: hide feature for app versions at or above this (optional safety)
126
+ * - showIn: range string, e.g. ">=2.5.0 <3.0.0"
127
+ */
128
+ version?: string | {
129
+ introduced?: string;
130
+ showNewUntil?: string;
131
+ deprecatedAt?: string;
132
+ showIn?: string;
133
+ };
134
+ /** ISO date when this feature was released */
135
+ releasedAt: string;
136
+ /** ISO date after which the "new" badge should stop showing */
137
+ showNewUntil: string;
138
+ /** Optional key to match navigation items (e.g. "/journal", "settings") */
139
+ sidebarKey?: string;
140
+ /** Optional grouping category (e.g. "ai", "billing", "core") */
141
+ category?: string;
142
+ /** Optional product scope (`"*"`, `"askverdict"`, etc.) for multi-product manifests */
143
+ product?: string;
144
+ /** Optional URL to link to (e.g. docs page, changelog entry) */
145
+ url?: string;
146
+ /** Optional feature flag key; requires a flag bridge to evaluate */
147
+ flagKey?: string;
148
+ /** Entry type — determines default icon/color in UI components */
149
+ type?: FeatureType;
150
+ /** Priority level — critical entries get special treatment in UI */
151
+ priority?: FeaturePriority;
152
+ /** Optional image/screenshot URL */
153
+ image?: string;
154
+ /** Optional call-to-action button */
155
+ cta?: FeatureCTA;
156
+ /** ISO date — entry is hidden until this date (scheduled publishing) */
157
+ publishAt?: string;
158
+ /** Optional arbitrary metadata */
159
+ meta?: Record<string, unknown>;
160
+ /** A/B variants keyed by variant name (e.g. control, treatment_a) */
161
+ variants?: Record<string, FeatureVariant>;
162
+ /** Percentage split per variant (same order as variants object keys) */
163
+ variantSplit?: number[];
164
+ /** Audience targeting — if set, only matching users see this feature */
165
+ audience?: AudienceRule;
166
+ /** Dependency requirements (progressive disclosure sequencing) */
167
+ dependsOn?: FeatureDependencies;
168
+ /** Contextual trigger rule */
169
+ trigger?: FeatureTrigger;
170
+ }
171
+ /** The full feature manifest — an array of feature entries */
172
+ type FeatureManifest = readonly FeatureEntry[];
173
+
174
+ /**
175
+ * featuredrop — Remix integration
176
+ *
177
+ * Server-side loader helpers for computing new feature state from session data.
178
+ *
179
+ * Typical usage in a Remix loader:
180
+ *
181
+ * ```ts
182
+ * import { getNewFeaturesServer, createFeatureDropHeaders } from "featuredrop/remix";
183
+ * import { manifest } from "~/features/manifest";
184
+ *
185
+ * export async function loader({ request }: LoaderFunctionArgs) {
186
+ * const session = await getSession(request.headers.get("Cookie"));
187
+ * const dismissedIds: string[] = session.get("fd_dismissed") ?? [];
188
+ *
189
+ * const newFeatures = getNewFeaturesServer(manifest, dismissedIds);
190
+ * const headers = createFeatureDropHeaders(manifest, dismissedIds);
191
+ *
192
+ * return json({ newFeatures }, { headers });
193
+ * }
194
+ * ```
195
+ *
196
+ * All helpers are pure functions — they create a transient MemoryAdapter
197
+ * internally so there are no shared global state concerns between requests.
198
+ */
199
+
200
+ /**
201
+ * Options bag for server-side newness checks.
202
+ *
203
+ * Mirrors the optional parameters of the core `getNewFeatures` function.
204
+ * All fields are optional — omit what you don't use.
205
+ */
206
+ interface IsNewOptions {
207
+ /** Override the current timestamp (defaults to `new Date()`). */
208
+ now?: Date;
209
+ /**
210
+ * User context for audience targeting rules.
211
+ * Required when any manifest entry has an `audience` field.
212
+ */
213
+ userContext?: UserContext;
214
+ /**
215
+ * Custom audience matching function.
216
+ * Replaces the built-in plan/role/region matcher when provided.
217
+ */
218
+ matchAudience?: AudienceMatchFn;
219
+ /**
220
+ * Current app version string (e.g. `"2.5.0"`).
221
+ * Required when any manifest entry uses semver `version` constraints.
222
+ */
223
+ appVersion?: string;
224
+ /** Runtime dependency state for progressive feature discovery. */
225
+ dependencyState?: FeatureDependencyState;
226
+ /**
227
+ * Trigger context for contextual display rules.
228
+ * On the server you typically omit this (triggers are client-side).
229
+ */
230
+ triggerContext?: TriggerContext;
231
+ /** Feature flag bridge for evaluating `flagKey` entries. */
232
+ flagBridge?: FeatureFlagBridge;
233
+ /**
234
+ * Product scope filter.
235
+ * Use when your manifest covers multiple products and you want to
236
+ * surface only entries for the current product.
237
+ */
238
+ product?: string;
239
+ }
240
+ /**
241
+ * Return all features that are currently "new" for the given user.
242
+ *
243
+ * Creates a short-lived `MemoryAdapter` seeded with the provided
244
+ * `dismissedIds`, then delegates to `getNewFeatures` from core.
245
+ *
246
+ * @param manifest The full feature manifest.
247
+ * @param dismissedIds IDs the user has already dismissed (from session/DB).
248
+ * @param options Optional targeting overrides.
249
+ * @returns Array of `FeatureEntry` objects considered new.
250
+ *
251
+ * @example
252
+ * ```ts
253
+ * // In a Remix loader
254
+ * const dismissed: string[] = session.get("fd_dismissed") ?? [];
255
+ * const newFeatures = getNewFeaturesServer(manifest, dismissed, {
256
+ * userContext: { plan: "pro" },
257
+ * });
258
+ * return json({ newFeatures });
259
+ * ```
260
+ */
261
+ declare function getNewFeaturesServer(manifest: FeatureManifest, dismissedIds?: readonly string[], options?: IsNewOptions): FeatureEntry[];
262
+ /**
263
+ * Return the count of features that are currently "new" for the given user.
264
+ *
265
+ * Lightweight alternative to `getNewFeaturesServer` when you only need the
266
+ * number — avoids allocating the full result array on the caller's side.
267
+ *
268
+ * @param manifest The full feature manifest.
269
+ * @param dismissedIds IDs the user has already dismissed.
270
+ * @param options Optional targeting overrides.
271
+ * @returns Integer count of new features.
272
+ *
273
+ * @example
274
+ * ```ts
275
+ * const count = getNewCountServer(manifest, session.get("fd_dismissed") ?? []);
276
+ * // Pass count to the client as a header or in the JSON payload
277
+ * ```
278
+ */
279
+ declare function getNewCountServer(manifest: FeatureManifest, dismissedIds?: readonly string[], options?: IsNewOptions): number;
280
+ /**
281
+ * Build a `Headers` object carrying the new-feature count.
282
+ *
283
+ * Useful when you want the client to know about the badge count without
284
+ * serialising the entire feature list into the JSON response body.
285
+ *
286
+ * The header `X-FD-New-Count` is set to the string representation of the
287
+ * count. Clients can read it via `useFetcher` or a custom hook.
288
+ *
289
+ * @param manifest The full feature manifest.
290
+ * @param dismissedIds IDs the user has already dismissed.
291
+ * @param options Optional targeting overrides.
292
+ * @returns A `Headers` instance with `X-FD-New-Count` set.
293
+ *
294
+ * @example
295
+ * ```ts
296
+ * export async function loader({ request }: LoaderFunctionArgs) {
297
+ * const dismissed: string[] = session.get("fd_dismissed") ?? [];
298
+ * const headers = createFeatureDropHeaders(manifest, dismissed);
299
+ * return json({ ok: true }, { headers });
300
+ * }
301
+ * ```
302
+ */
303
+ declare function createFeatureDropHeaders(manifest: FeatureManifest, dismissedIds?: readonly string[], options?: IsNewOptions): Headers;
304
+
305
+ export { type FeatureEntry, type FeatureManifest, type IsNewOptions, createFeatureDropHeaders, getNewCountServer, getNewFeaturesServer };
@@ -0,0 +1,305 @@
1
+ /** Entry type label — determines default icon/color in UI */
2
+ type FeatureType = "feature" | "improvement" | "fix" | "breaking";
3
+ /** Priority level for announcements */
4
+ type FeaturePriority = "critical" | "normal" | "low";
5
+ /** Call-to-action for a feature entry */
6
+ interface FeatureCTA {
7
+ /** Button/link label */
8
+ label: string;
9
+ /** URL to navigate to */
10
+ url: string;
11
+ }
12
+ /** Variant-level overrides for A/B announcement testing */
13
+ interface FeatureVariant {
14
+ /** Optional variant-specific label override */
15
+ label?: string;
16
+ /** Optional variant-specific description override */
17
+ description?: string;
18
+ /** Optional variant-specific image override */
19
+ image?: string;
20
+ /** Optional variant-specific CTA override */
21
+ cta?: FeatureCTA;
22
+ /** Optional variant-specific metadata overrides */
23
+ meta?: Record<string, unknown>;
24
+ }
25
+ /** Audience targeting rule — determines which user segments see a feature */
26
+ interface AudienceRule {
27
+ /** Plans that should see this feature (e.g. ["pro", "enterprise"]) */
28
+ plan?: string[];
29
+ /** Roles that should see this feature (e.g. ["admin", "editor"]) */
30
+ role?: string[];
31
+ /** Regions that should see this feature (e.g. ["us", "eu"]) */
32
+ region?: string[];
33
+ /** Arbitrary key-value pairs for custom matching logic */
34
+ custom?: Record<string, unknown>;
35
+ }
36
+ /** User context for audience targeting */
37
+ interface UserContext {
38
+ /** Current user's plan (e.g. "pro", "free") */
39
+ plan?: string;
40
+ /** Current user's role (e.g. "admin", "viewer") */
41
+ role?: string;
42
+ /** Current user's region (e.g. "us", "eu") */
43
+ region?: string;
44
+ /** Arbitrary traits for custom matching logic */
45
+ traits?: Record<string, unknown>;
46
+ }
47
+ /** Custom audience matcher function */
48
+ type AudienceMatchFn = (audience: AudienceRule, userContext: UserContext) => boolean;
49
+ /** Feature flag resolver interface for gating announcement visibility */
50
+ interface FeatureFlagBridge {
51
+ isEnabled: (flagKey: string, userContext?: UserContext) => boolean;
52
+ }
53
+ /** Dependency gates for progressive feature discovery */
54
+ interface FeatureDependencies {
55
+ /** Features the user must have seen before this one can surface */
56
+ seen?: string[];
57
+ /** Features the user must have clicked before this one can surface */
58
+ clicked?: string[];
59
+ /** Features the user must have dismissed before this one can surface */
60
+ dismissed?: string[];
61
+ }
62
+ /** Runtime interaction state used to resolve dependency chains */
63
+ interface FeatureDependencyState {
64
+ /** IDs marked as seen */
65
+ seenIds?: ReadonlySet<string>;
66
+ /** IDs marked as clicked */
67
+ clickedIds?: ReadonlySet<string>;
68
+ /** IDs marked as dismissed */
69
+ dismissedIds?: ReadonlySet<string>;
70
+ }
71
+ /** Runtime context used by trigger evaluation */
72
+ interface TriggerContext {
73
+ /** Current app route/path */
74
+ path?: string;
75
+ /** Named events observed in this session */
76
+ events?: ReadonlySet<string>;
77
+ /** Named milestone flags reached in this session */
78
+ milestones?: ReadonlySet<string>;
79
+ /** Usage counters keyed by event/pattern name */
80
+ usage?: Record<string, number>;
81
+ /** Session elapsed time in milliseconds */
82
+ elapsedMs?: number;
83
+ /** Scroll completion percentage (0-100) */
84
+ scrollPercent?: number;
85
+ /** Optional additional trigger context */
86
+ metadata?: Record<string, unknown>;
87
+ }
88
+ type FeatureTrigger = {
89
+ type: "page";
90
+ match: string | RegExp;
91
+ } | {
92
+ type: "usage";
93
+ event: string;
94
+ minActions?: number;
95
+ } | {
96
+ type: "time";
97
+ minSeconds: number;
98
+ } | {
99
+ type: "milestone";
100
+ event: string;
101
+ } | {
102
+ type: "frustration";
103
+ pattern: string;
104
+ threshold?: number;
105
+ } | {
106
+ type: "scroll";
107
+ minPercent?: number;
108
+ } | {
109
+ type: "custom";
110
+ evaluate: (context: TriggerContext) => boolean;
111
+ };
112
+ /** A single feature entry in the manifest */
113
+ interface FeatureEntry {
114
+ /** Unique identifier for the feature */
115
+ id: string;
116
+ /** Human-readable label (e.g. "Decision Journal") */
117
+ label: string;
118
+ /** Optional longer description (supports markdown in UI components) */
119
+ description?: string;
120
+ /**
121
+ * Semantic version targeting.
122
+ * If provided as an object, requires `appVersion` to be supplied to the provider/helpers.
123
+ * - introduced: earliest app version that includes this feature
124
+ * - showNewUntil: stop showing "new" once appVersion reaches this
125
+ * - deprecatedAt: hide feature for app versions at or above this (optional safety)
126
+ * - showIn: range string, e.g. ">=2.5.0 <3.0.0"
127
+ */
128
+ version?: string | {
129
+ introduced?: string;
130
+ showNewUntil?: string;
131
+ deprecatedAt?: string;
132
+ showIn?: string;
133
+ };
134
+ /** ISO date when this feature was released */
135
+ releasedAt: string;
136
+ /** ISO date after which the "new" badge should stop showing */
137
+ showNewUntil: string;
138
+ /** Optional key to match navigation items (e.g. "/journal", "settings") */
139
+ sidebarKey?: string;
140
+ /** Optional grouping category (e.g. "ai", "billing", "core") */
141
+ category?: string;
142
+ /** Optional product scope (`"*"`, `"askverdict"`, etc.) for multi-product manifests */
143
+ product?: string;
144
+ /** Optional URL to link to (e.g. docs page, changelog entry) */
145
+ url?: string;
146
+ /** Optional feature flag key; requires a flag bridge to evaluate */
147
+ flagKey?: string;
148
+ /** Entry type — determines default icon/color in UI components */
149
+ type?: FeatureType;
150
+ /** Priority level — critical entries get special treatment in UI */
151
+ priority?: FeaturePriority;
152
+ /** Optional image/screenshot URL */
153
+ image?: string;
154
+ /** Optional call-to-action button */
155
+ cta?: FeatureCTA;
156
+ /** ISO date — entry is hidden until this date (scheduled publishing) */
157
+ publishAt?: string;
158
+ /** Optional arbitrary metadata */
159
+ meta?: Record<string, unknown>;
160
+ /** A/B variants keyed by variant name (e.g. control, treatment_a) */
161
+ variants?: Record<string, FeatureVariant>;
162
+ /** Percentage split per variant (same order as variants object keys) */
163
+ variantSplit?: number[];
164
+ /** Audience targeting — if set, only matching users see this feature */
165
+ audience?: AudienceRule;
166
+ /** Dependency requirements (progressive disclosure sequencing) */
167
+ dependsOn?: FeatureDependencies;
168
+ /** Contextual trigger rule */
169
+ trigger?: FeatureTrigger;
170
+ }
171
+ /** The full feature manifest — an array of feature entries */
172
+ type FeatureManifest = readonly FeatureEntry[];
173
+
174
+ /**
175
+ * featuredrop — Remix integration
176
+ *
177
+ * Server-side loader helpers for computing new feature state from session data.
178
+ *
179
+ * Typical usage in a Remix loader:
180
+ *
181
+ * ```ts
182
+ * import { getNewFeaturesServer, createFeatureDropHeaders } from "featuredrop/remix";
183
+ * import { manifest } from "~/features/manifest";
184
+ *
185
+ * export async function loader({ request }: LoaderFunctionArgs) {
186
+ * const session = await getSession(request.headers.get("Cookie"));
187
+ * const dismissedIds: string[] = session.get("fd_dismissed") ?? [];
188
+ *
189
+ * const newFeatures = getNewFeaturesServer(manifest, dismissedIds);
190
+ * const headers = createFeatureDropHeaders(manifest, dismissedIds);
191
+ *
192
+ * return json({ newFeatures }, { headers });
193
+ * }
194
+ * ```
195
+ *
196
+ * All helpers are pure functions — they create a transient MemoryAdapter
197
+ * internally so there are no shared global state concerns between requests.
198
+ */
199
+
200
+ /**
201
+ * Options bag for server-side newness checks.
202
+ *
203
+ * Mirrors the optional parameters of the core `getNewFeatures` function.
204
+ * All fields are optional — omit what you don't use.
205
+ */
206
+ interface IsNewOptions {
207
+ /** Override the current timestamp (defaults to `new Date()`). */
208
+ now?: Date;
209
+ /**
210
+ * User context for audience targeting rules.
211
+ * Required when any manifest entry has an `audience` field.
212
+ */
213
+ userContext?: UserContext;
214
+ /**
215
+ * Custom audience matching function.
216
+ * Replaces the built-in plan/role/region matcher when provided.
217
+ */
218
+ matchAudience?: AudienceMatchFn;
219
+ /**
220
+ * Current app version string (e.g. `"2.5.0"`).
221
+ * Required when any manifest entry uses semver `version` constraints.
222
+ */
223
+ appVersion?: string;
224
+ /** Runtime dependency state for progressive feature discovery. */
225
+ dependencyState?: FeatureDependencyState;
226
+ /**
227
+ * Trigger context for contextual display rules.
228
+ * On the server you typically omit this (triggers are client-side).
229
+ */
230
+ triggerContext?: TriggerContext;
231
+ /** Feature flag bridge for evaluating `flagKey` entries. */
232
+ flagBridge?: FeatureFlagBridge;
233
+ /**
234
+ * Product scope filter.
235
+ * Use when your manifest covers multiple products and you want to
236
+ * surface only entries for the current product.
237
+ */
238
+ product?: string;
239
+ }
240
+ /**
241
+ * Return all features that are currently "new" for the given user.
242
+ *
243
+ * Creates a short-lived `MemoryAdapter` seeded with the provided
244
+ * `dismissedIds`, then delegates to `getNewFeatures` from core.
245
+ *
246
+ * @param manifest The full feature manifest.
247
+ * @param dismissedIds IDs the user has already dismissed (from session/DB).
248
+ * @param options Optional targeting overrides.
249
+ * @returns Array of `FeatureEntry` objects considered new.
250
+ *
251
+ * @example
252
+ * ```ts
253
+ * // In a Remix loader
254
+ * const dismissed: string[] = session.get("fd_dismissed") ?? [];
255
+ * const newFeatures = getNewFeaturesServer(manifest, dismissed, {
256
+ * userContext: { plan: "pro" },
257
+ * });
258
+ * return json({ newFeatures });
259
+ * ```
260
+ */
261
+ declare function getNewFeaturesServer(manifest: FeatureManifest, dismissedIds?: readonly string[], options?: IsNewOptions): FeatureEntry[];
262
+ /**
263
+ * Return the count of features that are currently "new" for the given user.
264
+ *
265
+ * Lightweight alternative to `getNewFeaturesServer` when you only need the
266
+ * number — avoids allocating the full result array on the caller's side.
267
+ *
268
+ * @param manifest The full feature manifest.
269
+ * @param dismissedIds IDs the user has already dismissed.
270
+ * @param options Optional targeting overrides.
271
+ * @returns Integer count of new features.
272
+ *
273
+ * @example
274
+ * ```ts
275
+ * const count = getNewCountServer(manifest, session.get("fd_dismissed") ?? []);
276
+ * // Pass count to the client as a header or in the JSON payload
277
+ * ```
278
+ */
279
+ declare function getNewCountServer(manifest: FeatureManifest, dismissedIds?: readonly string[], options?: IsNewOptions): number;
280
+ /**
281
+ * Build a `Headers` object carrying the new-feature count.
282
+ *
283
+ * Useful when you want the client to know about the badge count without
284
+ * serialising the entire feature list into the JSON response body.
285
+ *
286
+ * The header `X-FD-New-Count` is set to the string representation of the
287
+ * count. Clients can read it via `useFetcher` or a custom hook.
288
+ *
289
+ * @param manifest The full feature manifest.
290
+ * @param dismissedIds IDs the user has already dismissed.
291
+ * @param options Optional targeting overrides.
292
+ * @returns A `Headers` instance with `X-FD-New-Count` set.
293
+ *
294
+ * @example
295
+ * ```ts
296
+ * export async function loader({ request }: LoaderFunctionArgs) {
297
+ * const dismissed: string[] = session.get("fd_dismissed") ?? [];
298
+ * const headers = createFeatureDropHeaders(manifest, dismissed);
299
+ * return json({ ok: true }, { headers });
300
+ * }
301
+ * ```
302
+ */
303
+ declare function createFeatureDropHeaders(manifest: FeatureManifest, dismissedIds?: readonly string[], options?: IsNewOptions): Headers;
304
+
305
+ export { type FeatureEntry, type FeatureManifest, type IsNewOptions, createFeatureDropHeaders, getNewCountServer, getNewFeaturesServer };