aziosxjs 0.4.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +92 -267
  3. package/dist/cache/memoryCache.d.ts +4 -0
  4. package/dist/cache/memoryCache.js +32 -0
  5. package/dist/cache/memoryCache.js.map +1 -0
  6. package/dist/core/Azios.d.ts +33 -3
  7. package/dist/core/Azios.js +119 -2
  8. package/dist/core/Azios.js.map +1 -1
  9. package/dist/core/createInstance.js +3 -0
  10. package/dist/core/createInstance.js.map +1 -1
  11. package/dist/core/dispatchRequest.d.ts +1 -2
  12. package/dist/core/dispatchRequest.js +68 -42
  13. package/dist/core/dispatchRequest.js.map +1 -1
  14. package/dist/core/requestStore.d.ts +3 -0
  15. package/dist/core/requestStore.js +16 -0
  16. package/dist/core/requestStore.js.map +1 -0
  17. package/dist/errors/AziosError.d.ts +2 -2
  18. package/dist/errors/AziosError.js +6 -3
  19. package/dist/errors/AziosError.js.map +1 -1
  20. package/dist/helpers/buildURL.d.ts +1 -0
  21. package/dist/helpers/buildURL.js +27 -0
  22. package/dist/helpers/buildURL.js.map +1 -1
  23. package/dist/index.d.ts +15 -0
  24. package/dist/index.js +50 -0
  25. package/dist/index.js.map +1 -1
  26. package/dist/middleware/MiddlewarePipeline.d.ts +54 -0
  27. package/dist/middleware/MiddlewarePipeline.js +122 -0
  28. package/dist/middleware/MiddlewarePipeline.js.map +1 -0
  29. package/dist/middleware/compose.d.ts +1 -0
  30. package/dist/middleware/compose.js +25 -0
  31. package/dist/middleware/compose.js.map +1 -0
  32. package/dist/middleware/middlewareManager.d.ts +4 -0
  33. package/dist/middleware/middlewareManager.js +12 -0
  34. package/dist/middleware/middlewareManager.js.map +1 -0
  35. package/dist/plugins/pluginManager.d.ts +44 -0
  36. package/dist/plugins/pluginManager.js +120 -0
  37. package/dist/plugins/pluginManager.js.map +1 -0
  38. package/dist/rateLimiter/rateLimiter.d.ts +1 -0
  39. package/dist/rateLimiter/rateLimiter.js +31 -0
  40. package/dist/rateLimiter/rateLimiter.js.map +1 -0
  41. package/dist/runtimes/AdapterFactory.d.ts +22 -0
  42. package/dist/runtimes/AdapterFactory.js +43 -0
  43. package/dist/runtimes/AdapterFactory.js.map +1 -0
  44. package/dist/runtimes/HttpAdapter.d.ts +23 -0
  45. package/dist/runtimes/HttpAdapter.js +10 -0
  46. package/dist/runtimes/HttpAdapter.js.map +1 -0
  47. package/dist/runtimes/UniversalHttpAdapter.d.ts +19 -0
  48. package/dist/runtimes/UniversalHttpAdapter.js +114 -0
  49. package/dist/runtimes/UniversalHttpAdapter.js.map +1 -0
  50. package/dist/runtimes/detectRuntime.d.ts +34 -0
  51. package/dist/runtimes/detectRuntime.js +77 -0
  52. package/dist/runtimes/detectRuntime.js.map +1 -0
  53. package/dist/types/config.d.ts +8 -1
  54. package/dist/types/index.d.ts +5 -0
  55. package/dist/types/index.js +22 -0
  56. package/dist/types/index.js.map +1 -0
  57. package/dist/types/middleware.d.ts +16 -0
  58. package/dist/types/middleware.js +3 -0
  59. package/dist/types/middleware.js.map +1 -0
  60. package/dist/types/plugin.d.ts +43 -0
  61. package/dist/types/plugin.js +3 -0
  62. package/dist/types/plugin.js.map +1 -0
  63. package/dist/types/request.d.ts +15 -0
  64. package/dist/types/response.d.ts +23 -1
  65. package/package.json +24 -5
  66. package/src/adapters/httpAdapter.ts +0 -0
  67. package/src/adapters/index.ts +0 -0
  68. package/src/adapters/xhrAdapter.ts +0 -0
  69. package/src/cache/memoryCache.ts +45 -0
  70. package/src/config/mergeConfig.ts +0 -0
  71. package/src/core/Azios.ts +184 -0
  72. package/src/core/createInstance.ts +23 -0
  73. package/src/core/dispatchRequest.ts +111 -0
  74. package/src/core/requestMethods.ts +0 -0
  75. package/src/core/requestStore.ts +13 -0
  76. package/src/errors/AziosError.ts +26 -0
  77. package/src/helpers/buildURL.ts +44 -0
  78. package/src/helpers/normalizeHeaders.ts +0 -0
  79. package/src/helpers/parseHeaders.ts +0 -0
  80. package/src/index.ts +64 -0
  81. package/src/interceptors/InterceptorManager.ts +30 -0
  82. package/src/middleware/MiddlewarePipeline.ts +155 -0
  83. package/src/middleware/compose.ts +33 -0
  84. package/src/middleware/middlewareManager.ts +9 -0
  85. package/src/plugins/pluginManager.ts +143 -0
  86. package/src/rateLimiter/rateLimiter.ts +36 -0
  87. package/src/runtimes/AdapterFactory.ts +46 -0
  88. package/src/runtimes/HttpAdapter.ts +26 -0
  89. package/src/runtimes/UniversalHttpAdapter.ts +136 -0
  90. package/src/runtimes/detectRuntime.ts +85 -0
  91. package/src/types/config.ts +30 -0
  92. package/src/types/index.ts +5 -0
  93. package/src/types/middleware.ts +21 -0
  94. package/src/types/plugin.ts +50 -0
  95. package/src/types/request.ts +39 -0
  96. package/src/types/response.ts +37 -0
@@ -0,0 +1,30 @@
1
+ type FulfilledFn<T> = (val: T) => T | Promise<T>
2
+ type RejectedFn = (error: any) => any
3
+
4
+ interface Interceptor<T> {
5
+ fulfilled: FulfilledFn<T>
6
+ rejected?: RejectedFn
7
+ }
8
+
9
+ export default class InterceptorManager<T> {
10
+
11
+ private handlers: Array<Interceptor<T> | null> = []
12
+
13
+ use(fulfilled: FulfilledFn<T>, rejected?: RejectedFn) {
14
+ this.handlers.push({
15
+ fulfilled,
16
+ rejected
17
+ })
18
+
19
+ return this.handlers.length - 1
20
+ }
21
+
22
+ forEach(fn: (interceptor: Interceptor<T>) => void) {
23
+ this.handlers.forEach(h => {
24
+ if (h !== null) {
25
+ fn(h)
26
+ }
27
+ })
28
+ }
29
+
30
+ }
@@ -0,0 +1,155 @@
1
+ import type { AziosRequestConfig } from '../types/config'
2
+ import type { AziosResponse } from '../types/response'
3
+
4
+ /**
5
+ * Middleware handler types
6
+ * Supports async middleware in the style of Koa
7
+ */
8
+ export type RequestMiddleware = (
9
+ config: AziosRequestConfig,
10
+ next: () => Promise<AziosResponse>
11
+ ) => Promise<AziosResponse>
12
+
13
+ export type ResponseMiddleware = (
14
+ response: AziosResponse,
15
+ next: () => Promise<AziosResponse>
16
+ ) => Promise<AziosResponse>
17
+
18
+ /**
19
+ * Composition utility for middleware chaining
20
+ * Implements Koa-style middleware flow control
21
+ */
22
+ export class MiddlewarePipeline {
23
+ private requestMiddleware: RequestMiddleware[] = []
24
+ private responseMiddleware: ResponseMiddleware[] = []
25
+
26
+ /**
27
+ * Add request middleware
28
+ * Middleware will execute in registration order
29
+ */
30
+ useRequest(middleware: RequestMiddleware): this {
31
+ this.requestMiddleware.push(middleware)
32
+ return this
33
+ }
34
+
35
+ /**
36
+ * Add response middleware
37
+ * Middleware will execute in registration order
38
+ */
39
+ useResponse(middleware: ResponseMiddleware): this {
40
+ this.responseMiddleware.push(middleware)
41
+ return this
42
+ }
43
+
44
+ /**
45
+ * Compose request middleware with proper async error handling
46
+ */
47
+ private composeRequest(): RequestMiddleware {
48
+ const middleware = this.requestMiddleware
49
+
50
+ return async (config: AziosRequestConfig, next: () => Promise<AziosResponse>) => {
51
+ let index = -1
52
+
53
+ const dispatch = async (i: number): Promise<AziosResponse> => {
54
+ if (i <= index) {
55
+ throw new Error('next() called multiple times in middleware')
56
+ }
57
+
58
+ index = i
59
+
60
+ try {
61
+ if (i < middleware.length) {
62
+ return await middleware[i](config, () => dispatch(i + 1))
63
+ } else {
64
+ return await next()
65
+ }
66
+ } catch (err) {
67
+ throw err
68
+ }
69
+ }
70
+
71
+ return dispatch(0)
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Compose response middleware with proper async error handling
77
+ */
78
+ private composeResponse(): ResponseMiddleware {
79
+ const middleware = this.responseMiddleware
80
+
81
+ return async (response: AziosResponse, next: () => Promise<AziosResponse>) => {
82
+ let index = -1
83
+
84
+ const dispatch = async (i: number): Promise<AziosResponse> => {
85
+ if (i <= index) {
86
+ throw new Error('next() called multiple times in middleware')
87
+ }
88
+
89
+ index = i
90
+
91
+ try {
92
+ if (i < middleware.length) {
93
+ return await middleware[i](response, () => dispatch(i + 1))
94
+ } else {
95
+ return await next()
96
+ }
97
+ } catch (err) {
98
+ throw err
99
+ }
100
+ }
101
+
102
+ return dispatch(0)
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Execute request middleware pipeline
108
+ */
109
+ async executeRequestMiddleware(
110
+ config: AziosRequestConfig,
111
+ next: () => Promise<AziosResponse>
112
+ ): Promise<AziosResponse> {
113
+ if (this.requestMiddleware.length === 0) {
114
+ return next()
115
+ }
116
+
117
+ const composed = this.composeRequest()
118
+ return composed(config, next)
119
+ }
120
+
121
+ /**
122
+ * Execute response middleware pipeline
123
+ */
124
+ async executeResponseMiddleware(
125
+ response: AziosResponse,
126
+ next: () => Promise<AziosResponse>
127
+ ): Promise<AziosResponse> {
128
+ if (this.responseMiddleware.length === 0) {
129
+ return next()
130
+ }
131
+
132
+ const composed = this.composeResponse()
133
+ return composed(response, next)
134
+ }
135
+
136
+ /**
137
+ * Clear all middleware
138
+ */
139
+ clear(): void {
140
+ this.requestMiddleware = []
141
+ this.responseMiddleware = []
142
+ }
143
+
144
+ /**
145
+ * Get registered middleware count
146
+ */
147
+ getMiddlewareCount(): { request: number; response: number } {
148
+ return {
149
+ request: this.requestMiddleware.length,
150
+ response: this.responseMiddleware.length
151
+ }
152
+ }
153
+ }
154
+
155
+ export default MiddlewarePipeline
@@ -0,0 +1,33 @@
1
+ export function compose(middleware: any[]) {
2
+
3
+ return function (context: any) {
4
+
5
+ let index = -1
6
+
7
+ function dispatch(i: number): Promise<any> {
8
+
9
+ if (i <= index) {
10
+ return Promise.reject(new Error("next() called multiple times"))
11
+ }
12
+
13
+ index = i
14
+
15
+ const fn = middleware[i]
16
+
17
+ if (!fn) return Promise.resolve()
18
+
19
+ try {
20
+ return Promise.resolve(
21
+ fn(context, () => dispatch(i + 1))
22
+ )
23
+ } catch (err) {
24
+ return Promise.reject(err)
25
+ }
26
+
27
+ }
28
+
29
+ return dispatch(0)
30
+
31
+ }
32
+
33
+ }
@@ -0,0 +1,9 @@
1
+ export default class MiddlewareManager {
2
+
3
+ middlewares: any[] = []
4
+
5
+ use(fn: any) {
6
+ this.middlewares.push(fn)
7
+ }
8
+
9
+ }
@@ -0,0 +1,143 @@
1
+ import type { AziosPlugin, PluginRegistry } from '../types/plugin'
2
+ import type { AziosInstance } from '../types/request'
3
+ import type { AziosRequestConfig } from '../types/config'
4
+ import type { AziosResponse } from '../types/response'
5
+
6
+ /**
7
+ * PluginManager handles plugin lifecycle and hook execution
8
+ * Production-grade plugin system with error handling and validation
9
+ */
10
+ export default class PluginManager {
11
+ private plugins: PluginRegistry = {}
12
+
13
+ /**
14
+ * Install a plugin and trigger its onInstall hook
15
+ * @throws Error if plugin name is already registered
16
+ */
17
+ async install(plugin: AziosPlugin, instance: AziosInstance): Promise<void> {
18
+ if (this.plugins[plugin.name]) {
19
+ throw new Error(`Plugin "${plugin.name}" is already installed`)
20
+ }
21
+
22
+ this.plugins[plugin.name] = plugin
23
+
24
+ try {
25
+ if (plugin.hooks.onInstall) {
26
+ await plugin.hooks.onInstall(instance)
27
+ }
28
+ } catch (err) {
29
+ // Rollback on error
30
+ delete this.plugins[plugin.name]
31
+ throw err
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Uninstall a plugin and trigger its onUninstall hook
37
+ */
38
+ async uninstall(pluginName: string, instance: AziosInstance): Promise<void> {
39
+ const plugin = this.plugins[pluginName]
40
+
41
+ if (!plugin) {
42
+ throw new Error(`Plugin "${pluginName}" is not installed`)
43
+ }
44
+
45
+ try {
46
+ if (plugin.hooks.onUninstall) {
47
+ await plugin.hooks.onUninstall(instance)
48
+ }
49
+ } finally {
50
+ delete this.plugins[pluginName]
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Execute beforeRequest hooks from all plugins
56
+ */
57
+ async executeBeforeRequestHooks(
58
+ config: AziosRequestConfig
59
+ ): Promise<AziosRequestConfig> {
60
+ let result = config
61
+
62
+ for (const plugin of Object.values(this.plugins)) {
63
+ if (plugin.hooks.beforeRequest) {
64
+ try {
65
+ result = await plugin.hooks.beforeRequest(result)
66
+ } catch (err) {
67
+ throw new Error(
68
+ `Plugin "${plugin.name}" beforeRequest hook failed: ${err instanceof Error ? err.message : String(err)}`
69
+ )
70
+ }
71
+ }
72
+ }
73
+
74
+ return result
75
+ }
76
+
77
+ /**
78
+ * Execute afterResponse hooks from all plugins
79
+ */
80
+ async executeAfterResponseHooks(
81
+ response: AziosResponse
82
+ ): Promise<AziosResponse> {
83
+ let result = response
84
+
85
+ for (const plugin of Object.values(this.plugins)) {
86
+ if (plugin.hooks.afterResponse) {
87
+ try {
88
+ result = await plugin.hooks.afterResponse(result)
89
+ } catch (err) {
90
+ throw new Error(
91
+ `Plugin "${plugin.name}" afterResponse hook failed: ${err instanceof Error ? err.message : String(err)}`
92
+ )
93
+ }
94
+ }
95
+ }
96
+
97
+ return result
98
+ }
99
+
100
+ /**
101
+ * Execute onError hooks from all plugins
102
+ */
103
+ async executeOnErrorHooks(error: Error): Promise<Error> {
104
+ let result = error
105
+
106
+ for (const plugin of Object.values(this.plugins)) {
107
+ if (plugin.hooks.onError) {
108
+ try {
109
+ result = await plugin.hooks.onError(result)
110
+ } catch (err) {
111
+ // Log but don't throw - error handlers should not crash
112
+ console.warn(
113
+ `Plugin "${plugin.name}" onError hook failed:`,
114
+ err instanceof Error ? err.message : String(err)
115
+ )
116
+ }
117
+ }
118
+ }
119
+
120
+ return result
121
+ }
122
+
123
+ /**
124
+ * Get all currently installed plugins
125
+ */
126
+ getInstalledPlugins(): AziosPlugin[] {
127
+ return Object.values(this.plugins)
128
+ }
129
+
130
+ /**
131
+ * Check if a plugin is installed
132
+ */
133
+ isPluginInstalled(name: string): boolean {
134
+ return name in this.plugins
135
+ }
136
+
137
+ /**
138
+ * Clear all plugins
139
+ */
140
+ clear(): void {
141
+ this.plugins = {}
142
+ }
143
+ }
@@ -0,0 +1,36 @@
1
+ let queue: Function[] = [];
2
+ let activeRequests = 0;
3
+
4
+ export function schedule(task: Function, limit: number) {
5
+
6
+ return new Promise((resolve, reject) => {
7
+
8
+ const run = async () => {
9
+
10
+ activeRequests++;
11
+
12
+ try {
13
+ const result = await task();
14
+ resolve(result);
15
+ } catch (err) {
16
+ reject(err);
17
+ }
18
+
19
+ activeRequests--;
20
+
21
+ if (queue.length) {
22
+ const next = queue.shift();
23
+ next && next();
24
+ }
25
+
26
+ };
27
+
28
+ if (activeRequests < limit) {
29
+ run();
30
+ } else {
31
+ queue.push(run);
32
+ }
33
+
34
+ });
35
+
36
+ }
@@ -0,0 +1,46 @@
1
+ import type { HttpAdapter } from './HttpAdapter'
2
+ import { currentRuntime, RuntimeType } from './detectRuntime'
3
+ import UniversalHttpAdapter from './UniversalHttpAdapter'
4
+
5
+ /**
6
+ * Adapter factory for selecting the appropriate HTTP adapter
7
+ * Uses universal Fetch API when available with fallbacks for specific runtimes
8
+ */
9
+ export class AdapterFactory {
10
+ private static adapters: Map<RuntimeType, HttpAdapter> = new Map()
11
+
12
+ /**
13
+ * Get or create adapter for current runtime
14
+ */
15
+ static getAdapter(): HttpAdapter {
16
+ if (!this.adapters.has(currentRuntime)) {
17
+ this.adapters.set(currentRuntime, new UniversalHttpAdapter())
18
+ }
19
+
20
+ const adapter = this.adapters.get(currentRuntime)!
21
+
22
+ if (!adapter.isSupported()) {
23
+ throw new Error(
24
+ `No supported HTTP adapter found for runtime: ${currentRuntime}`
25
+ )
26
+ }
27
+
28
+ return adapter
29
+ }
30
+
31
+ /**
32
+ * Register custom adapter for specific runtime
33
+ */
34
+ static registerAdapter(runtime: RuntimeType, adapter: HttpAdapter): void {
35
+ this.adapters.set(runtime, adapter)
36
+ }
37
+
38
+ /**
39
+ * Clear adapter cache
40
+ */
41
+ static clearCache(): void {
42
+ this.adapters.clear()
43
+ }
44
+ }
45
+
46
+ export default AdapterFactory
@@ -0,0 +1,26 @@
1
+ import type { AziosRequestConfig } from '../types/config'
2
+ import type { AziosResponse } from '../types/response'
3
+
4
+ /**
5
+ * Universal HTTP adapter interface
6
+ * Abstracts HTTP functionality across different runtimes
7
+ */
8
+ export interface HttpAdapter {
9
+ /**
10
+ * Execute an HTTP request
11
+ */
12
+ request(config: AziosRequestConfig): Promise<AziosResponse>
13
+
14
+ /**
15
+ * Method to check if adapter supports the current runtime
16
+ */
17
+ isSupported(): boolean
18
+ }
19
+
20
+ /**
21
+ * Base adapter class with common logic
22
+ */
23
+ export abstract class BaseAdapter implements HttpAdapter {
24
+ abstract request(config: AziosRequestConfig): Promise<AziosResponse>
25
+ abstract isSupported(): boolean
26
+ }
@@ -0,0 +1,136 @@
1
+ import type { AziosRequestConfig } from '../types/config'
2
+ import type { AziosResponse } from '../types/response'
3
+ import { BaseAdapter } from './HttpAdapter'
4
+ import AziosError from '../errors/AziosError'
5
+ import { isRuntime, RuntimeType } from './detectRuntime'
6
+
7
+ /**
8
+ * Unified HTTP adapter using Fetch API
9
+ * Works across Node 18+, browsers, Bun, Deno, and edge runtimes
10
+ * Falls back to runtime-specific implementations if needed
11
+ */
12
+ export class UniversalHttpAdapter extends BaseAdapter {
13
+ /**
14
+ * Check if Fetch API is available
15
+ */
16
+ isSupported(): boolean {
17
+ return typeof fetch !== 'undefined'
18
+ }
19
+
20
+ /**
21
+ * Execute HTTP request using Fetch API
22
+ */
23
+ async request(config: AziosRequestConfig): Promise<AziosResponse> {
24
+ if (!this.isSupported()) {
25
+ throw new AziosError(
26
+ 'Fetch API is not available in this runtime',
27
+ 'UNSUPPORTED_RUNTIME',
28
+ config
29
+ )
30
+ }
31
+
32
+ try {
33
+ const { url, method = 'GET', headers, data, timeout, signal } = config
34
+
35
+ const baseURL = config.baseURL ? config.baseURL : ''
36
+ const fullURL = new URL(baseURL + url)
37
+
38
+ // Add query parameters
39
+ if (config.params) {
40
+ Object.entries(config.params).forEach(([key, value]) => {
41
+ if (value !== null && value !== undefined) {
42
+ if (Array.isArray(value)) {
43
+ value.forEach(v => fullURL.searchParams.append(key, String(v)))
44
+ } else {
45
+ fullURL.searchParams.set(key, String(value))
46
+ }
47
+ }
48
+ })
49
+ }
50
+
51
+ // Prepare request body
52
+ let body: string | null = null
53
+ if (data) {
54
+ body = typeof data === 'string' ? data : JSON.stringify(data)
55
+ }
56
+
57
+ // Set up timeout if specified (using AbortController)
58
+ let timeoutId: NodeJS.Timeout | undefined
59
+ const controller = new AbortController()
60
+ const finalSignal = signal || controller.signal
61
+
62
+ if (timeout) {
63
+ timeoutId = setTimeout(() => {
64
+ controller.abort()
65
+ }, timeout)
66
+ }
67
+
68
+ // Execute fetch
69
+ const response = await fetch(fullURL.toString(), {
70
+ method,
71
+ headers: {
72
+ 'Content-Type': 'application/json',
73
+ ...headers
74
+ },
75
+ body,
76
+ signal: finalSignal as any
77
+ })
78
+
79
+ if (timeoutId) {
80
+ clearTimeout(timeoutId)
81
+ }
82
+
83
+ // Parse response body
84
+ let responseData: any
85
+
86
+ if (response.status === 204) {
87
+ // No content
88
+ responseData = null
89
+ } else {
90
+ const contentType = response.headers.get('content-type')
91
+
92
+ if (contentType?.includes('application/json')) {
93
+ try {
94
+ responseData = await response.json()
95
+ } catch {
96
+ responseData = await response.text()
97
+ }
98
+ } else {
99
+ responseData = await response.text()
100
+ }
101
+ }
102
+
103
+ const aziosResponse: AziosResponse = {
104
+ data: responseData,
105
+ status: response.status,
106
+ statusText: response.statusText,
107
+ headers: Object.fromEntries(response.headers.entries()),
108
+ config
109
+ }
110
+
111
+ return aziosResponse
112
+ } catch (err) {
113
+ if (err instanceof TypeError && err.message.includes('aborted')) {
114
+ throw new AziosError(
115
+ 'Request aborted',
116
+ 'ABORTED',
117
+ config
118
+ )
119
+ }
120
+
121
+ if (err instanceof AziosError) {
122
+ throw err
123
+ }
124
+
125
+ throw new AziosError(
126
+ err instanceof Error ? err.message : 'Network request failed',
127
+ 'NETWORK_ERROR',
128
+ config,
129
+ undefined,
130
+ err instanceof Error ? err : new Error(String(err))
131
+ )
132
+ }
133
+ }
134
+ }
135
+
136
+ export default UniversalHttpAdapter
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Runtime detection utilities
3
+ * Identifies the current JavaScript runtime environment
4
+ * Supports: Node.js, Browser, Bun, Deno, Cloudflare Workers/Edge
5
+ */
6
+
7
+ export enum RuntimeType {
8
+ Node = 'node',
9
+ Browser = 'browser',
10
+ Bun = 'bun',
11
+ Deno = 'deno',
12
+ Edge = 'edge',
13
+ Unknown = 'unknown'
14
+ }
15
+
16
+ /**
17
+ * Detects the current runtime environment
18
+ * Production-grade detection with reliable environment checks
19
+ */
20
+ export function detectRuntime(): RuntimeType {
21
+ // Check for Deno
22
+ if (typeof (globalThis as any).Deno !== 'undefined') {
23
+ return RuntimeType.Deno
24
+ }
25
+
26
+ // Check for Bun
27
+ if (typeof (globalThis as any).Bun !== 'undefined') {
28
+ return RuntimeType.Bun
29
+ }
30
+
31
+ // Check for Cloudflare Workers / Edge
32
+ if (
33
+ typeof (globalThis as any).caches !== 'undefined' &&
34
+ typeof (globalThis as any).ENVIRONMENT !== 'undefined'
35
+ ) {
36
+ return RuntimeType.Edge
37
+ }
38
+
39
+ // Check for Node.js
40
+ if (
41
+ typeof process !== 'undefined' &&
42
+ process.versions &&
43
+ process.versions.node
44
+ ) {
45
+ return RuntimeType.Node
46
+ }
47
+
48
+ // Check for Browser
49
+ if (typeof (globalThis as any).window !== 'undefined' && typeof (globalThis as any).document !== 'undefined') {
50
+ return RuntimeType.Browser
51
+ }
52
+
53
+ return RuntimeType.Unknown
54
+ }
55
+
56
+ /**
57
+ * Get current runtime for use in adapters
58
+ */
59
+ export const currentRuntime = detectRuntime()
60
+
61
+ /**
62
+ * Check if running in a specific runtime
63
+ */
64
+ export function isRuntime(runtime: RuntimeType): boolean {
65
+ return currentRuntime === runtime
66
+ }
67
+
68
+ /**
69
+ * Check if running in server-side runtime
70
+ */
71
+ export function isServerRuntime(): boolean {
72
+ return (
73
+ currentRuntime === RuntimeType.Node ||
74
+ currentRuntime === RuntimeType.Bun ||
75
+ currentRuntime === RuntimeType.Deno ||
76
+ currentRuntime === RuntimeType.Edge
77
+ )
78
+ }
79
+
80
+ /**
81
+ * Check if running in browser runtime
82
+ */
83
+ export function isBrowserRuntime(): boolean {
84
+ return currentRuntime === RuntimeType.Browser
85
+ }