UNPKG

studiocms

Version:

Astro Native CMS for AstroDB. Built from the ground up by the Astro community.

763 lines (702 loc) 31.3 kB
import { eq, like } from 'astro:db'; import { Effect, genLogger, pipe, Schema } from '../../../effect.js'; import { AstroDB, type LibSQLClientError } from '../effect/db.js'; import { noUndefinedEntries, parseData, parsedDataResponse, type RecursiveSimplifyMutable, SelectPluginDataRespondOrFail, } from '../effect/pluginUtils.js'; import { tsPluginData } from '../tables.js'; import type { PluginDataCacheObject, PluginDataEntry, tsPluginDataInsert, tsPluginDataSelect, UsePluginDataOpts, UsePluginDataOptsBase, UserPluginDataOptsImplementation, ValidatorOptions, } from '../types/index.js'; import { CacheContext, isCacheEnabled, isCacheExpired } from '../utils.js'; /** * Provides effectful operations for managing plugin-specific data entries in the database. * * The `SDKCore_PLUGINS` service exposes utilities for plugins to store, retrieve, and update * their own data entries, scoped by a unique `pluginId` and `entryId`. All operations are * effectful and designed to be used within an Effect context. * * @remarks * - Depends on the `AstroDB` service for database access. * - All methods are effectful and yield results or errors as Effects. * * @example * ```typescript * const plugins = yield* SDKCore_PLUGINS; * const pluginDataOps = yield* plugins.usePluginData('myPlugin', 'entry1'); * const data = yield* pluginDataOps.select<MyDataType>(); * ``` * * @service * @module studiocms/sdk/SDKCore/modules/plugins */ export class SDKCore_PLUGINS extends Effect.Service<SDKCore_PLUGINS>()( 'studiocms/sdk/SDKCore/modules/plugins', { dependencies: [AstroDB.Default], effect: genLogger('studiocms/sdk/SDKCore/modules/plugins/effect')(function* () { const [dbService, { pluginData }] = yield* Effect.all([AstroDB, CacheContext]); /** * Provides a set of database operations for managing plugin data entries. */ const _db = { /** * Creates a batch request function for querying plugin data from the database. * * The returned function accepts an object with `batchSize` and `offset` properties, * and performs a database query to select a batch of plugin data records with the specified * limit and offset. */ batchRequest: dbService.makeQuery((query, o: { batchSize: number; offset: number }) => query((db) => db.select().from(tsPluginData).limit(o.batchSize).offset(o.offset)) ), /** * Retrieves plugin data entries from the database whose IDs match the specified plugin ID prefix. */ getEntriesPluginData: dbService.makeQuery((query, pluginId: string) => query((db) => db .select() .from(tsPluginData) .where(like(tsPluginData.id, `${pluginId}-%`)) ) ), /** * Executes a database query to select a single plugin data entry by its ID. */ selectPluginDataEntry: dbService.makeQuery((query, id: string) => query((db) => db.select().from(tsPluginData).where(eq(tsPluginData.id, id)).get()) ), /** * Inserts a new plugin data entry into the database and returns the inserted record. */ insertPluginDataEntry: dbService.makeQuery((query, data: tsPluginDataInsert) => query((db) => db.insert(tsPluginData).values(data).returning().get()) ), /** * Updates an existing plugin data entry in the database. */ updatePluginDataEntry: dbService.makeQuery((query, data: tsPluginDataSelect) => query((db) => db.update(tsPluginData).set(data).where(eq(tsPluginData.id, data.id)).returning().get() ) ), }; /** * Initializes the plugin data cache by loading entries from the database in batches. * * This generator function retrieves plugin data from the database using a fixed batch size, * and populates the `pluginData` cache with each entry. All cached entries share a single * timestamp indicating when the cache was last updated. The function continues fetching and * caching entries until no more entries are returned from the database. * * @remarks * - Uses a default batch size of 100 entries per database query to efficiently handle large datasets. * - Each cache entry is stored as a tuple containing the entry data and the shared timestamp. * - Intended to be used as an effect within an effectful programming model. * * @yields {void} Yields control to the effect system for each database operation. */ const initPluginDataCache = Effect.fn( 'studiocms/sdk/SDKCore/modules/plugins/effect/initPluginDataCache' )(function* (BATCH_SIZE?: number) { let batchSize = BATCH_SIZE || 100; // Default batch size if not provided if (batchSize <= 0) { batchSize = 100; // Ensure a positive batch size } let offset = 0; const sharedTimestamp = new Date(); // Single timestamp for all entries while (true) { const entries = yield* _db.batchRequest({ batchSize, offset }); if (entries.length === 0) break; // Batch insert into cache const cacheEntries = entries.map( (entry) => [entry.id, { data: entry, lastCacheUpdate: sharedTimestamp }] as const ); // Use Map constructor or batch set operations for (const [id, cacheData] of cacheEntries) { pluginData.set(id, cacheData); } offset += batchSize; } }); /** * Attempts to clear the plugin data cache using the `pluginData.clear()` method. * If an error occurs during the cache clearing process, it logs the error to the console * and returns a new `Error` instance with a descriptive message. * * @returns {Effect<unknown, Error, void>} An Effect that represents the attempt to clear the plugin data cache, * resolving to `void` on success or an `Error` on failure. */ const clearPluginDataCache = (): Effect.Effect<void, Error, never> => Effect.try({ try: () => pluginData.clear(), catch: () => new Error('Failed to clear plugin data cache'), }); /** * Retrieves a plugin data entry by its ID, utilizing a cache if enabled. * * - If caching is enabled and the entry exists in the cache and is not expired, returns the cached data. * - If the entry is not in the cache or the cache is expired, fetches fresh data from the database, * updates the cache, and returns the fresh data. * - If caching is not enabled, always fetches the latest data from the database. * * @param id - The unique identifier of the plugin data entry to retrieve. * @returns An object containing the plugin data entry, or `undefined` if not found. */ const _selectPluginDataEntry = Effect.fn( 'studiocms/sdk/SDKCore/modules/plugins/effect/_selectPluginDataEntry' )(function* (id: string) { if (yield* isCacheEnabled) { // Check the cache for the plugin data entry const cached = pluginData.get(id); if (cached && !isCacheExpired(cached)) { const { data: cacheData } = cached; // If the entry is found in the cache and is not expired, return it return cacheData; } // If the entry is not found in the cache or is expired, query the database // and update the cache with the new data const fresh = yield* _db.selectPluginDataEntry(id); if (fresh) { pluginData.set(id, { data: fresh, lastCacheUpdate: new Date(), }); } return fresh; } // If caching is not enabled, directly query the database // This ensures that we always get the latest data from the database return yield* _db.selectPluginDataEntry(id); }); /** * Inserts a new plugin data entry into the database and updates the cache if enabled. * * @param data - The plugin data to insert. * @returns The newly inserted plugin data entry. * * @remarks * If caching is enabled, this function will update the in-memory cache with the new data, * ensuring cache consistency with the database. */ const _insertPluginDataEntry = Effect.fn( 'studiocms/sdk/SDKCore/modules/plugins/effect/_insertPluginDataEntry' )(function* (data: tsPluginDataInsert) { // Insert the plugin data entry into the database const newData = yield* _db.insertPluginDataEntry(data); // If caching is enabled, update the cache with the new data // This ensures that the cache is always in sync with the database if (yield* isCacheEnabled) { pluginData.set(newData.id, { data: newData, lastCacheUpdate: new Date(), }); } // Return the newly inserted data return newData; }); /** * Updates a plugin data entry in the database and, if caching is enabled, updates the cache with the new data. * * @param data - The plugin data entry to update. * @returns The updated plugin data entry. * * @remarks * This function performs the update operation using `_dbUpdatePluginDataEntry`. If caching is enabled, * it also updates the in-memory cache with the new data and the current timestamp. */ const _updatePluginDataEntry = Effect.fn( 'studiocms/sdk/SDKCore/modules/plugins/effect/_updatePluginDataEntry' )(function* (data: tsPluginDataSelect) { // Update the plugin data entry in the database const updatedData = yield* _db.updatePluginDataEntry(data); // If caching is enabled, update the cache with the new data if (yield* isCacheEnabled) { pluginData.set(updatedData.id, { data: updatedData, lastCacheUpdate: new Date(), }); } // Return the updated data return updatedData; }); /** * Processes a plugin data cache entry, validating and updating it as necessary. * * This generator function checks if the cache entry is associated with the specified plugin, * determines if the cache is expired, and if so, fetches the latest data from the database. * It validates the data using the provided validator, updates the cache if needed, and returns * a parsed data response. If the cache is not expired, it returns the validated cached data. * * @template T - The type of the data object to validate and return. * @param {[string, PluginDataCacheObject]} param0 - A tuple containing the cache key and the cache object. * @param {string} pluginId - The ID of the plugin to filter cache entries. * @param {ValidatorOptions<T>} [validator] - Optional validator options for data validation. * @returns {Effect<unknown, unknown, (ParsedDataResponse<T> | undefined)>} The parsed data response for the entry, or undefined if not applicable. */ const _processEntryFromCache = Effect.fn(function* < T extends Schema.Struct<Schema.Struct.Fields> | object, >( [key, { data: entry, lastCacheUpdate }]: [string, PluginDataCacheObject], pluginId: string, validator?: ValidatorOptions<T> ) { // If the key does not start with the pluginId, skip it // This ensures that we only process entries related to the specified plugin if (!key.startsWith(pluginId)) { return undefined; } if ((yield* isCacheEnabled) && isCacheExpired({ lastCacheUpdate })) { // If the cache is expired, we need to fetch the latest data from the database const freshEntry = yield* _db.selectPluginDataEntry(entry.id); // If the entry is not found in the database, we can skip it if (!freshEntry) { yield* Effect.log(`Removing stale cache entry: ${entry.id}`); pluginData.delete(entry.id); return undefined; } // Validate the fresh entry data // This ensures that we always return valid data const validated = yield* parseData<T>(freshEntry.data, validator); // If the entry is found in the database, update the cache pluginData.set(entry.id, { data: freshEntry, lastCacheUpdate: new Date(), }); // Return the parsed data response for the entry // This ensures that we always return the most up-to-date data return yield* parsedDataResponse<T>(entry.id, validated); } // If the entry is not expired or not found in the database, return the cached data const validated = yield* parseData<T>(entry.data, validator); return yield* parsedDataResponse<T>(entry.id, validated); }); /** * Processes a plugin data entry retrieved from the database. * * @template T - The expected shape of the validated data object. * @param entry - The plugin data entry to process. * @param validator - Optional validator options to validate the entry's data. * @yields The validated data after parsing. * @yields Updates the cache with the entry if caching is enabled. * @returns The parsed data response for the entry. */ const _processEntryFromDB = Effect.fn(function* < T extends Schema.Struct<Schema.Struct.Fields> | object, >(entry: tsPluginDataSelect, validator?: ValidatorOptions<T>) { // Validate the data for each entry const validated = yield* parseData<T>(entry.data, validator); // If caching is not enabled, we do not update the cache if (yield* isCacheEnabled) { // If caching is enabled, update the cache with the new data pluginData.set(entry.id, { data: entry, lastCacheUpdate: new Date(), }); } // Return the parsed data response for the entry return yield* parsedDataResponse<T>(entry.id, validated); }); /** * Retrieves plugin data entries for a given plugin ID, with optional validation. * * This function first attempts to retrieve entries from the in-memory cache if caching is enabled. * - If cached entries are found and not expired, they are validated and returned. * - If cached entries are expired, the latest data is fetched from the database, validated, and the cache is updated. * - If no entries are found in the cache, all entries are fetched from the database, validated, and cached. * * If caching is not enabled, all entries are fetched directly from the database and validated. * * @typeParam T - The expected shape of the plugin data after validation. * @param pluginId - The unique identifier for the plugin whose entries are to be retrieved. * @param validator - (Optional) Validation options to apply to each entry's data. * @param filter - (Optional) A callback function to filter the entries after retrieval. * @returns An Effect yielding an array of validated and parsed plugin data responses. */ const _getEntries = Effect.fn('studiocms/sdk/SDKCore/modules/plugins/effect/_getEntries')( function* <T extends Schema.Struct<Schema.Struct.Fields> | object>( pluginId: string, validator?: ValidatorOptions<T>, filter?: (data: PluginDataEntry<T>[]) => PluginDataEntry<T>[] ) { if (yield* isCacheEnabled) { const data = yield* pipe( pluginData.entries(), Effect.forEach((entry) => _processEntryFromCache<T>(entry, pluginId, validator)), Effect.map(noUndefinedEntries) ); // If we have valid data from the cache, return it if (data.length > 0) return data; } // Make initial todo to fetch entries from the database and process them const todo = pipe( _db.getEntriesPluginData(pluginId), Effect.flatMap(Effect.forEach((entry) => _processEntryFromDB<T>(entry, validator))) ); // If a filter callback is provided, apply it to the todo result if (filter) return yield* todo.pipe(Effect.map(filter)); // Otherwise, return the todo result directly return yield* todo; } ); /** * Checks if plugin data with the specified ID exists and responds or fails based on the `shouldFail` flag. * * @param id - The unique identifier of the plugin data to check. * @param shouldFail - Determines the behavior when plugin data with the given ID exists: * - If `true` and the data exists, the effect fails with an error. * - If `false` and the data does not exist, returns `false`. * - Otherwise, returns `true`. * @returns * - Fails with an error if the data exists and `shouldFail` is `true`. * - Returns `false` if the data does not exist and `shouldFail` is `false`. * - Returns `true` otherwise. */ const _selectPluginDataEntryRespondOrFail = Effect.fn( 'studiocms/sdk/SDKCore/modules/plugins/effect/_selectPluginDataEntryRespondOrFail' )(function* (id: string, mode: SelectPluginDataRespondOrFail) { // Check if the plugin data with the given ID exists const existing = yield* _selectPluginDataEntry(id); // If the plugin data exists, we handle it based on the mode and shouldFail flag // If it does not exist, we handle it based on the mode and shouldFail switch (mode) { case SelectPluginDataRespondOrFail.ExistsNoFail: { // If it exists, return the existing data if (existing) return existing; // If it does not exist, return undefined return undefined; } case SelectPluginDataRespondOrFail.ExistsShouldFail: { // If it exists, fail with an error if (existing) return yield* Effect.fail(new Error(`Plugin data with ID ${id} already exists.`)); // If it does not exist, return undefined return undefined; } case SelectPluginDataRespondOrFail.NotExistsShouldFail: { // If it does not exist, fail with an error if (!existing) return yield* Effect.fail(new Error(`Plugin data with ID ${id} does not exist.`)); // If it exists, return undefined return undefined; } default: return yield* Effect.fail(new Error(`Invalid mode: ${mode}`)); } }); /** * Selects and parses plugin data for a given entry ID. * * This generator function attempts to retrieve plugin data associated with the specified `generatedEntryId`. * - If the data exists, it validates and parses the data using the provided `validator` (if any), and returns the parsed data response. * - If the data does not exist, it returns `undefined` without throwing an error, making it suitable for optional plugin data scenarios. * * @typeParam T - The expected schema or object type for the plugin data. * @param generatedEntryId - The unique identifier for the plugin data entry to select. * @param validator - (Optional) Validation options or schema for parsing the plugin data. * @returns The parsed plugin data response of type `T`, or `undefined` if the entry does not exist. */ const _select = Effect.fn( 'studiocms/sdk/SDKCore/modules/plugins/effect/usePluginData.select' )(function* <T extends Schema.Struct<Schema.Struct.Fields> | object>( generatedEntryId: string, validator?: ValidatorOptions<T> ) { // Check if the plugin data with the given ID exists // If it exists, proceed to validate and parse the data // If it does not exist, return undefined // This ensures that we only attempt to parse existing data // and do not throw an error when the data is not found // This is useful for cases where the plugin data is optional const existing = yield* _selectPluginDataEntryRespondOrFail( generatedEntryId, SelectPluginDataRespondOrFail.ExistsNoFail ); // If it does not exist, return undefined if (!existing) return undefined; // Validate and parse the existing data const data = yield* parseData<T>(existing.data, validator); // Return the parsed data response for the existing entry return yield* parsedDataResponse<T>(generatedEntryId, data); }); /** * Inserts a new plugin data entry into the database. * * This function first checks if an entry with the given `generatedEntryId` already exists. * If it does, the operation fails to ensure uniqueness and prevent duplicate entries. * If not, it validates the provided data using the optional `validator`, then inserts the new entry. * The inserted data is returned in a parsed response format. * * @template T - The type of the data to insert, which can be a Schema.Struct or a plain object. * @param generatedEntryId - The unique identifier for the plugin data entry. * @param data - The data to be inserted. * @param validator - Optional validation options for the data. * @returns The inserted and parsed plugin data entry. * @throws If an entry with the given ID already exists or if validation fails. */ const _insert = Effect.fn( 'studiocms/sdk/SDKCore/modules/plugins/effect/usePluginData.insert' )(function* <T extends Schema.Struct<Schema.Struct.Fields> | object>( generatedEntryId: string, data: T, validator?: ValidatorOptions<T> ) { // Check if the plugin data with the given ID already exists // If it exists, fail with an error // This ensures that we do not accidentally insert duplicate entries // and maintain the uniqueness of the plugin data entries // If it does not exist, proceed to insert the new data yield* _selectPluginDataEntryRespondOrFail( generatedEntryId, SelectPluginDataRespondOrFail.ExistsShouldFail ); // Validate the data before inserting const parsedData = yield* parseData<T>(data, validator); // Insert the new plugin data into the database // Note: The 'id' field is expected to be unique, so we use // it as the primary key in the table definition. const inserted = yield* _insertPluginDataEntry({ id: generatedEntryId, data: parsedData, }); // Return the inserted data return yield* parsedDataResponse<T>(inserted.id, parsedData); }); /** * Updates an existing plugin data entry by its generated ID. * * This function performs the following steps: * 1. Checks if the plugin data entry with the specified ID exists. * - If it does not exist, the operation fails to prevent accidental creation. * 2. Optionally validates the provided data using the given validator options. * 3. Updates the existing plugin data entry in the database with the validated data. * 4. Returns a parsed data response for the updated record. * * @template T - The shape of the data to update, either a Schema.Struct or a plain object. * @param generatedEntryId - The unique identifier of the plugin data entry to update. * @param data - The new data to update the entry with. * @param validator - (Optional) Validator options to validate the data before updating. * @returns The parsed data response for the updated plugin data entry. * @throws If the plugin data entry does not exist or if validation fails. */ const _update = Effect.fn( 'studiocms/sdk/SDKCore/modules/plugins/effect/usePluginData.update' )(function* <T extends Schema.Struct<Schema.Struct.Fields> | object>( generatedEntryId: string, data: T, validator?: ValidatorOptions<T> ) { // Check if the plugin data with the given ID exists // If it does not exist, fail with an error // This ensures that we only update existing records // and prevents accidental creation of new records // when trying to update non-existing data yield* _selectPluginDataEntryRespondOrFail( generatedEntryId, SelectPluginDataRespondOrFail.NotExistsShouldFail ); // Validate the data before updating const parsedData = yield* parseData<T>(data, validator); // Update the existing plugin data in the database const updated = yield* _updatePluginDataEntry({ id: generatedEntryId, data: parsedData, }); // Return the parsed data response for the updated record return yield* parsedDataResponse<T>(updated.id, parsedData); }); const buildReturn = <T extends Schema.Struct<Schema.Struct.Fields> | object>( pluginId: string, entryId: string, validator?: ValidatorOptions<T> ) => { const generatedEntryId = `${pluginId}-${entryId}`; return { /** * Generates a unique ID for the plugin data entry. * * @returns An Effect that yields the generated ID. In the format `${pluginId}-${entryId}` */ generatedId: () => Effect.succeed(generatedEntryId), /** * Selects a plugin data entry by its ID, validating the data if a validator is provided. * * @returns An Effect that yields the selected plugin data entry or `undefined` if not found. */ select: () => _select<T>(generatedEntryId, validator), /** * Inserts new plugin data into the database after validating the input. * * @param data - The plugin data to insert. * @yields Throws an error if validation fails or if the entry already exists. * @returns The parsed data response for the inserted entry. */ insert: (data: T) => _insert<T>(generatedEntryId, data, validator), /** * Updates existing plugin data in the database after validating the input. * * @param data - The updated plugin data. * @yields Throws an error if validation fails. * @returns The parsed data response for the updated entry. */ update: (data: T) => _update<T>(generatedEntryId, data, validator), }; }; // Function overloads for `usePluginData` to handle different cases: // This function provides a set of effectful operations for managing plugin data entries. // It can be called with just a pluginId to retrieve all entries, // or with both pluginId and entryId to perform CRUD operations on a specific entry. // This allows for flexible usage depending on whether the user wants to // manage all entries for a plugin or a specific entry. /** * Retrieves all plugin data entries for a given plugin ID. */ function usePluginData< T extends Schema.Struct<Schema.Struct.Fields> | object, // biome-ignore lint/suspicious/noExplicitAny: This is a generic type for the plugin data. R extends object = T extends Schema.Struct<any> ? RecursiveSimplifyMutable<T['Type']> : T, >( pluginId: string, opts?: UsePluginDataOptsBase<T> ): { getEntries: ( filter?: (data: PluginDataEntry<R>[]) => PluginDataEntry<R>[] ) => Effect.Effect<PluginDataEntry<R>[], LibSQLClientError | Error, never>; getEntry: (id: string) => { generatedId: () => Effect.Effect<string, never, never>; select: () => Effect.Effect< PluginDataEntry<R> | undefined, LibSQLClientError | Error, never >; insert: (data: R) => Effect.Effect<PluginDataEntry<R>, LibSQLClientError | Error, never>; update: (data: R) => Effect.Effect<PluginDataEntry<R>, LibSQLClientError | Error, never>; }; }; /** * Retrieves or manipulates plugin data entries for a specific plugin ID and entry ID. * * @param pluginId - The unique identifier for the plugin. * @param entryId - (Optional) The unique identifier for the plugin data entry. * @returns An object with methods to manage plugin data entries. */ function usePluginData< T extends Schema.Struct<Schema.Struct.Fields> | object, // biome-ignore lint/suspicious/noExplicitAny: This is a generic type for the plugin data. R extends object = T extends Schema.Struct<any> ? RecursiveSimplifyMutable<T['Type']> : T, >( pluginId: string, opts?: UsePluginDataOpts<T> ): { generatedId: () => Effect.Effect<string, never, never>; select: () => Effect.Effect< PluginDataEntry<R> | undefined, LibSQLClientError | Error, never >; insert: (data: R) => Effect.Effect<PluginDataEntry<R>, LibSQLClientError | Error, never>; update: (data: R) => Effect.Effect<PluginDataEntry<R>, LibSQLClientError | Error, never>; }; /** * Implementation of the `usePluginData` function that provides access to plugin data entries. */ function usePluginData<T extends Schema.Struct<Schema.Struct.Fields> | object>( pluginId: string, { entryId, validator }: UserPluginDataOptsImplementation<T> = {} ) { if (!entryId) { return { /** * Retrieves all plugin data entries for the specified plugin ID. * * @template T - The type of the plugin data object. * @param validator - Optional validator options for validating the plugin data. * @returns An Effect that yields an array of `PluginDataEntry<T>` objects. */ getEntries: (filter?: (data: PluginDataEntry<T>[]) => PluginDataEntry<T>[]) => _getEntries<T>(pluginId, validator, filter), getEntry: (id: string) => buildReturn<T>(pluginId, id, validator), }; } return buildReturn<T>(pluginId, entryId, validator); } /** * Utility class to infer types from a given Schema. * * @typeParam S - The schema type extending `Schema.Struct<any>`. * * @property _Schema - The schema instance used for type inference. * @property usePluginData - The inferred type from the schema, used for plugin data. * @property Insert - A recursively simplified, mutable version of the schema's type. * */ class InferType< // biome-ignore lint/suspicious/noExplicitAny: as this is a generic type for the plugin data. S extends Schema.Struct<any>, R = RecursiveSimplifyMutable<S['Type']>, > { readonly _Schema: S; readonly $UsePluginData!: S; readonly $Insert!: R; constructor(schema: S) { if (!schema || !Schema.isSchema(schema)) { throw new Error('InferType requires a valid Schema.Struct instance.'); } this._Schema = schema; } } return { /** * Provides a set of effectful operations for managing plugin data entries by plugin ID and optional entry ID. * * When an `entryId` is provided, returns an object with methods to: * - Generate a unique plugin data entry ID. * - Insert new plugin data after validation and duplicate checks. * - Select and validate existing plugin data by ID. * - Update existing plugin data after validation. * * When no `entryId` is provided, returns an object with a method to retrieve all entries for the given plugin. * * @param pluginId - The unique identifier for the plugin. * @param entryId - (Optional) The unique identifier for the plugin data entry. * @returns An object with effectful methods for plugin data management, varying by presence of `entryId`. */ usePluginData, /** * Initializes the plugin data cache by fetching all existing entries from the database * and populating the in-memory cache with these entries. */ initPluginDataCache, /** * Clears the plugin data cache, removing all cached entries. * * @returns An Effect that resolves to `void` on success or an `Error` on failure. */ clearPluginDataCache, /** * Utility class to infer types from a given Schema. * * @typeParam S - The schema type extending `Schema.Struct<any>`. * * @property _Schema - The schema instance used for type inference. * @property usePluginData - The inferred type from the schema, used for plugin data. * @property Insert - A recursively simplified, mutable version of the schema's type. * */ InferType, }; }), } ) {}