@routier/core 0.2.1 → 0.4.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.
Files changed (161) hide show
  1. package/LICENSE +21 -0
  2. package/{readme.md → README.md} +3 -3
  3. package/dist/assertions/index.cjs +191 -0
  4. package/dist/assertions/index.cjs.map +1 -0
  5. package/dist/assertions/index.js +43 -63
  6. package/dist/assertions/index.js.map +1 -1
  7. package/dist/capabilities/index.cjs +820 -0
  8. package/dist/capabilities/index.cjs.map +1 -0
  9. package/dist/capabilities/index.js +584 -558
  10. package/dist/capabilities/index.js.map +1 -1
  11. package/dist/codegen/handlers/CloneHandlerBuilder.d.ts +10 -1
  12. package/dist/codegen/handlers/clone/CloneArrayHandler.d.ts +3 -0
  13. package/dist/codegen/handlers/clone/CloneDateHandler.d.ts +13 -0
  14. package/dist/codegen/handlers/clone/CloneValueHandler.d.ts +16 -0
  15. package/dist/codegen/handlers/compare/CompareComputedHandler.d.ts +10 -0
  16. package/dist/codegen/handlers/compare/CompareFunctionHandler.d.ts +10 -0
  17. package/dist/codegen/handlers/deserialize/DeserializeArrayHandler.d.ts +14 -0
  18. package/dist/codegen/handlers/enableChangeTracking/EnableChangeTrackingArrayHandler.d.ts +11 -0
  19. package/dist/codegen/handlers/enrichment/EnrichmentArrayHandler.d.ts +11 -0
  20. package/dist/codegen/handlers/freeze/FreezeArrayHandler.d.ts +6 -0
  21. package/dist/codegen/handlers/hash/HashArrayHandler.d.ts +10 -0
  22. package/dist/codegen/handlers/hash/HashFileHandler.d.ts +24 -0
  23. package/dist/codegen/handlers/hash/HashObjectHandler.d.ts +10 -0
  24. package/dist/codegen/handlers/hash/HashTransformHandler.d.ts +23 -0
  25. package/dist/codegen/handlers/merge/MergeArrayHandler.d.ts +20 -0
  26. package/dist/codegen/handlers/merge/MergeObjectHandler.d.ts +10 -0
  27. package/dist/codegen/handlers/serialize/SerializeArrayHandler.d.ts +19 -0
  28. package/dist/codegen/handlers/strip/StripComputedHandler.d.ts +10 -0
  29. package/dist/codegen/handlers/strip/StripFunctionHandler.d.ts +9 -0
  30. package/dist/codegen/handlers/types.d.ts +55 -1
  31. package/dist/codegen/index.cjs +618 -0
  32. package/dist/codegen/index.cjs.map +1 -0
  33. package/dist/codegen/index.js +99 -127
  34. package/dist/codegen/index.js.map +1 -1
  35. package/dist/collections/MemoryDataCollection.d.ts +15 -1
  36. package/dist/collections/TagCollection.d.ts +0 -1
  37. package/dist/collections/index.cjs +702 -0
  38. package/dist/collections/index.cjs.map +1 -0
  39. package/dist/collections/index.js +282 -276
  40. package/dist/collections/index.js.map +1 -1
  41. package/dist/errors/OptimisticConcurrencyError.d.ts +18 -0
  42. package/dist/errors/PluginDestroyedError.d.ts +10 -0
  43. package/dist/errors/index.cjs +132 -0
  44. package/dist/errors/index.cjs.map +1 -0
  45. package/dist/errors/index.d.ts +2 -0
  46. package/dist/errors/index.js +61 -19
  47. package/dist/errors/index.js.map +1 -1
  48. package/dist/expressions/evaluate.d.ts +53 -0
  49. package/dist/expressions/index.cjs +2087 -0
  50. package/dist/expressions/index.cjs.map +1 -0
  51. package/dist/expressions/index.d.ts +1 -1
  52. package/dist/expressions/index.js +1727 -1082
  53. package/dist/expressions/index.js.map +1 -1
  54. package/dist/expressions/parser.d.ts +19 -0
  55. package/dist/expressions/types.d.ts +87 -2
  56. package/dist/index.cjs +14377 -0
  57. package/dist/index.cjs.map +1 -0
  58. package/dist/index.js +12374 -7931
  59. package/dist/index.js.map +1 -1
  60. package/dist/performance/index.cjs +217 -0
  61. package/dist/performance/index.cjs.map +1 -0
  62. package/dist/performance/index.js +120 -55
  63. package/dist/performance/index.js.map +1 -1
  64. package/dist/pipeline/index.cjs +576 -0
  65. package/dist/pipeline/index.cjs.map +1 -0
  66. package/dist/pipeline/index.js +223 -161
  67. package/dist/pipeline/index.js.map +1 -1
  68. package/dist/plugins/BatchingDbPlugin.d.ts +139 -0
  69. package/dist/plugins/CacheDbPlugin.d.ts +70 -0
  70. package/dist/plugins/ConcurrencyDbPlugin.d.ts +78 -0
  71. package/dist/plugins/EphemeralDataPlugin.d.ts +57 -1
  72. package/dist/plugins/RetryDbPlugin.d.ts +77 -0
  73. package/dist/plugins/index.cjs +6551 -0
  74. package/dist/plugins/index.cjs.map +1 -0
  75. package/dist/plugins/index.d.ts +5 -0
  76. package/dist/plugins/index.js +6271 -1170
  77. package/dist/plugins/index.js.map +1 -1
  78. package/dist/plugins/query/QueryOptionsCollection.d.ts +34 -0
  79. package/dist/plugins/query/explain.d.ts +82 -0
  80. package/dist/plugins/query/formatExplanation.d.ts +9 -0
  81. package/dist/plugins/query/index.d.ts +4 -0
  82. package/dist/plugins/query/join.d.ts +210 -0
  83. package/dist/plugins/query/similarity.d.ts +38 -0
  84. package/dist/plugins/query/types.d.ts +75 -1
  85. package/dist/plugins/translators/DataTranslator.d.ts +28 -0
  86. package/dist/plugins/translators/JsonTranslator.d.ts +27 -0
  87. package/dist/plugins/translators/SqlTranslator.d.ts +45 -0
  88. package/dist/plugins/translators/TupleTranslator.d.ts +45 -0
  89. package/dist/plugins/translators/index.d.ts +1 -0
  90. package/dist/plugins/types.d.ts +125 -3
  91. package/dist/plugins/wire/handler.d.ts +122 -0
  92. package/dist/plugins/wire/index.d.ts +4 -0
  93. package/dist/plugins/wire/persist.d.ts +43 -0
  94. package/dist/plugins/wire/query.d.ts +49 -0
  95. package/dist/plugins/wire/types.d.ts +139 -0
  96. package/dist/results/index.cjs +174 -0
  97. package/dist/results/index.cjs.map +1 -0
  98. package/dist/results/index.js +29 -44
  99. package/dist/results/index.js.map +1 -1
  100. package/dist/schema/PropertyInfo.d.ts +31 -2
  101. package/dist/schema/SchemaDefinition.d.ts +23 -1
  102. package/dist/schema/builder.d.ts +35 -1
  103. package/dist/schema/communication/broadcast.d.ts +17 -1
  104. package/dist/schema/index.cjs +6911 -0
  105. package/dist/schema/index.cjs.map +1 -0
  106. package/dist/schema/index.d.ts +2 -0
  107. package/dist/schema/index.js +5330 -4541
  108. package/dist/schema/index.js.map +1 -1
  109. package/dist/schema/property/base/SchemaBase.d.ts +48 -2
  110. package/dist/schema/property/modifiers/SchemaNullable.d.ts +10 -0
  111. package/dist/schema/property/modifiers/SchemaOptional.d.ts +19 -0
  112. package/dist/schema/property/modifiers/SchemaSearchable.d.ts +34 -0
  113. package/dist/schema/property/modifiers/index.d.ts +1 -0
  114. package/dist/schema/property/types/SchemaFile.d.ts +60 -0
  115. package/dist/schema/property/types/SchemaString.d.ts +21 -1
  116. package/dist/schema/property/types/SchemaVector.d.ts +61 -0
  117. package/dist/schema/property/types/index.d.ts +2 -0
  118. package/dist/schema/table/SchemaTransform.d.ts +40 -0
  119. package/dist/schema/table/index.d.ts +1 -0
  120. package/dist/schema/types.d.ts +166 -8
  121. package/dist/schema/utils/propertyKind.d.ts +4 -0
  122. package/dist/types/index.cjs +23 -0
  123. package/dist/types/index.cjs.map +1 -0
  124. package/dist/types/index.js +0 -14
  125. package/dist/types/index.js.map +1 -1
  126. package/dist/utilities/index.cjs +1013 -0
  127. package/dist/utilities/index.cjs.map +1 -0
  128. package/dist/utilities/index.js +541 -379
  129. package/dist/utilities/index.js.map +1 -1
  130. package/dist/utilities/logger.d.ts +48 -0
  131. package/package.json +156 -98
  132. package/dist/codegen/SlotPath.test.d.ts +0 -1
  133. package/dist/codegen/blocks.test.d.ts +0 -1
  134. package/dist/codegen/utils.test.d.ts +0 -1
  135. package/dist/collections/Changes.test.d.ts +0 -1
  136. package/dist/collections/IdSet.test.d.ts +0 -1
  137. package/dist/collections/MemoryDataCollection.test.d.ts +0 -1
  138. package/dist/collections/TagCollection.contract.test.d.ts +0 -1
  139. package/dist/collections/TagCollection.test.d.ts +0 -1
  140. package/dist/expressions/parser.test.d.ts +0 -1
  141. package/dist/expressions/sql.d.ts +0 -28
  142. package/dist/expressions/sql.test.d.ts +0 -1
  143. package/dist/expressions/utils.test.d.ts +0 -1
  144. package/dist/pipeline/SyncronousQueue.test.d.ts +0 -1
  145. package/dist/plugins/EphemeralDataPlugin.test.d.ts +0 -1
  146. package/dist/plugins/query/Query.test.d.ts +0 -1
  147. package/dist/plugins/query/QueryOptionsCollection.test.d.ts +0 -1
  148. package/dist/plugins/translators/JsonTranslator.test.d.ts +0 -1
  149. package/dist/plugins/translators/SqlTranslator.test.d.ts +0 -1
  150. package/dist/plugins/translators/TranslatedValues.test.d.ts +0 -1
  151. package/dist/results/Result.test.d.ts +0 -1
  152. package/dist/results/utils.test.d.ts +0 -1
  153. package/dist/schema/PropertyInfo.test.d.ts +0 -1
  154. package/dist/schema/communication/broadcast.test.d.ts +0 -1
  155. package/dist/schema/schemaGeneration.test.d.ts +0 -1
  156. package/dist/schema/utils/standardJsonSchema.test.d.ts +0 -1
  157. package/dist/utilities/arrays.test.d.ts +0 -1
  158. package/dist/utilities/dates.test.d.ts +0 -1
  159. package/dist/utilities/runtime.test.d.ts +0 -1
  160. package/dist/utilities/strings.test.d.ts +0 -1
  161. package/dist/utilities/uuid.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(): 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 * Enable logging by setting `globalThis.__ROUTIER_DEBUG__ = true` before any routier code runs.\n */\nconst shouldLog = (): boolean => {\n if (typeof globalThis !== 'undefined') {\n const g = globalThis as { __ROUTIER_DEBUG__?: boolean };\n if (g.__ROUTIER_DEBUG__ === true) return true;\n }\n\n if (typeof process !== 'undefined' && process.env != null) {\n const debug = process.env.DEBUG;\n if (debug === 'routier' || debug === '*') return true;\n const env = process.env.NODE_ENV?.toLowerCase();\n if (env === 'dev' || env === 'development' || env === 'test') return true;\n }\n\n return false;\n};\n\ntype LogMethods = \"log\" | \"info\" | \"warn\" | \"error\" | \"debug\" | \"table\";\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 databaseName: string;
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
+ }