@tanstack/db
Version:
A reactive client store for building super fast apps on sync
1 lines • 9.05 kB
Source Map (JSON)
{"version":3,"file":"validate-config.cjs","sources":["../../../src/collection/validate-config.ts"],"sourcesContent":["import {\n CollectionRequiresConfigError,\n CollectionRequiresSyncConfigError,\n} from '../errors'\nimport {\n CollectionRequiresGetKeyError,\n InvalidCallbackOptionError,\n InvalidGetKeyError,\n InvalidOptionTypeError,\n InvalidSyncConfigError,\n InvalidSyncFunctionError,\n} from './config-errors'\n\n/**\n * Known top-level config properties for createCollection.\n * Extra properties are allowed; these names are only used for typo suggestions.\n */\nconst KNOWN_CONFIG_KEYS = new Set([\n `id`,\n `schema`,\n `getKey`,\n `sync`,\n `gcTime`,\n `startSync`,\n `autoIndex`,\n `defaultIndexType`,\n `compare`,\n `syncMode`,\n `defaultStringCollation`,\n `onInsert`,\n `onUpdate`,\n `onDelete`,\n `utils`,\n `singleResult`,\n // Metadata returned by persistedCollectionOptions.\n `persistence`,\n])\n\n// Limit suggestions to casing mistakes and adjacent swaps in longer names.\n// Broader edit-distance matching can mistake adapter fields for core options.\nfunction findLikelyTypo(\n key: string,\n config: Record<string, unknown>,\n): string | undefined {\n for (const knownKey of KNOWN_CONFIG_KEYS) {\n if (knownKey in config) continue\n if (key.toLowerCase() === knownKey.toLowerCase()) return knownKey\n if (key.length < 5 || key.length !== knownKey.length) continue\n\n for (let i = 0; i < key.length - 1; i++) {\n const swapped = key.slice(0, i) + key[i + 1] + key[i] + key.slice(i + 2)\n if (swapped === knownKey) return knownKey\n }\n }\n return undefined\n}\n\nfunction describeType(value: unknown): string {\n if (value === null) return `null`\n if (value === undefined) return `undefined`\n if (Array.isArray(value)) return `an array`\n return typeof value\n}\n\n/**\n * Validates the collection config at runtime, providing clear error messages\n * for common misconfiguration mistakes that would otherwise surface as\n * unreadable TypeScript errors.\n *\n * This runs before the config is passed to CollectionImpl, catching issues\n * early with actionable error messages.\n */\nexport function validateCollectionConfig(config: unknown): void {\n // Check config exists and is an object\n if (!config || typeof config !== `object` || Array.isArray(config)) {\n throw new CollectionRequiresConfigError()\n }\n\n const configObj = config as Record<string, unknown>\n\n // Adapter metadata is valid. Warn only when an extra key is likely a typo.\n for (const key of Object.keys(configObj)) {\n if (!KNOWN_CONFIG_KEYS.has(key) && !key.startsWith(`_`)) {\n const suggestion = findLikelyTypo(key, configObj)\n if (suggestion) {\n console.warn(\n `Possible misspelling in collection config: \"${key}\". Did you mean \"${suggestion}\"?`,\n )\n }\n }\n }\n\n // Validate getKey\n if (!(`getKey` in configObj) || configObj.getKey === undefined) {\n throw new CollectionRequiresGetKeyError()\n }\n if (typeof configObj.getKey !== `function`) {\n throw new InvalidGetKeyError(describeType(configObj.getKey))\n }\n\n // Validate sync\n if (!configObj.sync) {\n throw new CollectionRequiresSyncConfigError()\n }\n if (typeof configObj.sync !== `object` || Array.isArray(configObj.sync)) {\n throw new InvalidSyncConfigError(describeType(configObj.sync))\n }\n const syncObj = configObj.sync as Record<string, unknown>\n if (typeof syncObj.sync !== `function`) {\n throw new InvalidSyncFunctionError(describeType(syncObj.sync))\n }\n\n // Validate callback options\n const callbackOptions = [\n `onInsert`,\n `onUpdate`,\n `onDelete`,\n `compare`,\n ] as const\n for (const optionName of callbackOptions) {\n if (\n optionName in configObj &&\n configObj[optionName] !== undefined &&\n typeof configObj[optionName] !== `function`\n ) {\n throw new InvalidCallbackOptionError(\n optionName,\n describeType(configObj[optionName]),\n )\n }\n }\n\n // Validate id\n if (`id` in configObj && configObj.id !== undefined) {\n if (typeof configObj.id !== `string`) {\n throw new InvalidOptionTypeError(\n `id`,\n `a string`,\n describeType(configObj.id),\n )\n }\n }\n\n // Validate gcTime\n if (`gcTime` in configObj && configObj.gcTime !== undefined) {\n if (typeof configObj.gcTime !== `number`) {\n throw new InvalidOptionTypeError(\n `gcTime`,\n `a number`,\n describeType(configObj.gcTime),\n )\n }\n }\n\n // Validate startSync\n if (`startSync` in configObj && configObj.startSync !== undefined) {\n if (typeof configObj.startSync !== `boolean`) {\n throw new InvalidOptionTypeError(\n `startSync`,\n `a boolean`,\n describeType(configObj.startSync),\n )\n }\n }\n\n // Validate autoIndex\n if (`autoIndex` in configObj && configObj.autoIndex !== undefined) {\n if (configObj.autoIndex !== `off` && configObj.autoIndex !== `eager`) {\n throw new InvalidOptionTypeError(\n `autoIndex`,\n `\"off\" or \"eager\"`,\n String(configObj.autoIndex),\n )\n }\n }\n\n // Validate syncMode\n if (`syncMode` in configObj && configObj.syncMode !== undefined) {\n if (configObj.syncMode !== `eager` && configObj.syncMode !== `on-demand`) {\n throw new InvalidOptionTypeError(\n `syncMode`,\n `\"eager\" or \"on-demand\"`,\n String(configObj.syncMode),\n )\n }\n }\n\n // Validate utils\n if (`utils` in configObj && configObj.utils !== undefined) {\n if (\n typeof configObj.utils !== `object` ||\n configObj.utils === null ||\n Array.isArray(configObj.utils)\n ) {\n throw new InvalidOptionTypeError(\n `utils`,\n `an object`,\n describeType(configObj.utils),\n )\n }\n }\n}\n"],"names":["CollectionRequiresConfigError","CollectionRequiresGetKeyError","InvalidGetKeyError","CollectionRequiresSyncConfigError","InvalidSyncConfigError","InvalidSyncFunctionError","InvalidCallbackOptionError","InvalidOptionTypeError"],"mappings":"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"}