|
| 1 | +import { BaseQueryArgsType } from "../../../lib/graphql/BaseQueryArgs.js"; |
| 2 | +import { AllowlistQueryStrategy } from "./AllowlistQueryStrategy.js"; |
| 3 | +import { AttestationsQueryStrategy } from "./AttestationQueryStrategy.js"; |
| 4 | +import { BlueprintsQueryStrategy } from "./BlueprintsQueryStrategy.js"; |
| 5 | +import { ClaimsQueryStrategy } from "./ClaimsQueryStrategy.js"; |
| 6 | +import { CollectionsQueryStrategy } from "./CollectionsQueryStrategy.js"; |
| 7 | +import { ContractsQueryStrategy } from "./ContractsQueryStrategy.js"; |
| 8 | +import { FractionsQueryStrategy } from "./FractionsQueryStrategy.js"; |
| 9 | +import { HyperboardsQueryStrategy } from "./HyperboardsQueryStrategy.js"; |
| 10 | +import { MarketplaceOrdersQueryStrategy } from "./MarketplaceOrdersQueryStrategy.js"; |
| 11 | +import { MetadataQueryStrategy } from "./MetadataQueryStrategy.js"; |
| 12 | +import { QueryStrategy, SupportedDatabases } from "./QueryStrategy.js"; |
| 13 | +import { SalesQueryStrategy } from "./SalesQueryStrategy.js"; |
| 14 | +import { SignatureRequestsQueryStrategy } from "./SignatureRequestsQueryStrategy.js"; |
| 15 | +import { SupportedSchemasQueryStrategy } from "./SupportedSchemasQueryStrategy.js"; |
| 16 | +import { UsersQueryStrategy } from "./UsersQueryStrategy.js"; |
| 17 | +import { EntityFields } from "../../../lib/graphql/createEntityArgs.js"; |
| 18 | +import { SortOptions } from "../../../lib/graphql/createEntitySortArgs.js"; |
| 19 | + |
| 20 | +/** |
| 21 | + * Base type for query arguments used across all strategies |
| 22 | + */ |
| 23 | +type QueryArgs = BaseQueryArgsType< |
| 24 | + Record<string, unknown>, |
| 25 | + SortOptions<EntityFields> |
| 26 | +>; |
| 27 | + |
| 28 | +/** |
| 29 | + * Type for strategy constructors to ensure they match the QueryStrategy interface |
| 30 | + */ |
| 31 | +type QueryStrategyConstructor< |
| 32 | + DB extends SupportedDatabases = SupportedDatabases, |
| 33 | + T extends keyof DB & string = keyof DB & string, |
| 34 | + Args extends QueryArgs = QueryArgs, |
| 35 | +> = new () => QueryStrategy<DB, T, Args>; |
| 36 | + |
| 37 | +/** |
| 38 | + * Type for the strategy registry mapping table names to their constructors |
| 39 | + */ |
| 40 | +type StrategyRegistry = { |
| 41 | + [K in keyof SupportedDatabases & string]: QueryStrategyConstructor< |
| 42 | + SupportedDatabases, |
| 43 | + K |
| 44 | + >; |
| 45 | +}; |
| 46 | + |
| 47 | +/** |
| 48 | + * Type for the strategy cache mapping table names to their instances |
| 49 | + */ |
| 50 | +type StrategyCache = { |
| 51 | + [K in keyof SupportedDatabases & string]?: QueryStrategy< |
| 52 | + SupportedDatabases, |
| 53 | + K |
| 54 | + >; |
| 55 | +}; |
| 56 | + |
| 57 | +/** |
| 58 | + * Factory class for creating query strategies for different tables |
| 59 | + * Uses a registry pattern for extensibility and a proxy for lazy loading |
| 60 | + */ |
| 61 | +export class QueryStrategyFactory { |
| 62 | + /** |
| 63 | + * Registry of strategy constructors |
| 64 | + * @private |
| 65 | + */ |
| 66 | + private static strategyRegistry: Partial<StrategyRegistry> = { |
| 67 | + attestations: AttestationsQueryStrategy, |
| 68 | + allowlist_records: AllowlistQueryStrategy, |
| 69 | + claimable_fractions_with_proofs: AllowlistQueryStrategy, |
| 70 | + blueprints_with_admins: BlueprintsQueryStrategy, |
| 71 | + blueprints: BlueprintsQueryStrategy, |
| 72 | + claims: ClaimsQueryStrategy, |
| 73 | + hypercerts: ClaimsQueryStrategy, |
| 74 | + collections: CollectionsQueryStrategy, |
| 75 | + contracts: ContractsQueryStrategy, |
| 76 | + fractions: FractionsQueryStrategy, |
| 77 | + fractions_view: FractionsQueryStrategy, |
| 78 | + hyperboards: HyperboardsQueryStrategy, |
| 79 | + metadata: MetadataQueryStrategy, |
| 80 | + orders: MarketplaceOrdersQueryStrategy, |
| 81 | + marketplace_orders: MarketplaceOrdersQueryStrategy, |
| 82 | + sales: SalesQueryStrategy, |
| 83 | + signature_requests: SignatureRequestsQueryStrategy, |
| 84 | + attestation_schema: SupportedSchemasQueryStrategy, |
| 85 | + eas_schema: SupportedSchemasQueryStrategy, |
| 86 | + supported_schemas: SupportedSchemasQueryStrategy, |
| 87 | + users: UsersQueryStrategy, |
| 88 | + }; |
| 89 | + |
| 90 | + /** |
| 91 | + * Cache of strategy instances |
| 92 | + * @private |
| 93 | + */ |
| 94 | + private static strategies: StrategyCache = new Proxy<StrategyCache>( |
| 95 | + {}, |
| 96 | + { |
| 97 | + get<K extends keyof SupportedDatabases & string>( |
| 98 | + target: StrategyCache, |
| 99 | + prop: K | string | symbol, |
| 100 | + ): QueryStrategy<SupportedDatabases, K> | undefined { |
| 101 | + if (typeof prop !== "string") { |
| 102 | + return undefined; |
| 103 | + } |
| 104 | + |
| 105 | + const key = prop as K; |
| 106 | + |
| 107 | + // Check if we already have a cached instance |
| 108 | + if (key in target && target[key]) { |
| 109 | + return target[key] as QueryStrategy<SupportedDatabases, K>; |
| 110 | + } |
| 111 | + |
| 112 | + // Get the constructor from the registry |
| 113 | + const Constructor = QueryStrategyFactory.strategyRegistry[key]; |
| 114 | + if (!Constructor) { |
| 115 | + throw new Error( |
| 116 | + `No strategy registered for table "${String(key)}". Available tables: ${Object.keys( |
| 117 | + QueryStrategyFactory.strategyRegistry, |
| 118 | + ).join(", ")}`, |
| 119 | + ); |
| 120 | + } |
| 121 | + |
| 122 | + // Create and cache a new instance |
| 123 | + const strategy = new Constructor() as QueryStrategy< |
| 124 | + SupportedDatabases, |
| 125 | + K |
| 126 | + >; |
| 127 | + (target as Record<K, QueryStrategy<SupportedDatabases, K>>)[key] = |
| 128 | + strategy; |
| 129 | + return strategy; |
| 130 | + }, |
| 131 | + }, |
| 132 | + ); |
| 133 | + |
| 134 | + /** |
| 135 | + * Get a strategy instance for a given table |
| 136 | + * Creates and caches the instance if it doesn't exist |
| 137 | + * |
| 138 | + * @param tableName - The name of the table to get a strategy for |
| 139 | + * @returns A query strategy instance for the given table |
| 140 | + * @throws Error if no strategy is registered for the table |
| 141 | + */ |
| 142 | + static getStrategy< |
| 143 | + DB extends SupportedDatabases, |
| 144 | + T extends keyof DB & string, |
| 145 | + Args extends QueryArgs = QueryArgs, |
| 146 | + >(tableName: T): QueryStrategy<DB, T, Args> { |
| 147 | + const strategy = (this.strategies as Record<T, QueryStrategy<DB, T, Args>>)[ |
| 148 | + tableName |
| 149 | + ]; |
| 150 | + if (!strategy) { |
| 151 | + throw new Error( |
| 152 | + `Failed to get strategy for table "${tableName}". This might be a type mismatch or the strategy is not properly registered.`, |
| 153 | + ); |
| 154 | + } |
| 155 | + return strategy; |
| 156 | + } |
| 157 | +} |
0 commit comments