@planet-matrix/mobius-model 0.4.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.
Files changed (179) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +134 -21
  3. package/dist/index.js +45 -4
  4. package/dist/index.js.map +186 -11
  5. package/oxlint.config.ts +6 -0
  6. package/package.json +16 -10
  7. package/src/abort/README.md +92 -0
  8. package/src/abort/abort-manager.ts +278 -0
  9. package/src/abort/abort-signal-listener-manager.ts +81 -0
  10. package/src/abort/index.ts +2 -0
  11. package/src/basic/README.md +69 -117
  12. package/src/basic/enhance.ts +10 -0
  13. package/src/basic/function.ts +81 -62
  14. package/src/basic/index.ts +2 -0
  15. package/src/basic/is.ts +152 -71
  16. package/src/basic/object.ts +82 -0
  17. package/src/basic/promise.ts +29 -8
  18. package/src/basic/string.ts +2 -33
  19. package/src/color/README.md +105 -0
  20. package/src/color/index.ts +3 -0
  21. package/src/color/internal.ts +42 -0
  22. package/src/color/rgb/analyze.ts +236 -0
  23. package/src/color/rgb/construct.ts +130 -0
  24. package/src/color/rgb/convert.ts +227 -0
  25. package/src/color/rgb/derive.ts +303 -0
  26. package/src/color/rgb/index.ts +6 -0
  27. package/src/color/rgb/internal.ts +208 -0
  28. package/src/color/rgb/parse.ts +302 -0
  29. package/src/color/rgb/serialize.ts +144 -0
  30. package/src/color/types.ts +57 -0
  31. package/src/color/xyz/analyze.ts +80 -0
  32. package/src/color/xyz/construct.ts +19 -0
  33. package/src/color/xyz/convert.ts +71 -0
  34. package/src/color/xyz/index.ts +3 -0
  35. package/src/color/xyz/internal.ts +23 -0
  36. package/src/css/README.md +93 -0
  37. package/src/css/class.ts +559 -0
  38. package/src/css/index.ts +1 -0
  39. package/src/encoding/README.md +92 -0
  40. package/src/encoding/base64.ts +107 -0
  41. package/src/encoding/index.ts +1 -0
  42. package/src/environment/README.md +97 -0
  43. package/src/environment/basic.ts +26 -0
  44. package/src/environment/device.ts +311 -0
  45. package/src/environment/feature.ts +285 -0
  46. package/src/environment/geo.ts +337 -0
  47. package/src/environment/index.ts +7 -0
  48. package/src/environment/runtime.ts +400 -0
  49. package/src/environment/snapshot.ts +60 -0
  50. package/src/environment/variable.ts +239 -0
  51. package/src/event/README.md +90 -0
  52. package/src/event/class-event-proxy.ts +228 -0
  53. package/src/event/common.ts +19 -0
  54. package/src/event/event-manager.ts +203 -0
  55. package/src/event/index.ts +4 -0
  56. package/src/event/instance-event-proxy.ts +186 -0
  57. package/src/event/internal.ts +24 -0
  58. package/src/exception/README.md +96 -0
  59. package/src/exception/browser.ts +219 -0
  60. package/src/exception/index.ts +4 -0
  61. package/src/exception/nodejs.ts +169 -0
  62. package/src/exception/normalize.ts +106 -0
  63. package/src/exception/types.ts +99 -0
  64. package/src/identifier/README.md +92 -0
  65. package/src/identifier/id.ts +119 -0
  66. package/src/identifier/index.ts +2 -0
  67. package/src/identifier/uuid.ts +187 -0
  68. package/src/index.ts +18 -1
  69. package/src/log/README.md +79 -0
  70. package/src/log/index.ts +5 -0
  71. package/src/log/log-emitter.ts +72 -0
  72. package/src/log/log-record.ts +10 -0
  73. package/src/log/log-scheduler.ts +74 -0
  74. package/src/log/log-type.ts +8 -0
  75. package/src/log/logger.ts +543 -0
  76. package/src/orchestration/README.md +89 -0
  77. package/src/orchestration/coordination/barrier.ts +214 -0
  78. package/src/orchestration/coordination/count-down-latch.ts +215 -0
  79. package/src/orchestration/coordination/errors.ts +98 -0
  80. package/src/orchestration/coordination/index.ts +16 -0
  81. package/src/orchestration/coordination/internal/wait-constraints.ts +95 -0
  82. package/src/orchestration/coordination/internal/wait-queue.ts +109 -0
  83. package/src/orchestration/coordination/keyed-lock.ts +168 -0
  84. package/src/orchestration/coordination/mutex.ts +257 -0
  85. package/src/orchestration/coordination/permit.ts +127 -0
  86. package/src/orchestration/coordination/read-write-lock.ts +444 -0
  87. package/src/orchestration/coordination/semaphore.ts +280 -0
  88. package/src/orchestration/index.ts +1 -0
  89. package/src/random/README.md +78 -0
  90. package/src/random/index.ts +1 -0
  91. package/src/random/string.ts +35 -0
  92. package/src/reactor/README.md +4 -0
  93. package/src/reactor/reactor-core/primitive.ts +9 -9
  94. package/src/reactor/reactor-core/reactive-system.ts +5 -5
  95. package/src/singleton/README.md +79 -0
  96. package/src/singleton/factory.ts +55 -0
  97. package/src/singleton/index.ts +2 -0
  98. package/src/singleton/manager.ts +204 -0
  99. package/src/storage/README.md +107 -0
  100. package/src/storage/index.ts +1 -0
  101. package/src/storage/table.ts +449 -0
  102. package/src/timer/README.md +86 -0
  103. package/src/timer/expiration/expiration-manager.ts +594 -0
  104. package/src/timer/expiration/index.ts +3 -0
  105. package/src/timer/expiration/min-heap.ts +208 -0
  106. package/src/timer/expiration/remaining-manager.ts +241 -0
  107. package/src/timer/index.ts +1 -0
  108. package/src/type/README.md +54 -307
  109. package/src/type/class.ts +2 -2
  110. package/src/type/index.ts +14 -14
  111. package/src/type/is.ts +265 -2
  112. package/src/type/object.ts +37 -0
  113. package/src/type/string.ts +7 -2
  114. package/src/type/tuple.ts +6 -6
  115. package/src/type/union.ts +16 -0
  116. package/src/web/README.md +77 -0
  117. package/src/web/capture.ts +35 -0
  118. package/src/web/clipboard.ts +97 -0
  119. package/src/web/dom.ts +117 -0
  120. package/src/web/download.ts +16 -0
  121. package/src/web/event.ts +46 -0
  122. package/src/web/index.ts +10 -0
  123. package/src/web/local-storage.ts +113 -0
  124. package/src/web/location.ts +28 -0
  125. package/src/web/permission.ts +172 -0
  126. package/src/web/script-loader.ts +432 -0
  127. package/tests/unit/abort/abort-manager.spec.ts +225 -0
  128. package/tests/unit/abort/abort-signal-listener-manager.spec.ts +62 -0
  129. package/tests/unit/basic/array.spec.ts +1 -1
  130. package/tests/unit/basic/object.spec.ts +32 -1
  131. package/tests/unit/basic/stream.spec.ts +1 -1
  132. package/tests/unit/basic/string.spec.ts +0 -9
  133. package/tests/unit/color/rgb/analyze.spec.ts +110 -0
  134. package/tests/unit/color/rgb/construct.spec.ts +56 -0
  135. package/tests/unit/color/rgb/convert.spec.ts +60 -0
  136. package/tests/unit/color/rgb/derive.spec.ts +103 -0
  137. package/tests/unit/color/rgb/parse.spec.ts +66 -0
  138. package/tests/unit/color/rgb/serialize.spec.ts +46 -0
  139. package/tests/unit/color/xyz/analyze.spec.ts +33 -0
  140. package/tests/unit/color/xyz/construct.spec.ts +10 -0
  141. package/tests/unit/color/xyz/convert.spec.ts +18 -0
  142. package/tests/unit/css/class.spec.ts +157 -0
  143. package/tests/unit/encoding/base64.spec.ts +40 -0
  144. package/tests/unit/environment/basic.spec.ts +20 -0
  145. package/tests/unit/environment/device.spec.ts +146 -0
  146. package/tests/unit/environment/feature.spec.ts +388 -0
  147. package/tests/unit/environment/geo.spec.ts +111 -0
  148. package/tests/unit/environment/runtime.spec.ts +364 -0
  149. package/tests/unit/environment/snapshot.spec.ts +4 -0
  150. package/tests/unit/environment/variable.spec.ts +190 -0
  151. package/tests/unit/event/class-event-proxy.spec.ts +225 -0
  152. package/tests/unit/event/event-manager.spec.ts +246 -0
  153. package/tests/unit/event/instance-event-proxy.spec.ts +187 -0
  154. package/tests/unit/exception/browser.spec.ts +213 -0
  155. package/tests/unit/exception/nodejs.spec.ts +144 -0
  156. package/tests/unit/exception/normalize.spec.ts +57 -0
  157. package/tests/unit/identifier/id.spec.ts +71 -0
  158. package/tests/unit/identifier/uuid.spec.ts +85 -0
  159. package/tests/unit/log/log-emitter.spec.ts +33 -0
  160. package/tests/unit/log/log-scheduler.spec.ts +40 -0
  161. package/tests/unit/log/log-type.spec.ts +7 -0
  162. package/tests/unit/log/logger.spec.ts +222 -0
  163. package/tests/unit/orchestration/coordination/barrier.spec.ts +96 -0
  164. package/tests/unit/orchestration/coordination/count-down-latch.spec.ts +63 -0
  165. package/tests/unit/orchestration/coordination/errors.spec.ts +29 -0
  166. package/tests/unit/orchestration/coordination/keyed-lock.spec.ts +109 -0
  167. package/tests/unit/orchestration/coordination/mutex.spec.ts +132 -0
  168. package/tests/unit/orchestration/coordination/permit.spec.ts +43 -0
  169. package/tests/unit/orchestration/coordination/read-write-lock.spec.ts +154 -0
  170. package/tests/unit/orchestration/coordination/semaphore.spec.ts +135 -0
  171. package/tests/unit/random/string.spec.ts +11 -0
  172. package/tests/unit/reactor/alien-signals-effect.spec.ts +11 -10
  173. package/tests/unit/reactor/preact-signal.spec.ts +1 -2
  174. package/tests/unit/singleton/singleton.spec.ts +49 -0
  175. package/tests/unit/storage/table.spec.ts +620 -0
  176. package/tests/unit/timer/expiration/expiration-manager.spec.ts +464 -0
  177. package/tests/unit/timer/expiration/min-heap.spec.ts +71 -0
  178. package/tests/unit/timer/expiration/remaining-manager.spec.ts +234 -0
  179. package/.oxlintrc.json +0 -5
@@ -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
+ }