@omss/core 0.0.2-beta.0 → 0.0.2-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +18 -16
- package/SECURITY.md +39 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +66 -63
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 Open Media Streaming Foundation
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Open Media Streaming Foundation
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
<picture>
|
|
4
|
+
<source
|
|
5
|
+
media="(prefers-color-scheme: dark)"
|
|
6
|
+
srcset="https://raw.githubusercontent.com/omss-spec/docs/refs/heads/main/assets/logo-dark.svg"
|
|
7
|
+
/>
|
|
8
|
+
<source
|
|
9
|
+
media="(prefers-color-scheme: light)"
|
|
10
|
+
srcset="https://raw.githubusercontent.com/omss-spec/docs/refs/heads/main/assets/logo-light.svg"
|
|
11
|
+
/>
|
|
7
12
|
<img
|
|
8
|
-
src="https://raw.githubusercontent.com/omss-spec/
|
|
13
|
+
src="https://raw.githubusercontent.com/omss-spec/docs/refs/heads/main/assets/logo-light.svg"
|
|
9
14
|
width="500"
|
|
10
15
|
height="auto"
|
|
11
16
|
alt="OMSS Core"
|
|
12
17
|
/>
|
|
13
|
-
|
|
14
|
-
-->
|
|
18
|
+
</picture>
|
|
15
19
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
<div align="center">
|
|
20
|
+
# Core
|
|
19
21
|
|
|
20
22
|
[](https://github.com/omss-spec/core/actions/workflows/ci.yml)
|
|
21
23
|
[](https://www.npmjs.com/package/@omss/core)
|
|
@@ -89,13 +91,13 @@ Do you want to know more? Check out the [documentation](https://omss.mintlify.si
|
|
|
89
91
|
|
|
90
92
|
## Features
|
|
91
93
|
|
|
92
|
-
- **Modular by design
|
|
93
|
-
- **OMSS Lifecycle
|
|
94
|
-
- **Fully typed
|
|
94
|
+
- **Modular by design**: The core ships with almost no functionality. Everything is a plugin.
|
|
95
|
+
- **OMSS Lifecycle**: Use hooks to get notified of OMSS lifecycle events.
|
|
96
|
+
- **Fully typed**: Built in TypeScript with full type exports for **Everything** (no `any` used!)
|
|
95
97
|
- **Extensible**: OMSS Core is fully extensible via its hooks, plugins, and decorators.
|
|
96
|
-
- **Middleware support
|
|
97
|
-
- **Developer friendly**:
|
|
98
|
-
- **Standards-compliant
|
|
98
|
+
- **Middleware support**: Certain services expose middleware chains that plugins can extend (e.g., caching layers).
|
|
99
|
+
- **Developer friendly**: The framework is built to be very expressive and help developers in their daily use without sacrificing performance.
|
|
100
|
+
- **Standards-compliant**: Built with the [OMSS Specification](https://github.com/omss-spec/omss-spec) in mind.
|
|
99
101
|
|
|
100
102
|
|
|
101
103
|
## Documentation _(coming soon)_
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Supported Versions
|
|
4
|
+
|
|
5
|
+
Only the latest version of the OMSS specification is supported.
|
|
6
|
+
|
|
7
|
+
Once the project reaches a stable release, a proper Long Term Support (LTS) matrix will be published here.
|
|
8
|
+
|
|
9
|
+
## Reporting a Vulnerability
|
|
10
|
+
|
|
11
|
+
The OMSS team takes security seriously. We appreciate your effort to responsibly disclose vulnerabilities.
|
|
12
|
+
|
|
13
|
+
**Please do NOT open a public GitHub issue for security vulnerabilities.**
|
|
14
|
+
|
|
15
|
+
To report a vulnerability, please use one of the following methods:
|
|
16
|
+
|
|
17
|
+
- **GitHub Security Advisories:** [Report a vulnerability privately](https://github.com/omss-spec/core/security/advisories/new) via GitHub's private reporting feature.
|
|
18
|
+
|
|
19
|
+
### What to include
|
|
20
|
+
|
|
21
|
+
Please include as much of the following information as possible:
|
|
22
|
+
|
|
23
|
+
- Type of vulnerability (e.g., injection, privilege escalation, information disclosure)
|
|
24
|
+
- Full paths of the affected source file(s)
|
|
25
|
+
- Location of the affected code (tag/branch/commit or direct URL)
|
|
26
|
+
- Any special configuration required to reproduce the issue
|
|
27
|
+
- Step-by-step instructions to reproduce the issue
|
|
28
|
+
- Proof-of-concept or exploit code (if possible)
|
|
29
|
+
- Impact of the vulnerability and how an attacker might exploit it
|
|
30
|
+
|
|
31
|
+
### Response timeline
|
|
32
|
+
|
|
33
|
+
We will acknowledge receipt of your report within **72 hours** and aim to provide an initial assessment within **10 business days**.
|
|
34
|
+
|
|
35
|
+
We will keep you informed of our progress and notify you when a fix is released. We kindly ask that you refrain from disclosing the vulnerability publicly until a fix has been released.
|
|
36
|
+
|
|
37
|
+
## Security Acknowledgements
|
|
38
|
+
|
|
39
|
+
We thank all security researchers who have responsibly disclosed vulnerabilities to us. Acknowledged contributors will be listed here upon their consent.
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["#hooks","#hookRegistry","#states","#server","#stack","#pluginRegistry","#hookRegistry","#insideBeforePluginRegister","#providerRegistry","#extractorService","#omssServer","#entries","#handlers","#hookRegistry","#core","#middleware","#inFlight","#cleaningFunction","#internalGetSources","#getInFlightKey","#providers","#providerRegistry","#hookRegistry","#middleware","#insideBeforeProviderRegister","#extractorRegistry","#hookRegistry","#insideBeforeRegisterExtractor","#extractors","#config"],"sources":["../src/features/hooks/HookRegistry.ts","../src/features/hooks/HookService.ts","../src/features/plugins/plugin-state.ts","../src/utils/error.ts","../src/utils/regexp.ts","../src/utils/utils.ts","../src/features/plugins/PluginRegistry.ts","../src/features/plugins/PluginService.ts","../src/features/resolvers/utils.ts","../src/features/providers/ProviderResultEmitter.ts","../src/features/source/SourceCore.ts","../src/utils/AsyncDeduper.ts","../src/utils/middleware.ts","../src/features/source/SourceService.ts","../src/features/providers/ProviderRegistry.ts","../src/features/providers/ProviderService.ts","../src/features/extractors/ExtractorService.ts","../src/features/extractors/ExtractorRegistry.ts","../src/core/server.ts","../src/features/providers/BaseProvider.ts","../src/features/resolvers/BaseResolver.ts"],"sourcesContent":["/**\n * Hook Registry\n *\n * Manages lifecycle hooks for OMSS events.\n */\nexport class HookRegistry<T> {\n /**\n * Map storing arrays of hook handlers for each hook name.\n */\n readonly #hooks = new Map<keyof T, unknown[]>()\n\n /**\n * Get all registered hooks immutable.\n * @dangerous - Be careful with this. what you are doing might cause side effects.\n */\n get hooks(): ReadonlyMap<keyof T, unknown[]> {\n return this.#hooks\n }\n\n /**\n * Clear all registered hooks.\n * @dangerous - Be careful with this. Might cause side effects.\n */\n reset(): void {\n return this.#hooks.clear()\n }\n\n /**\n * Run all hooks for a lifecycle event with the provided payload.\n *\n * @typeParam K - The name of the hook to run.\n * @param name - The hook name (key of THooks).\n * @param payload - The payload to pass to each hook handler.\n */\n async run<K extends keyof T>(name: K, payload: T[K] extends (payload: infer P) => unknown ? P : never): Promise<void> {\n const fns = this.#hooks.get(name) ?? []\n\n for (const fn of fns) {\n await (fn as (payload: unknown) => void | Promise<void>)(payload)\n }\n }\n\n /**\n * Add a hook handler for a lifecycle event.\n * @param name - The hook name (key of THooks).\n * @param cb - The hook handler function.\n */\n add<K extends keyof T>(name: K, cb: T[K]): void {\n const existing = this.#hooks.get(name) ?? []\n\n this.#hooks.set(name, [...existing, cb])\n }\n}\n","import { HookRegistry } from '@/features/hooks/HookRegistry.js'\n\nexport class HookService<T> {\n readonly #hookRegistry: HookRegistry<T>\n\n constructor(hookRegistry: HookRegistry<T> = new HookRegistry<T>()) {\n this.#hookRegistry = hookRegistry\n }\n\n /**\n * Get all registered hooks immutable. TO ADD HOOKS, USE THE ADD METHOD\n * @dangerous - Be careful with this. what you are doing might cause side effects.\n */\n get hooks(): ReadonlyMap<keyof T, unknown[]> {\n return this.#hookRegistry.hooks\n }\n\n /**\n * Register a hook for a lifecycle event.\n *\n * @typeParam K - The name of the hook to register.\n * @param name - The hook name (key of THooks).\n * @param cb - The handler function for this hook.\n */\n add<K extends keyof T>(name: K, cb: T[K]): ReturnType<HookRegistry<T>['add']> {\n this.#hookRegistry.add(name, cb)\n }\n\n /**\n * Clear all registered hooks.\n * @dangerous - Be careful with this. Might cause side effects.\n */\n reset(): ReturnType<HookRegistry<T>['reset']> {\n return this.#hookRegistry.reset()\n }\n\n /**\n * Get the hook registry.\n *\n * This is only exposed for internal purposes and should not be accessed in consumer projects.\n * @dangerous\n * @internal\n */\n __getRegistry(): HookRegistry<T> {\n return this.#hookRegistry\n }\n}\n","/**\n * The states of which a plugin can be.\n */\nexport enum PluginState {\n Registering,\n Registered,\n Unavailable,\n}\n","/**\n * Base class for all OMSS framework errors.\n * Use `instanceof OMSSError` to catch any framework error.\n * Always throw a specific subclass, never this directly.\n * Cause is not standardized and may change frequently.\n */\nexport class OMSSError extends Error {\n /**\n * Create a new OMSSError.\n * @param message - Error message\n * @param options - Additional options for the error\n */\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options)\n this.name = this.constructor.name\n }\n}\n\n/**\n * Returned when an error gets Returned in the OMSSServer class.\n *\n * @example\n * return ERR(OMSSServerError('config.name must be a non-empty string', {cause: config}))\n */\nexport class OMSSServerError extends OMSSError {}\n\n/**\n * Returned during plugin registration or execution.\n *\n * @example\n * return ERR(OMSSPluginError(`Plugin \"${name}\" is already registered`, { cause: plugin }))\n */\nexport class OMSSPluginError extends OMSSError {}\n\n/**\n * Returned during resolver registration or ID resolution.\n *\n * @example\n * return ERR(OMSSResolverError(`No resolver found for namespace \"xyz\"`, { cause: rawId }))\n */\nexport class OMSSResolverError extends OMSSError {}\n\n/**\n * Returned during provider registration or source fetching.\n *\n * @example\n * return ERR(OMSSProviderError('Provider must have at least one resolver', { cause: provider }))\n */\nexport class OMSSProviderError extends OMSSError {}\n\n/**\n * Returned when an extractor fails to extract the media from a host.\n *\n * @example\n * return ERR(OMSSExtractor('Failed to extract media from host due to host changes', { cause: html }))\n */\nexport class OMSSExtractorError extends OMSSError {}\n\n/**\n * Returned when something/several things fail during source gathering.\n *\n * @example\n * return ERR(OMSSSourceGatheringError('Failed to gather sources', { cause: providerResults }))\n */\nexport class OMSSSourceGatheringError extends OMSSError {}\n","/**\n * Regex for validating namespace names.\n */\nconst SAFE_UNIQUE_STRING_PATTERN = '[a-z0-9-]+'\nexport const SAFE_UNIQUE_STRING = new RegExp(`^${SAFE_UNIQUE_STRING_PATTERN}$`)\n\n/**\n * Regex for matching media formats by keyword or file extension.\n *\n * Matches:\n * - format keywords (`hls`, `dash`, `mp4`, etc.)\n * - common media file extensions\n * - extensions followed by URL query strings, fragments, paths, or the end of the URL\n */\nexport const HLS_REGEX = /\\bhls\\b|\\.(?:m3u8|ts)(?:$|[\\/?#])/i\nexport const MP4_REGEX = /\\bmp4\\b|\\.mp4(?:$|[\\/?#])/i\nexport const DASH_REGEX = /\\bdash\\b|\\.(?:mpd|m4a)(?:$|[\\/?#])/i\nexport const MKV_REGEX = /\\bmkv\\b|\\.mkv(?:$|[\\/?#])/i\nexport const VTT_REGEX = /\\bvtt\\b|\\.vtt(?:$|[\\/?#])/i\nexport const SRT_REGEX = /\\bsrt\\b|\\.srt(?:$|[\\/?#])/i\n\n/**\n * Regex for validating a single catalog entry value.\n *\n * A catalog entry must be either exactly `\"*\"` (wildcard — provider supports\n * all IDs in the namespace) or a safe unique string.\n *\n * Mixing `\"*\"` with other entries in the same catalog array is not allowed\n * and is validated separately at registration time.\n */\nexport const CATALOG_ENTRY = new RegExp(`^(?:\\\\*|${SAFE_UNIQUE_STRING_PATTERN})$`)\n","import { Result } from '@/types/utils.js'\nimport { OMSSError } from '@/utils/error.js'\nimport { SAFE_UNIQUE_STRING } from '@/utils/regexp.js'\n\n/**\n * Convenience factory for a successful result.\n */\nexport function OK(): Result<void, never>\nexport function OK<T>(value: T): Result<T, never>\n\nexport function OK(value?: unknown) {\n if (arguments.length === 0) {\n return { ok: true }\n }\n\n return { ok: true, value }\n}\n/**\n * Convenience factory for a failed result.\n */\nexport const ERR = <E extends Error>(error: E): Result<never, E> => ({ ok: false, error })\n\ntype ErrorConstructor<T extends OMSSError> = new (message: string, options?: { cause?: Error }) => T\n\n/**\n * Validate a string is safe for use as a unique identifier (only lowercase letters, numbers, and hyphens).\n * @param value - The string to validate\n * @param name - The name of the identifier, for error messages\n * @param ErrorType - The error type to return if validation fails\n */\nexport function validateSafeUniqueString<T extends OMSSError>(value: string, name: string, ErrorType: ErrorConstructor<T>): Result<void, T> {\n if (!SAFE_UNIQUE_STRING.test(value)) {\n return ERR(new ErrorType(`Invalid ${name} \"${value}\". Expected only letters (lowercase), numbers, and hyphens.`))\n }\n\n return OK()\n}\n","import type { OMSSConfiguredPluginType, OMSSPluginOptions, OMSSPluginType, UnknownPluginType } from '@/types/plugin.js'\nimport OMSSServer from '@/core/server.js'\nimport { PluginState } from '@/features/plugins/plugin-state.js'\nimport { OMSSPluginError } from '@/utils/error.js'\nimport { Result } from '@/types/utils.js'\nimport { ERR, OK } from '@/utils/utils.js'\n\n/**\n * Registry responsible for executing and managing OMSS Plugins.\n *\n * Plugins are executed when added.\n */\nexport class PluginRegistry {\n /**\n * States of plugin x\n */\n readonly #states = new Map<UnknownPluginType, PluginState>()\n readonly #server: OMSSServer\n\n /**\n * Registration stack used for circular dependency detection.\n */\n readonly #stack: UnknownPluginType[] = []\n\n constructor(server: OMSSServer) {\n this.#server = server\n }\n\n async add(plugin: OMSSPluginType): Promise<Result<PluginState.Registered, OMSSPluginError>>\n\n async add<T>(plugin: OMSSConfiguredPluginType<T>, options: OMSSPluginOptions<T>): Promise<Result<PluginState.Registered, OMSSPluginError>>\n\n /**\n * Adds and runs a plugin with its options.\n *\n * @typeParam T - Plugin options type.\n * @param plugin - The plugin function to register.\n * @param options - Plugin options or a factory function that resolves options.\n */\n async add(plugin: UnknownPluginType, options?: unknown): Promise<Result<PluginState.Registered, OMSSPluginError>> {\n // Check whether this plugin is already known\n const state = this.#states.get(plugin)\n\n if (state === PluginState.Registering) {\n const chain = [...this.#stack, plugin].map((p) => p.name).join(' -> ')\n\n return ERR(new OMSSPluginError(`Circular plugin dependency detected: ${chain}`))\n }\n\n if (state === PluginState.Registered) {\n return ERR(new OMSSPluginError(`Plugin \"${plugin.name}\" is already registered`))\n }\n\n // Start registering\n this.#states.set(plugin, PluginState.Registering)\n this.#stack.push(plugin)\n\n // Build options if a factory function is provided\n const resolved = typeof options === 'function' ? (options as (server: OMSSServer) => unknown)(this.#server) : options\n\n try {\n // Check if the plugin has a single argument\n if (plugin.length === 1) {\n // execute the plugin with the server instance\n await (plugin as OMSSPluginType)(this.#server)\n } else {\n // execute the plugin with the server instance and resolved options\n await plugin(this.#server, resolved)\n }\n\n this.#states.set(plugin, PluginState.Registered)\n return OK(PluginState.Registered)\n } catch (err) {\n this.#states.delete(plugin)\n return ERR(err instanceof OMSSPluginError ? err : new OMSSPluginError(String(err), { cause: err }))\n } finally {\n this.#stack.pop()\n }\n }\n\n /**\n * Get the current plugin state.\n */\n getState<T>(plugin: UnknownPluginType | OMSSPluginType | OMSSConfiguredPluginType<T>): PluginState {\n return this.#states.get(plugin as UnknownPluginType) ?? PluginState.Unavailable\n }\n}\n","import { PluginRegistry } from '@/features/plugins/PluginRegistry.js'\nimport { HookRegistry } from '@/features/hooks/HookRegistry.js'\nimport type { OMSSConfiguredPluginType, OMSSPluginOptions, OMSSPluginType, UnknownPluginType } from '@/types/plugin.js'\nimport OMSSServer from '@/core/server.js'\nimport { ERR } from '@/utils/utils.js'\nimport { OMSSPluginError } from '@/utils/error.js'\nimport type { OMSSHooks } from '@/types/hooks.js'\n\n/**\n * The public API for managing OMSS plugins.\n */\nexport class PluginService {\n readonly #pluginRegistry: PluginRegistry\n readonly #hookRegistry: HookRegistry<OMSSHooks>\n #insideBeforePluginRegister = false\n\n constructor(omssServer: OMSSServer, pluginRegistry: PluginRegistry, hookRegistry: HookRegistry<OMSSHooks>) {\n this.#pluginRegistry = pluginRegistry\n this.#hookRegistry = hookRegistry\n }\n\n /**\n * Register an OMSS plugin with no config into the system.\n * @param plugin - Plugin function\n */\n async register(plugin: OMSSPluginType): ReturnType<PluginRegistry['add']>\n\n /**\n * Register an OMSS plugin with a config into the system.\n * @param plugin - Plugin function\n * @param options - Plugin configuration\n */\n async register<T>(plugin: OMSSConfiguredPluginType<T>, options: OMSSPluginOptions<T>): ReturnType<PluginRegistry['add']>\n\n /**\n * Registers an OMSS plugin that can take a config into the system, but does not need to.\n * @param plugin - Plugin implementation\n * @param options - Plugin configuration\n */\n async register(plugin: UnknownPluginType, options?: unknown): ReturnType<PluginRegistry['add']> {\n if (this.#insideBeforePluginRegister) {\n return ERR(new OMSSPluginError('Plugins cannot be registered during beforePluginRegister'))\n }\n\n this.#insideBeforePluginRegister = true\n try {\n await this.#hookRegistry.run('beforePluginRegister', { plugin: plugin as UnknownPluginType, options })\n } finally {\n this.#insideBeforePluginRegister = false\n }\n\n const result = await this.#pluginRegistry.add(plugin, options)\n\n if (!result.ok) {\n await this.#hookRegistry.run('pluginRegisterFailed', { plugin, options, error: result.error })\n return result\n }\n\n await this.#hookRegistry.run('afterPluginRegister', { plugin, options })\n return result\n }\n\n /**\n * Get the current State of a plugin\n * @param plugin - the plugin to get the state from\n * @returns - a value of the PluginState enum\n */\n getPluginState(plugin: UnknownPluginType): ReturnType<PluginRegistry['getState']> {\n return this.#pluginRegistry.getState(plugin)\n }\n}\n","import type { OMSSId, ParsedOMSSId } from '@/types/resolver.js'\nimport { OMSSResolverError } from '@/utils/error.js'\nimport { Result } from '@/types/utils.js'\nimport { ERR, OK, validateSafeUniqueString } from '@/utils/utils.js'\n\n/**\n * Parses an OMSS ID in the form `namespace:value_1[:value_2[:...]]`.\n */\nexport function parseOMSSId(id: OMSSId): Result<ParsedOMSSId, OMSSResolverError> {\n if (/\\s/.test(id)) {\n return ERR(new OMSSResolverError(`Invalid OMSS ID \"${id}\": cannot contain whitespace`))\n }\n\n const parts = id.split(':')\n\n if (parts.length < 2) {\n return ERR(new OMSSResolverError(`Invalid OMSS ID \"${id}\": missing namespace separator \":\"`))\n }\n\n const [namespace, ...values] = parts\n\n if (!namespace) {\n return ERR(new OMSSResolverError(`Invalid OMSS ID \"${id}\": namespace cannot be empty`))\n }\n\n const req = validateSafeUniqueString(namespace, 'OMSS namespace', OMSSResolverError)\n\n if (!req.ok) {\n return ERR(req.error)\n }\n\n for (const [i, value] of values.entries()) {\n if (value.length === 0) {\n return ERR(new OMSSResolverError(`Invalid OMSS ID \"${id}\": value ${i + 1} cannot be empty`))\n }\n }\n\n const decodedValues = values.map((value) => decodeURIComponent(value))\n\n return OK({\n namespace,\n values: decodedValues,\n raw: id,\n })\n}\n","import {\r\n EmittedSource,\r\n EmittedSubtitle,\r\n OMSSProviderResult,\r\n ProviderResult,\r\n ProviderResultEmitter,\r\n Source,\r\n SourceQuality,\r\n SourceTypes,\r\n Subtitle,\r\n SubtitleFormat,\r\n UnknownProvider,\r\n} from '@/types/provider.js'\r\nimport { OMSSProviderError } from '@/utils/error.js'\r\nimport { Result } from '@/types/utils.js'\r\nimport { ERR, OK } from '@/utils/utils.js'\r\nimport { HookRegistry } from '@/features/hooks/HookRegistry.js'\r\nimport { ProviderHooks } from '@/types/hooks.js'\r\nimport { DASH_REGEX, HLS_REGEX, MKV_REGEX, MP4_REGEX, SRT_REGEX, VTT_REGEX } from '@/utils/regexp.js'\r\nimport { CleaningFunction } from '@/types/source.js'\r\nimport { ParsedOMSSId } from '@/types/resolver.js'\r\n\r\n/**\r\n * Creates a fresh `ProviderResultEmitter` instance scoped to a single\r\n * `getSources()` execution.\r\n *\r\n * A new emitter MUST be created per provider call. Emitters hold internal,\r\n * mutable state (accumulated sources/subtitles/errors) via closures, so\r\n * reusing a single emitter across concurrent provider executions would\r\n * cause data from one provider to leak into another's response.\r\n *\r\n * @param provider - The provider instance for which this emitter is being created.\r\n * @param hookReg - The hook registry instance for managing hooks.\r\n * @param cleaningFunc - A function to clean up source/subtitle URLs and headers.\r\n * @param id - The parsed OMSS ID of the current request.\r\n * @returns A new `ProviderResultEmitter` bound to this execution.\r\n *\r\n */\r\nexport function createProviderResultEmitter(provider: Readonly<UnknownProvider>, hookReg: HookRegistry<ProviderHooks>, cleaningFunc: CleaningFunction, id: ParsedOMSSId): ProviderResultEmitter {\r\n /**\r\n * Accumulated sources emitted via `source()` during this execution.\r\n * Flushed into the final result when `done()` is called.\r\n */\r\n const sources: Source[] = []\r\n\r\n /**\r\n * Accumulated subtitles emitted via `subtitle()` during this execution.\r\n * Subtitles are intentionally NOT linked to any source or audio track,\r\n * so they live in their own flat array regardless of how many sources\r\n * were emitted.\r\n */\r\n const subtitles: Subtitle[] = []\r\n\r\n /**\r\n * Accumulated NON-fatal errors emitted via `error()` during this execution.\r\n * These are returned alongside successful results (via `done()`) so\r\n * that partial failures (e.g. \"server 2 of 3 failed\") don't discard\r\n * otherwise-valid sources.\r\n */\r\n const errors: OMSSProviderError[] = []\r\n\r\n return {\r\n /**\r\n * Utilities to make your life easier\r\n */\r\n utils: {\r\n /**\r\n * Utilities for parsing metadata of sources\r\n */\r\n source: {\r\n /**\r\n * Parse a string into a possible source type.\r\n * @param possibleType - The string to parse\r\n */\r\n parseType(possibleType: string): SourceTypes {\r\n if (HLS_REGEX.test(possibleType)) {\r\n return 'hls'\r\n } else if (MP4_REGEX.test(possibleType)) {\r\n return 'mp4'\r\n } else if (DASH_REGEX.test(possibleType)) {\r\n return 'dash'\r\n } else if (MKV_REGEX.test(possibleType)) {\r\n return 'mkv'\r\n } else {\r\n // most commonly used is hls\r\n return 'hls'\r\n }\r\n },\r\n /**\r\n * Parse a string into a possible source quality.\r\n * @param possibleQuality - The string to parse\r\n */\r\n parseQuality(possibleQuality: string): SourceQuality {\r\n if (!possibleQuality) {\r\n return 'Auto'\r\n }\r\n\r\n const value = possibleQuality.toLowerCase().trim().replace(/_/g, ' ').replace(/-/g, ' ')\r\n\r\n // Direct quality labels / aliases\r\n if (/\\b(8k|uhd\\s*8k|4320p)\\b/.test(value)) {\r\n return '8K'\r\n }\r\n\r\n if (/\\b(4k|uhd|ultra\\s*hd|2160p)\\b/.test(value)) {\r\n return '4K'\r\n }\r\n\r\n if (/\\b(qhd|2k|1440p|2560x1440)\\b/.test(value)) {\r\n return 'QHD'\r\n }\r\n\r\n if (/\\b(fhd|full\\s*hd|1080p|1920x1080)\\b/.test(value)) {\r\n return 'FHD'\r\n }\r\n\r\n if (/\\b(hd|720p|1280x720)\\b/.test(value)) {\r\n return 'HD'\r\n }\r\n\r\n if (/\\b(sd|480p|576p|360p|240p)\\b/.test(value)) {\r\n return 'SD'\r\n }\r\n\r\n const resolutionMatch = value.match(/(\\d{3,4})(?:p|x\\d{3,4})?/)\r\n\r\n if (resolutionMatch) {\r\n const resolution = Number(resolutionMatch[1])\r\n\r\n if (resolution >= 4320) {\r\n return '8K'\r\n }\r\n\r\n if (resolution >= 2160) {\r\n return '4K'\r\n }\r\n\r\n if (resolution >= 1440) {\r\n return 'QHD'\r\n }\r\n\r\n if (resolution >= 1080) {\r\n return 'FHD'\r\n }\r\n\r\n if (resolution >= 720) {\r\n return 'HD'\r\n }\r\n\r\n if (resolution > 0) {\r\n return 'SD'\r\n }\r\n }\r\n\r\n const bitrateMatch = value.match(/(\\d+(?:\\.\\d+)?)\\s*(mbps|kbps)/)\r\n\r\n if (bitrateMatch) {\r\n const bitrate = Number(bitrateMatch[1])\r\n const unit = bitrateMatch[2]\r\n\r\n const mbps = unit === 'kbps' ? bitrate / 1000 : bitrate\r\n\r\n if (mbps >= 25) {\r\n return '4K'\r\n }\r\n\r\n if (mbps >= 8) {\r\n return 'FHD'\r\n }\r\n\r\n if (mbps >= 3) {\r\n return 'HD'\r\n }\r\n\r\n return 'SD'\r\n }\r\n\r\n return 'Auto'\r\n },\r\n },\r\n /**\r\n * Utilities for parsing metadata of subtitles\r\n */\r\n subtitle: {\r\n /**\r\n * Parse a string into a possible subtitle format.\r\n * @param possibleFormat - The string to parse\r\n */\r\n parseFormat(possibleFormat: string): SubtitleFormat {\r\n if (VTT_REGEX.test(possibleFormat)) {\r\n return 'vtt'\r\n } else if (SRT_REGEX.test(possibleFormat)) {\r\n return 'srt'\r\n } else {\r\n return 'vtt'\r\n }\r\n },\r\n },\r\n },\r\n\r\n /**\r\n * Emits a custom, provider-defined action/event.\r\n *\r\n * @param action - A custom event name (e.g. \"cache.hit\").\r\n * @param data - Arbitrary payload associated with the event.\r\n */\r\n emit(action: string, data: unknown): void {\r\n // action cannot be whitespace or another hook name\r\n if (/\\s/.test(action) || Object.keys(this).includes(action)) {\r\n return\r\n }\r\n hookReg.run(action, { data, provider, id, timestamp: new Date().toISOString() })\r\n },\r\n\r\n /**\r\n * Logs verbose debug information. Intended for development/troubleshooting\r\n * only and should be stripped or gated behind a debug flag in production.\r\n *\r\n * @param args - Values to log, forwarded as-is (same semantics as `console.debug`).\r\n */\r\n debug(...args: unknown[]): void {\r\n hookReg.run('debug', { provider, args, id, timestamp: new Date().toISOString() })\r\n },\r\n\r\n /**\r\n * Logs general informational messages about provider execution\r\n * (e.g. \"Fetched media\", \"Cache miss, fetching from upstream\").\r\n *\r\n * @param args - Values to log.\r\n */\r\n info(...args: unknown[]): void {\r\n hookReg.run('info', { provider, args, id, timestamp: new Date().toISOString() })\r\n },\r\n\r\n /**\r\n * Logs a non-fatal warning. Use this for degraded-but-recoverable\r\n * situations (e.g. \"missing quality metadata, defaulting to Auto\").\r\n *\r\n * @param args - Values to log.\r\n */\r\n warn(...args: unknown[]): void {\r\n hookReg.run('warn', { provider, args, id, timestamp: new Date().toISOString() })\r\n },\r\n\r\n /**\r\n * Records a NON-fatal error. The provider continues executing after\r\n * calling this — use `fatal()` instead if the provider cannot continue.\r\n *\r\n * The error is accumulated and returned to the requestor as part of\r\n * the `diagnostics`/`errors` field once `done()` is called, allowing\r\n * partial success (e.g. some sources found despite one upstream\r\n * server failing).\r\n *\r\n * @param error - The error to record. Will be surfaced to the client.\r\n */\r\n error(error: OMSSProviderError): void {\r\n errors.push(error)\r\n hookReg.run('error', { provider, error, id, timestamp: new Date().toISOString() })\r\n },\r\n\r\n /**\r\n * Emits a single resolved source.\r\n *\r\n * @param source - The fully-formed source object to emit.\r\n */\r\n source(source: EmittedSource): void {\r\n const cleanedSource = {\r\n ...source,\r\n }\r\n\r\n const cleaned = cleaningFunc({\r\n url: cleanedSource.url,\r\n header: cleanedSource.header,\r\n })\r\n\r\n cleanedSource.url = cleaned.url\r\n cleanedSource.header = cleaned.header\r\n\r\n if ('audioTracks' in cleanedSource && cleanedSource.audioTracks) {\r\n // had to split first and rest, since audioTracks requires least one track.\r\n const [first, ...rest] = cleanedSource.audioTracks\r\n\r\n cleanedSource.audioTracks = [\r\n {\r\n ...first,\r\n ...cleaningFunc({\r\n url: first.url,\r\n header: first.header,\r\n }),\r\n },\r\n ...rest.map((track) => ({\r\n ...track,\r\n ...cleaningFunc({\r\n url: track.url,\r\n header: track.header,\r\n }),\r\n })),\r\n ]\r\n }\r\n const fullSource: Source = {\r\n ...cleanedSource,\r\n provider: {\r\n id: provider.id,\r\n name: provider.name,\r\n },\r\n }\r\n\r\n sources.push(fullSource)\r\n\r\n hookReg.run('source', {\r\n provider,\r\n source: fullSource,\r\n id,\r\n timestamp: new Date().toISOString(),\r\n })\r\n },\r\n\r\n /**\r\n * Emits a single subtitle track.\r\n *\r\n * @param subtitle - The subtitle object to emit.\r\n */\r\n subtitle(subtitle: EmittedSubtitle): void {\r\n const obj = {\r\n url: subtitle.url,\r\n header: subtitle.header,\r\n }\r\n const { url, header } = cleaningFunc(obj)\r\n subtitle.url = url\r\n subtitle.header = header\r\n const fullSub = { ...subtitle, provider: { id: provider.id, name: provider.name } }\r\n subtitles.push(fullSub)\r\n hookReg.run('subtitle', { provider, subtitle: fullSub, id, timestamp: new Date().toISOString() })\r\n },\r\n\r\n /**\r\n * Immediately aborts provider execution with a fatal error.\r\n *\r\n * @param error - The fatal error describing why the provider could not proceed.\r\n * @returns An `ERR` result wrapping the given error.\r\n */\r\n fatal(error: OMSSProviderError): Result<never, OMSSProviderError> {\r\n const accumulatedError = new AggregateError([error, ...errors], error.message, { cause: error.cause })\r\n\r\n const finalErr = new OMSSProviderError(accumulatedError.message, { cause: accumulatedError })\r\n\r\n hookReg.run('error', { provider, error: finalErr, id, timestamp: new Date().toISOString() })\r\n\r\n return ERR(finalErr)\r\n },\r\n\r\n /**\r\n * Signals that the provider has finished emitting sources/subtitles\r\n * and finalizes the result.\r\n *\r\n * @returns An `OK` result containing all accumulated sources,\r\n * subtitles, and non-fatal errors for this execution.\r\n */\r\n done(): ProviderResult {\r\n const result: OMSSProviderResult = {\r\n sources,\r\n subtitles,\r\n errors,\r\n }\r\n\r\n hookReg.run('done', { provider, result, id, timestamp: new Date().toISOString() })\r\n\r\n return OK(result)\r\n },\r\n }\r\n}\r\n","import OMSSServer from '@/core/server.js'\r\nimport { ProviderRegistry } from '@/features/providers/ProviderRegistry.js'\r\nimport { parseOMSSId } from '@/features/resolvers/utils.js'\r\nimport type { ProviderResult, Source, Subtitle, UnknownProvider } from '@/types/provider.js'\r\nimport type { OMSSId, ResolverExecutionContext } from '@/types/resolver.js'\r\nimport { CleaningFunction, GatheredSources, GetSourcesOptions } from '@/types/source.js'\r\nimport type { Result } from '@/types/utils.js'\r\nimport { OMSSProviderError, OMSSSourceGatheringError } from '@/utils/error.js'\r\nimport { ERR, OK } from '@/utils/utils.js'\r\nimport { createProviderResultEmitter } from '@/features/providers/ProviderResultEmitter.js'\r\nimport { ProviderHooks } from '@/types/hooks.js'\r\nimport { ExtractorService } from '@/features/extractors/ExtractorService.js'\r\nimport { HookService } from '@/features/hooks/HookService.js'\r\n\r\n/**\r\n * Internal source gathering core.\r\n *\r\n * Handles OMSS ID parsing, provider lookup, resolver-level deduplication,\r\n * provider execution, and final result aggregation.\r\n *\r\n * This class intentionally does not know about OMSS Hooks, middleware, or in-flight\r\n * request sharing. Those concerns belong to SourceService.\r\n */\r\nexport class SourceCore {\r\n readonly #providerRegistry: ProviderRegistry\r\n readonly #extractorService: ExtractorService\r\n readonly #omssServer: OMSSServer\r\n\r\n constructor(omssServer: OMSSServer, providerRegistry: ProviderRegistry, extractorService: ExtractorService) {\r\n this.#omssServer = omssServer\r\n this.#providerRegistry = providerRegistry\r\n this.#extractorService = extractorService\r\n }\r\n\r\n /**\r\n * Gather sources for a single OMSS ID.\r\n *\r\n * @param omssId - Raw OMSS identifier.\r\n * @param opts - Optional source gathering parameters.\r\n * @param providerHookRegistry - Hook registry for provider hooks.\r\n * @param cleaningFunction - Optional custom function to clean url's and headers\r\n * @returns Aggregated provider results or a source gathering error.\r\n */\r\n async getSources(\r\n omssId: OMSSId,\r\n opts: GetSourcesOptions,\r\n providerHookRegistry: HookService<ProviderHooks>,\r\n cleaningFunction: CleaningFunction\r\n ): Promise<Result<GatheredSources, OMSSSourceGatheringError>> {\r\n // try to parse the OMSS ID\r\n const parsed = parseOMSSId(omssId)\r\n\r\n if (!parsed.ok) {\r\n return ERR(new OMSSSourceGatheringError(`Failed to parse OMSS id \"${omssId}\": ${parsed.error.message}`, { cause: parsed.error }))\r\n }\r\n\r\n // we know that the id is valid now. Now we got to find the providers that can handle that namespace. If a specific provider is requested, we only look for that one.\r\n const providers: UnknownProvider[] = opts.providerId\r\n ? this.#providerRegistry.getAll((p) => p.id === opts.providerId && p.resolver.namespace === parsed.value.namespace)\r\n : this.#providerRegistry.getAll((p) => p.resolver.namespace === parsed.value.namespace)\r\n\r\n // if no provider can handle that namespace, return an error\r\n if (providers.length === 0) {\r\n return ERR(new OMSSSourceGatheringError(`No providers found for namespace \"${parsed.value.namespace}\"` + (opts.providerId ? ` and provider \"${opts.providerId}\"` : '')))\r\n }\r\n\r\n // if no abortsignal comes, just create a new one (does not abort)\r\n const signal = opts.abortSignal ?? new AbortController().signal\r\n\r\n // create the resolver context\r\n const ctx: ResolverExecutionContext = {\r\n server: this.#omssServer,\r\n signal,\r\n }\r\n\r\n /**\r\n * Resolver-level deduplication: multiple providers that share the same\r\n * resolver run that resolver only once, then share the result.\r\n */\r\n const resolverCache = new Map<string, Promise<Result<unknown, OMSSSourceGatheringError>>>()\r\n\r\n /**\r\n * Get resolver metadata for a provider, reusing the same resolver\r\n * promise when multiple providers share the same resolver.\r\n *\r\n * @param provider - Provider whose resolver metadata should be loaded.\r\n * @returns Resolver metadata or a source gathering error.\r\n */\r\n const getResolvedMeta = (provider: UnknownProvider): Promise<Result<unknown, OMSSSourceGatheringError>> => {\r\n const resolverKey = `${provider.resolver.namespace}:${provider.resolver.name}`\r\n\r\n let promise = resolverCache.get(resolverKey)\r\n\r\n if (!promise) {\r\n promise = (async (): Promise<Result<unknown, OMSSSourceGatheringError>> => {\r\n if (signal.aborted) {\r\n return ERR(new OMSSSourceGatheringError('Operation aborted'))\r\n }\r\n\r\n const result = await provider.resolver.resolve(parsed.value, ctx)\r\n\r\n if (!result.ok) {\r\n return ERR(new OMSSSourceGatheringError(`Resolver failed for ${resolverKey}: ${result.error.message}`, { cause: result.error }))\r\n }\r\n\r\n return OK(result.value)\r\n })()\r\n\r\n resolverCache.set(resolverKey, promise)\r\n }\r\n\r\n return promise\r\n }\r\n\r\n /**\r\n * Resolve sources for a single provider.\r\n *\r\n * @param provider - Provider to execute.\r\n * @returns Provider result or a source gathering error.\r\n */\r\n const resolveForProvider = async (provider: UnknownProvider): Promise<ProviderResult | Result<never, OMSSSourceGatheringError>> => {\r\n if (signal.aborted) {\r\n return ERR(new OMSSSourceGatheringError('Operation aborted'))\r\n }\r\n\r\n const supportsId = await provider.supportsId(parsed.value)\r\n if (!supportsId) {\r\n return OK({ sources: [], subtitles: [], errors: [new OMSSProviderError(`Provider \"${provider.id}\" did not support this id: \"${parsed.value.raw}\"`)] })\r\n }\r\n\r\n if (signal.aborted) {\r\n return ERR(new OMSSSourceGatheringError('Operation aborted'))\r\n }\r\n\r\n const metaResult = await getResolvedMeta(provider)\r\n\r\n if (!metaResult.ok) {\r\n return metaResult\r\n }\r\n\r\n if (signal.aborted) {\r\n return ERR(new OMSSSourceGatheringError('Operation aborted'))\r\n }\r\n\r\n const resultEmitter = createProviderResultEmitter(provider, providerHookRegistry.__getRegistry(), cleaningFunction, parsed.value)\r\n\r\n return provider.getSources(\r\n {\r\n utils: {\r\n omssId: parsed.value,\r\n abortSignal: signal,\r\n findExtractor: this.#extractorService.find,\r\n },\r\n meta: metaResult.value,\r\n },\r\n resultEmitter\r\n )\r\n }\r\n\r\n const settled = await Promise.allSettled(providers.map((provider) => resolveForProvider(provider)))\r\n\r\n if (signal.aborted) {\r\n return ERR(new OMSSSourceGatheringError('Operation aborted'))\r\n }\r\n\r\n const allSources: Source[] = []\r\n const allSubtitles: Subtitle[] = []\r\n const unexpectedErrors: unknown[] = []\r\n const omssErrors: Array<Extract<ProviderResult, { ok: false }>['error'] | OMSSSourceGatheringError> = []\r\n let hasSuccess = false\r\n\r\n for (const item of settled) {\r\n if (item.status === 'rejected') {\r\n omssErrors.push(\r\n new OMSSSourceGatheringError(`Provider execution failed: ${item.reason instanceof Error ? item.reason.message : String(item.reason)}`, {\r\n cause: item.reason instanceof Error ? item.reason : undefined,\r\n })\r\n )\r\n unexpectedErrors.push(item.reason)\r\n continue\r\n }\r\n\r\n const res = item.value\r\n\r\n if (res.ok) {\r\n hasSuccess = true\r\n allSources.push(...res.value.sources)\r\n allSubtitles.push(...res.value.subtitles)\r\n omssErrors.push(...res.value.errors)\r\n continue\r\n }\r\n\r\n omssErrors.push(res.error)\r\n }\r\n\r\n if (!hasSuccess) {\r\n return ERR(\r\n new OMSSSourceGatheringError(`All providers failed for namespace \"${parsed.value.namespace}\" and id: \"${parsed.value.raw}\"`, {\r\n cause: new AggregateError([...omssErrors, ...unexpectedErrors], 'Multiple failures detected'),\r\n })\r\n )\r\n }\r\n\r\n return OK({\r\n sources: allSources,\r\n subtitles: allSubtitles,\r\n errors: omssErrors,\r\n })\r\n }\r\n}\r\n","/**\n * Generic helper for deduplicating concurrent asynchronous work by key.\n *\n * When the same key is requested multiple times while a request is still in\n * flight, all callers receive the same Promise. Once the Promise settles, the\n * key is removed automatically so the next request starts fresh.\n *\n * @typeParam TKey - Cache key type.\n * @typeParam TValue - Promise resolution type.\n */\nexport class AsyncDeduper<TKey, TValue> {\n readonly #entries = new Map<TKey, Promise<TValue>>()\n\n /**\n * Returns the current in-flight Promise for a key, if one exists.\n *\n * @param key - In-flight request key.\n * @returns Existing Promise or undefined.\n */\n get(key: TKey): Promise<TValue> | undefined {\n return this.#entries.get(key)\n }\n\n /**\n * Check whether a key currently has an in-flight Promise.\n *\n * @param key - In-flight request key.\n * @returns True if the key is currently in flight.\n */\n has(key: TKey): boolean {\n return this.#entries.has(key)\n }\n\n /**\n * Delete a key manually.\n *\n * @param key - In-flight request key.\n * @returns True if an entry existed and was removed.\n */\n delete(key: TKey): boolean {\n return this.#entries.delete(key)\n }\n\n /**\n * Clear all tracked in-flight requests.\n */\n clear(): void {\n this.#entries.clear()\n }\n\n /**\n * Run a Promise factory for a key, reusing an existing in-flight Promise\n * when available.\n *\n * @param key - In-flight request key.\n * @param factory - Factory that creates the Promise when no request exists yet.\n * @returns Shared or newly created Promise.\n */\n run(key: TKey, factory: () => Promise<TValue>): Promise<TValue> {\n const existing = this.#entries.get(key)\n\n if (existing) {\n return existing\n }\n\n const promise = factory().finally(() => {\n this.#entries.delete(key)\n })\n\n this.#entries.set(key, promise)\n\n return promise\n }\n}\n","import { MiddlewareHandler, MiddlewareOperationMap } from '@/types/middleware.js'\n\n/**\n * Reusable typed middleware runner.\n */\nexport class MiddlewareRunner<TOperations extends MiddlewareOperationMap> {\n readonly #handlers: Partial<{\n [K in keyof TOperations]: MiddlewareHandler<TOperations, K>[]\n }> = {}\n\n /**\n * Register middleware for a specific operation.\n *\n * @param method - Operation name.\n * @param handler - Middleware handler.\n */\n use<TMethod extends keyof TOperations>(method: TMethod, handler: MiddlewareHandler<TOperations, TMethod>): void {\n const list = (this.#handlers[method] ??= []) as MiddlewareHandler<TOperations, TMethod>[]\n\n list.push(handler)\n }\n\n /**\n * Run middleware chain for a specific operation.\n *\n * @param method - Operation name.\n * @param context - Operation context payload.\n * @param finalHandler - Final function to execute after middleware.\n * @returns The operation result.\n */\n run<TMethod extends keyof TOperations>(\n method: TMethod,\n context: TOperations[TMethod]['context'],\n finalHandler: () => Promise<TOperations[TMethod]['result']>\n ): Promise<TOperations[TMethod]['result']> {\n const handlers = (this.#handlers[method] ?? []) as readonly MiddlewareHandler<TOperations, TMethod>[]\n\n let index = -1\n\n const dispatch = (position: number): Promise<TOperations[TMethod]['result']> => {\n if (position <= index) {\n return Promise.reject(new Error('next() called multiple times'))\n }\n\n index = position\n\n const handler = handlers[position]\n\n if (!handler) {\n return finalHandler()\n }\n\n return handler(context, () => dispatch(position + 1))\n }\n\n return dispatch(0)\n }\n}\n","import OMSSServer from '@/core/server.js'\nimport { HookRegistry } from '@/features/hooks/HookRegistry.js'\nimport { ProviderRegistry } from '@/features/providers/ProviderRegistry.js'\nimport { SourceCore } from '@/features/source/SourceCore.js'\nimport type { CleaningFunction, GatheredSources, GetSourcesOptions, SourceServiceMiddleware, SourceServiceOperations } from '@/types/source.js'\nimport type { OMSSId } from '@/types/resolver.js'\nimport type { Result } from '@/types/utils.js'\nimport { OMSSSourceGatheringError } from '@/utils/error.js'\nimport { AsyncDeduper } from '@/utils/AsyncDeduper.js'\nimport { MiddlewareRunner } from '@/utils/middleware.js'\nimport type { OMSSHooks, ProviderHooks } from '@/types/hooks.js'\nimport { ExtractorService } from '@/features/extractors/ExtractorService.js'\nimport { HookService } from '@/features/hooks/HookService.js'\n\n/**\n * Public API for resolving sources for media.\n *\n * This service owns the public method surface, middleware execution,\n * lifecycle hook dispatching, and request coalescing. The actual source\n * gathering implementation lives in {@link SourceCore}.\n */\nexport class SourceService {\n readonly #hookRegistry: HookRegistry<OMSSHooks>\n readonly #core: SourceCore\n\n /**\n * Middleware runner for SourceService operations.\n */\n readonly #middleware = new MiddlewareRunner<SourceServiceOperations>()\n\n /**\n * Deduplicates concurrent getSources requests by request key.\n */\n readonly #inFlight = new AsyncDeduper<string, Result<GatheredSources, OMSSSourceGatheringError>>()\n\n constructor(omssServer: OMSSServer, providerRegistry: ProviderRegistry, hookRegistry: HookRegistry<OMSSHooks>, extractorService: ExtractorService) {\n this.#hookRegistry = hookRegistry\n this.#core = new SourceCore(omssServer, providerRegistry, extractorService)\n }\n\n public get cleaningFunction(): CleaningFunction {\n return this.#cleaningFunction\n }\n\n public set cleaningFunction(fn: CleaningFunction) {\n this.#cleaningFunction = fn\n }\n\n /**\n * Register middleware for a SourceService method.\n *\n * Middleware can be used for cross-cutting concerns such as caching,\n * logging, tracing, or metrics.\n *\n * @param method - Middleware-enabled method name.\n * @param handler - Middleware handler.\n */\n use<TMethod extends keyof SourceServiceOperations>(method: TMethod, handler: SourceServiceMiddleware<TMethod>): void {\n this.#middleware.use(method, handler)\n }\n\n /**\n * Fetch sources from all matching providers for an OMSS ID.\n *\n * This method is middleware-enabled. Concurrent requests for the same\n * `omssId` and `providerId` share the same in-flight Promise until the\n * request settles.\n *\n * @param omssId - OMSS identifier such as `\"tmdb:12345\"`.\n * @param options - Optional source gathering parameters.\n * @returns Aggregated provider results or a source gathering error.\n */\n async getSources(omssId: OMSSId, options: GetSourcesOptions = {}): Promise<Result<GatheredSources, OMSSSourceGatheringError>> {\n return this.#middleware.run('getSources', { omssId, options }, () => this.#internalGetSources(omssId, options))\n }\n\n /**\n * Get and set the cleaning function for the source core.\n */\n #cleaningFunction: CleaningFunction = (obj) => obj\n\n /**\n * Internal wrapper around source gathering.\n *\n * Runs lifecycle hooks and deduplicates concurrent requests before\n * delegating to {@link SourceCore}.\n *\n * @param omssId - OMSS identifier.\n * @param options - Optional source gathering parameters.\n * @returns Aggregated provider results or a source gathering error.\n */\n async #internalGetSources(omssId: OMSSId, options: GetSourcesOptions): Promise<Result<GatheredSources, OMSSSourceGatheringError>> {\n await this.#hookRegistry.run('beforeGetSources', {\n omssId,\n providerId: options.providerId,\n })\n\n const inFlightKey = this.#getInFlightKey(omssId, options.providerId)\n\n const result = await this.#inFlight.run(inFlightKey, () =>\n this.#core.getSources(omssId, options, options.providerHookService ?? new HookService<ProviderHooks>(), options.cleaningFunction ?? this.cleaningFunction)\n )\n\n if (result.ok) {\n await this.#hookRegistry.run('afterGetSources', {\n omssId,\n providerId: options.providerId,\n result: result.value,\n })\n\n return this.#middleware.run('afterGetSources', { omssId, options, result }, () => Promise.resolve(result))\n }\n\n await this.#hookRegistry.run('getSourcesFailed', {\n omssId,\n providerId: options.providerId,\n error: result.error,\n })\n\n return result\n }\n\n /**\n * Build the stable in-flight key for a getSources request.\n *\n * @param omssId - OMSS identifier.\n * @param providerId - Optional provider filter.\n * @returns Unique in-flight request key.\n */\n #getInFlightKey(omssId: OMSSId, providerId?: string): string {\n return `${omssId}|${providerId ?? ''}`\n }\n}\n","import type { UnknownProvider } from '@/types/provider.js'\nimport { OMSSProviderError } from '@/utils/error.js'\nimport { ERR, OK, validateSafeUniqueString } from '@/utils/utils.js'\nimport { Result } from '@/types/utils.js'\nimport { CATALOG_ENTRY } from '@/utils/regexp.js'\n\n/**\n * Registry responsible for storing and managing OMSS Providers.\n *\n * Providers are stored by their unique {@link UnknownProvider.id}.\n * This registry does not know about hooks.\n */\nexport class ProviderRegistry {\n /**\n * Internal map of registered providers, keyed by provider ID.\n */\n readonly #providers = new Map<string, UnknownProvider>()\n\n /**\n * Adds a provider to the registry.\n *\n * @param provider - The provider instance to register.\n * @returns `OK` if registration succeeded, `ERR` if the provider ID is already taken.\n */\n async add(provider: UnknownProvider): Promise<Result<UnknownProvider, OMSSProviderError>> {\n if (this.#providers.has(provider.id)) {\n return ERR(new OMSSProviderError(`Provider \"${provider.id}\" is already registered`))\n }\n\n const valProvIdReq = validateSafeUniqueString(provider.id, 'provider ID', OMSSProviderError)\n if (!valProvIdReq.ok) {\n return ERR(valProvIdReq.error)\n }\n\n const valResolverNamespaceReq = validateSafeUniqueString(provider.resolver.namespace, 'resolver namespace for resolver with name: \"' + provider.resolver.name + '\" and id:', OMSSProviderError)\n if (!valResolverNamespaceReq.ok) {\n return ERR(valResolverNamespaceReq.error)\n }\n\n if (this.getAll().some((p) => p.resolver.namespace === provider.resolver.namespace && p.resolver !== provider.resolver)) {\n return ERR(new OMSSProviderError(`Resolver namespace \"${provider.resolver.namespace}\" is already registered by another provider and another resolver. Use one resolver per namespace.`))\n }\n\n if (provider.catalog) {\n const entries = await provider.catalog()\n const hasWildcard = entries.includes('*')\n const hasOther = entries.some((id) => id !== '*')\n\n if (hasWildcard && hasOther) {\n return ERR(new OMSSProviderError(`Provider \"${provider.id}\" catalog contains \"*\" mixed with other IDs. Use either a single \"*\" or a list of valid IDs.`))\n }\n\n for (const id of entries) {\n if (!CATALOG_ENTRY.test(id)) {\n return ERR(new OMSSProviderError(`Provider \"${provider.id}\" catalog contains an invalid entry \"${id}\". Entries must be non-empty strings with no whitespace, or \"*\".`))\n }\n }\n }\n\n this.#providers.set(provider.id, provider)\n return OK(provider)\n }\n\n /**\n * Retrieves a registered provider by its ID.\n *\n * @param id - The unique provider ID.\n * @returns The provider instance, or `undefined` if not found.\n */\n get(id: string): UnknownProvider | undefined {\n return this.#providers.get(id)\n }\n\n /**\n * Returns all registered providers.\n */\n getAll(filter?: (p: UnknownProvider) => boolean): UnknownProvider[] {\n if (filter) {\n return [...this.#providers.values()].filter(filter)\n }\n return [...this.#providers.values()]\n }\n\n /**\n * Returns whether a provider with the given ID is registered.\n *\n * @param id - The unique provider ID.\n */\n has(id: string): boolean {\n return this.#providers.has(id)\n }\n}\n","import type { ProviderServiceMiddleware, ProviderServiceOperations, UnknownProvider } from '@/types/provider.js'\r\nimport { OMSSProviderError } from '@/utils/error.js'\r\nimport { ERR, OK } from '@/utils/utils.js'\r\nimport { ProviderRegistry } from '@/features/providers/ProviderRegistry.js'\r\nimport { HookRegistry } from '@/features/hooks/HookRegistry.js'\r\nimport type { Result } from '@/types/utils.js'\r\nimport type { OMSSHooks } from '@/types/hooks.js'\r\nimport { MiddlewareRunner } from '@/utils/middleware.js'\r\n\r\n/**\r\n * The public API for managing OMSS Providers.\r\n */\r\nexport class ProviderService {\r\n readonly #providerRegistry: ProviderRegistry\r\n readonly #hookRegistry: HookRegistry<OMSSHooks>\r\n readonly #middleware = new MiddlewareRunner<ProviderServiceOperations>()\r\n #insideBeforeProviderRegister = false\r\n\r\n constructor(providerRegistry: ProviderRegistry, hookRegistry: HookRegistry<OMSSHooks>) {\r\n this.#providerRegistry = providerRegistry\r\n this.#hookRegistry = hookRegistry\r\n }\r\n\r\n /**\r\n * Adds middleware to the `register` pipeline.\r\n * Middlewares run in insertion order, after hooks and before the\r\n * actual registry `add()` call.\r\n */\r\n use<TMethod extends keyof ProviderServiceOperations>(method: TMethod, handler: ProviderServiceMiddleware<TMethod>): this {\r\n this.#middleware.use(method, handler)\r\n return this\r\n }\r\n\r\n /**\r\n * Registers a provider into the system.\r\n */\r\n async register(provider: UnknownProvider): Promise<Result<UnknownProvider, OMSSProviderError>> {\r\n if (this.#insideBeforeProviderRegister) {\r\n return ERR(new OMSSProviderError('Providers cannot be registered during beforeProviderRegister'))\r\n }\r\n\r\n this.#insideBeforeProviderRegister = true\r\n try {\r\n await this.#hookRegistry.run('beforeProviderRegister', { provider })\r\n } finally {\r\n this.#insideBeforeProviderRegister = false\r\n }\r\n\r\n const result = await this.#middleware.run('register', { provider }, () => this.#providerRegistry.add(provider))\r\n\r\n if (!result.ok) {\r\n await this.#hookRegistry.run('providerRegisterFailed', {\r\n provider,\r\n error: result.error,\r\n })\r\n return ERR(result.error)\r\n }\r\n\r\n await this.#hookRegistry.run('afterProviderRegister', { provider })\r\n return result\r\n }\r\n\r\n /**\r\n * Retrieves a registered provider by its ID.\r\n *\r\n * @param id - The provider ID to look up.\r\n * @returns The provider instance, or `undefined` if not found.\r\n */\r\n get(id: string): ReturnType<ProviderRegistry['get']> {\r\n return this.#providerRegistry.get(id)\r\n }\r\n\r\n /**\r\n * Returns all registered providers.\r\n * @param filter - Optional filter function to apply to providers.\r\n */\r\n getAll(filter?: (p: UnknownProvider) => boolean): ReturnType<ProviderRegistry['getAll']> {\r\n return this.#providerRegistry.getAll(filter)\r\n }\r\n\r\n /**\r\n * Returns whether a provider with the given ID has been registered.\r\n *\r\n * @param id - The provider ID to check.\r\n */\r\n has(id: string): ReturnType<ProviderRegistry['has']> {\r\n return this.#providerRegistry.has(id)\r\n }\r\n\r\n /**\r\n * Returns a map of all namespaces and an array of all known identifiers for each namespace.\r\n * This list is BEST EFFORT ONLY. Do not rely on this. If any provider returns a `*` automatically, the namespace will support all identifiers (e.g. `\"tmdb\": [\"*\"], \"imdb\": [\"tt37636\", \"...\"]`.\r\n */\r\n async catalog(): Promise<Result<Map<string, string[]>, OMSSProviderError>> {\r\n const allProviders = this.getAll()\r\n const result = new Map<string, string[]>()\r\n\r\n await Promise.all(\r\n allProviders.map(async (provider) => {\r\n if (!provider.catalog) return\r\n\r\n const namespace = provider.resolver.namespace\r\n const entries = await provider.catalog()\r\n\r\n // If namespace already collapsed to wildcard, skip.\r\n if (result.get(namespace)?.[0] === '*') return\r\n\r\n if (entries.includes('*')) {\r\n // Any single wildcard provider collapses the whole namespace.\r\n result.set(namespace, ['*'])\r\n return\r\n }\r\n\r\n // Merge deduplicated IDs into the namespace bucket.\r\n const existing = result.get(namespace) ?? []\r\n const merged = Array.from(new Set([...existing, ...entries]))\r\n result.set(namespace, merged)\r\n })\r\n )\r\n\r\n return OK(result)\r\n }\r\n\r\n /**\r\n * Returns the catalog for a single namespace.\r\n * Returns `undefined` if no provider in that namespace exposes a catalog.\r\n *\r\n * @param namespace - The resolver namespace to look up (e.g. `\"tmdb\"`).\r\n * @returns Merged list of IDs for the namespace, `[\"*\"]` if any provider\r\n * signals wildcard support, or `undefined` if no catalog data exists.\r\n */\r\n async catalogForNamespace(namespace: string): Promise<Result<string[], OMSSProviderError>> {\r\n const providers = this.getAll((p) => p.resolver.namespace === namespace)\r\n if (providers.length === 0) return ERR(new OMSSProviderError(`No providers registered for namespace \"${namespace}\"`))\r\n\r\n const result: string[] = []\r\n\r\n for (const provider of providers) {\r\n if (!provider.catalog) continue\r\n\r\n const entries = await provider.catalog()\r\n\r\n if (entries.includes('*')) {\r\n return OK(['*'])\r\n }\r\n\r\n for (const id of entries) {\r\n if (!result.includes(id)) {\r\n result.push(id)\r\n }\r\n }\r\n }\r\n\r\n return result.length > 0 ? OK(result) : ERR(new OMSSProviderError(`No catalog data available for namespace \"${namespace}\"`))\r\n }\r\n}\r\n","import { ExtractorRegistry } from '@/features/extractors/ExtractorRegistry.js'\nimport { HookRegistry } from '@/features/hooks/HookRegistry.js'\nimport type { OMSSHooks } from '@/types/hooks.js'\nimport { Extractor } from '@/types/extractor.js'\nimport { Result } from '@/types/utils.js'\nimport { ERR, OK } from '@/utils/utils.js'\nimport { OMSSExtractorError } from '@/utils/error.js'\n\nexport class ExtractorService {\n readonly #extractorRegistry: ExtractorRegistry\n readonly #hookRegistry: HookRegistry<OMSSHooks>\n #insideBeforeRegisterExtractor = false\n\n constructor(extractorRegistry: ExtractorRegistry, hookRegistry: HookRegistry<OMSSHooks>) {\n this.#extractorRegistry = extractorRegistry\n this.#hookRegistry = hookRegistry\n }\n\n /**\n * Get all registered extractors (read-only).\n */\n get extractors(): Result<ReadonlyArray<Extractor>, Error> {\n return OK(this.#extractorRegistry.extractors)\n }\n\n /**\n * Find an extractor capable of handling the given URL.\n *\n * @param url - URL to search for.\n * @returns The first matching {@link Extractor} or an {@link OMSSExtractorError}.\n */\n async find(url: string): Promise<Result<Extractor, OMSSExtractorError>> {\n await this.#hookRegistry.run('beforeFindExtractor', { url })\n\n const extractors = this.#extractorRegistry.extractors\n\n const results = await Promise.all(extractors.map((extractor) => extractor.matcher(url)))\n\n for (let i = 0; i < extractors.length; i++) {\n if (results[i]!.ok) {\n const extractor = extractors[i]!\n\n await this.#hookRegistry.run('afterFindExtractor', {\n url,\n extractor,\n })\n\n return OK(extractor)\n }\n }\n\n const error = new OMSSExtractorError(`No extractor found for URL \"${url}\"`)\n\n await this.#hookRegistry.run('findExtractorFailed', {\n url,\n error,\n })\n\n return ERR(error)\n }\n\n /**\n * Register an extractor.\n *\n * @param extractor - Extractor to register.\n */\n async register(extractor: Extractor): Promise<Result<void, Error>> {\n if (this.#insideBeforeRegisterExtractor) {\n return ERR(new OMSSExtractorError('Extractors cannot be registered during beforeRegisterExtractor'))\n }\n\n this.#insideBeforeRegisterExtractor = true\n\n try {\n await this.#hookRegistry.run('beforeRegisterExtractor', {\n extractor,\n })\n } finally {\n this.#insideBeforeRegisterExtractor = false\n }\n\n try {\n this.#extractorRegistry.add(extractor)\n } catch (error) {\n const extractorError = error instanceof OMSSExtractorError ? error : new OMSSExtractorError(error instanceof Error ? error.message : String(error))\n\n await this.#hookRegistry.run('extractorRegisterFailed', {\n extractor,\n error: extractorError,\n })\n\n return ERR(extractorError)\n }\n\n await this.#hookRegistry.run('afterRegisterExtractor', {\n extractor,\n })\n\n return OK()\n }\n\n /**\n * Remove every registered extractor.\n */\n reset(): Result<void, Error> {\n this.#extractorRegistry.reset()\n return OK()\n }\n\n /**\n * Determine whether an extractor has already been registered.\n *\n * @param extractor - Extractor to check.\n */\n has(extractor: Extractor): Result<boolean, Error> {\n return OK(this.#extractorRegistry.has(extractor))\n }\n\n /**\n * Remove an extractor.\n *\n * @param extractor - Extractor to remove.\n * @returns Whether the extractor was removed.\n */\n remove(extractor: Extractor): Result<boolean, Error> {\n return OK(this.#extractorRegistry.remove(extractor))\n }\n}\n","import { Extractor } from '@/types/extractor.js'\n\n/**\n * Extractor Registry\n *\n * Manages all extractors.\n */\nexport class ExtractorRegistry {\n /**\n * Array of extractors.\n */\n readonly #extractors = Array<Extractor>()\n\n /**\n * Get all extractors.\n */\n get extractors(): Readonly<Extractor[]> {\n return this.#extractors\n }\n\n /**\n * Clear all extractors.\n */\n reset(): void {\n this.#extractors.length = 0\n }\n\n /**\n * Add an extractor.\n * @param extractor - Extractor to add.\n */\n add(extractor: Extractor): void {\n if (this.#extractors.some((e) => e === extractor)) return\n\n this.#extractors.push(extractor)\n }\n\n /**\n * Check if an extractor is already registered.\n * @param extractor - Extractor to check.\n */\n has(extractor: Extractor): boolean {\n return this.#extractors.includes(extractor)\n }\n\n /**\n * Remove an extractor.\n * @param extractor - Extractor to remove.\n * @returns True if the extractor was removed, false otherwise.\n */\n remove(extractor: Extractor): boolean {\n if (!this.has(extractor)) return false\n this.#extractors.splice(this.#extractors.indexOf(extractor), 1)\n return true\n }\n}\n","import { HookRegistry } from '@/features/hooks/HookRegistry.js'\r\nimport { HookService } from '@/features/hooks/HookService.js'\r\nimport { PluginRegistry } from '@/features/plugins/PluginRegistry.js'\r\nimport { PluginService } from '@/features/plugins/PluginService.js'\r\nimport { OMSSServerError } from '@/utils/error.js'\r\nimport type { OMSSConfig } from '@/types/config.js'\r\nimport { SourceService } from '@/features/source/SourceService.js'\r\nimport { ProviderRegistry } from '@/features/providers/ProviderRegistry.js'\r\nimport { ERR, OK } from '@/utils/utils.js'\r\nimport { Result } from '@/types/utils.js'\r\nimport { ProviderService } from '@/features/providers/ProviderService.js'\r\nimport { OMSSHooks } from '@/types/hooks.js'\r\nimport { ExtractorService } from '@/features/extractors/ExtractorService.js'\r\nimport { ExtractorRegistry } from '@/features/extractors/ExtractorRegistry.js'\r\n\r\n/**\r\n * Core server class for OMSS.\r\n */\r\nexport class OMSSServer {\r\n readonly hooks: HookService<OMSSHooks>\r\n readonly plugins: PluginService\r\n readonly providers: ProviderService\r\n readonly sources: SourceService\r\n readonly extractors: ExtractorService\r\n readonly #config: OMSSConfig\r\n\r\n /**\r\n * Creates a new OMSSServer instance.\r\n *\r\n * @param config - Immutable server configuration\r\n */\r\n constructor(config: OMSSConfig) {\r\n this.#config = config\r\n\r\n const hooksRegistry = new HookRegistry<OMSSHooks>()\r\n const extractorRegistry = new ExtractorRegistry()\r\n const pluginRegistry = new PluginRegistry(this)\r\n const providerRegistry = new ProviderRegistry()\r\n\r\n this.hooks = new HookService<OMSSHooks>(hooksRegistry)\r\n this.extractors = new ExtractorService(extractorRegistry, hooksRegistry)\r\n this.plugins = new PluginService(this, pluginRegistry, hooksRegistry)\r\n this.providers = new ProviderService(providerRegistry, hooksRegistry)\r\n this.sources = new SourceService(this, providerRegistry, hooksRegistry, this.extractors)\r\n }\r\n\r\n /**\r\n * Get the OMSS Config from the constructor\r\n * @returns the initialised OMSS Config\r\n */\r\n get config(): Readonly<OMSSConfig> {\r\n return this.#config\r\n }\r\n\r\n /**\r\n * Decorate the OMSSServer instance with a new property.\r\n * @param name - The name of the property to be decorated.\r\n * @param value - The value to be assigned to the property.\r\n * @param deps - An array of dependency names.\r\n * @returns The name of the decorated property in the {@link Result} object.\r\n */\r\n decorate<T>(name: string, value: T, deps: string[] = []): Result<string, OMSSServerError> {\r\n if (Object.hasOwn(this, name)) {\r\n return ERR(\r\n new OMSSServerError(`Decorator \"${name}\" already exists`, {\r\n cause: {\r\n existing: this[name as keyof this],\r\n },\r\n })\r\n )\r\n }\r\n\r\n for (const dep of deps) {\r\n if (!this.hasDecorator(dep)) {\r\n return ERR(new OMSSServerError(`\"${name}\" depends on \"${dep}\", which does not exist`))\r\n }\r\n }\r\n\r\n // modify the instance to make the decorator available as a property\r\n Object.defineProperty(this, name, {\r\n value,\r\n writable: false,\r\n configurable: false,\r\n enumerable: true,\r\n })\r\n\r\n return OK(name)\r\n }\r\n\r\n /**\r\n * Check if a decorator with the given name exists.\r\n * @param name - The name of the decorator to check.\r\n * @returns True if the decorator exists, false otherwise.\r\n */\r\n hasDecorator(name: string): boolean {\r\n return Object.hasOwn(this, name)\r\n }\r\n\r\n /**\r\n * Get a decorated property by its name.\r\n * @param name - The name of the property to retrieve.\r\n * @returns The decorated property value in the {@link Result} object.\r\n */\r\n getDecorator<T>(name: string): Result<T, OMSSServerError> {\r\n if (!Object.hasOwn(this, name)) {\r\n return ERR(new OMSSServerError(`Decorator \"${name}\" not found`))\r\n }\r\n\r\n return OK(this[name as keyof this] as T)\r\n }\r\n}\r\n\r\nexport default OMSSServer\r\n","import { BaseResolver } from '@/features/resolvers/BaseResolver.js'\r\nimport { OMSSProvider, ProviderResult, ProviderResultEmitter, ProviderSourcesMeta, ResolverMetadata } from '@/types/provider.js'\r\nimport type { ParsedOMSSId } from '@/types/resolver.js'\r\nimport { NonEmptyArray } from '@/types/utils.js'\r\n\r\n/**\r\n * Base class for all providers.\r\n */\r\nexport abstract class BaseProvider<P extends BaseResolver<unknown>> implements OMSSProvider<P> {\r\n /**\r\n * Provider ID. Must be unique.\r\n */\r\n abstract readonly id: string\r\n\r\n /**\r\n * Friendly name of the provider.\r\n */\r\n abstract readonly name: string\r\n\r\n /**\r\n * Whether the provider will be used.\r\n */\r\n abstract readonly enabled: boolean\r\n\r\n /**\r\n * Catalog of media this provider supports. It does not have to exist. If it does, it should be a list of media IDs.\r\n * This does not get queried for source resolving, but more metadata about the provider.\r\n */\r\n abstract readonly catalog?: () => Promise<NonEmptyArray<string>> | NonEmptyArray<string>\r\n\r\n /**\r\n * Provide a method that checks whether this provider supports a certain ID.\r\n * @param id - Parsed OMSS ID\r\n */\r\n abstract readonly supportsId: (id: ParsedOMSSId) => boolean | Promise<boolean>\r\n\r\n /**\r\n * Resolvers that this provider supports.\r\n */\r\n abstract readonly resolver: P\r\n\r\n /**\r\n * Fetch sources for a certain media.\r\n * @param media - Return object of the resolver's resolve() method.\r\n * @param result - The result emitter.\r\n */\r\n abstract getSources(media: ProviderSourcesMeta<ResolverMetadata<P>>, result: ProviderResultEmitter): Promise<ProviderResult>\r\n}\r\n","import { OMSSResolverError, Result } from '@/public-api.js'\nimport { OMSSId, OMSSResolver, ParsedOMSSId, ResolverExecutionContext, ResolverResult } from '@/types/resolver.js'\n\n/**\n * Base class for all OMSS resolvers.\n *\n * Resolvers convert an OMSS ID into usable metadata for providers.\n */\nexport abstract class BaseResolver<T> implements OMSSResolver<T> {\n /**\n * Namespace this resolver owns, e.g. \"tmdb\".\n * Must be unique for a single server instance.\n */\n abstract namespace: string\n\n /**\n * Human-readable resolver name.\n */\n abstract name: string\n\n /**\n * A map of ID converters.\n *\n * @key - The unhandled namespace\n * @value - A function that converts that id (from an unknown namespace/id provider) to this resolver's namespace.\n */\n abstract converter: Map<string, (noHandlerId: OMSSId, ctx: ResolverExecutionContext) => Promise<Result<OMSSId, OMSSResolverError>>>\n\n /**\n * Resolve a single ID into metadata.\n *\n * @param id - Parsed OMSS ID\n * @param ctx - Execution context.\n */\n abstract resolve(id: ParsedOMSSId, ctx: ResolverExecutionContext): Promise<ResolverResult<T>>\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["#hooks","#hookRegistry","#states","#server","#stack","#pluginRegistry","#hookRegistry","#insideBeforePluginRegister","#providerRegistry","#extractorService","#omssServer","#entries","#handlers","#hookRegistry","#core","#middleware","#inFlight","#cleaningFunction","#internalGetSources","#getInFlightKey","#providers","#providerRegistry","#hookRegistry","#middleware","#insideBeforeProviderRegister","#extractorRegistry","#hookRegistry","#insideBeforeRegisterExtractor","#extractors","#config"],"sources":["../src/features/hooks/HookRegistry.ts","../src/features/hooks/HookService.ts","../src/features/plugins/plugin-state.ts","../src/utils/error.ts","../src/utils/regexp.ts","../src/utils/utils.ts","../src/features/plugins/PluginRegistry.ts","../src/features/plugins/PluginService.ts","../src/features/resolvers/utils.ts","../src/features/providers/ProviderResultEmitter.ts","../src/features/source/SourceCore.ts","../src/utils/AsyncDeduper.ts","../src/utils/middleware.ts","../src/features/source/SourceService.ts","../src/features/providers/ProviderRegistry.ts","../src/features/providers/ProviderService.ts","../src/features/extractors/ExtractorService.ts","../src/features/extractors/ExtractorRegistry.ts","../src/core/server.ts","../src/features/providers/BaseProvider.ts","../src/features/resolvers/BaseResolver.ts"],"sourcesContent":["/**\n * Hook Registry\n *\n * Manages lifecycle hooks for OMSS events.\n */\nexport class HookRegistry<T> {\n /**\n * Map storing arrays of hook handlers for each hook name.\n */\n readonly #hooks = new Map<keyof T, unknown[]>()\n\n /**\n * Get all registered hooks immutable.\n * @dangerous - Be careful with this. what you are doing might cause side effects.\n */\n get hooks(): ReadonlyMap<keyof T, unknown[]> {\n return this.#hooks\n }\n\n /**\n * Clear all registered hooks.\n * @dangerous - Be careful with this. Might cause side effects.\n */\n reset(): void {\n return this.#hooks.clear()\n }\n\n /**\n * Run all hooks for a lifecycle event with the provided payload.\n *\n * @typeParam K - The name of the hook to run.\n * @param name - The hook name (key of THooks).\n * @param payload - The payload to pass to each hook handler.\n */\n async run<K extends keyof T>(name: K, payload: T[K] extends (payload: infer P) => unknown ? P : never): Promise<void> {\n const fns = this.#hooks.get(name) ?? []\n\n for (const fn of fns) {\n await (fn as (payload: unknown) => void | Promise<void>)(payload)\n }\n }\n\n /**\n * Add a hook handler for a lifecycle event.\n * @param name - The hook name (key of THooks).\n * @param cb - The hook handler function.\n */\n add<K extends keyof T>(name: K, cb: T[K]): void {\n const existing = this.#hooks.get(name) ?? []\n\n this.#hooks.set(name, [...existing, cb])\n }\n}\n","import { HookRegistry } from '@/features/hooks/HookRegistry.js'\n\nexport class HookService<T> {\n readonly #hookRegistry: HookRegistry<T>\n\n constructor(hookRegistry: HookRegistry<T> = new HookRegistry<T>()) {\n this.#hookRegistry = hookRegistry\n }\n\n /**\n * Get all registered hooks immutable. TO ADD HOOKS, USE THE ADD METHOD\n * @dangerous - Be careful with this. what you are doing might cause side effects.\n */\n get hooks(): ReadonlyMap<keyof T, unknown[]> {\n return this.#hookRegistry.hooks\n }\n\n /**\n * Register a hook for a lifecycle event.\n *\n * @typeParam K - The name of the hook to register.\n * @param name - The hook name (key of THooks).\n * @param cb - The handler function for this hook.\n */\n add<K extends keyof T>(name: K, cb: T[K]): ReturnType<HookRegistry<T>['add']> {\n this.#hookRegistry.add(name, cb)\n }\n\n /**\n * Clear all registered hooks.\n * @dangerous - Be careful with this. Might cause side effects.\n */\n reset(): ReturnType<HookRegistry<T>['reset']> {\n return this.#hookRegistry.reset()\n }\n\n /**\n * Get the hook registry.\n *\n * This is only exposed for internal purposes and should not be accessed in consumer projects.\n * @dangerous\n * @internal\n */\n __getRegistry(): HookRegistry<T> {\n return this.#hookRegistry\n }\n}\n","/**\n * The states of which a plugin can be.\n */\nexport enum PluginState {\n Registering,\n Registered,\n Unavailable,\n}\n","/**\n * Base class for all OMSS framework errors.\n * Use `instanceof OMSSError` to catch any framework error.\n * Always throw a specific subclass, never this directly.\n * Cause is not standardized and may change frequently.\n */\nexport class OMSSError extends Error {\n /**\n * Create a new OMSSError.\n * @param message - Error message\n * @param options - Additional options for the error\n */\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options)\n this.name = this.constructor.name\n }\n}\n\n/**\n * Returned when an error gets Returned in the OMSSServer class.\n *\n * @example\n * return ERR(OMSSServerError('config.name must be a non-empty string', {cause: config}))\n */\nexport class OMSSServerError extends OMSSError {}\n\n/**\n * Returned during plugin registration or execution.\n *\n * @example\n * return ERR(OMSSPluginError(`Plugin \"${name}\" is already registered`, { cause: plugin }))\n */\nexport class OMSSPluginError extends OMSSError {}\n\n/**\n * Returned during resolver registration or ID resolution.\n *\n * @example\n * return ERR(OMSSResolverError(`No resolver found for namespace \"xyz\"`, { cause: rawId }))\n */\nexport class OMSSResolverError extends OMSSError {}\n\n/**\n * Returned during provider registration or source fetching.\n *\n * @example\n * return ERR(OMSSProviderError('Provider must have at least one resolver', { cause: provider }))\n */\nexport class OMSSProviderError extends OMSSError {}\n\n/**\n * Returned when an extractor fails to extract the media from a host.\n *\n * @example\n * return ERR(OMSSExtractor('Failed to extract media from host due to host changes', { cause: html }))\n */\nexport class OMSSExtractorError extends OMSSError {}\n\n/**\n * Returned when something/several things fail during source gathering.\n *\n * @example\n * return ERR(OMSSSourceGatheringError('Failed to gather sources', { cause: providerResults }))\n */\nexport class OMSSSourceGatheringError extends OMSSError {}\n","/**\n * Regex for validating namespace names.\n */\nconst SAFE_UNIQUE_STRING_PATTERN = '[a-z0-9-]+'\nexport const SAFE_UNIQUE_STRING = new RegExp(`^${SAFE_UNIQUE_STRING_PATTERN}$`)\n\n/**\n * Regex for matching media formats by keyword or file extension.\n *\n * Matches:\n * - format keywords (`hls`, `dash`, `mp4`, etc.)\n * - common media file extensions\n * - extensions followed by URL query strings, fragments, paths, or the end of the URL\n */\nexport const HLS_REGEX = /\\bhls\\b|\\.(?:m3u8|ts)(?:$|[\\/?#])/i\nexport const MP4_REGEX = /\\bmp4\\b|\\.mp4(?:$|[\\/?#])/i\nexport const DASH_REGEX = /\\bdash\\b|\\.(?:mpd|m4a)(?:$|[\\/?#])/i\nexport const MKV_REGEX = /\\bmkv\\b|\\.mkv(?:$|[\\/?#])/i\nexport const VTT_REGEX = /\\bvtt\\b|\\.vtt(?:$|[\\/?#])/i\nexport const SRT_REGEX = /\\bsrt\\b|\\.srt(?:$|[\\/?#])/i\n\n/**\n * Regex for validating a single catalog entry value.\n *\n * A catalog entry must be either exactly `\"*\"` (wildcard — provider supports\n * all IDs in the namespace) or a safe unique string.\n *\n * Mixing `\"*\"` with other entries in the same catalog array is not allowed\n * and is validated separately at registration time.\n */\nexport const CATALOG_ENTRY = new RegExp(`^(?:\\\\*|${SAFE_UNIQUE_STRING_PATTERN})$`)\n","import { Result } from '@/types/utils.js'\nimport { OMSSError } from '@/utils/error.js'\nimport { SAFE_UNIQUE_STRING } from '@/utils/regexp.js'\n\n/**\n * Convenience factory for a successful result.\n */\nexport function OK(): Result<void, never>\nexport function OK<T>(value: T): Result<T, never>\n\nexport function OK(value?: unknown) {\n if (arguments.length === 0) {\n return { ok: true }\n }\n\n return { ok: true, value }\n}\n/**\n * Convenience factory for a failed result.\n */\nexport const ERR = <E extends Error>(error: E): Result<never, E> => ({ ok: false, error })\n\ntype ErrorConstructor<T extends OMSSError> = new (message: string, options?: { cause?: Error }) => T\n\n/**\n * Validate a string is safe for use as a unique identifier (only lowercase letters, numbers, and hyphens).\n * @param value - The string to validate\n * @param name - The name of the identifier, for error messages\n * @param ErrorType - The error type to return if validation fails\n */\nexport function validateSafeUniqueString<T extends OMSSError>(value: string, name: string, ErrorType: ErrorConstructor<T>): Result<void, T> {\n if (!SAFE_UNIQUE_STRING.test(value)) {\n return ERR(new ErrorType(`Invalid ${name} \"${value}\". Expected only letters (lowercase), numbers, and hyphens.`))\n }\n\n return OK()\n}\n","import type { OMSSConfiguredPluginType, OMSSPluginOptions, OMSSPluginType, UnknownPluginType } from '@/types/plugin.js'\nimport OMSSServer from '@/core/server.js'\nimport { PluginState } from '@/features/plugins/plugin-state.js'\nimport { OMSSPluginError } from '@/utils/error.js'\nimport { Result } from '@/types/utils.js'\nimport { ERR, OK } from '@/utils/utils.js'\n\n/**\n * Registry responsible for executing and managing OMSS Plugins.\n *\n * Plugins are executed when added.\n */\nexport class PluginRegistry {\n /**\n * States of plugin x\n */\n readonly #states = new Map<UnknownPluginType, PluginState>()\n readonly #server: OMSSServer\n\n /**\n * Registration stack used for circular dependency detection.\n */\n readonly #stack: UnknownPluginType[] = []\n\n constructor(server: OMSSServer) {\n this.#server = server\n }\n\n async add(plugin: OMSSPluginType): Promise<Result<PluginState.Registered, OMSSPluginError>>\n\n async add<T>(plugin: OMSSConfiguredPluginType<T>, options: OMSSPluginOptions<T>): Promise<Result<PluginState.Registered, OMSSPluginError>>\n\n /**\n * Adds and runs a plugin with its options.\n *\n * @typeParam T - Plugin options type.\n * @param plugin - The plugin function to register.\n * @param options - Plugin options or a factory function that resolves options.\n */\n async add(plugin: UnknownPluginType, options?: unknown): Promise<Result<PluginState.Registered, OMSSPluginError>> {\n // Check whether this plugin is already known\n const state = this.#states.get(plugin)\n\n if (state === PluginState.Registering) {\n const chain = [...this.#stack, plugin].map((p) => p.name).join(' -> ')\n\n return ERR(new OMSSPluginError(`Circular plugin dependency detected: ${chain}`))\n }\n\n if (state === PluginState.Registered) {\n return ERR(new OMSSPluginError(`Plugin \"${plugin.name}\" is already registered`))\n }\n\n // Start registering\n this.#states.set(plugin, PluginState.Registering)\n this.#stack.push(plugin)\n\n // Build options if a factory function is provided\n const resolved = typeof options === 'function' ? (options as (server: OMSSServer) => unknown)(this.#server) : options\n\n try {\n // Check if the plugin has a single argument\n if (plugin.length === 1) {\n // execute the plugin with the server instance\n await (plugin as OMSSPluginType)(this.#server)\n } else {\n // execute the plugin with the server instance and resolved options\n await plugin(this.#server, resolved)\n }\n\n this.#states.set(plugin, PluginState.Registered)\n return OK(PluginState.Registered)\n } catch (err) {\n this.#states.delete(plugin)\n return ERR(err instanceof OMSSPluginError ? err : new OMSSPluginError(String(err), { cause: err }))\n } finally {\n this.#stack.pop()\n }\n }\n\n /**\n * Get the current plugin state.\n */\n getState<T>(plugin: UnknownPluginType | OMSSPluginType | OMSSConfiguredPluginType<T>): PluginState {\n return this.#states.get(plugin as UnknownPluginType) ?? PluginState.Unavailable\n }\n}\n","import { PluginRegistry } from '@/features/plugins/PluginRegistry.js'\nimport { HookRegistry } from '@/features/hooks/HookRegistry.js'\nimport type { OMSSConfiguredPluginType, OMSSPluginOptions, OMSSPluginType, UnknownPluginType } from '@/types/plugin.js'\nimport OMSSServer from '@/core/server.js'\nimport { ERR } from '@/utils/utils.js'\nimport { OMSSPluginError } from '@/utils/error.js'\nimport type { OMSSHooks } from '@/types/hooks.js'\n\n/**\n * The public API for managing OMSS plugins.\n */\nexport class PluginService {\n readonly #pluginRegistry: PluginRegistry\n readonly #hookRegistry: HookRegistry<OMSSHooks>\n #insideBeforePluginRegister = false\n\n constructor(omssServer: OMSSServer, pluginRegistry: PluginRegistry, hookRegistry: HookRegistry<OMSSHooks>) {\n this.#pluginRegistry = pluginRegistry\n this.#hookRegistry = hookRegistry\n }\n\n /**\n * Register an OMSS plugin with no config into the system.\n * @param plugin - Plugin function\n */\n async register(plugin: OMSSPluginType): ReturnType<PluginRegistry['add']>\n\n /**\n * Register an OMSS plugin with a config into the system.\n * @param plugin - Plugin function\n * @param options - Plugin configuration\n */\n async register<T>(plugin: OMSSConfiguredPluginType<T>, options: OMSSPluginOptions<T>): ReturnType<PluginRegistry['add']>\n\n /**\n * Registers an OMSS plugin that can take a config into the system, but does not need to.\n * @param plugin - Plugin implementation\n * @param options - Plugin configuration\n */\n async register(plugin: UnknownPluginType, options?: unknown): ReturnType<PluginRegistry['add']> {\n if (this.#insideBeforePluginRegister) {\n return ERR(new OMSSPluginError('Plugins cannot be registered during beforePluginRegister'))\n }\n\n this.#insideBeforePluginRegister = true\n try {\n await this.#hookRegistry.run('beforePluginRegister', { plugin: plugin as UnknownPluginType, options })\n } finally {\n this.#insideBeforePluginRegister = false\n }\n\n const result = await this.#pluginRegistry.add(plugin, options)\n\n if (!result.ok) {\n await this.#hookRegistry.run('pluginRegisterFailed', { plugin, options, error: result.error })\n return result\n }\n\n await this.#hookRegistry.run('afterPluginRegister', { plugin, options })\n return result\n }\n\n /**\n * Get the current State of a plugin\n * @param plugin - the plugin to get the state from\n * @returns - a value of the PluginState enum\n */\n getPluginState(plugin: UnknownPluginType): ReturnType<PluginRegistry['getState']> {\n return this.#pluginRegistry.getState(plugin)\n }\n}\n","import type { OMSSId, ParsedOMSSId } from '@/types/resolver.js'\nimport { OMSSResolverError } from '@/utils/error.js'\nimport { Result } from '@/types/utils.js'\nimport { ERR, OK, validateSafeUniqueString } from '@/utils/utils.js'\n\n/**\n * Parses an OMSS ID in the form `namespace:value_1[:value_2[:...]]`.\n */\nexport function parseOMSSId(id: OMSSId): Result<ParsedOMSSId, OMSSResolverError> {\n if (/\\s/.test(id)) {\n return ERR(new OMSSResolverError(`Invalid OMSS ID \"${id}\": cannot contain whitespace`))\n }\n\n const parts = id.split(':')\n\n if (parts.length < 2) {\n return ERR(new OMSSResolverError(`Invalid OMSS ID \"${id}\": missing namespace separator \":\"`))\n }\n\n const [namespace, ...values] = parts\n\n if (!namespace) {\n return ERR(new OMSSResolverError(`Invalid OMSS ID \"${id}\": namespace cannot be empty`))\n }\n\n const req = validateSafeUniqueString(namespace, 'OMSS namespace', OMSSResolverError)\n\n if (!req.ok) {\n return ERR(req.error)\n }\n\n for (const [i, value] of values.entries()) {\n if (value.length === 0) {\n return ERR(new OMSSResolverError(`Invalid OMSS ID \"${id}\": value ${i + 1} cannot be empty`))\n }\n }\n\n const decodedValues = values.map((value) => decodeURIComponent(value))\n\n return OK({\n namespace,\n values: decodedValues,\n raw: id,\n })\n}\n","import {\n EmittedSource,\n EmittedSubtitle,\n OMSSProviderResult,\n ProviderResult,\n ProviderResultEmitter,\n Source,\n SourceQuality,\n SourceTypes,\n Subtitle,\n SubtitleFormat,\n UnknownProvider,\n} from '@/types/provider.js'\nimport { OMSSProviderError } from '@/utils/error.js'\nimport { Result } from '@/types/utils.js'\nimport { ERR, OK } from '@/utils/utils.js'\nimport { HookRegistry } from '@/features/hooks/HookRegistry.js'\nimport { ProviderHooks } from '@/types/hooks.js'\nimport { DASH_REGEX, HLS_REGEX, MKV_REGEX, MP4_REGEX, SRT_REGEX, VTT_REGEX } from '@/utils/regexp.js'\nimport { CleaningFunction } from '@/types/source.js'\nimport { ParsedOMSSId } from '@/types/resolver.js'\n\n/**\n * Creates a fresh `ProviderResultEmitter` instance scoped to a single\n * `getSources()` execution.\n *\n * A new emitter MUST be created per provider call. Emitters hold internal,\n * mutable state (accumulated sources/subtitles/errors) via closures, so\n * reusing a single emitter across concurrent provider executions would\n * cause data from one provider to leak into another's response.\n *\n * @param provider - The provider instance for which this emitter is being created.\n * @param hookReg - The hook registry instance for managing hooks.\n * @param cleaningFunc - A function to clean up source/subtitle URLs and headers.\n * @param id - The parsed OMSS ID of the current request.\n * @returns A new `ProviderResultEmitter` bound to this execution.\n *\n */\nexport function createProviderResultEmitter(provider: Readonly<UnknownProvider>, hookReg: HookRegistry<ProviderHooks>, cleaningFunc: CleaningFunction, id: ParsedOMSSId): ProviderResultEmitter {\n /**\n * Accumulated sources emitted via `source()` during this execution.\n * Flushed into the final result when `done()` is called.\n */\n const sources: Source[] = []\n\n /**\n * Accumulated subtitles emitted via `subtitle()` during this execution.\n * Subtitles are intentionally NOT linked to any source or audio track,\n * so they live in their own flat array regardless of how many sources\n * were emitted.\n */\n const subtitles: Subtitle[] = []\n\n /**\n * Accumulated NON-fatal errors emitted via `error()` during this execution.\n * These are returned alongside successful results (via `done()`) so\n * that partial failures (e.g. \"server 2 of 3 failed\") don't discard\n * otherwise-valid sources.\n */\n const errors: OMSSProviderError[] = []\n\n return {\n /**\n * Utilities to make your life easier\n */\n utils: {\n /**\n * Utilities for parsing metadata of sources\n */\n source: {\n /**\n * Parse a string into a possible source type.\n * @param possibleType - The string to parse\n */\n parseType(possibleType: string): SourceTypes {\n if (HLS_REGEX.test(possibleType)) {\n return 'hls'\n } else if (MP4_REGEX.test(possibleType)) {\n return 'mp4'\n } else if (DASH_REGEX.test(possibleType)) {\n return 'dash'\n } else if (MKV_REGEX.test(possibleType)) {\n return 'mkv'\n } else {\n // most commonly used is hls\n return 'hls'\n }\n },\n /**\n * Parse a string into a possible source quality.\n * @param possibleQuality - The string to parse\n */\n parseQuality(possibleQuality: string): SourceQuality {\n if (!possibleQuality) {\n return 'Auto'\n }\n\n const value = possibleQuality.toLowerCase().trim().replace(/_/g, ' ').replace(/-/g, ' ')\n\n // Direct quality labels / aliases\n if (/\\b(8k|uhd\\s*8k|4320p)\\b/.test(value)) {\n return '8K'\n }\n\n if (/\\b(4k|uhd|ultra\\s*hd|2160p)\\b/.test(value)) {\n return '4K'\n }\n\n if (/\\b(qhd|2k|1440p|2560x1440)\\b/.test(value)) {\n return 'QHD'\n }\n\n if (/\\b(fhd|full\\s*hd|1080p|1920x1080)\\b/.test(value)) {\n return 'FHD'\n }\n\n if (/\\b(hd|720p|1280x720)\\b/.test(value)) {\n return 'HD'\n }\n\n if (/\\b(sd|480p|576p|360p|240p)\\b/.test(value)) {\n return 'SD'\n }\n\n const resolutionMatch = value.match(/(\\d{3,4})(?:p|x\\d{3,4})?/)\n\n if (resolutionMatch) {\n const resolution = Number(resolutionMatch[1])\n\n if (resolution >= 4320) {\n return '8K'\n }\n\n if (resolution >= 2160) {\n return '4K'\n }\n\n if (resolution >= 1440) {\n return 'QHD'\n }\n\n if (resolution >= 1080) {\n return 'FHD'\n }\n\n if (resolution >= 720) {\n return 'HD'\n }\n\n if (resolution > 0) {\n return 'SD'\n }\n }\n\n const bitrateMatch = value.match(/(\\d+(?:\\.\\d+)?)\\s*(mbps|kbps)/)\n\n if (bitrateMatch) {\n const bitrate = Number(bitrateMatch[1])\n const unit = bitrateMatch[2]\n\n const mbps = unit === 'kbps' ? bitrate / 1000 : bitrate\n\n if (mbps >= 25) {\n return '4K'\n }\n\n if (mbps >= 8) {\n return 'FHD'\n }\n\n if (mbps >= 3) {\n return 'HD'\n }\n\n return 'SD'\n }\n\n return 'Auto'\n },\n },\n /**\n * Utilities for parsing metadata of subtitles\n */\n subtitle: {\n /**\n * Parse a string into a possible subtitle format.\n * @param possibleFormat - The string to parse\n */\n parseFormat(possibleFormat: string): SubtitleFormat {\n if (VTT_REGEX.test(possibleFormat)) {\n return 'vtt'\n } else if (SRT_REGEX.test(possibleFormat)) {\n return 'srt'\n } else {\n return 'vtt'\n }\n },\n },\n },\n\n /**\n * Emits a custom, provider-defined action/event.\n *\n * @param action - A custom event name (e.g. \"cache.hit\").\n * @param data - Arbitrary payload associated with the event.\n */\n emit(action: string, data: unknown): void {\n // action cannot be whitespace or another hook name\n if (/\\s/.test(action) || Object.keys(this).includes(action)) {\n return\n }\n hookReg.run(action, { data, provider, id, timestamp: new Date().toISOString() })\n },\n\n /**\n * Logs verbose debug information. Intended for development/troubleshooting\n * only and should be stripped or gated behind a debug flag in production.\n *\n * @param args - Values to log, forwarded as-is (same semantics as `console.debug`).\n */\n debug(...args: unknown[]): void {\n hookReg.run('debug', { provider, args, id, timestamp: new Date().toISOString() })\n },\n\n /**\n * Logs general informational messages about provider execution\n * (e.g. \"Fetched media\", \"Cache miss, fetching from upstream\").\n *\n * @param args - Values to log.\n */\n info(...args: unknown[]): void {\n hookReg.run('info', { provider, args, id, timestamp: new Date().toISOString() })\n },\n\n /**\n * Logs a non-fatal warning. Use this for degraded-but-recoverable\n * situations (e.g. \"missing quality metadata, defaulting to Auto\").\n *\n * @param args - Values to log.\n */\n warn(...args: unknown[]): void {\n hookReg.run('warn', { provider, args, id, timestamp: new Date().toISOString() })\n },\n\n /**\n * Records a NON-fatal error. The provider continues executing after\n * calling this — use `fatal()` instead if the provider cannot continue.\n *\n * The error is accumulated and returned to the requestor as part of\n * the `diagnostics`/`errors` field once `done()` is called, allowing\n * partial success (e.g. some sources found despite one upstream\n * server failing).\n *\n * @param error - The error to record. Will be surfaced to the client.\n */\n error(error: OMSSProviderError): void {\n errors.push(error)\n hookReg.run('error', { provider, error, id, timestamp: new Date().toISOString() })\n },\n\n /**\n * Emits a single resolved source.\n *\n * @param source - The fully-formed source object to emit.\n */\n source(source: EmittedSource): void {\n const cleanedSource = {\n ...source,\n }\n\n const cleaned = cleaningFunc({\n url: cleanedSource.url,\n header: cleanedSource.header,\n })\n\n cleanedSource.url = cleaned.url\n cleanedSource.header = cleaned.header\n\n if ('audioTracks' in cleanedSource && cleanedSource.audioTracks) {\n // had to split first and rest, since audioTracks requires least one track.\n const [first, ...rest] = cleanedSource.audioTracks\n\n cleanedSource.audioTracks = [\n {\n ...first,\n ...cleaningFunc({\n url: first.url,\n header: first.header,\n }),\n },\n ...rest.map((track) => ({\n ...track,\n ...cleaningFunc({\n url: track.url,\n header: track.header,\n }),\n })),\n ]\n }\n const fullSource: Source = {\n ...cleanedSource,\n provider: {\n id: provider.id,\n name: provider.name,\n },\n }\n\n sources.push(fullSource)\n\n hookReg.run('source', {\n provider,\n source: fullSource,\n id,\n timestamp: new Date().toISOString(),\n })\n },\n\n /**\n * Emits a single subtitle track.\n *\n * @param subtitle - The subtitle object to emit.\n */\n subtitle(subtitle: EmittedSubtitle): void {\n const obj = {\n url: subtitle.url,\n header: subtitle.header,\n }\n const { url, header } = cleaningFunc(obj)\n subtitle.url = url\n subtitle.header = header\n const fullSub = { ...subtitle, provider: { id: provider.id, name: provider.name } }\n subtitles.push(fullSub)\n hookReg.run('subtitle', { provider, subtitle: fullSub, id, timestamp: new Date().toISOString() })\n },\n\n /**\n * Immediately aborts provider execution with a fatal error.\n *\n * @param error - The fatal error describing why the provider could not proceed.\n * @returns An `ERR` result wrapping the given error.\n */\n fatal(error: OMSSProviderError): Result<never, OMSSProviderError> {\n const accumulatedError = new AggregateError([error, ...errors], error.message, { cause: error.cause })\n\n const finalErr = new OMSSProviderError(accumulatedError.message, { cause: accumulatedError })\n\n hookReg.run('error', { provider, error: finalErr, id, timestamp: new Date().toISOString() })\n\n return ERR(finalErr)\n },\n\n /**\n * Signals that the provider has finished emitting sources/subtitles\n * and finalizes the result.\n *\n * @returns An `OK` result containing all accumulated sources,\n * subtitles, and non-fatal errors for this execution.\n */\n done(): ProviderResult {\n const result: OMSSProviderResult = {\n sources,\n subtitles,\n errors,\n }\n\n hookReg.run('done', { provider, result, id, timestamp: new Date().toISOString() })\n\n return OK(result)\n },\n }\n}\n","import OMSSServer from '@/core/server.js'\nimport { ProviderRegistry } from '@/features/providers/ProviderRegistry.js'\nimport { parseOMSSId } from '@/features/resolvers/utils.js'\nimport type { ProviderResult, Source, Subtitle, UnknownProvider } from '@/types/provider.js'\nimport type { OMSSId, ResolverExecutionContext } from '@/types/resolver.js'\nimport { CleaningFunction, GatheredSources, GetSourcesOptions } from '@/types/source.js'\nimport type { Result } from '@/types/utils.js'\nimport { OMSSProviderError, OMSSSourceGatheringError } from '@/utils/error.js'\nimport { ERR, OK } from '@/utils/utils.js'\nimport { createProviderResultEmitter } from '@/features/providers/ProviderResultEmitter.js'\nimport { ProviderHooks } from '@/types/hooks.js'\nimport { ExtractorService } from '@/features/extractors/ExtractorService.js'\nimport { HookService } from '@/features/hooks/HookService.js'\n\n/**\n * Internal source gathering core.\n *\n * Handles OMSS ID parsing, provider lookup, resolver-level deduplication,\n * provider execution, and final result aggregation.\n *\n * This class intentionally does not know about OMSS Hooks, middleware, or in-flight\n * request sharing. Those concerns belong to SourceService.\n */\nexport class SourceCore {\n readonly #providerRegistry: ProviderRegistry\n readonly #extractorService: ExtractorService\n readonly #omssServer: OMSSServer\n\n constructor(omssServer: OMSSServer, providerRegistry: ProviderRegistry, extractorService: ExtractorService) {\n this.#omssServer = omssServer\n this.#providerRegistry = providerRegistry\n this.#extractorService = extractorService\n }\n\n /**\n * Gather sources for a single OMSS ID.\n *\n * @param omssId - Raw OMSS identifier.\n * @param opts - Optional source gathering parameters.\n * @param providerHookRegistry - Hook registry for provider hooks.\n * @param cleaningFunction - Optional custom function to clean url's and headers\n * @returns Aggregated provider results or a source gathering error.\n */\n async getSources(\n omssId: OMSSId,\n opts: GetSourcesOptions,\n providerHookRegistry: HookService<ProviderHooks>,\n cleaningFunction: CleaningFunction\n ): Promise<Result<GatheredSources, OMSSSourceGatheringError>> {\n // try to parse the OMSS ID\n const parsed = parseOMSSId(omssId)\n\n if (!parsed.ok) {\n return ERR(new OMSSSourceGatheringError(`Failed to parse OMSS id \"${omssId}\": ${parsed.error.message}`, { cause: parsed.error }))\n }\n\n // we know that the id is valid now. Now we got to find the providers that can handle that namespace. If a specific provider is requested, we only look for that one.\n const providers: UnknownProvider[] = opts.providerId\n ? this.#providerRegistry.getAll((p) => p.id === opts.providerId && p.resolver.namespace === parsed.value.namespace)\n : this.#providerRegistry.getAll((p) => p.resolver.namespace === parsed.value.namespace)\n\n // if no provider can handle that namespace, return an error\n if (providers.length === 0) {\n return ERR(new OMSSSourceGatheringError(`No providers found for namespace \"${parsed.value.namespace}\"` + (opts.providerId ? ` and provider \"${opts.providerId}\"` : '')))\n }\n\n // if no abortsignal comes, just create a new one (does not abort)\n const signal = opts.abortSignal ?? new AbortController().signal\n\n // create the resolver context\n const ctx: ResolverExecutionContext = {\n server: this.#omssServer,\n signal,\n }\n\n /**\n * Resolver-level deduplication: multiple providers that share the same\n * resolver run that resolver only once, then share the result.\n */\n const resolverCache = new Map<string, Promise<Result<unknown, OMSSSourceGatheringError>>>()\n\n /**\n * Get resolver metadata for a provider, reusing the same resolver\n * promise when multiple providers share the same resolver.\n *\n * @param provider - Provider whose resolver metadata should be loaded.\n * @returns Resolver metadata or a source gathering error.\n */\n const getResolvedMeta = (provider: UnknownProvider): Promise<Result<unknown, OMSSSourceGatheringError>> => {\n const resolverKey = `${provider.resolver.namespace}:${provider.resolver.name}`\n\n let promise = resolverCache.get(resolverKey)\n\n if (!promise) {\n promise = (async (): Promise<Result<unknown, OMSSSourceGatheringError>> => {\n if (signal.aborted) {\n return ERR(new OMSSSourceGatheringError('Operation aborted'))\n }\n\n const result = await provider.resolver.resolve(parsed.value, ctx)\n\n if (!result.ok) {\n return ERR(new OMSSSourceGatheringError(`Resolver failed for ${resolverKey}: ${result.error.message}`, { cause: result.error }))\n }\n\n return OK(result.value)\n })()\n\n resolverCache.set(resolverKey, promise)\n }\n\n return promise\n }\n\n /**\n * Resolve sources for a single provider.\n *\n * @param provider - Provider to execute.\n * @returns Provider result or a source gathering error.\n */\n const resolveForProvider = async (provider: UnknownProvider): Promise<ProviderResult | Result<never, OMSSSourceGatheringError>> => {\n if (signal.aborted) {\n return ERR(new OMSSSourceGatheringError('Operation aborted'))\n }\n\n const supportsId = await provider.supportsId(parsed.value)\n if (!supportsId) {\n return OK({ sources: [], subtitles: [], errors: [new OMSSProviderError(`Provider \"${provider.id}\" did not support this id: \"${parsed.value.raw}\"`)] })\n }\n\n if (signal.aborted) {\n return ERR(new OMSSSourceGatheringError('Operation aborted'))\n }\n\n const metaResult = await getResolvedMeta(provider)\n\n if (!metaResult.ok) {\n return metaResult\n }\n\n if (signal.aborted) {\n return ERR(new OMSSSourceGatheringError('Operation aborted'))\n }\n\n const resultEmitter = createProviderResultEmitter(provider, providerHookRegistry.__getRegistry(), cleaningFunction, parsed.value)\n\n return provider.getSources(\n {\n utils: {\n omssId: parsed.value,\n abortSignal: signal,\n findExtractor: this.#extractorService.find,\n },\n meta: metaResult.value,\n },\n resultEmitter\n )\n }\n\n const settled = await Promise.allSettled(providers.map((provider) => resolveForProvider(provider)))\n\n if (signal.aborted) {\n return ERR(new OMSSSourceGatheringError('Operation aborted'))\n }\n\n const allSources: Source[] = []\n const allSubtitles: Subtitle[] = []\n const unexpectedErrors: unknown[] = []\n const omssErrors: Array<Extract<ProviderResult, { ok: false }>['error'] | OMSSSourceGatheringError> = []\n let hasSuccess = false\n\n for (const item of settled) {\n if (item.status === 'rejected') {\n omssErrors.push(\n new OMSSSourceGatheringError(`Provider execution failed: ${item.reason instanceof Error ? item.reason.message : String(item.reason)}`, {\n cause: item.reason instanceof Error ? item.reason : undefined,\n })\n )\n unexpectedErrors.push(item.reason)\n continue\n }\n\n const res = item.value\n\n if (res.ok) {\n hasSuccess = true\n allSources.push(...res.value.sources)\n allSubtitles.push(...res.value.subtitles)\n omssErrors.push(...res.value.errors)\n continue\n }\n\n omssErrors.push(res.error)\n }\n\n if (!hasSuccess) {\n return ERR(\n new OMSSSourceGatheringError(`All providers failed for namespace \"${parsed.value.namespace}\" and id: \"${parsed.value.raw}\"`, {\n cause: new AggregateError([...omssErrors, ...unexpectedErrors], 'Multiple failures detected'),\n })\n )\n }\n\n return OK({\n sources: allSources,\n subtitles: allSubtitles,\n errors: omssErrors,\n })\n }\n}\n","/**\n * Generic helper for deduplicating concurrent asynchronous work by key.\n *\n * When the same key is requested multiple times while a request is still in\n * flight, all callers receive the same Promise. Once the Promise settles, the\n * key is removed automatically so the next request starts fresh.\n *\n * @typeParam TKey - Cache key type.\n * @typeParam TValue - Promise resolution type.\n */\nexport class AsyncDeduper<TKey, TValue> {\n readonly #entries = new Map<TKey, Promise<TValue>>()\n\n /**\n * Returns the current in-flight Promise for a key, if one exists.\n *\n * @param key - In-flight request key.\n * @returns Existing Promise or undefined.\n */\n get(key: TKey): Promise<TValue> | undefined {\n return this.#entries.get(key)\n }\n\n /**\n * Check whether a key currently has an in-flight Promise.\n *\n * @param key - In-flight request key.\n * @returns True if the key is currently in flight.\n */\n has(key: TKey): boolean {\n return this.#entries.has(key)\n }\n\n /**\n * Delete a key manually.\n *\n * @param key - In-flight request key.\n * @returns True if an entry existed and was removed.\n */\n delete(key: TKey): boolean {\n return this.#entries.delete(key)\n }\n\n /**\n * Clear all tracked in-flight requests.\n */\n clear(): void {\n this.#entries.clear()\n }\n\n /**\n * Run a Promise factory for a key, reusing an existing in-flight Promise\n * when available.\n *\n * @param key - In-flight request key.\n * @param factory - Factory that creates the Promise when no request exists yet.\n * @returns Shared or newly created Promise.\n */\n run(key: TKey, factory: () => Promise<TValue>): Promise<TValue> {\n const existing = this.#entries.get(key)\n\n if (existing) {\n return existing\n }\n\n const promise = factory().finally(() => {\n this.#entries.delete(key)\n })\n\n this.#entries.set(key, promise)\n\n return promise\n }\n}\n","import { MiddlewareHandler, MiddlewareOperationMap } from '@/types/middleware.js'\n\n/**\n * Reusable typed middleware runner.\n */\nexport class MiddlewareRunner<TOperations extends MiddlewareOperationMap> {\n readonly #handlers: Partial<{\n [K in keyof TOperations]: MiddlewareHandler<TOperations, K>[]\n }> = {}\n\n /**\n * Register middleware for a specific operation.\n *\n * @param method - Operation name.\n * @param handler - Middleware handler.\n */\n use<TMethod extends keyof TOperations>(method: TMethod, handler: MiddlewareHandler<TOperations, TMethod>): void {\n const list = (this.#handlers[method] ??= []) as MiddlewareHandler<TOperations, TMethod>[]\n\n list.push(handler)\n }\n\n /**\n * Run middleware chain for a specific operation.\n *\n * @param method - Operation name.\n * @param context - Operation context payload.\n * @param finalHandler - Final function to execute after middleware.\n * @returns The operation result.\n */\n run<TMethod extends keyof TOperations>(\n method: TMethod,\n context: TOperations[TMethod]['context'],\n finalHandler: () => Promise<TOperations[TMethod]['result']>\n ): Promise<TOperations[TMethod]['result']> {\n const handlers = (this.#handlers[method] ?? []) as readonly MiddlewareHandler<TOperations, TMethod>[]\n\n let index = -1\n\n const dispatch = (position: number): Promise<TOperations[TMethod]['result']> => {\n if (position <= index) {\n return Promise.reject(new Error('next() called multiple times'))\n }\n\n index = position\n\n const handler = handlers[position]\n\n if (!handler) {\n return finalHandler()\n }\n\n return handler(context, () => dispatch(position + 1))\n }\n\n return dispatch(0)\n }\n}\n","import OMSSServer from '@/core/server.js'\nimport { HookRegistry } from '@/features/hooks/HookRegistry.js'\nimport { ProviderRegistry } from '@/features/providers/ProviderRegistry.js'\nimport { SourceCore } from '@/features/source/SourceCore.js'\nimport type { CleaningFunction, GatheredSources, GetSourcesOptions, SourceServiceMiddleware, SourceServiceOperations } from '@/types/source.js'\nimport type { OMSSId } from '@/types/resolver.js'\nimport type { Result } from '@/types/utils.js'\nimport { OMSSSourceGatheringError } from '@/utils/error.js'\nimport { AsyncDeduper } from '@/utils/AsyncDeduper.js'\nimport { MiddlewareRunner } from '@/utils/middleware.js'\nimport type { OMSSHooks, ProviderHooks } from '@/types/hooks.js'\nimport { ExtractorService } from '@/features/extractors/ExtractorService.js'\nimport { HookService } from '@/features/hooks/HookService.js'\n\n/**\n * Public API for resolving sources for media.\n *\n * This service owns the public method surface, middleware execution,\n * lifecycle hook dispatching, and request coalescing. The actual source\n * gathering implementation lives in {@link SourceCore}.\n */\nexport class SourceService {\n readonly #hookRegistry: HookRegistry<OMSSHooks>\n readonly #core: SourceCore\n\n /**\n * Middleware runner for SourceService operations.\n */\n readonly #middleware = new MiddlewareRunner<SourceServiceOperations>()\n\n /**\n * Deduplicates concurrent getSources requests by request key.\n */\n readonly #inFlight = new AsyncDeduper<string, Result<GatheredSources, OMSSSourceGatheringError>>()\n\n constructor(omssServer: OMSSServer, providerRegistry: ProviderRegistry, hookRegistry: HookRegistry<OMSSHooks>, extractorService: ExtractorService) {\n this.#hookRegistry = hookRegistry\n this.#core = new SourceCore(omssServer, providerRegistry, extractorService)\n }\n\n public get cleaningFunction(): CleaningFunction {\n return this.#cleaningFunction\n }\n\n public set cleaningFunction(fn: CleaningFunction) {\n this.#cleaningFunction = fn\n }\n\n /**\n * Register middleware for a SourceService method.\n *\n * Middleware can be used for cross-cutting concerns such as caching,\n * logging, tracing, or metrics.\n *\n * @param method - Middleware-enabled method name.\n * @param handler - Middleware handler.\n */\n use<TMethod extends keyof SourceServiceOperations>(method: TMethod, handler: SourceServiceMiddleware<TMethod>): void {\n this.#middleware.use(method, handler)\n }\n\n /**\n * Fetch sources from all matching providers for an OMSS ID.\n *\n * This method is middleware-enabled. Concurrent requests for the same\n * `omssId` and `providerId` share the same in-flight Promise until the\n * request settles.\n *\n * @param omssId - OMSS identifier such as `\"tmdb:12345\"`.\n * @param options - Optional source gathering parameters.\n * @returns Aggregated provider results or a source gathering error.\n */\n async getSources(omssId: OMSSId, options: GetSourcesOptions = {}): Promise<Result<GatheredSources, OMSSSourceGatheringError>> {\n return this.#middleware.run('getSources', { omssId, options }, () => this.#internalGetSources(omssId, options))\n }\n\n /**\n * Get and set the cleaning function for the source core.\n */\n #cleaningFunction: CleaningFunction = (obj) => obj\n\n /**\n * Internal wrapper around source gathering.\n *\n * Runs lifecycle hooks and deduplicates concurrent requests before\n * delegating to {@link SourceCore}.\n *\n * @param omssId - OMSS identifier.\n * @param options - Optional source gathering parameters.\n * @returns Aggregated provider results or a source gathering error.\n */\n async #internalGetSources(omssId: OMSSId, options: GetSourcesOptions): Promise<Result<GatheredSources, OMSSSourceGatheringError>> {\n await this.#hookRegistry.run('beforeGetSources', {\n omssId,\n providerId: options.providerId,\n })\n\n const inFlightKey = this.#getInFlightKey(omssId, options.providerId)\n\n const result = await this.#inFlight.run(inFlightKey, () =>\n this.#core.getSources(omssId, options, options.providerHookService ?? new HookService<ProviderHooks>(), options.cleaningFunction ?? this.cleaningFunction)\n )\n\n if (result.ok) {\n await this.#hookRegistry.run('afterGetSources', {\n omssId,\n providerId: options.providerId,\n result: result.value,\n })\n\n return this.#middleware.run('afterGetSources', { omssId, options, result }, () => Promise.resolve(result))\n }\n\n await this.#hookRegistry.run('getSourcesFailed', {\n omssId,\n providerId: options.providerId,\n error: result.error,\n })\n\n return result\n }\n\n /**\n * Build the stable in-flight key for a getSources request.\n *\n * @param omssId - OMSS identifier.\n * @param providerId - Optional provider filter.\n * @returns Unique in-flight request key.\n */\n #getInFlightKey(omssId: OMSSId, providerId?: string): string {\n return `${omssId}|${providerId ?? ''}`\n }\n}\n","import type { UnknownProvider } from '@/types/provider.js'\nimport { OMSSProviderError } from '@/utils/error.js'\nimport { ERR, OK, validateSafeUniqueString } from '@/utils/utils.js'\nimport { Result } from '@/types/utils.js'\nimport { CATALOG_ENTRY } from '@/utils/regexp.js'\n\n/**\n * Registry responsible for storing and managing OMSS Providers.\n *\n * Providers are stored by their unique {@link UnknownProvider.id}.\n * This registry does not know about hooks.\n */\nexport class ProviderRegistry {\n /**\n * Internal map of registered providers, keyed by provider ID.\n */\n readonly #providers = new Map<string, UnknownProvider>()\n\n /**\n * Adds a provider to the registry.\n *\n * @param provider - The provider instance to register.\n * @returns `OK` if registration succeeded, `ERR` if the provider ID is already taken.\n */\n async add(provider: UnknownProvider): Promise<Result<UnknownProvider, OMSSProviderError>> {\n if (this.#providers.has(provider.id)) {\n return ERR(new OMSSProviderError(`Provider \"${provider.id}\" is already registered`))\n }\n\n const valProvIdReq = validateSafeUniqueString(provider.id, 'provider ID', OMSSProviderError)\n if (!valProvIdReq.ok) {\n return ERR(valProvIdReq.error)\n }\n\n const valResolverNamespaceReq = validateSafeUniqueString(provider.resolver.namespace, 'resolver namespace for resolver with name: \"' + provider.resolver.name + '\" and id:', OMSSProviderError)\n if (!valResolverNamespaceReq.ok) {\n return ERR(valResolverNamespaceReq.error)\n }\n\n if (this.getAll().some((p) => p.resolver.namespace === provider.resolver.namespace && p.resolver !== provider.resolver)) {\n return ERR(new OMSSProviderError(`Resolver namespace \"${provider.resolver.namespace}\" is already registered by another provider and another resolver. Use one resolver per namespace.`))\n }\n\n if (provider.catalog) {\n const entries = await provider.catalog()\n const hasWildcard = entries.includes('*')\n const hasOther = entries.some((id) => id !== '*')\n\n if (hasWildcard && hasOther) {\n return ERR(new OMSSProviderError(`Provider \"${provider.id}\" catalog contains \"*\" mixed with other IDs. Use either a single \"*\" or a list of valid IDs.`))\n }\n\n for (const id of entries) {\n if (!CATALOG_ENTRY.test(id)) {\n return ERR(new OMSSProviderError(`Provider \"${provider.id}\" catalog contains an invalid entry \"${id}\". Entries must be non-empty strings with no whitespace, or \"*\".`))\n }\n }\n }\n\n this.#providers.set(provider.id, provider)\n return OK(provider)\n }\n\n /**\n * Retrieves a registered provider by its ID.\n *\n * @param id - The unique provider ID.\n * @returns The provider instance, or `undefined` if not found.\n */\n get(id: string): UnknownProvider | undefined {\n return this.#providers.get(id)\n }\n\n /**\n * Returns all registered providers.\n */\n getAll(filter?: (p: UnknownProvider) => boolean): UnknownProvider[] {\n if (filter) {\n return [...this.#providers.values()].filter(filter)\n }\n return [...this.#providers.values()]\n }\n\n /**\n * Returns whether a provider with the given ID is registered.\n *\n * @param id - The unique provider ID.\n */\n has(id: string): boolean {\n return this.#providers.has(id)\n }\n}\n","import type { ProviderServiceMiddleware, ProviderServiceOperations, UnknownProvider } from '@/types/provider.js'\nimport { OMSSProviderError } from '@/utils/error.js'\nimport { ERR, OK } from '@/utils/utils.js'\nimport { ProviderRegistry } from '@/features/providers/ProviderRegistry.js'\nimport { HookRegistry } from '@/features/hooks/HookRegistry.js'\nimport type { Result } from '@/types/utils.js'\nimport type { OMSSHooks } from '@/types/hooks.js'\nimport { MiddlewareRunner } from '@/utils/middleware.js'\n\n/**\n * The public API for managing OMSS Providers.\n */\nexport class ProviderService {\n readonly #providerRegistry: ProviderRegistry\n readonly #hookRegistry: HookRegistry<OMSSHooks>\n readonly #middleware = new MiddlewareRunner<ProviderServiceOperations>()\n #insideBeforeProviderRegister = false\n\n constructor(providerRegistry: ProviderRegistry, hookRegistry: HookRegistry<OMSSHooks>) {\n this.#providerRegistry = providerRegistry\n this.#hookRegistry = hookRegistry\n }\n\n /**\n * Adds middleware to the `register` pipeline.\n * Middlewares run in insertion order, after hooks and before the\n * actual registry `add()` call.\n */\n use<TMethod extends keyof ProviderServiceOperations>(method: TMethod, handler: ProviderServiceMiddleware<TMethod>): this {\n this.#middleware.use(method, handler)\n return this\n }\n\n /**\n * Registers a provider into the system.\n */\n async register(provider: UnknownProvider): Promise<Result<UnknownProvider, OMSSProviderError>> {\n if (this.#insideBeforeProviderRegister) {\n return ERR(new OMSSProviderError('Providers cannot be registered during beforeProviderRegister'))\n }\n\n this.#insideBeforeProviderRegister = true\n try {\n await this.#hookRegistry.run('beforeProviderRegister', { provider })\n } finally {\n this.#insideBeforeProviderRegister = false\n }\n\n const result = await this.#middleware.run('register', { provider }, () => this.#providerRegistry.add(provider))\n\n if (!result.ok) {\n await this.#hookRegistry.run('providerRegisterFailed', {\n provider,\n error: result.error,\n })\n return ERR(result.error)\n }\n\n await this.#hookRegistry.run('afterProviderRegister', { provider })\n return result\n }\n\n /**\n * Retrieves a registered provider by its ID.\n *\n * @param id - The provider ID to look up.\n * @returns The provider instance, or `undefined` if not found.\n */\n get(id: string): ReturnType<ProviderRegistry['get']> {\n return this.#providerRegistry.get(id)\n }\n\n /**\n * Returns all registered providers.\n * @param filter - Optional filter function to apply to providers.\n */\n getAll(filter?: (p: UnknownProvider) => boolean): ReturnType<ProviderRegistry['getAll']> {\n return this.#providerRegistry.getAll(filter)\n }\n\n /**\n * Returns whether a provider with the given ID has been registered.\n *\n * @param id - The provider ID to check.\n */\n has(id: string): ReturnType<ProviderRegistry['has']> {\n return this.#providerRegistry.has(id)\n }\n\n /**\n * Returns a map of all namespaces and an array of all known identifiers for each namespace.\n * This list is BEST EFFORT ONLY. Do not rely on this. If any provider returns a `*` automatically, the namespace will support all identifiers (e.g. `\"tmdb\": [\"*\"], \"imdb\": [\"tt37636\", \"...\"]`.\n */\n async catalog(): Promise<Result<Map<string, string[]>, OMSSProviderError>> {\n const allProviders = this.getAll()\n const result = new Map<string, string[]>()\n\n await Promise.all(\n allProviders.map(async (provider) => {\n if (!provider.catalog) return\n\n const namespace = provider.resolver.namespace\n const entries = await provider.catalog()\n\n // If namespace already collapsed to wildcard, skip.\n if (result.get(namespace)?.[0] === '*') return\n\n if (entries.includes('*')) {\n // Any single wildcard provider collapses the whole namespace.\n result.set(namespace, ['*'])\n return\n }\n\n // Merge deduplicated IDs into the namespace bucket.\n const existing = result.get(namespace) ?? []\n const merged = Array.from(new Set([...existing, ...entries]))\n result.set(namespace, merged)\n })\n )\n\n return OK(result)\n }\n\n /**\n * Returns the catalog for a single namespace.\n * Returns `undefined` if no provider in that namespace exposes a catalog.\n *\n * @param namespace - The resolver namespace to look up (e.g. `\"tmdb\"`).\n * @returns Merged list of IDs for the namespace, `[\"*\"]` if any provider\n * signals wildcard support, or `undefined` if no catalog data exists.\n */\n async catalogForNamespace(namespace: string): Promise<Result<string[], OMSSProviderError>> {\n const providers = this.getAll((p) => p.resolver.namespace === namespace)\n if (providers.length === 0) return ERR(new OMSSProviderError(`No providers registered for namespace \"${namespace}\"`))\n\n const result: string[] = []\n\n for (const provider of providers) {\n if (!provider.catalog) continue\n\n const entries = await provider.catalog()\n\n if (entries.includes('*')) {\n return OK(['*'])\n }\n\n for (const id of entries) {\n if (!result.includes(id)) {\n result.push(id)\n }\n }\n }\n\n return result.length > 0 ? OK(result) : ERR(new OMSSProviderError(`No catalog data available for namespace \"${namespace}\"`))\n }\n}\n","import { ExtractorRegistry } from '@/features/extractors/ExtractorRegistry.js'\nimport { HookRegistry } from '@/features/hooks/HookRegistry.js'\nimport type { OMSSHooks } from '@/types/hooks.js'\nimport { Extractor } from '@/types/extractor.js'\nimport { Result } from '@/types/utils.js'\nimport { ERR, OK } from '@/utils/utils.js'\nimport { OMSSExtractorError } from '@/utils/error.js'\n\nexport class ExtractorService {\n readonly #extractorRegistry: ExtractorRegistry\n readonly #hookRegistry: HookRegistry<OMSSHooks>\n #insideBeforeRegisterExtractor = false\n\n constructor(extractorRegistry: ExtractorRegistry, hookRegistry: HookRegistry<OMSSHooks>) {\n this.#extractorRegistry = extractorRegistry\n this.#hookRegistry = hookRegistry\n }\n\n /**\n * Get all registered extractors (read-only).\n */\n get extractors(): Result<ReadonlyArray<Extractor>, Error> {\n return OK(this.#extractorRegistry.extractors)\n }\n\n /**\n * Find an extractor capable of handling the given URL.\n *\n * @param url - URL to search for.\n * @returns The first matching {@link Extractor} or an {@link OMSSExtractorError}.\n */\n async find(url: string): Promise<Result<Extractor, OMSSExtractorError>> {\n await this.#hookRegistry.run('beforeFindExtractor', { url })\n\n const extractors = this.#extractorRegistry.extractors\n\n const results = await Promise.all(extractors.map((extractor) => extractor.matcher(url)))\n\n for (let i = 0; i < extractors.length; i++) {\n if (results[i]!.ok) {\n const extractor = extractors[i]!\n\n await this.#hookRegistry.run('afterFindExtractor', {\n url,\n extractor,\n })\n\n return OK(extractor)\n }\n }\n\n const error = new OMSSExtractorError(`No extractor found for URL \"${url}\"`)\n\n await this.#hookRegistry.run('findExtractorFailed', {\n url,\n error,\n })\n\n return ERR(error)\n }\n\n /**\n * Register an extractor.\n *\n * @param extractor - Extractor to register.\n */\n async register(extractor: Extractor): Promise<Result<void, Error>> {\n if (this.#insideBeforeRegisterExtractor) {\n return ERR(new OMSSExtractorError('Extractors cannot be registered during beforeRegisterExtractor'))\n }\n\n this.#insideBeforeRegisterExtractor = true\n\n try {\n await this.#hookRegistry.run('beforeRegisterExtractor', {\n extractor,\n })\n } finally {\n this.#insideBeforeRegisterExtractor = false\n }\n\n try {\n this.#extractorRegistry.add(extractor)\n } catch (error) {\n const extractorError = error instanceof OMSSExtractorError ? error : new OMSSExtractorError(error instanceof Error ? error.message : String(error))\n\n await this.#hookRegistry.run('extractorRegisterFailed', {\n extractor,\n error: extractorError,\n })\n\n return ERR(extractorError)\n }\n\n await this.#hookRegistry.run('afterRegisterExtractor', {\n extractor,\n })\n\n return OK()\n }\n\n /**\n * Remove every registered extractor.\n */\n reset(): Result<void, Error> {\n this.#extractorRegistry.reset()\n return OK()\n }\n\n /**\n * Determine whether an extractor has already been registered.\n *\n * @param extractor - Extractor to check.\n */\n has(extractor: Extractor): Result<boolean, Error> {\n return OK(this.#extractorRegistry.has(extractor))\n }\n\n /**\n * Remove an extractor.\n *\n * @param extractor - Extractor to remove.\n * @returns Whether the extractor was removed.\n */\n remove(extractor: Extractor): Result<boolean, Error> {\n return OK(this.#extractorRegistry.remove(extractor))\n }\n}\n","import { Extractor } from '@/types/extractor.js'\n\n/**\n * Extractor Registry\n *\n * Manages all extractors.\n */\nexport class ExtractorRegistry {\n /**\n * Array of extractors.\n */\n readonly #extractors = Array<Extractor>()\n\n /**\n * Get all extractors.\n */\n get extractors(): Readonly<Extractor[]> {\n return this.#extractors\n }\n\n /**\n * Clear all extractors.\n */\n reset(): void {\n this.#extractors.length = 0\n }\n\n /**\n * Add an extractor.\n * @param extractor - Extractor to add.\n */\n add(extractor: Extractor): void {\n if (this.#extractors.some((e) => e === extractor)) return\n\n this.#extractors.push(extractor)\n }\n\n /**\n * Check if an extractor is already registered.\n * @param extractor - Extractor to check.\n */\n has(extractor: Extractor): boolean {\n return this.#extractors.includes(extractor)\n }\n\n /**\n * Remove an extractor.\n * @param extractor - Extractor to remove.\n * @returns True if the extractor was removed, false otherwise.\n */\n remove(extractor: Extractor): boolean {\n if (!this.has(extractor)) return false\n this.#extractors.splice(this.#extractors.indexOf(extractor), 1)\n return true\n }\n}\n","import { HookRegistry } from '@/features/hooks/HookRegistry.js'\nimport { HookService } from '@/features/hooks/HookService.js'\nimport { PluginRegistry } from '@/features/plugins/PluginRegistry.js'\nimport { PluginService } from '@/features/plugins/PluginService.js'\nimport { OMSSServerError } from '@/utils/error.js'\nimport type { OMSSConfig } from '@/types/config.js'\nimport { SourceService } from '@/features/source/SourceService.js'\nimport { ProviderRegistry } from '@/features/providers/ProviderRegistry.js'\nimport { ERR, OK } from '@/utils/utils.js'\nimport { Result } from '@/types/utils.js'\nimport { ProviderService } from '@/features/providers/ProviderService.js'\nimport { OMSSHooks } from '@/types/hooks.js'\nimport { ExtractorService } from '@/features/extractors/ExtractorService.js'\nimport { ExtractorRegistry } from '@/features/extractors/ExtractorRegistry.js'\n\n/**\n * Core server class for OMSS.\n */\nexport class OMSSServer {\n readonly hooks: HookService<OMSSHooks>\n readonly plugins: PluginService\n readonly providers: ProviderService\n readonly sources: SourceService\n readonly extractors: ExtractorService\n readonly #config: OMSSConfig\n\n /**\n * Creates a new OMSSServer instance.\n *\n * @param config - Immutable server configuration\n */\n constructor(config: OMSSConfig) {\n this.#config = config\n\n const hooksRegistry = new HookRegistry<OMSSHooks>()\n const extractorRegistry = new ExtractorRegistry()\n const pluginRegistry = new PluginRegistry(this)\n const providerRegistry = new ProviderRegistry()\n\n this.hooks = new HookService<OMSSHooks>(hooksRegistry)\n this.extractors = new ExtractorService(extractorRegistry, hooksRegistry)\n this.plugins = new PluginService(this, pluginRegistry, hooksRegistry)\n this.providers = new ProviderService(providerRegistry, hooksRegistry)\n this.sources = new SourceService(this, providerRegistry, hooksRegistry, this.extractors)\n }\n\n /**\n * Get the OMSS Config from the constructor\n * @returns the initialised OMSS Config\n */\n get config(): Readonly<OMSSConfig> {\n return this.#config\n }\n\n /**\n * Decorate the OMSSServer instance with a new property.\n * @param name - The name of the property to be decorated.\n * @param value - The value to be assigned to the property.\n * @param deps - An array of dependency names.\n * @returns The name of the decorated property in the {@link Result} object.\n */\n decorate<T>(name: string, value: T, deps: string[] = []): Result<string, OMSSServerError> {\n if (Object.hasOwn(this, name)) {\n return ERR(\n new OMSSServerError(`Decorator \"${name}\" already exists`, {\n cause: {\n existing: this[name as keyof this],\n },\n })\n )\n }\n\n for (const dep of deps) {\n if (!this.hasDecorator(dep)) {\n return ERR(new OMSSServerError(`\"${name}\" depends on \"${dep}\", which does not exist`))\n }\n }\n\n // modify the instance to make the decorator available as a property\n Object.defineProperty(this, name, {\n value,\n writable: false,\n configurable: false,\n enumerable: true,\n })\n\n return OK(name)\n }\n\n /**\n * Check if a decorator with the given name exists.\n * @param name - The name of the decorator to check.\n * @returns True if the decorator exists, false otherwise.\n */\n hasDecorator(name: string): boolean {\n return Object.hasOwn(this, name)\n }\n\n /**\n * Get a decorated property by its name.\n * @param name - The name of the property to retrieve.\n * @returns The decorated property value in the {@link Result} object.\n */\n getDecorator<T>(name: string): Result<T, OMSSServerError> {\n if (!Object.hasOwn(this, name)) {\n return ERR(new OMSSServerError(`Decorator \"${name}\" not found`))\n }\n\n return OK(this[name as keyof this] as T)\n }\n}\n\nexport default OMSSServer\n","import { BaseResolver } from '@/features/resolvers/BaseResolver.js'\nimport { OMSSProvider, ProviderResult, ProviderResultEmitter, ProviderSourcesMeta, ResolverMetadata } from '@/types/provider.js'\nimport type { ParsedOMSSId } from '@/types/resolver.js'\nimport { NonEmptyArray } from '@/types/utils.js'\n\n/**\n * Base class for all providers.\n */\nexport abstract class BaseProvider<P extends BaseResolver<unknown>> implements OMSSProvider<P> {\n /**\n * Provider ID. Must be unique.\n */\n abstract readonly id: string\n\n /**\n * Friendly name of the provider.\n */\n abstract readonly name: string\n\n /**\n * Whether the provider will be used.\n */\n abstract readonly enabled: boolean\n\n /**\n * Catalog of media this provider supports. It does not have to exist. If it does, it should be a list of media IDs.\n * This does not get queried for source resolving, but more metadata about the provider.\n */\n abstract readonly catalog?: () => Promise<NonEmptyArray<string>> | NonEmptyArray<string>\n\n /**\n * Provide a method that checks whether this provider supports a certain ID.\n * @param id - Parsed OMSS ID\n */\n abstract readonly supportsId: (id: ParsedOMSSId) => boolean | Promise<boolean>\n\n /**\n * Resolvers that this provider supports.\n */\n abstract readonly resolver: P\n\n /**\n * Fetch sources for a certain media.\n * @param media - Return object of the resolver's resolve() method.\n * @param result - The result emitter.\n */\n abstract getSources(media: ProviderSourcesMeta<ResolverMetadata<P>>, result: ProviderResultEmitter): Promise<ProviderResult>\n}\n","import { OMSSResolverError, Result } from '@/public-api.js'\nimport { OMSSId, OMSSResolver, ParsedOMSSId, ResolverExecutionContext, ResolverResult } from '@/types/resolver.js'\n\n/**\n * Base class for all OMSS resolvers.\n *\n * Resolvers convert an OMSS ID into usable metadata for providers.\n */\nexport abstract class BaseResolver<T> implements OMSSResolver<T> {\n /**\n * Namespace this resolver owns, e.g. \"tmdb\".\n * Must be unique for a single server instance.\n */\n abstract namespace: string\n\n /**\n * Human-readable resolver name.\n */\n abstract name: string\n\n /**\n * A map of ID converters.\n *\n * @key - The unhandled namespace\n * @value - A function that converts that id (from an unknown namespace/id provider) to this resolver's namespace.\n */\n abstract converter: Map<string, (noHandlerId: OMSSId, ctx: ResolverExecutionContext) => Promise<Result<OMSSId, OMSSResolverError>>>\n\n /**\n * Resolve a single ID into metadata.\n *\n * @param id - Parsed OMSS ID\n * @param ctx - Execution context.\n */\n abstract resolve(id: ParsedOMSSId, ctx: ResolverExecutionContext): Promise<ResolverResult<T>>\n}\n"],"mappings":"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"}
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["#hooks","#hookRegistry","#states","#server","#stack","#pluginRegistry","#hookRegistry","#insideBeforePluginRegister","#providerRegistry","#extractorService","#omssServer","#entries","#handlers","#hookRegistry","#core","#middleware","#inFlight","#cleaningFunction","#internalGetSources","#getInFlightKey","#providers","#providerRegistry","#hookRegistry","#middleware","#insideBeforeProviderRegister","#extractorRegistry","#hookRegistry","#insideBeforeRegisterExtractor","#extractors","#config"],"sources":["../src/features/hooks/HookRegistry.ts","../src/features/hooks/HookService.ts","../src/features/plugins/plugin-state.ts","../src/utils/error.ts","../src/utils/regexp.ts","../src/utils/utils.ts","../src/features/plugins/PluginRegistry.ts","../src/features/plugins/PluginService.ts","../src/features/resolvers/utils.ts","../src/features/providers/ProviderResultEmitter.ts","../src/features/source/SourceCore.ts","../src/utils/AsyncDeduper.ts","../src/utils/middleware.ts","../src/features/source/SourceService.ts","../src/features/providers/ProviderRegistry.ts","../src/features/providers/ProviderService.ts","../src/features/extractors/ExtractorService.ts","../src/features/extractors/ExtractorRegistry.ts","../src/core/server.ts","../src/features/providers/BaseProvider.ts","../src/features/resolvers/BaseResolver.ts"],"sourcesContent":["/**\n * Hook Registry\n *\n * Manages lifecycle hooks for OMSS events.\n */\nexport class HookRegistry<T> {\n /**\n * Map storing arrays of hook handlers for each hook name.\n */\n readonly #hooks = new Map<keyof T, unknown[]>()\n\n /**\n * Get all registered hooks immutable.\n * @dangerous - Be careful with this. what you are doing might cause side effects.\n */\n get hooks(): ReadonlyMap<keyof T, unknown[]> {\n return this.#hooks\n }\n\n /**\n * Clear all registered hooks.\n * @dangerous - Be careful with this. Might cause side effects.\n */\n reset(): void {\n return this.#hooks.clear()\n }\n\n /**\n * Run all hooks for a lifecycle event with the provided payload.\n *\n * @typeParam K - The name of the hook to run.\n * @param name - The hook name (key of THooks).\n * @param payload - The payload to pass to each hook handler.\n */\n async run<K extends keyof T>(name: K, payload: T[K] extends (payload: infer P) => unknown ? P : never): Promise<void> {\n const fns = this.#hooks.get(name) ?? []\n\n for (const fn of fns) {\n await (fn as (payload: unknown) => void | Promise<void>)(payload)\n }\n }\n\n /**\n * Add a hook handler for a lifecycle event.\n * @param name - The hook name (key of THooks).\n * @param cb - The hook handler function.\n */\n add<K extends keyof T>(name: K, cb: T[K]): void {\n const existing = this.#hooks.get(name) ?? []\n\n this.#hooks.set(name, [...existing, cb])\n }\n}\n","import { HookRegistry } from '@/features/hooks/HookRegistry.js'\n\nexport class HookService<T> {\n readonly #hookRegistry: HookRegistry<T>\n\n constructor(hookRegistry: HookRegistry<T> = new HookRegistry<T>()) {\n this.#hookRegistry = hookRegistry\n }\n\n /**\n * Get all registered hooks immutable. TO ADD HOOKS, USE THE ADD METHOD\n * @dangerous - Be careful with this. what you are doing might cause side effects.\n */\n get hooks(): ReadonlyMap<keyof T, unknown[]> {\n return this.#hookRegistry.hooks\n }\n\n /**\n * Register a hook for a lifecycle event.\n *\n * @typeParam K - The name of the hook to register.\n * @param name - The hook name (key of THooks).\n * @param cb - The handler function for this hook.\n */\n add<K extends keyof T>(name: K, cb: T[K]): ReturnType<HookRegistry<T>['add']> {\n this.#hookRegistry.add(name, cb)\n }\n\n /**\n * Clear all registered hooks.\n * @dangerous - Be careful with this. Might cause side effects.\n */\n reset(): ReturnType<HookRegistry<T>['reset']> {\n return this.#hookRegistry.reset()\n }\n\n /**\n * Get the hook registry.\n *\n * This is only exposed for internal purposes and should not be accessed in consumer projects.\n * @dangerous\n * @internal\n */\n __getRegistry(): HookRegistry<T> {\n return this.#hookRegistry\n }\n}\n","/**\n * The states of which a plugin can be.\n */\nexport enum PluginState {\n Registering,\n Registered,\n Unavailable,\n}\n","/**\n * Base class for all OMSS framework errors.\n * Use `instanceof OMSSError` to catch any framework error.\n * Always throw a specific subclass, never this directly.\n * Cause is not standardized and may change frequently.\n */\nexport class OMSSError extends Error {\n /**\n * Create a new OMSSError.\n * @param message - Error message\n * @param options - Additional options for the error\n */\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options)\n this.name = this.constructor.name\n }\n}\n\n/**\n * Returned when an error gets Returned in the OMSSServer class.\n *\n * @example\n * return ERR(OMSSServerError('config.name must be a non-empty string', {cause: config}))\n */\nexport class OMSSServerError extends OMSSError {}\n\n/**\n * Returned during plugin registration or execution.\n *\n * @example\n * return ERR(OMSSPluginError(`Plugin \"${name}\" is already registered`, { cause: plugin }))\n */\nexport class OMSSPluginError extends OMSSError {}\n\n/**\n * Returned during resolver registration or ID resolution.\n *\n * @example\n * return ERR(OMSSResolverError(`No resolver found for namespace \"xyz\"`, { cause: rawId }))\n */\nexport class OMSSResolverError extends OMSSError {}\n\n/**\n * Returned during provider registration or source fetching.\n *\n * @example\n * return ERR(OMSSProviderError('Provider must have at least one resolver', { cause: provider }))\n */\nexport class OMSSProviderError extends OMSSError {}\n\n/**\n * Returned when an extractor fails to extract the media from a host.\n *\n * @example\n * return ERR(OMSSExtractor('Failed to extract media from host due to host changes', { cause: html }))\n */\nexport class OMSSExtractorError extends OMSSError {}\n\n/**\n * Returned when something/several things fail during source gathering.\n *\n * @example\n * return ERR(OMSSSourceGatheringError('Failed to gather sources', { cause: providerResults }))\n */\nexport class OMSSSourceGatheringError extends OMSSError {}\n","/**\n * Regex for validating namespace names.\n */\nconst SAFE_UNIQUE_STRING_PATTERN = '[a-z0-9-]+'\nexport const SAFE_UNIQUE_STRING = new RegExp(`^${SAFE_UNIQUE_STRING_PATTERN}$`)\n\n/**\n * Regex for matching media formats by keyword or file extension.\n *\n * Matches:\n * - format keywords (`hls`, `dash`, `mp4`, etc.)\n * - common media file extensions\n * - extensions followed by URL query strings, fragments, paths, or the end of the URL\n */\nexport const HLS_REGEX = /\\bhls\\b|\\.(?:m3u8|ts)(?:$|[\\/?#])/i\nexport const MP4_REGEX = /\\bmp4\\b|\\.mp4(?:$|[\\/?#])/i\nexport const DASH_REGEX = /\\bdash\\b|\\.(?:mpd|m4a)(?:$|[\\/?#])/i\nexport const MKV_REGEX = /\\bmkv\\b|\\.mkv(?:$|[\\/?#])/i\nexport const VTT_REGEX = /\\bvtt\\b|\\.vtt(?:$|[\\/?#])/i\nexport const SRT_REGEX = /\\bsrt\\b|\\.srt(?:$|[\\/?#])/i\n\n/**\n * Regex for validating a single catalog entry value.\n *\n * A catalog entry must be either exactly `\"*\"` (wildcard — provider supports\n * all IDs in the namespace) or a safe unique string.\n *\n * Mixing `\"*\"` with other entries in the same catalog array is not allowed\n * and is validated separately at registration time.\n */\nexport const CATALOG_ENTRY = new RegExp(`^(?:\\\\*|${SAFE_UNIQUE_STRING_PATTERN})$`)\n","import { Result } from '@/types/utils.js'\nimport { OMSSError } from '@/utils/error.js'\nimport { SAFE_UNIQUE_STRING } from '@/utils/regexp.js'\n\n/**\n * Convenience factory for a successful result.\n */\nexport function OK(): Result<void, never>\nexport function OK<T>(value: T): Result<T, never>\n\nexport function OK(value?: unknown) {\n if (arguments.length === 0) {\n return { ok: true }\n }\n\n return { ok: true, value }\n}\n/**\n * Convenience factory for a failed result.\n */\nexport const ERR = <E extends Error>(error: E): Result<never, E> => ({ ok: false, error })\n\ntype ErrorConstructor<T extends OMSSError> = new (message: string, options?: { cause?: Error }) => T\n\n/**\n * Validate a string is safe for use as a unique identifier (only lowercase letters, numbers, and hyphens).\n * @param value - The string to validate\n * @param name - The name of the identifier, for error messages\n * @param ErrorType - The error type to return if validation fails\n */\nexport function validateSafeUniqueString<T extends OMSSError>(value: string, name: string, ErrorType: ErrorConstructor<T>): Result<void, T> {\n if (!SAFE_UNIQUE_STRING.test(value)) {\n return ERR(new ErrorType(`Invalid ${name} \"${value}\". Expected only letters (lowercase), numbers, and hyphens.`))\n }\n\n return OK()\n}\n","import type { OMSSConfiguredPluginType, OMSSPluginOptions, OMSSPluginType, UnknownPluginType } from '@/types/plugin.js'\nimport OMSSServer from '@/core/server.js'\nimport { PluginState } from '@/features/plugins/plugin-state.js'\nimport { OMSSPluginError } from '@/utils/error.js'\nimport { Result } from '@/types/utils.js'\nimport { ERR, OK } from '@/utils/utils.js'\n\n/**\n * Registry responsible for executing and managing OMSS Plugins.\n *\n * Plugins are executed when added.\n */\nexport class PluginRegistry {\n /**\n * States of plugin x\n */\n readonly #states = new Map<UnknownPluginType, PluginState>()\n readonly #server: OMSSServer\n\n /**\n * Registration stack used for circular dependency detection.\n */\n readonly #stack: UnknownPluginType[] = []\n\n constructor(server: OMSSServer) {\n this.#server = server\n }\n\n async add(plugin: OMSSPluginType): Promise<Result<PluginState.Registered, OMSSPluginError>>\n\n async add<T>(plugin: OMSSConfiguredPluginType<T>, options: OMSSPluginOptions<T>): Promise<Result<PluginState.Registered, OMSSPluginError>>\n\n /**\n * Adds and runs a plugin with its options.\n *\n * @typeParam T - Plugin options type.\n * @param plugin - The plugin function to register.\n * @param options - Plugin options or a factory function that resolves options.\n */\n async add(plugin: UnknownPluginType, options?: unknown): Promise<Result<PluginState.Registered, OMSSPluginError>> {\n // Check whether this plugin is already known\n const state = this.#states.get(plugin)\n\n if (state === PluginState.Registering) {\n const chain = [...this.#stack, plugin].map((p) => p.name).join(' -> ')\n\n return ERR(new OMSSPluginError(`Circular plugin dependency detected: ${chain}`))\n }\n\n if (state === PluginState.Registered) {\n return ERR(new OMSSPluginError(`Plugin \"${plugin.name}\" is already registered`))\n }\n\n // Start registering\n this.#states.set(plugin, PluginState.Registering)\n this.#stack.push(plugin)\n\n // Build options if a factory function is provided\n const resolved = typeof options === 'function' ? (options as (server: OMSSServer) => unknown)(this.#server) : options\n\n try {\n // Check if the plugin has a single argument\n if (plugin.length === 1) {\n // execute the plugin with the server instance\n await (plugin as OMSSPluginType)(this.#server)\n } else {\n // execute the plugin with the server instance and resolved options\n await plugin(this.#server, resolved)\n }\n\n this.#states.set(plugin, PluginState.Registered)\n return OK(PluginState.Registered)\n } catch (err) {\n this.#states.delete(plugin)\n return ERR(err instanceof OMSSPluginError ? err : new OMSSPluginError(String(err), { cause: err }))\n } finally {\n this.#stack.pop()\n }\n }\n\n /**\n * Get the current plugin state.\n */\n getState<T>(plugin: UnknownPluginType | OMSSPluginType | OMSSConfiguredPluginType<T>): PluginState {\n return this.#states.get(plugin as UnknownPluginType) ?? PluginState.Unavailable\n }\n}\n","import { PluginRegistry } from '@/features/plugins/PluginRegistry.js'\nimport { HookRegistry } from '@/features/hooks/HookRegistry.js'\nimport type { OMSSConfiguredPluginType, OMSSPluginOptions, OMSSPluginType, UnknownPluginType } from '@/types/plugin.js'\nimport OMSSServer from '@/core/server.js'\nimport { ERR } from '@/utils/utils.js'\nimport { OMSSPluginError } from '@/utils/error.js'\nimport type { OMSSHooks } from '@/types/hooks.js'\n\n/**\n * The public API for managing OMSS plugins.\n */\nexport class PluginService {\n readonly #pluginRegistry: PluginRegistry\n readonly #hookRegistry: HookRegistry<OMSSHooks>\n #insideBeforePluginRegister = false\n\n constructor(omssServer: OMSSServer, pluginRegistry: PluginRegistry, hookRegistry: HookRegistry<OMSSHooks>) {\n this.#pluginRegistry = pluginRegistry\n this.#hookRegistry = hookRegistry\n }\n\n /**\n * Register an OMSS plugin with no config into the system.\n * @param plugin - Plugin function\n */\n async register(plugin: OMSSPluginType): ReturnType<PluginRegistry['add']>\n\n /**\n * Register an OMSS plugin with a config into the system.\n * @param plugin - Plugin function\n * @param options - Plugin configuration\n */\n async register<T>(plugin: OMSSConfiguredPluginType<T>, options: OMSSPluginOptions<T>): ReturnType<PluginRegistry['add']>\n\n /**\n * Registers an OMSS plugin that can take a config into the system, but does not need to.\n * @param plugin - Plugin implementation\n * @param options - Plugin configuration\n */\n async register(plugin: UnknownPluginType, options?: unknown): ReturnType<PluginRegistry['add']> {\n if (this.#insideBeforePluginRegister) {\n return ERR(new OMSSPluginError('Plugins cannot be registered during beforePluginRegister'))\n }\n\n this.#insideBeforePluginRegister = true\n try {\n await this.#hookRegistry.run('beforePluginRegister', { plugin: plugin as UnknownPluginType, options })\n } finally {\n this.#insideBeforePluginRegister = false\n }\n\n const result = await this.#pluginRegistry.add(plugin, options)\n\n if (!result.ok) {\n await this.#hookRegistry.run('pluginRegisterFailed', { plugin, options, error: result.error })\n return result\n }\n\n await this.#hookRegistry.run('afterPluginRegister', { plugin, options })\n return result\n }\n\n /**\n * Get the current State of a plugin\n * @param plugin - the plugin to get the state from\n * @returns - a value of the PluginState enum\n */\n getPluginState(plugin: UnknownPluginType): ReturnType<PluginRegistry['getState']> {\n return this.#pluginRegistry.getState(plugin)\n }\n}\n","import type { OMSSId, ParsedOMSSId } from '@/types/resolver.js'\nimport { OMSSResolverError } from '@/utils/error.js'\nimport { Result } from '@/types/utils.js'\nimport { ERR, OK, validateSafeUniqueString } from '@/utils/utils.js'\n\n/**\n * Parses an OMSS ID in the form `namespace:value_1[:value_2[:...]]`.\n */\nexport function parseOMSSId(id: OMSSId): Result<ParsedOMSSId, OMSSResolverError> {\n if (/\\s/.test(id)) {\n return ERR(new OMSSResolverError(`Invalid OMSS ID \"${id}\": cannot contain whitespace`))\n }\n\n const parts = id.split(':')\n\n if (parts.length < 2) {\n return ERR(new OMSSResolverError(`Invalid OMSS ID \"${id}\": missing namespace separator \":\"`))\n }\n\n const [namespace, ...values] = parts\n\n if (!namespace) {\n return ERR(new OMSSResolverError(`Invalid OMSS ID \"${id}\": namespace cannot be empty`))\n }\n\n const req = validateSafeUniqueString(namespace, 'OMSS namespace', OMSSResolverError)\n\n if (!req.ok) {\n return ERR(req.error)\n }\n\n for (const [i, value] of values.entries()) {\n if (value.length === 0) {\n return ERR(new OMSSResolverError(`Invalid OMSS ID \"${id}\": value ${i + 1} cannot be empty`))\n }\n }\n\n const decodedValues = values.map((value) => decodeURIComponent(value))\n\n return OK({\n namespace,\n values: decodedValues,\n raw: id,\n })\n}\n","import {\r\n EmittedSource,\r\n EmittedSubtitle,\r\n OMSSProviderResult,\r\n ProviderResult,\r\n ProviderResultEmitter,\r\n Source,\r\n SourceQuality,\r\n SourceTypes,\r\n Subtitle,\r\n SubtitleFormat,\r\n UnknownProvider,\r\n} from '@/types/provider.js'\r\nimport { OMSSProviderError } from '@/utils/error.js'\r\nimport { Result } from '@/types/utils.js'\r\nimport { ERR, OK } from '@/utils/utils.js'\r\nimport { HookRegistry } from '@/features/hooks/HookRegistry.js'\r\nimport { ProviderHooks } from '@/types/hooks.js'\r\nimport { DASH_REGEX, HLS_REGEX, MKV_REGEX, MP4_REGEX, SRT_REGEX, VTT_REGEX } from '@/utils/regexp.js'\r\nimport { CleaningFunction } from '@/types/source.js'\r\nimport { ParsedOMSSId } from '@/types/resolver.js'\r\n\r\n/**\r\n * Creates a fresh `ProviderResultEmitter` instance scoped to a single\r\n * `getSources()` execution.\r\n *\r\n * A new emitter MUST be created per provider call. Emitters hold internal,\r\n * mutable state (accumulated sources/subtitles/errors) via closures, so\r\n * reusing a single emitter across concurrent provider executions would\r\n * cause data from one provider to leak into another's response.\r\n *\r\n * @param provider - The provider instance for which this emitter is being created.\r\n * @param hookReg - The hook registry instance for managing hooks.\r\n * @param cleaningFunc - A function to clean up source/subtitle URLs and headers.\r\n * @param id - The parsed OMSS ID of the current request.\r\n * @returns A new `ProviderResultEmitter` bound to this execution.\r\n *\r\n */\r\nexport function createProviderResultEmitter(provider: Readonly<UnknownProvider>, hookReg: HookRegistry<ProviderHooks>, cleaningFunc: CleaningFunction, id: ParsedOMSSId): ProviderResultEmitter {\r\n /**\r\n * Accumulated sources emitted via `source()` during this execution.\r\n * Flushed into the final result when `done()` is called.\r\n */\r\n const sources: Source[] = []\r\n\r\n /**\r\n * Accumulated subtitles emitted via `subtitle()` during this execution.\r\n * Subtitles are intentionally NOT linked to any source or audio track,\r\n * so they live in their own flat array regardless of how many sources\r\n * were emitted.\r\n */\r\n const subtitles: Subtitle[] = []\r\n\r\n /**\r\n * Accumulated NON-fatal errors emitted via `error()` during this execution.\r\n * These are returned alongside successful results (via `done()`) so\r\n * that partial failures (e.g. \"server 2 of 3 failed\") don't discard\r\n * otherwise-valid sources.\r\n */\r\n const errors: OMSSProviderError[] = []\r\n\r\n return {\r\n /**\r\n * Utilities to make your life easier\r\n */\r\n utils: {\r\n /**\r\n * Utilities for parsing metadata of sources\r\n */\r\n source: {\r\n /**\r\n * Parse a string into a possible source type.\r\n * @param possibleType - The string to parse\r\n */\r\n parseType(possibleType: string): SourceTypes {\r\n if (HLS_REGEX.test(possibleType)) {\r\n return 'hls'\r\n } else if (MP4_REGEX.test(possibleType)) {\r\n return 'mp4'\r\n } else if (DASH_REGEX.test(possibleType)) {\r\n return 'dash'\r\n } else if (MKV_REGEX.test(possibleType)) {\r\n return 'mkv'\r\n } else {\r\n // most commonly used is hls\r\n return 'hls'\r\n }\r\n },\r\n /**\r\n * Parse a string into a possible source quality.\r\n * @param possibleQuality - The string to parse\r\n */\r\n parseQuality(possibleQuality: string): SourceQuality {\r\n if (!possibleQuality) {\r\n return 'Auto'\r\n }\r\n\r\n const value = possibleQuality.toLowerCase().trim().replace(/_/g, ' ').replace(/-/g, ' ')\r\n\r\n // Direct quality labels / aliases\r\n if (/\\b(8k|uhd\\s*8k|4320p)\\b/.test(value)) {\r\n return '8K'\r\n }\r\n\r\n if (/\\b(4k|uhd|ultra\\s*hd|2160p)\\b/.test(value)) {\r\n return '4K'\r\n }\r\n\r\n if (/\\b(qhd|2k|1440p|2560x1440)\\b/.test(value)) {\r\n return 'QHD'\r\n }\r\n\r\n if (/\\b(fhd|full\\s*hd|1080p|1920x1080)\\b/.test(value)) {\r\n return 'FHD'\r\n }\r\n\r\n if (/\\b(hd|720p|1280x720)\\b/.test(value)) {\r\n return 'HD'\r\n }\r\n\r\n if (/\\b(sd|480p|576p|360p|240p)\\b/.test(value)) {\r\n return 'SD'\r\n }\r\n\r\n const resolutionMatch = value.match(/(\\d{3,4})(?:p|x\\d{3,4})?/)\r\n\r\n if (resolutionMatch) {\r\n const resolution = Number(resolutionMatch[1])\r\n\r\n if (resolution >= 4320) {\r\n return '8K'\r\n }\r\n\r\n if (resolution >= 2160) {\r\n return '4K'\r\n }\r\n\r\n if (resolution >= 1440) {\r\n return 'QHD'\r\n }\r\n\r\n if (resolution >= 1080) {\r\n return 'FHD'\r\n }\r\n\r\n if (resolution >= 720) {\r\n return 'HD'\r\n }\r\n\r\n if (resolution > 0) {\r\n return 'SD'\r\n }\r\n }\r\n\r\n const bitrateMatch = value.match(/(\\d+(?:\\.\\d+)?)\\s*(mbps|kbps)/)\r\n\r\n if (bitrateMatch) {\r\n const bitrate = Number(bitrateMatch[1])\r\n const unit = bitrateMatch[2]\r\n\r\n const mbps = unit === 'kbps' ? bitrate / 1000 : bitrate\r\n\r\n if (mbps >= 25) {\r\n return '4K'\r\n }\r\n\r\n if (mbps >= 8) {\r\n return 'FHD'\r\n }\r\n\r\n if (mbps >= 3) {\r\n return 'HD'\r\n }\r\n\r\n return 'SD'\r\n }\r\n\r\n return 'Auto'\r\n },\r\n },\r\n /**\r\n * Utilities for parsing metadata of subtitles\r\n */\r\n subtitle: {\r\n /**\r\n * Parse a string into a possible subtitle format.\r\n * @param possibleFormat - The string to parse\r\n */\r\n parseFormat(possibleFormat: string): SubtitleFormat {\r\n if (VTT_REGEX.test(possibleFormat)) {\r\n return 'vtt'\r\n } else if (SRT_REGEX.test(possibleFormat)) {\r\n return 'srt'\r\n } else {\r\n return 'vtt'\r\n }\r\n },\r\n },\r\n },\r\n\r\n /**\r\n * Emits a custom, provider-defined action/event.\r\n *\r\n * @param action - A custom event name (e.g. \"cache.hit\").\r\n * @param data - Arbitrary payload associated with the event.\r\n */\r\n emit(action: string, data: unknown): void {\r\n // action cannot be whitespace or another hook name\r\n if (/\\s/.test(action) || Object.keys(this).includes(action)) {\r\n return\r\n }\r\n hookReg.run(action, { data, provider, id, timestamp: new Date().toISOString() })\r\n },\r\n\r\n /**\r\n * Logs verbose debug information. Intended for development/troubleshooting\r\n * only and should be stripped or gated behind a debug flag in production.\r\n *\r\n * @param args - Values to log, forwarded as-is (same semantics as `console.debug`).\r\n */\r\n debug(...args: unknown[]): void {\r\n hookReg.run('debug', { provider, args, id, timestamp: new Date().toISOString() })\r\n },\r\n\r\n /**\r\n * Logs general informational messages about provider execution\r\n * (e.g. \"Fetched media\", \"Cache miss, fetching from upstream\").\r\n *\r\n * @param args - Values to log.\r\n */\r\n info(...args: unknown[]): void {\r\n hookReg.run('info', { provider, args, id, timestamp: new Date().toISOString() })\r\n },\r\n\r\n /**\r\n * Logs a non-fatal warning. Use this for degraded-but-recoverable\r\n * situations (e.g. \"missing quality metadata, defaulting to Auto\").\r\n *\r\n * @param args - Values to log.\r\n */\r\n warn(...args: unknown[]): void {\r\n hookReg.run('warn', { provider, args, id, timestamp: new Date().toISOString() })\r\n },\r\n\r\n /**\r\n * Records a NON-fatal error. The provider continues executing after\r\n * calling this — use `fatal()` instead if the provider cannot continue.\r\n *\r\n * The error is accumulated and returned to the requestor as part of\r\n * the `diagnostics`/`errors` field once `done()` is called, allowing\r\n * partial success (e.g. some sources found despite one upstream\r\n * server failing).\r\n *\r\n * @param error - The error to record. Will be surfaced to the client.\r\n */\r\n error(error: OMSSProviderError): void {\r\n errors.push(error)\r\n hookReg.run('error', { provider, error, id, timestamp: new Date().toISOString() })\r\n },\r\n\r\n /**\r\n * Emits a single resolved source.\r\n *\r\n * @param source - The fully-formed source object to emit.\r\n */\r\n source(source: EmittedSource): void {\r\n const cleanedSource = {\r\n ...source,\r\n }\r\n\r\n const cleaned = cleaningFunc({\r\n url: cleanedSource.url,\r\n header: cleanedSource.header,\r\n })\r\n\r\n cleanedSource.url = cleaned.url\r\n cleanedSource.header = cleaned.header\r\n\r\n if ('audioTracks' in cleanedSource && cleanedSource.audioTracks) {\r\n // had to split first and rest, since audioTracks requires least one track.\r\n const [first, ...rest] = cleanedSource.audioTracks\r\n\r\n cleanedSource.audioTracks = [\r\n {\r\n ...first,\r\n ...cleaningFunc({\r\n url: first.url,\r\n header: first.header,\r\n }),\r\n },\r\n ...rest.map((track) => ({\r\n ...track,\r\n ...cleaningFunc({\r\n url: track.url,\r\n header: track.header,\r\n }),\r\n })),\r\n ]\r\n }\r\n const fullSource: Source = {\r\n ...cleanedSource,\r\n provider: {\r\n id: provider.id,\r\n name: provider.name,\r\n },\r\n }\r\n\r\n sources.push(fullSource)\r\n\r\n hookReg.run('source', {\r\n provider,\r\n source: fullSource,\r\n id,\r\n timestamp: new Date().toISOString(),\r\n })\r\n },\r\n\r\n /**\r\n * Emits a single subtitle track.\r\n *\r\n * @param subtitle - The subtitle object to emit.\r\n */\r\n subtitle(subtitle: EmittedSubtitle): void {\r\n const obj = {\r\n url: subtitle.url,\r\n header: subtitle.header,\r\n }\r\n const { url, header } = cleaningFunc(obj)\r\n subtitle.url = url\r\n subtitle.header = header\r\n const fullSub = { ...subtitle, provider: { id: provider.id, name: provider.name } }\r\n subtitles.push(fullSub)\r\n hookReg.run('subtitle', { provider, subtitle: fullSub, id, timestamp: new Date().toISOString() })\r\n },\r\n\r\n /**\r\n * Immediately aborts provider execution with a fatal error.\r\n *\r\n * @param error - The fatal error describing why the provider could not proceed.\r\n * @returns An `ERR` result wrapping the given error.\r\n */\r\n fatal(error: OMSSProviderError): Result<never, OMSSProviderError> {\r\n const accumulatedError = new AggregateError([error, ...errors], error.message, { cause: error.cause })\r\n\r\n const finalErr = new OMSSProviderError(accumulatedError.message, { cause: accumulatedError })\r\n\r\n hookReg.run('error', { provider, error: finalErr, id, timestamp: new Date().toISOString() })\r\n\r\n return ERR(finalErr)\r\n },\r\n\r\n /**\r\n * Signals that the provider has finished emitting sources/subtitles\r\n * and finalizes the result.\r\n *\r\n * @returns An `OK` result containing all accumulated sources,\r\n * subtitles, and non-fatal errors for this execution.\r\n */\r\n done(): ProviderResult {\r\n const result: OMSSProviderResult = {\r\n sources,\r\n subtitles,\r\n errors,\r\n }\r\n\r\n hookReg.run('done', { provider, result, id, timestamp: new Date().toISOString() })\r\n\r\n return OK(result)\r\n },\r\n }\r\n}\r\n","import OMSSServer from '@/core/server.js'\r\nimport { ProviderRegistry } from '@/features/providers/ProviderRegistry.js'\r\nimport { parseOMSSId } from '@/features/resolvers/utils.js'\r\nimport type { ProviderResult, Source, Subtitle, UnknownProvider } from '@/types/provider.js'\r\nimport type { OMSSId, ResolverExecutionContext } from '@/types/resolver.js'\r\nimport { CleaningFunction, GatheredSources, GetSourcesOptions } from '@/types/source.js'\r\nimport type { Result } from '@/types/utils.js'\r\nimport { OMSSProviderError, OMSSSourceGatheringError } from '@/utils/error.js'\r\nimport { ERR, OK } from '@/utils/utils.js'\r\nimport { createProviderResultEmitter } from '@/features/providers/ProviderResultEmitter.js'\r\nimport { ProviderHooks } from '@/types/hooks.js'\r\nimport { ExtractorService } from '@/features/extractors/ExtractorService.js'\r\nimport { HookService } from '@/features/hooks/HookService.js'\r\n\r\n/**\r\n * Internal source gathering core.\r\n *\r\n * Handles OMSS ID parsing, provider lookup, resolver-level deduplication,\r\n * provider execution, and final result aggregation.\r\n *\r\n * This class intentionally does not know about OMSS Hooks, middleware, or in-flight\r\n * request sharing. Those concerns belong to SourceService.\r\n */\r\nexport class SourceCore {\r\n readonly #providerRegistry: ProviderRegistry\r\n readonly #extractorService: ExtractorService\r\n readonly #omssServer: OMSSServer\r\n\r\n constructor(omssServer: OMSSServer, providerRegistry: ProviderRegistry, extractorService: ExtractorService) {\r\n this.#omssServer = omssServer\r\n this.#providerRegistry = providerRegistry\r\n this.#extractorService = extractorService\r\n }\r\n\r\n /**\r\n * Gather sources for a single OMSS ID.\r\n *\r\n * @param omssId - Raw OMSS identifier.\r\n * @param opts - Optional source gathering parameters.\r\n * @param providerHookRegistry - Hook registry for provider hooks.\r\n * @param cleaningFunction - Optional custom function to clean url's and headers\r\n * @returns Aggregated provider results or a source gathering error.\r\n */\r\n async getSources(\r\n omssId: OMSSId,\r\n opts: GetSourcesOptions,\r\n providerHookRegistry: HookService<ProviderHooks>,\r\n cleaningFunction: CleaningFunction\r\n ): Promise<Result<GatheredSources, OMSSSourceGatheringError>> {\r\n // try to parse the OMSS ID\r\n const parsed = parseOMSSId(omssId)\r\n\r\n if (!parsed.ok) {\r\n return ERR(new OMSSSourceGatheringError(`Failed to parse OMSS id \"${omssId}\": ${parsed.error.message}`, { cause: parsed.error }))\r\n }\r\n\r\n // we know that the id is valid now. Now we got to find the providers that can handle that namespace. If a specific provider is requested, we only look for that one.\r\n const providers: UnknownProvider[] = opts.providerId\r\n ? this.#providerRegistry.getAll((p) => p.id === opts.providerId && p.resolver.namespace === parsed.value.namespace)\r\n : this.#providerRegistry.getAll((p) => p.resolver.namespace === parsed.value.namespace)\r\n\r\n // if no provider can handle that namespace, return an error\r\n if (providers.length === 0) {\r\n return ERR(new OMSSSourceGatheringError(`No providers found for namespace \"${parsed.value.namespace}\"` + (opts.providerId ? ` and provider \"${opts.providerId}\"` : '')))\r\n }\r\n\r\n // if no abortsignal comes, just create a new one (does not abort)\r\n const signal = opts.abortSignal ?? new AbortController().signal\r\n\r\n // create the resolver context\r\n const ctx: ResolverExecutionContext = {\r\n server: this.#omssServer,\r\n signal,\r\n }\r\n\r\n /**\r\n * Resolver-level deduplication: multiple providers that share the same\r\n * resolver run that resolver only once, then share the result.\r\n */\r\n const resolverCache = new Map<string, Promise<Result<unknown, OMSSSourceGatheringError>>>()\r\n\r\n /**\r\n * Get resolver metadata for a provider, reusing the same resolver\r\n * promise when multiple providers share the same resolver.\r\n *\r\n * @param provider - Provider whose resolver metadata should be loaded.\r\n * @returns Resolver metadata or a source gathering error.\r\n */\r\n const getResolvedMeta = (provider: UnknownProvider): Promise<Result<unknown, OMSSSourceGatheringError>> => {\r\n const resolverKey = `${provider.resolver.namespace}:${provider.resolver.name}`\r\n\r\n let promise = resolverCache.get(resolverKey)\r\n\r\n if (!promise) {\r\n promise = (async (): Promise<Result<unknown, OMSSSourceGatheringError>> => {\r\n if (signal.aborted) {\r\n return ERR(new OMSSSourceGatheringError('Operation aborted'))\r\n }\r\n\r\n const result = await provider.resolver.resolve(parsed.value, ctx)\r\n\r\n if (!result.ok) {\r\n return ERR(new OMSSSourceGatheringError(`Resolver failed for ${resolverKey}: ${result.error.message}`, { cause: result.error }))\r\n }\r\n\r\n return OK(result.value)\r\n })()\r\n\r\n resolverCache.set(resolverKey, promise)\r\n }\r\n\r\n return promise\r\n }\r\n\r\n /**\r\n * Resolve sources for a single provider.\r\n *\r\n * @param provider - Provider to execute.\r\n * @returns Provider result or a source gathering error.\r\n */\r\n const resolveForProvider = async (provider: UnknownProvider): Promise<ProviderResult | Result<never, OMSSSourceGatheringError>> => {\r\n if (signal.aborted) {\r\n return ERR(new OMSSSourceGatheringError('Operation aborted'))\r\n }\r\n\r\n const supportsId = await provider.supportsId(parsed.value)\r\n if (!supportsId) {\r\n return OK({ sources: [], subtitles: [], errors: [new OMSSProviderError(`Provider \"${provider.id}\" did not support this id: \"${parsed.value.raw}\"`)] })\r\n }\r\n\r\n if (signal.aborted) {\r\n return ERR(new OMSSSourceGatheringError('Operation aborted'))\r\n }\r\n\r\n const metaResult = await getResolvedMeta(provider)\r\n\r\n if (!metaResult.ok) {\r\n return metaResult\r\n }\r\n\r\n if (signal.aborted) {\r\n return ERR(new OMSSSourceGatheringError('Operation aborted'))\r\n }\r\n\r\n const resultEmitter = createProviderResultEmitter(provider, providerHookRegistry.__getRegistry(), cleaningFunction, parsed.value)\r\n\r\n return provider.getSources(\r\n {\r\n utils: {\r\n omssId: parsed.value,\r\n abortSignal: signal,\r\n findExtractor: this.#extractorService.find,\r\n },\r\n meta: metaResult.value,\r\n },\r\n resultEmitter\r\n )\r\n }\r\n\r\n const settled = await Promise.allSettled(providers.map((provider) => resolveForProvider(provider)))\r\n\r\n if (signal.aborted) {\r\n return ERR(new OMSSSourceGatheringError('Operation aborted'))\r\n }\r\n\r\n const allSources: Source[] = []\r\n const allSubtitles: Subtitle[] = []\r\n const unexpectedErrors: unknown[] = []\r\n const omssErrors: Array<Extract<ProviderResult, { ok: false }>['error'] | OMSSSourceGatheringError> = []\r\n let hasSuccess = false\r\n\r\n for (const item of settled) {\r\n if (item.status === 'rejected') {\r\n omssErrors.push(\r\n new OMSSSourceGatheringError(`Provider execution failed: ${item.reason instanceof Error ? item.reason.message : String(item.reason)}`, {\r\n cause: item.reason instanceof Error ? item.reason : undefined,\r\n })\r\n )\r\n unexpectedErrors.push(item.reason)\r\n continue\r\n }\r\n\r\n const res = item.value\r\n\r\n if (res.ok) {\r\n hasSuccess = true\r\n allSources.push(...res.value.sources)\r\n allSubtitles.push(...res.value.subtitles)\r\n omssErrors.push(...res.value.errors)\r\n continue\r\n }\r\n\r\n omssErrors.push(res.error)\r\n }\r\n\r\n if (!hasSuccess) {\r\n return ERR(\r\n new OMSSSourceGatheringError(`All providers failed for namespace \"${parsed.value.namespace}\" and id: \"${parsed.value.raw}\"`, {\r\n cause: new AggregateError([...omssErrors, ...unexpectedErrors], 'Multiple failures detected'),\r\n })\r\n )\r\n }\r\n\r\n return OK({\r\n sources: allSources,\r\n subtitles: allSubtitles,\r\n errors: omssErrors,\r\n })\r\n }\r\n}\r\n","/**\n * Generic helper for deduplicating concurrent asynchronous work by key.\n *\n * When the same key is requested multiple times while a request is still in\n * flight, all callers receive the same Promise. Once the Promise settles, the\n * key is removed automatically so the next request starts fresh.\n *\n * @typeParam TKey - Cache key type.\n * @typeParam TValue - Promise resolution type.\n */\nexport class AsyncDeduper<TKey, TValue> {\n readonly #entries = new Map<TKey, Promise<TValue>>()\n\n /**\n * Returns the current in-flight Promise for a key, if one exists.\n *\n * @param key - In-flight request key.\n * @returns Existing Promise or undefined.\n */\n get(key: TKey): Promise<TValue> | undefined {\n return this.#entries.get(key)\n }\n\n /**\n * Check whether a key currently has an in-flight Promise.\n *\n * @param key - In-flight request key.\n * @returns True if the key is currently in flight.\n */\n has(key: TKey): boolean {\n return this.#entries.has(key)\n }\n\n /**\n * Delete a key manually.\n *\n * @param key - In-flight request key.\n * @returns True if an entry existed and was removed.\n */\n delete(key: TKey): boolean {\n return this.#entries.delete(key)\n }\n\n /**\n * Clear all tracked in-flight requests.\n */\n clear(): void {\n this.#entries.clear()\n }\n\n /**\n * Run a Promise factory for a key, reusing an existing in-flight Promise\n * when available.\n *\n * @param key - In-flight request key.\n * @param factory - Factory that creates the Promise when no request exists yet.\n * @returns Shared or newly created Promise.\n */\n run(key: TKey, factory: () => Promise<TValue>): Promise<TValue> {\n const existing = this.#entries.get(key)\n\n if (existing) {\n return existing\n }\n\n const promise = factory().finally(() => {\n this.#entries.delete(key)\n })\n\n this.#entries.set(key, promise)\n\n return promise\n }\n}\n","import { MiddlewareHandler, MiddlewareOperationMap } from '@/types/middleware.js'\n\n/**\n * Reusable typed middleware runner.\n */\nexport class MiddlewareRunner<TOperations extends MiddlewareOperationMap> {\n readonly #handlers: Partial<{\n [K in keyof TOperations]: MiddlewareHandler<TOperations, K>[]\n }> = {}\n\n /**\n * Register middleware for a specific operation.\n *\n * @param method - Operation name.\n * @param handler - Middleware handler.\n */\n use<TMethod extends keyof TOperations>(method: TMethod, handler: MiddlewareHandler<TOperations, TMethod>): void {\n const list = (this.#handlers[method] ??= []) as MiddlewareHandler<TOperations, TMethod>[]\n\n list.push(handler)\n }\n\n /**\n * Run middleware chain for a specific operation.\n *\n * @param method - Operation name.\n * @param context - Operation context payload.\n * @param finalHandler - Final function to execute after middleware.\n * @returns The operation result.\n */\n run<TMethod extends keyof TOperations>(\n method: TMethod,\n context: TOperations[TMethod]['context'],\n finalHandler: () => Promise<TOperations[TMethod]['result']>\n ): Promise<TOperations[TMethod]['result']> {\n const handlers = (this.#handlers[method] ?? []) as readonly MiddlewareHandler<TOperations, TMethod>[]\n\n let index = -1\n\n const dispatch = (position: number): Promise<TOperations[TMethod]['result']> => {\n if (position <= index) {\n return Promise.reject(new Error('next() called multiple times'))\n }\n\n index = position\n\n const handler = handlers[position]\n\n if (!handler) {\n return finalHandler()\n }\n\n return handler(context, () => dispatch(position + 1))\n }\n\n return dispatch(0)\n }\n}\n","import OMSSServer from '@/core/server.js'\nimport { HookRegistry } from '@/features/hooks/HookRegistry.js'\nimport { ProviderRegistry } from '@/features/providers/ProviderRegistry.js'\nimport { SourceCore } from '@/features/source/SourceCore.js'\nimport type { CleaningFunction, GatheredSources, GetSourcesOptions, SourceServiceMiddleware, SourceServiceOperations } from '@/types/source.js'\nimport type { OMSSId } from '@/types/resolver.js'\nimport type { Result } from '@/types/utils.js'\nimport { OMSSSourceGatheringError } from '@/utils/error.js'\nimport { AsyncDeduper } from '@/utils/AsyncDeduper.js'\nimport { MiddlewareRunner } from '@/utils/middleware.js'\nimport type { OMSSHooks, ProviderHooks } from '@/types/hooks.js'\nimport { ExtractorService } from '@/features/extractors/ExtractorService.js'\nimport { HookService } from '@/features/hooks/HookService.js'\n\n/**\n * Public API for resolving sources for media.\n *\n * This service owns the public method surface, middleware execution,\n * lifecycle hook dispatching, and request coalescing. The actual source\n * gathering implementation lives in {@link SourceCore}.\n */\nexport class SourceService {\n readonly #hookRegistry: HookRegistry<OMSSHooks>\n readonly #core: SourceCore\n\n /**\n * Middleware runner for SourceService operations.\n */\n readonly #middleware = new MiddlewareRunner<SourceServiceOperations>()\n\n /**\n * Deduplicates concurrent getSources requests by request key.\n */\n readonly #inFlight = new AsyncDeduper<string, Result<GatheredSources, OMSSSourceGatheringError>>()\n\n constructor(omssServer: OMSSServer, providerRegistry: ProviderRegistry, hookRegistry: HookRegistry<OMSSHooks>, extractorService: ExtractorService) {\n this.#hookRegistry = hookRegistry\n this.#core = new SourceCore(omssServer, providerRegistry, extractorService)\n }\n\n public get cleaningFunction(): CleaningFunction {\n return this.#cleaningFunction\n }\n\n public set cleaningFunction(fn: CleaningFunction) {\n this.#cleaningFunction = fn\n }\n\n /**\n * Register middleware for a SourceService method.\n *\n * Middleware can be used for cross-cutting concerns such as caching,\n * logging, tracing, or metrics.\n *\n * @param method - Middleware-enabled method name.\n * @param handler - Middleware handler.\n */\n use<TMethod extends keyof SourceServiceOperations>(method: TMethod, handler: SourceServiceMiddleware<TMethod>): void {\n this.#middleware.use(method, handler)\n }\n\n /**\n * Fetch sources from all matching providers for an OMSS ID.\n *\n * This method is middleware-enabled. Concurrent requests for the same\n * `omssId` and `providerId` share the same in-flight Promise until the\n * request settles.\n *\n * @param omssId - OMSS identifier such as `\"tmdb:12345\"`.\n * @param options - Optional source gathering parameters.\n * @returns Aggregated provider results or a source gathering error.\n */\n async getSources(omssId: OMSSId, options: GetSourcesOptions = {}): Promise<Result<GatheredSources, OMSSSourceGatheringError>> {\n return this.#middleware.run('getSources', { omssId, options }, () => this.#internalGetSources(omssId, options))\n }\n\n /**\n * Get and set the cleaning function for the source core.\n */\n #cleaningFunction: CleaningFunction = (obj) => obj\n\n /**\n * Internal wrapper around source gathering.\n *\n * Runs lifecycle hooks and deduplicates concurrent requests before\n * delegating to {@link SourceCore}.\n *\n * @param omssId - OMSS identifier.\n * @param options - Optional source gathering parameters.\n * @returns Aggregated provider results or a source gathering error.\n */\n async #internalGetSources(omssId: OMSSId, options: GetSourcesOptions): Promise<Result<GatheredSources, OMSSSourceGatheringError>> {\n await this.#hookRegistry.run('beforeGetSources', {\n omssId,\n providerId: options.providerId,\n })\n\n const inFlightKey = this.#getInFlightKey(omssId, options.providerId)\n\n const result = await this.#inFlight.run(inFlightKey, () =>\n this.#core.getSources(omssId, options, options.providerHookService ?? new HookService<ProviderHooks>(), options.cleaningFunction ?? this.cleaningFunction)\n )\n\n if (result.ok) {\n await this.#hookRegistry.run('afterGetSources', {\n omssId,\n providerId: options.providerId,\n result: result.value,\n })\n\n return this.#middleware.run('afterGetSources', { omssId, options, result }, () => Promise.resolve(result))\n }\n\n await this.#hookRegistry.run('getSourcesFailed', {\n omssId,\n providerId: options.providerId,\n error: result.error,\n })\n\n return result\n }\n\n /**\n * Build the stable in-flight key for a getSources request.\n *\n * @param omssId - OMSS identifier.\n * @param providerId - Optional provider filter.\n * @returns Unique in-flight request key.\n */\n #getInFlightKey(omssId: OMSSId, providerId?: string): string {\n return `${omssId}|${providerId ?? ''}`\n }\n}\n","import type { UnknownProvider } from '@/types/provider.js'\nimport { OMSSProviderError } from '@/utils/error.js'\nimport { ERR, OK, validateSafeUniqueString } from '@/utils/utils.js'\nimport { Result } from '@/types/utils.js'\nimport { CATALOG_ENTRY } from '@/utils/regexp.js'\n\n/**\n * Registry responsible for storing and managing OMSS Providers.\n *\n * Providers are stored by their unique {@link UnknownProvider.id}.\n * This registry does not know about hooks.\n */\nexport class ProviderRegistry {\n /**\n * Internal map of registered providers, keyed by provider ID.\n */\n readonly #providers = new Map<string, UnknownProvider>()\n\n /**\n * Adds a provider to the registry.\n *\n * @param provider - The provider instance to register.\n * @returns `OK` if registration succeeded, `ERR` if the provider ID is already taken.\n */\n async add(provider: UnknownProvider): Promise<Result<UnknownProvider, OMSSProviderError>> {\n if (this.#providers.has(provider.id)) {\n return ERR(new OMSSProviderError(`Provider \"${provider.id}\" is already registered`))\n }\n\n const valProvIdReq = validateSafeUniqueString(provider.id, 'provider ID', OMSSProviderError)\n if (!valProvIdReq.ok) {\n return ERR(valProvIdReq.error)\n }\n\n const valResolverNamespaceReq = validateSafeUniqueString(provider.resolver.namespace, 'resolver namespace for resolver with name: \"' + provider.resolver.name + '\" and id:', OMSSProviderError)\n if (!valResolverNamespaceReq.ok) {\n return ERR(valResolverNamespaceReq.error)\n }\n\n if (this.getAll().some((p) => p.resolver.namespace === provider.resolver.namespace && p.resolver !== provider.resolver)) {\n return ERR(new OMSSProviderError(`Resolver namespace \"${provider.resolver.namespace}\" is already registered by another provider and another resolver. Use one resolver per namespace.`))\n }\n\n if (provider.catalog) {\n const entries = await provider.catalog()\n const hasWildcard = entries.includes('*')\n const hasOther = entries.some((id) => id !== '*')\n\n if (hasWildcard && hasOther) {\n return ERR(new OMSSProviderError(`Provider \"${provider.id}\" catalog contains \"*\" mixed with other IDs. Use either a single \"*\" or a list of valid IDs.`))\n }\n\n for (const id of entries) {\n if (!CATALOG_ENTRY.test(id)) {\n return ERR(new OMSSProviderError(`Provider \"${provider.id}\" catalog contains an invalid entry \"${id}\". Entries must be non-empty strings with no whitespace, or \"*\".`))\n }\n }\n }\n\n this.#providers.set(provider.id, provider)\n return OK(provider)\n }\n\n /**\n * Retrieves a registered provider by its ID.\n *\n * @param id - The unique provider ID.\n * @returns The provider instance, or `undefined` if not found.\n */\n get(id: string): UnknownProvider | undefined {\n return this.#providers.get(id)\n }\n\n /**\n * Returns all registered providers.\n */\n getAll(filter?: (p: UnknownProvider) => boolean): UnknownProvider[] {\n if (filter) {\n return [...this.#providers.values()].filter(filter)\n }\n return [...this.#providers.values()]\n }\n\n /**\n * Returns whether a provider with the given ID is registered.\n *\n * @param id - The unique provider ID.\n */\n has(id: string): boolean {\n return this.#providers.has(id)\n }\n}\n","import type { ProviderServiceMiddleware, ProviderServiceOperations, UnknownProvider } from '@/types/provider.js'\r\nimport { OMSSProviderError } from '@/utils/error.js'\r\nimport { ERR, OK } from '@/utils/utils.js'\r\nimport { ProviderRegistry } from '@/features/providers/ProviderRegistry.js'\r\nimport { HookRegistry } from '@/features/hooks/HookRegistry.js'\r\nimport type { Result } from '@/types/utils.js'\r\nimport type { OMSSHooks } from '@/types/hooks.js'\r\nimport { MiddlewareRunner } from '@/utils/middleware.js'\r\n\r\n/**\r\n * The public API for managing OMSS Providers.\r\n */\r\nexport class ProviderService {\r\n readonly #providerRegistry: ProviderRegistry\r\n readonly #hookRegistry: HookRegistry<OMSSHooks>\r\n readonly #middleware = new MiddlewareRunner<ProviderServiceOperations>()\r\n #insideBeforeProviderRegister = false\r\n\r\n constructor(providerRegistry: ProviderRegistry, hookRegistry: HookRegistry<OMSSHooks>) {\r\n this.#providerRegistry = providerRegistry\r\n this.#hookRegistry = hookRegistry\r\n }\r\n\r\n /**\r\n * Adds middleware to the `register` pipeline.\r\n * Middlewares run in insertion order, after hooks and before the\r\n * actual registry `add()` call.\r\n */\r\n use<TMethod extends keyof ProviderServiceOperations>(method: TMethod, handler: ProviderServiceMiddleware<TMethod>): this {\r\n this.#middleware.use(method, handler)\r\n return this\r\n }\r\n\r\n /**\r\n * Registers a provider into the system.\r\n */\r\n async register(provider: UnknownProvider): Promise<Result<UnknownProvider, OMSSProviderError>> {\r\n if (this.#insideBeforeProviderRegister) {\r\n return ERR(new OMSSProviderError('Providers cannot be registered during beforeProviderRegister'))\r\n }\r\n\r\n this.#insideBeforeProviderRegister = true\r\n try {\r\n await this.#hookRegistry.run('beforeProviderRegister', { provider })\r\n } finally {\r\n this.#insideBeforeProviderRegister = false\r\n }\r\n\r\n const result = await this.#middleware.run('register', { provider }, () => this.#providerRegistry.add(provider))\r\n\r\n if (!result.ok) {\r\n await this.#hookRegistry.run('providerRegisterFailed', {\r\n provider,\r\n error: result.error,\r\n })\r\n return ERR(result.error)\r\n }\r\n\r\n await this.#hookRegistry.run('afterProviderRegister', { provider })\r\n return result\r\n }\r\n\r\n /**\r\n * Retrieves a registered provider by its ID.\r\n *\r\n * @param id - The provider ID to look up.\r\n * @returns The provider instance, or `undefined` if not found.\r\n */\r\n get(id: string): ReturnType<ProviderRegistry['get']> {\r\n return this.#providerRegistry.get(id)\r\n }\r\n\r\n /**\r\n * Returns all registered providers.\r\n * @param filter - Optional filter function to apply to providers.\r\n */\r\n getAll(filter?: (p: UnknownProvider) => boolean): ReturnType<ProviderRegistry['getAll']> {\r\n return this.#providerRegistry.getAll(filter)\r\n }\r\n\r\n /**\r\n * Returns whether a provider with the given ID has been registered.\r\n *\r\n * @param id - The provider ID to check.\r\n */\r\n has(id: string): ReturnType<ProviderRegistry['has']> {\r\n return this.#providerRegistry.has(id)\r\n }\r\n\r\n /**\r\n * Returns a map of all namespaces and an array of all known identifiers for each namespace.\r\n * This list is BEST EFFORT ONLY. Do not rely on this. If any provider returns a `*` automatically, the namespace will support all identifiers (e.g. `\"tmdb\": [\"*\"], \"imdb\": [\"tt37636\", \"...\"]`.\r\n */\r\n async catalog(): Promise<Result<Map<string, string[]>, OMSSProviderError>> {\r\n const allProviders = this.getAll()\r\n const result = new Map<string, string[]>()\r\n\r\n await Promise.all(\r\n allProviders.map(async (provider) => {\r\n if (!provider.catalog) return\r\n\r\n const namespace = provider.resolver.namespace\r\n const entries = await provider.catalog()\r\n\r\n // If namespace already collapsed to wildcard, skip.\r\n if (result.get(namespace)?.[0] === '*') return\r\n\r\n if (entries.includes('*')) {\r\n // Any single wildcard provider collapses the whole namespace.\r\n result.set(namespace, ['*'])\r\n return\r\n }\r\n\r\n // Merge deduplicated IDs into the namespace bucket.\r\n const existing = result.get(namespace) ?? []\r\n const merged = Array.from(new Set([...existing, ...entries]))\r\n result.set(namespace, merged)\r\n })\r\n )\r\n\r\n return OK(result)\r\n }\r\n\r\n /**\r\n * Returns the catalog for a single namespace.\r\n * Returns `undefined` if no provider in that namespace exposes a catalog.\r\n *\r\n * @param namespace - The resolver namespace to look up (e.g. `\"tmdb\"`).\r\n * @returns Merged list of IDs for the namespace, `[\"*\"]` if any provider\r\n * signals wildcard support, or `undefined` if no catalog data exists.\r\n */\r\n async catalogForNamespace(namespace: string): Promise<Result<string[], OMSSProviderError>> {\r\n const providers = this.getAll((p) => p.resolver.namespace === namespace)\r\n if (providers.length === 0) return ERR(new OMSSProviderError(`No providers registered for namespace \"${namespace}\"`))\r\n\r\n const result: string[] = []\r\n\r\n for (const provider of providers) {\r\n if (!provider.catalog) continue\r\n\r\n const entries = await provider.catalog()\r\n\r\n if (entries.includes('*')) {\r\n return OK(['*'])\r\n }\r\n\r\n for (const id of entries) {\r\n if (!result.includes(id)) {\r\n result.push(id)\r\n }\r\n }\r\n }\r\n\r\n return result.length > 0 ? OK(result) : ERR(new OMSSProviderError(`No catalog data available for namespace \"${namespace}\"`))\r\n }\r\n}\r\n","import { ExtractorRegistry } from '@/features/extractors/ExtractorRegistry.js'\nimport { HookRegistry } from '@/features/hooks/HookRegistry.js'\nimport type { OMSSHooks } from '@/types/hooks.js'\nimport { Extractor } from '@/types/extractor.js'\nimport { Result } from '@/types/utils.js'\nimport { ERR, OK } from '@/utils/utils.js'\nimport { OMSSExtractorError } from '@/utils/error.js'\n\nexport class ExtractorService {\n readonly #extractorRegistry: ExtractorRegistry\n readonly #hookRegistry: HookRegistry<OMSSHooks>\n #insideBeforeRegisterExtractor = false\n\n constructor(extractorRegistry: ExtractorRegistry, hookRegistry: HookRegistry<OMSSHooks>) {\n this.#extractorRegistry = extractorRegistry\n this.#hookRegistry = hookRegistry\n }\n\n /**\n * Get all registered extractors (read-only).\n */\n get extractors(): Result<ReadonlyArray<Extractor>, Error> {\n return OK(this.#extractorRegistry.extractors)\n }\n\n /**\n * Find an extractor capable of handling the given URL.\n *\n * @param url - URL to search for.\n * @returns The first matching {@link Extractor} or an {@link OMSSExtractorError}.\n */\n async find(url: string): Promise<Result<Extractor, OMSSExtractorError>> {\n await this.#hookRegistry.run('beforeFindExtractor', { url })\n\n const extractors = this.#extractorRegistry.extractors\n\n const results = await Promise.all(extractors.map((extractor) => extractor.matcher(url)))\n\n for (let i = 0; i < extractors.length; i++) {\n if (results[i]!.ok) {\n const extractor = extractors[i]!\n\n await this.#hookRegistry.run('afterFindExtractor', {\n url,\n extractor,\n })\n\n return OK(extractor)\n }\n }\n\n const error = new OMSSExtractorError(`No extractor found for URL \"${url}\"`)\n\n await this.#hookRegistry.run('findExtractorFailed', {\n url,\n error,\n })\n\n return ERR(error)\n }\n\n /**\n * Register an extractor.\n *\n * @param extractor - Extractor to register.\n */\n async register(extractor: Extractor): Promise<Result<void, Error>> {\n if (this.#insideBeforeRegisterExtractor) {\n return ERR(new OMSSExtractorError('Extractors cannot be registered during beforeRegisterExtractor'))\n }\n\n this.#insideBeforeRegisterExtractor = true\n\n try {\n await this.#hookRegistry.run('beforeRegisterExtractor', {\n extractor,\n })\n } finally {\n this.#insideBeforeRegisterExtractor = false\n }\n\n try {\n this.#extractorRegistry.add(extractor)\n } catch (error) {\n const extractorError = error instanceof OMSSExtractorError ? error : new OMSSExtractorError(error instanceof Error ? error.message : String(error))\n\n await this.#hookRegistry.run('extractorRegisterFailed', {\n extractor,\n error: extractorError,\n })\n\n return ERR(extractorError)\n }\n\n await this.#hookRegistry.run('afterRegisterExtractor', {\n extractor,\n })\n\n return OK()\n }\n\n /**\n * Remove every registered extractor.\n */\n reset(): Result<void, Error> {\n this.#extractorRegistry.reset()\n return OK()\n }\n\n /**\n * Determine whether an extractor has already been registered.\n *\n * @param extractor - Extractor to check.\n */\n has(extractor: Extractor): Result<boolean, Error> {\n return OK(this.#extractorRegistry.has(extractor))\n }\n\n /**\n * Remove an extractor.\n *\n * @param extractor - Extractor to remove.\n * @returns Whether the extractor was removed.\n */\n remove(extractor: Extractor): Result<boolean, Error> {\n return OK(this.#extractorRegistry.remove(extractor))\n }\n}\n","import { Extractor } from '@/types/extractor.js'\n\n/**\n * Extractor Registry\n *\n * Manages all extractors.\n */\nexport class ExtractorRegistry {\n /**\n * Array of extractors.\n */\n readonly #extractors = Array<Extractor>()\n\n /**\n * Get all extractors.\n */\n get extractors(): Readonly<Extractor[]> {\n return this.#extractors\n }\n\n /**\n * Clear all extractors.\n */\n reset(): void {\n this.#extractors.length = 0\n }\n\n /**\n * Add an extractor.\n * @param extractor - Extractor to add.\n */\n add(extractor: Extractor): void {\n if (this.#extractors.some((e) => e === extractor)) return\n\n this.#extractors.push(extractor)\n }\n\n /**\n * Check if an extractor is already registered.\n * @param extractor - Extractor to check.\n */\n has(extractor: Extractor): boolean {\n return this.#extractors.includes(extractor)\n }\n\n /**\n * Remove an extractor.\n * @param extractor - Extractor to remove.\n * @returns True if the extractor was removed, false otherwise.\n */\n remove(extractor: Extractor): boolean {\n if (!this.has(extractor)) return false\n this.#extractors.splice(this.#extractors.indexOf(extractor), 1)\n return true\n }\n}\n","import { HookRegistry } from '@/features/hooks/HookRegistry.js'\r\nimport { HookService } from '@/features/hooks/HookService.js'\r\nimport { PluginRegistry } from '@/features/plugins/PluginRegistry.js'\r\nimport { PluginService } from '@/features/plugins/PluginService.js'\r\nimport { OMSSServerError } from '@/utils/error.js'\r\nimport type { OMSSConfig } from '@/types/config.js'\r\nimport { SourceService } from '@/features/source/SourceService.js'\r\nimport { ProviderRegistry } from '@/features/providers/ProviderRegistry.js'\r\nimport { ERR, OK } from '@/utils/utils.js'\r\nimport { Result } from '@/types/utils.js'\r\nimport { ProviderService } from '@/features/providers/ProviderService.js'\r\nimport { OMSSHooks } from '@/types/hooks.js'\r\nimport { ExtractorService } from '@/features/extractors/ExtractorService.js'\r\nimport { ExtractorRegistry } from '@/features/extractors/ExtractorRegistry.js'\r\n\r\n/**\r\n * Core server class for OMSS.\r\n */\r\nexport class OMSSServer {\r\n readonly hooks: HookService<OMSSHooks>\r\n readonly plugins: PluginService\r\n readonly providers: ProviderService\r\n readonly sources: SourceService\r\n readonly extractors: ExtractorService\r\n readonly #config: OMSSConfig\r\n\r\n /**\r\n * Creates a new OMSSServer instance.\r\n *\r\n * @param config - Immutable server configuration\r\n */\r\n constructor(config: OMSSConfig) {\r\n this.#config = config\r\n\r\n const hooksRegistry = new HookRegistry<OMSSHooks>()\r\n const extractorRegistry = new ExtractorRegistry()\r\n const pluginRegistry = new PluginRegistry(this)\r\n const providerRegistry = new ProviderRegistry()\r\n\r\n this.hooks = new HookService<OMSSHooks>(hooksRegistry)\r\n this.extractors = new ExtractorService(extractorRegistry, hooksRegistry)\r\n this.plugins = new PluginService(this, pluginRegistry, hooksRegistry)\r\n this.providers = new ProviderService(providerRegistry, hooksRegistry)\r\n this.sources = new SourceService(this, providerRegistry, hooksRegistry, this.extractors)\r\n }\r\n\r\n /**\r\n * Get the OMSS Config from the constructor\r\n * @returns the initialised OMSS Config\r\n */\r\n get config(): Readonly<OMSSConfig> {\r\n return this.#config\r\n }\r\n\r\n /**\r\n * Decorate the OMSSServer instance with a new property.\r\n * @param name - The name of the property to be decorated.\r\n * @param value - The value to be assigned to the property.\r\n * @param deps - An array of dependency names.\r\n * @returns The name of the decorated property in the {@link Result} object.\r\n */\r\n decorate<T>(name: string, value: T, deps: string[] = []): Result<string, OMSSServerError> {\r\n if (Object.hasOwn(this, name)) {\r\n return ERR(\r\n new OMSSServerError(`Decorator \"${name}\" already exists`, {\r\n cause: {\r\n existing: this[name as keyof this],\r\n },\r\n })\r\n )\r\n }\r\n\r\n for (const dep of deps) {\r\n if (!this.hasDecorator(dep)) {\r\n return ERR(new OMSSServerError(`\"${name}\" depends on \"${dep}\", which does not exist`))\r\n }\r\n }\r\n\r\n // modify the instance to make the decorator available as a property\r\n Object.defineProperty(this, name, {\r\n value,\r\n writable: false,\r\n configurable: false,\r\n enumerable: true,\r\n })\r\n\r\n return OK(name)\r\n }\r\n\r\n /**\r\n * Check if a decorator with the given name exists.\r\n * @param name - The name of the decorator to check.\r\n * @returns True if the decorator exists, false otherwise.\r\n */\r\n hasDecorator(name: string): boolean {\r\n return Object.hasOwn(this, name)\r\n }\r\n\r\n /**\r\n * Get a decorated property by its name.\r\n * @param name - The name of the property to retrieve.\r\n * @returns The decorated property value in the {@link Result} object.\r\n */\r\n getDecorator<T>(name: string): Result<T, OMSSServerError> {\r\n if (!Object.hasOwn(this, name)) {\r\n return ERR(new OMSSServerError(`Decorator \"${name}\" not found`))\r\n }\r\n\r\n return OK(this[name as keyof this] as T)\r\n }\r\n}\r\n\r\nexport default OMSSServer\r\n","import { BaseResolver } from '@/features/resolvers/BaseResolver.js'\r\nimport { OMSSProvider, ProviderResult, ProviderResultEmitter, ProviderSourcesMeta, ResolverMetadata } from '@/types/provider.js'\r\nimport type { ParsedOMSSId } from '@/types/resolver.js'\r\nimport { NonEmptyArray } from '@/types/utils.js'\r\n\r\n/**\r\n * Base class for all providers.\r\n */\r\nexport abstract class BaseProvider<P extends BaseResolver<unknown>> implements OMSSProvider<P> {\r\n /**\r\n * Provider ID. Must be unique.\r\n */\r\n abstract readonly id: string\r\n\r\n /**\r\n * Friendly name of the provider.\r\n */\r\n abstract readonly name: string\r\n\r\n /**\r\n * Whether the provider will be used.\r\n */\r\n abstract readonly enabled: boolean\r\n\r\n /**\r\n * Catalog of media this provider supports. It does not have to exist. If it does, it should be a list of media IDs.\r\n * This does not get queried for source resolving, but more metadata about the provider.\r\n */\r\n abstract readonly catalog?: () => Promise<NonEmptyArray<string>> | NonEmptyArray<string>\r\n\r\n /**\r\n * Provide a method that checks whether this provider supports a certain ID.\r\n * @param id - Parsed OMSS ID\r\n */\r\n abstract readonly supportsId: (id: ParsedOMSSId) => boolean | Promise<boolean>\r\n\r\n /**\r\n * Resolvers that this provider supports.\r\n */\r\n abstract readonly resolver: P\r\n\r\n /**\r\n * Fetch sources for a certain media.\r\n * @param media - Return object of the resolver's resolve() method.\r\n * @param result - The result emitter.\r\n */\r\n abstract getSources(media: ProviderSourcesMeta<ResolverMetadata<P>>, result: ProviderResultEmitter): Promise<ProviderResult>\r\n}\r\n","import { OMSSResolverError, Result } from '@/public-api.js'\nimport { OMSSId, OMSSResolver, ParsedOMSSId, ResolverExecutionContext, ResolverResult } from '@/types/resolver.js'\n\n/**\n * Base class for all OMSS resolvers.\n *\n * Resolvers convert an OMSS ID into usable metadata for providers.\n */\nexport abstract class BaseResolver<T> implements OMSSResolver<T> {\n /**\n * Namespace this resolver owns, e.g. \"tmdb\".\n * Must be unique for a single server instance.\n */\n abstract namespace: string\n\n /**\n * Human-readable resolver name.\n */\n abstract name: string\n\n /**\n * A map of ID converters.\n *\n * @key - The unhandled namespace\n * @value - A function that converts that id (from an unknown namespace/id provider) to this resolver's namespace.\n */\n abstract converter: Map<string, (noHandlerId: OMSSId, ctx: ResolverExecutionContext) => Promise<Result<OMSSId, OMSSResolverError>>>\n\n /**\n * Resolve a single ID into metadata.\n *\n * @param id - Parsed OMSS ID\n * @param ctx - Execution context.\n */\n abstract resolve(id: ParsedOMSSId, ctx: ResolverExecutionContext): Promise<ResolverResult<T>>\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["#hooks","#hookRegistry","#states","#server","#stack","#pluginRegistry","#hookRegistry","#insideBeforePluginRegister","#providerRegistry","#extractorService","#omssServer","#entries","#handlers","#hookRegistry","#core","#middleware","#inFlight","#cleaningFunction","#internalGetSources","#getInFlightKey","#providers","#providerRegistry","#hookRegistry","#middleware","#insideBeforeProviderRegister","#extractorRegistry","#hookRegistry","#insideBeforeRegisterExtractor","#extractors","#config"],"sources":["../src/features/hooks/HookRegistry.ts","../src/features/hooks/HookService.ts","../src/features/plugins/plugin-state.ts","../src/utils/error.ts","../src/utils/regexp.ts","../src/utils/utils.ts","../src/features/plugins/PluginRegistry.ts","../src/features/plugins/PluginService.ts","../src/features/resolvers/utils.ts","../src/features/providers/ProviderResultEmitter.ts","../src/features/source/SourceCore.ts","../src/utils/AsyncDeduper.ts","../src/utils/middleware.ts","../src/features/source/SourceService.ts","../src/features/providers/ProviderRegistry.ts","../src/features/providers/ProviderService.ts","../src/features/extractors/ExtractorService.ts","../src/features/extractors/ExtractorRegistry.ts","../src/core/server.ts","../src/features/providers/BaseProvider.ts","../src/features/resolvers/BaseResolver.ts"],"sourcesContent":["/**\n * Hook Registry\n *\n * Manages lifecycle hooks for OMSS events.\n */\nexport class HookRegistry<T> {\n /**\n * Map storing arrays of hook handlers for each hook name.\n */\n readonly #hooks = new Map<keyof T, unknown[]>()\n\n /**\n * Get all registered hooks immutable.\n * @dangerous - Be careful with this. what you are doing might cause side effects.\n */\n get hooks(): ReadonlyMap<keyof T, unknown[]> {\n return this.#hooks\n }\n\n /**\n * Clear all registered hooks.\n * @dangerous - Be careful with this. Might cause side effects.\n */\n reset(): void {\n return this.#hooks.clear()\n }\n\n /**\n * Run all hooks for a lifecycle event with the provided payload.\n *\n * @typeParam K - The name of the hook to run.\n * @param name - The hook name (key of THooks).\n * @param payload - The payload to pass to each hook handler.\n */\n async run<K extends keyof T>(name: K, payload: T[K] extends (payload: infer P) => unknown ? P : never): Promise<void> {\n const fns = this.#hooks.get(name) ?? []\n\n for (const fn of fns) {\n await (fn as (payload: unknown) => void | Promise<void>)(payload)\n }\n }\n\n /**\n * Add a hook handler for a lifecycle event.\n * @param name - The hook name (key of THooks).\n * @param cb - The hook handler function.\n */\n add<K extends keyof T>(name: K, cb: T[K]): void {\n const existing = this.#hooks.get(name) ?? []\n\n this.#hooks.set(name, [...existing, cb])\n }\n}\n","import { HookRegistry } from '@/features/hooks/HookRegistry.js'\n\nexport class HookService<T> {\n readonly #hookRegistry: HookRegistry<T>\n\n constructor(hookRegistry: HookRegistry<T> = new HookRegistry<T>()) {\n this.#hookRegistry = hookRegistry\n }\n\n /**\n * Get all registered hooks immutable. TO ADD HOOKS, USE THE ADD METHOD\n * @dangerous - Be careful with this. what you are doing might cause side effects.\n */\n get hooks(): ReadonlyMap<keyof T, unknown[]> {\n return this.#hookRegistry.hooks\n }\n\n /**\n * Register a hook for a lifecycle event.\n *\n * @typeParam K - The name of the hook to register.\n * @param name - The hook name (key of THooks).\n * @param cb - The handler function for this hook.\n */\n add<K extends keyof T>(name: K, cb: T[K]): ReturnType<HookRegistry<T>['add']> {\n this.#hookRegistry.add(name, cb)\n }\n\n /**\n * Clear all registered hooks.\n * @dangerous - Be careful with this. Might cause side effects.\n */\n reset(): ReturnType<HookRegistry<T>['reset']> {\n return this.#hookRegistry.reset()\n }\n\n /**\n * Get the hook registry.\n *\n * This is only exposed for internal purposes and should not be accessed in consumer projects.\n * @dangerous\n * @internal\n */\n __getRegistry(): HookRegistry<T> {\n return this.#hookRegistry\n }\n}\n","/**\n * The states of which a plugin can be.\n */\nexport enum PluginState {\n Registering,\n Registered,\n Unavailable,\n}\n","/**\n * Base class for all OMSS framework errors.\n * Use `instanceof OMSSError` to catch any framework error.\n * Always throw a specific subclass, never this directly.\n * Cause is not standardized and may change frequently.\n */\nexport class OMSSError extends Error {\n /**\n * Create a new OMSSError.\n * @param message - Error message\n * @param options - Additional options for the error\n */\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options)\n this.name = this.constructor.name\n }\n}\n\n/**\n * Returned when an error gets Returned in the OMSSServer class.\n *\n * @example\n * return ERR(OMSSServerError('config.name must be a non-empty string', {cause: config}))\n */\nexport class OMSSServerError extends OMSSError {}\n\n/**\n * Returned during plugin registration or execution.\n *\n * @example\n * return ERR(OMSSPluginError(`Plugin \"${name}\" is already registered`, { cause: plugin }))\n */\nexport class OMSSPluginError extends OMSSError {}\n\n/**\n * Returned during resolver registration or ID resolution.\n *\n * @example\n * return ERR(OMSSResolverError(`No resolver found for namespace \"xyz\"`, { cause: rawId }))\n */\nexport class OMSSResolverError extends OMSSError {}\n\n/**\n * Returned during provider registration or source fetching.\n *\n * @example\n * return ERR(OMSSProviderError('Provider must have at least one resolver', { cause: provider }))\n */\nexport class OMSSProviderError extends OMSSError {}\n\n/**\n * Returned when an extractor fails to extract the media from a host.\n *\n * @example\n * return ERR(OMSSExtractor('Failed to extract media from host due to host changes', { cause: html }))\n */\nexport class OMSSExtractorError extends OMSSError {}\n\n/**\n * Returned when something/several things fail during source gathering.\n *\n * @example\n * return ERR(OMSSSourceGatheringError('Failed to gather sources', { cause: providerResults }))\n */\nexport class OMSSSourceGatheringError extends OMSSError {}\n","/**\n * Regex for validating namespace names.\n */\nconst SAFE_UNIQUE_STRING_PATTERN = '[a-z0-9-]+'\nexport const SAFE_UNIQUE_STRING = new RegExp(`^${SAFE_UNIQUE_STRING_PATTERN}$`)\n\n/**\n * Regex for matching media formats by keyword or file extension.\n *\n * Matches:\n * - format keywords (`hls`, `dash`, `mp4`, etc.)\n * - common media file extensions\n * - extensions followed by URL query strings, fragments, paths, or the end of the URL\n */\nexport const HLS_REGEX = /\\bhls\\b|\\.(?:m3u8|ts)(?:$|[\\/?#])/i\nexport const MP4_REGEX = /\\bmp4\\b|\\.mp4(?:$|[\\/?#])/i\nexport const DASH_REGEX = /\\bdash\\b|\\.(?:mpd|m4a)(?:$|[\\/?#])/i\nexport const MKV_REGEX = /\\bmkv\\b|\\.mkv(?:$|[\\/?#])/i\nexport const VTT_REGEX = /\\bvtt\\b|\\.vtt(?:$|[\\/?#])/i\nexport const SRT_REGEX = /\\bsrt\\b|\\.srt(?:$|[\\/?#])/i\n\n/**\n * Regex for validating a single catalog entry value.\n *\n * A catalog entry must be either exactly `\"*\"` (wildcard — provider supports\n * all IDs in the namespace) or a safe unique string.\n *\n * Mixing `\"*\"` with other entries in the same catalog array is not allowed\n * and is validated separately at registration time.\n */\nexport const CATALOG_ENTRY = new RegExp(`^(?:\\\\*|${SAFE_UNIQUE_STRING_PATTERN})$`)\n","import { Result } from '@/types/utils.js'\nimport { OMSSError } from '@/utils/error.js'\nimport { SAFE_UNIQUE_STRING } from '@/utils/regexp.js'\n\n/**\n * Convenience factory for a successful result.\n */\nexport function OK(): Result<void, never>\nexport function OK<T>(value: T): Result<T, never>\n\nexport function OK(value?: unknown) {\n if (arguments.length === 0) {\n return { ok: true }\n }\n\n return { ok: true, value }\n}\n/**\n * Convenience factory for a failed result.\n */\nexport const ERR = <E extends Error>(error: E): Result<never, E> => ({ ok: false, error })\n\ntype ErrorConstructor<T extends OMSSError> = new (message: string, options?: { cause?: Error }) => T\n\n/**\n * Validate a string is safe for use as a unique identifier (only lowercase letters, numbers, and hyphens).\n * @param value - The string to validate\n * @param name - The name of the identifier, for error messages\n * @param ErrorType - The error type to return if validation fails\n */\nexport function validateSafeUniqueString<T extends OMSSError>(value: string, name: string, ErrorType: ErrorConstructor<T>): Result<void, T> {\n if (!SAFE_UNIQUE_STRING.test(value)) {\n return ERR(new ErrorType(`Invalid ${name} \"${value}\". Expected only letters (lowercase), numbers, and hyphens.`))\n }\n\n return OK()\n}\n","import type { OMSSConfiguredPluginType, OMSSPluginOptions, OMSSPluginType, UnknownPluginType } from '@/types/plugin.js'\nimport OMSSServer from '@/core/server.js'\nimport { PluginState } from '@/features/plugins/plugin-state.js'\nimport { OMSSPluginError } from '@/utils/error.js'\nimport { Result } from '@/types/utils.js'\nimport { ERR, OK } from '@/utils/utils.js'\n\n/**\n * Registry responsible for executing and managing OMSS Plugins.\n *\n * Plugins are executed when added.\n */\nexport class PluginRegistry {\n /**\n * States of plugin x\n */\n readonly #states = new Map<UnknownPluginType, PluginState>()\n readonly #server: OMSSServer\n\n /**\n * Registration stack used for circular dependency detection.\n */\n readonly #stack: UnknownPluginType[] = []\n\n constructor(server: OMSSServer) {\n this.#server = server\n }\n\n async add(plugin: OMSSPluginType): Promise<Result<PluginState.Registered, OMSSPluginError>>\n\n async add<T>(plugin: OMSSConfiguredPluginType<T>, options: OMSSPluginOptions<T>): Promise<Result<PluginState.Registered, OMSSPluginError>>\n\n /**\n * Adds and runs a plugin with its options.\n *\n * @typeParam T - Plugin options type.\n * @param plugin - The plugin function to register.\n * @param options - Plugin options or a factory function that resolves options.\n */\n async add(plugin: UnknownPluginType, options?: unknown): Promise<Result<PluginState.Registered, OMSSPluginError>> {\n // Check whether this plugin is already known\n const state = this.#states.get(plugin)\n\n if (state === PluginState.Registering) {\n const chain = [...this.#stack, plugin].map((p) => p.name).join(' -> ')\n\n return ERR(new OMSSPluginError(`Circular plugin dependency detected: ${chain}`))\n }\n\n if (state === PluginState.Registered) {\n return ERR(new OMSSPluginError(`Plugin \"${plugin.name}\" is already registered`))\n }\n\n // Start registering\n this.#states.set(plugin, PluginState.Registering)\n this.#stack.push(plugin)\n\n // Build options if a factory function is provided\n const resolved = typeof options === 'function' ? (options as (server: OMSSServer) => unknown)(this.#server) : options\n\n try {\n // Check if the plugin has a single argument\n if (plugin.length === 1) {\n // execute the plugin with the server instance\n await (plugin as OMSSPluginType)(this.#server)\n } else {\n // execute the plugin with the server instance and resolved options\n await plugin(this.#server, resolved)\n }\n\n this.#states.set(plugin, PluginState.Registered)\n return OK(PluginState.Registered)\n } catch (err) {\n this.#states.delete(plugin)\n return ERR(err instanceof OMSSPluginError ? err : new OMSSPluginError(String(err), { cause: err }))\n } finally {\n this.#stack.pop()\n }\n }\n\n /**\n * Get the current plugin state.\n */\n getState<T>(plugin: UnknownPluginType | OMSSPluginType | OMSSConfiguredPluginType<T>): PluginState {\n return this.#states.get(plugin as UnknownPluginType) ?? PluginState.Unavailable\n }\n}\n","import { PluginRegistry } from '@/features/plugins/PluginRegistry.js'\nimport { HookRegistry } from '@/features/hooks/HookRegistry.js'\nimport type { OMSSConfiguredPluginType, OMSSPluginOptions, OMSSPluginType, UnknownPluginType } from '@/types/plugin.js'\nimport OMSSServer from '@/core/server.js'\nimport { ERR } from '@/utils/utils.js'\nimport { OMSSPluginError } from '@/utils/error.js'\nimport type { OMSSHooks } from '@/types/hooks.js'\n\n/**\n * The public API for managing OMSS plugins.\n */\nexport class PluginService {\n readonly #pluginRegistry: PluginRegistry\n readonly #hookRegistry: HookRegistry<OMSSHooks>\n #insideBeforePluginRegister = false\n\n constructor(omssServer: OMSSServer, pluginRegistry: PluginRegistry, hookRegistry: HookRegistry<OMSSHooks>) {\n this.#pluginRegistry = pluginRegistry\n this.#hookRegistry = hookRegistry\n }\n\n /**\n * Register an OMSS plugin with no config into the system.\n * @param plugin - Plugin function\n */\n async register(plugin: OMSSPluginType): ReturnType<PluginRegistry['add']>\n\n /**\n * Register an OMSS plugin with a config into the system.\n * @param plugin - Plugin function\n * @param options - Plugin configuration\n */\n async register<T>(plugin: OMSSConfiguredPluginType<T>, options: OMSSPluginOptions<T>): ReturnType<PluginRegistry['add']>\n\n /**\n * Registers an OMSS plugin that can take a config into the system, but does not need to.\n * @param plugin - Plugin implementation\n * @param options - Plugin configuration\n */\n async register(plugin: UnknownPluginType, options?: unknown): ReturnType<PluginRegistry['add']> {\n if (this.#insideBeforePluginRegister) {\n return ERR(new OMSSPluginError('Plugins cannot be registered during beforePluginRegister'))\n }\n\n this.#insideBeforePluginRegister = true\n try {\n await this.#hookRegistry.run('beforePluginRegister', { plugin: plugin as UnknownPluginType, options })\n } finally {\n this.#insideBeforePluginRegister = false\n }\n\n const result = await this.#pluginRegistry.add(plugin, options)\n\n if (!result.ok) {\n await this.#hookRegistry.run('pluginRegisterFailed', { plugin, options, error: result.error })\n return result\n }\n\n await this.#hookRegistry.run('afterPluginRegister', { plugin, options })\n return result\n }\n\n /**\n * Get the current State of a plugin\n * @param plugin - the plugin to get the state from\n * @returns - a value of the PluginState enum\n */\n getPluginState(plugin: UnknownPluginType): ReturnType<PluginRegistry['getState']> {\n return this.#pluginRegistry.getState(plugin)\n }\n}\n","import type { OMSSId, ParsedOMSSId } from '@/types/resolver.js'\nimport { OMSSResolverError } from '@/utils/error.js'\nimport { Result } from '@/types/utils.js'\nimport { ERR, OK, validateSafeUniqueString } from '@/utils/utils.js'\n\n/**\n * Parses an OMSS ID in the form `namespace:value_1[:value_2[:...]]`.\n */\nexport function parseOMSSId(id: OMSSId): Result<ParsedOMSSId, OMSSResolverError> {\n if (/\\s/.test(id)) {\n return ERR(new OMSSResolverError(`Invalid OMSS ID \"${id}\": cannot contain whitespace`))\n }\n\n const parts = id.split(':')\n\n if (parts.length < 2) {\n return ERR(new OMSSResolverError(`Invalid OMSS ID \"${id}\": missing namespace separator \":\"`))\n }\n\n const [namespace, ...values] = parts\n\n if (!namespace) {\n return ERR(new OMSSResolverError(`Invalid OMSS ID \"${id}\": namespace cannot be empty`))\n }\n\n const req = validateSafeUniqueString(namespace, 'OMSS namespace', OMSSResolverError)\n\n if (!req.ok) {\n return ERR(req.error)\n }\n\n for (const [i, value] of values.entries()) {\n if (value.length === 0) {\n return ERR(new OMSSResolverError(`Invalid OMSS ID \"${id}\": value ${i + 1} cannot be empty`))\n }\n }\n\n const decodedValues = values.map((value) => decodeURIComponent(value))\n\n return OK({\n namespace,\n values: decodedValues,\n raw: id,\n })\n}\n","import {\n EmittedSource,\n EmittedSubtitle,\n OMSSProviderResult,\n ProviderResult,\n ProviderResultEmitter,\n Source,\n SourceQuality,\n SourceTypes,\n Subtitle,\n SubtitleFormat,\n UnknownProvider,\n} from '@/types/provider.js'\nimport { OMSSProviderError } from '@/utils/error.js'\nimport { Result } from '@/types/utils.js'\nimport { ERR, OK } from '@/utils/utils.js'\nimport { HookRegistry } from '@/features/hooks/HookRegistry.js'\nimport { ProviderHooks } from '@/types/hooks.js'\nimport { DASH_REGEX, HLS_REGEX, MKV_REGEX, MP4_REGEX, SRT_REGEX, VTT_REGEX } from '@/utils/regexp.js'\nimport { CleaningFunction } from '@/types/source.js'\nimport { ParsedOMSSId } from '@/types/resolver.js'\n\n/**\n * Creates a fresh `ProviderResultEmitter` instance scoped to a single\n * `getSources()` execution.\n *\n * A new emitter MUST be created per provider call. Emitters hold internal,\n * mutable state (accumulated sources/subtitles/errors) via closures, so\n * reusing a single emitter across concurrent provider executions would\n * cause data from one provider to leak into another's response.\n *\n * @param provider - The provider instance for which this emitter is being created.\n * @param hookReg - The hook registry instance for managing hooks.\n * @param cleaningFunc - A function to clean up source/subtitle URLs and headers.\n * @param id - The parsed OMSS ID of the current request.\n * @returns A new `ProviderResultEmitter` bound to this execution.\n *\n */\nexport function createProviderResultEmitter(provider: Readonly<UnknownProvider>, hookReg: HookRegistry<ProviderHooks>, cleaningFunc: CleaningFunction, id: ParsedOMSSId): ProviderResultEmitter {\n /**\n * Accumulated sources emitted via `source()` during this execution.\n * Flushed into the final result when `done()` is called.\n */\n const sources: Source[] = []\n\n /**\n * Accumulated subtitles emitted via `subtitle()` during this execution.\n * Subtitles are intentionally NOT linked to any source or audio track,\n * so they live in their own flat array regardless of how many sources\n * were emitted.\n */\n const subtitles: Subtitle[] = []\n\n /**\n * Accumulated NON-fatal errors emitted via `error()` during this execution.\n * These are returned alongside successful results (via `done()`) so\n * that partial failures (e.g. \"server 2 of 3 failed\") don't discard\n * otherwise-valid sources.\n */\n const errors: OMSSProviderError[] = []\n\n return {\n /**\n * Utilities to make your life easier\n */\n utils: {\n /**\n * Utilities for parsing metadata of sources\n */\n source: {\n /**\n * Parse a string into a possible source type.\n * @param possibleType - The string to parse\n */\n parseType(possibleType: string): SourceTypes {\n if (HLS_REGEX.test(possibleType)) {\n return 'hls'\n } else if (MP4_REGEX.test(possibleType)) {\n return 'mp4'\n } else if (DASH_REGEX.test(possibleType)) {\n return 'dash'\n } else if (MKV_REGEX.test(possibleType)) {\n return 'mkv'\n } else {\n // most commonly used is hls\n return 'hls'\n }\n },\n /**\n * Parse a string into a possible source quality.\n * @param possibleQuality - The string to parse\n */\n parseQuality(possibleQuality: string): SourceQuality {\n if (!possibleQuality) {\n return 'Auto'\n }\n\n const value = possibleQuality.toLowerCase().trim().replace(/_/g, ' ').replace(/-/g, ' ')\n\n // Direct quality labels / aliases\n if (/\\b(8k|uhd\\s*8k|4320p)\\b/.test(value)) {\n return '8K'\n }\n\n if (/\\b(4k|uhd|ultra\\s*hd|2160p)\\b/.test(value)) {\n return '4K'\n }\n\n if (/\\b(qhd|2k|1440p|2560x1440)\\b/.test(value)) {\n return 'QHD'\n }\n\n if (/\\b(fhd|full\\s*hd|1080p|1920x1080)\\b/.test(value)) {\n return 'FHD'\n }\n\n if (/\\b(hd|720p|1280x720)\\b/.test(value)) {\n return 'HD'\n }\n\n if (/\\b(sd|480p|576p|360p|240p)\\b/.test(value)) {\n return 'SD'\n }\n\n const resolutionMatch = value.match(/(\\d{3,4})(?:p|x\\d{3,4})?/)\n\n if (resolutionMatch) {\n const resolution = Number(resolutionMatch[1])\n\n if (resolution >= 4320) {\n return '8K'\n }\n\n if (resolution >= 2160) {\n return '4K'\n }\n\n if (resolution >= 1440) {\n return 'QHD'\n }\n\n if (resolution >= 1080) {\n return 'FHD'\n }\n\n if (resolution >= 720) {\n return 'HD'\n }\n\n if (resolution > 0) {\n return 'SD'\n }\n }\n\n const bitrateMatch = value.match(/(\\d+(?:\\.\\d+)?)\\s*(mbps|kbps)/)\n\n if (bitrateMatch) {\n const bitrate = Number(bitrateMatch[1])\n const unit = bitrateMatch[2]\n\n const mbps = unit === 'kbps' ? bitrate / 1000 : bitrate\n\n if (mbps >= 25) {\n return '4K'\n }\n\n if (mbps >= 8) {\n return 'FHD'\n }\n\n if (mbps >= 3) {\n return 'HD'\n }\n\n return 'SD'\n }\n\n return 'Auto'\n },\n },\n /**\n * Utilities for parsing metadata of subtitles\n */\n subtitle: {\n /**\n * Parse a string into a possible subtitle format.\n * @param possibleFormat - The string to parse\n */\n parseFormat(possibleFormat: string): SubtitleFormat {\n if (VTT_REGEX.test(possibleFormat)) {\n return 'vtt'\n } else if (SRT_REGEX.test(possibleFormat)) {\n return 'srt'\n } else {\n return 'vtt'\n }\n },\n },\n },\n\n /**\n * Emits a custom, provider-defined action/event.\n *\n * @param action - A custom event name (e.g. \"cache.hit\").\n * @param data - Arbitrary payload associated with the event.\n */\n emit(action: string, data: unknown): void {\n // action cannot be whitespace or another hook name\n if (/\\s/.test(action) || Object.keys(this).includes(action)) {\n return\n }\n hookReg.run(action, { data, provider, id, timestamp: new Date().toISOString() })\n },\n\n /**\n * Logs verbose debug information. Intended for development/troubleshooting\n * only and should be stripped or gated behind a debug flag in production.\n *\n * @param args - Values to log, forwarded as-is (same semantics as `console.debug`).\n */\n debug(...args: unknown[]): void {\n hookReg.run('debug', { provider, args, id, timestamp: new Date().toISOString() })\n },\n\n /**\n * Logs general informational messages about provider execution\n * (e.g. \"Fetched media\", \"Cache miss, fetching from upstream\").\n *\n * @param args - Values to log.\n */\n info(...args: unknown[]): void {\n hookReg.run('info', { provider, args, id, timestamp: new Date().toISOString() })\n },\n\n /**\n * Logs a non-fatal warning. Use this for degraded-but-recoverable\n * situations (e.g. \"missing quality metadata, defaulting to Auto\").\n *\n * @param args - Values to log.\n */\n warn(...args: unknown[]): void {\n hookReg.run('warn', { provider, args, id, timestamp: new Date().toISOString() })\n },\n\n /**\n * Records a NON-fatal error. The provider continues executing after\n * calling this — use `fatal()` instead if the provider cannot continue.\n *\n * The error is accumulated and returned to the requestor as part of\n * the `diagnostics`/`errors` field once `done()` is called, allowing\n * partial success (e.g. some sources found despite one upstream\n * server failing).\n *\n * @param error - The error to record. Will be surfaced to the client.\n */\n error(error: OMSSProviderError): void {\n errors.push(error)\n hookReg.run('error', { provider, error, id, timestamp: new Date().toISOString() })\n },\n\n /**\n * Emits a single resolved source.\n *\n * @param source - The fully-formed source object to emit.\n */\n source(source: EmittedSource): void {\n const cleanedSource = {\n ...source,\n }\n\n const cleaned = cleaningFunc({\n url: cleanedSource.url,\n header: cleanedSource.header,\n })\n\n cleanedSource.url = cleaned.url\n cleanedSource.header = cleaned.header\n\n if ('audioTracks' in cleanedSource && cleanedSource.audioTracks) {\n // had to split first and rest, since audioTracks requires least one track.\n const [first, ...rest] = cleanedSource.audioTracks\n\n cleanedSource.audioTracks = [\n {\n ...first,\n ...cleaningFunc({\n url: first.url,\n header: first.header,\n }),\n },\n ...rest.map((track) => ({\n ...track,\n ...cleaningFunc({\n url: track.url,\n header: track.header,\n }),\n })),\n ]\n }\n const fullSource: Source = {\n ...cleanedSource,\n provider: {\n id: provider.id,\n name: provider.name,\n },\n }\n\n sources.push(fullSource)\n\n hookReg.run('source', {\n provider,\n source: fullSource,\n id,\n timestamp: new Date().toISOString(),\n })\n },\n\n /**\n * Emits a single subtitle track.\n *\n * @param subtitle - The subtitle object to emit.\n */\n subtitle(subtitle: EmittedSubtitle): void {\n const obj = {\n url: subtitle.url,\n header: subtitle.header,\n }\n const { url, header } = cleaningFunc(obj)\n subtitle.url = url\n subtitle.header = header\n const fullSub = { ...subtitle, provider: { id: provider.id, name: provider.name } }\n subtitles.push(fullSub)\n hookReg.run('subtitle', { provider, subtitle: fullSub, id, timestamp: new Date().toISOString() })\n },\n\n /**\n * Immediately aborts provider execution with a fatal error.\n *\n * @param error - The fatal error describing why the provider could not proceed.\n * @returns An `ERR` result wrapping the given error.\n */\n fatal(error: OMSSProviderError): Result<never, OMSSProviderError> {\n const accumulatedError = new AggregateError([error, ...errors], error.message, { cause: error.cause })\n\n const finalErr = new OMSSProviderError(accumulatedError.message, { cause: accumulatedError })\n\n hookReg.run('error', { provider, error: finalErr, id, timestamp: new Date().toISOString() })\n\n return ERR(finalErr)\n },\n\n /**\n * Signals that the provider has finished emitting sources/subtitles\n * and finalizes the result.\n *\n * @returns An `OK` result containing all accumulated sources,\n * subtitles, and non-fatal errors for this execution.\n */\n done(): ProviderResult {\n const result: OMSSProviderResult = {\n sources,\n subtitles,\n errors,\n }\n\n hookReg.run('done', { provider, result, id, timestamp: new Date().toISOString() })\n\n return OK(result)\n },\n }\n}\n","import OMSSServer from '@/core/server.js'\nimport { ProviderRegistry } from '@/features/providers/ProviderRegistry.js'\nimport { parseOMSSId } from '@/features/resolvers/utils.js'\nimport type { ProviderResult, Source, Subtitle, UnknownProvider } from '@/types/provider.js'\nimport type { OMSSId, ResolverExecutionContext } from '@/types/resolver.js'\nimport { CleaningFunction, GatheredSources, GetSourcesOptions } from '@/types/source.js'\nimport type { Result } from '@/types/utils.js'\nimport { OMSSProviderError, OMSSSourceGatheringError } from '@/utils/error.js'\nimport { ERR, OK } from '@/utils/utils.js'\nimport { createProviderResultEmitter } from '@/features/providers/ProviderResultEmitter.js'\nimport { ProviderHooks } from '@/types/hooks.js'\nimport { ExtractorService } from '@/features/extractors/ExtractorService.js'\nimport { HookService } from '@/features/hooks/HookService.js'\n\n/**\n * Internal source gathering core.\n *\n * Handles OMSS ID parsing, provider lookup, resolver-level deduplication,\n * provider execution, and final result aggregation.\n *\n * This class intentionally does not know about OMSS Hooks, middleware, or in-flight\n * request sharing. Those concerns belong to SourceService.\n */\nexport class SourceCore {\n readonly #providerRegistry: ProviderRegistry\n readonly #extractorService: ExtractorService\n readonly #omssServer: OMSSServer\n\n constructor(omssServer: OMSSServer, providerRegistry: ProviderRegistry, extractorService: ExtractorService) {\n this.#omssServer = omssServer\n this.#providerRegistry = providerRegistry\n this.#extractorService = extractorService\n }\n\n /**\n * Gather sources for a single OMSS ID.\n *\n * @param omssId - Raw OMSS identifier.\n * @param opts - Optional source gathering parameters.\n * @param providerHookRegistry - Hook registry for provider hooks.\n * @param cleaningFunction - Optional custom function to clean url's and headers\n * @returns Aggregated provider results or a source gathering error.\n */\n async getSources(\n omssId: OMSSId,\n opts: GetSourcesOptions,\n providerHookRegistry: HookService<ProviderHooks>,\n cleaningFunction: CleaningFunction\n ): Promise<Result<GatheredSources, OMSSSourceGatheringError>> {\n // try to parse the OMSS ID\n const parsed = parseOMSSId(omssId)\n\n if (!parsed.ok) {\n return ERR(new OMSSSourceGatheringError(`Failed to parse OMSS id \"${omssId}\": ${parsed.error.message}`, { cause: parsed.error }))\n }\n\n // we know that the id is valid now. Now we got to find the providers that can handle that namespace. If a specific provider is requested, we only look for that one.\n const providers: UnknownProvider[] = opts.providerId\n ? this.#providerRegistry.getAll((p) => p.id === opts.providerId && p.resolver.namespace === parsed.value.namespace)\n : this.#providerRegistry.getAll((p) => p.resolver.namespace === parsed.value.namespace)\n\n // if no provider can handle that namespace, return an error\n if (providers.length === 0) {\n return ERR(new OMSSSourceGatheringError(`No providers found for namespace \"${parsed.value.namespace}\"` + (opts.providerId ? ` and provider \"${opts.providerId}\"` : '')))\n }\n\n // if no abortsignal comes, just create a new one (does not abort)\n const signal = opts.abortSignal ?? new AbortController().signal\n\n // create the resolver context\n const ctx: ResolverExecutionContext = {\n server: this.#omssServer,\n signal,\n }\n\n /**\n * Resolver-level deduplication: multiple providers that share the same\n * resolver run that resolver only once, then share the result.\n */\n const resolverCache = new Map<string, Promise<Result<unknown, OMSSSourceGatheringError>>>()\n\n /**\n * Get resolver metadata for a provider, reusing the same resolver\n * promise when multiple providers share the same resolver.\n *\n * @param provider - Provider whose resolver metadata should be loaded.\n * @returns Resolver metadata or a source gathering error.\n */\n const getResolvedMeta = (provider: UnknownProvider): Promise<Result<unknown, OMSSSourceGatheringError>> => {\n const resolverKey = `${provider.resolver.namespace}:${provider.resolver.name}`\n\n let promise = resolverCache.get(resolverKey)\n\n if (!promise) {\n promise = (async (): Promise<Result<unknown, OMSSSourceGatheringError>> => {\n if (signal.aborted) {\n return ERR(new OMSSSourceGatheringError('Operation aborted'))\n }\n\n const result = await provider.resolver.resolve(parsed.value, ctx)\n\n if (!result.ok) {\n return ERR(new OMSSSourceGatheringError(`Resolver failed for ${resolverKey}: ${result.error.message}`, { cause: result.error }))\n }\n\n return OK(result.value)\n })()\n\n resolverCache.set(resolverKey, promise)\n }\n\n return promise\n }\n\n /**\n * Resolve sources for a single provider.\n *\n * @param provider - Provider to execute.\n * @returns Provider result or a source gathering error.\n */\n const resolveForProvider = async (provider: UnknownProvider): Promise<ProviderResult | Result<never, OMSSSourceGatheringError>> => {\n if (signal.aborted) {\n return ERR(new OMSSSourceGatheringError('Operation aborted'))\n }\n\n const supportsId = await provider.supportsId(parsed.value)\n if (!supportsId) {\n return OK({ sources: [], subtitles: [], errors: [new OMSSProviderError(`Provider \"${provider.id}\" did not support this id: \"${parsed.value.raw}\"`)] })\n }\n\n if (signal.aborted) {\n return ERR(new OMSSSourceGatheringError('Operation aborted'))\n }\n\n const metaResult = await getResolvedMeta(provider)\n\n if (!metaResult.ok) {\n return metaResult\n }\n\n if (signal.aborted) {\n return ERR(new OMSSSourceGatheringError('Operation aborted'))\n }\n\n const resultEmitter = createProviderResultEmitter(provider, providerHookRegistry.__getRegistry(), cleaningFunction, parsed.value)\n\n return provider.getSources(\n {\n utils: {\n omssId: parsed.value,\n abortSignal: signal,\n findExtractor: this.#extractorService.find,\n },\n meta: metaResult.value,\n },\n resultEmitter\n )\n }\n\n const settled = await Promise.allSettled(providers.map((provider) => resolveForProvider(provider)))\n\n if (signal.aborted) {\n return ERR(new OMSSSourceGatheringError('Operation aborted'))\n }\n\n const allSources: Source[] = []\n const allSubtitles: Subtitle[] = []\n const unexpectedErrors: unknown[] = []\n const omssErrors: Array<Extract<ProviderResult, { ok: false }>['error'] | OMSSSourceGatheringError> = []\n let hasSuccess = false\n\n for (const item of settled) {\n if (item.status === 'rejected') {\n omssErrors.push(\n new OMSSSourceGatheringError(`Provider execution failed: ${item.reason instanceof Error ? item.reason.message : String(item.reason)}`, {\n cause: item.reason instanceof Error ? item.reason : undefined,\n })\n )\n unexpectedErrors.push(item.reason)\n continue\n }\n\n const res = item.value\n\n if (res.ok) {\n hasSuccess = true\n allSources.push(...res.value.sources)\n allSubtitles.push(...res.value.subtitles)\n omssErrors.push(...res.value.errors)\n continue\n }\n\n omssErrors.push(res.error)\n }\n\n if (!hasSuccess) {\n return ERR(\n new OMSSSourceGatheringError(`All providers failed for namespace \"${parsed.value.namespace}\" and id: \"${parsed.value.raw}\"`, {\n cause: new AggregateError([...omssErrors, ...unexpectedErrors], 'Multiple failures detected'),\n })\n )\n }\n\n return OK({\n sources: allSources,\n subtitles: allSubtitles,\n errors: omssErrors,\n })\n }\n}\n","/**\n * Generic helper for deduplicating concurrent asynchronous work by key.\n *\n * When the same key is requested multiple times while a request is still in\n * flight, all callers receive the same Promise. Once the Promise settles, the\n * key is removed automatically so the next request starts fresh.\n *\n * @typeParam TKey - Cache key type.\n * @typeParam TValue - Promise resolution type.\n */\nexport class AsyncDeduper<TKey, TValue> {\n readonly #entries = new Map<TKey, Promise<TValue>>()\n\n /**\n * Returns the current in-flight Promise for a key, if one exists.\n *\n * @param key - In-flight request key.\n * @returns Existing Promise or undefined.\n */\n get(key: TKey): Promise<TValue> | undefined {\n return this.#entries.get(key)\n }\n\n /**\n * Check whether a key currently has an in-flight Promise.\n *\n * @param key - In-flight request key.\n * @returns True if the key is currently in flight.\n */\n has(key: TKey): boolean {\n return this.#entries.has(key)\n }\n\n /**\n * Delete a key manually.\n *\n * @param key - In-flight request key.\n * @returns True if an entry existed and was removed.\n */\n delete(key: TKey): boolean {\n return this.#entries.delete(key)\n }\n\n /**\n * Clear all tracked in-flight requests.\n */\n clear(): void {\n this.#entries.clear()\n }\n\n /**\n * Run a Promise factory for a key, reusing an existing in-flight Promise\n * when available.\n *\n * @param key - In-flight request key.\n * @param factory - Factory that creates the Promise when no request exists yet.\n * @returns Shared or newly created Promise.\n */\n run(key: TKey, factory: () => Promise<TValue>): Promise<TValue> {\n const existing = this.#entries.get(key)\n\n if (existing) {\n return existing\n }\n\n const promise = factory().finally(() => {\n this.#entries.delete(key)\n })\n\n this.#entries.set(key, promise)\n\n return promise\n }\n}\n","import { MiddlewareHandler, MiddlewareOperationMap } from '@/types/middleware.js'\n\n/**\n * Reusable typed middleware runner.\n */\nexport class MiddlewareRunner<TOperations extends MiddlewareOperationMap> {\n readonly #handlers: Partial<{\n [K in keyof TOperations]: MiddlewareHandler<TOperations, K>[]\n }> = {}\n\n /**\n * Register middleware for a specific operation.\n *\n * @param method - Operation name.\n * @param handler - Middleware handler.\n */\n use<TMethod extends keyof TOperations>(method: TMethod, handler: MiddlewareHandler<TOperations, TMethod>): void {\n const list = (this.#handlers[method] ??= []) as MiddlewareHandler<TOperations, TMethod>[]\n\n list.push(handler)\n }\n\n /**\n * Run middleware chain for a specific operation.\n *\n * @param method - Operation name.\n * @param context - Operation context payload.\n * @param finalHandler - Final function to execute after middleware.\n * @returns The operation result.\n */\n run<TMethod extends keyof TOperations>(\n method: TMethod,\n context: TOperations[TMethod]['context'],\n finalHandler: () => Promise<TOperations[TMethod]['result']>\n ): Promise<TOperations[TMethod]['result']> {\n const handlers = (this.#handlers[method] ?? []) as readonly MiddlewareHandler<TOperations, TMethod>[]\n\n let index = -1\n\n const dispatch = (position: number): Promise<TOperations[TMethod]['result']> => {\n if (position <= index) {\n return Promise.reject(new Error('next() called multiple times'))\n }\n\n index = position\n\n const handler = handlers[position]\n\n if (!handler) {\n return finalHandler()\n }\n\n return handler(context, () => dispatch(position + 1))\n }\n\n return dispatch(0)\n }\n}\n","import OMSSServer from '@/core/server.js'\nimport { HookRegistry } from '@/features/hooks/HookRegistry.js'\nimport { ProviderRegistry } from '@/features/providers/ProviderRegistry.js'\nimport { SourceCore } from '@/features/source/SourceCore.js'\nimport type { CleaningFunction, GatheredSources, GetSourcesOptions, SourceServiceMiddleware, SourceServiceOperations } from '@/types/source.js'\nimport type { OMSSId } from '@/types/resolver.js'\nimport type { Result } from '@/types/utils.js'\nimport { OMSSSourceGatheringError } from '@/utils/error.js'\nimport { AsyncDeduper } from '@/utils/AsyncDeduper.js'\nimport { MiddlewareRunner } from '@/utils/middleware.js'\nimport type { OMSSHooks, ProviderHooks } from '@/types/hooks.js'\nimport { ExtractorService } from '@/features/extractors/ExtractorService.js'\nimport { HookService } from '@/features/hooks/HookService.js'\n\n/**\n * Public API for resolving sources for media.\n *\n * This service owns the public method surface, middleware execution,\n * lifecycle hook dispatching, and request coalescing. The actual source\n * gathering implementation lives in {@link SourceCore}.\n */\nexport class SourceService {\n readonly #hookRegistry: HookRegistry<OMSSHooks>\n readonly #core: SourceCore\n\n /**\n * Middleware runner for SourceService operations.\n */\n readonly #middleware = new MiddlewareRunner<SourceServiceOperations>()\n\n /**\n * Deduplicates concurrent getSources requests by request key.\n */\n readonly #inFlight = new AsyncDeduper<string, Result<GatheredSources, OMSSSourceGatheringError>>()\n\n constructor(omssServer: OMSSServer, providerRegistry: ProviderRegistry, hookRegistry: HookRegistry<OMSSHooks>, extractorService: ExtractorService) {\n this.#hookRegistry = hookRegistry\n this.#core = new SourceCore(omssServer, providerRegistry, extractorService)\n }\n\n public get cleaningFunction(): CleaningFunction {\n return this.#cleaningFunction\n }\n\n public set cleaningFunction(fn: CleaningFunction) {\n this.#cleaningFunction = fn\n }\n\n /**\n * Register middleware for a SourceService method.\n *\n * Middleware can be used for cross-cutting concerns such as caching,\n * logging, tracing, or metrics.\n *\n * @param method - Middleware-enabled method name.\n * @param handler - Middleware handler.\n */\n use<TMethod extends keyof SourceServiceOperations>(method: TMethod, handler: SourceServiceMiddleware<TMethod>): void {\n this.#middleware.use(method, handler)\n }\n\n /**\n * Fetch sources from all matching providers for an OMSS ID.\n *\n * This method is middleware-enabled. Concurrent requests for the same\n * `omssId` and `providerId` share the same in-flight Promise until the\n * request settles.\n *\n * @param omssId - OMSS identifier such as `\"tmdb:12345\"`.\n * @param options - Optional source gathering parameters.\n * @returns Aggregated provider results or a source gathering error.\n */\n async getSources(omssId: OMSSId, options: GetSourcesOptions = {}): Promise<Result<GatheredSources, OMSSSourceGatheringError>> {\n return this.#middleware.run('getSources', { omssId, options }, () => this.#internalGetSources(omssId, options))\n }\n\n /**\n * Get and set the cleaning function for the source core.\n */\n #cleaningFunction: CleaningFunction = (obj) => obj\n\n /**\n * Internal wrapper around source gathering.\n *\n * Runs lifecycle hooks and deduplicates concurrent requests before\n * delegating to {@link SourceCore}.\n *\n * @param omssId - OMSS identifier.\n * @param options - Optional source gathering parameters.\n * @returns Aggregated provider results or a source gathering error.\n */\n async #internalGetSources(omssId: OMSSId, options: GetSourcesOptions): Promise<Result<GatheredSources, OMSSSourceGatheringError>> {\n await this.#hookRegistry.run('beforeGetSources', {\n omssId,\n providerId: options.providerId,\n })\n\n const inFlightKey = this.#getInFlightKey(omssId, options.providerId)\n\n const result = await this.#inFlight.run(inFlightKey, () =>\n this.#core.getSources(omssId, options, options.providerHookService ?? new HookService<ProviderHooks>(), options.cleaningFunction ?? this.cleaningFunction)\n )\n\n if (result.ok) {\n await this.#hookRegistry.run('afterGetSources', {\n omssId,\n providerId: options.providerId,\n result: result.value,\n })\n\n return this.#middleware.run('afterGetSources', { omssId, options, result }, () => Promise.resolve(result))\n }\n\n await this.#hookRegistry.run('getSourcesFailed', {\n omssId,\n providerId: options.providerId,\n error: result.error,\n })\n\n return result\n }\n\n /**\n * Build the stable in-flight key for a getSources request.\n *\n * @param omssId - OMSS identifier.\n * @param providerId - Optional provider filter.\n * @returns Unique in-flight request key.\n */\n #getInFlightKey(omssId: OMSSId, providerId?: string): string {\n return `${omssId}|${providerId ?? ''}`\n }\n}\n","import type { UnknownProvider } from '@/types/provider.js'\nimport { OMSSProviderError } from '@/utils/error.js'\nimport { ERR, OK, validateSafeUniqueString } from '@/utils/utils.js'\nimport { Result } from '@/types/utils.js'\nimport { CATALOG_ENTRY } from '@/utils/regexp.js'\n\n/**\n * Registry responsible for storing and managing OMSS Providers.\n *\n * Providers are stored by their unique {@link UnknownProvider.id}.\n * This registry does not know about hooks.\n */\nexport class ProviderRegistry {\n /**\n * Internal map of registered providers, keyed by provider ID.\n */\n readonly #providers = new Map<string, UnknownProvider>()\n\n /**\n * Adds a provider to the registry.\n *\n * @param provider - The provider instance to register.\n * @returns `OK` if registration succeeded, `ERR` if the provider ID is already taken.\n */\n async add(provider: UnknownProvider): Promise<Result<UnknownProvider, OMSSProviderError>> {\n if (this.#providers.has(provider.id)) {\n return ERR(new OMSSProviderError(`Provider \"${provider.id}\" is already registered`))\n }\n\n const valProvIdReq = validateSafeUniqueString(provider.id, 'provider ID', OMSSProviderError)\n if (!valProvIdReq.ok) {\n return ERR(valProvIdReq.error)\n }\n\n const valResolverNamespaceReq = validateSafeUniqueString(provider.resolver.namespace, 'resolver namespace for resolver with name: \"' + provider.resolver.name + '\" and id:', OMSSProviderError)\n if (!valResolverNamespaceReq.ok) {\n return ERR(valResolverNamespaceReq.error)\n }\n\n if (this.getAll().some((p) => p.resolver.namespace === provider.resolver.namespace && p.resolver !== provider.resolver)) {\n return ERR(new OMSSProviderError(`Resolver namespace \"${provider.resolver.namespace}\" is already registered by another provider and another resolver. Use one resolver per namespace.`))\n }\n\n if (provider.catalog) {\n const entries = await provider.catalog()\n const hasWildcard = entries.includes('*')\n const hasOther = entries.some((id) => id !== '*')\n\n if (hasWildcard && hasOther) {\n return ERR(new OMSSProviderError(`Provider \"${provider.id}\" catalog contains \"*\" mixed with other IDs. Use either a single \"*\" or a list of valid IDs.`))\n }\n\n for (const id of entries) {\n if (!CATALOG_ENTRY.test(id)) {\n return ERR(new OMSSProviderError(`Provider \"${provider.id}\" catalog contains an invalid entry \"${id}\". Entries must be non-empty strings with no whitespace, or \"*\".`))\n }\n }\n }\n\n this.#providers.set(provider.id, provider)\n return OK(provider)\n }\n\n /**\n * Retrieves a registered provider by its ID.\n *\n * @param id - The unique provider ID.\n * @returns The provider instance, or `undefined` if not found.\n */\n get(id: string): UnknownProvider | undefined {\n return this.#providers.get(id)\n }\n\n /**\n * Returns all registered providers.\n */\n getAll(filter?: (p: UnknownProvider) => boolean): UnknownProvider[] {\n if (filter) {\n return [...this.#providers.values()].filter(filter)\n }\n return [...this.#providers.values()]\n }\n\n /**\n * Returns whether a provider with the given ID is registered.\n *\n * @param id - The unique provider ID.\n */\n has(id: string): boolean {\n return this.#providers.has(id)\n }\n}\n","import type { ProviderServiceMiddleware, ProviderServiceOperations, UnknownProvider } from '@/types/provider.js'\nimport { OMSSProviderError } from '@/utils/error.js'\nimport { ERR, OK } from '@/utils/utils.js'\nimport { ProviderRegistry } from '@/features/providers/ProviderRegistry.js'\nimport { HookRegistry } from '@/features/hooks/HookRegistry.js'\nimport type { Result } from '@/types/utils.js'\nimport type { OMSSHooks } from '@/types/hooks.js'\nimport { MiddlewareRunner } from '@/utils/middleware.js'\n\n/**\n * The public API for managing OMSS Providers.\n */\nexport class ProviderService {\n readonly #providerRegistry: ProviderRegistry\n readonly #hookRegistry: HookRegistry<OMSSHooks>\n readonly #middleware = new MiddlewareRunner<ProviderServiceOperations>()\n #insideBeforeProviderRegister = false\n\n constructor(providerRegistry: ProviderRegistry, hookRegistry: HookRegistry<OMSSHooks>) {\n this.#providerRegistry = providerRegistry\n this.#hookRegistry = hookRegistry\n }\n\n /**\n * Adds middleware to the `register` pipeline.\n * Middlewares run in insertion order, after hooks and before the\n * actual registry `add()` call.\n */\n use<TMethod extends keyof ProviderServiceOperations>(method: TMethod, handler: ProviderServiceMiddleware<TMethod>): this {\n this.#middleware.use(method, handler)\n return this\n }\n\n /**\n * Registers a provider into the system.\n */\n async register(provider: UnknownProvider): Promise<Result<UnknownProvider, OMSSProviderError>> {\n if (this.#insideBeforeProviderRegister) {\n return ERR(new OMSSProviderError('Providers cannot be registered during beforeProviderRegister'))\n }\n\n this.#insideBeforeProviderRegister = true\n try {\n await this.#hookRegistry.run('beforeProviderRegister', { provider })\n } finally {\n this.#insideBeforeProviderRegister = false\n }\n\n const result = await this.#middleware.run('register', { provider }, () => this.#providerRegistry.add(provider))\n\n if (!result.ok) {\n await this.#hookRegistry.run('providerRegisterFailed', {\n provider,\n error: result.error,\n })\n return ERR(result.error)\n }\n\n await this.#hookRegistry.run('afterProviderRegister', { provider })\n return result\n }\n\n /**\n * Retrieves a registered provider by its ID.\n *\n * @param id - The provider ID to look up.\n * @returns The provider instance, or `undefined` if not found.\n */\n get(id: string): ReturnType<ProviderRegistry['get']> {\n return this.#providerRegistry.get(id)\n }\n\n /**\n * Returns all registered providers.\n * @param filter - Optional filter function to apply to providers.\n */\n getAll(filter?: (p: UnknownProvider) => boolean): ReturnType<ProviderRegistry['getAll']> {\n return this.#providerRegistry.getAll(filter)\n }\n\n /**\n * Returns whether a provider with the given ID has been registered.\n *\n * @param id - The provider ID to check.\n */\n has(id: string): ReturnType<ProviderRegistry['has']> {\n return this.#providerRegistry.has(id)\n }\n\n /**\n * Returns a map of all namespaces and an array of all known identifiers for each namespace.\n * This list is BEST EFFORT ONLY. Do not rely on this. If any provider returns a `*` automatically, the namespace will support all identifiers (e.g. `\"tmdb\": [\"*\"], \"imdb\": [\"tt37636\", \"...\"]`.\n */\n async catalog(): Promise<Result<Map<string, string[]>, OMSSProviderError>> {\n const allProviders = this.getAll()\n const result = new Map<string, string[]>()\n\n await Promise.all(\n allProviders.map(async (provider) => {\n if (!provider.catalog) return\n\n const namespace = provider.resolver.namespace\n const entries = await provider.catalog()\n\n // If namespace already collapsed to wildcard, skip.\n if (result.get(namespace)?.[0] === '*') return\n\n if (entries.includes('*')) {\n // Any single wildcard provider collapses the whole namespace.\n result.set(namespace, ['*'])\n return\n }\n\n // Merge deduplicated IDs into the namespace bucket.\n const existing = result.get(namespace) ?? []\n const merged = Array.from(new Set([...existing, ...entries]))\n result.set(namespace, merged)\n })\n )\n\n return OK(result)\n }\n\n /**\n * Returns the catalog for a single namespace.\n * Returns `undefined` if no provider in that namespace exposes a catalog.\n *\n * @param namespace - The resolver namespace to look up (e.g. `\"tmdb\"`).\n * @returns Merged list of IDs for the namespace, `[\"*\"]` if any provider\n * signals wildcard support, or `undefined` if no catalog data exists.\n */\n async catalogForNamespace(namespace: string): Promise<Result<string[], OMSSProviderError>> {\n const providers = this.getAll((p) => p.resolver.namespace === namespace)\n if (providers.length === 0) return ERR(new OMSSProviderError(`No providers registered for namespace \"${namespace}\"`))\n\n const result: string[] = []\n\n for (const provider of providers) {\n if (!provider.catalog) continue\n\n const entries = await provider.catalog()\n\n if (entries.includes('*')) {\n return OK(['*'])\n }\n\n for (const id of entries) {\n if (!result.includes(id)) {\n result.push(id)\n }\n }\n }\n\n return result.length > 0 ? OK(result) : ERR(new OMSSProviderError(`No catalog data available for namespace \"${namespace}\"`))\n }\n}\n","import { ExtractorRegistry } from '@/features/extractors/ExtractorRegistry.js'\nimport { HookRegistry } from '@/features/hooks/HookRegistry.js'\nimport type { OMSSHooks } from '@/types/hooks.js'\nimport { Extractor } from '@/types/extractor.js'\nimport { Result } from '@/types/utils.js'\nimport { ERR, OK } from '@/utils/utils.js'\nimport { OMSSExtractorError } from '@/utils/error.js'\n\nexport class ExtractorService {\n readonly #extractorRegistry: ExtractorRegistry\n readonly #hookRegistry: HookRegistry<OMSSHooks>\n #insideBeforeRegisterExtractor = false\n\n constructor(extractorRegistry: ExtractorRegistry, hookRegistry: HookRegistry<OMSSHooks>) {\n this.#extractorRegistry = extractorRegistry\n this.#hookRegistry = hookRegistry\n }\n\n /**\n * Get all registered extractors (read-only).\n */\n get extractors(): Result<ReadonlyArray<Extractor>, Error> {\n return OK(this.#extractorRegistry.extractors)\n }\n\n /**\n * Find an extractor capable of handling the given URL.\n *\n * @param url - URL to search for.\n * @returns The first matching {@link Extractor} or an {@link OMSSExtractorError}.\n */\n async find(url: string): Promise<Result<Extractor, OMSSExtractorError>> {\n await this.#hookRegistry.run('beforeFindExtractor', { url })\n\n const extractors = this.#extractorRegistry.extractors\n\n const results = await Promise.all(extractors.map((extractor) => extractor.matcher(url)))\n\n for (let i = 0; i < extractors.length; i++) {\n if (results[i]!.ok) {\n const extractor = extractors[i]!\n\n await this.#hookRegistry.run('afterFindExtractor', {\n url,\n extractor,\n })\n\n return OK(extractor)\n }\n }\n\n const error = new OMSSExtractorError(`No extractor found for URL \"${url}\"`)\n\n await this.#hookRegistry.run('findExtractorFailed', {\n url,\n error,\n })\n\n return ERR(error)\n }\n\n /**\n * Register an extractor.\n *\n * @param extractor - Extractor to register.\n */\n async register(extractor: Extractor): Promise<Result<void, Error>> {\n if (this.#insideBeforeRegisterExtractor) {\n return ERR(new OMSSExtractorError('Extractors cannot be registered during beforeRegisterExtractor'))\n }\n\n this.#insideBeforeRegisterExtractor = true\n\n try {\n await this.#hookRegistry.run('beforeRegisterExtractor', {\n extractor,\n })\n } finally {\n this.#insideBeforeRegisterExtractor = false\n }\n\n try {\n this.#extractorRegistry.add(extractor)\n } catch (error) {\n const extractorError = error instanceof OMSSExtractorError ? error : new OMSSExtractorError(error instanceof Error ? error.message : String(error))\n\n await this.#hookRegistry.run('extractorRegisterFailed', {\n extractor,\n error: extractorError,\n })\n\n return ERR(extractorError)\n }\n\n await this.#hookRegistry.run('afterRegisterExtractor', {\n extractor,\n })\n\n return OK()\n }\n\n /**\n * Remove every registered extractor.\n */\n reset(): Result<void, Error> {\n this.#extractorRegistry.reset()\n return OK()\n }\n\n /**\n * Determine whether an extractor has already been registered.\n *\n * @param extractor - Extractor to check.\n */\n has(extractor: Extractor): Result<boolean, Error> {\n return OK(this.#extractorRegistry.has(extractor))\n }\n\n /**\n * Remove an extractor.\n *\n * @param extractor - Extractor to remove.\n * @returns Whether the extractor was removed.\n */\n remove(extractor: Extractor): Result<boolean, Error> {\n return OK(this.#extractorRegistry.remove(extractor))\n }\n}\n","import { Extractor } from '@/types/extractor.js'\n\n/**\n * Extractor Registry\n *\n * Manages all extractors.\n */\nexport class ExtractorRegistry {\n /**\n * Array of extractors.\n */\n readonly #extractors = Array<Extractor>()\n\n /**\n * Get all extractors.\n */\n get extractors(): Readonly<Extractor[]> {\n return this.#extractors\n }\n\n /**\n * Clear all extractors.\n */\n reset(): void {\n this.#extractors.length = 0\n }\n\n /**\n * Add an extractor.\n * @param extractor - Extractor to add.\n */\n add(extractor: Extractor): void {\n if (this.#extractors.some((e) => e === extractor)) return\n\n this.#extractors.push(extractor)\n }\n\n /**\n * Check if an extractor is already registered.\n * @param extractor - Extractor to check.\n */\n has(extractor: Extractor): boolean {\n return this.#extractors.includes(extractor)\n }\n\n /**\n * Remove an extractor.\n * @param extractor - Extractor to remove.\n * @returns True if the extractor was removed, false otherwise.\n */\n remove(extractor: Extractor): boolean {\n if (!this.has(extractor)) return false\n this.#extractors.splice(this.#extractors.indexOf(extractor), 1)\n return true\n }\n}\n","import { HookRegistry } from '@/features/hooks/HookRegistry.js'\nimport { HookService } from '@/features/hooks/HookService.js'\nimport { PluginRegistry } from '@/features/plugins/PluginRegistry.js'\nimport { PluginService } from '@/features/plugins/PluginService.js'\nimport { OMSSServerError } from '@/utils/error.js'\nimport type { OMSSConfig } from '@/types/config.js'\nimport { SourceService } from '@/features/source/SourceService.js'\nimport { ProviderRegistry } from '@/features/providers/ProviderRegistry.js'\nimport { ERR, OK } from '@/utils/utils.js'\nimport { Result } from '@/types/utils.js'\nimport { ProviderService } from '@/features/providers/ProviderService.js'\nimport { OMSSHooks } from '@/types/hooks.js'\nimport { ExtractorService } from '@/features/extractors/ExtractorService.js'\nimport { ExtractorRegistry } from '@/features/extractors/ExtractorRegistry.js'\n\n/**\n * Core server class for OMSS.\n */\nexport class OMSSServer {\n readonly hooks: HookService<OMSSHooks>\n readonly plugins: PluginService\n readonly providers: ProviderService\n readonly sources: SourceService\n readonly extractors: ExtractorService\n readonly #config: OMSSConfig\n\n /**\n * Creates a new OMSSServer instance.\n *\n * @param config - Immutable server configuration\n */\n constructor(config: OMSSConfig) {\n this.#config = config\n\n const hooksRegistry = new HookRegistry<OMSSHooks>()\n const extractorRegistry = new ExtractorRegistry()\n const pluginRegistry = new PluginRegistry(this)\n const providerRegistry = new ProviderRegistry()\n\n this.hooks = new HookService<OMSSHooks>(hooksRegistry)\n this.extractors = new ExtractorService(extractorRegistry, hooksRegistry)\n this.plugins = new PluginService(this, pluginRegistry, hooksRegistry)\n this.providers = new ProviderService(providerRegistry, hooksRegistry)\n this.sources = new SourceService(this, providerRegistry, hooksRegistry, this.extractors)\n }\n\n /**\n * Get the OMSS Config from the constructor\n * @returns the initialised OMSS Config\n */\n get config(): Readonly<OMSSConfig> {\n return this.#config\n }\n\n /**\n * Decorate the OMSSServer instance with a new property.\n * @param name - The name of the property to be decorated.\n * @param value - The value to be assigned to the property.\n * @param deps - An array of dependency names.\n * @returns The name of the decorated property in the {@link Result} object.\n */\n decorate<T>(name: string, value: T, deps: string[] = []): Result<string, OMSSServerError> {\n if (Object.hasOwn(this, name)) {\n return ERR(\n new OMSSServerError(`Decorator \"${name}\" already exists`, {\n cause: {\n existing: this[name as keyof this],\n },\n })\n )\n }\n\n for (const dep of deps) {\n if (!this.hasDecorator(dep)) {\n return ERR(new OMSSServerError(`\"${name}\" depends on \"${dep}\", which does not exist`))\n }\n }\n\n // modify the instance to make the decorator available as a property\n Object.defineProperty(this, name, {\n value,\n writable: false,\n configurable: false,\n enumerable: true,\n })\n\n return OK(name)\n }\n\n /**\n * Check if a decorator with the given name exists.\n * @param name - The name of the decorator to check.\n * @returns True if the decorator exists, false otherwise.\n */\n hasDecorator(name: string): boolean {\n return Object.hasOwn(this, name)\n }\n\n /**\n * Get a decorated property by its name.\n * @param name - The name of the property to retrieve.\n * @returns The decorated property value in the {@link Result} object.\n */\n getDecorator<T>(name: string): Result<T, OMSSServerError> {\n if (!Object.hasOwn(this, name)) {\n return ERR(new OMSSServerError(`Decorator \"${name}\" not found`))\n }\n\n return OK(this[name as keyof this] as T)\n }\n}\n\nexport default OMSSServer\n","import { BaseResolver } from '@/features/resolvers/BaseResolver.js'\nimport { OMSSProvider, ProviderResult, ProviderResultEmitter, ProviderSourcesMeta, ResolverMetadata } from '@/types/provider.js'\nimport type { ParsedOMSSId } from '@/types/resolver.js'\nimport { NonEmptyArray } from '@/types/utils.js'\n\n/**\n * Base class for all providers.\n */\nexport abstract class BaseProvider<P extends BaseResolver<unknown>> implements OMSSProvider<P> {\n /**\n * Provider ID. Must be unique.\n */\n abstract readonly id: string\n\n /**\n * Friendly name of the provider.\n */\n abstract readonly name: string\n\n /**\n * Whether the provider will be used.\n */\n abstract readonly enabled: boolean\n\n /**\n * Catalog of media this provider supports. It does not have to exist. If it does, it should be a list of media IDs.\n * This does not get queried for source resolving, but more metadata about the provider.\n */\n abstract readonly catalog?: () => Promise<NonEmptyArray<string>> | NonEmptyArray<string>\n\n /**\n * Provide a method that checks whether this provider supports a certain ID.\n * @param id - Parsed OMSS ID\n */\n abstract readonly supportsId: (id: ParsedOMSSId) => boolean | Promise<boolean>\n\n /**\n * Resolvers that this provider supports.\n */\n abstract readonly resolver: P\n\n /**\n * Fetch sources for a certain media.\n * @param media - Return object of the resolver's resolve() method.\n * @param result - The result emitter.\n */\n abstract getSources(media: ProviderSourcesMeta<ResolverMetadata<P>>, result: ProviderResultEmitter): Promise<ProviderResult>\n}\n","import { OMSSResolverError, Result } from '@/public-api.js'\nimport { OMSSId, OMSSResolver, ParsedOMSSId, ResolverExecutionContext, ResolverResult } from '@/types/resolver.js'\n\n/**\n * Base class for all OMSS resolvers.\n *\n * Resolvers convert an OMSS ID into usable metadata for providers.\n */\nexport abstract class BaseResolver<T> implements OMSSResolver<T> {\n /**\n * Namespace this resolver owns, e.g. \"tmdb\".\n * Must be unique for a single server instance.\n */\n abstract namespace: string\n\n /**\n * Human-readable resolver name.\n */\n abstract name: string\n\n /**\n * A map of ID converters.\n *\n * @key - The unhandled namespace\n * @value - A function that converts that id (from an unknown namespace/id provider) to this resolver's namespace.\n */\n abstract converter: Map<string, (noHandlerId: OMSSId, ctx: ResolverExecutionContext) => Promise<Result<OMSSId, OMSSResolverError>>>\n\n /**\n * Resolve a single ID into metadata.\n *\n * @param id - Parsed OMSS ID\n * @param ctx - Execution context.\n */\n abstract resolve(id: ParsedOMSSId, ctx: ResolverExecutionContext): Promise<ResolverResult<T>>\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,63 +1,66 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@omss/core",
|
|
3
|
-
"version": "0.0.2-beta.
|
|
4
|
-
"description": "Official TypeScript runtime and plugin orchestrator to create all kinds of OMSS based services!",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "./dist/index.cjs",
|
|
7
|
-
"module": "./dist/index.
|
|
8
|
-
"types": "./dist/index.d.
|
|
9
|
-
"exports": {
|
|
10
|
-
".": {
|
|
11
|
-
"import": {
|
|
12
|
-
"types": "./dist/index.d.
|
|
13
|
-
"default": "./dist/index.
|
|
14
|
-
},
|
|
15
|
-
"require": {
|
|
16
|
-
"types": "./dist/index.d.cts",
|
|
17
|
-
"default": "./dist/index.cjs"
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
},
|
|
21
|
-
"files": [
|
|
22
|
-
"dist"
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
"
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
"
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
"
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
"
|
|
54
|
-
},
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
"url": "https://github.com/omss-spec/core
|
|
61
|
-
},
|
|
62
|
-
"
|
|
63
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@omss/core",
|
|
3
|
+
"version": "0.0.2-beta.3",
|
|
4
|
+
"description": "Official TypeScript runtime and plugin orchestrator to create all kinds of OMSS based services!",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.mjs",
|
|
8
|
+
"types": "./dist/index.d.mts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": {
|
|
12
|
+
"types": "./dist/index.d.mts",
|
|
13
|
+
"default": "./dist/index.mjs"
|
|
14
|
+
},
|
|
15
|
+
"require": {
|
|
16
|
+
"types": "./dist/index.d.cts",
|
|
17
|
+
"default": "./dist/index.cjs"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist",
|
|
23
|
+
"README.md",
|
|
24
|
+
"SECURITY.md",
|
|
25
|
+
"LICENSE"
|
|
26
|
+
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsdown",
|
|
29
|
+
"format": "prettier --write .",
|
|
30
|
+
"dev": "tsdown --watch",
|
|
31
|
+
"example": "tsx example/example.ts",
|
|
32
|
+
"test": "vitest run",
|
|
33
|
+
"prepublishOnly": "npm run test && npm run build"
|
|
34
|
+
},
|
|
35
|
+
"keywords": [
|
|
36
|
+
"omss",
|
|
37
|
+
"streaming",
|
|
38
|
+
"plugin",
|
|
39
|
+
"media"
|
|
40
|
+
],
|
|
41
|
+
"license": "MIT",
|
|
42
|
+
"publishConfig": {
|
|
43
|
+
"access": "public"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@codecov/rollup-plugin": "^2.0.1",
|
|
47
|
+
"@types/node": "^22.20.0",
|
|
48
|
+
"@vitest/coverage-v8": "^4.1.9",
|
|
49
|
+
"prettier": "^3.8.4",
|
|
50
|
+
"tsdown": "^0.22.3",
|
|
51
|
+
"tsx": "^4.22.4",
|
|
52
|
+
"typescript": "^5.8.0",
|
|
53
|
+
"vitest": "^4.1.9"
|
|
54
|
+
},
|
|
55
|
+
"engines": {
|
|
56
|
+
"node": ">=20"
|
|
57
|
+
},
|
|
58
|
+
"repository": {
|
|
59
|
+
"type": "git",
|
|
60
|
+
"url": "https://github.com/omss-spec/core.git"
|
|
61
|
+
},
|
|
62
|
+
"bugs": {
|
|
63
|
+
"url": "https://github.com/omss-spec/core/issues"
|
|
64
|
+
},
|
|
65
|
+
"homepage": "https://github.com/omss-spec/core#readme"
|
|
66
|
+
}
|