@abdokouta/react-support 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,6 +1,4 @@
1
1
  export { Collection as CollectJsCollection } from 'collect.js';
2
- import { Newable, ModuleContainer, ServiceIdentifier } from '@abdokouta/react-di';
3
- export { ModuleContainer, Newable, ServiceIdentifier } from '@abdokouta/react-di';
4
2
 
5
3
  /**
6
4
  * Laravel-style string manipulation class
@@ -869,55 +867,15 @@ declare class SetCollection<T = any> {
869
867
  declare function collectSet<T>(items?: Iterable<T>): SetCollection<T>;
870
868
 
871
869
  /**
872
- * @fileoverview Base registry implementation
873
- *
874
- * This file provides a generic base registry class that uses the Collection
875
- * interface for storage. It provides a consistent API for all registry
876
- * implementations across the application.
877
- *
878
- * Key Features:
879
- * - Generic type support for any item type
880
- * - Default item support (fallback when item not found)
881
- * - Validation hooks (before add, after add)
882
- * - Collection-based storage (efficient O(1) operations)
883
- * - Type-safe API
884
- * - Consistent interface across all registries
870
+ * Custom driver creator function.
885
871
  *
886
- * Use Cases:
887
- * - Theme registry (storing and retrieving themes)
888
- * - Token registry (managing design tokens)
889
- * - Plugin registry (managing plugins)
890
- * - Configuration registry (storing configs)
891
- * - Any key-value registry needs
892
- *
893
- * @module @pixielity/support
894
- * @category Registries
872
+ * Receives the raw instance config and returns a driver instance.
895
873
  */
896
- /**
897
- * Collection interface for registry storage
898
- */
899
- interface Collection<T> {
900
- add(key: string, value: T): void;
901
- get(key: string): T | undefined;
902
- getAll(): T[];
903
- getKeys(): string[];
904
- getAsRecord(): Record<string, T>;
905
- has(key: string): boolean;
906
- remove(key: string): boolean;
907
- clear(): void;
908
- size(): number;
909
- isEmpty(): boolean;
910
- forEach(callback: (value: T, key: string) => void): void;
911
- map<U>(callback: (value: T, key: string) => U): U[];
912
- filter(predicate: (value: T, key: string) => boolean): T[];
913
- find(predicate: (value: T, key: string) => boolean): T | undefined;
914
- }
874
+ type DriverCreator<T> = (config: Record<string, any>) => T;
875
+
915
876
  /**
916
877
  * Validation result for registry operations
917
878
  *
918
- * Used by validation hooks to indicate whether an operation
919
- * should proceed or be rejected.
920
- *
921
879
  * @example
922
880
  * ```typescript
923
881
  * const result: ValidationResult = {
@@ -936,6 +894,7 @@ interface ValidationResult {
936
894
  */
937
895
  error?: string;
938
896
  }
897
+
939
898
  /**
940
899
  * Base registry options
941
900
  *
@@ -974,776 +933,396 @@ interface BaseRegistryOptions<T> {
974
933
  */
975
934
  afterAdd?: (key: string, item: T) => void;
976
935
  }
936
+
977
937
  /**
978
- * Base registry class
979
- *
980
- * A generic registry implementation that provides a consistent API for
981
- * storing and retrieving items by key. Uses the Collection interface
982
- * for efficient storage with O(1) operations.
983
- *
984
- * This class extends Collection functionality by adding:
985
- * - Default item support
986
- * - Validation hooks
987
- * - Consistent registry API
988
- *
989
- * All Collection methods are directly accessible on the registry instance.
990
- *
991
- * Performance Characteristics:
992
- * - register(): O(1) + validation time
993
- * - get(): O(1)
994
- * - has(): O(1)
995
- * - remove(): O(1)
996
- * - getAll(): O(n)
997
- * - clear(): O(1)
998
- *
999
- * @template T - The type of items stored in the registry
1000
- *
1001
- * @example
1002
- * ```typescript
1003
- * // Create a theme registry
1004
- * const themeRegistry = new BaseRegistry<Theme>({
1005
- * defaultItem: defaultTheme,
1006
- * validateBeforeAdd: (key, theme) => {
1007
- * if (!theme.name) {
1008
- * return { valid: false, error: 'Theme must have a name' };
1009
- * }
1010
- * return { valid: true };
1011
- * }
1012
- * });
1013
- *
1014
- * // Register themes
1015
- * themeRegistry.register('blue', blueTheme);
1016
- * themeRegistry.register('red', redTheme);
938
+ * Collection interface for registry storage.
1017
939
  *
1018
- * // Get a theme
1019
- * const theme = themeRegistry.get('blue');
940
+ * Defines the contract for a key-value store used internally by
941
+ * registries. Every method operates on string keys and generic values,
942
+ * providing O(1) lookups, inserts, and deletes when backed by a Map.
1020
943
  *
1021
- * // Get all themes
1022
- * const allThemes = themeRegistry.getAll();
1023
- *
1024
- * // Check if theme exists
1025
- * if (themeRegistry.has('blue')) {
1026
- * console.log('Blue theme exists');
1027
- * }
1028
- * ```
1029
- *
1030
- * @example
1031
- * ```typescript
1032
- * // Create a simple token registry
1033
- * const tokenRegistry = new BaseRegistry<Token>();
1034
- *
1035
- * tokenRegistry.register('primary', { value: '#0000FF' });
1036
- * tokenRegistry.register('secondary', { value: '#FF0000' });
1037
- *
1038
- * const allTokens = tokenRegistry.getAll();
1039
- * console.log(allTokens.length); // 2
1040
- * ```
944
+ * @typeParam T - The type of values stored in the collection
1041
945
  */
1042
- declare class BaseRegistry<T> implements Collection<T> {
1043
- /**
1044
- * Internal collection for storing registry items
1045
- *
1046
- * Uses Collection interface for flexible storage implementation.
1047
- * By default, uses MapCollection for O(1) operations.
1048
- */
1049
- protected collection: Collection<T>;
1050
- /**
1051
- * Default item to return when requested item is not found
1052
- *
1053
- * If set, get() will return this item instead of undefined
1054
- * when the requested key doesn't exist in the registry.
1055
- */
1056
- protected defaultItem?: T;
1057
- /**
1058
- * Validation hook called before adding an item
1059
- *
1060
- * If provided, this function is called before every register()
1061
- * operation to validate the item. If validation fails, the
1062
- * item is not added and an error is thrown.
1063
- */
1064
- protected validateBeforeAdd?: (key: string, item: T) => ValidationResult;
1065
- /**
1066
- * Hook called after an item is successfully added
1067
- *
1068
- * If provided, this function is called after every successful
1069
- * register() operation. Useful for side effects like logging
1070
- * or triggering dependent updates.
1071
- */
1072
- protected afterAdd?: (key: string, item: T) => void;
1073
- /**
1074
- * Creates a new BaseRegistry instance
1075
- *
1076
- * Initializes the registry with optional configuration for default
1077
- * item and validation hooks. By default, uses MapCollection for storage.
1078
- *
1079
- * @param options - Registry configuration options
1080
- *
1081
- * @example
1082
- * ```typescript
1083
- * // Simple registry without options
1084
- * const registry = new BaseRegistry<Theme>();
1085
- * ```
1086
- *
1087
- * @example
1088
- * ```typescript
1089
- * // Registry with default item
1090
- * const registry = new BaseRegistry<Theme>({
1091
- * defaultItem: defaultTheme
1092
- * });
1093
- * ```
1094
- *
1095
- * @example
1096
- * ```typescript
1097
- * // Registry with validation
1098
- * const registry = new BaseRegistry<Theme>({
1099
- * validateBeforeAdd: (key, theme) => {
1100
- * if (!theme.name) {
1101
- * return { valid: false, error: 'Theme must have a name' };
1102
- * }
1103
- * return { valid: true };
1104
- * },
1105
- * afterAdd: (key, theme) => {
1106
- * console.log(`Registered theme: ${theme.name}`);
1107
- * }
1108
- * });
1109
- * ```
1110
- */
1111
- constructor(options?: BaseRegistryOptions<T>);
1112
- /**
1113
- * Register an item in the registry
1114
- *
1115
- * Adds or updates an item in the registry with the specified key.
1116
- * If an item with the same key already exists, it will be replaced.
1117
- *
1118
- * If a validation hook is configured, it will be called before adding
1119
- * the item. If validation fails, an error is thrown and the item is
1120
- * not added.
1121
- *
1122
- * If an afterAdd hook is configured, it will be called after the item
1123
- * is successfully added.
1124
- *
1125
- * Time Complexity: O(1) + validation time
1126
- *
1127
- * @param key - Unique identifier for the item
1128
- * @param item - Item to register
1129
- * @throws Error if validation fails
1130
- *
1131
- * @example
1132
- * ```typescript
1133
- * const registry = new BaseRegistry<Theme>();
1134
- *
1135
- * // Register a theme
1136
- * registry.register('blue', {
1137
- * name: 'Blue',
1138
- * colors: { accent: '#0000FF' }
1139
- * });
1140
- *
1141
- * // Update existing theme
1142
- * registry.register('blue', {
1143
- * name: 'Blue',
1144
- * colors: { accent: '#0066FF' }
1145
- * });
1146
- * ```
1147
- */
1148
- register(key: string, item: T): void;
946
+ interface Collection<T> {
1149
947
  /**
1150
- * Add an item to the collection (alias for register)
948
+ * Add or replace a value under the given key.
1151
949
  *
1152
- * This method is part of the Collection interface.
1153
- * It delegates to register() to ensure validation hooks are called.
1154
- *
1155
- * Time Complexity: O(1) + validation time
1156
- *
1157
- * @param key - Unique identifier for the item
1158
- * @param value - Item to add
950
+ * @param key - Unique string identifier
951
+ * @param value - Value to store
1159
952
  */
1160
953
  add(key: string, value: T): void;
1161
954
  /**
1162
- * Get an item from the registry
1163
- *
1164
- * Retrieves an item by its key. If the item doesn't exist:
1165
- * - Returns the default item if one was configured
1166
- * - Returns undefined if no default item was configured
1167
- *
1168
- * Time Complexity: O(1)
955
+ * Retrieve a value by its key.
1169
956
  *
1170
- * @param key - Item identifier
1171
- * @returns Item if found, default item if configured, or undefined
1172
- *
1173
- * @example
1174
- * ```typescript
1175
- * const theme = registry.get('blue');
1176
- * ```
957
+ * @param key - The key to look up
958
+ * @returns The stored value, or `undefined` if the key does not exist
1177
959
  */
1178
960
  get(key: string): T | undefined;
1179
961
  /**
1180
- * Get all items in the registry
1181
- *
1182
- * Returns an array containing all items in the registry.
1183
- * The order of items depends on the collection implementation
1184
- * (MapCollection maintains insertion order).
1185
- *
1186
- * Time Complexity: O(n) where n is the number of items
962
+ * Return every value in the collection as an array.
1187
963
  *
1188
- * @returns Array of all items in the registry
964
+ * Iteration order matches insertion order (Map semantics).
1189
965
  *
1190
- * @example
1191
- * ```typescript
1192
- * const allThemes = registry.getAll();
1193
- * ```
966
+ * @returns An array of all stored values
1194
967
  */
1195
968
  getAll(): T[];
1196
969
  /**
1197
- * Get all keys in the registry
970
+ * Return every key in the collection as an array.
1198
971
  *
1199
- * Returns an array containing all keys in the registry.
1200
- * Useful for iteration or checking what items are registered.
1201
- *
1202
- * Time Complexity: O(n) where n is the number of items
1203
- *
1204
- * @returns Array of all keys in the registry
1205
- *
1206
- * @example
1207
- * ```typescript
1208
- * const keys = registry.getKeys();
1209
- * ```
972
+ * @returns An array of all registered keys
1210
973
  */
1211
974
  getKeys(): string[];
1212
975
  /**
1213
- * Get registry as a record object
1214
- *
1215
- * Converts the registry to a plain JavaScript object (record)
1216
- * with keys mapping to values. Useful for serialization.
976
+ * Convert the collection to a plain record object.
1217
977
  *
1218
- * Time Complexity: O(n) where n is the number of items
978
+ * Useful for serialisation or passing data to APIs that
979
+ * expect a simple `Record<string, T>`.
1219
980
  *
1220
- * @returns Record object with keys mapping to values
1221
- *
1222
- * @example
1223
- * ```typescript
1224
- * const record = registry.getAsRecord();
1225
- * ```
981
+ * @returns A shallow copy of the collection as a record
1226
982
  */
1227
983
  getAsRecord(): Record<string, T>;
1228
984
  /**
1229
- * Check if an item is registered
1230
- *
1231
- * Checks whether an item with the specified key exists in the registry.
1232
- * Does not check the value, only the presence of the key.
1233
- *
1234
- * Time Complexity: O(1)
985
+ * Check whether a key exists in the collection.
1235
986
  *
1236
- * @param key - Item identifier to check
1237
- * @returns True if item is registered, false otherwise
1238
- *
1239
- * @example
1240
- * ```typescript
1241
- * if (registry.has('blue')) {
1242
- * console.log('Blue theme exists');
1243
- * }
1244
- * ```
987
+ * @param key - The key to test
988
+ * @returns `true` if the key is present
1245
989
  */
1246
990
  has(key: string): boolean;
1247
991
  /**
1248
- * Remove an item from the registry
1249
- *
1250
- * Removes an item with the specified key from the registry.
1251
- * Returns true if the item was removed, false if it didn't exist.
992
+ * Remove a key and its associated value.
1252
993
  *
1253
- * Time Complexity: O(1)
1254
- *
1255
- * @param key - Item identifier to remove
1256
- * @returns True if item was removed, false if it didn't exist
1257
- *
1258
- * @example
1259
- * ```typescript
1260
- * const removed = registry.remove('blue');
1261
- * ```
994
+ * @param key - The key to remove
995
+ * @returns `true` if the key existed and was removed, `false` otherwise
1262
996
  */
1263
997
  remove(key: string): boolean;
1264
998
  /**
1265
- * Clear all items from the registry
1266
- *
1267
- * Removes all items from the registry, leaving it empty.
1268
- * This operation is irreversible.
1269
- *
1270
- * Time Complexity: O(1)
1271
- *
1272
- * @example
1273
- * ```typescript
1274
- * registry.clear();
1275
- * ```
999
+ * Remove all entries from the collection.
1276
1000
  */
1277
1001
  clear(): void;
1278
1002
  /**
1279
- * Get the number of items in the registry
1280
- *
1281
- * Returns the total count of items currently registered.
1282
- *
1283
- * Time Complexity: O(1)
1003
+ * Return the number of entries in the collection.
1284
1004
  *
1285
- * @returns Number of items in the registry
1286
- *
1287
- * @example
1288
- * ```typescript
1289
- * console.log(registry.size()); // 2
1290
- * ```
1005
+ * @returns The current entry count
1291
1006
  */
1292
1007
  size(): number;
1293
1008
  /**
1294
- * Check if the registry is empty
1295
- *
1296
- * Returns true if the registry contains no items, false otherwise.
1297
- * This is a convenience method equivalent to checking if size() === 0.
1009
+ * Check whether the collection contains zero entries.
1298
1010
  *
1299
- * Time Complexity: O(1)
1300
- *
1301
- * @returns True if registry has no items, false otherwise
1302
- *
1303
- * @example
1304
- * ```typescript
1305
- * console.log(registry.isEmpty()); // true
1306
- * ```
1011
+ * @returns `true` when `size() === 0`
1307
1012
  */
1308
1013
  isEmpty(): boolean;
1309
1014
  /**
1310
- * Iterate over all items in the registry
1311
- *
1312
- * Executes a callback function for each item in the registry.
1313
- * Items are iterated in insertion order (for MapCollection).
1314
- *
1315
- * Time Complexity: O(n) where n is the number of items
1015
+ * Execute a callback for every entry in insertion order.
1316
1016
  *
1317
- * @param callback - Function to call for each item (value, key)
1318
- *
1319
- * @example
1320
- * ```typescript
1321
- * registry.forEach((theme, key) => {
1322
- * console.log(`${key}: ${theme.name}`);
1323
- * });
1324
- * ```
1017
+ * @param callback - Receives `(value, key)` for each entry
1325
1018
  */
1326
1019
  forEach(callback: (value: T, key: string) => void): void;
1327
1020
  /**
1328
- * Map over all items in the registry
1329
- *
1330
- * Transforms each item in the registry using a callback function
1331
- * and returns an array of the transformed values.
1332
- *
1333
- * Time Complexity: O(n) where n is the number of items
1021
+ * Transform every entry and collect the results into an array.
1334
1022
  *
1335
- * @template U - The type of the transformed items
1336
- * @param callback - Function to transform each item (value, key) => U
1337
- * @returns Array of transformed items
1338
- *
1339
- * @example
1340
- * ```typescript
1341
- * const names = registry.map(theme => theme.name);
1342
- * ```
1023
+ * @typeParam U - The return type of the mapping function
1024
+ * @param callback - Receives `(value, key)` and returns the mapped value
1025
+ * @returns An array of mapped results in insertion order
1343
1026
  */
1344
1027
  map<U>(callback: (value: T, key: string) => U): U[];
1345
1028
  /**
1346
- * Filter items in the registry
1347
- *
1348
- * Returns an array of items that pass the test implemented by the
1349
- * provided predicate function.
1029
+ * Return all values whose entries satisfy the predicate.
1350
1030
  *
1351
- * Time Complexity: O(n) where n is the number of items
1352
- *
1353
- * @param predicate - Function to test each item (value, key) => boolean
1354
- * @returns Array of items that pass the test
1355
- *
1356
- * @example
1357
- * ```typescript
1358
- * const darkThemes = registry.filter(theme => theme.isDark);
1359
- * ```
1031
+ * @param predicate - Receives `(value, key)` and returns `true` to keep
1032
+ * @returns An array of matching values
1360
1033
  */
1361
1034
  filter(predicate: (value: T, key: string) => boolean): T[];
1362
1035
  /**
1363
- * Find an item in the registry
1364
- *
1365
- * Returns the first item that satisfies the provided predicate function.
1366
- * Returns undefined if no item passes the test.
1367
- *
1368
- * Time Complexity: O(n) worst case, O(1) best case
1036
+ * Return the first value whose entry satisfies the predicate.
1369
1037
  *
1370
- * @param predicate - Function to test each item (value, key) => boolean
1371
- * @returns First item that passes the test, or undefined
1372
- *
1373
- * @example
1374
- * ```typescript
1375
- * const defaultTheme = registry.find(theme => theme.isDefault);
1376
- * ```
1038
+ * @param predicate - Receives `(value, key)` and returns `true` to match
1039
+ * @returns The first matching value, or `undefined` if none match
1377
1040
  */
1378
1041
  find(predicate: (value: T, key: string) => boolean): T | undefined;
1379
1042
  }
1380
1043
 
1381
1044
  /**
1382
- * @fileoverview Facade interfaces
1045
+ * @fileoverview Base registry implementation
1383
1046
  *
1384
- * Defines the interfaces for the Facade pattern implementation.
1385
- * Facades provide a static interface to services resolved from the DI container.
1047
+ * A generic key-value registry backed by {@link MapCollection}.
1048
+ * Provides O(1) lookups, optional default values, and validation hooks.
1386
1049
  *
1387
- * @module @abdokouta/react-support
1388
- * @category Facades
1050
+ * @module @abdokouta/support
1051
+ * @category Registries
1389
1052
  */
1053
+
1390
1054
  /**
1391
- * Application interface for facade resolution
1055
+ * Base registry class
1056
+ *
1057
+ * Stores items by string key using {@link MapCollection} for O(1) operations.
1058
+ * Adds default-item fallback, before-add validation, and after-add hooks
1059
+ * on top of the raw collection.
1060
+ *
1061
+ * @typeParam T - The type of items stored in the registry
1062
+ *
1063
+ * @example
1064
+ * ```typescript
1065
+ * const registry = new BaseRegistry<Theme>({
1066
+ * defaultItem: defaultTheme,
1067
+ * validateBeforeAdd: (key, theme) => {
1068
+ * if (!theme.name) return { valid: false, error: 'Theme must have a name' };
1069
+ * return { valid: true };
1070
+ * },
1071
+ * });
1392
1072
  *
1393
- * This interface represents the minimal contract that an application/container
1394
- * must implement to work with facades. It mirrors the Laravel Application contract.
1073
+ * registry.register('blue', blueTheme);
1074
+ * registry.get('blue'); // blueTheme
1075
+ * registry.get('nope'); // defaultTheme
1076
+ * ```
1395
1077
  */
1396
- interface FacadeApplication {
1078
+ declare class BaseRegistry<T> implements Collection<T> {
1079
+ /**
1080
+ * Internal map-based storage.
1081
+ */
1082
+ protected readonly storage: MapCollection<string, T>;
1397
1083
  /**
1398
- * Resolve a service from the container
1084
+ * Fallback value returned by {@link get} when a key is missing.
1085
+ */
1086
+ protected defaultItem?: T;
1087
+ /**
1088
+ * Optional validation executed before every {@link register} call.
1089
+ */
1090
+ protected validateBeforeAdd?: (key: string, item: T) => ValidationResult;
1091
+ /**
1092
+ * Optional callback executed after a successful {@link register}.
1093
+ */
1094
+ protected afterAdd?: (key: string, item: T) => void;
1095
+ /**
1096
+ * Create a new registry.
1399
1097
  *
1400
- * @param abstract - Service identifier (string, symbol, or class)
1401
- * @returns The resolved service instance
1098
+ * @param options - Optional default item, validation, and lifecycle hooks
1402
1099
  */
1403
- get<T>(abstract: string | symbol | (new (...args: unknown[]) => T)): T;
1100
+ constructor(options?: BaseRegistryOptions<T>);
1404
1101
  /**
1405
- * Check if a service has been resolved
1102
+ * Register (add or replace) an item.
1103
+ *
1104
+ * Runs the `validateBeforeAdd` hook first; throws on failure.
1105
+ * Fires the `afterAdd` hook on success.
1406
1106
  *
1407
- * @param abstract - Service identifier
1408
- * @returns True if the service has been resolved
1107
+ * @param key - Unique identifier
1108
+ * @param item - Value to store
1109
+ * @throws Error if validation fails
1409
1110
  */
1410
- resolved?(abstract: string | symbol): boolean;
1111
+ register(key: string, item: T): void;
1411
1112
  /**
1412
- * Register a callback to run after a service is resolved
1113
+ * @inheritdoc delegates to {@link register} so hooks still fire.
1114
+ */
1115
+ add(key: string, value: T): void;
1116
+ /**
1117
+ * Retrieve an item by key.
1118
+ *
1119
+ * Falls back to {@link defaultItem} when the key is not found.
1413
1120
  *
1414
- * @param abstract - Service identifier
1415
- * @param callback - Callback to run after resolution
1121
+ * @param key - Item identifier
1122
+ * @returns The stored value, the default item, or `undefined`
1123
+ */
1124
+ get(key: string): T | undefined;
1125
+ /**
1126
+ * Return every stored value in insertion order.
1127
+ */
1128
+ getAll(): T[];
1129
+ /**
1130
+ * Return every registered key in insertion order.
1131
+ */
1132
+ getKeys(): string[];
1133
+ /**
1134
+ * Convert the registry to a plain `Record<string, T>`.
1416
1135
  */
1417
- afterResolving?(abstract: string | symbol, callback: (service: unknown, app: FacadeApplication) => void): void;
1136
+ getAsRecord(): Record<string, T>;
1418
1137
  /**
1419
- * Bind an instance to the container
1138
+ * Check whether a key exists.
1139
+ */
1140
+ has(key: string): boolean;
1141
+ /**
1142
+ * Remove an item by key.
1420
1143
  *
1421
- * @param abstract - Service identifier
1422
- * @param instance - Instance to bind
1144
+ * @returns `true` if the key existed and was removed
1423
1145
  */
1424
- instance?(abstract: string | symbol, instance: unknown): void;
1425
- }
1426
- /**
1427
- * Fake interface for testing
1428
- *
1429
- * Facades can be swapped with fakes for testing purposes.
1430
- * Any object implementing this interface is considered a fake.
1431
- */
1432
- interface Fake {
1146
+ remove(key: string): boolean;
1147
+ /**
1148
+ * Remove all items from the registry.
1149
+ */
1150
+ clear(): void;
1433
1151
  /**
1434
- * Marker to identify fake instances
1152
+ * Return the number of registered items.
1435
1153
  */
1436
- readonly __isFake: true;
1154
+ size(): number;
1155
+ /**
1156
+ * Return `true` when the registry is empty.
1157
+ */
1158
+ isEmpty(): boolean;
1159
+ /**
1160
+ * Iterate over every entry in insertion order.
1161
+ */
1162
+ forEach(callback: (value: T, key: string) => void): void;
1163
+ /**
1164
+ * Map every entry to a new value and return the results as an array.
1165
+ */
1166
+ map<U>(callback: (value: T, key: string) => U): U[];
1167
+ /**
1168
+ * Return all values whose entries satisfy the predicate.
1169
+ */
1170
+ filter(predicate: (value: T, key: string) => boolean): T[];
1171
+ /**
1172
+ * Return the first value whose entry satisfies the predicate.
1173
+ */
1174
+ find(predicate: (value: T, key: string) => boolean): T | undefined;
1437
1175
  }
1438
- /**
1439
- * Check if an object is a Fake
1440
- *
1441
- * @param obj - Object to check
1442
- * @returns True if the object is a Fake
1443
- */
1444
- declare function isFake(obj: unknown): obj is Fake;
1445
1176
 
1446
1177
  /**
1447
- * @fileoverview Base Facade implementation
1178
+ * Multiple Instance Manager
1448
1179
  *
1449
- * This file provides the base Facade class that enables static access to
1450
- * services resolved from the DI container. Inspired by Laravel's Facade pattern.
1180
+ * Abstract base class for managing multiple named instances backed by
1181
+ * different drivers. TypeScript adaptation of Laravel's
1182
+ * `MultipleInstanceManager` pattern.
1451
1183
  *
1452
- * Uses @abdokouta/react-di's getModuleContainer to resolve services,
1453
- * enabling facades to work outside of React components.
1184
+ * Concrete managers extend this class and implement:
1185
+ * - `getDefaultInstance()` which instance name to use by default
1186
+ * - `getInstanceConfig(name)` — read config for a named instance
1187
+ * - `createDriver(driver, config)` — create an instance for a known driver
1454
1188
  *
1455
- * Key Features:
1456
- * - Static interface to container-resolved services
1457
- * - Instance caching for performance
1458
- * - Hot-swapping for testing
1459
- * - Fake detection for test assertions
1460
- * - Works outside React components
1461
- *
1462
- * @module @abdokouta/react-support
1463
- * @category Facades
1464
- */
1465
-
1466
- /**
1467
- * Base Facade class
1189
+ * Config is injected via DI in the concrete manager's constructor using
1190
+ * `@Inject(CONFIG_TOKEN)`. The base class handles lazy resolution,
1191
+ * caching, custom driver registration, and instance lifecycle.
1468
1192
  *
1469
- * Provides a static interface to services resolved from the DI container.
1470
- * Subclasses must implement `getFacadeAccessor()` to specify which service
1471
- * the facade represents.
1193
+ * @typeParam T - The type of instance this manager creates
1472
1194
  *
1473
1195
  * @example
1474
1196
  * ```typescript
1475
- * import { Facade } from '@abdokouta/react-support';
1476
- * import { LoggerService } from './logger.service';
1477
- * import { AppModule } from './app.module';
1478
- *
1479
- * class Log extends Facade {
1480
- * protected static getFacadeAccessor(): ServiceIdentifier {
1481
- * return LoggerService;
1197
+ * @Injectable()
1198
+ * class CacheManager extends MultipleInstanceManager<Store> {
1199
+ * constructor(@Inject(CACHE_CONFIG) private config: CacheModuleOptions) {
1200
+ * super();
1482
1201
  * }
1483
- * }
1484
1202
  *
1485
- * // Set the module once at app bootstrap
1486
- * Facade.setFacadeModule(AppModule);
1203
+ * getDefaultInstance() { return this.config.default; }
1204
+ * getInstanceConfig(name) { return this.config.stores[name]; }
1487
1205
  *
1488
- * // Use the facade statically anywhere
1489
- * Log.info('Hello, world!');
1490
- * Log.error('Something went wrong');
1206
+ * protected createDriver(driver, config) {
1207
+ * switch (driver) {
1208
+ * case 'memory': return new MemoryStore(config);
1209
+ * case 'redis': return new RedisStore(config);
1210
+ * case 'null': return new NullStore();
1211
+ * default: throw new Error(`Driver [${driver}] not supported.`);
1212
+ * }
1213
+ * }
1214
+ * }
1491
1215
  * ```
1492
1216
  *
1493
- * @example
1494
- * ```typescript
1495
- * // Swap with a fake for testing
1496
- * const fakeLogs: string[] = [];
1497
- * const fakeLogger = {
1498
- * __isFake: true as const,
1499
- * info: (msg: string) => fakeLogs.push(msg),
1500
- * error: (msg: string) => fakeLogs.push(msg),
1501
- * };
1217
+ * @module services/base-manager
1218
+ */
1219
+
1220
+ /**
1221
+ * Abstract base manager for multi-instance, multi-driver services.
1502
1222
  *
1503
- * Log.swap(fakeLogger);
1504
- * Log.info('Test message');
1505
- * expect(fakeLogs).toContain('Test message');
1506
- * ```
1223
+ * @typeParam T - The type of instance managed (e.g., Store, RedisConnection)
1507
1224
  */
1508
- declare abstract class Facade {
1225
+ declare abstract class MultipleInstanceManager<T> {
1509
1226
  /**
1510
- * The root module class for resolving services
1227
+ * Resolved instances, keyed by instance name.
1228
+ * Instances are created once and reused on subsequent calls.
1511
1229
  */
1512
- protected static moduleClass: Newable | null;
1230
+ private readonly instances;
1513
1231
  /**
1514
- * The module container instance (cached)
1232
+ * Custom driver creators registered via `extend()`.
1233
+ * Keyed by driver name.
1515
1234
  */
1516
- protected static container: ModuleContainer | null;
1235
+ private readonly customCreators;
1517
1236
  /**
1518
- * The resolved object instances
1237
+ * The config key that identifies the driver.
1238
+ * Override in subclasses if your config uses a different field name.
1519
1239
  *
1520
- * Caches resolved instances by their accessor key for performance.
1240
+ * @default 'driver'
1521
1241
  */
1522
- protected static resolvedInstance: Map<string | symbol, unknown>;
1242
+ protected readonly driverKey: string;
1523
1243
  /**
1524
- * Indicates if the resolved instance should be cached
1525
- *
1526
- * Set to false in subclasses to always resolve fresh instances.
1527
- */
1528
- protected static cached: boolean;
1529
- /**
1530
- * Hotswap the underlying instance behind the facade
1531
- *
1532
- * Useful for testing - swap the real service with a mock or fake.
1244
+ * Get the default instance name.
1533
1245
  *
1534
- * @param instance - Instance to swap in
1246
+ * Reads from the injected config (e.g., `this.config.default`).
1535
1247
  *
1536
- * @example
1537
- * ```typescript
1538
- * // In tests
1539
- * const mockLogger = { info: vi.fn(), error: vi.fn() };
1540
- * Log.swap(mockLogger);
1541
- *
1542
- * // Now Log.info() calls mockLogger.info()
1543
- * Log.info('test');
1544
- * expect(mockLogger.info).toHaveBeenCalledWith('test');
1545
- * ```
1248
+ * @returns The name of the default instance
1546
1249
  */
1547
- static swap(instance: unknown): void;
1250
+ abstract getDefaultInstance(): string;
1548
1251
  /**
1549
- * Determines whether a "fake" has been set as the facade instance
1252
+ * Get the configuration for a named instance.
1550
1253
  *
1551
- * @returns True if the current instance is a Fake
1254
+ * Reads from the injected config (e.g., `this.config.stores[name]`).
1552
1255
  *
1553
- * @example
1554
- * ```typescript
1555
- * if (Log.isFake()) {
1556
- * console.log('Using fake logger');
1557
- * }
1558
- * ```
1256
+ * @param name - Instance name
1257
+ * @returns The config object, or undefined if not configured
1559
1258
  */
1560
- static isFake(): boolean;
1259
+ abstract getInstanceConfig(name: string): Record<string, any> | undefined;
1561
1260
  /**
1562
- * Get the root object behind the facade
1261
+ * Create a driver instance for a known driver type.
1563
1262
  *
1564
- * Resolves and returns the actual service instance.
1263
+ * Called when no custom creator is registered for the driver.
1264
+ * Implement with a switch/map over your package's built-in drivers.
1565
1265
  *
1566
- * @returns The resolved service instance
1266
+ * @param driver - The driver name (e.g., 'memory', 'redis', 'smtp')
1267
+ * @param config - The raw instance config
1268
+ * @returns A new driver instance
1269
+ * @throws Error if the driver is not supported
1567
1270
  */
1568
- static getFacadeRoot<T = unknown>(): T;
1271
+ protected abstract createDriver(driver: string, config: Record<string, any>): T;
1569
1272
  /**
1570
- * Get the registered name of the component
1571
- *
1572
- * Subclasses MUST override this method to specify which service
1573
- * the facade represents.
1273
+ * Get an instance by name.
1574
1274
  *
1575
- * @returns Service identifier (string, symbol, or class)
1576
- * @throws Error if not implemented
1275
+ * Returns a cached instance if available, otherwise resolves and caches it.
1276
+ * If no name is provided, returns the default instance.
1577
1277
  *
1578
- * @example
1579
- * ```typescript
1580
- * class Log extends Facade {
1581
- * protected static getFacadeAccessor(): ServiceIdentifier {
1582
- * return LoggerService; // or 'logger' string token
1583
- * }
1584
- * }
1585
- * ```
1278
+ * @param name - Instance name (uses default if omitted)
1279
+ * @returns The resolved instance
1280
+ * @throws Error if the instance is not configured
1586
1281
  */
1587
- protected static getFacadeAccessor(): ServiceIdentifier;
1282
+ instance(name?: string): T;
1588
1283
  /**
1589
- * Get a consistent key for the accessor
1590
- */
1591
- protected static getAccessorKey(accessor: ServiceIdentifier): string | symbol;
1592
- /**
1593
- * Resolve the facade root instance from the container
1284
+ * Register a custom driver creator.
1594
1285
  *
1595
- * @param identifier - Service identifier
1596
- * @returns Resolved service instance
1597
- */
1598
- protected static resolveFacadeInstance<T>(identifier: ServiceIdentifier<T>): T;
1599
- /**
1600
- * Get the module container
1286
+ * Custom creators take priority over built-in drivers.
1601
1287
  *
1602
- * @returns The module container instance
1288
+ * @param driver - Driver name
1289
+ * @param creator - Factory function that creates an instance from config
1290
+ * @returns this (for chaining)
1603
1291
  */
1604
- protected static getContainer(): ModuleContainer | null;
1292
+ extend(driver: string, creator: DriverCreator<T>): this;
1605
1293
  /**
1606
- * Clear a resolved facade instance
1294
+ * Remove a cached instance, forcing re-creation on next access.
1607
1295
  *
1608
- * @param name - Service identifier to clear (defaults to this facade's accessor)
1296
+ * @param name - Instance name (uses default if omitted)
1297
+ * @returns this (for chaining)
1609
1298
  */
1610
- static clearResolvedInstance(name?: string | symbol): void;
1299
+ forgetInstance(name?: string): this;
1611
1300
  /**
1612
- * Clear all resolved instances
1613
- *
1614
- * Useful for test cleanup.
1301
+ * Remove all cached instances.
1615
1302
  */
1616
- static clearResolvedInstances(): void;
1303
+ purge(): void;
1617
1304
  /**
1618
- * Get the module class
1305
+ * Check if an instance has been resolved and cached.
1619
1306
  *
1620
- * @returns The module class
1307
+ * @param name - Instance name
1621
1308
  */
1622
- static getFacadeModule(): Newable | null;
1309
+ hasResolvedInstance(name: string): boolean;
1623
1310
  /**
1624
- * Set the module class for facade resolution
1625
- *
1626
- * Must be called during application bootstrap to enable facades.
1627
- * Call this AFTER Inversiland.run() or Container.configure().build().
1628
- *
1629
- * @param module - The root module class
1630
- *
1631
- * @example
1632
- * ```typescript
1633
- * // In your app bootstrap (main.tsx)
1634
- * import { Facade } from '@abdokouta/react-support';
1635
- * import { Container, ContainerProvider } from '@abdokouta/react-di';
1636
- * import { AppModule } from './app.module';
1637
- *
1638
- * // Initialize container
1639
- * Container.configure().withModule(AppModule).withDefaults().build();
1640
- *
1641
- * // Set facade module
1642
- * Facade.setFacadeModule(AppModule);
1643
- *
1644
- * // Now facades work anywhere
1645
- * ReactDOM.createRoot(document.getElementById("root")!).render(
1646
- * <ContainerProvider module={AppModule}>
1647
- * <App />
1648
- * </ContainerProvider>
1649
- * );
1650
- * ```
1311
+ * Get all resolved instance names.
1651
1312
  */
1652
- static setFacadeModule(module: Newable | null): void;
1313
+ getResolvedInstances(): string[];
1653
1314
  /**
1654
- * Set the container directly (alternative to setFacadeModule)
1315
+ * Resolve an instance by name.
1655
1316
  *
1656
- * @param container - The module container instance
1657
- */
1658
- static setFacadeContainer(container: ModuleContainer | null): void;
1659
- /**
1660
- * @deprecated Use setFacadeModule instead
1661
- */
1662
- static setFacadeApplication(app: FacadeApplication | null): void;
1663
- /**
1664
- * @deprecated Use getFacadeModule instead
1665
- */
1666
- static getFacadeApplication(): FacadeApplication | null;
1667
- }
1668
-
1669
- /**
1670
- * @fileoverview Facade factory with Proxy support
1671
- *
1672
- * Creates facades with automatic method forwarding using JavaScript Proxy.
1673
- * This enables the Laravel-style `Facade::method()` pattern in TypeScript.
1674
- *
1675
- * @module @abdokouta/react-support
1676
- * @category Facades
1677
- */
1678
-
1679
- /**
1680
- * Facade class type with static methods and proxied service methods
1681
- */
1682
- type FacadeClass<T> = typeof Facade & {
1683
- getFacadeRoot<R = T>(): R;
1684
- } & {
1685
- [K in keyof T]: T[K];
1686
- };
1687
- /**
1688
- * Options for creating a facade
1689
- */
1690
- interface CreateFacadeOptions<T> {
1691
- /**
1692
- * Service identifier (string token, symbol, or class)
1693
- */
1694
- accessor: ServiceIdentifier<T>;
1695
- /**
1696
- * Whether to cache the resolved instance
1697
- * @default true
1317
+ * 1. Reads config via `getInstanceConfig()` (from injected config)
1318
+ * 2. Extracts the driver name from the config
1319
+ * 3. Checks custom creators first
1320
+ * 4. Falls back to `createDriver()`
1321
+ *
1322
+ * @param name - Instance name
1323
+ * @returns A new instance
1698
1324
  */
1699
- cached?: boolean;
1325
+ private resolve;
1700
1326
  }
1701
- /**
1702
- * Create a facade for a service
1703
- *
1704
- * Creates a Proxy-based facade that forwards all method calls to the
1705
- * resolved service instance. This enables the Laravel-style pattern
1706
- * where you can call methods directly on the facade class.
1707
- *
1708
- * @param options - Facade configuration
1709
- * @returns Proxied facade class
1710
- *
1711
- * @example
1712
- * ```typescript
1713
- * // Define your service interface
1714
- * interface ILogger {
1715
- * info(message: string): void;
1716
- * error(message: string): void;
1717
- * debug(message: string): void;
1718
- * }
1719
- *
1720
- * // Create the facade
1721
- * const Log = createFacade<ILogger>({
1722
- * accessor: 'logger', // or LoggerService class
1723
- * });
1724
- *
1725
- * // Set module at bootstrap
1726
- * Facade.setFacadeModule(AppModule);
1727
- *
1728
- * // Use it statically
1729
- * Log.info('Hello!');
1730
- * Log.error('Oops!');
1731
- * ```
1732
- */
1733
- declare function createFacade<T extends object>(options: CreateFacadeOptions<T>): FacadeClass<T>;
1734
- /**
1735
- * Create a typed facade class (without proxy)
1736
- *
1737
- * @param accessor - Service identifier
1738
- * @returns Facade class constructor
1739
- */
1740
- declare function createFacadeClass<T>(accessor: ServiceIdentifier<T>): typeof Facade;
1741
- /**
1742
- * Get container from a module class
1743
- *
1744
- * @param moduleClass - The module class
1745
- * @returns ModuleContainer instance
1746
- */
1747
- declare function getContainerFromModule(moduleClass: Newable): ModuleContainer;
1748
1327
 
1749
- export { BaseRegistry, type BaseRegistryOptions, Collection$1 as Collection, type CreateFacadeOptions, Facade, type FacadeApplication, type FacadeClass, type Fake, MapCollection, SetCollection, Str, type ValidationResult, collect, collectMap, collectSet, createFacade, createFacadeClass, getContainerFromModule, isFake };
1328
+ export { BaseRegistry, type BaseRegistryOptions, Collection$1 as Collection, type DriverCreator, MapCollection, MultipleInstanceManager, type Collection as RegistryCollection, SetCollection, Str, type ValidationResult, collect, collectMap, collectSet };