@schematichq/schematic-react 1.3.1 → 1.4.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.
package/README.md CHANGED
@@ -136,6 +136,45 @@ const MyComponent = () => {
136
136
 
137
137
  *Note: `useSchematicIsPending` is checking if entitlement data has been loaded, typically via `identify`. It should, therefore, be used to wrap flag and entitlement checks, but never the initial call to `identify`.*
138
138
 
139
+ ### Company plan information
140
+
141
+ To access the current company's plan and trial status, you can use the `useSchematicPlan` hook:
142
+
143
+ ```tsx
144
+ import { useSchematicPlan } from "@schematichq/schematic-react";
145
+
146
+ const MyComponent = () => {
147
+ const plan = useSchematicPlan();
148
+
149
+ if (!plan) {
150
+ return <div>No plan assigned</div>;
151
+ }
152
+
153
+ return (
154
+ <div>
155
+ <p>Current plan: {plan.name}</p>
156
+
157
+ {plan.trialStatus === "active" && (
158
+ <p>Trial ends: {plan.trialEndDate?.toLocaleDateString()}</p>
159
+ )}
160
+
161
+ {plan.trialStatus === "expired" && (
162
+ <p>Your trial has ended. <a href="/upgrade">Upgrade now</a></p>
163
+ )}
164
+ </div>
165
+ );
166
+ };
167
+ ```
168
+
169
+ The hook returns an object with the following properties:
170
+
171
+ | Property | Type | Description |
172
+ | --- | --- | --- |
173
+ | `id` | `string` | The plan ID |
174
+ | `name` | `string` | The plan name |
175
+ | `trialEndDate` | `Date \| undefined` | The trial end date, if the company has or had a trial |
176
+ | `trialStatus` | `"active" \| "expired" \| "converted" \| undefined` | The company's trial status: `active` if the trial is ongoing, `expired` if the trial ended without conversion, `converted` if the company converted to a paid plan, or `undefined` if the company has never trialed |
177
+
139
178
  ## Fallback Behavior
140
179
 
141
180
  The SDK includes built-in fallback behavior you can use to ensure your application continues to function even when unable to reach Schematic (e.g., during service disruptions or network issues).
@@ -809,6 +809,7 @@ var UsagePeriod = /* @__PURE__ */ ((UsagePeriod2) => {
809
809
  var CheckFlagReturnFromJSON = (json) => {
810
810
  const {
811
811
  companyId,
812
+ entitlement,
812
813
  error,
813
814
  featureAllocation,
814
815
  featureUsage,
@@ -826,20 +827,27 @@ var CheckFlagReturnFromJSON = (json) => {
826
827
  const featureUsageExceeded = !value && // if flag is not false, then we haven't exceeded usage
827
828
  (ruleType == "company_override_usage_exceeded" || // if the rule type is one of these, then we have exceeded usage
828
829
  ruleType == "plan_entitlement_usage_exceeded");
830
+ const resolvedAllocation = entitlement?.allocation ?? featureAllocation;
831
+ const resolvedUsage = entitlement?.usage ?? featureUsage;
832
+ const resolvedEvent = entitlement?.eventName ?? featureUsageEvent;
833
+ const resolvedPeriod = entitlement?.metricPeriod ?? featureUsagePeriod;
834
+ const resolvedResetAt = entitlement?.metricResetAt ?? featureUsageResetAt;
829
835
  return {
830
836
  featureUsageExceeded,
831
837
  companyId: companyId == null ? void 0 : companyId,
838
+ creditRemaining: entitlement?.creditRemaining == null ? void 0 : entitlement.creditRemaining,
832
839
  error: error == null ? void 0 : error,
833
- featureAllocation: featureAllocation == null ? void 0 : featureAllocation,
834
- featureUsage: featureUsage == null ? void 0 : featureUsage,
835
- featureUsageEvent: featureUsageEvent === null ? void 0 : featureUsageEvent,
836
- featureUsagePeriod: featureUsagePeriod == null ? void 0 : featureUsagePeriod,
837
- featureUsageResetAt: featureUsageResetAt == null ? void 0 : featureUsageResetAt,
840
+ featureAllocation: resolvedAllocation == null ? void 0 : resolvedAllocation,
841
+ featureUsage: resolvedUsage == null ? void 0 : resolvedUsage,
842
+ featureUsageEvent: resolvedEvent == null ? void 0 : resolvedEvent,
843
+ featureUsagePeriod: resolvedPeriod == null ? void 0 : resolvedPeriod,
844
+ featureUsageResetAt: resolvedResetAt == null ? void 0 : resolvedResetAt,
838
845
  flag,
839
846
  flagId: flagId == null ? void 0 : flagId,
840
847
  reason,
841
848
  ruleId: ruleId == null ? void 0 : ruleId,
842
849
  ruleType: ruleType == null ? void 0 : ruleType,
850
+ softLimit: entitlement?.softLimit == null ? void 0 : entitlement.softLimit,
843
851
  userId: userId == null ? void 0 : userId,
844
852
  value
845
853
  };
@@ -867,7 +875,7 @@ function contextString(context) {
867
875
  }, {});
868
876
  return JSON.stringify(sortedContext);
869
877
  }
870
- var version = "1.3.1";
878
+ var version = "1.4.0";
871
879
  var anonymousIdKey = "schematicId";
872
880
  var Schematic = class {
873
881
  additionalHeaders = {};
@@ -2304,7 +2312,7 @@ var notifyPlanListener = (listener, value) => {
2304
2312
  var import_react = __toESM(require("react"));
2305
2313
 
2306
2314
  // src/version.ts
2307
- var version2 = "1.3.1";
2315
+ var version2 = "1.4.1";
2308
2316
 
2309
2317
  // src/context/schematic.tsx
2310
2318
  var import_jsx_runtime = require("react/jsx-runtime");
@@ -1,47 +1,792 @@
1
- import { CheckFlagReturn } from '@schematichq/schematic-js';
2
- import { CheckPlanReturn } from '@schematichq/schematic-js';
3
- import { Event as Event_2 } from '@schematichq/schematic-js';
4
- import { EventBody } from '@schematichq/schematic-js';
5
- import { EventBodyIdentify } from '@schematichq/schematic-js';
6
- import { EventBodyTrack } from '@schematichq/schematic-js';
7
- import { EventType } from '@schematichq/schematic-js';
8
- import { Keys } from '@schematichq/schematic-js';
9
1
  import { default as React_2 } from 'react';
10
- import { RuleType } from '@schematichq/schematic-js';
11
- import { Schematic } from '@schematichq/schematic-js';
12
- import { SchematicContext } from '@schematichq/schematic-js';
13
- import * as SchematicJS from '@schematichq/schematic-js';
14
- import { SchematicOptions } from '@schematichq/schematic-js';
15
- import { StoragePersister } from '@schematichq/schematic-js';
16
- import { Traits } from '@schematichq/schematic-js';
17
- import { TrialStatus } from '@schematichq/schematic-js';
18
- import { UsagePeriod } from '@schematichq/schematic-js';
19
2
 
20
3
  declare type BaseSchematicProviderProps = Omit<SchematicJS.SchematicOptions, "client" | "publishableKey" | "useWebSocket"> & {
21
4
  children: React_2.ReactNode;
22
5
  };
23
6
 
24
- export { CheckFlagReturn }
7
+ declare type BooleanListenerFn = (value: boolean) => void;
8
+
9
+ /**
10
+ *
11
+ * @export
12
+ * @interface CheckFlagResponse
13
+ */
14
+ declare interface CheckFlagResponse {
15
+ /**
16
+ *
17
+ * @type {CheckFlagResponseData}
18
+ * @memberof CheckFlagResponse
19
+ */
20
+ data: CheckFlagResponseData;
21
+ /**
22
+ * Input parameters
23
+ * @type {object}
24
+ * @memberof CheckFlagResponse
25
+ */
26
+ params: object;
27
+ }
28
+
29
+ /**
30
+ *
31
+ * @export
32
+ * @interface CheckFlagResponseData
33
+ */
34
+ declare interface CheckFlagResponseData {
35
+ /**
36
+ * If company keys were provided and matched a company, its ID
37
+ * @type {string}
38
+ * @memberof CheckFlagResponseData
39
+ */
40
+ companyId?: string | null;
41
+ /**
42
+ * If a feature entitlement rule was matched, its entitlement details
43
+ * @type {FeatureEntitlement}
44
+ * @memberof CheckFlagResponseData
45
+ */
46
+ entitlement?: FeatureEntitlement;
47
+ /**
48
+ * If an error occurred while checking the flag, the error message
49
+ * @type {string}
50
+ * @memberof CheckFlagResponseData
51
+ */
52
+ error?: string | null;
53
+ /**
54
+ * Deprecated: Use Entitlement.Allocation instead.
55
+ * @type {number}
56
+ * @memberof CheckFlagResponseData
57
+ * @deprecated
58
+ */
59
+ featureAllocation?: number | null;
60
+ /**
61
+ * Deprecated: Use Entitlement.Usage instead.
62
+ * @type {number}
63
+ * @memberof CheckFlagResponseData
64
+ * @deprecated
65
+ */
66
+ featureUsage?: number | null;
67
+ /**
68
+ * Deprecated: Use Entitlement.EventName instead.
69
+ * @type {string}
70
+ * @memberof CheckFlagResponseData
71
+ * @deprecated
72
+ */
73
+ featureUsageEvent?: string | null;
74
+ /**
75
+ * Deprecated: Use Entitlement.MetricPeriod instead.
76
+ * @type {string}
77
+ * @memberof CheckFlagResponseData
78
+ * @deprecated
79
+ */
80
+ featureUsagePeriod?: string | null;
81
+ /**
82
+ * Deprecated: Use Entitlement.MetricResetAt instead.
83
+ * @type {Date}
84
+ * @memberof CheckFlagResponseData
85
+ * @deprecated
86
+ */
87
+ featureUsageResetAt?: Date | null;
88
+ /**
89
+ * The key used to check the flag
90
+ * @type {string}
91
+ * @memberof CheckFlagResponseData
92
+ */
93
+ flag: string;
94
+ /**
95
+ * If a flag was found, its ID
96
+ * @type {string}
97
+ * @memberof CheckFlagResponseData
98
+ */
99
+ flagId?: string | null;
100
+ /**
101
+ * A human-readable explanation of the result
102
+ * @type {string}
103
+ * @memberof CheckFlagResponseData
104
+ */
105
+ reason: string;
106
+ /**
107
+ * If a rule was found, its ID
108
+ * @type {string}
109
+ * @memberof CheckFlagResponseData
110
+ */
111
+ ruleId?: string | null;
112
+ /**
113
+ * If a rule was found, its type
114
+ * @type {string}
115
+ * @memberof CheckFlagResponseData
116
+ */
117
+ ruleType?: string | null;
118
+ /**
119
+ * If user keys were provided and matched a user, its ID
120
+ * @type {string}
121
+ * @memberof CheckFlagResponseData
122
+ */
123
+ userId?: string | null;
124
+ /**
125
+ * A boolean flag check result; for feature entitlements, this represents whether further consumption of the feature is permitted
126
+ * @type {boolean}
127
+ * @memberof CheckFlagResponseData
128
+ */
129
+ value: boolean;
130
+ }
25
131
 
26
- export { CheckPlanReturn }
132
+ declare function CheckFlagResponseFromJSON(json: any): CheckFlagResponse;
133
+
134
+ export declare type CheckFlagReturn = {
135
+ /** The company has access to the feature, but has exceeded the usage limit */
136
+ featureUsageExceeded?: boolean;
137
+ /** If company keys were provided and matched a company, its ID */
138
+ companyId?: string;
139
+ /** If the company has a credit-based entitlement for this feature, the remaining credit amount */
140
+ creditRemaining?: number;
141
+ /** If an error occurred while checking the flag, the error message */
142
+ error?: string;
143
+ /** If a numeric feature entitlement rule was matched, its allocation */
144
+ featureAllocation?: number;
145
+ /** If a numeric feature entitlement rule was matched, the company's usage */
146
+ featureUsage?: number;
147
+ /** Event representing the feature usage */
148
+ featureUsageEvent?: string;
149
+ /** For event-based feature entitlement rules, the period over which usage is tracked (current_month, current_day, current_week, all_time) */
150
+ featureUsagePeriod?: UsagePeriod;
151
+ /** For event-based feature entitlement rules, when the usage period will reset */
152
+ featureUsageResetAt?: Date;
153
+ /** The key used to check the flag */
154
+ flag: string;
155
+ /** If a flag was found, its ID */
156
+ flagId?: string;
157
+ /** A human-readable explanation of the result */
158
+ reason: string;
159
+ /** If a rule was found, its ID */
160
+ ruleId?: string;
161
+ /** If a rule was found, its type */
162
+ ruleType?: RuleType;
163
+ /** For usage-based pricing, the soft limit for overage charges or the next tier boundary */
164
+ softLimit?: number;
165
+ /** If user keys were provided and matched a user, its ID */
166
+ userId?: string;
167
+ /** A boolean flag check result; for feature entitlements, this represents whether further consumption of the feature is permitted */
168
+ value: boolean;
169
+ };
170
+
171
+ declare const CheckFlagReturnFromJSON: (json: any) => CheckFlagReturn;
172
+
173
+ declare type CheckFlagReturnListenerFn = (value: CheckFlagReturn) => void;
174
+
175
+ /**
176
+ *
177
+ * @export
178
+ * @interface CheckFlagsResponse
179
+ */
180
+ declare interface CheckFlagsResponse {
181
+ /**
182
+ *
183
+ * @type {CheckFlagsResponseData}
184
+ * @memberof CheckFlagsResponse
185
+ */
186
+ data: CheckFlagsResponseData;
187
+ /**
188
+ * Input parameters
189
+ * @type {object}
190
+ * @memberof CheckFlagsResponse
191
+ */
192
+ params: object;
193
+ }
27
194
 
195
+ /**
196
+ *
197
+ * @export
198
+ * @interface CheckFlagsResponseData
199
+ */
200
+ declare interface CheckFlagsResponseData {
201
+ /**
202
+ *
203
+ * @type {Array<CheckFlagResponseData>}
204
+ * @memberof CheckFlagsResponseData
205
+ */
206
+ flags: Array<CheckFlagResponseData>;
207
+ /**
208
+ *
209
+ * @type {DatastreamCompanyPlan}
210
+ * @memberof CheckFlagsResponseData
211
+ */
212
+ plan?: DatastreamCompanyPlan;
213
+ }
214
+
215
+ declare function CheckFlagsResponseFromJSON(json: any): CheckFlagsResponse;
216
+
217
+ declare type CheckOptions = {
218
+ context?: SchematicContext;
219
+ fallback?: boolean;
220
+ key: string;
221
+ };
222
+
223
+ export declare type CheckPlanReturn = {
224
+ id: string;
225
+ name: string;
226
+ trialEndDate?: Date;
227
+ trialStatus?: TrialStatus;
228
+ };
229
+
230
+ declare const CheckPlanReturnFromJSON: (json: any) => CheckPlanReturn;
231
+
232
+ declare type CheckPlanReturnListenerFn = (value: CheckPlanReturn) => void;
233
+
234
+ /**
235
+ *
236
+ * @export
237
+ * @interface DatastreamCompanyPlan
238
+ */
239
+ declare interface DatastreamCompanyPlan {
240
+ /**
241
+ *
242
+ * @type {string}
243
+ * @memberof DatastreamCompanyPlan
244
+ */
245
+ id: string;
246
+ /**
247
+ *
248
+ * @type {string}
249
+ * @memberof DatastreamCompanyPlan
250
+ */
251
+ name: string;
252
+ /**
253
+ *
254
+ * @type {Date}
255
+ * @memberof DatastreamCompanyPlan
256
+ */
257
+ trialEndDate?: Date | null;
258
+ /**
259
+ *
260
+ * @type {TrialStatus}
261
+ * @memberof DatastreamCompanyPlan
262
+ */
263
+ trialStatus?: TrialStatus | null;
264
+ }
265
+
266
+ declare function DatastreamCompanyPlanFromJSON(json: any): DatastreamCompanyPlan;
267
+
268
+ declare type EmptyListenerFn = () => void;
269
+
270
+ /**
271
+ *
272
+ * @export
273
+ */
274
+ declare const EntitlementValueType: {
275
+ readonly Boolean: "boolean";
276
+ readonly Credit: "credit";
277
+ readonly Numeric: "numeric";
278
+ readonly Trait: "trait";
279
+ readonly Unknown: "unknown";
280
+ readonly Unlimited: "unlimited";
281
+ };
282
+
283
+ declare type EntitlementValueType = (typeof EntitlementValueType)[keyof typeof EntitlementValueType];
284
+
285
+ declare type Event_2 = {
286
+ api_key: string;
287
+ body: EventBody;
288
+ sent_at: string;
289
+ tracker_event_id: string;
290
+ tracker_user_id: string;
291
+ type: EventType;
292
+ retry_count?: number;
293
+ next_retry_at?: number;
294
+ };
28
295
  export { Event_2 as Event }
29
296
 
30
- export { EventBody }
297
+ export declare type EventBody = EventBodyIdentify | EventBodyTrack | EventBodyFlagCheck;
298
+
299
+ /**
300
+ * Schematic API
301
+ * Schematic API
302
+ *
303
+ * The version of the OpenAPI document: 0.1
304
+ *
305
+ *
306
+ * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
307
+ * https://openapi-generator.tech
308
+ * Do not edit the class manually.
309
+ */
310
+ /**
311
+ *
312
+ * @export
313
+ * @interface EventBodyFlagCheck
314
+ */
315
+ declare interface EventBodyFlagCheck {
316
+ /**
317
+ * Schematic company ID (starting with 'comp_') of the company evaluated, if any
318
+ * @type {string}
319
+ * @memberof EventBodyFlagCheck
320
+ */
321
+ companyId?: string | null;
322
+ /**
323
+ * Report an error that occurred during the flag check
324
+ * @type {string}
325
+ * @memberof EventBodyFlagCheck
326
+ */
327
+ error?: string | null;
328
+ /**
329
+ * Schematic flag ID (starting with 'flag_') for the flag matching the key, if any
330
+ * @type {string}
331
+ * @memberof EventBodyFlagCheck
332
+ */
333
+ flagId?: string | null;
334
+ /**
335
+ * The key of the flag being checked
336
+ * @type {string}
337
+ * @memberof EventBodyFlagCheck
338
+ */
339
+ flagKey: string;
340
+ /**
341
+ * The reason why the value was returned
342
+ * @type {string}
343
+ * @memberof EventBodyFlagCheck
344
+ */
345
+ reason: string;
346
+ /**
347
+ * Key-value pairs used to to identify company for which the flag was checked
348
+ * @type {{ [key: string]: string; }}
349
+ * @memberof EventBodyFlagCheck
350
+ */
351
+ reqCompany?: {
352
+ [key: string]: string;
353
+ } | null;
354
+ /**
355
+ * Key-value pairs used to to identify user for which the flag was checked
356
+ * @type {{ [key: string]: string; }}
357
+ * @memberof EventBodyFlagCheck
358
+ */
359
+ reqUser?: {
360
+ [key: string]: string;
361
+ } | null;
362
+ /**
363
+ * Schematic rule ID (starting with 'rule_') of the rule that matched for the flag, if any
364
+ * @type {string}
365
+ * @memberof EventBodyFlagCheck
366
+ */
367
+ ruleId?: string | null;
368
+ /**
369
+ * Schematic user ID (starting with 'user_') of the user evaluated, if any
370
+ * @type {string}
371
+ * @memberof EventBodyFlagCheck
372
+ */
373
+ userId?: string | null;
374
+ /**
375
+ * The value of the flag for the given company and/or user
376
+ * @type {boolean}
377
+ * @memberof EventBodyFlagCheck
378
+ */
379
+ value: boolean;
380
+ }
381
+
382
+ declare function EventBodyFlagCheckToJSON(json: any): EventBodyFlagCheck;
383
+
384
+ export declare type EventBodyIdentify = {
385
+ company?: {
386
+ keys?: Keys;
387
+ name?: string;
388
+ traits?: Traits;
389
+ };
390
+ keys?: Keys;
391
+ name?: string;
392
+ traits?: Traits;
393
+ };
394
+
395
+ export declare type EventBodyTrack = SchematicContext & {
396
+ event: string;
397
+ quantity?: number;
398
+ traits?: Traits;
399
+ };
31
400
 
32
- export { EventBodyIdentify }
401
+ export declare type EventType = "identify" | "track" | "flag_check";
402
+
403
+ /**
404
+ *
405
+ * @export
406
+ * @interface FeatureEntitlement
407
+ */
408
+ declare interface FeatureEntitlement {
409
+ /**
410
+ * If the company has a numeric entitlement for this feature, the allocated amount
411
+ * @type {number}
412
+ * @memberof FeatureEntitlement
413
+ */
414
+ allocation?: number | null;
415
+ /**
416
+ * If the company has a credit-based entitlement for this feature, the ID of the credit
417
+ * @type {string}
418
+ * @memberof FeatureEntitlement
419
+ */
420
+ creditId?: string | null;
421
+ /**
422
+ * If the company has a credit-based entitlement for this feature, the remaining credit amount
423
+ * @type {number}
424
+ * @memberof FeatureEntitlement
425
+ */
426
+ creditRemaining?: number | null;
427
+ /**
428
+ * If the company has a credit-based entitlement for this feature, the total credit amount
429
+ * @type {number}
430
+ * @memberof FeatureEntitlement
431
+ */
432
+ creditTotal?: number | null;
433
+ /**
434
+ * If the company has a credit-based entitlement for this feature, the amount of credit used
435
+ * @type {number}
436
+ * @memberof FeatureEntitlement
437
+ */
438
+ creditUsed?: number | null;
439
+ /**
440
+ * If the feature is event-based, the name of the event tracked for usage
441
+ * @type {string}
442
+ * @memberof FeatureEntitlement
443
+ */
444
+ eventName?: string | null;
445
+ /**
446
+ * The ID of the feature
447
+ * @type {string}
448
+ * @memberof FeatureEntitlement
449
+ */
450
+ featureId: string;
451
+ /**
452
+ * The key of the flag associated with the feature
453
+ * @type {string}
454
+ * @memberof FeatureEntitlement
455
+ */
456
+ featureKey: string;
457
+ /**
458
+ * For event-based feature entitlements, the period over which usage is tracked
459
+ * @type {string}
460
+ * @memberof FeatureEntitlement
461
+ */
462
+ metricPeriod?: FeatureEntitlementMetricPeriodEnum | null;
463
+ /**
464
+ * For event-based feature entitlements, when the usage period will reset
465
+ * @type {Date}
466
+ * @memberof FeatureEntitlement
467
+ */
468
+ metricResetAt?: Date | null;
469
+ /**
470
+ * For event-based feature entitlements that have a monthly period, whether that monthly reset is based on the calendar month or a billing cycle
471
+ * @type {string}
472
+ * @memberof FeatureEntitlement
473
+ */
474
+ monthReset?: FeatureEntitlementMonthResetEnum | null;
475
+ /**
476
+ * For usage-based pricing, the soft limit for overage charges or the next tier boundary
477
+ * @type {number}
478
+ * @memberof FeatureEntitlement
479
+ */
480
+ softLimit?: number | null;
481
+ /**
482
+ * If the company has a numeric entitlement for this feature, the current usage amount
483
+ * @type {number}
484
+ * @memberof FeatureEntitlement
485
+ */
486
+ usage?: number | null;
487
+ /**
488
+ * The type of the entitlement value
489
+ * @type {EntitlementValueType}
490
+ * @memberof FeatureEntitlement
491
+ */
492
+ valueType: EntitlementValueType;
493
+ }
33
494
 
34
- export { EventBodyTrack }
495
+ /**
496
+ * @export
497
+ */
498
+ declare const FeatureEntitlementMetricPeriodEnum: {
499
+ readonly AllTime: "all_time";
500
+ readonly CurrentDay: "current_day";
501
+ readonly CurrentMonth: "current_month";
502
+ readonly CurrentWeek: "current_week";
503
+ };
35
504
 
36
- export { EventType }
505
+ declare type FeatureEntitlementMetricPeriodEnum = (typeof FeatureEntitlementMetricPeriodEnum)[keyof typeof FeatureEntitlementMetricPeriodEnum];
37
506
 
38
- export { Keys }
507
+ /**
508
+ * @export
509
+ */
510
+ declare const FeatureEntitlementMonthResetEnum: {
511
+ readonly FirstOfMonth: "first_of_month";
512
+ readonly BillingCycle: "billing_cycle";
513
+ };
39
514
 
40
- export { RuleType }
515
+ declare type FeatureEntitlementMonthResetEnum = (typeof FeatureEntitlementMonthResetEnum)[keyof typeof FeatureEntitlementMonthResetEnum];
41
516
 
42
- export { Schematic }
517
+ declare type FlagCheckListenerFn = CheckFlagReturnListenerFn | EmptyListenerFn;
43
518
 
44
- export { SchematicContext }
519
+ declare type FlagValueListenerFn = BooleanListenerFn | EmptyListenerFn;
520
+
521
+ /** A record of unique key-value pairs used for identifying a company or user */
522
+ export declare type Keys = Record<string, string>;
523
+
524
+ declare type PendingListenerFn = BooleanListenerFn | EmptyListenerFn;
525
+
526
+ declare type PlanListenerFn = CheckPlanReturnListenerFn | EmptyListenerFn;
527
+
528
+ export declare enum RuleType {
529
+ /** A global rule that, if present, will override all other rules for a flag */
530
+ GLOBAL_OVERRIDE = "global_override",
531
+ /** Rule type indicating feature access provisioned to a company via an override */
532
+ COMPANY_OVERRIDE = "company_override",
533
+ /** Rule type indicating that feature access has been provisione to a company via an override, but the usage limit has been reached or exceeded */
534
+ COMPANY_OVERRIDE_USAGE_EXCEEDED = "company_override_usage_exceeded",
535
+ /** Rule type indicating feature access provisioned to a company via its base plan or add-ons */
536
+ PLAN_ENTITLEMENT = "plan_entitlement",
537
+ /** Rule type indicating that feature access has been provisione to a company via base plan or add-ons, but the usage limit has been reached or exceeded */
538
+ PLAN_ENTITLEMENT_USAGE_EXCEEDED = "plan_entitlement_usage_exceeded",
539
+ /** General-purpose targeting rule */
540
+ STANDARD = "standard",
541
+ /** Default rule type that will be used if no other rules are matched */
542
+ DEFAULT = "default"
543
+ }
544
+
545
+ export declare class Schematic {
546
+ private additionalHeaders;
547
+ private apiKey;
548
+ private apiUrl;
549
+ private conn;
550
+ private context;
551
+ private debugEnabled;
552
+ private offlineEnabled;
553
+ private eventQueue;
554
+ private contextDependentEventQueue;
555
+ private eventUrl;
556
+ private flagCheckListeners;
557
+ private flagValueListeners;
558
+ private isPending;
559
+ private isPendingListeners;
560
+ private planListeners;
561
+ private storage;
562
+ private useWebSocket;
563
+ private checks;
564
+ private featureUsageEventMap;
565
+ private planChecks;
566
+ private webSocketUrl;
567
+ private webSocketConnectionTimeout;
568
+ private webSocketReconnect;
569
+ private webSocketMaxReconnectAttempts;
570
+ private webSocketMaxConnectionAttempts;
571
+ private webSocketInitialRetryDelay;
572
+ private webSocketMaxRetryDelay;
573
+ private wsReconnectAttempts;
574
+ private wsReconnectTimer;
575
+ private wsIntentionalDisconnect;
576
+ private currentWebSocket;
577
+ private isConnecting;
578
+ private maxEventQueueSize;
579
+ private maxEventRetries;
580
+ private eventRetryInitialDelay;
581
+ private eventRetryMaxDelay;
582
+ private retryTimer;
583
+ private flagValueDefaults;
584
+ private flagCheckDefaults;
585
+ private fallbackCheckCache;
586
+ constructor(apiKey: string, options?: SchematicOptions);
587
+ /**
588
+ * Resolve fallback value according to priority order:
589
+ * 1. Callsite fallback value (if provided)
590
+ * 2. Boolean value from flagCheckDefaults initialization option
591
+ * 3. Boolean value from flagValueDefaults initialization option
592
+ * 4. Default to false
593
+ */
594
+ private resolveFallbackValue;
595
+ /**
596
+ * Resolve complete CheckFlagReturn object according to priority order:
597
+ * 1. Use callsite fallback for boolean value, construct CheckFlagReturn
598
+ * 2. Use flagCheckDefaults if available for this flag
599
+ * 3. Use flagValueDefaults if available for this flag, construct CheckFlagReturn
600
+ * 4. Default CheckFlagReturn with value: false
601
+ */
602
+ private resolveFallbackCheckFlagReturn;
603
+ /**
604
+ * Get value for a single flag.
605
+ * In WebSocket mode, returns cached values if connection is active, otherwise establishes
606
+ * new connection and then returns the requested value. Falls back to preconfigured fallback
607
+ * values if WebSocket connection fails.
608
+ * In REST mode, makes an API call for each check.
609
+ */
610
+ checkFlag(options: CheckOptions): Promise<boolean>;
611
+ /**
612
+ * Helper function to log debug messages
613
+ * Only logs if debug mode is enabled
614
+ */
615
+ debug(message: string, ...args: unknown[]): void;
616
+ /**
617
+ * Create a persistent message handler for websocket flag updates
618
+ */
619
+ private createPersistentMessageHandler;
620
+ /**
621
+ * Helper function to check if client is in offline mode
622
+ */
623
+ private isOffline;
624
+ /**
625
+ * Submit a flag check event
626
+ * Records data about a flag check for analytics
627
+ */
628
+ private submitFlagCheckEvent;
629
+ /**
630
+ * Make an API call to fetch all flag values for a given context.
631
+ * Recommended for use in REST mode only.
632
+ * In offline mode, returns an empty object.
633
+ */
634
+ checkFlags: (context?: SchematicContext) => Promise<Record<string, boolean>>;
635
+ /**
636
+ * Send an identify event.
637
+ * This will set the context for subsequent flag evaluation and events, and will also
638
+ * send an identify event to the Schematic API which will upsert a user and company.
639
+ */
640
+ identify: (body: EventBodyIdentify) => Promise<void>;
641
+ /**
642
+ * Set the flag evaluation context.
643
+ * In WebSocket mode, this will:
644
+ * 1. Open a websocket connection if not already open
645
+ * 2. Send the context to the server
646
+ * 3. Wait for initial flag values to be returned
647
+ * The promise resolves when initial flag values are received.
648
+ * In offline mode, this will just set the context locally without connecting.
649
+ */
650
+ setContext: (context: SchematicContext) => Promise<void>;
651
+ /**
652
+ * Send a track event
653
+ * Track usage for a company and/or user.
654
+ * Optimistically updates feature usage flags if tracking a featureUsageEvent.
655
+ */
656
+ track: (body: EventBodyTrack) => Promise<void>;
657
+ /**
658
+ * Optimistically update feature usage flags associated with a tracked event
659
+ * This updates flags in memory with updated usage counts and value/featureUsageExceeded flags
660
+ * before the network request completes
661
+ */
662
+ private optimisticallyUpdateFeatureUsage;
663
+ /**
664
+ * Event processing
665
+ */
666
+ private hasContext;
667
+ private flushContextDependentEventQueue;
668
+ private startRetryTimer;
669
+ private stopRetryTimer;
670
+ private flushEventQueue;
671
+ private getAnonymousId;
672
+ private handleEvent;
673
+ private sendEvent;
674
+ private storeEvent;
675
+ /**
676
+ * Websocket management
677
+ */
678
+ /**
679
+ * Force an immediate WebSocket reconnection.
680
+ * This is useful when the application returns from a background state (e.g., mobile app
681
+ * coming back to foreground) and wants to immediately re-establish the connection
682
+ * rather than waiting for the exponential backoff timer.
683
+ *
684
+ * This method will:
685
+ * - Cancel any pending reconnection timer
686
+ * - Reset the reconnection attempt counter
687
+ * - Close any existing connection
688
+ * - Immediately attempt to reconnect
689
+ * - Re-send the current context to get fresh flag values
690
+ *
691
+ * Use this when you need guaranteed fresh values (e.g., after an in-app purchase).
692
+ *
693
+ * @example
694
+ * ```typescript
695
+ * // React Native example: reconnect when app comes to foreground
696
+ * useEffect(() => {
697
+ * const subscription = AppState.addEventListener("change", (state) => {
698
+ * if (state === "active") {
699
+ * client.forceReconnect();
700
+ * }
701
+ * });
702
+ * return () => subscription.remove();
703
+ * }, [client]);
704
+ * ```
705
+ */
706
+ forceReconnect: () => Promise<void>;
707
+ /**
708
+ * Reconnect the WebSocket connection only if the current connection is unhealthy.
709
+ * This is useful when the application returns from a background state and wants to
710
+ * ensure a healthy connection exists, but doesn't need to force a reconnection if
711
+ * the connection is still active.
712
+ *
713
+ * This method will:
714
+ * - Check if an existing connection is healthy (readyState === OPEN)
715
+ * - If healthy, return immediately without reconnecting
716
+ * - If unhealthy, perform the same reconnection logic as forceReconnect()
717
+ *
718
+ * Use this when you want efficient reconnection that avoids unnecessary disconnects.
719
+ *
720
+ * @example
721
+ * ```typescript
722
+ * // React Native example: reconnect only if needed when app comes to foreground
723
+ * useEffect(() => {
724
+ * const subscription = AppState.addEventListener("change", (state) => {
725
+ * if (state === "active") {
726
+ * client.reconnectIfNeeded();
727
+ * }
728
+ * });
729
+ * return () => subscription.remove();
730
+ * }, [client]);
731
+ * ```
732
+ */
733
+ reconnectIfNeeded: () => Promise<void>;
734
+ /**
735
+ * Internal method to handle reconnection logic for both forceReconnect and reconnectIfNeeded.
736
+ */
737
+ private reconnect;
738
+ /**
739
+ * If using websocket mode, close the connection when done.
740
+ * In offline mode, this is a no-op.
741
+ */
742
+ cleanup: () => Promise<void>;
743
+ /**
744
+ * Calculate the delay for the next reconnection attempt using exponential backoff with jitter.
745
+ * This helps prevent dogpiling when the server recovers from an outage.
746
+ */
747
+ private calculateReconnectDelay;
748
+ /**
749
+ * Handle browser going offline
750
+ */
751
+ private handleNetworkOffline;
752
+ /**
753
+ * Handle browser coming back online
754
+ */
755
+ private handleNetworkOnline;
756
+ /**
757
+ * Attempt to reconnect the WebSocket connection with exponential backoff.
758
+ * Called automatically when the connection closes unexpectedly.
759
+ */
760
+ private attemptReconnect;
761
+ private wsConnect;
762
+ private wsConnectOnce;
763
+ private wsSendMessage;
764
+ /**
765
+ * State management
766
+ */
767
+ getIsPending: () => boolean;
768
+ addIsPendingListener: (listener: PendingListenerFn) => () => void;
769
+ private setIsPending;
770
+ getPlan: () => CheckPlanReturn | undefined;
771
+ getFlagCheck: (flagKey: string) => CheckFlagReturn | undefined;
772
+ getFlagValue: (flagKey: string) => boolean | undefined;
773
+ /** Register an event listener that will be notified with the boolean value for a given flag when this value changes */
774
+ addFlagValueListener: (flagKey: string, listener: FlagValueListenerFn) => () => void;
775
+ /** Register an event listener that will be notified with the full flag check response for a given flag whenever this value changes */
776
+ addFlagCheckListener: (flagKey: string, listener: FlagCheckListenerFn) => () => void;
777
+ addPlanListener: (listener: PlanListenerFn) => () => void;
778
+ private notifyFlagCheckListeners;
779
+ /** Add or update a CheckFlagReturn in the featureUsageEventMap */
780
+ private updateFeatureUsageEventMap;
781
+ private notifyFlagValueListeners;
782
+ private notifyPlanListeners;
783
+ }
784
+
785
+ /** Context for checking flags and sending events */
786
+ export declare type SchematicContext = {
787
+ company?: Keys;
788
+ user?: Keys;
789
+ };
45
790
 
46
791
  declare interface SchematicContextProps {
47
792
  client: SchematicJS.Schematic;
@@ -51,7 +796,87 @@ export declare interface SchematicHookOpts {
51
796
  client?: SchematicJS.Schematic;
52
797
  }
53
798
 
54
- export { SchematicOptions }
799
+ declare namespace SchematicJS {
800
+ export {
801
+ CheckFlagResponseFromJSON,
802
+ CheckFlagsResponseFromJSON,
803
+ DatastreamCompanyPlanFromJSON,
804
+ EventBodyFlagCheckToJSON,
805
+ BooleanListenerFn,
806
+ CheckFlagResponseData,
807
+ CheckFlagReturn,
808
+ CheckFlagReturnFromJSON,
809
+ CheckFlagReturnListenerFn,
810
+ CheckOptions,
811
+ CheckPlanReturn,
812
+ CheckPlanReturnFromJSON,
813
+ CheckPlanReturnListenerFn,
814
+ EmptyListenerFn,
815
+ Event_2 as Event,
816
+ EventBody,
817
+ EventBodyFlagCheck,
818
+ EventBodyIdentify,
819
+ EventBodyTrack,
820
+ EventType,
821
+ FlagCheckListenerFn,
822
+ FlagValueListenerFn,
823
+ Keys,
824
+ PendingListenerFn,
825
+ PlanListenerFn,
826
+ RuleType,
827
+ Schematic,
828
+ SchematicContext,
829
+ SchematicOptions,
830
+ StoragePersister,
831
+ Traits,
832
+ TrialStatus,
833
+ UsagePeriod
834
+ }
835
+ }
836
+
837
+ export declare type SchematicOptions = {
838
+ /** Optionally provide any additional headers to include in the request */
839
+ additionalHeaders?: Record<string, string>;
840
+ /** Optionally provide a custom API URL */
841
+ apiUrl?: string;
842
+ /** Enable debug mode to log flag check results and events to the console.
843
+ * Can also be enabled at runtime via URL query parameter "schematic_debug=true" */
844
+ debug?: boolean;
845
+ /** Optionally provide a custom event URL */
846
+ eventUrl?: string;
847
+ /** Enable offline mode to prevent all network requests.
848
+ * When enabled, events are only logged not sent, and flag checks return fallback values.
849
+ * Can also be enabled at runtime via URL query parameter "schematic_offline=true" */
850
+ offline?: boolean;
851
+ /** Optionally provide a custom storage persister for client-side storage */
852
+ storage?: StoragePersister;
853
+ /** Use a WebSocket connection for real-time flag checks; if using this, run the cleanup function to close the connection */
854
+ useWebSocket?: boolean;
855
+ /** Optionally provide a custom WebSocket URL */
856
+ webSocketUrl?: string;
857
+ /** WebSocket connection timeout in milliseconds (default: 10000) */
858
+ webSocketConnectionTimeout?: number;
859
+ /** Enable automatic reconnection on WebSocket disconnect (default: true) */
860
+ webSocketReconnect?: boolean;
861
+ /** Maximum number of reconnection attempts (default: 7, set to Infinity for unlimited) */
862
+ webSocketMaxReconnectAttempts?: number;
863
+ /** Initial retry delay in milliseconds for exponential backoff (default: 1000) */
864
+ webSocketInitialRetryDelay?: number;
865
+ /** Maximum retry delay in milliseconds for exponential backoff (default: 30000) */
866
+ webSocketMaxRetryDelay?: number;
867
+ /** Maximum number of events to queue for retry when network is down (default: 100) */
868
+ maxEventQueueSize?: number;
869
+ /** Maximum number of retry attempts for failed events (default: 5) */
870
+ maxEventRetries?: number;
871
+ /** Initial retry delay in milliseconds for failed events (default: 1000) */
872
+ eventRetryInitialDelay?: number;
873
+ /** Maximum retry delay in milliseconds for failed events (default: 30000) */
874
+ eventRetryMaxDelay?: number;
875
+ /** Default boolean values for flags when Schematic API cannot be reached and no callsite fallback is provided */
876
+ flagValueDefaults?: Record<string, boolean>;
877
+ /** Default CheckFlagReturn objects for flags when Schematic API cannot be reached and no callsite fallback is provided */
878
+ flagCheckDefaults?: Record<string, CheckFlagReturn>;
879
+ };
55
880
 
56
881
  export declare const SchematicProvider: React_2.FC<SchematicProviderProps>;
57
882
 
@@ -67,13 +892,36 @@ declare type SchematicProviderPropsWithPublishableKey = BaseSchematicProviderPro
67
892
  publishableKey: string;
68
893
  };
69
894
 
70
- export { StoragePersister }
895
+ /** Optional type for implementing custom client-side storage */
896
+ export declare type StoragePersister = {
897
+ setItem(key: string, value: any): void;
898
+ getItem(key: string): any;
899
+ removeItem(key: string): void;
900
+ };
71
901
 
72
- export { Traits }
902
+ /**
903
+ * A flexible key/value type that can store any type of value on a company or user.
904
+ */
905
+ export declare type Traits = Record<string, any>;
906
+
907
+ /**
908
+ *
909
+ * @export
910
+ */
911
+ export declare const TrialStatus: {
912
+ readonly Active: "active";
913
+ readonly Converted: "converted";
914
+ readonly Expired: "expired";
915
+ };
73
916
 
74
- export { TrialStatus }
917
+ export declare type TrialStatus = (typeof TrialStatus)[keyof typeof TrialStatus];
75
918
 
76
- export { UsagePeriod }
919
+ export declare enum UsagePeriod {
920
+ ALL_TIME = "all_time",
921
+ CURRENT_DAY = "current_day",
922
+ CURRENT_MONTH = "current_month",
923
+ CURRENT_WEEK = "current_week"
924
+ }
77
925
 
78
926
  export declare const useSchematic: () => SchematicContextProps;
79
927
 
@@ -762,6 +762,7 @@ var UsagePeriod = /* @__PURE__ */ ((UsagePeriod2) => {
762
762
  var CheckFlagReturnFromJSON = (json) => {
763
763
  const {
764
764
  companyId,
765
+ entitlement,
765
766
  error,
766
767
  featureAllocation,
767
768
  featureUsage,
@@ -779,20 +780,27 @@ var CheckFlagReturnFromJSON = (json) => {
779
780
  const featureUsageExceeded = !value && // if flag is not false, then we haven't exceeded usage
780
781
  (ruleType == "company_override_usage_exceeded" || // if the rule type is one of these, then we have exceeded usage
781
782
  ruleType == "plan_entitlement_usage_exceeded");
783
+ const resolvedAllocation = entitlement?.allocation ?? featureAllocation;
784
+ const resolvedUsage = entitlement?.usage ?? featureUsage;
785
+ const resolvedEvent = entitlement?.eventName ?? featureUsageEvent;
786
+ const resolvedPeriod = entitlement?.metricPeriod ?? featureUsagePeriod;
787
+ const resolvedResetAt = entitlement?.metricResetAt ?? featureUsageResetAt;
782
788
  return {
783
789
  featureUsageExceeded,
784
790
  companyId: companyId == null ? void 0 : companyId,
791
+ creditRemaining: entitlement?.creditRemaining == null ? void 0 : entitlement.creditRemaining,
785
792
  error: error == null ? void 0 : error,
786
- featureAllocation: featureAllocation == null ? void 0 : featureAllocation,
787
- featureUsage: featureUsage == null ? void 0 : featureUsage,
788
- featureUsageEvent: featureUsageEvent === null ? void 0 : featureUsageEvent,
789
- featureUsagePeriod: featureUsagePeriod == null ? void 0 : featureUsagePeriod,
790
- featureUsageResetAt: featureUsageResetAt == null ? void 0 : featureUsageResetAt,
793
+ featureAllocation: resolvedAllocation == null ? void 0 : resolvedAllocation,
794
+ featureUsage: resolvedUsage == null ? void 0 : resolvedUsage,
795
+ featureUsageEvent: resolvedEvent == null ? void 0 : resolvedEvent,
796
+ featureUsagePeriod: resolvedPeriod == null ? void 0 : resolvedPeriod,
797
+ featureUsageResetAt: resolvedResetAt == null ? void 0 : resolvedResetAt,
791
798
  flag,
792
799
  flagId: flagId == null ? void 0 : flagId,
793
800
  reason,
794
801
  ruleId: ruleId == null ? void 0 : ruleId,
795
802
  ruleType: ruleType == null ? void 0 : ruleType,
803
+ softLimit: entitlement?.softLimit == null ? void 0 : entitlement.softLimit,
796
804
  userId: userId == null ? void 0 : userId,
797
805
  value
798
806
  };
@@ -820,7 +828,7 @@ function contextString(context) {
820
828
  }, {});
821
829
  return JSON.stringify(sortedContext);
822
830
  }
823
- var version = "1.3.1";
831
+ var version = "1.4.0";
824
832
  var anonymousIdKey = "schematicId";
825
833
  var Schematic = class {
826
834
  additionalHeaders = {};
@@ -2257,7 +2265,7 @@ var notifyPlanListener = (listener, value) => {
2257
2265
  import React, { createContext, useEffect, useMemo, useRef } from "react";
2258
2266
 
2259
2267
  // src/version.ts
2260
- var version2 = "1.3.1";
2268
+ var version2 = "1.4.1";
2261
2269
 
2262
2270
  // src/context/schematic.tsx
2263
2271
  import { jsx } from "react/jsx-runtime";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@schematichq/schematic-react",
3
- "version": "1.3.1",
3
+ "version": "1.4.1",
4
4
  "main": "dist/schematic-react.cjs.js",
5
5
  "module": "dist/schematic-react.esm.js",
6
6
  "types": "dist/schematic-react.d.ts",
@@ -30,12 +30,10 @@
30
30
  "tsc": "npx tsc",
31
31
  "prepare": "husky"
32
32
  },
33
- "dependencies": {
34
- "@schematichq/schematic-js": "^1.3.1"
35
- },
36
33
  "devDependencies": {
37
34
  "@eslint/js": "^10.0.1",
38
- "@microsoft/api-extractor": "^7.58.1",
35
+ "@microsoft/api-extractor": "^7.58.2",
36
+ "@schematichq/schematic-js": "^1.4.0",
39
37
  "@testing-library/dom": "^10.4.1",
40
38
  "@testing-library/jest-dom": "^6.9.1",
41
39
  "@testing-library/react": "^16.3.2",
@@ -45,16 +43,16 @@
45
43
  "eslint": "^10.2.0",
46
44
  "eslint-plugin-import": "^2.32.0",
47
45
  "eslint-plugin-react": "^7.37.5",
48
- "eslint-plugin-react-hooks": "^7.0.1",
49
- "globals": "^17.4.0",
50
- "happy-dom": "^20.8.9",
46
+ "eslint-plugin-react-hooks": "^7.1.0",
47
+ "globals": "^17.5.0",
48
+ "happy-dom": "^20.9.0",
51
49
  "husky": "^9.1.7",
52
- "jsdom": "^29.0.1",
53
- "prettier": "^3.8.1",
54
- "react": "^19.2.4",
55
- "react-dom": "^19.2.4",
56
- "typescript": "^6.0.2",
57
- "typescript-eslint": "^8.58.0",
50
+ "jsdom": "^29.0.2",
51
+ "prettier": "^3.8.3",
52
+ "react": "^19.2.5",
53
+ "react-dom": "^19.2.5",
54
+ "typescript": "^6.0.3",
55
+ "typescript-eslint": "^8.58.2",
58
56
  "vitest": "^4.0.18"
59
57
  },
60
58
  "peerDependencies": {