@relax.js/core 1.0.3 → 1.0.4

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 (234) hide show
  1. package/README.md +194 -188
  2. package/dist/DependencyInjection.d.ts +42 -24
  3. package/dist/di/index.js +1 -1
  4. package/dist/di/index.js.map +3 -3
  5. package/dist/di/index.mjs +1 -1
  6. package/dist/di/index.mjs.map +3 -3
  7. package/dist/errors.d.ts +20 -0
  8. package/dist/forms/FormValidator.d.ts +1 -20
  9. package/dist/forms/ValidationRules.d.ts +2 -0
  10. package/dist/forms/index.js +1 -1
  11. package/dist/forms/index.js.map +4 -4
  12. package/dist/forms/index.mjs +1 -1
  13. package/dist/forms/index.mjs.map +4 -4
  14. package/dist/html/TableRenderer.d.ts +1 -0
  15. package/dist/html/index.js.map +2 -2
  16. package/dist/html/index.mjs.map +2 -2
  17. package/dist/html/template.d.ts +4 -0
  18. package/dist/http/http.d.ts +1 -0
  19. package/dist/http/index.js.map +2 -2
  20. package/dist/http/index.mjs.map +2 -2
  21. package/dist/index.js +3 -3
  22. package/dist/index.js.map +3 -3
  23. package/dist/index.mjs +3 -3
  24. package/dist/index.mjs.map +3 -3
  25. package/dist/routing/index.js +3 -3
  26. package/dist/routing/index.js.map +3 -3
  27. package/dist/routing/index.mjs +3 -3
  28. package/dist/routing/index.mjs.map +3 -3
  29. package/dist/routing/routeTargetRegistry.d.ts +1 -0
  30. package/dist/routing/types.d.ts +2 -1
  31. package/dist/templates/NodeTemplate.d.ts +2 -0
  32. package/dist/utils/index.js +1 -1
  33. package/dist/utils/index.js.map +2 -2
  34. package/dist/utils/index.mjs +1 -1
  35. package/dist/utils/index.mjs.map +2 -2
  36. package/docs/Architecture.md +333 -333
  37. package/docs/DependencyInjection.md +277 -237
  38. package/docs/Errors.md +87 -87
  39. package/docs/GettingStarted.md +231 -231
  40. package/docs/Pipes.md +5 -5
  41. package/docs/Translations.md +167 -312
  42. package/docs/WhyRelaxjs.md +336 -336
  43. package/docs/api/.nojekyll +1 -0
  44. package/docs/api/assets/hierarchy.js +1 -0
  45. package/docs/api/assets/highlight.css +120 -0
  46. package/docs/api/assets/icons.js +18 -0
  47. package/docs/api/assets/icons.svg +1 -0
  48. package/docs/api/assets/main.js +60 -0
  49. package/docs/api/assets/navigation.js +1 -0
  50. package/docs/api/assets/search.js +1 -0
  51. package/docs/api/assets/style.css +1633 -0
  52. package/docs/api/classes/http.WebSocketClient.html +26 -0
  53. package/docs/api/classes/i18n.LocaleChangeEvent.html +66 -0
  54. package/docs/api/classes/index.Blueprint.html +3 -0
  55. package/docs/api/classes/index.BoundNode.html +3 -0
  56. package/docs/api/classes/index.DigitsValidation.html +10 -0
  57. package/docs/api/classes/index.FormValidator.html +32 -0
  58. package/docs/api/classes/index.HttpError.html +13 -0
  59. package/docs/api/classes/index.LinkedList.html +26 -0
  60. package/docs/api/classes/index.NavigateRouteEvent.html +76 -0
  61. package/docs/api/classes/index.Node.html +15 -0
  62. package/docs/api/classes/index.PageSelectedEvent.html +61 -0
  63. package/docs/api/classes/index.Pager.html +4 -0
  64. package/docs/api/classes/index.RangeValidation.html +15 -0
  65. package/docs/api/classes/index.RelaxError.html +17 -0
  66. package/docs/api/classes/index.RequiredValidation.html +10 -0
  67. package/docs/api/classes/index.RouteError.html +11 -0
  68. package/docs/api/classes/index.RouteGuardError.html +12 -0
  69. package/docs/api/classes/index.RouteLink.html +779 -0
  70. package/docs/api/classes/index.RouteTarget.html +788 -0
  71. package/docs/api/classes/index.SSEClient.html +13 -0
  72. package/docs/api/classes/index.SSEDataEvent.html +63 -0
  73. package/docs/api/classes/index.ServiceCollection.html +28 -0
  74. package/docs/api/classes/index.ServiceContainer.html +24 -0
  75. package/docs/api/classes/index.SortChangeEvent.html +61 -0
  76. package/docs/api/classes/index.TableRenderer.html +5 -0
  77. package/docs/api/classes/index.TableSorter.html +4 -0
  78. package/docs/api/enums/index.GuardResult.html +9 -0
  79. package/docs/api/functions/elements.formError.html +6 -0
  80. package/docs/api/functions/elements.selectOne.html +6 -0
  81. package/docs/api/functions/i18n.getCurrentLocale.html +3 -0
  82. package/docs/api/functions/i18n.loadNamespace.html +7 -0
  83. package/docs/api/functions/i18n.loadNamespaces.html +6 -0
  84. package/docs/api/functions/i18n.onMissingTranslation.html +7 -0
  85. package/docs/api/functions/i18n.setLocale.html +7 -0
  86. package/docs/api/functions/i18n.setMessageFormatter.html +7 -0
  87. package/docs/api/functions/i18n.t.html +9 -0
  88. package/docs/api/functions/index.BooleanConverter.html +6 -0
  89. package/docs/api/functions/index.ContainerService.html +13 -0
  90. package/docs/api/functions/index.DateConverter.html +11 -0
  91. package/docs/api/functions/index.Inject.html +16 -0
  92. package/docs/api/functions/index.NumberConverter.html +5 -0
  93. package/docs/api/functions/index.RegisterValidator.html +7 -0
  94. package/docs/api/functions/index.applyPipes.html +17 -0
  95. package/docs/api/functions/index.asyncHandler.html +11 -0
  96. package/docs/api/functions/index.capitalizePipe.html +4 -0
  97. package/docs/api/functions/index.clearPendingNavigations.html +1 -0
  98. package/docs/api/functions/index.compileTemplate.html +26 -0
  99. package/docs/api/functions/index.configure.html +5 -0
  100. package/docs/api/functions/index.createBluePrint.html +1 -0
  101. package/docs/api/functions/index.createConverterFromDataType.html +4 -0
  102. package/docs/api/functions/index.createConverterFromInputType.html +5 -0
  103. package/docs/api/functions/index.createPipeRegistry.html +12 -0
  104. package/docs/api/functions/index.currencyPipe.html +9 -0
  105. package/docs/api/functions/index.datePipe.html +9 -0
  106. package/docs/api/functions/index.daysAgoPipe.html +8 -0
  107. package/docs/api/functions/index.defaultPipe.html +5 -0
  108. package/docs/api/functions/index.defineRoutes.html +8 -0
  109. package/docs/api/functions/index.del.html +8 -0
  110. package/docs/api/functions/index.findRouteByName.html +5 -0
  111. package/docs/api/functions/index.findRouteByUrl.html +4 -0
  112. package/docs/api/functions/index.firstPipe.html +4 -0
  113. package/docs/api/functions/index.generateSequentialId.html +21 -0
  114. package/docs/api/functions/index.get.html +9 -0
  115. package/docs/api/functions/index.getDataConverter.html +11 -0
  116. package/docs/api/functions/index.getParentComponent.html +18 -0
  117. package/docs/api/functions/index.getValidator.html +4 -0
  118. package/docs/api/functions/index.html.html +19 -0
  119. package/docs/api/functions/index.joinPipe.html +5 -0
  120. package/docs/api/functions/index.keysPipe.html +4 -0
  121. package/docs/api/functions/index.lastPipe.html +4 -0
  122. package/docs/api/functions/index.lowercasePipe.html +4 -0
  123. package/docs/api/functions/index.mapFormToClass.html +17 -0
  124. package/docs/api/functions/index.matchRoute.html +5 -0
  125. package/docs/api/functions/index.navigate.html +8 -0
  126. package/docs/api/functions/index.onError.html +8 -0
  127. package/docs/api/functions/index.piecesPipe.html +8 -0
  128. package/docs/api/functions/index.post.html +9 -0
  129. package/docs/api/functions/index.printRoutes.html +2 -0
  130. package/docs/api/functions/index.put.html +9 -0
  131. package/docs/api/functions/index.readData.html +17 -0
  132. package/docs/api/functions/index.registerRouteTarget.html +9 -0
  133. package/docs/api/functions/index.reportError.html +10 -0
  134. package/docs/api/functions/index.request.html +8 -0
  135. package/docs/api/functions/index.resolveValue.html +18 -0
  136. package/docs/api/functions/index.setFetch.html +6 -0
  137. package/docs/api/functions/index.setFormData.html +17 -0
  138. package/docs/api/functions/index.shortenPipe.html +5 -0
  139. package/docs/api/functions/index.startRouting.html +6 -0
  140. package/docs/api/functions/index.ternaryPipe.html +6 -0
  141. package/docs/api/functions/index.trimPipe.html +4 -0
  142. package/docs/api/functions/index.unregisterRouteTarget.html +3 -0
  143. package/docs/api/functions/index.uppercasePipe.html +4 -0
  144. package/docs/api/hierarchy.html +1 -0
  145. package/docs/api/index.html +323 -0
  146. package/docs/api/interfaces/http.SimpleDataEvent.html +3 -0
  147. package/docs/api/interfaces/http.WebSocketAbstraction.html +9 -0
  148. package/docs/api/interfaces/http.WebSocketCodec.html +4 -0
  149. package/docs/api/interfaces/http.WebSocketOptions.html +20 -0
  150. package/docs/api/interfaces/index.CompiledTemplate.html +10 -0
  151. package/docs/api/interfaces/index.DataLoader.html +19 -0
  152. package/docs/api/interfaces/index.EngineConfig.html +11 -0
  153. package/docs/api/interfaces/index.ErrorContext.html +4 -0
  154. package/docs/api/interfaces/index.FormReaderOptions.html +8 -0
  155. package/docs/api/interfaces/index.HttpOptions.html +16 -0
  156. package/docs/api/interfaces/index.HttpResponse.html +17 -0
  157. package/docs/api/interfaces/index.LoadRoute.html +7 -0
  158. package/docs/api/interfaces/index.NavigateOptions.html +7 -0
  159. package/docs/api/interfaces/index.PipeRegistry.html +12 -0
  160. package/docs/api/interfaces/index.RegistrationOptions.html +22 -0
  161. package/docs/api/interfaces/index.RenderTemplate.html +7 -0
  162. package/docs/api/interfaces/index.RequestOptions.html +11 -0
  163. package/docs/api/interfaces/index.Routable.html +10 -0
  164. package/docs/api/interfaces/index.Route.html +13 -0
  165. package/docs/api/interfaces/index.RouteGuard.html +2 -0
  166. package/docs/api/interfaces/index.RouteValue.html +6 -0
  167. package/docs/api/interfaces/index.SSEOptions.html +24 -0
  168. package/docs/api/interfaces/index.ValidationContext.html +8 -0
  169. package/docs/api/interfaces/index.ValidatorOptions.html +14 -0
  170. package/docs/api/media/Architecture.md +333 -0
  171. package/docs/api/media/DependencyInjection.md +277 -0
  172. package/docs/api/media/GettingStarted.md +231 -0
  173. package/docs/api/media/HttpClient.md +459 -0
  174. package/docs/api/media/Pipes.md +211 -0
  175. package/docs/api/media/Routing.md +332 -0
  176. package/docs/api/media/WhyRelaxjs.md +336 -0
  177. package/docs/api/media/forms.md +99 -0
  178. package/docs/api/media/html.md +175 -0
  179. package/docs/api/media/i18n.md +354 -0
  180. package/docs/api/media/utilities.md +143 -0
  181. package/docs/api/media/validation.md +351 -0
  182. package/docs/api/modules/collections_Index.html +1 -0
  183. package/docs/api/modules/di.html +1 -0
  184. package/docs/api/modules/elements.html +1 -0
  185. package/docs/api/modules/forms.html +1 -0
  186. package/docs/api/modules/html.html +1 -0
  187. package/docs/api/modules/http.html +1 -0
  188. package/docs/api/modules/i18n.html +1 -0
  189. package/docs/api/modules/index.html +1 -0
  190. package/docs/api/modules/routing.html +1 -0
  191. package/docs/api/modules/utils.html +1 -0
  192. package/docs/api/modules.html +1 -0
  193. package/docs/api/types/http.WebSocketFactory.html +2 -0
  194. package/docs/api/types/i18n.MessageFormatter.html +3 -0
  195. package/docs/api/types/i18n.MissingTranslationHandler.html +1 -0
  196. package/docs/api/types/index.Constructor.html +7 -0
  197. package/docs/api/types/index.ConverterFunc.html +2 -0
  198. package/docs/api/types/index.DataType.html +2 -0
  199. package/docs/api/types/index.InputType.html +2 -0
  200. package/docs/api/types/index.PipeFunction.html +6 -0
  201. package/docs/api/types/index.RouteData.html +1 -0
  202. package/docs/api/types/index.RouteMatchResult.html +9 -0
  203. package/docs/api/types/index.RouteParamType.html +1 -0
  204. package/docs/api/types/index.RouteSegmentType.html +2 -0
  205. package/docs/api/types/index.SSEEventFactory.html +5 -0
  206. package/docs/api/types/index.ServiceScope.html +10 -0
  207. package/docs/api/types/index.SortColumn.html +3 -0
  208. package/docs/api/variables/i18n.formatICU.html +3 -0
  209. package/docs/api/variables/index.container.html +6 -0
  210. package/docs/api/variables/index.defaultPipes.html +6 -0
  211. package/docs/api/variables/index.internalRoutes.html +1 -0
  212. package/docs/api/variables/index.serviceCollection.html +6 -0
  213. package/docs/api.json +93171 -0
  214. package/docs/elements/dom.md +102 -102
  215. package/docs/forms/creating-form-components.md +924 -924
  216. package/docs/forms/form-api.md +94 -94
  217. package/docs/forms/forms.md +99 -99
  218. package/docs/forms/patterns.md +311 -311
  219. package/docs/forms/reading-writing.md +365 -365
  220. package/docs/forms/validation.md +351 -351
  221. package/docs/html/TableRenderer.md +291 -291
  222. package/docs/html/html.md +175 -175
  223. package/docs/html/index.md +54 -54
  224. package/docs/html/template.md +422 -422
  225. package/docs/http/HttpClient.md +459 -459
  226. package/docs/http/ServerSentEvents.md +184 -184
  227. package/docs/http/index.md +109 -109
  228. package/docs/i18n/i18n.md +49 -4
  229. package/docs/i18n/intl-standard.md +178 -178
  230. package/docs/routing/RouteLink.md +98 -98
  231. package/docs/routing/Routing.md +332 -332
  232. package/docs/routing/layouts.md +207 -207
  233. package/docs/utilities.md +143 -143
  234. package/package.json +4 -3
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/di/index.ts", "../../src/errors.ts", "../../src/DependencyInjection.ts"],
4
- "sourcesContent": ["export * from '../DependencyInjection';\r\n", "/**\r\n * Global error handling for Relaxjs.\r\n * Register a handler with `onError()` to intercept errors before they throw.\r\n * Call `ctx.suppress()` in the handler to prevent the error from being thrown.\r\n *\r\n * @example\r\n * import { onError } from 'relaxjs';\r\n *\r\n * onError((error, ctx) => {\r\n * logToService(error.message, error.context);\r\n * showToast(error.message);\r\n * ctx.suppress();\r\n * });\r\n */\r\n\r\n/**\r\n * Passed to error handlers to control error behavior.\r\n * Call `suppress()` to prevent the error from being thrown.\r\n */\r\nexport interface ErrorContext {\r\n suppress(): void;\r\n}\r\n\r\n/**\r\n * Error with structured context for debugging.\r\n * The `context` record contains details like route name, component tag, route data.\r\n *\r\n * @example\r\n * onError((error, ctx) => {\r\n * console.log(error.context.route);\r\n * console.log(error.context.componentTagName);\r\n * });\r\n */\r\nexport class RelaxError extends Error {\r\n constructor(\r\n message: string,\r\n public context: Record<string, unknown>,\r\n ) {\r\n super(message);\r\n }\r\n}\r\n\r\ntype ErrorHandler = (error: RelaxError, ctx: ErrorContext) => void;\r\n\r\nlet handler: ErrorHandler | null = null;\r\n\r\n/**\r\n * Registers a global error handler for Relaxjs errors.\r\n * The handler receives the error and an `ErrorContext`.\r\n * Call `ctx.suppress()` to prevent the error from being thrown.\r\n * Only one handler can be active at a time; subsequent calls replace the previous handler.\r\n *\r\n * @example\r\n * onError((error, ctx) => {\r\n * if (error.context.route === 'optional-panel') {\r\n * ctx.suppress();\r\n * return;\r\n * }\r\n * showErrorDialog(error.message);\r\n * });\r\n */\r\nexport function onError(fn: ErrorHandler) {\r\n handler = fn;\r\n}\r\n\r\n/**\r\n * Reports an error through the global handler.\r\n * Returns the `RelaxError` if it should be thrown, or `null` if the handler suppressed it.\r\n * The caller is responsible for throwing the returned error.\r\n *\r\n * @param message - Human-readable error description\r\n * @param context - Structured data for debugging (route, component, params, cause, etc.)\r\n * @returns The error to throw, or `null` if suppressed\r\n *\r\n * @example\r\n * const error = reportError('Failed to load route component', {\r\n * route: 'user',\r\n * componentTagName: 'user-profile',\r\n * routeData: { id: 123 },\r\n * });\r\n * if (error) throw error;\r\n */\r\nexport function reportError(message: string, context: Record<string, unknown>): RelaxError | null {\r\n const error = new RelaxError(message, context);\r\n if (handler) {\r\n let suppressed = false;\r\n const ctx: ErrorContext = {\r\n suppress() { suppressed = true; },\r\n };\r\n handler(error, ctx);\r\n if (suppressed) {\r\n return null;\r\n }\r\n }\r\n return error;\r\n}\r\n", "import { reportError } from './errors';\r\n\r\n/**\r\n * Generic constructor type used for dependency registration and injection.\r\n * Represents any class constructor that can be used with the DI container.\r\n *\r\n * @template T - The type of object the constructor creates\r\n *\r\n * @example\r\n * // Use with service registration\r\n * class UserService {}\r\n * const ctor: Constructor<UserService> = UserService;\r\n * serviceCollection.registerByType(ctor, { inject: [] });\r\n */\r\nexport type Constructor<T extends object = object> = new (...args: any[]) => T;\r\n\r\n/**\r\n * Controls how service instances are shared across the container hierarchy.\r\n * Used when registering services to define their lifetime behavior.\r\n *\r\n * - `global`: Single instance shared everywhere (singleton pattern)\r\n * - `closest`: New instance per container scope (scoped lifetime)\r\n *\r\n * @example\r\n * // Singleton service - same instance everywhere\r\n * serviceCollection.register(LoggerService, { scope: 'global', inject: [] });\r\n *\r\n * // Scoped service - new instance per scope\r\n * serviceCollection.register(RequestContext, { scope: 'closest', inject: [] });\r\n */\r\nexport type ServiceScope = 'global' | 'closest';\r\n\r\n/**\r\n * Configuration options for registering a service in the DI container.\r\n * Controls identification, lifetime, and dependency resolution.\r\n *\r\n * @example\r\n * // Register with constructor injection\r\n * const options: RegistrationOptions = {\r\n * scope: 'global',\r\n * inject: [DatabaseConnection, ConfigService]\r\n * };\r\n * serviceCollection.register(UserRepository, options);\r\n *\r\n * @example\r\n * // Register with property injection\r\n * const options: RegistrationOptions = {\r\n * inject: [],\r\n * properties: { logger: Logger, config: 'appConfig' }\r\n * };\r\n *\r\n * @example\r\n * // Register with a pre-created instance\r\n * const options: RegistrationOptions = {\r\n * inject: [],\r\n * instance: existingService\r\n * };\r\n */\r\nexport interface RegistrationOptions {\r\n /** Service lifetime - 'global' for singleton, 'closest' for scoped */\r\n scope?: ServiceScope;\r\n /** Optional string key for resolving by name instead of type */\r\n key?: string;\r\n /** Pre-existing instance to use instead of creating new one */\r\n instance?: unknown;\r\n /** Types or keys for constructor parameters, in order */\r\n inject: (string | Constructor)[];\r\n /** Map of property names to their injection types/keys */\r\n properties?: Record<string, string | Constructor>;\r\n}\r\n\r\n/**\r\n * Field decorator that collects property injection configuration.\r\n * Updates or creates the properties mapping in registration options.\r\n * \r\n * @example\r\n * @ContainerService({\r\n * inject: [Database],\r\n * properties: {\r\n * logger: Logger, // Inject by type\r\n * audit: 'auditLogger' // Inject by key\r\n * }\r\n * })\r\n * class UserService {\r\n * @Inject(Logger)\r\n * private logger!: Logger;\r\n * \r\n * @Inject('auditLogger')\r\n * private audit!: Logger;\r\n * \r\n * constructor(db: Database) {}\r\n * }\r\n */\r\nexport function Inject<T extends object>(typeOrKey: Constructor<T> | string) {\r\n return (_: undefined, context: ClassFieldDecoratorContext) => {\r\n var instance = container.resolve(typeOrKey);\r\n return function(this: any) {\r\n return instance;\r\n }\r\n };\r\n}\r\n\r\n// Temporary collector of property injections - cleared after registration\r\n//const propertyCollector = new WeakMap<object, Record<string, string>>();\r\n\r\n/**\r\n * Class decorator that automatically registers a service in the global DI container.\r\n * Use this to declaratively register services without manual registration calls.\r\n *\r\n * Services are registered at module load time, so ensure this file is imported\r\n * before attempting to resolve the decorated service.\r\n *\r\n * @param options - Registration configuration including scope and dependencies\r\n *\r\n * @example\r\n * // Simple service with constructor injection\r\n * @ContainerService({ inject: [DatabaseConnection] })\r\n * class UserRepository {\r\n * constructor(private db: DatabaseConnection) {}\r\n * }\r\n *\r\n * @example\r\n * // Service with custom key for named resolution\r\n * @ContainerService({ key: 'primaryCache', scope: 'global', inject: [] })\r\n * class CacheService {}\r\n *\r\n * // Later resolve by key\r\n * const cache = container.resolve('primaryCache');\r\n */\r\nexport function ContainerService<T extends object>(\r\n options?: RegistrationOptions\r\n) {\r\n return (target: Constructor<T>) => {\r\n const opts = options ?? {inject: []};\r\n\r\n if (opts.key) {\r\n serviceCollection.register(target, opts);\r\n } else {\r\n serviceCollection.registerByType(target, opts);\r\n }\r\n };\r\n}\r\n\r\n/**\r\n * Internal class representing a registered service's metadata.\r\n * Holds all information needed to create and configure service instances.\r\n *\r\n * @internal This is an implementation detail and should not be used directly.\r\n */\r\nclass Registration {\r\n /**\r\n * Creates a new registration record.\r\n *\r\n * @param classConstructor - The class constructor function\r\n * @param scope - Instance sharing behavior\r\n * @param inject - Constructor parameter dependencies\r\n * @param properties - Property injection mappings\r\n * @param key - Optional string identifier\r\n * @param instance - Optional pre-created instance\r\n */\r\n constructor(\r\n public classConstructor: Constructor,\r\n public scope: ServiceScope,\r\n public inject: (string | Constructor)[],\r\n public properties: Record<string, string | Constructor> = {},\r\n public key?: string,\r\n public instance?: unknown\r\n ) {}\r\n}\r\n\r\n/**\r\n * Registry that stores service registration metadata.\r\n * Use this to register services before they can be resolved by a ServiceContainer.\r\n *\r\n * Typically you'll use the global `serviceCollection` instance rather than creating your own.\r\n *\r\n * @example\r\n * // Register a service by type\r\n * serviceCollection.registerByType(LoggerService, { inject: [] });\r\n *\r\n * // Register with a string key\r\n * serviceCollection.register(CacheService, { key: 'cache', inject: [] });\r\n *\r\n * // Check if service is registered\r\n * const reg = serviceCollection.tryGet(LoggerService);\r\n * if (reg) {\r\n * console.log('Logger is registered');\r\n * }\r\n */\r\nexport class ServiceCollection {\r\n private servicesByKey = new Map<string, Registration>();\r\n private servicesByClassName = new Map<string, Registration>();\r\n\r\n /**\r\n * Registers a service with full configuration options.\r\n * The service will be resolvable by both its class name and optional key.\r\n *\r\n * @param constructor - The service class constructor\r\n * @param options - Registration configuration\r\n */\r\n register<T extends object>(constructor: Constructor<T>, options: RegistrationOptions): void {\r\n this.validateRegistration(constructor, options);\r\n\r\n const reg = new Registration(\r\n constructor,\r\n options.scope ?? 'global',\r\n options.inject,\r\n options.properties ?? {},\r\n options.key,\r\n options.instance\r\n );\r\n\r\n if (options.key) {\r\n this.servicesByKey.set(options.key, reg);\r\n }\r\n this.servicesByClassName.set(constructor.name, reg);\r\n }\r\n\r\n /**\r\n * Registers a service by its class type.\r\n * The service will be resolvable by its class constructor.\r\n *\r\n * @param constructor - The service class constructor\r\n * @param options - Optional registration configuration\r\n */\r\n registerByType<T extends object>(\r\n constructor: Constructor<T>,\r\n options?: RegistrationOptions\r\n ): void {\r\n this.checkNameCollision(constructor);\r\n if (options) this.validateRegistration(constructor, options);\r\n\r\n const reg = new Registration(constructor, options?.scope, options?.inject ?? [], options?.properties, options?.key, options?.instance);\r\n if (options?.key) {\r\n this.servicesByKey.set(options.key, reg);\r\n }\r\n this.servicesByClassName.set(constructor.name, reg);\r\n }\r\n\r\n private checkNameCollision<T extends object>(constructor: Constructor<T>): void {\r\n const existing = this.servicesByClassName.get(constructor.name);\r\n if (existing && existing.classConstructor !== constructor) {\r\n const error = reportError('Service name collision: different class registered with same name', {\r\n service: constructor.name,\r\n });\r\n if (error) throw error;\r\n }\r\n }\r\n\r\n private validateRegistration<T extends object>(constructor: Constructor<T>, options: RegistrationOptions): void {\r\n this.checkNameCollision(constructor);\r\n\r\n if (options.key) {\r\n const existingByKey = this.servicesByKey.get(options.key);\r\n if (existingByKey && existingByKey.classConstructor !== constructor) {\r\n const error = reportError('Service key already registered to a different class', {\r\n key: options.key,\r\n existingClass: existingByKey.classConstructor.name,\r\n newClass: constructor.name,\r\n });\r\n if (error) throw error;\r\n }\r\n }\r\n\r\n if (options.instance && options.inject.length > 0) {\r\n const error = reportError('Service has both instance and inject (inject will be ignored)', {\r\n service: constructor.name,\r\n });\r\n if (error) throw error;\r\n }\r\n }\r\n\r\n /**\r\n * Attempts to retrieve a service registration.\r\n * Returns undefined if the service is not registered.\r\n *\r\n * @param key - Either a string key or class constructor\r\n * @returns The registration or undefined\r\n */\r\n tryGet<T extends object>(key: string | Constructor<T>): Registration | undefined {\r\n if (typeof key === 'string') {\r\n return this.servicesByKey.get(key);\r\n }\r\n return this.servicesByClassName.get(key.name);\r\n }\r\n\r\n /**\r\n * Retrieves a service registration or throws if not found.\r\n *\r\n * @param key - Either a string key or class constructor\r\n * @returns The registration\r\n * @throws Error if the service is not registered\r\n */\r\n get<T extends object>(key: string | Constructor<T>): Registration {\r\n const reg = this.tryGet(key);\r\n if (!reg) {\r\n const service = typeof key === 'string' ? key : key.name;\r\n const error = reportError(`Failed to resolve service '${service}'`, {\r\n service,\r\n registeredTypes: Array.from(this.servicesByClassName.keys()),\r\n registeredKeys: Array.from(this.servicesByKey.keys()),\r\n });\r\n if (error) throw error;\r\n }\r\n return reg!;\r\n }\r\n}\r\n\r\n/**\r\n * Internal storage for tracking injected fields during service resolution.\r\n * @internal\r\n */\r\nconst injectedFields = new WeakMap<object, Map<string, string>>();\r\n\r\n/**\r\n * IoC container that resolves and manages service instances.\r\n * Creates instances based on registrations in a ServiceCollection,\r\n * handling constructor injection, property injection, and lifetime management.\r\n *\r\n * Typically you'll use the global `container` instance rather than creating your own.\r\n *\r\n * @example\r\n * // Resolve a service by class\r\n * const logger = container.resolve(LoggerService);\r\n *\r\n * // Resolve by string key\r\n * const cache = container.resolve<CacheService>('primaryCache');\r\n *\r\n * @example\r\n * // Full setup workflow\r\n * serviceCollection.register(UserService, {\r\n * inject: [DatabaseConnection],\r\n * scope: 'global'\r\n * });\r\n *\r\n * const userService = container.resolve(UserService);\r\n */\r\nexport class ServiceContainer {\r\n private instances = new Map<string, any>();\r\n\r\n /**\r\n * Creates a new container backed by the given service collection.\r\n *\r\n * @param serviceCollection - The registry containing service registrations\r\n */\r\n constructor(private serviceCollection: ServiceCollection) {}\r\n\r\n /**\r\n * Resolves a service instance by class type or string key.\r\n * Creates the instance if not already cached (for global scope).\r\n * Handles constructor and property injection automatically.\r\n *\r\n * @param keyOrType - Either a string key or class constructor\r\n * @returns The resolved service instance\r\n * @throws Error if the service is not registered\r\n *\r\n * @example\r\n * const service = container.resolve(MyService);\r\n */\r\n resolve<T extends object>(keyOrType: string | Constructor<T>): T {\r\n const key = typeof keyOrType === 'string' ? keyOrType : keyOrType.name;\r\n\r\n if (this.instances.has(key)) {\r\n return this.instances.get(key);\r\n }\r\n\r\n const registration = this.serviceCollection.get(keyOrType);\r\n if (!registration) {\r\n const error = reportError(`Failed to resolve service '${key}'`, { service: key });\r\n if (error) throw error;\r\n return undefined as unknown as T;\r\n }\r\n\r\n if (registration.instance) {\r\n const inst = registration.instance as T;\r\n this.injectFields(inst, registration);\r\n this.instances.set(key, inst);\r\n return inst;\r\n }\r\n\r\n const instance = this.createInstance<T>(registration);\r\n if (registration.scope === 'global') {\r\n this.instances.set(key, instance);\r\n }\r\n this.injectFields(instance, registration);\r\n\r\n return instance;\r\n }\r\n\r\n /**\r\n * Creates a new instance of a service, resolving all constructor dependencies.\r\n */\r\n private createInstance<T extends object>(registration: Registration): T {\r\n const constructor = registration.classConstructor as Constructor<T>;\r\n\r\n const dependencies = registration.inject.map(dep => this.resolve(dep));\r\n return new constructor(...dependencies);\r\n }\r\n\r\n /**\r\n * Injects dependencies into instance properties based on registration config.\r\n */\r\n private injectFields<T extends object>(instance: T, registration: Registration): void {\r\n for (const [fieldName, keyOrType] of Object.entries(registration.properties)) {\r\n (instance as any)[fieldName] = this.resolve(keyOrType);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Global service collection instance for registering services.\r\n * Use this to register services that can later be resolved by the container.\r\n *\r\n * @example\r\n * import { serviceCollection } from 'relaxjs';\r\n *\r\n * serviceCollection.register(MyService, { inject: [Dependency] });\r\n */\r\nexport const serviceCollection = new ServiceCollection();\r\n\r\n/**\r\n * Global service container instance for resolving dependencies.\r\n * Use this to obtain service instances with all dependencies injected.\r\n *\r\n * @example\r\n * import { container } from 'relaxjs';\r\n *\r\n * const service = container.resolve(MyService);\r\n */\r\nexport const container = new ServiceContainer(serviceCollection);"],
5
- "mappings": "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",
6
- "names": ["index_exports", "__export", "ContainerService", "Inject", "ServiceCollection", "ServiceContainer", "container", "serviceCollection", "__toCommonJS", "RelaxError", "message", "context", "handler", "reportError", "message", "context", "error", "RelaxError", "handler", "suppressed", "Inject", "typeOrKey", "_", "context", "instance", "container", "ContainerService", "options", "target", "opts", "serviceCollection", "Registration", "classConstructor", "scope", "inject", "properties", "key", "ServiceCollection", "constructor", "reg", "existing", "error", "reportError", "existingByKey", "service", "ServiceContainer", "serviceCollection", "keyOrType", "key", "registration", "error", "reportError", "inst", "instance", "constructor", "dependencies", "dep", "fieldName", "ServiceCollection", "container"]
4
+ "sourcesContent": ["export * from '../DependencyInjection';\r\n", "/**\r\n * Global error handling for Relaxjs.\r\n * Register a handler with `onError()` to intercept errors before they throw.\r\n * Call `ctx.suppress()` in the handler to prevent the error from being thrown.\r\n *\r\n * @example\r\n * import { onError } from 'relaxjs';\r\n *\r\n * onError((error, ctx) => {\r\n * logToService(error.message, error.context);\r\n * showToast(error.message);\r\n * ctx.suppress();\r\n * });\r\n */\r\n\r\n/**\r\n * Passed to error handlers to control error behavior.\r\n * Call `suppress()` to prevent the error from being thrown.\r\n */\r\nexport interface ErrorContext {\r\n suppress(): void;\r\n}\r\n\r\n/**\r\n * Error with structured context for debugging.\r\n * The `context` record contains details like route name, component tag, route data.\r\n *\r\n * @example\r\n * onError((error, ctx) => {\r\n * console.log(error.context.route);\r\n * console.log(error.context.componentTagName);\r\n * });\r\n */\r\nexport class RelaxError extends Error {\r\n constructor(\r\n message: string,\r\n public context: Record<string, unknown>,\r\n ) {\r\n super(message);\r\n }\r\n}\r\n\r\n/** @internal */\r\ntype ErrorHandler = (error: RelaxError, ctx: ErrorContext) => void;\r\n\r\nlet handler: ErrorHandler | null = null;\r\n\r\n/**\r\n * Registers a global error handler for Relaxjs errors.\r\n * The handler receives the error and an `ErrorContext`.\r\n * Call `ctx.suppress()` to prevent the error from being thrown.\r\n * Only one handler can be active at a time; subsequent calls replace the previous handler.\r\n *\r\n * @example\r\n * onError((error, ctx) => {\r\n * if (error.context.route === 'optional-panel') {\r\n * ctx.suppress();\r\n * return;\r\n * }\r\n * showErrorDialog(error.message);\r\n * });\r\n */\r\nexport function onError(fn: ErrorHandler) {\r\n handler = fn;\r\n}\r\n\r\n/**\r\n * Reports an error through the global handler.\r\n * Returns the `RelaxError` if it should be thrown, or `null` if the handler suppressed it.\r\n * The caller is responsible for throwing the returned error.\r\n *\r\n * @param message - Human-readable error description\r\n * @param context - Structured data for debugging (route, component, params, cause, etc.)\r\n * @returns The error to throw, or `null` if suppressed\r\n *\r\n * @example\r\n * const error = reportError('Failed to load route component', {\r\n * route: 'user',\r\n * componentTagName: 'user-profile',\r\n * routeData: { id: 123 },\r\n * });\r\n * if (error) throw error;\r\n */\r\nexport function reportError(message: string, context: Record<string, unknown>): RelaxError | null {\r\n const error = new RelaxError(message, context);\r\n if (handler) {\r\n let suppressed = false;\r\n const ctx: ErrorContext = {\r\n suppress() { suppressed = true; },\r\n };\r\n handler(error, ctx);\r\n if (suppressed) {\r\n return null;\r\n }\r\n }\r\n return error;\r\n}\r\n\r\n/**\r\n * Wraps an async function into a synchronous callback suitable for addEventListener.\r\n * Catches promise rejections and reports them through the global error handler.\r\n *\r\n * @param fn - Async function to wrap\r\n * @returns Synchronous function that can be passed to addEventListener\r\n *\r\n * @example\r\n * button.addEventListener('click', asyncHandler(async (e) => {\r\n * await saveData();\r\n * }));\r\n *\r\n * @example\r\n * form.addEventListener('submit', asyncHandler(async (e) => {\r\n * e.preventDefault();\r\n * await submitForm();\r\n * }));\r\n */\r\nexport function asyncHandler<TArgs extends unknown[]>(\r\n fn: (...args: TArgs) => Promise<void>,\r\n): (...args: TArgs) => void {\r\n return function (this: any, ...args: TArgs) {\r\n fn.call(this, ...args).catch((cause: unknown) => {\r\n const error = reportError('Async callback failed', { cause });\r\n if (error) throw error;\r\n });\r\n };\r\n}\r\n", "import { reportError } from './errors';\r\n\r\n/**\r\n * Generic constructor type used for dependency registration and injection.\r\n * Represents any class constructor that can be used with the DI container.\r\n *\r\n * @template T - The type of object the constructor creates\r\n *\r\n * @example\r\n * // Use with service registration\r\n * class UserService {}\r\n * const ctor: Constructor<UserService> = UserService;\r\n * serviceCollection.registerByType(ctor, { inject: [] });\r\n */\r\nexport type Constructor<T extends object = object> = new (...args: any[]) => T;\r\n\r\n/**\r\n * Controls how service instances are shared across the container hierarchy.\r\n * Used when registering services to define their lifetime behavior.\r\n *\r\n * - `global`: Single instance shared everywhere (singleton pattern)\r\n * - `closest`: New instance per container scope (scoped lifetime)\r\n *\r\n * @example\r\n * // Singleton service - same instance everywhere\r\n * serviceCollection.register(LoggerService, { scope: 'global', inject: [] });\r\n *\r\n * // Scoped service - new instance per scope\r\n * serviceCollection.register(RequestContext, { scope: 'closest', inject: [] });\r\n */\r\nexport type ServiceScope = 'global' | 'closest';\r\n\r\n/**\r\n * Configuration options for registering a service in the DI container.\r\n * Controls identification, lifetime, and dependency resolution.\r\n *\r\n * @example\r\n * // Register with constructor injection\r\n * const options: RegistrationOptions = {\r\n * scope: 'global',\r\n * inject: [DatabaseConnection, ConfigService]\r\n * };\r\n * serviceCollection.register(UserRepository, options);\r\n *\r\n * @example\r\n * // Register with property injection\r\n * const options: RegistrationOptions = {\r\n * inject: [],\r\n * properties: { logger: Logger, config: 'appConfig' }\r\n * };\r\n *\r\n * @example\r\n * // Register with a pre-created instance\r\n * const options: RegistrationOptions = {\r\n * inject: [],\r\n * instance: existingService\r\n * };\r\n */\r\nexport interface RegistrationOptions {\r\n /** Service lifetime - 'global' for singleton, 'closest' for scoped */\r\n scope?: ServiceScope;\r\n /** Optional string key for resolving by name instead of type */\r\n key?: string;\r\n /** Pre-existing instance to use instead of creating new one */\r\n instance?: unknown;\r\n /** Types or keys for constructor parameters, in order */\r\n inject: (string | Constructor)[];\r\n /** Map of property names to their injection types/keys */\r\n properties?: Record<string, string | Constructor>;\r\n}\r\n\r\n/**\r\n * Field decorator that injects a service from the global DI container.\r\n * The service is resolved when the class instance is created (not at class definition time),\r\n * so services must be registered before the first instance is created.\r\n *\r\n * Works with web components regardless of how they are created:\r\n * - By the browser (HTML parsing): services are resolved during construction\r\n * - By application code (`document.createElement` or `new`): same behavior\r\n * - Injected fields are available in `connectedCallback` and all lifecycle methods\r\n *\r\n * @example\r\n * // Using `@Inject` in a web component\r\n * class UserPanel extends HTMLElement {\r\n * @Inject(UserService)\r\n * private userService!: UserService;\r\n *\r\n * connectedCallback() {\r\n * // userService is already resolved and ready to use\r\n * const user = this.userService.getCurrentUser();\r\n * this.render(user);\r\n * }\r\n * }\r\n *\r\n * @example\r\n * // Services must be registered before components are created.\r\n * // In your app entry point (e.g. main.ts):\r\n * serviceCollection.registerByType(UserService, { inject: [ApiClient] });\r\n * serviceCollection.registerByType(ApiClient, { inject: [] });\r\n *\r\n * // Now components can be created (by browser or code)\r\n * customElements.define('user-panel', UserPanel);\r\n */\r\nexport function Inject<T extends object>(typeOrKey: Constructor<T> | string) {\r\n return (_: undefined, context: ClassFieldDecoratorContext) => {\r\n return function(this: any) {\r\n return container.resolve(typeOrKey);\r\n };\r\n };\r\n}\r\n\r\n// Temporary collector of property injections - cleared after registration\r\n//const propertyCollector = new WeakMap<object, Record<string, string>>();\r\n\r\n/**\r\n * Class decorator that registers a service in the global DI container.\r\n * Registration happens at class definition time (when the module loads),\r\n * so import the module before creating instances that depend on this service.\r\n *\r\n * For web components: use `@ContainerService` on services, not on the\r\n * components themselves. Components use `@Inject` to consume services.\r\n *\r\n * @param options - Registration configuration including scope and dependencies\r\n *\r\n * @example\r\n * // Register a service that components can inject\r\n * @ContainerService({ inject: [ApiClient] })\r\n * class UserService {\r\n * constructor(private api: ApiClient) {}\r\n * getCurrentUser() { return this.api.get('/user'); }\r\n * }\r\n *\r\n * // Component consumes the service\r\n * class UserPanel extends HTMLElement {\r\n * @Inject(UserService)\r\n * private userService!: UserService;\r\n * }\r\n *\r\n * @example\r\n * // Service with custom key for named resolution\r\n * @ContainerService({ key: 'primaryCache', scope: 'global', inject: [] })\r\n * class CacheService {}\r\n *\r\n * // Later resolve by key\r\n * const cache = container.resolve('primaryCache');\r\n */\r\nexport function ContainerService<T extends object>(\r\n options?: RegistrationOptions\r\n) {\r\n return (target: Constructor<T>) => {\r\n const opts = options ?? {inject: []};\r\n\r\n if (opts.key) {\r\n serviceCollection.register(target, opts);\r\n } else {\r\n serviceCollection.registerByType(target, opts);\r\n }\r\n };\r\n}\r\n\r\n/**\r\n * Internal class representing a registered service's metadata.\r\n * Holds all information needed to create and configure service instances.\r\n *\r\n * @internal This is an implementation detail and should not be used directly.\r\n */\r\nclass Registration {\r\n /**\r\n * Creates a new registration record.\r\n *\r\n * @param classConstructor - The class constructor function\r\n * @param scope - Instance sharing behavior\r\n * @param inject - Constructor parameter dependencies\r\n * @param properties - Property injection mappings\r\n * @param key - Optional string identifier\r\n * @param instance - Optional pre-created instance\r\n */\r\n constructor(\r\n public classConstructor: Constructor,\r\n public scope: ServiceScope,\r\n public inject: (string | Constructor)[],\r\n public properties: Record<string, string | Constructor> = {},\r\n public key?: string,\r\n public instance?: unknown\r\n ) {}\r\n}\r\n\r\n/**\r\n * Registry that stores service registration metadata.\r\n * Use this to register services before they can be resolved by a ServiceContainer.\r\n *\r\n * Typically you'll use the global `serviceCollection` instance rather than creating your own.\r\n *\r\n * @example\r\n * // Register a service by type\r\n * serviceCollection.registerByType(LoggerService, { inject: [] });\r\n *\r\n * // Register with a string key\r\n * serviceCollection.register(CacheService, { key: 'cache', inject: [] });\r\n *\r\n * // Check if service is registered\r\n * const reg = serviceCollection.tryGet(LoggerService);\r\n * if (reg) {\r\n * console.log('Logger is registered');\r\n * }\r\n */\r\nexport class ServiceCollection {\r\n private servicesByKey = new Map<string, Registration>();\r\n private servicesByClassName = new Map<string, Registration>();\r\n\r\n /**\r\n * Registers a service with full configuration options.\r\n * The service will be resolvable by both its class name and optional key.\r\n *\r\n * @param constructor - The service class constructor\r\n * @param options - Registration configuration\r\n */\r\n register<T extends object>(constructor: Constructor<T>, options: RegistrationOptions): void {\r\n this.validateRegistration(constructor, options);\r\n\r\n const reg = new Registration(\r\n constructor,\r\n options.scope ?? 'global',\r\n options.inject,\r\n options.properties ?? {},\r\n options.key,\r\n options.instance\r\n );\r\n\r\n if (options.key) {\r\n this.servicesByKey.set(options.key, reg);\r\n }\r\n this.servicesByClassName.set(constructor.name, reg);\r\n }\r\n\r\n /**\r\n * Registers a service by its class type.\r\n * The service will be resolvable by its class constructor.\r\n *\r\n * @param constructor - The service class constructor\r\n * @param options - Optional registration configuration\r\n */\r\n registerByType<T extends object>(\r\n constructor: Constructor<T>,\r\n options?: RegistrationOptions\r\n ): void {\r\n this.checkNameCollision(constructor);\r\n if (options) this.validateRegistration(constructor, options);\r\n\r\n const reg = new Registration(constructor, options?.scope, options?.inject ?? [], options?.properties, options?.key, options?.instance);\r\n if (options?.key) {\r\n this.servicesByKey.set(options.key, reg);\r\n }\r\n this.servicesByClassName.set(constructor.name, reg);\r\n }\r\n\r\n private checkNameCollision<T extends object>(constructor: Constructor<T>): void {\r\n const existing = this.servicesByClassName.get(constructor.name);\r\n if (existing && existing.classConstructor !== constructor) {\r\n const error = reportError('Service name collision: different class registered with same name', {\r\n service: constructor.name,\r\n });\r\n if (error) throw error;\r\n }\r\n }\r\n\r\n private validateRegistration<T extends object>(constructor: Constructor<T>, options: RegistrationOptions): void {\r\n this.checkNameCollision(constructor);\r\n\r\n if (options.key) {\r\n const existingByKey = this.servicesByKey.get(options.key);\r\n if (existingByKey && existingByKey.classConstructor !== constructor) {\r\n const error = reportError('Service key already registered to a different class', {\r\n key: options.key,\r\n existingClass: existingByKey.classConstructor.name,\r\n newClass: constructor.name,\r\n });\r\n if (error) throw error;\r\n }\r\n }\r\n\r\n if (options.instance && options.inject.length > 0) {\r\n const error = reportError('Service has both instance and inject (inject will be ignored)', {\r\n service: constructor.name,\r\n });\r\n if (error) throw error;\r\n }\r\n }\r\n\r\n /**\r\n * Attempts to retrieve a service registration.\r\n * Returns undefined if the service is not registered.\r\n *\r\n * @param key - Either a string key or class constructor\r\n * @returns The registration or undefined\r\n */\r\n tryGet<T extends object>(key: string | Constructor<T>): Registration | undefined {\r\n if (typeof key === 'string') {\r\n return this.servicesByKey.get(key);\r\n }\r\n return this.servicesByClassName.get(key.name);\r\n }\r\n\r\n /**\r\n * Retrieves a service registration or throws if not found.\r\n *\r\n * @param key - Either a string key or class constructor\r\n * @returns The registration\r\n * @throws Error if the service is not registered\r\n */\r\n get<T extends object>(key: string | Constructor<T>): Registration {\r\n const reg = this.tryGet(key);\r\n if (!reg) {\r\n const service = typeof key === 'string' ? key : key.name;\r\n const error = reportError(`Failed to resolve service '${service}'`, {\r\n service,\r\n registeredTypes: Array.from(this.servicesByClassName.keys()),\r\n registeredKeys: Array.from(this.servicesByKey.keys()),\r\n });\r\n if (error) throw error;\r\n }\r\n return reg!;\r\n }\r\n}\r\n\r\n/**\r\n * Internal storage for tracking injected fields during service resolution.\r\n * @internal\r\n */\r\nconst injectedFields = new WeakMap<object, Map<string, string>>();\r\n\r\n/**\r\n * IoC container that resolves and manages service instances.\r\n * Creates instances based on registrations in a ServiceCollection,\r\n * handling constructor injection, property injection, and lifetime management.\r\n *\r\n * Typically you'll use the global `container` instance rather than creating your own.\r\n *\r\n * @example\r\n * // Resolve a service by class\r\n * const logger = container.resolve(LoggerService);\r\n *\r\n * // Resolve by string key\r\n * const cache = container.resolve<CacheService>('primaryCache');\r\n *\r\n * @example\r\n * // Full setup workflow\r\n * serviceCollection.register(UserService, {\r\n * inject: [DatabaseConnection],\r\n * scope: 'global'\r\n * });\r\n *\r\n * const userService = container.resolve(UserService);\r\n */\r\nexport class ServiceContainer {\r\n private instances = new Map<string, any>();\r\n\r\n /**\r\n * Creates a new container backed by the given service collection.\r\n *\r\n * @param serviceCollection - The registry containing service registrations\r\n */\r\n constructor(private serviceCollection: ServiceCollection) {}\r\n\r\n /**\r\n * Resolves a service instance by class type or string key.\r\n * Creates the instance if not already cached (for global scope).\r\n * Handles constructor and property injection automatically.\r\n *\r\n * @param keyOrType - Either a string key or class constructor\r\n * @returns The resolved service instance\r\n * @throws Error if the service is not registered\r\n *\r\n * @example\r\n * const service = container.resolve(MyService);\r\n */\r\n resolve<T extends object>(keyOrType: string | Constructor<T>): T {\r\n const key = typeof keyOrType === 'string' ? keyOrType : keyOrType.name;\r\n\r\n if (this.instances.has(key)) {\r\n return this.instances.get(key);\r\n }\r\n\r\n const registration = this.serviceCollection.get(keyOrType);\r\n if (!registration) {\r\n const error = reportError(`Failed to resolve service '${key}'`, { service: key });\r\n if (error) throw error;\r\n return undefined as unknown as T;\r\n }\r\n\r\n if (registration.instance) {\r\n const inst = registration.instance as T;\r\n this.injectFields(inst, registration);\r\n this.instances.set(key, inst);\r\n return inst;\r\n }\r\n\r\n const instance = this.createInstance<T>(registration);\r\n if (registration.scope === 'global') {\r\n this.instances.set(key, instance);\r\n }\r\n this.injectFields(instance, registration);\r\n\r\n return instance;\r\n }\r\n\r\n /**\r\n * Creates a new instance of a service, resolving all constructor dependencies.\r\n */\r\n private createInstance<T extends object>(registration: Registration): T {\r\n const constructor = registration.classConstructor as Constructor<T>;\r\n\r\n const dependencies = registration.inject.map(dep => this.resolve(dep));\r\n return new constructor(...dependencies);\r\n }\r\n\r\n /**\r\n * Injects dependencies into instance properties based on registration config.\r\n */\r\n private injectFields<T extends object>(instance: T, registration: Registration): void {\r\n for (const [fieldName, keyOrType] of Object.entries(registration.properties)) {\r\n (instance as any)[fieldName] = this.resolve(keyOrType);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Global service collection instance for registering services.\r\n * Use this to register services that can later be resolved by the container.\r\n *\r\n * @example\r\n * import { serviceCollection } from 'relaxjs';\r\n *\r\n * serviceCollection.register(MyService, { inject: [Dependency] });\r\n */\r\nexport const serviceCollection = new ServiceCollection();\r\n\r\n/**\r\n * Global service container instance for resolving dependencies.\r\n * Use this to obtain service instances with all dependencies injected.\r\n *\r\n * @example\r\n * import { container } from 'relaxjs';\r\n *\r\n * const service = container.resolve(MyService);\r\n */\r\nexport const container = new ServiceContainer(serviceCollection);"],
5
+ "mappings": "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",
6
+ "names": ["index_exports", "__export", "ContainerService", "Inject", "ServiceCollection", "ServiceContainer", "container", "serviceCollection", "__toCommonJS", "RelaxError", "message", "context", "handler", "reportError", "message", "context", "error", "RelaxError", "handler", "suppressed", "Inject", "typeOrKey", "_", "context", "container", "ContainerService", "options", "target", "opts", "serviceCollection", "Registration", "classConstructor", "scope", "inject", "properties", "key", "instance", "ServiceCollection", "constructor", "reg", "existing", "error", "reportError", "existingByKey", "service", "ServiceContainer", "serviceCollection", "keyOrType", "key", "registration", "error", "reportError", "inst", "instance", "constructor", "dependencies", "dep", "fieldName", "ServiceCollection", "container"]
7
7
  }
package/dist/di/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- var a=class extends Error{constructor(e,r){super(e);this.context=r}},p=null;function o(n,t){let e=new a(n,t);if(p){let r=!1;if(p(e,{suppress(){r=!0}}),r)return null}return e}function h(n){return(t,e)=>{var r=u.resolve(n);return function(){return r}}}function x(n){return t=>{let e=n??{inject:[]};e.key?d.register(t,e):d.registerByType(t,e)}}var c=class{constructor(t,e,r,s={},i,v){this.classConstructor=t;this.scope=e;this.inject=r;this.properties=s;this.key=i;this.instance=v}},l=class{constructor(){this.servicesByKey=new Map;this.servicesByClassName=new Map}register(t,e){this.validateRegistration(t,e);let r=new c(t,e.scope??"global",e.inject,e.properties??{},e.key,e.instance);e.key&&this.servicesByKey.set(e.key,r),this.servicesByClassName.set(t.name,r)}registerByType(t,e){this.checkNameCollision(t),e&&this.validateRegistration(t,e);let r=new c(t,e?.scope,e?.inject??[],e?.properties,e?.key,e?.instance);e?.key&&this.servicesByKey.set(e.key,r),this.servicesByClassName.set(t.name,r)}checkNameCollision(t){let e=this.servicesByClassName.get(t.name);if(e&&e.classConstructor!==t){let r=o("Service name collision: different class registered with same name",{service:t.name});if(r)throw r}}validateRegistration(t,e){if(this.checkNameCollision(t),e.key){let r=this.servicesByKey.get(e.key);if(r&&r.classConstructor!==t){let s=o("Service key already registered to a different class",{key:e.key,existingClass:r.classConstructor.name,newClass:t.name});if(s)throw s}}if(e.instance&&e.inject.length>0){let r=o("Service has both instance and inject (inject will be ignored)",{service:t.name});if(r)throw r}}tryGet(t){return typeof t=="string"?this.servicesByKey.get(t):this.servicesByClassName.get(t.name)}get(t){let e=this.tryGet(t);if(!e){let r=typeof t=="string"?t:t.name,s=o(`Failed to resolve service '${r}'`,{service:r,registeredTypes:Array.from(this.servicesByClassName.keys()),registeredKeys:Array.from(this.servicesByKey.keys())});if(s)throw s}return e}};var g=class{constructor(t){this.serviceCollection=t;this.instances=new Map}resolve(t){let e=typeof t=="string"?t:t.name;if(this.instances.has(e))return this.instances.get(e);let r=this.serviceCollection.get(t);if(!r){let i=o(`Failed to resolve service '${e}'`,{service:e});if(i)throw i;return}if(r.instance){let i=r.instance;return this.injectFields(i,r),this.instances.set(e,i),i}let s=this.createInstance(r);return r.scope==="global"&&this.instances.set(e,s),this.injectFields(s,r),s}createInstance(t){let e=t.classConstructor,r=t.inject.map(s=>this.resolve(s));return new e(...r)}injectFields(t,e){for(let[r,s]of Object.entries(e.properties))t[r]=this.resolve(s)}},d=new l,u=new g(d);export{x as ContainerService,h as Inject,l as ServiceCollection,g as ServiceContainer,u as container,d as serviceCollection};
1
+ var a=class extends Error{constructor(e,r){super(e);this.context=r}},u=null;function o(n,t){let e=new a(n,t);if(u){let r=!1;if(u(e,{suppress(){r=!0}}),r)return null}return e}function h(n){return(t,e)=>function(){return p.resolve(n)}}function x(n){return t=>{let e=n??{inject:[]};e.key?d.register(t,e):d.registerByType(t,e)}}var c=class{constructor(t,e,r,s={},i,v){this.classConstructor=t;this.scope=e;this.inject=r;this.properties=s;this.key=i;this.instance=v}},l=class{constructor(){this.servicesByKey=new Map;this.servicesByClassName=new Map}register(t,e){this.validateRegistration(t,e);let r=new c(t,e.scope??"global",e.inject,e.properties??{},e.key,e.instance);e.key&&this.servicesByKey.set(e.key,r),this.servicesByClassName.set(t.name,r)}registerByType(t,e){this.checkNameCollision(t),e&&this.validateRegistration(t,e);let r=new c(t,e?.scope,e?.inject??[],e?.properties,e?.key,e?.instance);e?.key&&this.servicesByKey.set(e.key,r),this.servicesByClassName.set(t.name,r)}checkNameCollision(t){let e=this.servicesByClassName.get(t.name);if(e&&e.classConstructor!==t){let r=o("Service name collision: different class registered with same name",{service:t.name});if(r)throw r}}validateRegistration(t,e){if(this.checkNameCollision(t),e.key){let r=this.servicesByKey.get(e.key);if(r&&r.classConstructor!==t){let s=o("Service key already registered to a different class",{key:e.key,existingClass:r.classConstructor.name,newClass:t.name});if(s)throw s}}if(e.instance&&e.inject.length>0){let r=o("Service has both instance and inject (inject will be ignored)",{service:t.name});if(r)throw r}}tryGet(t){return typeof t=="string"?this.servicesByKey.get(t):this.servicesByClassName.get(t.name)}get(t){let e=this.tryGet(t);if(!e){let r=typeof t=="string"?t:t.name,s=o(`Failed to resolve service '${r}'`,{service:r,registeredTypes:Array.from(this.servicesByClassName.keys()),registeredKeys:Array.from(this.servicesByKey.keys())});if(s)throw s}return e}};var g=class{constructor(t){this.serviceCollection=t;this.instances=new Map}resolve(t){let e=typeof t=="string"?t:t.name;if(this.instances.has(e))return this.instances.get(e);let r=this.serviceCollection.get(t);if(!r){let i=o(`Failed to resolve service '${e}'`,{service:e});if(i)throw i;return}if(r.instance){let i=r.instance;return this.injectFields(i,r),this.instances.set(e,i),i}let s=this.createInstance(r);return r.scope==="global"&&this.instances.set(e,s),this.injectFields(s,r),s}createInstance(t){let e=t.classConstructor,r=t.inject.map(s=>this.resolve(s));return new e(...r)}injectFields(t,e){for(let[r,s]of Object.entries(e.properties))t[r]=this.resolve(s)}},d=new l,p=new g(d);export{x as ContainerService,h as Inject,l as ServiceCollection,g as ServiceContainer,p as container,d as serviceCollection};
2
2
  //# sourceMappingURL=index.mjs.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/errors.ts", "../../src/DependencyInjection.ts"],
4
- "sourcesContent": ["/**\r\n * Global error handling for Relaxjs.\r\n * Register a handler with `onError()` to intercept errors before they throw.\r\n * Call `ctx.suppress()` in the handler to prevent the error from being thrown.\r\n *\r\n * @example\r\n * import { onError } from 'relaxjs';\r\n *\r\n * onError((error, ctx) => {\r\n * logToService(error.message, error.context);\r\n * showToast(error.message);\r\n * ctx.suppress();\r\n * });\r\n */\r\n\r\n/**\r\n * Passed to error handlers to control error behavior.\r\n * Call `suppress()` to prevent the error from being thrown.\r\n */\r\nexport interface ErrorContext {\r\n suppress(): void;\r\n}\r\n\r\n/**\r\n * Error with structured context for debugging.\r\n * The `context` record contains details like route name, component tag, route data.\r\n *\r\n * @example\r\n * onError((error, ctx) => {\r\n * console.log(error.context.route);\r\n * console.log(error.context.componentTagName);\r\n * });\r\n */\r\nexport class RelaxError extends Error {\r\n constructor(\r\n message: string,\r\n public context: Record<string, unknown>,\r\n ) {\r\n super(message);\r\n }\r\n}\r\n\r\ntype ErrorHandler = (error: RelaxError, ctx: ErrorContext) => void;\r\n\r\nlet handler: ErrorHandler | null = null;\r\n\r\n/**\r\n * Registers a global error handler for Relaxjs errors.\r\n * The handler receives the error and an `ErrorContext`.\r\n * Call `ctx.suppress()` to prevent the error from being thrown.\r\n * Only one handler can be active at a time; subsequent calls replace the previous handler.\r\n *\r\n * @example\r\n * onError((error, ctx) => {\r\n * if (error.context.route === 'optional-panel') {\r\n * ctx.suppress();\r\n * return;\r\n * }\r\n * showErrorDialog(error.message);\r\n * });\r\n */\r\nexport function onError(fn: ErrorHandler) {\r\n handler = fn;\r\n}\r\n\r\n/**\r\n * Reports an error through the global handler.\r\n * Returns the `RelaxError` if it should be thrown, or `null` if the handler suppressed it.\r\n * The caller is responsible for throwing the returned error.\r\n *\r\n * @param message - Human-readable error description\r\n * @param context - Structured data for debugging (route, component, params, cause, etc.)\r\n * @returns The error to throw, or `null` if suppressed\r\n *\r\n * @example\r\n * const error = reportError('Failed to load route component', {\r\n * route: 'user',\r\n * componentTagName: 'user-profile',\r\n * routeData: { id: 123 },\r\n * });\r\n * if (error) throw error;\r\n */\r\nexport function reportError(message: string, context: Record<string, unknown>): RelaxError | null {\r\n const error = new RelaxError(message, context);\r\n if (handler) {\r\n let suppressed = false;\r\n const ctx: ErrorContext = {\r\n suppress() { suppressed = true; },\r\n };\r\n handler(error, ctx);\r\n if (suppressed) {\r\n return null;\r\n }\r\n }\r\n return error;\r\n}\r\n", "import { reportError } from './errors';\r\n\r\n/**\r\n * Generic constructor type used for dependency registration and injection.\r\n * Represents any class constructor that can be used with the DI container.\r\n *\r\n * @template T - The type of object the constructor creates\r\n *\r\n * @example\r\n * // Use with service registration\r\n * class UserService {}\r\n * const ctor: Constructor<UserService> = UserService;\r\n * serviceCollection.registerByType(ctor, { inject: [] });\r\n */\r\nexport type Constructor<T extends object = object> = new (...args: any[]) => T;\r\n\r\n/**\r\n * Controls how service instances are shared across the container hierarchy.\r\n * Used when registering services to define their lifetime behavior.\r\n *\r\n * - `global`: Single instance shared everywhere (singleton pattern)\r\n * - `closest`: New instance per container scope (scoped lifetime)\r\n *\r\n * @example\r\n * // Singleton service - same instance everywhere\r\n * serviceCollection.register(LoggerService, { scope: 'global', inject: [] });\r\n *\r\n * // Scoped service - new instance per scope\r\n * serviceCollection.register(RequestContext, { scope: 'closest', inject: [] });\r\n */\r\nexport type ServiceScope = 'global' | 'closest';\r\n\r\n/**\r\n * Configuration options for registering a service in the DI container.\r\n * Controls identification, lifetime, and dependency resolution.\r\n *\r\n * @example\r\n * // Register with constructor injection\r\n * const options: RegistrationOptions = {\r\n * scope: 'global',\r\n * inject: [DatabaseConnection, ConfigService]\r\n * };\r\n * serviceCollection.register(UserRepository, options);\r\n *\r\n * @example\r\n * // Register with property injection\r\n * const options: RegistrationOptions = {\r\n * inject: [],\r\n * properties: { logger: Logger, config: 'appConfig' }\r\n * };\r\n *\r\n * @example\r\n * // Register with a pre-created instance\r\n * const options: RegistrationOptions = {\r\n * inject: [],\r\n * instance: existingService\r\n * };\r\n */\r\nexport interface RegistrationOptions {\r\n /** Service lifetime - 'global' for singleton, 'closest' for scoped */\r\n scope?: ServiceScope;\r\n /** Optional string key for resolving by name instead of type */\r\n key?: string;\r\n /** Pre-existing instance to use instead of creating new one */\r\n instance?: unknown;\r\n /** Types or keys for constructor parameters, in order */\r\n inject: (string | Constructor)[];\r\n /** Map of property names to their injection types/keys */\r\n properties?: Record<string, string | Constructor>;\r\n}\r\n\r\n/**\r\n * Field decorator that collects property injection configuration.\r\n * Updates or creates the properties mapping in registration options.\r\n * \r\n * @example\r\n * @ContainerService({\r\n * inject: [Database],\r\n * properties: {\r\n * logger: Logger, // Inject by type\r\n * audit: 'auditLogger' // Inject by key\r\n * }\r\n * })\r\n * class UserService {\r\n * @Inject(Logger)\r\n * private logger!: Logger;\r\n * \r\n * @Inject('auditLogger')\r\n * private audit!: Logger;\r\n * \r\n * constructor(db: Database) {}\r\n * }\r\n */\r\nexport function Inject<T extends object>(typeOrKey: Constructor<T> | string) {\r\n return (_: undefined, context: ClassFieldDecoratorContext) => {\r\n var instance = container.resolve(typeOrKey);\r\n return function(this: any) {\r\n return instance;\r\n }\r\n };\r\n}\r\n\r\n// Temporary collector of property injections - cleared after registration\r\n//const propertyCollector = new WeakMap<object, Record<string, string>>();\r\n\r\n/**\r\n * Class decorator that automatically registers a service in the global DI container.\r\n * Use this to declaratively register services without manual registration calls.\r\n *\r\n * Services are registered at module load time, so ensure this file is imported\r\n * before attempting to resolve the decorated service.\r\n *\r\n * @param options - Registration configuration including scope and dependencies\r\n *\r\n * @example\r\n * // Simple service with constructor injection\r\n * @ContainerService({ inject: [DatabaseConnection] })\r\n * class UserRepository {\r\n * constructor(private db: DatabaseConnection) {}\r\n * }\r\n *\r\n * @example\r\n * // Service with custom key for named resolution\r\n * @ContainerService({ key: 'primaryCache', scope: 'global', inject: [] })\r\n * class CacheService {}\r\n *\r\n * // Later resolve by key\r\n * const cache = container.resolve('primaryCache');\r\n */\r\nexport function ContainerService<T extends object>(\r\n options?: RegistrationOptions\r\n) {\r\n return (target: Constructor<T>) => {\r\n const opts = options ?? {inject: []};\r\n\r\n if (opts.key) {\r\n serviceCollection.register(target, opts);\r\n } else {\r\n serviceCollection.registerByType(target, opts);\r\n }\r\n };\r\n}\r\n\r\n/**\r\n * Internal class representing a registered service's metadata.\r\n * Holds all information needed to create and configure service instances.\r\n *\r\n * @internal This is an implementation detail and should not be used directly.\r\n */\r\nclass Registration {\r\n /**\r\n * Creates a new registration record.\r\n *\r\n * @param classConstructor - The class constructor function\r\n * @param scope - Instance sharing behavior\r\n * @param inject - Constructor parameter dependencies\r\n * @param properties - Property injection mappings\r\n * @param key - Optional string identifier\r\n * @param instance - Optional pre-created instance\r\n */\r\n constructor(\r\n public classConstructor: Constructor,\r\n public scope: ServiceScope,\r\n public inject: (string | Constructor)[],\r\n public properties: Record<string, string | Constructor> = {},\r\n public key?: string,\r\n public instance?: unknown\r\n ) {}\r\n}\r\n\r\n/**\r\n * Registry that stores service registration metadata.\r\n * Use this to register services before they can be resolved by a ServiceContainer.\r\n *\r\n * Typically you'll use the global `serviceCollection` instance rather than creating your own.\r\n *\r\n * @example\r\n * // Register a service by type\r\n * serviceCollection.registerByType(LoggerService, { inject: [] });\r\n *\r\n * // Register with a string key\r\n * serviceCollection.register(CacheService, { key: 'cache', inject: [] });\r\n *\r\n * // Check if service is registered\r\n * const reg = serviceCollection.tryGet(LoggerService);\r\n * if (reg) {\r\n * console.log('Logger is registered');\r\n * }\r\n */\r\nexport class ServiceCollection {\r\n private servicesByKey = new Map<string, Registration>();\r\n private servicesByClassName = new Map<string, Registration>();\r\n\r\n /**\r\n * Registers a service with full configuration options.\r\n * The service will be resolvable by both its class name and optional key.\r\n *\r\n * @param constructor - The service class constructor\r\n * @param options - Registration configuration\r\n */\r\n register<T extends object>(constructor: Constructor<T>, options: RegistrationOptions): void {\r\n this.validateRegistration(constructor, options);\r\n\r\n const reg = new Registration(\r\n constructor,\r\n options.scope ?? 'global',\r\n options.inject,\r\n options.properties ?? {},\r\n options.key,\r\n options.instance\r\n );\r\n\r\n if (options.key) {\r\n this.servicesByKey.set(options.key, reg);\r\n }\r\n this.servicesByClassName.set(constructor.name, reg);\r\n }\r\n\r\n /**\r\n * Registers a service by its class type.\r\n * The service will be resolvable by its class constructor.\r\n *\r\n * @param constructor - The service class constructor\r\n * @param options - Optional registration configuration\r\n */\r\n registerByType<T extends object>(\r\n constructor: Constructor<T>,\r\n options?: RegistrationOptions\r\n ): void {\r\n this.checkNameCollision(constructor);\r\n if (options) this.validateRegistration(constructor, options);\r\n\r\n const reg = new Registration(constructor, options?.scope, options?.inject ?? [], options?.properties, options?.key, options?.instance);\r\n if (options?.key) {\r\n this.servicesByKey.set(options.key, reg);\r\n }\r\n this.servicesByClassName.set(constructor.name, reg);\r\n }\r\n\r\n private checkNameCollision<T extends object>(constructor: Constructor<T>): void {\r\n const existing = this.servicesByClassName.get(constructor.name);\r\n if (existing && existing.classConstructor !== constructor) {\r\n const error = reportError('Service name collision: different class registered with same name', {\r\n service: constructor.name,\r\n });\r\n if (error) throw error;\r\n }\r\n }\r\n\r\n private validateRegistration<T extends object>(constructor: Constructor<T>, options: RegistrationOptions): void {\r\n this.checkNameCollision(constructor);\r\n\r\n if (options.key) {\r\n const existingByKey = this.servicesByKey.get(options.key);\r\n if (existingByKey && existingByKey.classConstructor !== constructor) {\r\n const error = reportError('Service key already registered to a different class', {\r\n key: options.key,\r\n existingClass: existingByKey.classConstructor.name,\r\n newClass: constructor.name,\r\n });\r\n if (error) throw error;\r\n }\r\n }\r\n\r\n if (options.instance && options.inject.length > 0) {\r\n const error = reportError('Service has both instance and inject (inject will be ignored)', {\r\n service: constructor.name,\r\n });\r\n if (error) throw error;\r\n }\r\n }\r\n\r\n /**\r\n * Attempts to retrieve a service registration.\r\n * Returns undefined if the service is not registered.\r\n *\r\n * @param key - Either a string key or class constructor\r\n * @returns The registration or undefined\r\n */\r\n tryGet<T extends object>(key: string | Constructor<T>): Registration | undefined {\r\n if (typeof key === 'string') {\r\n return this.servicesByKey.get(key);\r\n }\r\n return this.servicesByClassName.get(key.name);\r\n }\r\n\r\n /**\r\n * Retrieves a service registration or throws if not found.\r\n *\r\n * @param key - Either a string key or class constructor\r\n * @returns The registration\r\n * @throws Error if the service is not registered\r\n */\r\n get<T extends object>(key: string | Constructor<T>): Registration {\r\n const reg = this.tryGet(key);\r\n if (!reg) {\r\n const service = typeof key === 'string' ? key : key.name;\r\n const error = reportError(`Failed to resolve service '${service}'`, {\r\n service,\r\n registeredTypes: Array.from(this.servicesByClassName.keys()),\r\n registeredKeys: Array.from(this.servicesByKey.keys()),\r\n });\r\n if (error) throw error;\r\n }\r\n return reg!;\r\n }\r\n}\r\n\r\n/**\r\n * Internal storage for tracking injected fields during service resolution.\r\n * @internal\r\n */\r\nconst injectedFields = new WeakMap<object, Map<string, string>>();\r\n\r\n/**\r\n * IoC container that resolves and manages service instances.\r\n * Creates instances based on registrations in a ServiceCollection,\r\n * handling constructor injection, property injection, and lifetime management.\r\n *\r\n * Typically you'll use the global `container` instance rather than creating your own.\r\n *\r\n * @example\r\n * // Resolve a service by class\r\n * const logger = container.resolve(LoggerService);\r\n *\r\n * // Resolve by string key\r\n * const cache = container.resolve<CacheService>('primaryCache');\r\n *\r\n * @example\r\n * // Full setup workflow\r\n * serviceCollection.register(UserService, {\r\n * inject: [DatabaseConnection],\r\n * scope: 'global'\r\n * });\r\n *\r\n * const userService = container.resolve(UserService);\r\n */\r\nexport class ServiceContainer {\r\n private instances = new Map<string, any>();\r\n\r\n /**\r\n * Creates a new container backed by the given service collection.\r\n *\r\n * @param serviceCollection - The registry containing service registrations\r\n */\r\n constructor(private serviceCollection: ServiceCollection) {}\r\n\r\n /**\r\n * Resolves a service instance by class type or string key.\r\n * Creates the instance if not already cached (for global scope).\r\n * Handles constructor and property injection automatically.\r\n *\r\n * @param keyOrType - Either a string key or class constructor\r\n * @returns The resolved service instance\r\n * @throws Error if the service is not registered\r\n *\r\n * @example\r\n * const service = container.resolve(MyService);\r\n */\r\n resolve<T extends object>(keyOrType: string | Constructor<T>): T {\r\n const key = typeof keyOrType === 'string' ? keyOrType : keyOrType.name;\r\n\r\n if (this.instances.has(key)) {\r\n return this.instances.get(key);\r\n }\r\n\r\n const registration = this.serviceCollection.get(keyOrType);\r\n if (!registration) {\r\n const error = reportError(`Failed to resolve service '${key}'`, { service: key });\r\n if (error) throw error;\r\n return undefined as unknown as T;\r\n }\r\n\r\n if (registration.instance) {\r\n const inst = registration.instance as T;\r\n this.injectFields(inst, registration);\r\n this.instances.set(key, inst);\r\n return inst;\r\n }\r\n\r\n const instance = this.createInstance<T>(registration);\r\n if (registration.scope === 'global') {\r\n this.instances.set(key, instance);\r\n }\r\n this.injectFields(instance, registration);\r\n\r\n return instance;\r\n }\r\n\r\n /**\r\n * Creates a new instance of a service, resolving all constructor dependencies.\r\n */\r\n private createInstance<T extends object>(registration: Registration): T {\r\n const constructor = registration.classConstructor as Constructor<T>;\r\n\r\n const dependencies = registration.inject.map(dep => this.resolve(dep));\r\n return new constructor(...dependencies);\r\n }\r\n\r\n /**\r\n * Injects dependencies into instance properties based on registration config.\r\n */\r\n private injectFields<T extends object>(instance: T, registration: Registration): void {\r\n for (const [fieldName, keyOrType] of Object.entries(registration.properties)) {\r\n (instance as any)[fieldName] = this.resolve(keyOrType);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Global service collection instance for registering services.\r\n * Use this to register services that can later be resolved by the container.\r\n *\r\n * @example\r\n * import { serviceCollection } from 'relaxjs';\r\n *\r\n * serviceCollection.register(MyService, { inject: [Dependency] });\r\n */\r\nexport const serviceCollection = new ServiceCollection();\r\n\r\n/**\r\n * Global service container instance for resolving dependencies.\r\n * Use this to obtain service instances with all dependencies injected.\r\n *\r\n * @example\r\n * import { container } from 'relaxjs';\r\n *\r\n * const service = container.resolve(MyService);\r\n */\r\nexport const container = new ServiceContainer(serviceCollection);"],
5
- "mappings": "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",
6
- "names": ["RelaxError", "message", "context", "handler", "reportError", "message", "context", "error", "RelaxError", "handler", "suppressed", "Inject", "typeOrKey", "_", "context", "instance", "container", "ContainerService", "options", "target", "opts", "serviceCollection", "Registration", "classConstructor", "scope", "inject", "properties", "key", "ServiceCollection", "constructor", "reg", "existing", "error", "reportError", "existingByKey", "service", "ServiceContainer", "serviceCollection", "keyOrType", "key", "registration", "error", "reportError", "inst", "instance", "constructor", "dependencies", "dep", "fieldName", "ServiceCollection", "container"]
4
+ "sourcesContent": ["/**\r\n * Global error handling for Relaxjs.\r\n * Register a handler with `onError()` to intercept errors before they throw.\r\n * Call `ctx.suppress()` in the handler to prevent the error from being thrown.\r\n *\r\n * @example\r\n * import { onError } from 'relaxjs';\r\n *\r\n * onError((error, ctx) => {\r\n * logToService(error.message, error.context);\r\n * showToast(error.message);\r\n * ctx.suppress();\r\n * });\r\n */\r\n\r\n/**\r\n * Passed to error handlers to control error behavior.\r\n * Call `suppress()` to prevent the error from being thrown.\r\n */\r\nexport interface ErrorContext {\r\n suppress(): void;\r\n}\r\n\r\n/**\r\n * Error with structured context for debugging.\r\n * The `context` record contains details like route name, component tag, route data.\r\n *\r\n * @example\r\n * onError((error, ctx) => {\r\n * console.log(error.context.route);\r\n * console.log(error.context.componentTagName);\r\n * });\r\n */\r\nexport class RelaxError extends Error {\r\n constructor(\r\n message: string,\r\n public context: Record<string, unknown>,\r\n ) {\r\n super(message);\r\n }\r\n}\r\n\r\n/** @internal */\r\ntype ErrorHandler = (error: RelaxError, ctx: ErrorContext) => void;\r\n\r\nlet handler: ErrorHandler | null = null;\r\n\r\n/**\r\n * Registers a global error handler for Relaxjs errors.\r\n * The handler receives the error and an `ErrorContext`.\r\n * Call `ctx.suppress()` to prevent the error from being thrown.\r\n * Only one handler can be active at a time; subsequent calls replace the previous handler.\r\n *\r\n * @example\r\n * onError((error, ctx) => {\r\n * if (error.context.route === 'optional-panel') {\r\n * ctx.suppress();\r\n * return;\r\n * }\r\n * showErrorDialog(error.message);\r\n * });\r\n */\r\nexport function onError(fn: ErrorHandler) {\r\n handler = fn;\r\n}\r\n\r\n/**\r\n * Reports an error through the global handler.\r\n * Returns the `RelaxError` if it should be thrown, or `null` if the handler suppressed it.\r\n * The caller is responsible for throwing the returned error.\r\n *\r\n * @param message - Human-readable error description\r\n * @param context - Structured data for debugging (route, component, params, cause, etc.)\r\n * @returns The error to throw, or `null` if suppressed\r\n *\r\n * @example\r\n * const error = reportError('Failed to load route component', {\r\n * route: 'user',\r\n * componentTagName: 'user-profile',\r\n * routeData: { id: 123 },\r\n * });\r\n * if (error) throw error;\r\n */\r\nexport function reportError(message: string, context: Record<string, unknown>): RelaxError | null {\r\n const error = new RelaxError(message, context);\r\n if (handler) {\r\n let suppressed = false;\r\n const ctx: ErrorContext = {\r\n suppress() { suppressed = true; },\r\n };\r\n handler(error, ctx);\r\n if (suppressed) {\r\n return null;\r\n }\r\n }\r\n return error;\r\n}\r\n\r\n/**\r\n * Wraps an async function into a synchronous callback suitable for addEventListener.\r\n * Catches promise rejections and reports them through the global error handler.\r\n *\r\n * @param fn - Async function to wrap\r\n * @returns Synchronous function that can be passed to addEventListener\r\n *\r\n * @example\r\n * button.addEventListener('click', asyncHandler(async (e) => {\r\n * await saveData();\r\n * }));\r\n *\r\n * @example\r\n * form.addEventListener('submit', asyncHandler(async (e) => {\r\n * e.preventDefault();\r\n * await submitForm();\r\n * }));\r\n */\r\nexport function asyncHandler<TArgs extends unknown[]>(\r\n fn: (...args: TArgs) => Promise<void>,\r\n): (...args: TArgs) => void {\r\n return function (this: any, ...args: TArgs) {\r\n fn.call(this, ...args).catch((cause: unknown) => {\r\n const error = reportError('Async callback failed', { cause });\r\n if (error) throw error;\r\n });\r\n };\r\n}\r\n", "import { reportError } from './errors';\r\n\r\n/**\r\n * Generic constructor type used for dependency registration and injection.\r\n * Represents any class constructor that can be used with the DI container.\r\n *\r\n * @template T - The type of object the constructor creates\r\n *\r\n * @example\r\n * // Use with service registration\r\n * class UserService {}\r\n * const ctor: Constructor<UserService> = UserService;\r\n * serviceCollection.registerByType(ctor, { inject: [] });\r\n */\r\nexport type Constructor<T extends object = object> = new (...args: any[]) => T;\r\n\r\n/**\r\n * Controls how service instances are shared across the container hierarchy.\r\n * Used when registering services to define their lifetime behavior.\r\n *\r\n * - `global`: Single instance shared everywhere (singleton pattern)\r\n * - `closest`: New instance per container scope (scoped lifetime)\r\n *\r\n * @example\r\n * // Singleton service - same instance everywhere\r\n * serviceCollection.register(LoggerService, { scope: 'global', inject: [] });\r\n *\r\n * // Scoped service - new instance per scope\r\n * serviceCollection.register(RequestContext, { scope: 'closest', inject: [] });\r\n */\r\nexport type ServiceScope = 'global' | 'closest';\r\n\r\n/**\r\n * Configuration options for registering a service in the DI container.\r\n * Controls identification, lifetime, and dependency resolution.\r\n *\r\n * @example\r\n * // Register with constructor injection\r\n * const options: RegistrationOptions = {\r\n * scope: 'global',\r\n * inject: [DatabaseConnection, ConfigService]\r\n * };\r\n * serviceCollection.register(UserRepository, options);\r\n *\r\n * @example\r\n * // Register with property injection\r\n * const options: RegistrationOptions = {\r\n * inject: [],\r\n * properties: { logger: Logger, config: 'appConfig' }\r\n * };\r\n *\r\n * @example\r\n * // Register with a pre-created instance\r\n * const options: RegistrationOptions = {\r\n * inject: [],\r\n * instance: existingService\r\n * };\r\n */\r\nexport interface RegistrationOptions {\r\n /** Service lifetime - 'global' for singleton, 'closest' for scoped */\r\n scope?: ServiceScope;\r\n /** Optional string key for resolving by name instead of type */\r\n key?: string;\r\n /** Pre-existing instance to use instead of creating new one */\r\n instance?: unknown;\r\n /** Types or keys for constructor parameters, in order */\r\n inject: (string | Constructor)[];\r\n /** Map of property names to their injection types/keys */\r\n properties?: Record<string, string | Constructor>;\r\n}\r\n\r\n/**\r\n * Field decorator that injects a service from the global DI container.\r\n * The service is resolved when the class instance is created (not at class definition time),\r\n * so services must be registered before the first instance is created.\r\n *\r\n * Works with web components regardless of how they are created:\r\n * - By the browser (HTML parsing): services are resolved during construction\r\n * - By application code (`document.createElement` or `new`): same behavior\r\n * - Injected fields are available in `connectedCallback` and all lifecycle methods\r\n *\r\n * @example\r\n * // Using `@Inject` in a web component\r\n * class UserPanel extends HTMLElement {\r\n * @Inject(UserService)\r\n * private userService!: UserService;\r\n *\r\n * connectedCallback() {\r\n * // userService is already resolved and ready to use\r\n * const user = this.userService.getCurrentUser();\r\n * this.render(user);\r\n * }\r\n * }\r\n *\r\n * @example\r\n * // Services must be registered before components are created.\r\n * // In your app entry point (e.g. main.ts):\r\n * serviceCollection.registerByType(UserService, { inject: [ApiClient] });\r\n * serviceCollection.registerByType(ApiClient, { inject: [] });\r\n *\r\n * // Now components can be created (by browser or code)\r\n * customElements.define('user-panel', UserPanel);\r\n */\r\nexport function Inject<T extends object>(typeOrKey: Constructor<T> | string) {\r\n return (_: undefined, context: ClassFieldDecoratorContext) => {\r\n return function(this: any) {\r\n return container.resolve(typeOrKey);\r\n };\r\n };\r\n}\r\n\r\n// Temporary collector of property injections - cleared after registration\r\n//const propertyCollector = new WeakMap<object, Record<string, string>>();\r\n\r\n/**\r\n * Class decorator that registers a service in the global DI container.\r\n * Registration happens at class definition time (when the module loads),\r\n * so import the module before creating instances that depend on this service.\r\n *\r\n * For web components: use `@ContainerService` on services, not on the\r\n * components themselves. Components use `@Inject` to consume services.\r\n *\r\n * @param options - Registration configuration including scope and dependencies\r\n *\r\n * @example\r\n * // Register a service that components can inject\r\n * @ContainerService({ inject: [ApiClient] })\r\n * class UserService {\r\n * constructor(private api: ApiClient) {}\r\n * getCurrentUser() { return this.api.get('/user'); }\r\n * }\r\n *\r\n * // Component consumes the service\r\n * class UserPanel extends HTMLElement {\r\n * @Inject(UserService)\r\n * private userService!: UserService;\r\n * }\r\n *\r\n * @example\r\n * // Service with custom key for named resolution\r\n * @ContainerService({ key: 'primaryCache', scope: 'global', inject: [] })\r\n * class CacheService {}\r\n *\r\n * // Later resolve by key\r\n * const cache = container.resolve('primaryCache');\r\n */\r\nexport function ContainerService<T extends object>(\r\n options?: RegistrationOptions\r\n) {\r\n return (target: Constructor<T>) => {\r\n const opts = options ?? {inject: []};\r\n\r\n if (opts.key) {\r\n serviceCollection.register(target, opts);\r\n } else {\r\n serviceCollection.registerByType(target, opts);\r\n }\r\n };\r\n}\r\n\r\n/**\r\n * Internal class representing a registered service's metadata.\r\n * Holds all information needed to create and configure service instances.\r\n *\r\n * @internal This is an implementation detail and should not be used directly.\r\n */\r\nclass Registration {\r\n /**\r\n * Creates a new registration record.\r\n *\r\n * @param classConstructor - The class constructor function\r\n * @param scope - Instance sharing behavior\r\n * @param inject - Constructor parameter dependencies\r\n * @param properties - Property injection mappings\r\n * @param key - Optional string identifier\r\n * @param instance - Optional pre-created instance\r\n */\r\n constructor(\r\n public classConstructor: Constructor,\r\n public scope: ServiceScope,\r\n public inject: (string | Constructor)[],\r\n public properties: Record<string, string | Constructor> = {},\r\n public key?: string,\r\n public instance?: unknown\r\n ) {}\r\n}\r\n\r\n/**\r\n * Registry that stores service registration metadata.\r\n * Use this to register services before they can be resolved by a ServiceContainer.\r\n *\r\n * Typically you'll use the global `serviceCollection` instance rather than creating your own.\r\n *\r\n * @example\r\n * // Register a service by type\r\n * serviceCollection.registerByType(LoggerService, { inject: [] });\r\n *\r\n * // Register with a string key\r\n * serviceCollection.register(CacheService, { key: 'cache', inject: [] });\r\n *\r\n * // Check if service is registered\r\n * const reg = serviceCollection.tryGet(LoggerService);\r\n * if (reg) {\r\n * console.log('Logger is registered');\r\n * }\r\n */\r\nexport class ServiceCollection {\r\n private servicesByKey = new Map<string, Registration>();\r\n private servicesByClassName = new Map<string, Registration>();\r\n\r\n /**\r\n * Registers a service with full configuration options.\r\n * The service will be resolvable by both its class name and optional key.\r\n *\r\n * @param constructor - The service class constructor\r\n * @param options - Registration configuration\r\n */\r\n register<T extends object>(constructor: Constructor<T>, options: RegistrationOptions): void {\r\n this.validateRegistration(constructor, options);\r\n\r\n const reg = new Registration(\r\n constructor,\r\n options.scope ?? 'global',\r\n options.inject,\r\n options.properties ?? {},\r\n options.key,\r\n options.instance\r\n );\r\n\r\n if (options.key) {\r\n this.servicesByKey.set(options.key, reg);\r\n }\r\n this.servicesByClassName.set(constructor.name, reg);\r\n }\r\n\r\n /**\r\n * Registers a service by its class type.\r\n * The service will be resolvable by its class constructor.\r\n *\r\n * @param constructor - The service class constructor\r\n * @param options - Optional registration configuration\r\n */\r\n registerByType<T extends object>(\r\n constructor: Constructor<T>,\r\n options?: RegistrationOptions\r\n ): void {\r\n this.checkNameCollision(constructor);\r\n if (options) this.validateRegistration(constructor, options);\r\n\r\n const reg = new Registration(constructor, options?.scope, options?.inject ?? [], options?.properties, options?.key, options?.instance);\r\n if (options?.key) {\r\n this.servicesByKey.set(options.key, reg);\r\n }\r\n this.servicesByClassName.set(constructor.name, reg);\r\n }\r\n\r\n private checkNameCollision<T extends object>(constructor: Constructor<T>): void {\r\n const existing = this.servicesByClassName.get(constructor.name);\r\n if (existing && existing.classConstructor !== constructor) {\r\n const error = reportError('Service name collision: different class registered with same name', {\r\n service: constructor.name,\r\n });\r\n if (error) throw error;\r\n }\r\n }\r\n\r\n private validateRegistration<T extends object>(constructor: Constructor<T>, options: RegistrationOptions): void {\r\n this.checkNameCollision(constructor);\r\n\r\n if (options.key) {\r\n const existingByKey = this.servicesByKey.get(options.key);\r\n if (existingByKey && existingByKey.classConstructor !== constructor) {\r\n const error = reportError('Service key already registered to a different class', {\r\n key: options.key,\r\n existingClass: existingByKey.classConstructor.name,\r\n newClass: constructor.name,\r\n });\r\n if (error) throw error;\r\n }\r\n }\r\n\r\n if (options.instance && options.inject.length > 0) {\r\n const error = reportError('Service has both instance and inject (inject will be ignored)', {\r\n service: constructor.name,\r\n });\r\n if (error) throw error;\r\n }\r\n }\r\n\r\n /**\r\n * Attempts to retrieve a service registration.\r\n * Returns undefined if the service is not registered.\r\n *\r\n * @param key - Either a string key or class constructor\r\n * @returns The registration or undefined\r\n */\r\n tryGet<T extends object>(key: string | Constructor<T>): Registration | undefined {\r\n if (typeof key === 'string') {\r\n return this.servicesByKey.get(key);\r\n }\r\n return this.servicesByClassName.get(key.name);\r\n }\r\n\r\n /**\r\n * Retrieves a service registration or throws if not found.\r\n *\r\n * @param key - Either a string key or class constructor\r\n * @returns The registration\r\n * @throws Error if the service is not registered\r\n */\r\n get<T extends object>(key: string | Constructor<T>): Registration {\r\n const reg = this.tryGet(key);\r\n if (!reg) {\r\n const service = typeof key === 'string' ? key : key.name;\r\n const error = reportError(`Failed to resolve service '${service}'`, {\r\n service,\r\n registeredTypes: Array.from(this.servicesByClassName.keys()),\r\n registeredKeys: Array.from(this.servicesByKey.keys()),\r\n });\r\n if (error) throw error;\r\n }\r\n return reg!;\r\n }\r\n}\r\n\r\n/**\r\n * Internal storage for tracking injected fields during service resolution.\r\n * @internal\r\n */\r\nconst injectedFields = new WeakMap<object, Map<string, string>>();\r\n\r\n/**\r\n * IoC container that resolves and manages service instances.\r\n * Creates instances based on registrations in a ServiceCollection,\r\n * handling constructor injection, property injection, and lifetime management.\r\n *\r\n * Typically you'll use the global `container` instance rather than creating your own.\r\n *\r\n * @example\r\n * // Resolve a service by class\r\n * const logger = container.resolve(LoggerService);\r\n *\r\n * // Resolve by string key\r\n * const cache = container.resolve<CacheService>('primaryCache');\r\n *\r\n * @example\r\n * // Full setup workflow\r\n * serviceCollection.register(UserService, {\r\n * inject: [DatabaseConnection],\r\n * scope: 'global'\r\n * });\r\n *\r\n * const userService = container.resolve(UserService);\r\n */\r\nexport class ServiceContainer {\r\n private instances = new Map<string, any>();\r\n\r\n /**\r\n * Creates a new container backed by the given service collection.\r\n *\r\n * @param serviceCollection - The registry containing service registrations\r\n */\r\n constructor(private serviceCollection: ServiceCollection) {}\r\n\r\n /**\r\n * Resolves a service instance by class type or string key.\r\n * Creates the instance if not already cached (for global scope).\r\n * Handles constructor and property injection automatically.\r\n *\r\n * @param keyOrType - Either a string key or class constructor\r\n * @returns The resolved service instance\r\n * @throws Error if the service is not registered\r\n *\r\n * @example\r\n * const service = container.resolve(MyService);\r\n */\r\n resolve<T extends object>(keyOrType: string | Constructor<T>): T {\r\n const key = typeof keyOrType === 'string' ? keyOrType : keyOrType.name;\r\n\r\n if (this.instances.has(key)) {\r\n return this.instances.get(key);\r\n }\r\n\r\n const registration = this.serviceCollection.get(keyOrType);\r\n if (!registration) {\r\n const error = reportError(`Failed to resolve service '${key}'`, { service: key });\r\n if (error) throw error;\r\n return undefined as unknown as T;\r\n }\r\n\r\n if (registration.instance) {\r\n const inst = registration.instance as T;\r\n this.injectFields(inst, registration);\r\n this.instances.set(key, inst);\r\n return inst;\r\n }\r\n\r\n const instance = this.createInstance<T>(registration);\r\n if (registration.scope === 'global') {\r\n this.instances.set(key, instance);\r\n }\r\n this.injectFields(instance, registration);\r\n\r\n return instance;\r\n }\r\n\r\n /**\r\n * Creates a new instance of a service, resolving all constructor dependencies.\r\n */\r\n private createInstance<T extends object>(registration: Registration): T {\r\n const constructor = registration.classConstructor as Constructor<T>;\r\n\r\n const dependencies = registration.inject.map(dep => this.resolve(dep));\r\n return new constructor(...dependencies);\r\n }\r\n\r\n /**\r\n * Injects dependencies into instance properties based on registration config.\r\n */\r\n private injectFields<T extends object>(instance: T, registration: Registration): void {\r\n for (const [fieldName, keyOrType] of Object.entries(registration.properties)) {\r\n (instance as any)[fieldName] = this.resolve(keyOrType);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Global service collection instance for registering services.\r\n * Use this to register services that can later be resolved by the container.\r\n *\r\n * @example\r\n * import { serviceCollection } from 'relaxjs';\r\n *\r\n * serviceCollection.register(MyService, { inject: [Dependency] });\r\n */\r\nexport const serviceCollection = new ServiceCollection();\r\n\r\n/**\r\n * Global service container instance for resolving dependencies.\r\n * Use this to obtain service instances with all dependencies injected.\r\n *\r\n * @example\r\n * import { container } from 'relaxjs';\r\n *\r\n * const service = container.resolve(MyService);\r\n */\r\nexport const container = new ServiceContainer(serviceCollection);"],
5
+ "mappings": "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",
6
+ "names": ["RelaxError", "message", "context", "handler", "reportError", "message", "context", "error", "RelaxError", "handler", "suppressed", "Inject", "typeOrKey", "_", "context", "container", "ContainerService", "options", "target", "opts", "serviceCollection", "Registration", "classConstructor", "scope", "inject", "properties", "key", "instance", "ServiceCollection", "constructor", "reg", "existing", "error", "reportError", "existingByKey", "service", "ServiceContainer", "serviceCollection", "keyOrType", "key", "registration", "error", "reportError", "inst", "instance", "constructor", "dependencies", "dep", "fieldName", "ServiceCollection", "container"]
7
7
  }
package/dist/errors.d.ts CHANGED
@@ -33,6 +33,7 @@ export declare class RelaxError extends Error {
33
33
  context: Record<string, unknown>;
34
34
  constructor(message: string, context: Record<string, unknown>);
35
35
  }
36
+ /** @internal */
36
37
  type ErrorHandler = (error: RelaxError, ctx: ErrorContext) => void;
37
38
  /**
38
39
  * Registers a global error handler for Relaxjs errors.
@@ -68,4 +69,23 @@ export declare function onError(fn: ErrorHandler): void;
68
69
  * if (error) throw error;
69
70
  */
70
71
  export declare function reportError(message: string, context: Record<string, unknown>): RelaxError | null;
72
+ /**
73
+ * Wraps an async function into a synchronous callback suitable for addEventListener.
74
+ * Catches promise rejections and reports them through the global error handler.
75
+ *
76
+ * @param fn - Async function to wrap
77
+ * @returns Synchronous function that can be passed to addEventListener
78
+ *
79
+ * @example
80
+ * button.addEventListener('click', asyncHandler(async (e) => {
81
+ * await saveData();
82
+ * }));
83
+ *
84
+ * @example
85
+ * form.addEventListener('submit', asyncHandler(async (e) => {
86
+ * e.preventDefault();
87
+ * await submitForm();
88
+ * }));
89
+ */
90
+ export declare function asyncHandler<TArgs extends unknown[]>(fn: (...args: TArgs) => Promise<void>): (...args: TArgs) => void;
71
91
  export {};
@@ -1,22 +1,3 @@
1
- /**
2
- * @module FormValidator
3
- * Form validation with support for native HTML5 validation and error summaries.
4
- * Provides automatic validation on submit with customizable behavior.
5
- *
6
- * @example
7
- * // Basic usage with submit callback
8
- * const form = document.querySelector('form');
9
- * const validator = new FormValidator(form, {
10
- * submitCallback: () => saveData()
11
- * });
12
- *
13
- * @example
14
- * // With auto-validation on input
15
- * const validator = new FormValidator(form, {
16
- * autoValidate: true,
17
- * useSummary: true
18
- * });
19
- */
20
1
  /**
21
2
  * Configuration options for FormValidator.
22
3
  */
@@ -32,7 +13,7 @@ export interface ValidatorOptions {
32
13
  /** Prevent default on validation failure (default: true) */
33
14
  preventDefaultOnFailed?: boolean;
34
15
  /** Callback invoked when form passes validation */
35
- submitCallback?: () => void;
16
+ submitCallback?: () => void | Promise<void>;
36
17
  }
37
18
  /**
38
19
  * Form validation helper that integrates with HTML5 validation.
@@ -26,6 +26,7 @@ export interface ValidationContext {
26
26
  }
27
27
  /**
28
28
  * Interface for custom validators.
29
+ * @internal
29
30
  */
30
31
  interface Validator {
31
32
  /**
@@ -35,6 +36,7 @@ interface Validator {
35
36
  */
36
37
  validate(value: string, context: ValidationContext): void;
37
38
  }
39
+ /** @internal */
38
40
  interface ValidatorRegistryEntry {
39
41
  validator: {
40
42
  new (): Validator;
@@ -1,2 +1,2 @@
1
- var ee=Object.create;var g=Object.defineProperty;var te=Object.getOwnPropertyDescriptor;var Le=Object.getOwnPropertyNames;var ke=Object.getPrototypeOf,De=Object.prototype.hasOwnProperty;var re=(t,e)=>(e=Symbol[t])?e:Symbol.for("Symbol."+t),T=t=>{throw TypeError(t)};var Ne=(t,e,r)=>e in t?g(t,e,{enumerable:!0,configurable:!0,writable:!0,value:r}):t[e]=r;var Z=(t,e)=>g(t,"name",{value:e,configurable:!0});var ne=t=>e=>{var r=t[e];if(r)return r();throw new Error("Module not found in bundle: "+e)};var y=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports),Ae=(t,e)=>{for(var r in e)g(t,r,{get:e[r],enumerable:!0})},oe=(t,e,r,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of Le(e))!De.call(t,o)&&o!==r&&g(t,o,{get:()=>e[o],enumerable:!(n=te(e,o))||n.enumerable});return t};var h=(t,e,r)=>(r=t!=null?ee(ke(t)):{},oe(e||!t||!t.__esModule?g(r,"default",{value:t,enumerable:!0}):r,t)),He=t=>oe(g({},"__esModule",{value:!0}),t);var C=t=>[,,,ee(t?.[re("metadata")]??null)],ae=["class","method","getter","setter","accessor","field","value","get","set"],w=t=>t!==void 0&&typeof t!="function"?T("Function expected"):t,Re=(t,e,r,n,o)=>({kind:ae[t],name:e,metadata:n,addInitializer:a=>r._?T("Already initialized"):o.push(w(a||null))}),$e=(t,e)=>Ne(e,re("metadata"),t[3]),S=(t,e,r,n)=>{for(var o=0,a=t[e>>1],i=a&&a.length;o<i;o++)e&1?a[o].call(r):n=a[o].call(r,n);return n},L=(t,e,r,n,o,a)=>{var i,s,u,c,p,l=e&7,v=!!(e&8),f=!!(e&16),V=l>3?t.length+1:l?v?1:2:0,Q=ae[l+5],X=l>3&&(t[V-1]=[]),Se=t[V]||(t[V]=[]),m=l&&(!f&&!v&&(o=o.prototype),l<5&&(l>3||!f)&&te(l<4?o:{get[r](){return K(this,a)},set[r](d){return _(this,a,d)}},r));l?f&&l<4&&Z(a,(l>2?"set ":l>1?"get ":"")+r):Z(o,r);for(var I=n.length-1;I>=0;I--)c=Re(l,r,u={},t[3],Se),l&&(c.static=v,c.private=f,p=c.access={has:f?d=>Oe(o,d):d=>r in d},l^3&&(p.get=f?d=>(l^1?K:Ve)(d,o,l^4?a:m.get):d=>d[r]),l>2&&(p.set=f?(d,P)=>_(d,o,P,l^4?a:m.set):(d,P)=>d[r]=P)),s=(0,n[I])(l?l<4?f?a:m[Q]:l>4?void 0:{get:m.get,set:m.set}:o,c),u._=1,l^4||s===void 0?w(s)&&(l>4?X.unshift(s):l?f?a=s:m[Q]=s:o=s):typeof s!="object"||s===null?T("Object expected"):(w(i=s.get)&&(m.get=i),w(i=s.set)&&(m.set=i),w(i=s.init)&&X.unshift(i));return l||$e(t,o),m&&g(o,r,m),f?l^4?a:m:o};var q=(t,e,r)=>e.has(t)||T("Cannot "+r),Oe=(t,e)=>Object(e)!==e?T('Cannot use the "in" operator on this value'):t.has(e),K=(t,e,r)=>(q(t,e,"read from private field"),r?r.call(t):e.get(t));var _=(t,e,r,n)=>(q(t,e,"write to private field"),n?n.call(t,r):e.set(t,r),r),Ve=(t,e,r)=>(q(t,e,"access private method"),r);var le=y((tt,qe)=>{qe.exports={greeting:"Hello, {name}!",items:"{count, plural, one {# item} other {# items}}"}});var ce=y((rt,je)=>{je.exports={today:"today",yesterday:"yesterday",daysAgo:"{count, plural, one {# day ago} other {# days ago}}",pieces:"{count, plural, =0 {none} one {one} other {# pcs}}"}});var ue=y((nt,Be)=>{Be.exports={required:"This field is required.",range:"Number must be between {min} and {max}, was {actual}.",digits:"Please enter only digits."}});var de=y((ot,Ue)=>{Ue.exports={greeting:"Hej, {name}!",items:"{count, plural, one {# sak} other {# saker}}"}});var me=y((at,We)=>{We.exports={today:"idag",yesterday:"ig\xE5r",daysAgo:"{count, plural, one {# dag sedan} other {# dagar sedan}}",pieces:"{count, plural, =0 {inga} one {en} other {# st}}"}});var fe=y((it,ze)=>{ze.exports={required:"Detta f\xE4lt \xE4r obligatoriskt.",range:"Talet m\xE5ste vara mellan {min} och {max}, var {actual}.",digits:"Ange endast siffror."}});var Ke={};Ae(Ke,{BooleanConverter:()=>F,DateConverter:()=>A,DigitsValidation:()=>O,FormValidator:()=>H,NumberConverter:()=>N,RangeValidation:()=>$,RegisterValidator:()=>M,RequiredValidation:()=>R,createConverterFromDataType:()=>W,createConverterFromInputType:()=>z,getDataConverter:()=>U,getValidator:()=>Ce,mapFormToClass:()=>ye,readData:()=>he,setFormData:()=>xe});module.exports=He(Ke);var j=new Map;function Ie(t){return j.has(t)||j.set(t,new Intl.PluralRules(t)),j.get(t)}function ie(t){return t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function Pe(t,e,r="en"){return t.replace(/\{(\w+)(?:, (plural|select),((?:[^{}]*\{[^{}]*\})+))?\}/g,(n,o,a,i)=>{let s=e[o];if(a==="plural"){let u=new RegExp(`=${ie(String(s))}\\s*\\{([^{}]*)\\}`).exec(i);if(u)return u[1].replace(`{${o}}`,String(s)).replace("#",String(s));let p=Ie(r).select(s),l=new RegExp(`${p}\\s*\\{([^{}]*)\\}`).exec(i)||new RegExp("other\\s*\\{([^{}]*)\\}").exec(i);return l?l[1].replace(`{${o}}`,String(s)).replace("#",String(s)):String(s)}if(a==="select"){let u=ie(String(s)),c=new RegExp(`\\b${u}\\s*\\{([^{}]*)\\}`).exec(i)||new RegExp("\\bother\\s*\\{([^{}]*)\\}").exec(i);return c?c[1]:String(s)}return s!==void 0?String(s):`{${o}}`})}var se=Pe;var ct=ne({"./locales/en/r-common.json":()=>Promise.resolve().then(()=>h(le())),"./locales/en/r-pipes.json":()=>Promise.resolve().then(()=>h(ce())),"./locales/en/r-validation.json":()=>Promise.resolve().then(()=>h(ue())),"./locales/sv/r-common.json":()=>Promise.resolve().then(()=>h(de())),"./locales/sv/r-pipes.json":()=>Promise.resolve().then(()=>h(me())),"./locales/sv/r-validation.json":()=>Promise.resolve().then(()=>h(fe()))});var Ye="en",B=Ye;var Ge={},pe=null;function k(t,e){let[r,n]=t.includes(":")?t.split(":"):["r-common",t],o=Ge[r]?.[n];if(!o)return pe&&pe(n,r,B),t;try{return se(o,e,B)}catch{return t}}function ge(){return B}function ye(t,e,r={}){let n=t.querySelectorAll("input, select, textarea");if(n.forEach(o=>{if(!o.hasAttribute("name")||D(o,"disabled"))return;let a=o.getAttribute("name");if(!(a in e)){if(r.throwOnMissingProperty)throw new Error(`Form field "${a}" has no matching property in class instance`);return}let i=Qe(o);i!==be&&(e[a]=i)}),r.throwOnMissingField){let o=new Set;n.forEach(a=>{a.hasAttribute("name")&&o.add(a.getAttribute("name"))});for(let a in e)if(typeof e[a]!="function"&&Object.prototype.hasOwnProperty.call(e,a)&&!o.has(a))throw new Error(`Class property "${a}" has no matching form field`)}return e}function U(t){let e=t.getAttribute("data-type");return e?W(e):t instanceof HTMLInputElement?z(t.type):"checked"in t&&typeof t.checked=="boolean"?F:r=>r}function he(t){let e={},r=new FormData(t),n=new Set;r.forEach((o,a)=>{if(n.has(a))return;n.add(a);let i=r.getAll(a),s=t.elements.namedItem(a),u=s?U(s):c=>c;if(i.length===1){let c=i[0];e[a]=typeof c=="string"?u(c):c}else e[a]=i.map(c=>typeof c=="string"?u(c):c)});for(let o=0;o<t.elements.length;o++){let a=t.elements[o];a.type==="checkbox"&&a.name&&!n.has(a.name)&&(n.add(a.name),e[a.name]=!1)}return e}function F(t){if(!t||t=="")return;let e=t.toLowerCase();if(e==="true"||e==="on"||Number(t)>0)return!0;if(e==="false"||e==="off"||Number(t)<=0)return!1;throw new Error("Could not convert value '"+t+"' to boolean.")}function N(t){if(!t||t=="")return;let e=Number(t);if(!isNaN(e))return e;throw new Error("Could not convert value '"+t+"' to number.")}function Je(t){return new Intl.DateTimeFormat(t).formatToParts(new Date(2024,0,15)).filter(r=>r.type==="day"||r.type==="month"||r.type==="year").map(r=>r.type)}function A(t){if(!t||t==="")return;if(/^\d{4}-\d{2}-\d{2}(T|$)/.test(t)){let n=new Date(t);if(!isNaN(n.getTime()))return n}let e=t.split(/[\/.\-\s]/);if(e.length>=3&&e.every(n=>/^\d+$/.test(n))){let n=ge(),o=Je(n),a={};if(o.forEach((i,s)=>{a[i]=parseInt(e[s],10)}),a.year!==void 0&&a.month!==void 0&&a.day!==void 0){a.year<100&&(a.year+=2e3);let i=new Date(a.year,a.month-1,a.day);if(!isNaN(i.getTime()))return i}}let r=new Date(t);if(isNaN(r.getTime()))throw new Error("Invalid date format");return r}function W(t){switch(t){case"boolean":return F;case"number":return N;case"Date":return A;case"string":return e=>!e||e==""?void 0:e;default:throw new Error(`Unknown data-type "${t}".`)}}function z(t){switch(t){case"checkbox":return F;case"number":return N;case"date":case"datetime-local":return A;case"month":return e=>{let[r,n]=e.split("-").map(Number);return new Date(r,n-1)};case"week":return e=>{let[r,n]=e.split("-W").map(Number);return{year:r,week:n}};case"time":return e=>{let[r,n,o=0]=e.split(":").map(Number);return{hours:r,minutes:n,seconds:o}};default:return e=>!e||e==""?void 0:e}}function D(t,e){let r=t;if(e in r&&typeof r[e]=="boolean")return r[e];let n=t.getAttribute(e);return n===null?!1:n===""||n.toLowerCase()==="true"||n.toLowerCase()===e}var be=Symbol("skip");function Qe(t){let e=t,r=e.type||t.getAttribute("type")||"";if(r==="checkbox")return D(t,"checked");if(r==="radio")return D(t,"checked")?e.value:be;if(r==="number")return e.value?Number(e.value):null;if(r==="date")return e.value?new Date(e.value):null;if("selectedOptions"in e&&D(t,"multiple"))return Array.from(e.selectedOptions).map(n=>n.value);if("value"in e)return e.value}function Xe(t){let e=t.getAttribute("id");if(e){let r=t.closest("form");if(r){let n=r.querySelector(`label[for="${e}"]`);if(n)return n.textContent?.trim()||null}}return null}var H=class{constructor(e,r){this.form=e;this.options=r;if(!this.form)throw new Error("Form must be specified.");this.form.addEventListener("submit",n=>{(r?.preventDefault||this.options?.submitCallback!=null)&&n.preventDefault(),this.options?.customChecks&&this.options.customChecks(e),this.validateForm()?this.options?.submitCallback?.apply(this):r?.preventDefaultOnFailed!==!1&&n.preventDefault()}),r?.autoValidate&&e.addEventListener("input",()=>{this.validateForm()})}validateForm(){let e=Array.from(this.form.querySelectorAll("input,textarea,select")),r=!0;if(this.options?.useSummary!==!0)return this.form.checkValidity()?!0:(this.form.reportValidity(),this.focusFirstErrorElement(),!1);let n=[];return e.forEach(o=>{if(!o.checkValidity()){r=!1;let a=Xe.call(this,o)||o.name||"Unnamed Field";n.push(`${a}: ${o.validationMessage}`)}}),r?this.clearErrorSummary():(this.displayErrorSummary(n),this.focusFirstErrorElement()),r}displayErrorSummary(e){this.clearErrorSummary(),this.errorSummary||this.createErrorSummary();let r=this.errorSummary.querySelector("ul");e.forEach(n=>{let o=document.createElement("li");o.textContent=n,r.appendChild(o)})}createErrorSummary(){let e=document.createElement("div");e.className="error-summary",e.style.color="red",e.setAttribute("role","alert"),e.setAttribute("aria-live","assertive"),e.setAttribute("aria-atomic","true"),this.errorSummary=e;let r=document.createElement("ul");this.errorSummary.appendChild(r),this.form.prepend(e)}addErrorToSummary(e,r){this.errorSummary||this.createErrorSummary();let n=this.errorSummary.querySelector("ul"),o=document.createElement("li");o.textContent=`${e}: ${r}`,n.appendChild(o)}clearErrorSummary(){this.errorSummary&&(this.errorSummary.querySelector("ul").innerHTML="")}focusFirstErrorElement(){let e=this.form.querySelector(":invalid");e instanceof HTMLElement&&document.activeElement!==e&&e.focus()}static FindForm(e){if(e.parentElement?.tagName=="FORM")return e.parentElement;for(let r=0;r<e.children.length;r++){let n=e.children[r];if(n.tagName=="FORM")return n}throw new Error("Parent or a direct child must be a FORM for class "+e.constructor.name)}};function xe(t,e){t.querySelectorAll("[name]").forEach(n=>{let o=n.getAttribute("name");if(!o)return;if(o.endsWith("[]")){let i=o.slice(0,-2),s=Ee(e,i);if(Array.isArray(s)){let u=n,c=u.type||n.getAttribute("type")||"";if(c==="checkbox"||c==="radio")u.checked=s.includes(u.value);else if("options"in u&&ve(n,"multiple"))s.forEach(p=>{let l=Array.from(u.options).find(v=>v.value===String(p));l&&(l.selected=!0)});else if("value"in u){let p=t.querySelectorAll(`[name="${o}"]`),l=Array.from(p).indexOf(n);l>=0&&l<s.length&&(u.value=String(s[l]))}}return}let a=Ee(e,o);a!=null&&Ze(n,a)})}function Ee(t,e){let r=[],n="",o=!1;for(let a=0;a<e.length;a++){let i=e[a];i==="["&&!o?(n&&(r.push(n),n=""),o=!0,n+=i):i==="]"&&o?(n+=i,r.push(n),n="",o=!1):i==="."&&!o?n&&(r.push(n),n=""):n+=i}return n&&r.push(n),r.reduce((a,i)=>{if(!(!a||typeof a!="object")){if(i.startsWith("[")&&i.endsWith("]")){let s=i.slice(1,-1);return a[s]}return a[i]}},t)}function Ze(t,e){let r=t,n=r.type||t.getAttribute("type")||"";if(n==="checkbox")r.checked=!!e;else if(n==="radio")r.checked=r.value===String(e);else if(n==="date"&&e instanceof Date)r.value=e.toISOString().split("T")[0];else if(n==="datetime-local"&&e instanceof Date){let o=a=>String(a).padStart(2,"0");r.value=`${e.getFullYear()}-${o(e.getMonth()+1)}-${o(e.getDate())}T${o(e.getHours())}:${o(e.getMinutes())}`}else if("options"in r&&ve(t,"multiple")&&Array.isArray(e)){let o=Array.from(r.options),a=e.map(String);o.forEach(i=>{i.selected=a.includes(i.value)})}else"value"in r&&(r.value=String(e))}function ve(t,e){let r=t;if(e in r&&typeof r[e]=="boolean")return r[e];let n=t.getAttribute(e);return n===null?!1:n===""||n.toLowerCase()==="true"||n.toLowerCase()===e}var Me=new Map;function M(t,e=[]){return function(r){Me.set(t,{validator:r,validInputTypes:e})}}function Ce(t){return Me.get(t)}var we,Y;we=[M("required")];var b=class b{static create(e){return e==="required"?new b:null}validate(e,r){e.trim()===""&&r.addError(this.getMessage())}getMessage(){return k("r-validation:required")}};Y=C(null),b=L(Y,0,"RequiredValidation",we,b),S(Y,1,b);var R=b,Te,G;Te=[M("range",["number"])];var E=class E{constructor(e,r){this.min=void 0;this.max=void 0;this.min=e,this.max=r}static create(e){let r=e.match(/^range\((-?\d+(?:\.\d+)?)-(-?\d+(?:\.\d+)?)\)$/);if(r){let[,n,o]=r;return new E(parseFloat(n),parseFloat(o))}return null}validate(e,r){if(e.trim()==="")return;let n=parseFloat(e);!isNaN(n)&&n>=this.min&&n<=this.max||r.addError(this.getMessage(e))}getMessage(e){return k("r-validation:range",{min:this.min,max:this.max,actual:e})}};G=C(null),E=L(G,0,"RangeValidation",Te,E),S(G,1,E);var $=E,Fe,J;Fe=[M("digits",["number"])];var x=class x{static create(e){return e==="digits"?new x:null}validate(e,r){/^\d+$/.test(e)||r.addError(this.getMessage())}getMessage(){return k("r-validation:digits")}};J=C(null),x=L(J,0,"DigitsValidation",Fe,x),S(J,1,x);var O=x;
1
+ var re=Object.create;var g=Object.defineProperty;var ne=Object.getOwnPropertyDescriptor;var He=Object.getOwnPropertyNames;var De=Object.getPrototypeOf,Ne=Object.prototype.hasOwnProperty;var oe=(t,e)=>(e=Symbol[t])?e:Symbol.for("Symbol."+t),T=t=>{throw TypeError(t)};var Re=(t,e,r)=>e in t?g(t,e,{enumerable:!0,configurable:!0,writable:!0,value:r}):t[e]=r;var _=(t,e)=>g(t,"name",{value:e,configurable:!0});var ae=t=>e=>{var r=t[e];if(r)return r();throw new Error("Module not found in bundle: "+e)};var y=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports),$e=(t,e)=>{for(var r in e)g(t,r,{get:e[r],enumerable:!0})},ie=(t,e,r,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of He(e))!Ne.call(t,o)&&o!==r&&g(t,o,{get:()=>e[o],enumerable:!(n=ne(e,o))||n.enumerable});return t};var h=(t,e,r)=>(r=t!=null?re(De(t)):{},ie(e||!t||!t.__esModule?g(r,"default",{value:t,enumerable:!0}):r,t)),Oe=t=>ie(g({},"__esModule",{value:!0}),t);var M=t=>[,,,re(t?.[oe("metadata")]??null)],se=["class","method","getter","setter","accessor","field","value","get","set"],v=t=>t!==void 0&&typeof t!="function"?T("Function expected"):t,Ve=(t,e,r,n,o)=>({kind:se[t],name:e,metadata:n,addInitializer:a=>r._?T("Already initialized"):o.push(v(a||null))}),Ie=(t,e)=>Re(e,oe("metadata"),t[3]),S=(t,e,r,n)=>{for(var o=0,a=t[e>>1],i=a&&a.length;o<i;o++)e&1?a[o].call(r):n=a[o].call(r,n);return n},L=(t,e,r,n,o,a)=>{var i,s,u,c,p,l=e&7,w=!!(e&8),f=!!(e&16),V=l>3?t.length+1:l?w?1:2:0,Z=se[l+5],K=l>3&&(t[V-1]=[]),Ae=t[V]||(t[V]=[]),m=l&&(!f&&!w&&(o=o.prototype),l<5&&(l>3||!f)&&ne(l<4?o:{get[r](){return ee(this,a)},set[r](d){return te(this,a,d)}},r));l?f&&l<4&&_(a,(l>2?"set ":l>1?"get ":"")+r):_(o,r);for(var I=n.length-1;I>=0;I--)c=Ve(l,r,u={},t[3],Ae),l&&(c.static=w,c.private=f,p=c.access={has:f?d=>Pe(o,d):d=>r in d},l^3&&(p.get=f?d=>(l^1?ee:qe)(d,o,l^4?a:m.get):d=>d[r]),l>2&&(p.set=f?(d,P)=>te(d,o,P,l^4?a:m.set):(d,P)=>d[r]=P)),s=(0,n[I])(l?l<4?f?a:m[Z]:l>4?void 0:{get:m.get,set:m.set}:o,c),u._=1,l^4||s===void 0?v(s)&&(l>4?K.unshift(s):l?f?a=s:m[Z]=s:o=s):typeof s!="object"||s===null?T("Object expected"):(v(i=s.get)&&(m.get=i),v(i=s.set)&&(m.set=i),v(i=s.init)&&K.unshift(i));return l||Ie(t,o),m&&g(o,r,m),f?l^4?a:m:o};var q=(t,e,r)=>e.has(t)||T("Cannot "+r),Pe=(t,e)=>Object(e)!==e?T('Cannot use the "in" operator on this value'):t.has(e),ee=(t,e,r)=>(q(t,e,"read from private field"),r?r.call(t):e.get(t));var te=(t,e,r,n)=>(q(t,e,"write to private field"),n?n.call(t,r):e.set(t,r),r),qe=(t,e,r)=>(q(t,e,"access private method"),r);var ue=y((ot,Ue)=>{Ue.exports={greeting:"Hello, {name}!",items:"{count, plural, one {# item} other {# items}}"}});var de=y((at,We)=>{We.exports={today:"today",yesterday:"yesterday",daysAgo:"{count, plural, one {# day ago} other {# days ago}}",pieces:"{count, plural, =0 {none} one {one} other {# pcs}}"}});var me=y((it,ze)=>{ze.exports={required:"This field is required.",range:"Number must be between {min} and {max}, was {actual}.",digits:"Please enter only digits."}});var fe=y((st,Ye)=>{Ye.exports={greeting:"Hej, {name}!",items:"{count, plural, one {# sak} other {# saker}}"}});var pe=y((lt,Ge)=>{Ge.exports={today:"idag",yesterday:"ig\xE5r",daysAgo:"{count, plural, one {# dag sedan} other {# dagar sedan}}",pieces:"{count, plural, =0 {inga} one {en} other {# st}}"}});var ge=y((ct,Je)=>{Je.exports={required:"Detta f\xE4lt \xE4r obligatoriskt.",range:"Talet m\xE5ste vara mellan {min} och {max}, var {actual}.",digits:"Ange endast siffror."}});var tt={};$e(tt,{BooleanConverter:()=>C,DateConverter:()=>D,DigitsValidation:()=>O,FormValidator:()=>N,NumberConverter:()=>H,RangeValidation:()=>$,RegisterValidator:()=>F,RequiredValidation:()=>R,createConverterFromDataType:()=>W,createConverterFromInputType:()=>z,getDataConverter:()=>U,getValidator:()=>ke,mapFormToClass:()=>be,readData:()=>Ee,setFormData:()=>Te});module.exports=Oe(tt);var j=new Map;function je(t){return j.has(t)||j.set(t,new Intl.PluralRules(t)),j.get(t)}function le(t){return t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function Be(t,e,r="en"){return t.replace(/\{(\w+)(?:, (plural|select),((?:[^{}]*\{[^{}]*\})+))?\}/g,(n,o,a,i)=>{let s=e[o];if(a==="plural"){let u=new RegExp(`=${le(String(s))}\\s*\\{([^{}]*)\\}`).exec(i);if(u)return u[1].replace(`{${o}}`,String(s)).replace("#",String(s));let p=je(r).select(s),l=new RegExp(`${p}\\s*\\{([^{}]*)\\}`).exec(i)||new RegExp("other\\s*\\{([^{}]*)\\}").exec(i);return l?l[1].replace(`{${o}}`,String(s)).replace("#",String(s)):String(s)}if(a==="select"){let u=le(String(s)),c=new RegExp(`\\b${u}\\s*\\{([^{}]*)\\}`).exec(i)||new RegExp("\\bother\\s*\\{([^{}]*)\\}").exec(i);return c?c[1]:String(s)}return s!==void 0?String(s):`{${o}}`})}var ce=Be;var mt=ae({"./locales/en/r-common.json":()=>Promise.resolve().then(()=>h(ue())),"./locales/en/r-pipes.json":()=>Promise.resolve().then(()=>h(de())),"./locales/en/r-validation.json":()=>Promise.resolve().then(()=>h(me())),"./locales/sv/r-common.json":()=>Promise.resolve().then(()=>h(fe())),"./locales/sv/r-pipes.json":()=>Promise.resolve().then(()=>h(pe())),"./locales/sv/r-validation.json":()=>Promise.resolve().then(()=>h(ge()))});var Qe="en",B=Qe;var Xe={},ye=null;function k(t,e){let[r,n]=t.includes(":")?t.split(":"):["r-common",t],o=Xe[r]?.[n];if(!o)return ye&&ye(n,r,B),t;try{return ce(o,e,B)}catch{return t}}function he(){return B}function be(t,e,r={}){let n=t.querySelectorAll("input, select, textarea");if(n.forEach(o=>{if(!o.hasAttribute("name")||A(o,"disabled"))return;let a=o.getAttribute("name");if(!(a in e)){if(r.throwOnMissingProperty)throw new Error(`Form field "${a}" has no matching property in class instance`);return}let i=Ke(o);i!==xe&&(e[a]=i)}),r.throwOnMissingField){let o=new Set;n.forEach(a=>{a.hasAttribute("name")&&o.add(a.getAttribute("name"))});for(let a in e)if(typeof e[a]!="function"&&Object.prototype.hasOwnProperty.call(e,a)&&!o.has(a))throw new Error(`Class property "${a}" has no matching form field`)}return e}function U(t){let e=t.getAttribute("data-type");return e?W(e):t instanceof HTMLInputElement?z(t.type):"checked"in t&&typeof t.checked=="boolean"?C:r=>r}function Ee(t){let e={},r=new FormData(t),n=new Set;r.forEach((o,a)=>{if(n.has(a))return;n.add(a);let i=r.getAll(a),s=t.elements.namedItem(a),u=s?U(s):c=>c;if(i.length===1){let c=i[0];e[a]=typeof c=="string"?u(c):c}else e[a]=i.map(c=>typeof c=="string"?u(c):c)});for(let o=0;o<t.elements.length;o++){let a=t.elements[o];a.type==="checkbox"&&a.name&&!n.has(a.name)&&(n.add(a.name),e[a.name]=!1)}return e}function C(t){if(!t||t=="")return;let e=t.toLowerCase();if(e==="true"||e==="on"||Number(t)>0)return!0;if(e==="false"||e==="off"||Number(t)<=0)return!1;throw new Error("Could not convert value '"+t+"' to boolean.")}function H(t){if(!t||t=="")return;let e=Number(t);if(!isNaN(e))return e;throw new Error("Could not convert value '"+t+"' to number.")}function Ze(t){return new Intl.DateTimeFormat(t).formatToParts(new Date(2024,0,15)).filter(r=>r.type==="day"||r.type==="month"||r.type==="year").map(r=>r.type)}function D(t){if(!t||t==="")return;if(/^\d{4}-\d{2}-\d{2}(T|$)/.test(t)){let n=new Date(t);if(!isNaN(n.getTime()))return n}let e=t.split(/[\/.\-\s]/);if(e.length>=3&&e.every(n=>/^\d+$/.test(n))){let n=he(),o=Ze(n),a={};if(o.forEach((i,s)=>{a[i]=parseInt(e[s],10)}),a.year!==void 0&&a.month!==void 0&&a.day!==void 0){a.year<100&&(a.year+=2e3);let i=new Date(a.year,a.month-1,a.day);if(!isNaN(i.getTime()))return i}}let r=new Date(t);if(isNaN(r.getTime()))throw new Error("Invalid date format");return r}function W(t){switch(t){case"boolean":return C;case"number":return H;case"Date":return D;case"string":return e=>!e||e==""?void 0:e;default:throw new Error(`Unknown data-type "${t}".`)}}function z(t){switch(t){case"checkbox":return C;case"number":return H;case"date":case"datetime-local":return D;case"month":return e=>{let[r,n]=e.split("-").map(Number);return new Date(r,n-1)};case"week":return e=>{let[r,n]=e.split("-W").map(Number);return{year:r,week:n}};case"time":return e=>{let[r,n,o=0]=e.split(":").map(Number);return{hours:r,minutes:n,seconds:o}};default:return e=>!e||e==""?void 0:e}}function A(t,e){let r=t;if(e in r&&typeof r[e]=="boolean")return r[e];let n=t.getAttribute(e);return n===null?!1:n===""||n.toLowerCase()==="true"||n.toLowerCase()===e}var xe=Symbol("skip");function Ke(t){let e=t,r=e.type||t.getAttribute("type")||"";if(r==="checkbox")return A(t,"checked");if(r==="radio")return A(t,"checked")?e.value:xe;if(r==="number")return e.value?Number(e.value):null;if(r==="date")return e.value?new Date(e.value):null;if("selectedOptions"in e&&A(t,"multiple"))return Array.from(e.selectedOptions).map(n=>n.value);if("value"in e)return e.value}var Y=class extends Error{constructor(r,n){super(r);this.context=n}},we=null;function G(t,e){let r=new Y(t,e);if(we){let n=!1;if(we(r,{suppress(){n=!0}}),n)return null}return r}function _e(t){let e=t.getAttribute("id");if(e){let r=t.closest("form");if(r){let n=r.querySelector(`label[for="${e}"]`);if(n)return n.textContent?.trim()||null}}return null}var N=class{constructor(e,r){this.form=e;this.options=r;if(!this.form)throw new Error("Form must be specified.");this.form.addEventListener("submit",n=>{if((r?.preventDefault||this.options?.submitCallback!=null)&&n.preventDefault(),this.options?.customChecks&&this.options.customChecks(e),this.validateForm())try{let o=this.options?.submitCallback?.call(this);o instanceof Promise&&o.catch(a=>{let i=G("submitCallback failed",{cause:a});if(i)throw i})}catch(o){let a=G("submitCallback failed",{cause:o});if(a)throw a}else r?.preventDefaultOnFailed!==!1&&n.preventDefault()}),r?.autoValidate&&e.addEventListener("input",()=>{this.validateForm()})}validateForm(){let e=Array.from(this.form.querySelectorAll("input,textarea,select")),r=!0;if(this.options?.useSummary!==!0)return this.form.checkValidity()?!0:(this.form.reportValidity(),this.focusFirstErrorElement(),!1);let n=[];return e.forEach(o=>{if(!o.checkValidity()){r=!1;let a=_e.call(this,o)||o.name||"Unnamed Field";n.push(`${a}: ${o.validationMessage}`)}}),r?this.clearErrorSummary():(this.displayErrorSummary(n),this.focusFirstErrorElement()),r}displayErrorSummary(e){this.clearErrorSummary(),this.errorSummary||this.createErrorSummary();let r=this.errorSummary.querySelector("ul");e.forEach(n=>{let o=document.createElement("li");o.textContent=n,r.appendChild(o)})}createErrorSummary(){let e=document.createElement("div");e.className="error-summary",e.style.color="red",e.setAttribute("role","alert"),e.setAttribute("aria-live","assertive"),e.setAttribute("aria-atomic","true"),this.errorSummary=e;let r=document.createElement("ul");this.errorSummary.appendChild(r),this.form.prepend(e)}addErrorToSummary(e,r){this.errorSummary||this.createErrorSummary();let n=this.errorSummary.querySelector("ul"),o=document.createElement("li");o.textContent=`${e}: ${r}`,n.appendChild(o)}clearErrorSummary(){this.errorSummary&&(this.errorSummary.querySelector("ul").innerHTML="")}focusFirstErrorElement(){let e=this.form.querySelector(":invalid");e instanceof HTMLElement&&document.activeElement!==e&&e.focus()}static FindForm(e){if(e.parentElement?.tagName=="FORM")return e.parentElement;for(let r=0;r<e.children.length;r++){let n=e.children[r];if(n.tagName=="FORM")return n}throw new Error("Parent or a direct child must be a FORM for class "+e.constructor.name)}};function Te(t,e){t.querySelectorAll("[name]").forEach(n=>{let o=n.getAttribute("name");if(!o)return;if(o.endsWith("[]")){let i=o.slice(0,-2),s=ve(e,i);if(Array.isArray(s)){let u=n,c=u.type||n.getAttribute("type")||"";if(c==="checkbox"||c==="radio")u.checked=s.includes(u.value);else if("options"in u&&Ce(n,"multiple"))s.forEach(p=>{let l=Array.from(u.options).find(w=>w.value===String(p));l&&(l.selected=!0)});else if("value"in u){let p=t.querySelectorAll(`[name="${o}"]`),l=Array.from(p).indexOf(n);l>=0&&l<s.length&&(u.value=String(s[l]))}}return}let a=ve(e,o);a!=null&&et(n,a)})}function ve(t,e){let r=[],n="",o=!1;for(let a=0;a<e.length;a++){let i=e[a];i==="["&&!o?(n&&(r.push(n),n=""),o=!0,n+=i):i==="]"&&o?(n+=i,r.push(n),n="",o=!1):i==="."&&!o?n&&(r.push(n),n=""):n+=i}return n&&r.push(n),r.reduce((a,i)=>{if(!(!a||typeof a!="object")){if(i.startsWith("[")&&i.endsWith("]")){let s=i.slice(1,-1);return a[s]}return a[i]}},t)}function et(t,e){let r=t,n=r.type||t.getAttribute("type")||"";if(n==="checkbox")r.checked=!!e;else if(n==="radio")r.checked=r.value===String(e);else if(n==="date"&&e instanceof Date)r.value=e.toISOString().split("T")[0];else if(n==="datetime-local"&&e instanceof Date){let o=a=>String(a).padStart(2,"0");r.value=`${e.getFullYear()}-${o(e.getMonth()+1)}-${o(e.getDate())}T${o(e.getHours())}:${o(e.getMinutes())}`}else if("options"in r&&Ce(t,"multiple")&&Array.isArray(e)){let o=Array.from(r.options),a=e.map(String);o.forEach(i=>{i.selected=a.includes(i.value)})}else"value"in r&&(r.value=String(e))}function Ce(t,e){let r=t;if(e in r&&typeof r[e]=="boolean")return r[e];let n=t.getAttribute(e);return n===null?!1:n===""||n.toLowerCase()==="true"||n.toLowerCase()===e}var Le=new Map;function F(t,e=[]){return function(r){Le.set(t,{validator:r,validInputTypes:e})}}function ke(t){return Le.get(t)}var Fe,J;Fe=[F("required")];var b=class b{static create(e){return e==="required"?new b:null}validate(e,r){e.trim()===""&&r.addError(this.getMessage())}getMessage(){return k("r-validation:required")}};J=M(null),b=L(J,0,"RequiredValidation",Fe,b),S(J,1,b);var R=b,Me,Q;Me=[F("range",["number"])];var E=class E{constructor(e,r){this.min=void 0;this.max=void 0;this.min=e,this.max=r}static create(e){let r=e.match(/^range\((-?\d+(?:\.\d+)?)-(-?\d+(?:\.\d+)?)\)$/);if(r){let[,n,o]=r;return new E(parseFloat(n),parseFloat(o))}return null}validate(e,r){if(e.trim()==="")return;let n=parseFloat(e);!isNaN(n)&&n>=this.min&&n<=this.max||r.addError(this.getMessage(e))}getMessage(e){return k("r-validation:range",{min:this.min,max:this.max,actual:e})}};Q=M(null),E=L(Q,0,"RangeValidation",Me,E),S(Q,1,E);var $=E,Se,X;Se=[F("digits",["number"])];var x=class x{static create(e){return e==="digits"?new x:null}validate(e,r){/^\d+$/.test(e)||r.addError(this.getMessage())}getMessage(){return k("r-validation:digits")}};X=M(null),x=L(X,0,"DigitsValidation",Se,x),S(X,1,x);var O=x;
2
2
  //# sourceMappingURL=index.js.map