@routier/core 0.2.0 → 0.3.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/LICENSE +21 -0
- package/{readme.md → README.md} +2 -2
- package/dist/assertions/index.cjs +191 -0
- package/dist/assertions/index.cjs.map +1 -0
- package/dist/assertions/index.d.ts +30 -0
- package/dist/assertions/index.js +94 -30
- package/dist/assertions/index.js.map +1 -1
- package/dist/capabilities/index.cjs +820 -0
- package/dist/capabilities/index.cjs.map +1 -0
- package/dist/capabilities/index.js +580 -545
- package/dist/capabilities/index.js.map +1 -1
- package/dist/codegen/handlers/CloneHandlerBuilder.d.ts +10 -1
- package/dist/codegen/handlers/SetHandlerBuilder.d.ts +4 -0
- package/dist/codegen/handlers/clone/CloneArrayHandler.d.ts +3 -0
- package/dist/codegen/handlers/clone/CloneDateHandler.d.ts +13 -0
- package/dist/codegen/handlers/clone/CloneValueHandler.d.ts +16 -0
- package/dist/codegen/handlers/compare/CompareComputedHandler.d.ts +10 -0
- package/dist/codegen/handlers/compare/CompareFunctionHandler.d.ts +10 -0
- package/dist/codegen/handlers/deserialize/DeserializeArrayHandler.d.ts +14 -0
- package/dist/codegen/handlers/enableChangeTracking/EnableChangeTrackingArrayHandler.d.ts +11 -0
- package/dist/codegen/handlers/enrichment/EnrichmentArrayHandler.d.ts +11 -0
- package/dist/codegen/handlers/freeze/FreezeArrayHandler.d.ts +6 -0
- package/dist/codegen/handlers/hash/HashArrayHandler.d.ts +10 -0
- package/dist/codegen/handlers/hash/HashFileHandler.d.ts +24 -0
- package/dist/codegen/handlers/hash/HashObjectHandler.d.ts +10 -0
- package/dist/codegen/handlers/hash/HashTransformHandler.d.ts +23 -0
- package/dist/codegen/handlers/index.d.ts +1 -0
- package/dist/codegen/handlers/merge/MergeArrayHandler.d.ts +20 -0
- package/dist/codegen/handlers/merge/MergeObjectHandler.d.ts +10 -0
- package/dist/codegen/handlers/serialize/SerializeArrayHandler.d.ts +19 -0
- package/dist/codegen/handlers/serialize/SerializeComputedHandler.d.ts +6 -0
- package/dist/codegen/handlers/set/SetComplexHandler.d.ts +6 -0
- package/dist/codegen/handlers/strip/StripComputedHandler.d.ts +10 -0
- package/dist/codegen/handlers/strip/StripFunctionHandler.d.ts +9 -0
- package/dist/codegen/handlers/types.d.ts +55 -1
- package/dist/codegen/index.cjs +618 -0
- package/dist/codegen/index.cjs.map +1 -0
- package/dist/codegen/index.js +100 -127
- package/dist/codegen/index.js.map +1 -1
- package/dist/collections/Changes.d.ts +2 -2
- package/dist/collections/MemoryDataCollection.d.ts +15 -1
- package/dist/collections/TagCollection.d.ts +7 -8
- package/dist/collections/index.cjs +702 -0
- package/dist/collections/index.cjs.map +1 -0
- package/dist/collections/index.js +282 -276
- package/dist/collections/index.js.map +1 -1
- package/dist/errors/OptimisticConcurrencyError.d.ts +18 -0
- package/dist/errors/PluginDestroyedError.d.ts +10 -0
- package/dist/errors/index.cjs +132 -0
- package/dist/errors/index.cjs.map +1 -0
- package/dist/errors/index.d.ts +2 -0
- package/dist/errors/index.js +61 -19
- package/dist/errors/index.js.map +1 -1
- package/dist/expressions/constants.d.ts +2 -0
- package/dist/expressions/evaluate.d.ts +53 -0
- package/dist/expressions/index.cjs +2087 -0
- package/dist/expressions/index.cjs.map +1 -0
- package/dist/expressions/index.d.ts +2 -0
- package/dist/expressions/index.js +1784 -772
- package/dist/expressions/index.js.map +1 -1
- package/dist/expressions/parser.d.ts +19 -0
- package/dist/expressions/types.d.ts +87 -2
- package/dist/expressions/utils.d.ts +1 -2
- package/dist/index.cjs +13925 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.js +12094 -7049
- package/dist/index.js.map +1 -1
- package/dist/performance/index.cjs +217 -0
- package/dist/performance/index.cjs.map +1 -0
- package/dist/performance/index.js +126 -50
- package/dist/performance/index.js.map +1 -1
- package/dist/pipeline/index.cjs +576 -0
- package/dist/pipeline/index.cjs.map +1 -0
- package/dist/pipeline/index.js +229 -156
- package/dist/pipeline/index.js.map +1 -1
- package/dist/plugins/BatchingDbPlugin.d.ts +139 -0
- package/dist/plugins/CacheDbPlugin.d.ts +70 -0
- package/dist/plugins/ConcurrencyDbPlugin.d.ts +78 -0
- package/dist/plugins/EphemeralDataPlugin.d.ts +57 -1
- package/dist/plugins/RetryDbPlugin.d.ts +77 -0
- package/dist/plugins/index.cjs +6104 -0
- package/dist/plugins/index.cjs.map +1 -0
- package/dist/plugins/index.d.ts +5 -1
- package/dist/plugins/index.js +5572 -1491
- package/dist/plugins/index.js.map +1 -1
- package/dist/plugins/query/QueryOptionsCollection.d.ts +22 -0
- package/dist/plugins/query/index.d.ts +2 -0
- package/dist/plugins/query/join.d.ts +210 -0
- package/dist/plugins/query/similarity.d.ts +38 -0
- package/dist/plugins/query/types.d.ts +64 -1
- package/dist/plugins/translators/DataTranslator.d.ts +28 -0
- package/dist/plugins/translators/JsonTranslator.d.ts +27 -0
- package/dist/plugins/translators/SqlTranslator.d.ts +45 -0
- package/dist/plugins/translators/TranslatedArrayValue.d.ts +3 -1
- package/dist/plugins/translators/TranslatedGroupValue.d.ts +3 -1
- package/dist/plugins/translators/TranslatedSingleValue.d.ts +3 -1
- package/dist/plugins/translators/TupleTranslator.d.ts +45 -0
- package/dist/plugins/translators/index.d.ts +1 -0
- package/dist/plugins/translators/types.d.ts +8 -1
- package/dist/plugins/types.d.ts +87 -11
- package/dist/plugins/wire/handler.d.ts +122 -0
- package/dist/plugins/wire/index.d.ts +4 -0
- package/dist/plugins/wire/persist.d.ts +43 -0
- package/dist/plugins/wire/query.d.ts +49 -0
- package/dist/plugins/wire/types.d.ts +123 -0
- package/dist/results/Result.d.ts +2 -2
- package/dist/results/index.cjs +174 -0
- package/dist/results/index.cjs.map +1 -0
- package/dist/results/index.js +29 -44
- package/dist/results/index.js.map +1 -1
- package/dist/schema/PropertyInfo.d.ts +35 -2
- package/dist/schema/SchemaDefinition.d.ts +51 -1
- package/dist/schema/builder.d.ts +35 -1
- package/dist/schema/communication/broadcast.d.ts +17 -1
- package/dist/schema/index.cjs +6911 -0
- package/dist/schema/index.cjs.map +1 -0
- package/dist/schema/index.d.ts +4 -0
- package/dist/schema/index.js +5781 -3973
- package/dist/schema/index.js.map +1 -1
- package/dist/schema/property/base/SchemaBase.d.ts +50 -2
- package/dist/schema/property/modifiers/SchemaDeserialize.d.ts +5 -1
- package/dist/schema/property/modifiers/SchemaForeignKey.d.ts +15 -0
- package/dist/schema/property/modifiers/SchemaKey.d.ts +2 -0
- package/dist/schema/property/modifiers/SchemaNullable.d.ts +15 -1
- package/dist/schema/property/modifiers/SchemaOptional.d.ts +22 -1
- package/dist/schema/property/modifiers/SchemaSearchable.d.ts +34 -0
- package/dist/schema/property/modifiers/SchemaTag.d.ts +27 -0
- package/dist/schema/property/modifiers/SchemaTracked.d.ts +5 -1
- package/dist/schema/property/modifiers/index.d.ts +3 -0
- package/dist/schema/property/types/SchemaArray.d.ts +2 -0
- package/dist/schema/property/types/SchemaBoolean.d.ts +2 -0
- package/dist/schema/property/types/SchemaDate.d.ts +2 -0
- package/dist/schema/property/types/SchemaFile.d.ts +60 -0
- package/dist/schema/property/types/SchemaNumber.d.ts +5 -1
- package/dist/schema/property/types/SchemaObject.d.ts +2 -0
- package/dist/schema/property/types/SchemaString.d.ts +25 -1
- package/dist/schema/property/types/SchemaVector.d.ts +61 -0
- package/dist/schema/property/types/index.d.ts +2 -0
- package/dist/schema/table/SchemaTransform.d.ts +40 -0
- package/dist/schema/table/index.d.ts +1 -0
- package/dist/schema/types.d.ts +200 -32
- package/dist/schema/utils/propertyKind.d.ts +4 -0
- package/dist/schema/utils/standardJsonSchema.d.ts +90 -0
- package/dist/types/index.cjs +23 -0
- package/dist/types/index.cjs.map +1 -0
- package/dist/types/index.js +0 -14
- package/dist/types/index.js.map +1 -1
- package/dist/utilities/functions.d.ts +1 -1
- package/dist/utilities/index.cjs +982 -0
- package/dist/utilities/index.cjs.map +1 -0
- package/dist/utilities/index.js +590 -321
- package/dist/utilities/index.js.map +1 -1
- package/dist/utilities/logger.d.ts +48 -0
- package/package.json +152 -98
- package/dist/collections/Changes.test.d.ts +0 -1
- package/dist/collections/MemoryDataCollection.test.d.ts +0 -1
- package/dist/collections/TagCollection.test.d.ts +0 -1
- package/dist/expressions/parser.test.d.ts +0 -1
- package/dist/expressions/utils.test.d.ts +0 -1
- package/dist/pipeline/SyncronousQueue.test.d.ts +0 -1
- package/dist/pipeline/WorkPipeline.test.d.ts +0 -1
- package/dist/plugins/EphemeralDataPlugin.test.d.ts +0 -1
- package/dist/plugins/query/QueryOptionsCollection.test.d.ts +0 -1
- package/dist/plugins/replication/OptimisticReplicationDbPlugin.d.ts +0 -23
- package/dist/plugins/replication/ReplicationDbPlugin.d.ts +0 -22
- package/dist/plugins/replication/index.d.ts +0 -3
- package/dist/plugins/replication/types.d.ts +0 -5
- package/dist/plugins/translators/JsonTranslator.test.d.ts +0 -1
- package/dist/schema/PropertyInfo.test.d.ts +0 -1
- package/dist/schema/schemaGeneration.test.d.ts +0 -1
- package/dist/schema/testSchemas.test.d.ts +0 -314
- package/dist/utilities/arrays.test.d.ts +0 -1
- package/dist/utilities/dates.test.d.ts +0 -1
- package/dist/utilities/strings.test.d.ts +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pipeline/index.js","sources":["webpack://@routier/core/./src/pipeline/SyncronousQueue.ts","webpack://@routier/core/./src/pipeline/TrampolinePipeline.ts","webpack://@routier/core/./src/results/Result.ts","webpack://@routier/core/./src/utilities/logger.ts","webpack://@routier/core/webpack/runtime/define_property_getters","webpack://@routier/core/webpack/runtime/has_own_property","webpack://@routier/core/webpack/runtime/make_namespace_object","webpack://@routier/core/./src/pipeline/index.ts"],"sourcesContent":["export type SyncronousUnitOfWork = (done: () => void) => void\n\nexport class SyncronousQueue {\n\n private readonly _queue: SyncronousUnitOfWork[] = [];\n private _current: SyncronousUnitOfWork | null = null;\n\n enqueue(unitOfWork: SyncronousUnitOfWork) {\n this._queue.push(unitOfWork);\n this._next();\n }\n\n private _next() {\n if (this._current != null || this._queue.length === 0) {\n return;\n }\n\n // Take first item\n this._current = this._queue.shift() ?? null;\n\n if (this._current == null) {\n return;\n }\n\n this._current(() => {\n this._current = null;\n this._next();\n })\n }\n}","import { logger } from \"../utilities\";\nimport { CallbackResult, Result, ResultType } from \"../results\";\n\n/**\n * Type definition for an asynchronous function that takes data and a callback.\n * TIn: The input data type.\n * TOut: The output data type (passed to the callback).\n */\nexport type Processor<TIn, TOut> = (data: TIn, callback: (result: TOut, error?: any) => void) => void;\n\n// Return type for a step execution: Either the next step function or null if waiting/done.\ntype StepResult<TData> = TrampolineStep<TData> | null;\ntype TrampolineStep<TData> = () => StepResult<TData>;\n\nexport class TrampolinePipeline<TInitial, TCurrent = TInitial> {\n private _list: Processor<any, any>[] = [];\n private _hasErrored: boolean = false; // Flag to prevent calling done on error\n\n filter<TFinal>(initialData: TInitial, done: (data: TFinal, error?: any) => void) {\n\n this._hasErrored = false; // Reset error flag on new execution\n\n if (this._list.length === 0) {\n done(initialData as unknown as TFinal);\n return;\n }\n\n let index = 0;\n let currentData: any = initialData;\n let isRunning = false; // Guard against overlapping trampoline calls\n\n try {\n // --- Revised Completion Logic --- (Moved up for clarity)\n const finalStepSentinel = (): StepResult<any> => { // A special step function for the very end\n // Only call done if no error has occurred\n if (!this._hasErrored) {\n done(currentData as TFinal);\n }\n return null; // Stop the trampoline\n };\n\n const createStepRevised = (idx: number): TrampolineStep<any> => {\n return () => {\n if (this._hasErrored) return null; // Stop if an error occurred elsewhere\n\n if (idx >= this._list.length) {\n return finalStepSentinel(); // Execute the dedicated final step\n }\n\n const processor = this._list[idx];\n // Initialize syncCallbackResult to null to satisfy StepResult type\n let syncCallbackResult: StepResult<any> = null;\n let calledSync = false;\n\n try {\n processor(currentData, (result, error) => {\n // --- Error Handling ---\n if (error) {\n logger.error(`Error reported by processor at index ${idx}:`, error);\n this._hasErrored = true; // Set flag\n // Throw the error to be caught by outer try...catch blocks\n throw error;\n }\n // --- /Error Handling ---\n\n // If no error, proceed as before\n currentData = result;\n index = idx + 1; // Update index for the next step\n const nextStep = createStepRevised(index); // Use updated index\n\n if (isRunning) {\n // Callback was synchronous\n syncCallbackResult = nextStep; // Store next step function\n calledSync = true;\n } else {\n // Callback was asynchronous, restart trampoline\n trampoline(nextStep);\n }\n });\n } catch (error) {\n if (!this._hasErrored) { // Check flag to avoid double logging if error was from callback\n logger.error(`Error thrown by processor at index ${idx} or its callback:`, error);\n this._hasErrored = true;\n }\n // Rethrow to be caught by the trampoline's catch block\n throw error;\n }\n\n if (calledSync) {\n // Return the next step function for the sync loop\n return syncCallbackResult;\n } else {\n // Pause trampoline for async, loop will stop as step returns null\n return null;\n }\n };\n };\n\n // The trampoline loop\n const trampoline = (step: TrampolineStep<any> | null) => {\n\n if (isRunning) {\n return;\n }\n\n isRunning = true;\n let currentStep = step;\n\n while (currentStep !== null) {\n try {\n // Stop immediately if an error was flagged elsewhere\n if (this._hasErrored) {\n currentStep = null;\n break;\n }\n currentStep = currentStep(); // Execute step, get next step or null\n } catch (trampolineError) {\n // Catch errors propagated from step execution (processor or callback errors)\n if (!this._hasErrored) { // Avoid double logging\n logger.error(\"Error during trampoline step execution:\", trampolineError);\n this._hasErrored = true;\n }\n currentStep = null; // Stop the loop\n // We don't call `done` here because an error occurred.\n // The application should handle the uncaught exception if desired.\n break; // Explicitly break loop on error\n }\n }\n // Loop ends when currentStep is null or loop is broken by error\n isRunning = false;\n\n // Completion check is now handled by finalStepSentinel ensuring `done` isn't called on error.\n };\n\n // --- Start the process ---\n index = 0; // Reset index\n currentData = initialData; // Reset data\n trampoline(createStepRevised(0)); // Start with the revised step creator\n } catch (error: any) {\n done(currentData, error);\n }\n }\n\n pipe<TNext>(processor: Processor<TCurrent, TNext>) {\n this._list.push(processor);\n return this as unknown as TrampolinePipeline<TInitial, TNext>;\n }\n\n pipeEach(items: TCurrent[], fn: (payload: ResultType<TCurrent>, done: CallbackResult<TCurrent>) => void, map: (previous: ResultType<TCurrent>, current: ResultType<TCurrent>) => ResultType<TCurrent>) {\n for (let i = 0, length = items.length; i < length; i++) {\n\n this.pipe<ResultType<TCurrent>>((previous, done) => {\n\n fn(map(previous as ResultType<TCurrent>, Result.success(items[i])), done);\n });\n }\n }\n}\n\nexport type UnitOfWork = (done: CallbackResult<never>) => void;\n\n/**\n * Processes functions with callbacks asynchronously.\n * \n * This pipeline handles work items that contain callback functions,\n * executing them in an asynchronous manner while maintaining proper\n * flow control and error handling.\n */\nexport class WorkPipeline {\n private unitsOfWork: UnitOfWork[] = [];\n private _hasErrored: boolean = false; // Flag to prevent calling done on error\n\n filter(done: CallbackResult<never>) {\n\n this._hasErrored = false; // Reset error flag on new execution\n\n if (this.unitsOfWork.length === 0) {\n done(Result.success());\n return;\n }\n\n let index = 0;\n let isRunning = false; // Guard against overlapping trampoline calls\n\n try {\n // --- Revised Completion Logic --- (Moved up for clarity)\n const finalStepSentinel = (): StepResult<never> => { // A special step function for the very end\n // Only call done if no error has occurred\n if (!this._hasErrored) {\n done(Result.success());\n }\n return null; // Stop the trampoline\n };\n\n const createStepRevised = (idx: number): TrampolineStep<any> => {\n return () => {\n if (this._hasErrored) return null; // Stop if an error occurred elsewhere\n\n if (idx >= this.unitsOfWork.length) {\n return finalStepSentinel(); // Execute the dedicated final step\n }\n\n const processor = this.unitsOfWork[idx];\n // Initialize syncCallbackResult to null to satisfy StepResult type\n let syncCallbackResult: StepResult<any> = null;\n let calledSync = false;\n\n try {\n processor((result) => {\n // --- Error Handling ---\n if (result.ok === Result.ERROR) {\n logger.error(`Error reported by AsyncPipeline at index ${idx}:`, result.error);\n this._hasErrored = true; // Set flag\n // Throw the error to be caught by outer try...catch blocks\n throw result.error;\n }\n // --- /Error Handling ---\n\n // If no error, proceed as before\n index = idx + 1; // Update index for the next step\n const nextStep = createStepRevised(index); // Use updated index\n\n if (isRunning) {\n // Callback was synchronous\n syncCallbackResult = nextStep; // Store next step function\n calledSync = true;\n } else {\n // Callback was asynchronous, restart trampoline\n trampoline(nextStep);\n }\n });\n } catch (error) {\n if (!this._hasErrored) { // Check flag to avoid double logging if error was from callback\n logger.error(`Error thrown by processor at index ${idx} or its callback:`, error);\n this._hasErrored = true;\n }\n // Rethrow to be caught by the trampoline's catch block\n throw error;\n }\n\n if (calledSync) {\n // Return the next step function for the sync loop\n return syncCallbackResult;\n } else {\n // Pause trampoline for async, loop will stop as step returns null\n return null;\n }\n };\n };\n\n // The trampoline loop\n const trampoline = (step: TrampolineStep<any> | null) => {\n\n if (isRunning) {\n return;\n }\n\n isRunning = true;\n let currentStep = step;\n\n while (currentStep !== null) {\n try {\n // Stop immediately if an error was flagged elsewhere\n if (this._hasErrored) {\n currentStep = null;\n break;\n }\n currentStep = currentStep(); // Execute step, get next step or null\n } catch (trampolineError) {\n // Catch errors propagated from step execution (processor or callback errors)\n if (!this._hasErrored) { // Avoid double logging\n logger.error(\"Error during trampoline step execution:\", trampolineError);\n this._hasErrored = true;\n }\n currentStep = null; // Stop the loop\n // We don't call `done` here because an error occurred.\n // The application should handle the uncaught exception if desired.\n break; // Explicitly break loop on error\n }\n }\n // Loop ends when currentStep is null or loop is broken by error\n isRunning = false;\n\n // Completion check is now handled by finalStepSentinel ensuring `done` isn't called on error.\n };\n\n // --- Start the process ---\n index = 0; // Reset index\n trampoline(createStepRevised(0)); // Start with the revised step creator\n } catch (error: any) {\n done(Result.error(error));\n }\n }\n\n pipe(work: UnitOfWork) {\n this.unitsOfWork.push(work);\n }\n}","import { PartialResultType, PluginEventPartialResultType, PluginEventResultType, ResultType } from \"./types\";\n\nabstract class BaseResult {\n static ERROR = \"error\" as const;\n static SUCCESS = \"success\" as const;\n static PARTIAL = \"partial\" as const;\n\n static resolve<T>(result: ResultType<T> | PartialResultType<T>, resolve: (data: T) => void, reject: (error?: any) => void) {\n if (result.ok === BaseResult.SUCCESS) {\n resolve(result.data);\n return;\n }\n\n if (result.ok === BaseResult.PARTIAL) {\n reject({\n partial: result.data,\n error: result.error\n });\n return;\n }\n\n reject(result.error);\n }\n\n static assertSuccess<T>(result: any): asserts result is { ok: \"success\"; data: T } {\n if (result.ok !== BaseResult.SUCCESS) {\n throw new Error(`Expected success result, but got ${result.ok}: ${result.error}`);\n }\n }\n}\n\nexport class Result extends BaseResult {\n static success<T>(data: T): ResultType<T>;\n static success<T>(): ResultType<never>;\n static success<T>(data?: T): ResultType<T> {\n return {\n ok: Result.SUCCESS,\n data\n }\n }\n\n static error<T>(error: any): ResultType<T> {\n return {\n ok: Result.ERROR,\n error\n }\n }\n\n static partial<T>(data: T, error: any): PartialResultType<T> {\n return {\n ok: Result.PARTIAL,\n data,\n error\n }\n }\n}\n\nexport class PluginEventResult extends BaseResult {\n static success<T>(id: string, data: T): PluginEventResultType<T>;\n static success<T>(id: string): PluginEventResultType<never>;\n static success<T>(id: string, data?: T): PluginEventResultType<T> {\n return {\n id,\n ok: Result.SUCCESS,\n data\n }\n }\n\n static error<T>(id: string, error: any): PluginEventResultType<T> {\n return {\n id,\n ok: Result.ERROR,\n error\n }\n }\n\n static partial<T>(id: string, data: T, error: any): PluginEventPartialResultType<T> {\n return {\n id,\n ok: Result.PARTIAL,\n data,\n error\n }\n }\n\n static assertSuccess<T>(result: PluginEventResultType<T>): asserts result is { ok: \"success\"; data: T; id: string } {\n if (result.ok !== Result.SUCCESS) {\n throw new Error(`Expected success result, but got ${result.ok}: ${result.error}`);\n }\n }\n}\n","const isDevelopment = (): boolean => {\n if (typeof process === 'undefined' || process.env == null) {\n return false;\n }\n const env = process.env.NODE_ENV?.toLowerCase();\n return env === 'dev' || env === 'development' || env === 'test';\n};\n\ntype LogMethods = \"log\" | \"info\" | \"warn\" | \"error\" | \"debug\" | \"table\";\nconst shouldLog = isDevelopment();\nconst tryLog = (type: LogMethods, ...args: unknown[]) => {\n if (shouldLog) {\n (console[type] as (...args: unknown[]) => void)(...args);\n }\n};\n\nexport const logger = {\n log: (...args: unknown[]): void => {\n tryLog(\"log\", ...args);\n },\n info: (...args: unknown[]): void => {\n tryLog(\"info\", ...args);\n },\n warn: (...args: unknown[]): void => {\n tryLog(\"warn\", ...args);\n },\n error: (...args: unknown[]): void => {\n tryLog(\"error\", ...args);\n },\n debug: (...args: unknown[]): void => {\n tryLog(\"debug\", ...args);\n },\n table: (...args: unknown[]): void => {\n tryLog(\"table\", ...args);\n },\n};\n\n","__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","export * from './SyncronousQueue';\nexport * from './TrampolinePipeline';"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"pipeline/index.js","sources":["webpack://@routier/core/./src/pipeline/TrampolinePipeline.ts","webpack://@routier/core/./src/results/Result.ts","webpack://@routier/core/./src/utilities/logger.ts","webpack://@routier/core/webpack/runtime/define_property_getters","webpack://@routier/core/webpack/runtime/has_own_property","webpack://@routier/core/./src/pipeline/SyncronousQueue.ts","webpack://@routier/core/./src/pipeline/index.ts"],"sourcesContent":["import { logger } from \"../utilities\";\nimport { CallbackResult, Result, ResultType } from \"../results\";\n\n/**\n * Type definition for an asynchronous function that takes data and a callback.\n * TIn: The input data type.\n * TOut: The output data type (passed to the callback).\n */\nexport type Processor<TIn, TOut> = (data: TIn, callback: (result: TOut, error?: any) => void) => void;\n\n// Return type for a step execution: Either the next step function or null if waiting/done.\ntype StepResult<TData> = TrampolineStep<TData> | null;\ntype TrampolineStep<TData> = () => StepResult<TData>;\n\nexport class TrampolinePipeline<TInitial, TCurrent = TInitial> {\n private _list: Processor<any, any>[] = [];\n private _hasErrored: boolean = false; // Flag to prevent calling done on error\n\n filter<TFinal>(initialData: TInitial, done: (data: TFinal, error?: any) => void) {\n\n this._hasErrored = false; // Reset error flag on new execution\n\n if (this._list.length === 0) {\n done(initialData as unknown as TFinal);\n return;\n }\n\n let index = 0;\n let currentData: any = initialData;\n let isRunning = false; // Guard against overlapping trampoline calls\n\n try {\n // --- Revised Completion Logic --- (Moved up for clarity)\n const finalStepSentinel = (): StepResult<any> => { // A special step function for the very end\n // Only call done if no error has occurred\n if (!this._hasErrored) {\n done(currentData as TFinal);\n }\n return null; // Stop the trampoline\n };\n\n const createStepRevised = (idx: number): TrampolineStep<any> => {\n return () => {\n if (this._hasErrored) return null; // Stop if an error occurred elsewhere\n\n if (idx >= this._list.length) {\n return finalStepSentinel(); // Execute the dedicated final step\n }\n\n const processor = this._list[idx];\n // Initialize syncCallbackResult to null to satisfy StepResult type\n let syncCallbackResult: StepResult<any> = null;\n let calledSync = false;\n\n try {\n processor(currentData, (result, error) => {\n // --- Error Handling ---\n if (error) {\n logger.error(`Error reported by processor at index ${idx}:`, error);\n this._hasErrored = true; // Set flag\n // Throw the error to be caught by outer try...catch blocks\n throw error;\n }\n // --- /Error Handling ---\n\n // If no error, proceed as before\n currentData = result;\n index = idx + 1; // Update index for the next step\n const nextStep = createStepRevised(index); // Use updated index\n\n if (isRunning) {\n // Callback was synchronous\n syncCallbackResult = nextStep; // Store next step function\n calledSync = true;\n } else {\n // Callback was asynchronous, restart trampoline\n trampoline(nextStep);\n }\n });\n } catch (error) {\n if (!this._hasErrored) { // Check flag to avoid double logging if error was from callback\n logger.error(`Error thrown by processor at index ${idx} or its callback:`, error);\n this._hasErrored = true;\n }\n // Rethrow to be caught by the trampoline's catch block\n throw error;\n }\n\n if (calledSync) {\n // Return the next step function for the sync loop\n return syncCallbackResult;\n } else {\n // Pause trampoline for async, loop will stop as step returns null\n return null;\n }\n };\n };\n\n // The trampoline loop\n const trampoline = (step: TrampolineStep<any> | null) => {\n\n if (isRunning) {\n return;\n }\n\n isRunning = true;\n let currentStep = step;\n\n while (currentStep !== null) {\n try {\n // Stop immediately if an error was flagged elsewhere\n if (this._hasErrored) {\n currentStep = null;\n break;\n }\n currentStep = currentStep(); // Execute step, get next step or null\n } catch (trampolineError) {\n // Catch errors propagated from step execution (processor or callback errors)\n if (!this._hasErrored) { // Avoid double logging\n logger.error(\"Error during trampoline step execution:\", trampolineError);\n this._hasErrored = true;\n }\n currentStep = null; // Stop the loop\n\n // Report the failure to the caller.\n //\n // This used to break without calling `done`, on the reasoning that\n // the application should handle the uncaught exception. It cannot:\n // the error is caught right here, so nothing escapes to be handled,\n // and every caller awaiting the callback waits forever. A hang is\n // strictly worse than an error — it gives the caller nothing to act\n // on and no way to time out.\n //\n // `done` runs exactly once per execution: this path returns\n // immediately, and finalStepSentinel checks `_hasErrored` before\n // reporting success, so the two are mutually exclusive.\n isRunning = false;\n done(currentData as TFinal, trampolineError);\n return;\n }\n }\n // Loop ends when currentStep is null or loop is broken by error\n isRunning = false;\n\n // Completion check is now handled by finalStepSentinel ensuring `done` isn't called on error.\n };\n\n // --- Start the process ---\n index = 0; // Reset index\n currentData = initialData; // Reset data\n trampoline(createStepRevised(0)); // Start with the revised step creator\n } catch (error: any) {\n done(currentData, error);\n }\n }\n\n pipe<TNext>(processor: Processor<TCurrent, TNext>) {\n this._list.push(processor);\n return this as unknown as TrampolinePipeline<TInitial, TNext>;\n }\n\n pipeEach(items: TCurrent[], fn: (payload: ResultType<TCurrent>, done: CallbackResult<TCurrent>) => void, map: (previous: ResultType<TCurrent>, current: ResultType<TCurrent>) => ResultType<TCurrent>) {\n for (let i = 0, length = items.length; i < length; i++) {\n\n this.pipe<ResultType<TCurrent>>((previous, done) => {\n\n fn(map(previous as ResultType<TCurrent>, Result.success(items[i])), done);\n });\n }\n }\n}\n\nexport type UnitOfWork = (done: CallbackResult<never>) => void;\n\n/**\n * Processes functions with callbacks asynchronously.\n * \n * This pipeline handles work items that contain callback functions,\n * executing them in an asynchronous manner while maintaining proper\n * flow control and error handling.\n */\nexport class WorkPipeline {\n private unitsOfWork: UnitOfWork[] = [];\n private _hasErrored: boolean = false; // Flag to prevent calling done on error\n\n filter(done: CallbackResult<never>) {\n\n this._hasErrored = false; // Reset error flag on new execution\n\n if (this.unitsOfWork.length === 0) {\n done(Result.success());\n return;\n }\n\n let index = 0;\n let isRunning = false; // Guard against overlapping trampoline calls\n\n try {\n // --- Revised Completion Logic --- (Moved up for clarity)\n const finalStepSentinel = (): StepResult<never> => { // A special step function for the very end\n // Only call done if no error has occurred\n if (!this._hasErrored) {\n done(Result.success());\n }\n return null; // Stop the trampoline\n };\n\n const createStepRevised = (idx: number): TrampolineStep<any> => {\n return () => {\n if (this._hasErrored) return null; // Stop if an error occurred elsewhere\n\n if (idx >= this.unitsOfWork.length) {\n return finalStepSentinel(); // Execute the dedicated final step\n }\n\n const processor = this.unitsOfWork[idx];\n // Initialize syncCallbackResult to null to satisfy StepResult type\n let syncCallbackResult: StepResult<any> = null;\n let calledSync = false;\n\n try {\n processor((result) => {\n // --- Error Handling ---\n if (result.ok === Result.ERROR) {\n logger.error(`Error reported by AsyncPipeline at index ${idx}:`, result.error);\n this._hasErrored = true; // Set flag\n // Throw the error to be caught by outer try...catch blocks\n throw result.error;\n }\n // --- /Error Handling ---\n\n // If no error, proceed as before\n index = idx + 1; // Update index for the next step\n const nextStep = createStepRevised(index); // Use updated index\n\n if (isRunning) {\n // Callback was synchronous\n syncCallbackResult = nextStep; // Store next step function\n calledSync = true;\n } else {\n // Callback was asynchronous, restart trampoline\n trampoline(nextStep);\n }\n });\n } catch (error) {\n if (!this._hasErrored) { // Check flag to avoid double logging if error was from callback\n logger.error(`Error thrown by processor at index ${idx} or its callback:`, error);\n this._hasErrored = true;\n }\n // Rethrow to be caught by the trampoline's catch block\n throw error;\n }\n\n if (calledSync) {\n // Return the next step function for the sync loop\n return syncCallbackResult;\n } else {\n // Pause trampoline for async, loop will stop as step returns null\n return null;\n }\n };\n };\n\n // The trampoline loop\n const trampoline = (step: TrampolineStep<any> | null) => {\n\n if (isRunning) {\n return;\n }\n\n isRunning = true;\n let currentStep = step;\n\n while (currentStep !== null) {\n try {\n // Stop immediately if an error was flagged elsewhere\n if (this._hasErrored) {\n currentStep = null;\n break;\n }\n currentStep = currentStep(); // Execute step, get next step or null\n } catch (trampolineError) {\n // Catch errors propagated from step execution (processor or callback errors)\n if (!this._hasErrored) { // Avoid double logging\n logger.error(\"Error during trampoline step execution:\", trampolineError);\n this._hasErrored = true;\n }\n currentStep = null; // Stop the loop\n\n // Report the failure to the caller. See the note on the same path in\n // TrampolinePipeline: swallowing the error here left every caller\n // awaiting `done` hanging instead of receiving a failure.\n isRunning = false;\n done(Result.error(trampolineError));\n return;\n }\n }\n // Loop ends when currentStep is null or loop is broken by error\n isRunning = false;\n\n // Completion check is now handled by finalStepSentinel ensuring `done` isn't called on error.\n };\n\n // --- Start the process ---\n index = 0; // Reset index\n trampoline(createStepRevised(0)); // Start with the revised step creator\n } catch (error: any) {\n done(Result.error(error));\n }\n }\n\n pipe(work: UnitOfWork) {\n this.unitsOfWork.push(work);\n }\n}","import { PartialResultType, PluginEventPartialResultType, PluginEventResultType, ResultType } from \"./types\";\n\nabstract class BaseResult {\n static ERROR = \"error\" as const;\n static SUCCESS = \"success\" as const;\n static PARTIAL = \"partial\" as const;\n\n static resolve<T>(result: ResultType<T> | PartialResultType<T>, resolve: (data: T) => void, reject: (error?: any) => void) {\n if (result.ok === BaseResult.SUCCESS) {\n resolve(result.data);\n return;\n }\n\n if (result.ok === BaseResult.PARTIAL) {\n reject({\n partial: result.data,\n error: result.error\n });\n return;\n }\n\n reject(result.error);\n }\n\n static assertSuccess<T>(result: any): asserts result is { ok: \"success\"; data: T } {\n if (result.ok !== BaseResult.SUCCESS) {\n throw new Error(`Expected success result, but got ${result.ok}: ${result.error}`);\n }\n }\n}\n\nexport class Result extends BaseResult {\n static success<T>(data: T): ResultType<T>;\n static success(): ResultType<never>;\n static success<T>(data?: T): ResultType<T> {\n return {\n ok: Result.SUCCESS,\n data\n }\n }\n\n static error<T>(error: any): ResultType<T> {\n return {\n ok: Result.ERROR,\n error\n }\n }\n\n static partial<T>(data: T, error: any): PartialResultType<T> {\n return {\n ok: Result.PARTIAL,\n data,\n error\n }\n }\n}\n\nexport class PluginEventResult extends BaseResult {\n static success<T>(id: string, data: T): PluginEventResultType<T>;\n static success(id: string): PluginEventResultType<never>;\n static success<T>(id: string, data?: T): PluginEventResultType<T> {\n return {\n id,\n ok: Result.SUCCESS,\n data\n }\n }\n\n static error<T>(id: string, error: any): PluginEventResultType<T> {\n return {\n id,\n ok: Result.ERROR,\n error\n }\n }\n\n static partial<T>(id: string, data: T, error: any): PluginEventPartialResultType<T> {\n return {\n id,\n ok: Result.PARTIAL,\n data,\n error\n }\n }\n\n static assertSuccess<T>(result: PluginEventResultType<T>): asserts result is { ok: \"success\"; data: T; id: string } {\n if (result.ok !== Result.SUCCESS) {\n throw new Error(`Expected success result, but got ${result.ok}: ${result.error}`);\n }\n }\n}\n","/**\n * Levelled logging, resolved once.\n *\n * Three things about the previous implementation drove this shape:\n *\n * - **There was no way to turn logging off.** `globalThis.__ROUTIER_DEBUG__` was only ever\n * compared against `true`, so setting it to `false` did nothing — while\n * `docs/how-to/debug-logging.md` documented exactly that as the way to force logging off.\n * A documented switch that silently does nothing is worse than no switch.\n * - **`NODE_ENV === 'test'` enabled it.** Every Jest run therefore logged, because Jest always\n * sets `NODE_ENV=test`. Measured on the S7 stress scenario, which drives ~2,000 saves through\n * a plugin that logs three lines per query: 12.4s with logging, ~6s without. Test runners also\n * capture console output by snapshotting a stack trace per call, so the cost is far above what\n * writing to a terminal would suggest — and the output buries whatever the failure was.\n * - **It was all-or-nothing, and re-resolved per call.** An error could not be kept while debug\n * was dropped, and every one of the ~97 call sites re-read `globalThis` and `process.env`.\n *\n * Levels are compared numerically against a value cached at module load. Measured against a\n * no-op console at 200k calls: an enabled call costs ~70ns, a call rejected by the gate ~3ns.\n * Building the arguments the call site passes in accounts for ~0.2ns of that 3ns, which is why\n * this keeps the ordinary `logger.debug(msg, payload)` signature instead of taking a thunk —\n * a lazy API would recover 0.3% of an enabled call's cost and would have to change every call\n * site to do it.\n */\n\n/** Ordered from most severe to most verbose. `silent` discards everything. */\nexport const LOG_LEVELS = ['silent', 'error', 'warn', 'info', 'debug'] as const;\n\nexport type LogLevel = (typeof LOG_LEVELS)[number];\n\n/** Numeric rank, so a gate is one integer comparison. */\nconst RANK: Record<LogLevel, number> = {\n silent: 0,\n error: 1,\n warn: 2,\n info: 3,\n debug: 4,\n};\n\nconst isLogLevel = (value: unknown): value is LogLevel =>\n typeof value === 'string' && (LOG_LEVELS as readonly string[]).includes(value);\n\n/**\n * Resolves the configured level, in precedence order.\n *\n * Ordered most specific first: an explicit level beats a boolean flag, a boolean flag beats an\n * environment variable, and an environment variable beats an inference from `NODE_ENV`. Anything\n * unrecognised is ignored rather than treated as an error — a typo'd level should not take down\n * an application, and `silent` is the safe direction to fall back to.\n */\nconst resolveLevel = (): LogLevel => {\n if (typeof globalThis !== 'undefined') {\n const g = globalThis as { __ROUTIER_LOG_LEVEL__?: unknown; __ROUTIER_DEBUG__?: unknown };\n\n if (isLogLevel(g.__ROUTIER_LOG_LEVEL__)) {\n return g.__ROUTIER_LOG_LEVEL__;\n }\n\n // Both directions honoured. `=== false` used to fall through to the NODE_ENV checks\n // below and re-enable the logging it was asked to suppress.\n if (g.__ROUTIER_DEBUG__ === true) return 'debug';\n if (g.__ROUTIER_DEBUG__ === false) return 'silent';\n }\n\n // There is deliberately no `import.meta.env` branch, although the documentation used to\n // promise one. It could never work: this package is bundled with rspack, which replaces\n // `import.meta` with `undefined`, so the check would read the *library's* build-time\n // environment rather than the application's — and referencing `import.meta` at all is a parse\n // error under a CommonJS build target, which is how the test suite loads this file. Vite and\n // similar apps set `__ROUTIER_LOG_LEVEL__` or `__ROUTIER_DEBUG__` from their own\n // `import.meta.env`, which is what the docs now describe.\n if (typeof process !== 'undefined' && process.env != null) {\n if (isLogLevel(process.env.ROUTIER_LOG_LEVEL)) {\n return process.env.ROUTIER_LOG_LEVEL as LogLevel;\n }\n\n const debug = process.env.DEBUG;\n if (debug === 'routier' || debug === '*') return 'debug';\n\n const env = process.env.NODE_ENV?.toLowerCase();\n\n // `test` is deliberately absent. It used to be here, which meant no test suite anywhere\n // could run Routier quietly. Opt in with DEBUG=routier or ROUTIER_LOG_LEVEL when a test\n // needs the output.\n if (env === 'dev' || env === 'development') return 'debug';\n }\n\n return 'silent';\n};\n\nlet level: LogLevel = resolveLevel();\nlet rank = RANK[level];\n\n/**\n * Overrides the level for the rest of the process.\n *\n * The configuration above is read once, at import, which is what makes the gate cheap — but it\n * also means an application that decides its verbosity after startup, or a test that wants to\n * assert on output, has no way in. This is that way in.\n */\nexport const setLogLevel = (next: LogLevel): void => {\n if (isLogLevel(next) === false) {\n throw new Error(`Unknown log level \"${next}\". Expected one of: ${LOG_LEVELS.join(', ')}`);\n }\n\n level = next;\n rank = RANK[next];\n};\n\nexport const getLogLevel = (): LogLevel => level;\n\n/** Re-reads the environment. For tests that change it after this module was imported. */\nexport const resetLogLevel = (): void => {\n level = resolveLevel();\n rank = RANK[level];\n};\n\n/**\n * Whether a message at this level would be emitted.\n *\n * For the rare call site whose *arguments* are expensive to build — a serialization, a deep\n * clone, a join over a large collection. An ordinary payload object is not worth guarding; see\n * the measurement in the header.\n */\nexport const isLogLevelEnabled = (at: LogLevel): boolean => rank >= RANK[at];\n\ntype ConsoleMethod = 'log' | 'info' | 'warn' | 'error' | 'debug' | 'table';\n\nconst emit = (at: LogLevel, method: ConsoleMethod, args: unknown[]) => {\n if (rank < RANK[at]) {\n return;\n }\n\n // Resolved at call time rather than captured once: test harnesses and browser devtools both\n // replace console methods after modules have loaded, and a captured reference would keep\n // writing past the replacement.\n (console[method] as (...a: unknown[]) => void)(...args);\n};\n\nexport const logger = {\n /** General-purpose output. Carried at `info`, since `log` names a console method, not a level. */\n log: (...args: unknown[]): void => emit('info', 'log', args),\n info: (...args: unknown[]): void => emit('info', 'info', args),\n warn: (...args: unknown[]): void => emit('warn', 'warn', args),\n error: (...args: unknown[]): void => emit('error', 'error', args),\n debug: (...args: unknown[]): void => emit('debug', 'debug', args),\n /** Diagnostic tabular output; verbose by nature, so it sits at `debug`. */\n table: (...args: unknown[]): void => emit('debug', 'table', args),\n};\n","__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","export type SyncronousUnitOfWork = (done: () => void) => void\n\nexport class SyncronousQueue {\n\n private readonly _queue: SyncronousUnitOfWork[] = [];\n private _current: SyncronousUnitOfWork | null = null;\n\n enqueue(unitOfWork: SyncronousUnitOfWork) {\n this._queue.push(unitOfWork);\n this._next();\n }\n\n private _next() {\n if (this._current != null || this._queue.length === 0) {\n return;\n }\n\n // Take first item\n this._current = this._queue.shift() ?? null;\n\n if (this._current == null) {\n return;\n }\n\n this._current(() => {\n this._current = null;\n this._next();\n })\n }\n}","export * from './SyncronousQueue';\nexport * from './TrampolinePipeline';"],"names":["logger","Result","TrampolinePipeline","initialData","done","index","currentData","isRunning","finalStepSentinel","createStepRevised","idx","processor","syncCallbackResult","calledSync","result","error","nextStep","trampoline","step","currentStep","trampolineError","items","fn","map","i","length","previous","WorkPipeline","work","BaseResult","resolve","reject","Error","data","PluginEventResult","id","LOG_LEVELS","RANK","isLogLevel","value","resolveLevel","globalThis","g","process","debug","env","level","rank","setLogLevel","next","getLogLevel","resetLogLevel","isLogLevelEnabled","at","emit","method","args","console","SyncronousQueue","unitOfWork"],"mappings":"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"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { BulkPersistResult } from "../collections";
|
|
2
|
+
import { PluginEventCallbackPartialResult, PluginEventCallbackResult } from "../results";
|
|
3
|
+
import { ITranslatedValue } from "./translators";
|
|
4
|
+
import { DbPluginBulkPersistEvent, DbPluginEvent, DbPluginQueryEvent, IDbPlugin } from "./types";
|
|
5
|
+
export type BatchingDbPluginOptions = {
|
|
6
|
+
/**
|
|
7
|
+
* The caller PROMISING that a failed `bulkPersist` beneath this wrapper leaves NOTHING
|
|
8
|
+
* applied. Only then may separate callers' saves be merged into one write, because the
|
|
9
|
+
* failure path re-runs a failed batch item by item — and re-running a HALF-APPLIED batch
|
|
10
|
+
* applies the landed items twice, which for adds means duplicate rows under fresh
|
|
11
|
+
* identities, with nothing raised.
|
|
12
|
+
*
|
|
13
|
+
* Omitted, the wrapper still queues and serializes; it just writes batches of one, which
|
|
14
|
+
* is what happens without it in the stack at all. True of SQLite, PostgreSQL and MySQL —
|
|
15
|
+
* one transaction, rolled back entire. Not true of anything that applies writes as it
|
|
16
|
+
* goes without a way to undo them.
|
|
17
|
+
*/
|
|
18
|
+
isAtomic?: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* The most writes one drain may take. Default 100.
|
|
21
|
+
*
|
|
22
|
+
* A drain takes everything waiting, so a burst puts an unbounded statement set in one
|
|
23
|
+
* transaction — and some engines cap that outright, D1's `batch()` in particular. Taking N
|
|
24
|
+
* per pass changes none of the reasoning: the remainder is still waiting, the next drain
|
|
25
|
+
* runs the moment this one returns, and a batch is still only what had already arrived.
|
|
26
|
+
*
|
|
27
|
+
* It also bounds the failure fallback, which costs N+1 writes for a failing batch of N.
|
|
28
|
+
*/
|
|
29
|
+
maxBatchSize?: number;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Coalesces overlapping writes into single round trips.
|
|
33
|
+
*
|
|
34
|
+
* ## Why
|
|
35
|
+
*
|
|
36
|
+
* One logical change produces more than one write: the caller's `saveChanges`, then every view
|
|
37
|
+
* reconciling in response to it. A store with three views issues four writes, none coordinated
|
|
38
|
+
* with the others. On a local file that is invisible; against a server the round trip dominates
|
|
39
|
+
* everything else a save does.
|
|
40
|
+
*
|
|
41
|
+
* ## The shape, and why it cannot cost anything
|
|
42
|
+
*
|
|
43
|
+
* A write arrives and joins the queue. If one is already in flight, the running drain will take
|
|
44
|
+
* it. Otherwise drain immediately: take everything waiting up to `maxBatchSize` and write it,
|
|
45
|
+
* then drain again for whatever arrived meanwhile and whatever the ceiling left behind.
|
|
46
|
+
*
|
|
47
|
+
* Nothing polls, nothing sleeps, nothing waits for a batch to fill. **A batch is only what had
|
|
48
|
+
* already arrived**, so when writes do not overlap the queue is empty, the batch is one item,
|
|
49
|
+
* and the write is byte for byte what happens without this wrapper. Latency cannot increase;
|
|
50
|
+
* throughput improves exactly when there is contention to improve.
|
|
51
|
+
*
|
|
52
|
+
* ## What may be merged
|
|
53
|
+
*
|
|
54
|
+
* Only items whose schemas do not overlap, and only with `isAtomic`. Two writes to one
|
|
55
|
+
* collection are genuinely ordered — a plugin applies removes, then updates, then adds WITHIN a
|
|
56
|
+
* schema, so merging an add of a row with a later update of it would run the update first,
|
|
57
|
+
* against a row that does not exist yet, and lose it silently. Items sharing a schema therefore
|
|
58
|
+
* go in separate writes, in arrival order.
|
|
59
|
+
*
|
|
60
|
+
* That same rule is what lets a merged result be split back by SCHEMA rather than by position:
|
|
61
|
+
* each schema in a merged write came from exactly one item, so no assumption about the order a
|
|
62
|
+
* plugin echoes rows in is needed anywhere.
|
|
63
|
+
*
|
|
64
|
+
* @see specs/write-batching.md
|
|
65
|
+
*/
|
|
66
|
+
export declare class BatchingDbPlugin implements IDbPlugin {
|
|
67
|
+
private readonly plugin;
|
|
68
|
+
private readonly canMerge;
|
|
69
|
+
private readonly maxBatchSize;
|
|
70
|
+
private readonly queue;
|
|
71
|
+
private isWriting;
|
|
72
|
+
private isDestroyed;
|
|
73
|
+
/** Set when `destroy` arrives mid-write; run once the in-flight batch settles. */
|
|
74
|
+
private onSettled;
|
|
75
|
+
constructor(plugin: IDbPlugin, options?: BatchingDbPluginOptions);
|
|
76
|
+
get databaseName(): string;
|
|
77
|
+
/** Reads are not batched: they have no lock to contend for and no ordering to preserve. */
|
|
78
|
+
query<TRoot extends {}, TShape extends any = TRoot>(event: DbPluginQueryEvent<TRoot, TShape>, done: PluginEventCallbackResult<ITranslatedValue<TShape>>): void;
|
|
79
|
+
bulkPersist(event: DbPluginBulkPersistEvent, done: PluginEventCallbackPartialResult<BulkPersistResult>): void;
|
|
80
|
+
/**
|
|
81
|
+
* Destroys the inner plugin, answering everything this wrapper is holding first.
|
|
82
|
+
*
|
|
83
|
+
* `destroy` is destructive rather than graceful — the SQLite plugin deletes the database —
|
|
84
|
+
* so draining first would spend round trips producing state the next call destroys, and
|
|
85
|
+
* the drain loop refills, making teardown unbounded. Queued items are therefore failed
|
|
86
|
+
* rather than flushed.
|
|
87
|
+
*
|
|
88
|
+
* An in-flight write is a different matter: it has already been sent, destroying cannot
|
|
89
|
+
* un-send it, and its callers are owed the real result. So the inner `destroy` waits for it
|
|
90
|
+
* to settle — dropping a `deleteDatabase` on top of an open transaction is its own failure.
|
|
91
|
+
*/
|
|
92
|
+
destroy(event: DbPluginEvent, done: PluginEventCallbackResult<never>): void;
|
|
93
|
+
/**
|
|
94
|
+
* Takes everything waiting and writes it, then schedules itself again for whatever arrived
|
|
95
|
+
* while that ran.
|
|
96
|
+
*/
|
|
97
|
+
private drain;
|
|
98
|
+
/**
|
|
99
|
+
* Partitions the batch, APPEND-ONLY: an item joins the last group if none of its schemas is
|
|
100
|
+
* claimed there, and otherwise closes that group and starts a new one.
|
|
101
|
+
*
|
|
102
|
+
* Not first-fit. Scanning every open group for one an item fits merges more, and reorders
|
|
103
|
+
* doing it: with arrivals A(schema S), B(schema S), C(schema T), first-fit would put C in
|
|
104
|
+
* A's group and write it before the earlier-arrived B. Nothing is lost inside a table, but a
|
|
105
|
+
* caller who saved B before C could observe C land first. The last-group rule keeps arrival
|
|
106
|
+
* order unconditionally — everything in group N arrived before everything in group N+1 — and
|
|
107
|
+
* on the case that matters, several views over distinct schemas, both rules produce the same
|
|
108
|
+
* single group.
|
|
109
|
+
*/
|
|
110
|
+
private prepare;
|
|
111
|
+
/**
|
|
112
|
+
* One event standing for several. Synthetic THROUGHOUT: its own id, its operation the union
|
|
113
|
+
* of the items' operations, and its schemas the union of theirs — a plugin resolving
|
|
114
|
+
* `event.schemas.get(schemaId)` must find every schema any item brought.
|
|
115
|
+
*
|
|
116
|
+
* A single-item group passes its own event through untouched. That is most writes, so the
|
|
117
|
+
* uncontended path never reaches the merging machinery at all.
|
|
118
|
+
*/
|
|
119
|
+
private mergeEvents;
|
|
120
|
+
/** Groups go out in order, because two writes to one collection are genuinely ordered. */
|
|
121
|
+
private writeGroups;
|
|
122
|
+
/**
|
|
123
|
+
* Hands each item its own slice of a merged result, keyed by SCHEMA.
|
|
124
|
+
*
|
|
125
|
+
* Disjoint schemas are what make this possible: each schema in the write came from exactly
|
|
126
|
+
* one item, so there is no counting, no slicing by position, and no reliance on the order a
|
|
127
|
+
* plugin echoes rows in — an assumption that is load-bearing elsewhere and would be SILENT
|
|
128
|
+
* here, handing one caller another's database-assigned identities.
|
|
129
|
+
*/
|
|
130
|
+
private split;
|
|
131
|
+
/** One write per item, in arrival order — what happens with no batching at all. */
|
|
132
|
+
private writeIndividually;
|
|
133
|
+
/**
|
|
134
|
+
* A caller's callback, guarded. One caller's `done` throwing must not eat its batchmates'
|
|
135
|
+
* results, and must not strand the latch — which it would, since the continuation runs
|
|
136
|
+
* after the answers.
|
|
137
|
+
*/
|
|
138
|
+
private answer;
|
|
139
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { BulkPersistResult } from "../collections";
|
|
2
|
+
import { PluginEventCallbackPartialResult, PluginEventCallbackResult } from "../results";
|
|
3
|
+
import { ITranslatedValue } from "./translators";
|
|
4
|
+
import { DbPluginBulkPersistEvent, DbPluginEvent, DbPluginQueryEvent, IDbPlugin } from "./types";
|
|
5
|
+
/**
|
|
6
|
+
* A read-through LRU in front of a slower plugin.
|
|
7
|
+
*
|
|
8
|
+
* ## What it can and cannot promise
|
|
9
|
+
*
|
|
10
|
+
* Invalidation is the whole problem with a cache, and this one solves exactly one version of
|
|
11
|
+
* it: **writes that go through this wrapper**. A save invalidates every cached read for the
|
|
12
|
+
* schemas it touched, so a store that reads and writes through the same instance never sees
|
|
13
|
+
* its own stale data.
|
|
14
|
+
*
|
|
15
|
+
* It cannot see anything else. A write by another process, another tab, or another store over
|
|
16
|
+
* the same database leaves this cache holding rows that no longer exist, until they age out.
|
|
17
|
+
* That is not a defect to fix later — a cache in front of a plugin has no way to learn about a
|
|
18
|
+
* change it did not make — it is the condition for using this at all. Put it in front of data
|
|
19
|
+
* that is slow to fetch and tolerant of being briefly wrong, and nowhere else.
|
|
20
|
+
*
|
|
21
|
+
* Invalidation is per SCHEMA rather than per row, deliberately. Deciding whether a changed row
|
|
22
|
+
* would have matched a cached filter means evaluating every cached query against it, which is
|
|
23
|
+
* the work the cache exists to avoid, and getting it wrong keeps a stale row visible. Dropping
|
|
24
|
+
* a schema's entries is cheap and cannot be subtly wrong.
|
|
25
|
+
*
|
|
26
|
+
* ```ts
|
|
27
|
+
* const store = new MyStore(new CacheDbPlugin(new SomeDbPlugin(...), { max: 100 }));
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export type CacheDbPluginOptions = {
|
|
31
|
+
/** How many query results to keep. Default 100; the least recently used is evicted. */
|
|
32
|
+
max?: number;
|
|
33
|
+
};
|
|
34
|
+
export declare class CacheDbPlugin implements IDbPlugin {
|
|
35
|
+
private readonly plugin;
|
|
36
|
+
private readonly max;
|
|
37
|
+
/**
|
|
38
|
+
* Insertion-ordered, which is what makes this an LRU without a second structure: a hit
|
|
39
|
+
* deletes and re-sets the key to move it to the end, so the oldest is always first.
|
|
40
|
+
*/
|
|
41
|
+
private readonly entries;
|
|
42
|
+
constructor(plugin: IDbPlugin, options?: CacheDbPluginOptions);
|
|
43
|
+
get databaseName(): string;
|
|
44
|
+
/** `schemaId` first, so invalidating a schema is a prefix match. */
|
|
45
|
+
private keyFor;
|
|
46
|
+
/**
|
|
47
|
+
* A fresh translated value over a copy of the cached data.
|
|
48
|
+
*
|
|
49
|
+
* Never the cached object itself, and never its array. `TranslatedArrayValue.forEach`
|
|
50
|
+
* REASSIGNS its own slots — that is how the change tracker swaps in attached entities —
|
|
51
|
+
* so handing out the cached instance would let the first caller replace the cache's
|
|
52
|
+
* contents with its own tracked proxies. The second caller would then receive entities
|
|
53
|
+
* attached to somebody else's store.
|
|
54
|
+
*
|
|
55
|
+
* It protects a second thing that is easy to miss, so do not replace it with a shallow copy
|
|
56
|
+
* or hand out the entry directly. `ConcurrencyDbPlugin` strips `__version` from result rows
|
|
57
|
+
* IN PLACE after observing them. Stacked as `ConcurrencyDbPlugin(CacheDbPlugin(...))` the
|
|
58
|
+
* strip therefore lands on this copy, and the cached row keeps its token — without that,
|
|
59
|
+
* every hit after the first would carry no version, the observer would record nothing, and
|
|
60
|
+
* the next update would be written UNCHECKED with no error anywhere.
|
|
61
|
+
* Pinned by `datastore/src/collections/wrapperStacking.test.ts`.
|
|
62
|
+
*/
|
|
63
|
+
private rebuild;
|
|
64
|
+
query<TRoot extends {}, TShape extends any = TRoot>(event: DbPluginQueryEvent<TRoot, TShape>, done: PluginEventCallbackResult<ITranslatedValue<TShape>>): void;
|
|
65
|
+
private store;
|
|
66
|
+
/** Drops every cached read for these schemas. */
|
|
67
|
+
private invalidate;
|
|
68
|
+
bulkPersist(event: DbPluginBulkPersistEvent, done: PluginEventCallbackPartialResult<BulkPersistResult>): void;
|
|
69
|
+
destroy(event: DbPluginEvent, done: PluginEventCallbackResult<never>): void;
|
|
70
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { BulkPersistResult } from "../collections";
|
|
2
|
+
import { DbPluginBulkPersistEvent, DbPluginEvent, DbPluginQueryEvent, IDbPlugin, ITranslatedValue } from ".";
|
|
3
|
+
import { PluginEventCallbackPartialResult, PluginEventCallbackResult } from "../results";
|
|
4
|
+
/**
|
|
5
|
+
* Optimistic concurrency as a wrapper plugin — the whole opt-in is one wrap:
|
|
6
|
+
*
|
|
7
|
+
* ```ts
|
|
8
|
+
* class Bank extends DataStore {
|
|
9
|
+
* constructor() {
|
|
10
|
+
* super(new ConcurrencyDbPlugin(new SqlitePlugin('bank.db')));
|
|
11
|
+
* }
|
|
12
|
+
* }
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* Nothing is declared on the schema and nothing on the collection builder: the plugin
|
|
16
|
+
* maintains a hidden `__version` column in the SAME tables/records as the data, entirely
|
|
17
|
+
* below the entity surface. Rows start at version 1; every update is applied ONLY IF the
|
|
18
|
+
* stored version still matches what this store last read (and bumps it); a stale write
|
|
19
|
+
* rejects the save with `OptimisticConcurrencyError` naming the rows instead of silently
|
|
20
|
+
* overwriting another writer. Recovery is always: re-read, reapply, save again.
|
|
21
|
+
*
|
|
22
|
+
* ## How the hidden column exists without schema changes
|
|
23
|
+
*
|
|
24
|
+
* The wrapper hands the inner plugin an AUGMENTED VIEW of each compiled schema — the same
|
|
25
|
+
* object via prototype delegation, with one synthetic `__version` property appended to
|
|
26
|
+
* `properties`. That list is exactly what the storage plugins read to build DDL, INSERT
|
|
27
|
+
* and SELECT column lists, so the column materializes and round-trips through completely
|
|
28
|
+
* unmodified plugin code. Above the wrapper the real schema is untouched, and the
|
|
29
|
+
* datastore's generated deserialize/enrich drop undeclared fields, so `__version` never
|
|
30
|
+
* reaches an entity a caller holds.
|
|
31
|
+
*
|
|
32
|
+
* The synthetic property carries `from: '__version'` on purpose: EphemeralDataPlugin
|
|
33
|
+
* deep-copies query results with `structuredClone` (rather than the generated clone, which
|
|
34
|
+
* drops undeclared fields) when any property is renamed — which is what lets the hidden
|
|
35
|
+
* column survive reads from the in-process plugins so this wrapper can observe it.
|
|
36
|
+
*
|
|
37
|
+
* ## What this store "read"
|
|
38
|
+
*
|
|
39
|
+
* `expected` is per store instance: the version this wrapper last saw for the row, from a
|
|
40
|
+
* query result or a persist echo. A row updated WITHOUT ever being read through this store
|
|
41
|
+
* (rare — an attach of a foreign instance) has no expected value and is written unchecked,
|
|
42
|
+
* initializing its token; the row is protected from the next read on.
|
|
43
|
+
*
|
|
44
|
+
* ## Enforcement and limits
|
|
45
|
+
*
|
|
46
|
+
* The conditional check itself is performed by the INNER plugin via the
|
|
47
|
+
* `EntityUpdateInfo.concurrency` contract — memory, file-system, sqlite and postgresql
|
|
48
|
+
* enforce it (see specs/optimistic-concurrency.md for the not-yet list). Existing SQL
|
|
49
|
+
* tables created before the wrapper was adopted lack the column and need
|
|
50
|
+
* `ALTER TABLE ... ADD COLUMN "__version" <number type>` — new tables get it from the
|
|
51
|
+
* augmented DDL automatically.
|
|
52
|
+
*/
|
|
53
|
+
export declare class ConcurrencyDbPlugin implements IDbPlugin {
|
|
54
|
+
static readonly VERSION_COLUMN = "__version";
|
|
55
|
+
private readonly plugin;
|
|
56
|
+
/** Augmented schema views, one per schema — identity matters, so they are cached. */
|
|
57
|
+
private readonly augmentedSchemas;
|
|
58
|
+
/** collectionName -> rowId -> the version this store last observed for the row. */
|
|
59
|
+
private readonly observedVersions;
|
|
60
|
+
constructor(plugin: IDbPlugin);
|
|
61
|
+
get databaseName(): string;
|
|
62
|
+
query<TRoot extends {}, TShape extends any = TRoot>(event: DbPluginQueryEvent<TRoot, TShape>, done: PluginEventCallbackResult<ITranslatedValue<TShape>>): void;
|
|
63
|
+
bulkPersist(event: DbPluginBulkPersistEvent, done: PluginEventCallbackPartialResult<BulkPersistResult>): void;
|
|
64
|
+
destroy(event: DbPluginEvent, done: PluginEventCallbackResult<never>): void;
|
|
65
|
+
private versionsFor;
|
|
66
|
+
/** Extracts entity rows from a translated query result; scalars/aggregates have none. */
|
|
67
|
+
private rowsOf;
|
|
68
|
+
private captureAndStrip;
|
|
69
|
+
private forget;
|
|
70
|
+
/** A SchemaCollection whose every schema is the augmented view. */
|
|
71
|
+
private augmentSchemas;
|
|
72
|
+
/**
|
|
73
|
+
* The augmented view of a compiled schema: the same object via prototype delegation,
|
|
74
|
+
* with a synthetic `__version` property appended to `properties`. Everything else —
|
|
75
|
+
* generated functions, ids, id properties — delegates to the real schema.
|
|
76
|
+
*/
|
|
77
|
+
private augment;
|
|
78
|
+
}
|
|
@@ -4,10 +4,66 @@ import { PluginEventCallbackPartialResult, PluginEventCallbackResult } from '../
|
|
|
4
4
|
import { CompiledSchema } from '../schema';
|
|
5
5
|
import { MemoryDataCollection } from '../collections/MemoryDataCollection';
|
|
6
6
|
export declare abstract class EphemeralDataPlugin implements IDbPlugin {
|
|
7
|
-
protected
|
|
7
|
+
protected readonly _databaseName: string;
|
|
8
8
|
constructor(databaseName: string);
|
|
9
|
+
/**
|
|
10
|
+
* See `IDbPlugin.databaseName`. A getter rather than the field itself so a subclass whose
|
|
11
|
+
* database is identified by more than a name can widen it — `FileSystemPlugin` returns the
|
|
12
|
+
* resolved file path, because one name in two directories is two databases.
|
|
13
|
+
*/
|
|
14
|
+
get databaseName(): string;
|
|
9
15
|
protected abstract resolveCollection<TEntity extends {}>(schema: CompiledSchema<TEntity>): MemoryDataCollection;
|
|
16
|
+
/**
|
|
17
|
+
* All-or-nothing across every collection in the save.
|
|
18
|
+
*
|
|
19
|
+
* The naive shape — validate/apply/save one schema at a time — leaks partial saves:
|
|
20
|
+
* a conflict in the SECOND collection left the first collection's changes applied
|
|
21
|
+
* (measured in the finance stress app as one orphan ledger row per conflict). So the
|
|
22
|
+
* work is phased: every collection loads and validates BEFORE anything is applied,
|
|
23
|
+
* mutations apply with an undo log, and a failure anywhere reverts the memory state
|
|
24
|
+
* (and re-saves any files already written) so the caller sees a save that did nothing.
|
|
25
|
+
*
|
|
26
|
+
* The remaining honesty gap is crash-safety across FILES: a process dying between two
|
|
27
|
+
* file writes can leave disk partially updated. Guarding that needs a journal, which
|
|
28
|
+
* a memory-first plugin does not pretend to have.
|
|
29
|
+
*/
|
|
10
30
|
bulkPersist(event: DbPluginBulkPersistEvent, done: PluginEventCallbackPartialResult<BulkPersistResult>): void;
|
|
31
|
+
/**
|
|
32
|
+
* Loads the inner side of a join, when the query carries one.
|
|
33
|
+
*
|
|
34
|
+
* This is the whole of "interpretation 2" for the ephemeral family — memory, file-system and
|
|
35
|
+
* browser-storage all reach the inner collection the same way they reach the outer one, so
|
|
36
|
+
* the wiring lives here once rather than in each subclass. The shared hash join in
|
|
37
|
+
* `JsonTranslator.join` does everything after it.
|
|
38
|
+
*
|
|
39
|
+
* `load()` is called on the inner collection too: a durable subclass has nothing in memory
|
|
40
|
+
* until it reads, and a join that skipped it would return no pairs on the first query after
|
|
41
|
+
* a restart — an empty result, not an error.
|
|
42
|
+
*
|
|
43
|
+
* The rows are CLONED, like the outer side's are. Deserialization builds a new object for the
|
|
44
|
+
* entity and its nested objects, but an array property can come through by reference — and
|
|
45
|
+
* here that reference is the one this plugin's own storage holds, so a caller appending to a
|
|
46
|
+
* joined row's array would be editing the database. The outer path has always cloned for this
|
|
47
|
+
* reason; the inner path is the same read.
|
|
48
|
+
*
|
|
49
|
+
* @param outerKeys The distinct keys the outer rows actually hold — the semi-join prefilter.
|
|
50
|
+
* Rows whose key is not among them cannot pair with anything, so they are skipped before the
|
|
51
|
+
* clone rather than after the hash join, which is where the saving is. `null` means the outer
|
|
52
|
+
* side had more distinct keys than `semiJoinKeyThreshold`, so every inner row is taken and the
|
|
53
|
+
* join discards the surplus. Same pairs either way.
|
|
54
|
+
*/
|
|
55
|
+
private resolveJoinInnerSide;
|
|
56
|
+
/**
|
|
57
|
+
* How to copy a stored record of this schema.
|
|
58
|
+
*
|
|
59
|
+
* Stored records use the storage shape (`from` names). `schema.clone` reads IN-MEMORY property
|
|
60
|
+
* names and would silently drop renamed fields, so a schema with renames uses the cloner
|
|
61
|
+
* generated against the storage shape instead. Both are generated code; the renamed case used
|
|
62
|
+
* to fall back to `structuredClone`, which is roughly an order of magnitude slower and was paid
|
|
63
|
+
* on EVERY read of EVERY schema that renames a property.
|
|
64
|
+
*/
|
|
65
|
+
private recordCloner;
|
|
11
66
|
query<TEntity extends {}, TShape extends any = TEntity>(event: DbPluginQueryEvent<TEntity, TShape>, done: PluginEventCallbackResult<ITranslatedValue<TShape>>): void;
|
|
67
|
+
private _query;
|
|
12
68
|
abstract destroy(event: DbPluginEvent, done: PluginEventCallbackResult<never>): void;
|
|
13
69
|
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { BulkPersistResult } from "../collections";
|
|
2
|
+
import { PluginEventCallbackPartialResult, PluginEventCallbackResult } from "../results";
|
|
3
|
+
import { ITranslatedValue } from "./translators";
|
|
4
|
+
import { DbPluginBulkPersistEvent, DbPluginEvent, DbPluginQueryEvent, IDbPlugin } from "./types";
|
|
5
|
+
/**
|
|
6
|
+
* Retries a failed READ, and never a write.
|
|
7
|
+
*
|
|
8
|
+
* ## Why reads only
|
|
9
|
+
*
|
|
10
|
+
* A read is idempotent by construction: running the same query twice asks the same question
|
|
11
|
+
* and changes nothing, so a second attempt is free apart from latency. That is what makes a
|
|
12
|
+
* blanket retry safe here, and it is why this wrapper does not need to know which errors are
|
|
13
|
+
* transient — see `shouldRetry`.
|
|
14
|
+
*
|
|
15
|
+
* A save is not. `bulkPersist` is a batch of adds, updates and removes, and a failure gives no
|
|
16
|
+
* general way to know how much of it landed: a backend without atomic batches may have applied
|
|
17
|
+
* part of it, and a retry then re-applies what already succeeded. Adds are the sharpest case —
|
|
18
|
+
* an identity is assigned by the database, so a repeated INSERT does not collide, it
|
|
19
|
+
* DUPLICATES. Rows appear that no caller asked for, no error is reported, and the change
|
|
20
|
+
* tracker records a successful save.
|
|
21
|
+
*
|
|
22
|
+
* Retrying a write safely would need the write to carry an idempotency key the backend
|
|
23
|
+
* deduplicates on, which is a property of a backend rather than something a wrapper can add.
|
|
24
|
+
* So this refuses to guess, and a failed save is surfaced for the caller to decide about.
|
|
25
|
+
*
|
|
26
|
+
* ```ts
|
|
27
|
+
* const store = new MyStore(new RetryDbPlugin(new SomeDbPlugin(...), { attempts: 3 }));
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export type RetryDbPluginOptions = {
|
|
31
|
+
/**
|
|
32
|
+
* Total attempts, including the first. Default 3, minimum 1.
|
|
33
|
+
*
|
|
34
|
+
* "Attempts" rather than "retries" because off-by-one here is a real cost — the difference
|
|
35
|
+
* between three calls and four against a struggling backend.
|
|
36
|
+
*/
|
|
37
|
+
attempts?: number;
|
|
38
|
+
/**
|
|
39
|
+
* Milliseconds to wait before attempt `attempt` (2 for the first retry).
|
|
40
|
+
*
|
|
41
|
+
* Default is exponential — 50ms, 100ms, 200ms — with no jitter. Jitter matters when many
|
|
42
|
+
* clients retry in lockstep after a shared outage, and a caller with that problem knows it
|
|
43
|
+
* and can supply a delay; adding randomness by default would make tests non-deterministic
|
|
44
|
+
* for everyone else.
|
|
45
|
+
*/
|
|
46
|
+
delayMs?: (attempt: number) => number;
|
|
47
|
+
/**
|
|
48
|
+
* Whether an error is worth another attempt. Default: every error is.
|
|
49
|
+
*
|
|
50
|
+
* That default is deliberate and only defensible because this retries reads alone. A
|
|
51
|
+
* wrapper cannot know which of a backend's errors are transient without naming that
|
|
52
|
+
* backend, and guessing wrong in the cautious direction means not retrying the failure the
|
|
53
|
+
* caller installed this for. Retrying a permanent error instead costs a bounded amount of
|
|
54
|
+
* latency and nothing else.
|
|
55
|
+
*
|
|
56
|
+
* Supply this to narrow it — a syntax error in a filter will never succeed on a second try.
|
|
57
|
+
*/
|
|
58
|
+
shouldRetry?: (error: unknown, attempt: number) => boolean;
|
|
59
|
+
};
|
|
60
|
+
export declare class RetryDbPlugin implements IDbPlugin {
|
|
61
|
+
private readonly plugin;
|
|
62
|
+
private readonly attempts;
|
|
63
|
+
private readonly delayMs;
|
|
64
|
+
private readonly shouldRetry;
|
|
65
|
+
constructor(plugin: IDbPlugin, options?: RetryDbPluginOptions);
|
|
66
|
+
get databaseName(): string;
|
|
67
|
+
query<TRoot extends {}, TShape extends any = TRoot>(event: DbPluginQueryEvent<TRoot, TShape>, done: PluginEventCallbackResult<ITranslatedValue<TShape>>): void;
|
|
68
|
+
/**
|
|
69
|
+
* One attempt, scheduling the next on failure.
|
|
70
|
+
*
|
|
71
|
+
* Recursive rather than a loop because the inner plugin is callback-based: there is no
|
|
72
|
+
* promise to await, and wrapping each attempt in one would change when errors surface.
|
|
73
|
+
*/
|
|
74
|
+
private attempt;
|
|
75
|
+
bulkPersist(event: DbPluginBulkPersistEvent, done: PluginEventCallbackPartialResult<BulkPersistResult>): void;
|
|
76
|
+
destroy(event: DbPluginEvent, done: PluginEventCallbackResult<never>): void;
|
|
77
|
+
}
|