@planet-matrix/mobius-model 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/README.md +123 -36
- package/dist/index.js +45 -4
- package/dist/index.js.map +183 -11
- package/oxlint.config.ts +6 -0
- package/package.json +16 -10
- package/src/abort/README.md +92 -0
- package/src/abort/abort-manager.ts +278 -0
- package/src/abort/abort-signal-listener-manager.ts +81 -0
- package/src/abort/index.ts +2 -0
- package/src/basic/README.md +69 -118
- package/src/basic/function.ts +81 -62
- package/src/basic/is.ts +152 -71
- package/src/basic/promise.ts +29 -8
- package/src/basic/string.ts +2 -33
- package/src/color/README.md +105 -0
- package/src/color/index.ts +3 -0
- package/src/color/internal.ts +42 -0
- package/src/color/rgb/analyze.ts +236 -0
- package/src/color/rgb/construct.ts +130 -0
- package/src/color/rgb/convert.ts +227 -0
- package/src/color/rgb/derive.ts +303 -0
- package/src/color/rgb/index.ts +6 -0
- package/src/color/rgb/internal.ts +208 -0
- package/src/color/rgb/parse.ts +302 -0
- package/src/color/rgb/serialize.ts +144 -0
- package/src/color/types.ts +57 -0
- package/src/color/xyz/analyze.ts +80 -0
- package/src/color/xyz/construct.ts +19 -0
- package/src/color/xyz/convert.ts +71 -0
- package/src/color/xyz/index.ts +3 -0
- package/src/color/xyz/internal.ts +23 -0
- package/src/css/README.md +93 -0
- package/src/css/class.ts +559 -0
- package/src/css/index.ts +1 -0
- package/src/encoding/README.md +66 -79
- package/src/encoding/base64.ts +13 -4
- package/src/environment/README.md +97 -0
- package/src/environment/basic.ts +26 -0
- package/src/environment/device.ts +311 -0
- package/src/environment/feature.ts +285 -0
- package/src/environment/geo.ts +337 -0
- package/src/environment/index.ts +7 -0
- package/src/environment/runtime.ts +400 -0
- package/src/environment/snapshot.ts +60 -0
- package/src/environment/variable.ts +239 -0
- package/src/event/README.md +90 -0
- package/src/event/class-event-proxy.ts +228 -0
- package/src/event/common.ts +19 -0
- package/src/event/event-manager.ts +203 -0
- package/src/event/index.ts +4 -0
- package/src/event/instance-event-proxy.ts +186 -0
- package/src/event/internal.ts +24 -0
- package/src/exception/README.md +96 -0
- package/src/exception/browser.ts +219 -0
- package/src/exception/index.ts +4 -0
- package/src/exception/nodejs.ts +169 -0
- package/src/exception/normalize.ts +106 -0
- package/src/exception/types.ts +99 -0
- package/src/identifier/README.md +92 -0
- package/src/identifier/id.ts +119 -0
- package/src/identifier/index.ts +2 -0
- package/src/identifier/uuid.ts +187 -0
- package/src/index.ts +16 -1
- package/src/log/README.md +79 -0
- package/src/log/index.ts +5 -0
- package/src/log/log-emitter.ts +72 -0
- package/src/log/log-record.ts +10 -0
- package/src/log/log-scheduler.ts +74 -0
- package/src/log/log-type.ts +8 -0
- package/src/log/logger.ts +543 -0
- package/src/orchestration/README.md +89 -0
- package/src/orchestration/coordination/barrier.ts +214 -0
- package/src/orchestration/coordination/count-down-latch.ts +215 -0
- package/src/orchestration/coordination/errors.ts +98 -0
- package/src/orchestration/coordination/index.ts +16 -0
- package/src/orchestration/coordination/internal/wait-constraints.ts +95 -0
- package/src/orchestration/coordination/internal/wait-queue.ts +109 -0
- package/src/orchestration/coordination/keyed-lock.ts +168 -0
- package/src/orchestration/coordination/mutex.ts +257 -0
- package/src/orchestration/coordination/permit.ts +127 -0
- package/src/orchestration/coordination/read-write-lock.ts +444 -0
- package/src/orchestration/coordination/semaphore.ts +280 -0
- package/src/orchestration/index.ts +1 -0
- package/src/random/README.md +55 -86
- package/src/random/index.ts +1 -1
- package/src/random/string.ts +35 -0
- package/src/reactor/README.md +4 -0
- package/src/reactor/reactor-core/primitive.ts +9 -9
- package/src/reactor/reactor-core/reactive-system.ts +5 -5
- package/src/singleton/README.md +79 -0
- package/src/singleton/factory.ts +55 -0
- package/src/singleton/index.ts +2 -0
- package/src/singleton/manager.ts +204 -0
- package/src/storage/README.md +107 -0
- package/src/storage/index.ts +1 -0
- package/src/storage/table.ts +449 -0
- package/src/timer/README.md +86 -0
- package/src/timer/expiration/expiration-manager.ts +594 -0
- package/src/timer/expiration/index.ts +3 -0
- package/src/timer/expiration/min-heap.ts +208 -0
- package/src/timer/expiration/remaining-manager.ts +241 -0
- package/src/timer/index.ts +1 -0
- package/src/type/README.md +54 -307
- package/src/type/class.ts +2 -2
- package/src/type/index.ts +14 -14
- package/src/type/is.ts +265 -2
- package/src/type/object.ts +37 -0
- package/src/type/string.ts +7 -2
- package/src/type/tuple.ts +6 -6
- package/src/type/union.ts +16 -0
- package/src/web/README.md +77 -0
- package/src/web/capture.ts +35 -0
- package/src/web/clipboard.ts +97 -0
- package/src/web/dom.ts +117 -0
- package/src/web/download.ts +16 -0
- package/src/web/event.ts +46 -0
- package/src/web/index.ts +10 -0
- package/src/web/local-storage.ts +113 -0
- package/src/web/location.ts +28 -0
- package/src/web/permission.ts +172 -0
- package/src/web/script-loader.ts +432 -0
- package/tests/unit/abort/abort-manager.spec.ts +225 -0
- package/tests/unit/abort/abort-signal-listener-manager.spec.ts +62 -0
- package/tests/unit/basic/array.spec.ts +1 -1
- package/tests/unit/basic/stream.spec.ts +1 -1
- package/tests/unit/basic/string.spec.ts +0 -9
- package/tests/unit/color/rgb/analyze.spec.ts +110 -0
- package/tests/unit/color/rgb/construct.spec.ts +56 -0
- package/tests/unit/color/rgb/convert.spec.ts +60 -0
- package/tests/unit/color/rgb/derive.spec.ts +103 -0
- package/tests/unit/color/rgb/parse.spec.ts +66 -0
- package/tests/unit/color/rgb/serialize.spec.ts +46 -0
- package/tests/unit/color/xyz/analyze.spec.ts +33 -0
- package/tests/unit/color/xyz/construct.spec.ts +10 -0
- package/tests/unit/color/xyz/convert.spec.ts +18 -0
- package/tests/unit/css/class.spec.ts +157 -0
- package/tests/unit/environment/basic.spec.ts +20 -0
- package/tests/unit/environment/device.spec.ts +146 -0
- package/tests/unit/environment/feature.spec.ts +388 -0
- package/tests/unit/environment/geo.spec.ts +111 -0
- package/tests/unit/environment/runtime.spec.ts +364 -0
- package/tests/unit/environment/snapshot.spec.ts +4 -0
- package/tests/unit/environment/variable.spec.ts +190 -0
- package/tests/unit/event/class-event-proxy.spec.ts +225 -0
- package/tests/unit/event/event-manager.spec.ts +246 -0
- package/tests/unit/event/instance-event-proxy.spec.ts +187 -0
- package/tests/unit/exception/browser.spec.ts +213 -0
- package/tests/unit/exception/nodejs.spec.ts +144 -0
- package/tests/unit/exception/normalize.spec.ts +57 -0
- package/tests/unit/identifier/id.spec.ts +71 -0
- package/tests/unit/identifier/uuid.spec.ts +85 -0
- package/tests/unit/log/log-emitter.spec.ts +33 -0
- package/tests/unit/log/log-scheduler.spec.ts +40 -0
- package/tests/unit/log/log-type.spec.ts +7 -0
- package/tests/unit/log/logger.spec.ts +222 -0
- package/tests/unit/orchestration/coordination/barrier.spec.ts +96 -0
- package/tests/unit/orchestration/coordination/count-down-latch.spec.ts +63 -0
- package/tests/unit/orchestration/coordination/errors.spec.ts +29 -0
- package/tests/unit/orchestration/coordination/keyed-lock.spec.ts +109 -0
- package/tests/unit/orchestration/coordination/mutex.spec.ts +132 -0
- package/tests/unit/orchestration/coordination/permit.spec.ts +43 -0
- package/tests/unit/orchestration/coordination/read-write-lock.spec.ts +154 -0
- package/tests/unit/orchestration/coordination/semaphore.spec.ts +135 -0
- package/tests/unit/random/string.spec.ts +11 -0
- package/tests/unit/reactor/alien-signals-effect.spec.ts +11 -10
- package/tests/unit/reactor/preact-signal.spec.ts +1 -2
- package/tests/unit/singleton/singleton.spec.ts +49 -0
- package/tests/unit/storage/table.spec.ts +620 -0
- package/tests/unit/timer/expiration/expiration-manager.spec.ts +464 -0
- package/tests/unit/timer/expiration/min-heap.spec.ts +71 -0
- package/tests/unit/timer/expiration/remaining-manager.spec.ts +234 -0
- package/.oxlintrc.json +0 -5
- package/src/random/uuid.ts +0 -103
- package/tests/unit/random/uuid.spec.ts +0 -37
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
// oxlint-disable no-unsafe-member-access
|
|
2
|
+
// oxlint-disable no-undef
|
|
3
|
+
// oxlint-disable no-typeof-undefined
|
|
4
|
+
|
|
5
|
+
import type { StringAutoCompletable } from "#Source/type/index.ts"
|
|
6
|
+
import type { Use } from "./basic.ts"
|
|
7
|
+
|
|
8
|
+
import { useFactory } from "./basic.ts"
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Define built-in and custom runtime keys.
|
|
12
|
+
*/
|
|
13
|
+
export type Runtime =
|
|
14
|
+
| StringAutoCompletable
|
|
15
|
+
| "browser"
|
|
16
|
+
| "nodejs"
|
|
17
|
+
| "deno"
|
|
18
|
+
| "bun"
|
|
19
|
+
| "web-worker"
|
|
20
|
+
| "service-worker"
|
|
21
|
+
| "unknown"
|
|
22
|
+
|
|
23
|
+
interface RuntimeRegistryItem {
|
|
24
|
+
runtime: Runtime
|
|
25
|
+
detect: () => boolean
|
|
26
|
+
// oxlint-disable-next-line no-explicit-any
|
|
27
|
+
getGlobalContext: () => any
|
|
28
|
+
// oxlint-disable-next-line no-explicit-any
|
|
29
|
+
use: Use<any>
|
|
30
|
+
}
|
|
31
|
+
const internalRuntimeRegistry = new Map<Runtime, RuntimeRegistryItem>();
|
|
32
|
+
/**
|
|
33
|
+
* Check whether a runtime is registered.
|
|
34
|
+
*/
|
|
35
|
+
export const isRuntimeRegistered = (runtime: Runtime): boolean => {
|
|
36
|
+
return internalRuntimeRegistry.has(runtime);
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Register a runtime detector and context provider.
|
|
40
|
+
*/
|
|
41
|
+
export const registerRuntime = (item: RuntimeRegistryItem): void => {
|
|
42
|
+
internalRuntimeRegistry.set(item.runtime, item);
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Unregister a runtime by key.
|
|
46
|
+
*/
|
|
47
|
+
export const unregisterRuntime = (name: Runtime): void => {
|
|
48
|
+
internalRuntimeRegistry.delete(name);
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* List all registered runtime keys.
|
|
52
|
+
*/
|
|
53
|
+
export const listRuntimes = (): Runtime[] => {
|
|
54
|
+
return Array.from(internalRuntimeRegistry.keys());
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Check if the environment is a browser.
|
|
59
|
+
*/
|
|
60
|
+
export const isBrowser = (): boolean => {
|
|
61
|
+
return typeof window !== "undefined" && typeof window.document !== "undefined"
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Describe runtime context values available in browser.
|
|
65
|
+
*/
|
|
66
|
+
export interface RuntimeContextBrowser {
|
|
67
|
+
global: Window & typeof globalThis
|
|
68
|
+
self: Window & typeof globalThis
|
|
69
|
+
window: Window & typeof globalThis
|
|
70
|
+
globalThis: typeof globalThis
|
|
71
|
+
importMeta: ImportMeta
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Return browser runtime context values.
|
|
75
|
+
*/
|
|
76
|
+
export const getRuntimeContextBrowser = (): RuntimeContextBrowser => {
|
|
77
|
+
return { global: window, self, window, globalThis, importMeta: import.meta }
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Execute logic with browser runtime context when available.
|
|
81
|
+
*/
|
|
82
|
+
export const useBrowser: Use<RuntimeContextBrowser> = useFactory(getRuntimeContextBrowser, isBrowser)
|
|
83
|
+
registerRuntime({
|
|
84
|
+
runtime: "browser",
|
|
85
|
+
detect: isBrowser,
|
|
86
|
+
getGlobalContext: getRuntimeContextBrowser,
|
|
87
|
+
use: useBrowser,
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Check if the environment is Node.js.
|
|
92
|
+
*/
|
|
93
|
+
export const isNodejs = (): boolean => {
|
|
94
|
+
return typeof process !== "undefined" && process.versions !== null && process.versions.node !== null
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Describe runtime context values available in Node.js.
|
|
98
|
+
*/
|
|
99
|
+
export interface RuntimeContextNodejs {
|
|
100
|
+
global: typeof globalThis
|
|
101
|
+
globalThis: typeof globalThis
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Return Node.js runtime context values.
|
|
105
|
+
*/
|
|
106
|
+
export const getRuntimeContextNodejs = (): RuntimeContextNodejs => {
|
|
107
|
+
return { global: globalThis, globalThis }
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Execute logic with Node.js runtime context when available.
|
|
111
|
+
*/
|
|
112
|
+
export const useNodejs: Use<RuntimeContextNodejs> = useFactory(getRuntimeContextNodejs, isNodejs)
|
|
113
|
+
registerRuntime({
|
|
114
|
+
runtime: "nodejs",
|
|
115
|
+
detect: isNodejs,
|
|
116
|
+
getGlobalContext: getRuntimeContextNodejs,
|
|
117
|
+
use: useNodejs,
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Check if the environment is Deno.
|
|
122
|
+
*/
|
|
123
|
+
export const isDeno = (): boolean => {
|
|
124
|
+
// @ts-expect-error Deno is not defined in some environments
|
|
125
|
+
return typeof Deno !== "undefined" && typeof Deno.version !== "undefined" && typeof Deno.version.deno !== "undefined"
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Describe runtime context values available in Deno.
|
|
129
|
+
*/
|
|
130
|
+
export interface RuntimeContextDeno {
|
|
131
|
+
global: typeof globalThis
|
|
132
|
+
globalThis: typeof globalThis
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Return Deno runtime context values.
|
|
136
|
+
*/
|
|
137
|
+
export const getRuntimeContextDeno = (): RuntimeContextDeno => {
|
|
138
|
+
return { global: globalThis, globalThis }
|
|
139
|
+
};
|
|
140
|
+
/**
|
|
141
|
+
* Execute logic with Deno runtime context when available.
|
|
142
|
+
*/
|
|
143
|
+
export const useDeno: Use<RuntimeContextDeno> = useFactory(getRuntimeContextDeno, isDeno)
|
|
144
|
+
registerRuntime({
|
|
145
|
+
runtime: "deno",
|
|
146
|
+
detect: isDeno,
|
|
147
|
+
getGlobalContext: getRuntimeContextDeno,
|
|
148
|
+
use: useDeno,
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Check if the environment is Bun.
|
|
153
|
+
*/
|
|
154
|
+
export const isBun = (): boolean => {
|
|
155
|
+
return typeof Bun !== "undefined" && typeof Bun.version !== "undefined" && typeof Bun.version !== "undefined"
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Describe runtime context values available in Bun.
|
|
159
|
+
*/
|
|
160
|
+
export interface RuntimeContextBun {
|
|
161
|
+
global: typeof globalThis
|
|
162
|
+
Bun: typeof Bun
|
|
163
|
+
globalThis: typeof globalThis
|
|
164
|
+
importMeta: ImportMeta
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Return Bun runtime context values.
|
|
168
|
+
*/
|
|
169
|
+
export const getRuntimeContextBun = (): RuntimeContextBun => {
|
|
170
|
+
return { global: globalThis, Bun, globalThis, importMeta: import.meta }
|
|
171
|
+
};
|
|
172
|
+
/**
|
|
173
|
+
* Execute logic with Bun runtime context when available.
|
|
174
|
+
*/
|
|
175
|
+
export const useBun: Use<RuntimeContextBun> = useFactory(getRuntimeContextBun, isBun)
|
|
176
|
+
registerRuntime({
|
|
177
|
+
runtime: "bun",
|
|
178
|
+
detect: isBun,
|
|
179
|
+
getGlobalContext: getRuntimeContextBun,
|
|
180
|
+
use: useBun,
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Check if the environment is a Web Worker.
|
|
185
|
+
*/
|
|
186
|
+
export const isWebWorker = (): boolean => {
|
|
187
|
+
return (
|
|
188
|
+
typeof self !== "undefined" &&
|
|
189
|
+
typeof WorkerGlobalScope !== "undefined" &&
|
|
190
|
+
self instanceof WorkerGlobalScope &&
|
|
191
|
+
(
|
|
192
|
+
typeof ServiceWorkerGlobalScope === "undefined" ||
|
|
193
|
+
!(self instanceof ServiceWorkerGlobalScope)
|
|
194
|
+
)
|
|
195
|
+
)
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Describe runtime context values available in Web Worker.
|
|
199
|
+
*/
|
|
200
|
+
export interface RuntimeContextWebWorker {
|
|
201
|
+
global: typeof globalThis
|
|
202
|
+
self: WorkerGlobalScope & typeof globalThis
|
|
203
|
+
globalThis: typeof globalThis
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Return Web Worker runtime context values.
|
|
207
|
+
*/
|
|
208
|
+
export const getRuntimeContextWebWorker = (): RuntimeContextWebWorker => {
|
|
209
|
+
// @ts-expect-error - self is not defined in some environments
|
|
210
|
+
return { global: self, self, globalThis }
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Execute logic with Web Worker runtime context when available.
|
|
214
|
+
*/
|
|
215
|
+
export const useWebWorker: Use<RuntimeContextWebWorker> = useFactory(getRuntimeContextWebWorker, isWebWorker)
|
|
216
|
+
registerRuntime({
|
|
217
|
+
runtime: "web-worker",
|
|
218
|
+
detect: isWebWorker,
|
|
219
|
+
getGlobalContext: getRuntimeContextWebWorker,
|
|
220
|
+
use: useWebWorker,
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Check if the environment is a Service Worker.
|
|
225
|
+
*/
|
|
226
|
+
export const isServiceWorker = (): boolean => {
|
|
227
|
+
return (
|
|
228
|
+
typeof self !== "undefined" &&
|
|
229
|
+
typeof ServiceWorkerGlobalScope !== "undefined" &&
|
|
230
|
+
self instanceof ServiceWorkerGlobalScope
|
|
231
|
+
)
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Describe runtime context values available in Service Worker.
|
|
235
|
+
*/
|
|
236
|
+
export interface RuntimeContextServiceWorker {
|
|
237
|
+
global: typeof globalThis
|
|
238
|
+
self: ServiceWorkerGlobalScope & typeof globalThis
|
|
239
|
+
globalThis: typeof globalThis
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Return Service Worker runtime context values.
|
|
243
|
+
*/
|
|
244
|
+
export const getServiceWorkerContext = (): RuntimeContextServiceWorker => {
|
|
245
|
+
// @ts-expect-error - self is not defined in some environments
|
|
246
|
+
return { global: self, self, globalThis }
|
|
247
|
+
};
|
|
248
|
+
/**
|
|
249
|
+
* Execute logic with Service Worker runtime context when available.
|
|
250
|
+
*/
|
|
251
|
+
export const useServiceWorker: Use<RuntimeContextServiceWorker> = useFactory(getServiceWorkerContext, isServiceWorker)
|
|
252
|
+
registerRuntime({
|
|
253
|
+
runtime: "service-worker",
|
|
254
|
+
detect: isServiceWorker,
|
|
255
|
+
getGlobalContext: getServiceWorkerContext,
|
|
256
|
+
use: useServiceWorker,
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Check if the environment is unknown.
|
|
261
|
+
*/
|
|
262
|
+
export const isUnknown = (): boolean => {
|
|
263
|
+
return getRuntimeRegistryItem().runtime === "unknown";
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Describe runtime context values for unknown runtime.
|
|
267
|
+
*/
|
|
268
|
+
export interface RuntimeContextUnknown {
|
|
269
|
+
global: unknown
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Return unknown runtime context values.
|
|
273
|
+
*/
|
|
274
|
+
export const getRuntimeContextUnknown = (): RuntimeContextUnknown => {
|
|
275
|
+
return { global: undefined }
|
|
276
|
+
};
|
|
277
|
+
/**
|
|
278
|
+
* Execute logic with unknown runtime context when available.
|
|
279
|
+
*/
|
|
280
|
+
export const useUnknown: Use<RuntimeContextUnknown> = useFactory(getRuntimeContextUnknown, isUnknown)
|
|
281
|
+
registerRuntime({
|
|
282
|
+
runtime: "unknown",
|
|
283
|
+
detect: isUnknown,
|
|
284
|
+
getGlobalContext: getRuntimeContextUnknown,
|
|
285
|
+
use: useUnknown,
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Get the current runtime environment.
|
|
290
|
+
*/
|
|
291
|
+
export const getRuntimeRegistryItem = (): RuntimeRegistryItem => {
|
|
292
|
+
// 如果不是其它任何 Runtime,则是 unknown
|
|
293
|
+
// 这样写可以兼容用户自行注册的 Runtime
|
|
294
|
+
for (const [name, item] of internalRuntimeRegistry) {
|
|
295
|
+
if (name === "unknown") {
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
if (item.detect() === true) {
|
|
299
|
+
return item;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
return internalRuntimeRegistry.get("unknown")!;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Get the key of the current runtime environment.
|
|
307
|
+
*/
|
|
308
|
+
export const getRuntime = (): RuntimeRegistryItem['runtime'] => {
|
|
309
|
+
return getRuntimeRegistryItem().runtime;
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Define detection results for registered runtimes.
|
|
314
|
+
*/
|
|
315
|
+
export type RuntimeFlags = Record<Runtime, boolean>;
|
|
316
|
+
/**
|
|
317
|
+
* Define a function type that resolves all runtime flags.
|
|
318
|
+
*/
|
|
319
|
+
export type GetRuntimeFlags = () => RuntimeFlags;
|
|
320
|
+
/**
|
|
321
|
+
* Get the runtime flags for all registered runtimes.
|
|
322
|
+
*/
|
|
323
|
+
export const getRuntimeFlags: GetRuntimeFlags = (): RuntimeFlags => {
|
|
324
|
+
// oxlint-disable-next-line no-unsafe-type-assertion
|
|
325
|
+
const flags = {} as RuntimeFlags;
|
|
326
|
+
|
|
327
|
+
for (const [name, item] of internalRuntimeRegistry) {
|
|
328
|
+
flags[name] = item.detect() === true;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
return flags
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Check if the current runtime satisfies the specified runtime.
|
|
336
|
+
*/
|
|
337
|
+
export const isSatisfiesRuntime = (runtime: Runtime): boolean => {
|
|
338
|
+
const runtimeRegistryItem = internalRuntimeRegistry.get(runtime);
|
|
339
|
+
if (runtimeRegistryItem === undefined) {
|
|
340
|
+
throw new Error(`Runtime "${runtime}" is not registered.`);
|
|
341
|
+
}
|
|
342
|
+
return runtimeRegistryItem.detect() === true;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Check if the current runtime satisfies the specified runtimes condition.
|
|
347
|
+
*/
|
|
348
|
+
export const isSatisfiesRuntimes = (condition: Partial<RuntimeFlags>): boolean => {
|
|
349
|
+
let result: boolean = true
|
|
350
|
+
Object.keys(condition).forEach(runtime => {
|
|
351
|
+
const runtimeRegistryItem = internalRuntimeRegistry.get(runtime);
|
|
352
|
+
if (runtimeRegistryItem === undefined) {
|
|
353
|
+
throw new Error(`Runtime "${runtime}" is not registered.`);
|
|
354
|
+
}
|
|
355
|
+
if (runtimeRegistryItem.detect() !== condition[runtime]) {
|
|
356
|
+
result = false
|
|
357
|
+
}
|
|
358
|
+
})
|
|
359
|
+
return result
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Map runtime keys to their typed context payloads.
|
|
364
|
+
*/
|
|
365
|
+
export interface RuntimeContexts {
|
|
366
|
+
browser: RuntimeContextBrowser
|
|
367
|
+
nodejs: RuntimeContextNodejs
|
|
368
|
+
deno: RuntimeContextDeno
|
|
369
|
+
bun: RuntimeContextBun
|
|
370
|
+
"web-worker": RuntimeContextWebWorker
|
|
371
|
+
"service-worker": RuntimeContextServiceWorker
|
|
372
|
+
unknown: RuntimeContextUnknown
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Define handler options for runtime-conditional execution.
|
|
376
|
+
*/
|
|
377
|
+
export type UseRuntimesOptions<R> = {
|
|
378
|
+
// 这里用 unknown 更合理,但 unknown 会有类型问题无法解决,暂时不得不用 any
|
|
379
|
+
// 若有解决方案请替换回 unknown
|
|
380
|
+
// oxlint-disable-next-line no-explicit-any
|
|
381
|
+
[K in Runtime]?: (context: K extends keyof RuntimeContexts ? RuntimeContexts[K] : any) => R
|
|
382
|
+
} & {
|
|
383
|
+
default?: (() => R)
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Execute a runtime-specific handler with fallback support.
|
|
387
|
+
*/
|
|
388
|
+
export const useRuntimes = <R>(options: UseRuntimesOptions<R>): R => {
|
|
389
|
+
const runtimeRegisterItem = getRuntimeRegistryItem()
|
|
390
|
+
const runtime = runtimeRegisterItem.runtime
|
|
391
|
+
|
|
392
|
+
if (options[runtime] !== undefined) {
|
|
393
|
+
return options[runtime](runtimeRegisterItem.getGlobalContext())
|
|
394
|
+
}
|
|
395
|
+
if (options.default !== undefined) {
|
|
396
|
+
return options.default()
|
|
397
|
+
} else {
|
|
398
|
+
throw new Error(`Neither runtime-specific nor default handler provided for runtime: ${String(runtime)}`)
|
|
399
|
+
}
|
|
400
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { getDeviceInfo } from "./device.ts"
|
|
2
|
+
import { getFeatureFlags } from "./feature.ts"
|
|
3
|
+
import { getGeoInfo } from "./geo.ts"
|
|
4
|
+
import { getRuntimeFlags } from "./runtime.ts"
|
|
5
|
+
import { getVariableHosted } from "./variable.ts"
|
|
6
|
+
|
|
7
|
+
import type { AnyVariable } from "./variable.ts"
|
|
8
|
+
import type { DeviceInfo } from "./device.ts"
|
|
9
|
+
import type { FeatureFlags } from "./feature.ts"
|
|
10
|
+
import type { GeoInfo } from "./geo.ts"
|
|
11
|
+
import type { RuntimeFlags } from "./runtime.ts"
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Describe a consolidated environment snapshot.
|
|
15
|
+
*/
|
|
16
|
+
export interface Snapshot {
|
|
17
|
+
geo: GeoInfo
|
|
18
|
+
device: DeviceInfo
|
|
19
|
+
runtime: RuntimeFlags
|
|
20
|
+
feature: FeatureFlags
|
|
21
|
+
variable: AnyVariable
|
|
22
|
+
timestamp: number
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Collect a consolidated environment snapshot.
|
|
27
|
+
*/
|
|
28
|
+
export const getSnapshot = async (): Promise<Snapshot> => {
|
|
29
|
+
const geoInfo = await getGeoInfo();
|
|
30
|
+
const deviceInfo = getDeviceInfo(navigator.userAgent);
|
|
31
|
+
const runtimeFlags = getRuntimeFlags();
|
|
32
|
+
const featureFlags = getFeatureFlags();
|
|
33
|
+
const variable = getVariableHosted();
|
|
34
|
+
const timestamp = Date.now();
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
geo: geoInfo,
|
|
38
|
+
device: deviceInfo,
|
|
39
|
+
runtime: runtimeFlags,
|
|
40
|
+
feature: featureFlags,
|
|
41
|
+
variable: variable,
|
|
42
|
+
timestamp: timestamp
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Print the consolidated environment snapshot to console.
|
|
48
|
+
*/
|
|
49
|
+
export const printSnapshot = async (): Promise<void> => {
|
|
50
|
+
const snap = await getSnapshot();
|
|
51
|
+
|
|
52
|
+
console.group('[detect]');
|
|
53
|
+
console.table(snap.geo);
|
|
54
|
+
console.table(snap.device);
|
|
55
|
+
console.table(snap.runtime);
|
|
56
|
+
console.table(snap.feature);
|
|
57
|
+
console.table(snap.variable);
|
|
58
|
+
console.log('time:', new Date(snap.timestamp).toISOString());
|
|
59
|
+
console.groupEnd();
|
|
60
|
+
}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import type { StandardSchemaV1 } from "@standard-schema/spec"
|
|
2
|
+
|
|
3
|
+
import { isPromise } from "#Source/basic/index.ts"
|
|
4
|
+
import { parse } from "@dotenvx/dotenvx"
|
|
5
|
+
import { useRuntimes } from "./runtime.ts"
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Define a key-value map for environment variables.
|
|
9
|
+
*/
|
|
10
|
+
export type AnyVariable = Record<string, string>
|
|
11
|
+
/**
|
|
12
|
+
* Define options for parsing dotenv-style variable content.
|
|
13
|
+
*/
|
|
14
|
+
export interface ParseVariableOptions {
|
|
15
|
+
variableContent: string
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Parses an environment variable string into an object.
|
|
19
|
+
*/
|
|
20
|
+
export const parseVariable = (options: ParseVariableOptions): AnyVariable => {
|
|
21
|
+
const { variableContent } = options;
|
|
22
|
+
const dotenvParseOutput = parse(variableContent);
|
|
23
|
+
return dotenvParseOutput;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Describe a writable host for resolved environment variables.
|
|
28
|
+
*/
|
|
29
|
+
export interface VariableHost {
|
|
30
|
+
/**
|
|
31
|
+
* Can be used to change the default timezone at runtime
|
|
32
|
+
*
|
|
33
|
+
* @see {@link NodeJS.ProcessEnv}, {@link Bun.Env}
|
|
34
|
+
*/
|
|
35
|
+
TZ?: string | undefined
|
|
36
|
+
/**
|
|
37
|
+
* @see {@link Bun.Env}
|
|
38
|
+
*/
|
|
39
|
+
NODE_ENV?: string | undefined
|
|
40
|
+
/**
|
|
41
|
+
* @see {@link ImportMetaEnv}
|
|
42
|
+
*/
|
|
43
|
+
[key: string]: string | undefined
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Get the variable host for the current runtime.
|
|
47
|
+
*/
|
|
48
|
+
export const getVariableHost = (): VariableHost => {
|
|
49
|
+
const variableHost = useRuntimes<VariableHost>({
|
|
50
|
+
browser: (context) => {
|
|
51
|
+
return context.importMeta.env;
|
|
52
|
+
},
|
|
53
|
+
nodejs: (context) => {
|
|
54
|
+
return context.global.process.env;
|
|
55
|
+
},
|
|
56
|
+
bun: (context) => {
|
|
57
|
+
return context.global.process.env;
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
return variableHost
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Define options for loading parsed variables into a host.
|
|
65
|
+
*/
|
|
66
|
+
export interface LoadVariableOptions {
|
|
67
|
+
variable: AnyVariable
|
|
68
|
+
variablePrefixs: string[]
|
|
69
|
+
variableHost: VariableHost
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Define the result of loading variables into host storage.
|
|
73
|
+
*/
|
|
74
|
+
export interface LoadVariableResult {
|
|
75
|
+
variable: AnyVariable
|
|
76
|
+
variableLoaded: AnyVariable
|
|
77
|
+
variableHost: VariableHost
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Load environment variables from the provided `variable` object to the `variableHost`.
|
|
81
|
+
* Variables without prefix will be ignored.
|
|
82
|
+
*
|
|
83
|
+
* @see {@link https://github.com/vitejs/vite/blob/main/packages/vite/src/node/env.ts#L27-L95}
|
|
84
|
+
*/
|
|
85
|
+
export const loadVariable = (options: LoadVariableOptions): LoadVariableResult => {
|
|
86
|
+
const { variable, variablePrefixs, variableHost } = options
|
|
87
|
+
|
|
88
|
+
const variableLoaded: AnyVariable = {}
|
|
89
|
+
|
|
90
|
+
// only keys that start with prefix are exposed to client
|
|
91
|
+
for (const [key, value] of Object.entries(variable)) {
|
|
92
|
+
if (variablePrefixs.some(prefix => key.startsWith(prefix))) {
|
|
93
|
+
variableHost[key] = value
|
|
94
|
+
variableLoaded[key] = value
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// check if there are actual env variables starting with variablePrefix
|
|
99
|
+
// these are typically provided inline and should be prioritized
|
|
100
|
+
for (const key in variableHost) {
|
|
101
|
+
if (variablePrefixs.some(prefix => key.startsWith(prefix))) {
|
|
102
|
+
variableLoaded[key] = String(variableHost[key])
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
variable,
|
|
108
|
+
variableLoaded,
|
|
109
|
+
variableHost
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Define options for validating variables with a standard schema.
|
|
115
|
+
*/
|
|
116
|
+
export interface VerifyVariableOptions<VariableSchema extends StandardSchemaV1> {
|
|
117
|
+
variable: AnyVariable
|
|
118
|
+
variableSchema: VariableSchema
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Verifies the provided variable against the given schema.
|
|
122
|
+
*
|
|
123
|
+
* @see {@link https://github.com/t3-oss/t3-env}
|
|
124
|
+
*/
|
|
125
|
+
export const verifyVariable = <VariableSchema extends StandardSchemaV1>(
|
|
126
|
+
options: VerifyVariableOptions<VariableSchema>
|
|
127
|
+
): StandardSchemaV1.InferOutput<VariableSchema> => {
|
|
128
|
+
const { variable, variableSchema } = options
|
|
129
|
+
|
|
130
|
+
const validateResult = variableSchema['~standard'].validate(variable)
|
|
131
|
+
|
|
132
|
+
if (isPromise(validateResult)) {
|
|
133
|
+
throw new Error("Validation result is a promise, expected synchronous validation.")
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (validateResult.issues !== undefined) {
|
|
137
|
+
throw new Error(`Variable validation failed: ${JSON.stringify(validateResult.issues)}`)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const value = validateResult.value
|
|
141
|
+
return value
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Gets the hosted variable.
|
|
146
|
+
*/
|
|
147
|
+
export const getVariableHosted = (): AnyVariable => {
|
|
148
|
+
const variableHost = getVariableHost();
|
|
149
|
+
// oxlint-disable-next-line no-unsafe-type-assertion
|
|
150
|
+
return variableHost as AnyVariable
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Define options for parsing, loading, and validating variables.
|
|
155
|
+
*/
|
|
156
|
+
export interface GetVariableOptions<VariableSchema extends StandardSchemaV1> {
|
|
157
|
+
variableContent?: ParseVariableOptions["variableContent"] | undefined
|
|
158
|
+
variablePrefixs?: LoadVariableOptions["variablePrefixs"] | undefined
|
|
159
|
+
variableHost?: LoadVariableOptions["variableHost"] | undefined
|
|
160
|
+
variableSchema: VerifyVariableOptions<VariableSchema>["variableSchema"]
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Gets the variable based on the provided options.
|
|
164
|
+
*/
|
|
165
|
+
export const getVariable = <VariableSchema extends StandardSchemaV1>(
|
|
166
|
+
options: GetVariableOptions<VariableSchema>
|
|
167
|
+
): StandardSchemaV1.InferOutput<VariableSchema> => {
|
|
168
|
+
const hasVariableContent = options.variableContent !== undefined;
|
|
169
|
+
|
|
170
|
+
const parseResult = parseVariable({ variableContent: options.variableContent ?? "" })
|
|
171
|
+
|
|
172
|
+
const loadResult = loadVariable({
|
|
173
|
+
variable: parseResult,
|
|
174
|
+
variablePrefixs: options.variablePrefixs ?? [],
|
|
175
|
+
variableHost: options.variableHost ?? getVariableHost()
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
const variableToVerify = hasVariableContent === true
|
|
179
|
+
? loadResult.variableLoaded
|
|
180
|
+
// oxlint-disable-next-line no-unsafe-type-assertion
|
|
181
|
+
: loadResult.variableHost as AnyVariable;
|
|
182
|
+
const verifyResult = verifyVariable({
|
|
183
|
+
variable: variableToVerify,
|
|
184
|
+
variableSchema: options.variableSchema
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
return verifyResult
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Define options used to construct a variable manager.
|
|
192
|
+
*/
|
|
193
|
+
export interface VariableManagerOptions<VariableSchema extends StandardSchemaV1> {
|
|
194
|
+
variableContent?: ParseVariableOptions["variableContent"] | undefined
|
|
195
|
+
variablePrefixs?: LoadVariableOptions["variablePrefixs"] | undefined
|
|
196
|
+
variableHost?: LoadVariableOptions["variableHost"] | undefined
|
|
197
|
+
variableSchema: VariableSchema
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Manage typed environment variables with cached access.
|
|
201
|
+
*/
|
|
202
|
+
export class VariableManager<VariableSchema extends StandardSchemaV1> {
|
|
203
|
+
private options: VariableManagerOptions<VariableSchema>
|
|
204
|
+
private variableSchema: VariableSchema
|
|
205
|
+
private variable: StandardSchemaV1.InferOutput<VariableSchema>
|
|
206
|
+
|
|
207
|
+
constructor(options: VariableManagerOptions<VariableSchema>) {
|
|
208
|
+
this.options = options;
|
|
209
|
+
this.variableSchema = options.variableSchema;
|
|
210
|
+
this.variable = this.getFreshVariable();
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Refresh variables from current options and schema.
|
|
215
|
+
*/
|
|
216
|
+
getFreshVariable(): StandardSchemaV1.InferOutput<VariableSchema> {
|
|
217
|
+
const variable = getVariable({
|
|
218
|
+
variableContent: this.options.variableContent,
|
|
219
|
+
variablePrefixs: this.options.variablePrefixs,
|
|
220
|
+
variableHost: this.options.variableHost,
|
|
221
|
+
variableSchema: this.variableSchema
|
|
222
|
+
});
|
|
223
|
+
return variable;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Return the schema used by this manager.
|
|
228
|
+
*/
|
|
229
|
+
getSchema(): VariableSchema {
|
|
230
|
+
return this.variableSchema;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Return the cached variable snapshot.
|
|
235
|
+
*/
|
|
236
|
+
getVariable(): StandardSchemaV1.InferOutput<VariableSchema> {
|
|
237
|
+
return this.variable;
|
|
238
|
+
}
|
|
239
|
+
}
|