@plyaz/types 1.3.7 → 1.3.8

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.
@@ -331,6 +331,24 @@ export interface FeatureFlagHelpers<FeatureFlagKey extends string = string> {
331
331
  isAllEnabled: (keys: FeatureFlagKey[], context?: FeatureFlagContext) => Promise<boolean>;
332
332
  whenEnabled: <T>(key: FeatureFlagKey, callback: () => T | Promise<T>, fallback?: () => T | Promise<T>, context?: FeatureFlagContext) => Promise<T | undefined>;
333
333
  }
334
+ /**
335
+ * Response structure for fetching feature flag data.
336
+ * Contains both flags and their associated rules.
337
+ *
338
+ * @template FeatureFlagKey - Type of feature flag keys
339
+ *
340
+ * @example
341
+ * ```typescript
342
+ * const response: FetchFeatureFlagDataResponse<AppFeatures> = {
343
+ * flags: [
344
+ * { key: 'newUI', name: 'New UI', value: true, isEnabled: true }
345
+ * ],
346
+ * rules: [
347
+ * { flagKey: 'newUI', conditions: { userRole: 'beta' }, value: true }
348
+ * ]
349
+ * };
350
+ * ```
351
+ */
334
352
  export interface FetchFeatureFlagDataResponse<FeatureFlagKey extends string> {
335
353
  flags: FeatureFlag<FeatureFlagKey>[];
336
354
  rules: FeatureFlagRule<FeatureFlagKey>[];
@@ -460,66 +478,257 @@ export interface ModuleConfigurationTestInput {
460
478
  config: Record<string, unknown>;
461
479
  expectedConfig: Record<string, unknown>;
462
480
  }
481
+ /**
482
+ * Input for provider type test cases.
483
+ * Tests different provider implementations.
484
+ *
485
+ * @example
486
+ * ```typescript
487
+ * const input: ProviderTypeTestInput = {
488
+ * provider: 'LaunchDarklyProvider'
489
+ * };
490
+ * ```
491
+ */
463
492
  export interface ProviderTypeTestInput {
493
+ /** Name or type of the provider to test */
464
494
  provider: string;
465
495
  }
496
+ /**
497
+ * Input for feature flag operation test cases.
498
+ * Tests CRUD and evaluation operations on flags.
499
+ *
500
+ * @template FeatureFlagKey - Type of feature flag keys
501
+ *
502
+ * @example
503
+ * ```typescript
504
+ * const input: FlagOperationTestInput<'darkMode'> = {
505
+ * operation: 'evaluate',
506
+ * flagKey: 'darkMode',
507
+ * context: { userId: '123', userRole: 'admin' },
508
+ * expectedResult: true
509
+ * };
510
+ * ```
511
+ */
466
512
  export interface FlagOperationTestInput<FeatureFlagKey extends string> {
513
+ /** Type of operation to perform */
467
514
  operation: 'create' | 'update' | 'delete' | 'evaluate';
515
+ /** Key of the flag to operate on */
468
516
  flagKey: FeatureFlagKey;
517
+ /** Data for create/update operations */
469
518
  data?: unknown;
519
+ /** Context for evaluation operations */
470
520
  context?: FeatureFlagContext;
521
+ /** Expected result of the operation */
471
522
  expectedResult?: unknown;
472
523
  }
524
+ /**
525
+ * Input for permission test cases.
526
+ * Tests role-based access control for operations.
527
+ *
528
+ * @example
529
+ * ```typescript
530
+ * const input: PermissionTestInput = {
531
+ * role: 'editor',
532
+ * operation: 'update',
533
+ * resource: 'feature-flag',
534
+ * expected: true
535
+ * };
536
+ * ```
537
+ */
473
538
  export interface PermissionTestInput {
539
+ /** User role to test */
474
540
  role: string;
541
+ /** Operation being performed */
475
542
  operation: string;
543
+ /** Resource being accessed */
476
544
  resource: string;
545
+ /** Expected permission result */
477
546
  expected: boolean;
478
547
  }
548
+ /**
549
+ * Input for cache operation test cases.
550
+ * Tests caching behavior for feature flags.
551
+ *
552
+ * @template FeatureFlagKey - Type of feature flag keys
553
+ *
554
+ * @example
555
+ * ```typescript
556
+ * const input: CacheOperationTestInput<'apiLimit'> = {
557
+ * operation: 'refresh',
558
+ * flagKey: 'apiLimit',
559
+ * expectedBehavior: 'Cache should be updated with new value'
560
+ * };
561
+ * ```
562
+ */
479
563
  export interface CacheOperationTestInput<FeatureFlagKey extends string> {
564
+ /** Cache operation to perform */
480
565
  operation: 'set' | 'get' | 'refresh' | 'clear';
566
+ /** Flag key for operations (optional for 'clear') */
481
567
  flagKey?: FeatureFlagKey;
568
+ /** Description of expected cache behavior */
482
569
  expectedBehavior: string;
483
570
  }
571
+ /**
572
+ * Input for rule evaluation test cases.
573
+ * Tests conditional flag value evaluation based on rules.
574
+ *
575
+ * @template FeatureFlagKey - Type of feature flag keys
576
+ *
577
+ * @example
578
+ * ```typescript
579
+ * const input: RuleEvaluationTestInput<'discount'> = {
580
+ * flagKey: 'discount',
581
+ * rules: [
582
+ * { conditions: { userType: 'premium' }, value: 0.2 },
583
+ * { conditions: { userType: 'regular' }, value: 0.1 }
584
+ * ],
585
+ * context: { userType: 'premium' },
586
+ * expectedValue: 0.2
587
+ * };
588
+ * ```
589
+ */
484
590
  export interface RuleEvaluationTestInput<FeatureFlagKey extends string> {
591
+ /** Flag key to evaluate */
485
592
  flagKey: FeatureFlagKey;
593
+ /** Array of rules with conditions and values */
486
594
  rules: Array<{
595
+ /** Conditions that must be met */
487
596
  conditions: Record<string, unknown>;
597
+ /** Value to return if conditions match */
488
598
  value: FeatureFlagValue;
489
599
  }>;
600
+ /** Context for rule evaluation */
490
601
  context: FeatureFlagContext;
602
+ /** Expected evaluated value */
491
603
  expectedValue: FeatureFlagValue;
492
604
  }
605
+ /**
606
+ * Input for batch operation test cases.
607
+ * Tests multiple flag operations executed together.
608
+ *
609
+ * @template FeatureFlagKey - Type of feature flag keys
610
+ *
611
+ * @example
612
+ * ```typescript
613
+ * const input: BatchOperationTestInput<AppFeatures> = {
614
+ * operations: [
615
+ * { type: 'create', flagKey: 'feature1', data: { value: true } },
616
+ * { type: 'update', flagKey: 'feature2', data: { isEnabled: false } },
617
+ * { type: 'delete', flagKey: 'feature3' }
618
+ * ],
619
+ * expectedResults: [true, true, true]
620
+ * };
621
+ * ```
622
+ */
493
623
  export interface BatchOperationTestInput<FeatureFlagKey extends string> {
624
+ /** Array of operations to execute in batch */
494
625
  operations: Array<{
626
+ /** Type of operation */
495
627
  type: 'create' | 'update' | 'delete';
628
+ /** Flag key to operate on */
496
629
  flagKey: FeatureFlagKey;
630
+ /** Operation data (for create/update) */
497
631
  data?: unknown;
498
632
  }>;
633
+ /** Expected results for each operation */
499
634
  expectedResults: unknown[];
500
635
  }
636
+ /**
637
+ * Input for validation test cases.
638
+ * Tests input validation for various fields.
639
+ *
640
+ * @example
641
+ * ```typescript
642
+ * const input: ValidationTestInput = {
643
+ * input: 'invalid-email',
644
+ * field: 'email',
645
+ * expectedError: 'Invalid email format',
646
+ * isValid: false
647
+ * };
648
+ * ```
649
+ */
501
650
  export interface ValidationTestInput {
651
+ /** Input value to validate */
502
652
  input: unknown;
653
+ /** Field name being validated */
503
654
  field: string;
655
+ /** Expected error message if validation fails */
504
656
  expectedError?: string;
657
+ /** Whether the input should be valid */
505
658
  isValid: boolean;
506
659
  }
660
+ /**
661
+ * Input for dynamic module configuration test cases.
662
+ * Tests module setup with different configurations.
663
+ *
664
+ * @example
665
+ * ```typescript
666
+ * const input: DynamicModuleTestInput = {
667
+ * config: { apiKey: 'test-key', environment: 'staging' },
668
+ * expectedProviders: ['ConfigService', 'FeatureFlagService'],
669
+ * expectedImports: ['HttpModule'],
670
+ * expectedExports: ['FeatureFlagService']
671
+ * };
672
+ * ```
673
+ */
507
674
  export interface DynamicModuleTestInput {
675
+ /** Module configuration object */
508
676
  config: Record<string, unknown>;
677
+ /** Expected providers to be registered */
509
678
  expectedProviders?: string[];
679
+ /** Expected modules to be imported */
510
680
  expectedImports?: string[];
681
+ /** Expected services to be exported */
511
682
  expectedExports?: string[];
512
683
  }
684
+ /**
685
+ * Input for async module configuration test cases.
686
+ * Tests asynchronous module setup with factory functions.
687
+ *
688
+ * @example
689
+ * ```typescript
690
+ * const input: AsyncModuleConfigTestInput = {
691
+ * imports: ['ConfigModule'],
692
+ * inject: ['ConfigService'],
693
+ * useFactory: (config) => ({ apiKey: config.get('API_KEY') }),
694
+ * expectedImports: ['ConfigModule', 'HttpModule'],
695
+ * expectedProviders: ['FeatureFlagService']
696
+ * };
697
+ * ```
698
+ */
513
699
  export interface AsyncModuleConfigTestInput {
700
+ /** Modules to import */
514
701
  imports?: string[];
702
+ /** Services to inject into factory */
515
703
  inject?: string[];
704
+ /** Factory function for configuration */
516
705
  useFactory?: (...args: unknown[]) => Record<string, unknown>;
706
+ /** Expected modules in result */
517
707
  expectedImports?: string[];
708
+ /** Expected providers in result */
518
709
  expectedProviders?: string[];
519
710
  }
711
+ /**
712
+ * Input for repository operation test cases.
713
+ * Tests data persistence operations for feature flags.
714
+ *
715
+ * @example
716
+ * ```typescript
717
+ * const input: RepositoryOperationTestInput = {
718
+ * operation: 'create',
719
+ * data: { key: 'newFeature', value: true },
720
+ * expectedResult: { id: 1, key: 'newFeature', value: true },
721
+ * shouldThrow: false
722
+ * };
723
+ * ```
724
+ */
520
725
  export interface RepositoryOperationTestInput {
726
+ /** Repository operation to test */
521
727
  operation: 'create' | 'update' | 'delete' | 'find';
728
+ /** Data for the operation */
522
729
  data?: unknown;
730
+ /** Expected operation result */
523
731
  expectedResult?: unknown;
732
+ /** Whether operation should throw an error */
524
733
  shouldThrow?: boolean;
525
734
  }
@@ -411,3 +411,164 @@ export interface CreateTableDrivenTestOptions<TInput, TExpected> {
411
411
  expected?: TExpected;
412
412
  }) => void | Promise<void>;
413
413
  }
414
+ /**
415
+ * Statistics collected for operation performance tracking.
416
+ * Provides detailed metrics about operation execution.
417
+ *
418
+ * @example
419
+ * ```typescript
420
+ * const stats: OperationStats = {
421
+ * count: 1000,
422
+ * totalDuration: 45000,
423
+ * avgDuration: 45,
424
+ * minDuration: 10,
425
+ * maxDuration: 250,
426
+ * errorCount: 3,
427
+ * errors: [new Error('Timeout'), new Error('Connection failed')]
428
+ * };
429
+ *
430
+ * console.log(`Average operation time: ${stats.avgDuration}ms`);
431
+ * console.log(`Error rate: ${(stats.errorCount / stats.count * 100).toFixed(2)}%`);
432
+ * ```
433
+ *
434
+ * @public
435
+ */
436
+ export interface OperationStats {
437
+ /** Total number of operations executed */
438
+ count: number;
439
+ /** Total duration of all operations in milliseconds */
440
+ totalDuration: number;
441
+ /** Average duration per operation in milliseconds */
442
+ avgDuration: number;
443
+ /** Minimum operation duration in milliseconds */
444
+ minDuration: number;
445
+ /** Maximum operation duration in milliseconds */
446
+ maxDuration: number;
447
+ /** Number of operations that resulted in errors */
448
+ errorCount: number;
449
+ /** Array of errors encountered during operations */
450
+ errors: Error[];
451
+ }
452
+ /**
453
+ * Interface for tracking and measuring operation performance.
454
+ * Collects timing and error statistics for named operations.
455
+ *
456
+ * @example
457
+ * ```typescript
458
+ * const tracker: OperationTracker = createOperationTracker();
459
+ *
460
+ * // Track an operation
461
+ * const result = await tracker.track('createUser', async () => {
462
+ * return await userService.create({ name: 'John' });
463
+ * });
464
+ *
465
+ * // Get statistics
466
+ * const stats = tracker.getStats();
467
+ * console.log(stats.createUser.avgDuration);
468
+ *
469
+ * // Reset tracking data
470
+ * tracker.reset();
471
+ * ```
472
+ *
473
+ * @public
474
+ */
475
+ export interface OperationTracker {
476
+ /** Track execution of a named operation */
477
+ track: <T>(operationName: string, operation: () => Promise<T>) => Promise<T>;
478
+ /** Get statistics for all tracked operations */
479
+ getStats: () => Record<string, OperationStats>;
480
+ /** Reset all tracking data */
481
+ reset: () => void;
482
+ }
483
+ /**
484
+ * Test operation for stress testing
485
+ *
486
+ * @public
487
+ */
488
+ export interface TestOperation {
489
+ name: string;
490
+ operation: () => unknown | Promise<unknown>;
491
+ weight?: number;
492
+ }
493
+ /**
494
+ * Context information for worker threads in stress testing.
495
+ * Tracks the state and configuration for individual worker processes.
496
+ *
497
+ * @template TestOperation - Type of test operations being executed
498
+ *
499
+ * @example
500
+ * ```typescript
501
+ * const context: WorkerContext<TestOperation> = {
502
+ * workerId: 1,
503
+ * operationsPerWorker: 1000,
504
+ * operations: [createOp, updateOp, deleteOp],
505
+ * totalWeight: 10,
506
+ * operationCounts: { create: 400, update: 400, delete: 200 },
507
+ * errors: [],
508
+ * delayBetweenOps: 50
509
+ * };
510
+ * ```
511
+ *
512
+ * @public
513
+ */
514
+ export interface WorkerContext<TestOperation> {
515
+ /** Unique identifier for this worker thread */
516
+ workerId: number;
517
+ /** Number of operations this worker should execute */
518
+ operationsPerWorker: number;
519
+ /** Available operations to execute */
520
+ operations: TestOperation[];
521
+ /** Total weight sum for weighted random selection */
522
+ totalWeight: number;
523
+ /** Tracking counts for each operation type */
524
+ operationCounts: Record<string, number>;
525
+ /** Errors encountered during execution */
526
+ errors: Array<{
527
+ operation: string;
528
+ error: Error;
529
+ }>;
530
+ /** Delay in milliseconds between operations */
531
+ delayBetweenOps: number;
532
+ }
533
+ /**
534
+ * Resolved configuration for worker-based stress testing.
535
+ * Contains the final computed values after applying defaults.
536
+ *
537
+ * @example
538
+ * ```typescript
539
+ * const config: ResolvedWorkerConfig = {
540
+ * workerCount: 4,
541
+ * operationsPerWorker: 1000,
542
+ * operations: [
543
+ * { name: 'create', operation: createUser, weight: 2 },
544
+ * { name: 'read', operation: readUser, weight: 5 },
545
+ * { name: 'update', operation: updateUser, weight: 2 },
546
+ * { name: 'delete', operation: deleteUser, weight: 1 }
547
+ * ]
548
+ * };
549
+ * ```
550
+ *
551
+ * @public
552
+ */
553
+ export interface ResolvedWorkerConfig {
554
+ /** Number of worker threads to spawn */
555
+ workerCount: number;
556
+ /** Operations each worker should perform */
557
+ operationsPerWorker: number;
558
+ /** Test operations to be distributed among workers */
559
+ operations: TestOperation[];
560
+ }
561
+ /**
562
+ * Stress test configuration
563
+ *
564
+ * @public
565
+ */
566
+ export interface StressTestConfig {
567
+ workerCount?: number;
568
+ operationsPerWorker?: number;
569
+ delayBetweenOps?: number;
570
+ operations?: TestOperation[];
571
+ operationCount?: number;
572
+ concurrency?: number;
573
+ operationMix?: Record<string, number>;
574
+ }
@@ -101,14 +101,62 @@ export interface WrapperComponent {
101
101
  export interface ChildrenProps {
102
102
  children: React.ReactNode;
103
103
  }
104
+ /**
105
+ * Options for configuring test wrapper components.
106
+ * Allows specifying providers and their props for test setup.
107
+ *
108
+ * @example
109
+ * ```typescript
110
+ * const options: WrapperOptions = {
111
+ * providers: [ThemeProvider, AuthProvider],
112
+ * providerProps: {
113
+ * theme: darkTheme,
114
+ * user: mockUser
115
+ * }
116
+ * };
117
+ * ```
118
+ */
104
119
  export interface WrapperOptions {
120
+ /** Array of provider components to wrap tests with */
105
121
  providers?: Array<React.ComponentType<{
106
122
  children: React.ReactNode;
107
123
  }>>;
124
+ /** Props to pass to the providers */
108
125
  providerProps?: Record<string, unknown>;
109
126
  }
127
+ /**
128
+ * Extended render options combining RTL options with wrapper configuration.
129
+ * Provides a unified interface for rendering components with providers.
130
+ *
131
+ * @example
132
+ * ```typescript
133
+ * const options: GenericRenderOptions = {
134
+ * providers: [QueryClientProvider],
135
+ * providerProps: { client: queryClient },
136
+ * container: document.body
137
+ * };
138
+ *
139
+ * const { getByText } = render(<MyComponent />, options);
140
+ * ```
141
+ */
110
142
  export interface GenericRenderOptions extends Omit<RenderOptions, 'wrapper'>, WrapperOptions {
111
143
  }
144
+ /**
145
+ * Extended render hook options combining RTL hook options with wrapper configuration.
146
+ * Provides a unified interface for rendering hooks with providers.
147
+ *
148
+ * @template TProps - Type of props passed to the hook
149
+ *
150
+ * @example
151
+ * ```typescript
152
+ * const options: GenericRenderHookOptions<UseCounterProps> = {
153
+ * providers: [CounterProvider],
154
+ * initialProps: { initialValue: 10 }
155
+ * };
156
+ *
157
+ * const { result } = renderHook(() => useCounter(props), options);
158
+ * ```
159
+ */
112
160
  export interface GenericRenderHookOptions<TProps> extends Omit<RenderHookOptions<TProps>, 'wrapper'>, WrapperOptions {
113
161
  }
114
162
  /**
@@ -139,8 +187,25 @@ export interface ErrorBoundaryState {
139
187
  export type RenderWithRouterResult = TestingLibraryReact.RenderResult & {
140
188
  router: MockNextRouter;
141
189
  };
190
+ /**
191
+ * Props for error boundary components in testing.
192
+ * Catches errors in child components during rendering.
193
+ *
194
+ * @example
195
+ * ```typescript
196
+ * const props: ErrorBoundaryProps = {
197
+ * children: <ComponentThatMightThrow />,
198
+ * onError: (error, errorInfo) => {
199
+ * console.error('Component error:', error);
200
+ * console.log('Error stack:', errorInfo.componentStack);
201
+ * }
202
+ * };
203
+ * ```
204
+ */
142
205
  export interface ErrorBoundaryProps {
206
+ /** Child components to wrap */
143
207
  children: React.ReactNode;
208
+ /** Callback when error is caught */
144
209
  onError?: (error: Error, errorInfo: React.ErrorInfo) => void;
145
210
  }
146
211
  /**
@@ -173,72 +238,284 @@ export interface MockProviderContextValue<T> {
173
238
  setValue: (value: T) => void;
174
239
  reset: () => void;
175
240
  }
241
+ /**
242
+ * Props for mock provider components.
243
+ * Allows setting initial values for testing contexts.
244
+ *
245
+ * @template T - Type of the provided value
246
+ *
247
+ * @example
248
+ * ```typescript
249
+ * const props: MockProviderProps<User> = {
250
+ * children: <UserProfile />,
251
+ * initialValue: { id: 1, name: 'Test User' }
252
+ * };
253
+ * ```
254
+ */
176
255
  export interface MockProviderProps<T> {
256
+ /** Child components to provide context to */
177
257
  children: React.ReactNode;
258
+ /** Initial value for the mock context */
178
259
  initialValue?: T;
179
260
  }
261
+ /**
262
+ * Return type for createMockProvider function.
263
+ * Provides components and hooks for testing with mock contexts.
264
+ *
265
+ * @template T - Type of the provided value
266
+ *
267
+ * @example
268
+ * ```typescript
269
+ * const mockAuth: MockProviderReturn<AuthState> = createMockProvider();
270
+ *
271
+ * // Use in tests
272
+ * render(
273
+ * <mockAuth.Provider initialValue={testAuth}>
274
+ * <AuthenticatedComponent />
275
+ * </mockAuth.Provider>
276
+ * );
277
+ *
278
+ * // Access context in tests
279
+ * const { value, setValue } = mockAuth.useValue();
280
+ * ```
281
+ */
180
282
  export interface MockProviderReturn<T> {
283
+ /** Mock provider component */
181
284
  Provider: React.FC<MockProviderProps<T>>;
285
+ /** Hook to access mock context value */
182
286
  useValue: () => MockProviderContextValue<T>;
287
+ /** The React context instance */
183
288
  Context: React.Context<MockProviderContextValue<T> | null>;
184
289
  }
290
+ /**
291
+ * Props for tracked provider components.
292
+ * Monitors render counts and value changes for performance testing.
293
+ *
294
+ * @template T - Type of the provided value
295
+ *
296
+ * @example
297
+ * ```typescript
298
+ * const props: TrackedProviderProps<Config> = {
299
+ * children: <ConfigConsumer />,
300
+ * value: { theme: 'dark', locale: 'en' },
301
+ * onRender: (count) => console.log(`Rendered ${count} times`)
302
+ * };
303
+ * ```
304
+ */
185
305
  export interface TrackedProviderProps<T> {
306
+ /** Child components to track */
186
307
  children: React.ReactNode;
308
+ /** Value to provide and track */
187
309
  value: T;
310
+ /** Callback on each render with count */
188
311
  onRender?: (count: number) => void;
189
312
  }
313
+ /**
314
+ * Return type for createTrackedProvider function.
315
+ * Provides tracking capabilities for renders and updates.
316
+ *
317
+ * @template T - Type of the tracked value
318
+ *
319
+ * @example
320
+ * ```typescript
321
+ * const tracker: TrackedProviderReturn<State> = createTrackedProvider();
322
+ *
323
+ * // Check render performance
324
+ * expect(tracker.getRenderCount()).toBeLessThan(3);
325
+ *
326
+ * // Verify update history
327
+ * const history = tracker.getUpdateHistory();
328
+ * expect(history[0].value).toEqual(initialState);
329
+ * ```
330
+ */
190
331
  export interface TrackedProviderReturn<T> {
332
+ /** Tracked provider component */
191
333
  Provider: React.FC<TrackedProviderProps<T>>;
334
+ /** Get total render count */
192
335
  getRenderCount: () => number;
336
+ /** Get history of value updates */
193
337
  getUpdateHistory: () => Array<UpdateHistoryEntry<T>>;
338
+ /** Reset tracking data */
194
339
  reset: () => void;
195
340
  }
341
+ /**
342
+ * Context value for async data providers.
343
+ * Manages loading states and error handling for async operations.
344
+ *
345
+ * @template T - Type of the async data
346
+ *
347
+ * @example
348
+ * ```typescript
349
+ * const context: AsyncProviderContextValue<User[]> = {
350
+ * data: undefined,
351
+ * loading: true,
352
+ * error: null,
353
+ * refetch: async () => {
354
+ * // Refetch user data
355
+ * }
356
+ * };
357
+ * ```
358
+ */
196
359
  export interface AsyncProviderContextValue<T> {
360
+ /** The loaded data (undefined while loading) */
197
361
  data: T | undefined;
362
+ /** Loading state indicator */
198
363
  loading: boolean;
364
+ /** Error from failed async operation */
199
365
  error: Error | null;
366
+ /** Function to refetch the data */
200
367
  refetch: () => Promise<void>;
201
368
  }
369
+ /**
370
+ * Props for async provider components.
371
+ * Simple wrapper for components that need async data.
372
+ *
373
+ * @example
374
+ * ```typescript
375
+ * const props: AsyncProviderProps = {
376
+ * children: <DataConsumer />
377
+ * };
378
+ * ```
379
+ */
202
380
  export interface AsyncProviderProps {
381
+ /** Child components that will consume async data */
203
382
  children: React.ReactNode;
204
383
  }
384
+ /**
385
+ * Return type for createAsyncProvider function.
386
+ * Provides components and hooks for async data management.
387
+ *
388
+ * @template T - Type of the async data
389
+ *
390
+ * @example
391
+ * ```typescript
392
+ * const asyncData: AsyncProviderReturn<Products> = createAsyncProvider();
393
+ *
394
+ * // Use in component
395
+ * const { data, loading, error } = asyncData.useAsyncValue();
396
+ * if (loading) return <Spinner />;
397
+ * if (error) return <ErrorMessage error={error} />;
398
+ * return <ProductList products={data} />;
399
+ * ```
400
+ */
205
401
  export interface AsyncProviderReturn<T> {
402
+ /** Async provider component */
206
403
  Provider: React.FC<AsyncProviderProps>;
404
+ /** Hook to access async data state */
207
405
  useAsyncValue: () => AsyncProviderContextValue<T>;
406
+ /** The React context instance */
208
407
  Context: React.Context<AsyncProviderContextValue<T> | null>;
209
408
  }
409
+ /**
410
+ * Props for Suspense wrapper components.
411
+ * Wraps components with React Suspense for async rendering.
412
+ *
413
+ * @example
414
+ * ```typescript
415
+ * const props: SuspenseWrapperProps = {
416
+ * children: <LazyComponent />,
417
+ * fallback: <LoadingSpinner />
418
+ * };
419
+ * ```
420
+ */
210
421
  export interface SuspenseWrapperProps {
422
+ /** Components that might suspend */
211
423
  children: React.ReactNode;
424
+ /** Fallback UI while suspended */
212
425
  fallback?: React.ReactNode;
213
426
  }
427
+ /**
428
+ * Options for creating a comprehensive test wrapper.
429
+ * Combines multiple common testing wrappers in one.
430
+ *
431
+ * @example
432
+ * ```typescript
433
+ * const options: AllProvidersOptions = {
434
+ * includeErrorBoundary: true,
435
+ * includeSuspense: true,
436
+ * includeStrictMode: process.env.NODE_ENV === 'test',
437
+ * errorBoundaryProps: {
438
+ * onError: (error) => console.error('Test error:', error)
439
+ * },
440
+ * suspenseProps: {
441
+ * fallback: <div>Loading...</div>
442
+ * },
443
+ * additionalProviders: [ThemeProvider, I18nProvider]
444
+ * };
445
+ * ```
446
+ */
214
447
  export interface AllProvidersOptions {
448
+ /** Include error boundary wrapper */
215
449
  includeErrorBoundary?: boolean;
450
+ /** Include Suspense wrapper */
216
451
  includeSuspense?: boolean;
452
+ /** Include React StrictMode */
217
453
  includeStrictMode?: boolean;
454
+ /** Props for error boundary */
218
455
  errorBoundaryProps?: {
219
456
  onError?: (error: Error, errorInfo: React.ErrorInfo) => void;
220
457
  };
458
+ /** Props for Suspense wrapper */
221
459
  suspenseProps?: {
222
460
  fallback?: React.ReactNode;
223
461
  };
462
+ /** Additional provider components */
224
463
  additionalProviders?: Array<React.ComponentType<{
225
464
  children: React.ReactNode;
226
465
  }>>;
227
466
  }
467
+ /**
468
+ * Generic context provider interface.
469
+ * Represents the structure of React context providers.
470
+ *
471
+ * @template T - Type of the context value
472
+ *
473
+ * @example
474
+ * ```typescript
475
+ * const AuthContext: ContextProvider<AuthState> = {
476
+ * Provider: AuthProvider,
477
+ * Consumer: AuthConsumer,
478
+ * displayName: 'AuthContext'
479
+ * };
480
+ * ```
481
+ */
228
482
  export interface ContextProvider<T = unknown> {
483
+ /** Provider component that supplies the value */
229
484
  Provider: React.ComponentType<{
230
485
  value: T;
231
486
  children: React.ReactNode;
232
487
  }>;
488
+ /** Consumer component for render prop pattern */
233
489
  Consumer: React.ComponentType<{
234
490
  children: (value: T) => React.ReactNode;
235
491
  }>;
492
+ /** Optional display name for debugging */
236
493
  displayName?: string;
237
494
  }
495
+ /**
496
+ * Configuration options for providers in a stack.
497
+ * Defines how providers are organized and their dependencies.
498
+ *
499
+ * @template T - Type of the provider's value
500
+ *
501
+ * @example
502
+ * ```typescript
503
+ * const config: ProviderConfigOptions<ThemeConfig> = {
504
+ * provider: ThemeContext,
505
+ * value: { mode: 'dark', colors: darkColors },
506
+ * name: 'theme',
507
+ * dependencies: ['i18n'] // Depends on i18n provider
508
+ * };
509
+ * ```
510
+ */
238
511
  export interface ProviderConfigOptions<T = unknown> {
512
+ /** The context provider to configure */
239
513
  provider: ContextProvider<T>;
514
+ /** Value to provide through the context */
240
515
  value: T;
516
+ /** Optional name for identification */
241
517
  name?: string;
518
+ /** Names of providers this depends on */
242
519
  dependencies?: string[];
243
520
  }
244
521
  /**
@@ -296,78 +573,394 @@ export interface WrapperComposition {
296
573
  clear: () => WrapperComposition;
297
574
  clone: () => WrapperComposition;
298
575
  }
576
+ /**
577
+ * Options for creating mock providers.
578
+ * Configures initial state and change handlers.
579
+ *
580
+ * @template T - Type of the mock value
581
+ *
582
+ * @example
583
+ * ```typescript
584
+ * const options: MockProviderOptions<User> = {
585
+ * defaultValue: { id: 1, name: 'John' },
586
+ * onValueChange: (newUser) => {
587
+ * console.log('User changed:', newUser);
588
+ * }
589
+ * };
590
+ * ```
591
+ */
299
592
  export interface MockProviderOptions<T> {
593
+ /** Default value for the mock provider */
300
594
  defaultValue: T;
595
+ /** Callback when value changes */
301
596
  onValueChange?: (newValue: T) => void;
302
597
  }
598
+ /**
599
+ * Options for tracked providers.
600
+ * Configures render tracking and logging.
601
+ *
602
+ * @example
603
+ * ```typescript
604
+ * const options: TrackedProviderOptions = {
605
+ * name: 'UserProvider',
606
+ * logRenders: true // Log each render to console
607
+ * };
608
+ * ```
609
+ */
303
610
  export interface TrackedProviderOptions {
611
+ /** Name for identification in logs */
304
612
  name: string;
613
+ /** Whether to log renders to console */
305
614
  logRenders?: boolean;
306
615
  }
616
+ /**
617
+ * Options for async data providers.
618
+ * Configures data loading and error handling.
619
+ *
620
+ * @template T - Type of the async data
621
+ *
622
+ * @example
623
+ * ```typescript
624
+ * const options: AsyncProviderOptions<Products[]> = {
625
+ * loadData: () => fetchProducts(),
626
+ * fallback: [],
627
+ * onError: (error) => console.error('Failed to load:', error)
628
+ * };
629
+ * ```
630
+ */
307
631
  export interface AsyncProviderOptions<T> {
632
+ /** Function to load async data */
308
633
  loadData: () => Promise<T>;
634
+ /** Fallback value while loading */
309
635
  fallback?: T;
636
+ /** Error handler callback */
310
637
  onError?: (error: Error) => void;
311
638
  }
639
+ /**
640
+ * Options for subscribable providers.
641
+ * Configures providers that support subscription patterns.
642
+ *
643
+ * @template T - Type of the subscribable value
644
+ *
645
+ * @example
646
+ * ```typescript
647
+ * const options: SubscribableProviderOptions<Settings> = {
648
+ * initialValue: { theme: 'light', lang: 'en' },
649
+ * name: 'SettingsProvider'
650
+ * };
651
+ * ```
652
+ */
312
653
  export interface SubscribableProviderOptions<T> {
654
+ /** Initial value for subscribers */
313
655
  initialValue: T;
656
+ /** Provider name for debugging */
314
657
  name: string;
315
658
  }
659
+ /**
660
+ * Options for time-travel providers.
661
+ * Configures state history and undo/redo functionality.
662
+ *
663
+ * @template T - Type of the state to track
664
+ *
665
+ * @example
666
+ * ```typescript
667
+ * const options: TimeTravelProviderOptions<EditorState> = {
668
+ * initialState: { content: '', cursor: 0 },
669
+ * maxHistory: 50, // Keep last 50 states
670
+ * name: 'EditorHistory'
671
+ * };
672
+ * ```
673
+ */
316
674
  export interface TimeTravelProviderOptions<T> {
675
+ /** Initial state value */
317
676
  initialState: T;
677
+ /** Maximum history entries to keep */
318
678
  maxHistory?: number;
679
+ /** Provider name for debugging */
319
680
  name: string;
320
681
  }
682
+ /**
683
+ * Options for mock API providers.
684
+ * Configures endpoints and response behaviors.
685
+ *
686
+ * @example
687
+ * ```typescript
688
+ * const options: MockApiProviderOptions = {
689
+ * endpoints: {
690
+ * getUser: () => ({ id: 1, name: 'John' }),
691
+ * getPosts: async () => [
692
+ * { id: 1, title: 'First Post' }
693
+ * ]
694
+ * },
695
+ * delay: 100, // Simulate network delay
696
+ * name: 'MockAPI'
697
+ * };
698
+ * ```
699
+ */
321
700
  export interface MockApiProviderOptions {
701
+ /** Mock endpoint implementations */
322
702
  endpoints: Record<string, () => Promise<unknown> | unknown>;
703
+ /** Artificial delay in ms */
323
704
  delay?: number;
705
+ /** Provider name for debugging */
324
706
  name: string;
325
707
  }
708
+ /**
709
+ * Entry in the update history for tracked providers.
710
+ * Records state changes over time.
711
+ *
712
+ * @template T - Type of the tracked state
713
+ *
714
+ * @example
715
+ * ```typescript
716
+ * const entry: UpdateHistoryEntry<UserProfile> = {
717
+ * timestamp: Date.now(),
718
+ * updates: { name: 'Jane', email: 'jane@example.com' }
719
+ * };
720
+ * ```
721
+ */
326
722
  export interface UpdateHistoryEntry<T> {
723
+ /** When the update occurred */
327
724
  timestamp: number;
725
+ /** Partial state updates applied */
328
726
  updates: Partial<T>;
329
727
  }
728
+ /**
729
+ * Item in a provider chain.
730
+ * Represents a single provider with its props.
731
+ *
732
+ * @example
733
+ * ```typescript
734
+ * const chainItem: ProviderChainItem = {
735
+ * provider: ThemeProvider,
736
+ * props: { theme: darkTheme }
737
+ * };
738
+ * ```
739
+ */
330
740
  export interface ProviderChainItem {
741
+ /** Provider component */
331
742
  provider: React.ComponentType<{
332
743
  children: React.ReactNode;
333
744
  }>;
745
+ /** Props to pass to provider */
334
746
  props?: Record<string, unknown>;
335
747
  }
748
+ /**
749
+ * Context value for subscribable providers.
750
+ * Implements pub/sub pattern for state updates.
751
+ *
752
+ * @template T - Type of the subscribable value
753
+ *
754
+ * @example
755
+ * ```typescript
756
+ * const context: SubscribableContextValue<Config> = {
757
+ * value: currentConfig,
758
+ * subscribe: (callback) => {
759
+ * subscribers.add(callback);
760
+ * return () => subscribers.delete(callback);
761
+ * },
762
+ * update: (newConfig) => {
763
+ * currentConfig = newConfig;
764
+ * subscribers.forEach(cb => cb(newConfig));
765
+ * }
766
+ * };
767
+ * ```
768
+ */
336
769
  export interface SubscribableContextValue<T> {
770
+ /** Current value */
337
771
  value: T;
772
+ /** Subscribe to value changes */
338
773
  subscribe: (callback: (value: T) => void) => () => void;
774
+ /** Update the value and notify subscribers */
339
775
  update: (value: T) => void;
340
776
  }
777
+ /**
778
+ * Return type for createSubscribableProvider.
779
+ * Provides subscription-based state management.
780
+ *
781
+ * @template T - Type of the subscribable value
782
+ *
783
+ * @example
784
+ * ```typescript
785
+ * const store: SubscribableProviderReturn<AppState> = createSubscribableProvider();
786
+ *
787
+ * // Check subscriber count
788
+ * expect(store.getSubscriberCount()).toBe(3);
789
+ *
790
+ * // Clear all subscriptions
791
+ * store.clearSubscribers();
792
+ * ```
793
+ */
341
794
  export interface SubscribableProviderReturn<T> {
795
+ /** Subscribable provider component */
342
796
  Provider: React.FC<{
343
797
  children: React.ReactNode;
344
798
  }>;
799
+ /** Hook to access subscribable context */
345
800
  useSubscribable: () => SubscribableContextValue<T>;
801
+ /** Get current subscriber count */
346
802
  getSubscriberCount: () => number;
803
+ /** Clear all active subscriptions */
347
804
  clearSubscribers: () => void;
348
805
  }
806
+ /**
807
+ * Callback function type for subscribing to value changes.
808
+ * Invoked whenever the subscribed value is updated.
809
+ *
810
+ * @template T - Type of the value being subscribed to
811
+ *
812
+ * @param value - The new value after an update
813
+ *
814
+ * @example
815
+ * ```typescript
816
+ * // Subscribe to user state changes
817
+ * const userSubscriber: Subscriber<User> = (user) => {
818
+ * console.log('User updated:', user);
819
+ * updateUI(user);
820
+ * };
821
+ *
822
+ * // Subscribe to theme changes
823
+ * const themeSubscriber: Subscriber<Theme> = (theme) => {
824
+ * document.body.className = theme.mode;
825
+ * };
826
+ * ```
827
+ */
349
828
  export type Subscriber<T> = (value: T) => void;
829
+ /**
830
+ * Context value for time-travel providers.
831
+ * Provides undo/redo functionality with state history management.
832
+ *
833
+ * @template T - Type of the state being tracked
834
+ *
835
+ * @example
836
+ * ```typescript
837
+ * const context: TimeTravelContextValue<EditorState> = {
838
+ * state: { content: 'Hello', cursor: 5 },
839
+ * canUndo: true,
840
+ * canRedo: false,
841
+ * undo: () => {
842
+ * // Restore previous state
843
+ * },
844
+ * redo: () => {
845
+ * // Restore next state
846
+ * },
847
+ * update: (newState) => {
848
+ * // Add to history and update current
849
+ * },
850
+ * reset: () => {
851
+ * // Clear all history
852
+ * },
853
+ * getHistory: () => [
854
+ * { content: '', cursor: 0 },
855
+ * { content: 'Hello', cursor: 5 }
856
+ * ]
857
+ * };
858
+ * ```
859
+ */
350
860
  export interface TimeTravelContextValue<T> {
861
+ /** Current state value */
351
862
  state: T;
863
+ /** Whether undo operation is available */
352
864
  canUndo: boolean;
865
+ /** Whether redo operation is available */
353
866
  canRedo: boolean;
867
+ /** Restore the previous state from history */
354
868
  undo: () => void;
869
+ /** Restore the next state from history */
355
870
  redo: () => void;
871
+ /** Update state and add to history */
356
872
  update: (newState: T) => void;
873
+ /** Clear all history and reset to initial state */
357
874
  reset: () => void;
875
+ /** Get the complete state history */
358
876
  getHistory: () => T[];
359
877
  }
878
+ /**
879
+ * Return type for createTimeTravelProvider.
880
+ * Provides components and hooks for time-travel state management.
881
+ *
882
+ * @template T - Type of the state with history tracking
883
+ *
884
+ * @example
885
+ * ```typescript
886
+ * const editor: TimeTravelProviderReturn<EditorState> = createTimeTravelProvider();
887
+ *
888
+ * // Use in component
889
+ * function Editor() {
890
+ * const { state, canUndo, undo, update } = editor.useTimeTravel();
891
+ *
892
+ * return (
893
+ * <div>
894
+ * <button disabled={!canUndo} onClick={undo}>Undo</button>
895
+ * <textarea
896
+ * value={state.content}
897
+ * onChange={(e) => update({ ...state, content: e.target.value })}
898
+ * />
899
+ * </div>
900
+ * );
901
+ * }
902
+ *
903
+ * // Wrap with provider
904
+ * <editor.Provider>
905
+ * <Editor />
906
+ * </editor.Provider>
907
+ * ```
908
+ */
360
909
  export interface TimeTravelProviderReturn<T> {
910
+ /** Time-travel provider component */
361
911
  Provider: React.FC<{
362
912
  children: React.ReactNode;
363
913
  }>;
914
+ /** Hook to access time-travel functionality */
364
915
  useTimeTravel: () => TimeTravelContextValue<T>;
365
916
  }
917
+ /**
918
+ * Return type for createMockApiProvider.
919
+ * Provides mock API endpoints for testing API interactions.
920
+ *
921
+ * @template T - Type of the API interface as a record of endpoint methods
922
+ *
923
+ * @example
924
+ * ```typescript
925
+ * interface UserAPI {
926
+ * getUser: (id: number) => Promise<User>;
927
+ * createUser: (data: CreateUserData) => Promise<User>;
928
+ * updateUser: (id: number, data: UpdateUserData) => Promise<User>;
929
+ * deleteUser: (id: number) => Promise<void>;
930
+ * }
931
+ *
932
+ * const api: MockApiProviderReturn<UserAPI> = createMockApiProvider();
933
+ *
934
+ * // Configure mock responses
935
+ * api.mockApi.getUser = vi.fn().mockResolvedValue({ id: 1, name: 'John' });
936
+ *
937
+ * // Use in component
938
+ * function UserProfile() {
939
+ * const { getUser } = api.useApi();
940
+ * const [user, setUser] = useState<User>();
941
+ *
942
+ * useEffect(() => {
943
+ * getUser(1).then(setUser);
944
+ * }, []);
945
+ *
946
+ * return <div>{user?.name}</div>;
947
+ * }
948
+ *
949
+ * // Reset mocks between tests
950
+ * afterEach(() => {
951
+ * api.resetMocks();
952
+ * });
953
+ * ```
954
+ */
366
955
  export interface MockApiProviderReturn<T extends Record<string, unknown>> {
956
+ /** Mock API provider component */
367
957
  Provider: React.FC<{
368
958
  children: React.ReactNode;
369
959
  }>;
960
+ /** Hook to access mock API methods */
370
961
  useApi: () => T;
962
+ /** Direct access to mock API implementations for configuration */
371
963
  mockApi: T;
964
+ /** Reset all mock implementations to their default state */
372
965
  resetMocks: () => void;
373
966
  }
@@ -1291,33 +1291,81 @@ export interface FeatureFlagFileData<FeatureFlagKey extends string> {
1291
1291
  *
1292
1292
  * @public
1293
1293
  */
1294
- export interface FeatureFlagFileTestContext {
1294
+ export interface FileTestContext {
1295
1295
  testDir: string;
1296
1296
  jsonFile: string;
1297
1297
  yamlFile: string;
1298
1298
  cleanup: () => Promise<void>;
1299
1299
  }
1300
1300
  /**
1301
- * Provider test operation for stress testing
1301
+ * Provider interface that supports dynamic rule addition.
1302
+ * Used for testing providers that allow runtime rule configuration.
1303
+ *
1304
+ * @template FeatureFlagKey - Type of feature flag keys
1305
+ *
1306
+ * @example
1307
+ * ```typescript
1308
+ * const provider: ProviderWithRules<MyFlags> = createTestProvider();
1309
+ *
1310
+ * // Add a targeting rule
1311
+ * provider.addRule({
1312
+ * id: 'premium-users',
1313
+ * flagKey: 'premium-feature',
1314
+ * name: 'Target premium users',
1315
+ * conditions: [{ field: 'userRole', operator: 'equals', value: 'premium' }],
1316
+ * value: true,
1317
+ * priority: 1,
1318
+ * isEnabled: true
1319
+ * });
1320
+ *
1321
+ * // Evaluate flag with rules
1322
+ * const evaluation = await provider.getFlag('premium-feature');
1323
+ * ```
1302
1324
  *
1303
1325
  * @public
1304
1326
  */
1305
- export interface ProviderTestOperation {
1306
- name: string;
1307
- operation: () => unknown | Promise<unknown>;
1308
- weight?: number;
1327
+ export interface ProviderWithRules<FeatureFlagKey extends string> {
1328
+ /** Add a new rule to the provider */
1329
+ addRule: (rule: FeatureFlagRule<FeatureFlagKey>) => void;
1330
+ /** Evaluate a flag considering all rules */
1331
+ getFlag: (key: FeatureFlagKey) => Promise<FeatureFlagEvaluation<FeatureFlagKey>>;
1309
1332
  }
1310
1333
  /**
1311
- * Provider stress test configuration
1334
+ * Test scenario interface for file-based feature flag providers.
1335
+ * Extends FileTestContext to provide file-specific testing utilities.
1336
+ *
1337
+ * @template FeatureFlagKey - Type of feature flag keys
1338
+ *
1339
+ * @example
1340
+ * ```typescript
1341
+ * const scenario: FileProviderTestScenario<'feature1' | 'feature2'> = {
1342
+ * testDir: '/tmp/test-flags',
1343
+ * jsonFile: '/tmp/test-flags/flags.json',
1344
+ * yamlFile: '/tmp/test-flags/flags.yaml',
1345
+ * filePath: '/tmp/test-flags/flags.json',
1346
+ * format: 'json',
1347
+ * cleanup: async () => {
1348
+ * await fs.rmdir(testDir, { recursive: true });
1349
+ * },
1350
+ * updateFile: async (updates) => {
1351
+ * const current = await readFile(filePath);
1352
+ * await writeFile(filePath, { ...current, ...updates });
1353
+ * },
1354
+ * waitForFileChange: async (ms = 100) => {
1355
+ * await new Promise(resolve => setTimeout(resolve, ms));
1356
+ * }
1357
+ * };
1358
+ * ```
1312
1359
  *
1313
1360
  * @public
1314
1361
  */
1315
- export interface ProviderStressTestConfig {
1316
- workerCount?: number;
1317
- operationsPerWorker?: number;
1318
- delayBetweenOps?: number;
1319
- operations?: ProviderTestOperation[];
1320
- operationCount?: number;
1321
- concurrency?: number;
1322
- operationMix?: Record<string, number>;
1362
+ export interface FileProviderTestScenario<FeatureFlagKey extends string> extends FileTestContext {
1363
+ /** Path to the active test file */
1364
+ filePath: string;
1365
+ /** Format of the feature flag file */
1366
+ format: 'json' | 'yaml';
1367
+ /** Update flag values in the test file */
1368
+ updateFile: (updates: Record<FeatureFlagKey, FeatureFlagValue>) => Promise<void>;
1369
+ /** Wait for file system changes to propagate */
1370
+ waitForFileChange: (ms?: number) => Promise<void>;
1323
1371
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plyaz/types",
3
- "version": "1.3.7",
3
+ "version": "1.3.8",
4
4
  "author": "Redeemer Pace",
5
5
  "license": "ISC",
6
6
  "description": "Provides shared TypeScript types and schema utilities for validation and parsing in the @playz ecosystem.",