@steve02081504/virtual-console 0.0.6 → 0.0.8

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 (4) hide show
  1. package/browser.mjs +192 -0
  2. package/main.mjs +9 -197
  3. package/node.mjs +197 -0
  4. package/package.json +1 -1
package/browser.mjs ADDED
@@ -0,0 +1,192 @@
1
+ import { FullProxy } from 'full-proxy'
2
+
3
+ /**
4
+ * 存储原始的浏览器 console 对象。
5
+ */
6
+ const originalConsole = window.console
7
+
8
+ /**
9
+ * 格式化 console 参数为字符串。
10
+ * @param {any[]} args - console 方法接收的参数数组。
11
+ * @returns {string} 格式化后的单行字符串。
12
+ */
13
+ function formatArgs(args) {
14
+ return args.map(arg => {
15
+ if (Object(arg) instanceof String) return arg
16
+ if (arg instanceof Error && arg.stack) return arg.stack
17
+ try {
18
+ return JSON.stringify(arg, null, '\t')
19
+ }
20
+ catch {
21
+ return String(arg)
22
+ }
23
+ }).join(' ')
24
+ }
25
+
26
+ /**
27
+ * 创建一个虚拟控制台,用于捕获输出,同时可以选择性地将输出传递给真实的浏览器控制台。
28
+ */
29
+ export class VirtualConsole {
30
+ /** @type {string} - 捕获的所有输出 */
31
+ outputs = ''
32
+
33
+ /** @type {object} - 最终合并后的配置项 */
34
+ options
35
+
36
+ /** @type {Console} - 用于 realConsoleOutput 的底层控制台实例 */
37
+ #base_console
38
+
39
+ /** @private @type {string | null} - 用于 freshLine 功能,记录上一次 freshLine 的 ID */
40
+ #loggedFreshLineId = null
41
+
42
+ /**
43
+ * @param {object} [options={}] - 配置选项。
44
+ * @param {boolean} [options.realConsoleOutput=false] - 如果为 true,则在捕获输出的同时,也调用底层控制台进行实际输出。
45
+ * @param {boolean} [options.recordOutput=true] - 如果为 true,则捕获输出并保存在 outputs 属性中。
46
+ * @param {Console} [options.base_console=window.console] - 用于 realConsoleOutput 的底层控制台实例。
47
+ */
48
+ constructor(options = {}) {
49
+ this.#base_console = options.base_console || originalConsole
50
+ delete options.base_console
51
+
52
+ this.options = {
53
+ realConsoleOutput: false,
54
+ recordOutput: true,
55
+ ...options,
56
+ }
57
+
58
+ const methods = ['log', 'info', 'warn', 'debug', 'error', 'table', 'dir', 'assert', 'count', 'countReset', 'time', 'timeLog', 'timeEnd', 'group', 'groupCollapsed', 'groupEnd']
59
+ for (const method of methods)
60
+ if (typeof this.#base_console[method] === 'function')
61
+ this[method] = (...args) => {
62
+ this.#loggedFreshLineId = null // 任何常规输出都会中断 freshLine 序列
63
+
64
+ if (this.options.recordOutput)
65
+ this.outputs += formatArgs(args) + '\n'
66
+
67
+ // 实际输出
68
+ if (this.options.realConsoleOutput)
69
+ this.#base_console[method](...args)
70
+ }
71
+
72
+
73
+
74
+ this.freshLine = this.freshLine.bind(this)
75
+ this.clear = this.clear.bind(this)
76
+ }
77
+
78
+ /**
79
+ * 在新的异步上下文中执行fn,并将该上下文的控制台替换为此对象。
80
+ * 这是对 Node.js 中 AsyncLocalStorage.run 的浏览器模拟。
81
+ * @template T
82
+ * @overload
83
+ * @param {() => T | Promise<T>} fn - 在新的异步上下文中执行的函数。
84
+ * @returns {Promise<T>} 返回 fn 函数的 Promise 结果。
85
+ */
86
+ /**
87
+ * 将当前“异步上下文”中的控制台替换为此对象。
88
+ * [浏览器限制] 这在浏览器中是全局性的,会影响所有后续代码,直到被再次更改。
89
+ * @overload
90
+ * @returns {void}
91
+ */
92
+ /**
93
+ * 若提供fn,则在新的异步上下文中执行fn,并将fn上下文的控制台替换为此对象。
94
+ * 否则,将当前异步上下文中的控制台替换为此对象。
95
+ * @param {(() => T | Promise<T>) | undefined} [fn]
96
+ * @returns {Promise<T> | void}
97
+ */
98
+ hookAsyncContext(fn) {
99
+ if (fn) return consoleReflectRun(this, fn)
100
+ else consoleReflectSet(this)
101
+ }
102
+
103
+
104
+ /**
105
+ * 在终端中打印一行。
106
+ * [浏览器限制] 由于浏览器控制台不支持 ANSI 光标移动,
107
+ * 此方法无法像在 Node.js 终端中那样覆盖上一行。
108
+ * 它目前等同于 console.log。
109
+ * @param {string} id - 用于标识行的唯一ID (在浏览器中未使用)。
110
+ * @param {...any} args - 要打印的内容。
111
+ */
112
+ freshLine(id, ...args) {
113
+ // 在浏览器中,我们无法移动光标,所以这基本上就是一个 log
114
+ // 我们仍然可以模拟逻辑,以防未来浏览器支持类似功能
115
+ // 注意:我们不像原生版本那样清除上一行,因为做不到
116
+ this.log(...args)
117
+ this.#loggedFreshLineId = id
118
+ }
119
+
120
+ /**
121
+ * 清空捕获的输出,并可以选择性地清空真实控制台。
122
+ */
123
+ clear() {
124
+ this.#loggedFreshLineId = null
125
+ this.outputs = ''
126
+ if (this.options.realConsoleOutput)
127
+ this.#base_console.clear()
128
+
129
+ }
130
+ }
131
+
132
+ export const defaultConsole = new VirtualConsole({
133
+ base_console: originalConsole,
134
+ recordOutput: false,
135
+ realConsoleOutput: true,
136
+ })
137
+
138
+ export const globalConsoleAdditionalProperties = {}
139
+
140
+ // 模拟 AsyncLocalStorage 的上下文存储
141
+ let currentAsyncConsole = null
142
+
143
+ /** @type {() => VirtualConsole} */
144
+ let consoleReflect = () => currentAsyncConsole ?? defaultConsole
145
+
146
+ /** @type {(value: VirtualConsole) => void} */
147
+ let consoleReflectSet = (v) => {
148
+ currentAsyncConsole = v
149
+ }
150
+
151
+ /**
152
+ * @template T
153
+ * @type {(value: VirtualConsole, fn: () => T | Promise<T>) => Promise<T>}
154
+ */
155
+ let consoleReflectRun = async (v, fn) => {
156
+ const previousConsole = currentAsyncConsole
157
+ currentAsyncConsole = v
158
+ try {
159
+ const result = fn()
160
+ return await Promise.resolve(result)
161
+ }
162
+ finally {
163
+ currentAsyncConsole = previousConsole
164
+ }
165
+ }
166
+
167
+ // 暴露设置和获取反射逻辑的函数,以完全匹配原始API
168
+ export function setGlobalConsoleReflect(Reflect, ReflectSet, ReflectRun) {
169
+ consoleReflect = () => Reflect(defaultConsole)
170
+ consoleReflectSet = ReflectSet
171
+ consoleReflectRun = ReflectRun
172
+ }
173
+ export function getGlobalConsoleReflect() {
174
+ return {
175
+ Reflect: consoleReflect,
176
+ ReflectSet: consoleReflectSet,
177
+ ReflectRun: consoleReflectRun
178
+ }
179
+ }
180
+
181
+ /**
182
+ * 导出一个代理对象作为全局 console,它将所有操作委托给当前的活动控制台。
183
+ * 这与原始 Node.js 版本的实现完全相同。
184
+ */
185
+ export const console = globalThis.console = new FullProxy(() => Object.assign({}, globalConsoleAdditionalProperties, consoleReflect()), {
186
+ set: (target, property, value) => {
187
+ target = consoleReflect()
188
+ if (property in target) return Reflect.set(target, property, value)
189
+ globalConsoleAdditionalProperties[property] = value
190
+ return true
191
+ }
192
+ })
package/main.mjs CHANGED
@@ -1,197 +1,9 @@
1
- import { AsyncLocalStorage } from 'node:async_hooks'
2
- import { Console } from 'node:console'
3
- import process from 'node:process'
4
- import { Writable } from 'node:stream'
5
-
6
- import ansiEscapes from 'ansi-escapes'
7
- import { FullProxy } from 'full-proxy'
8
- import supportsAnsi from 'supports-ansi'
9
-
10
- export const consoleAsyncStorage = new AsyncLocalStorage()
11
- const cleanupRegistry = new FinalizationRegistry(cleanupToken => {
12
- const { stream, listener } = cleanupToken
13
- stream.off?.('resize', listener)
14
- })
15
-
16
- /**
17
- * 创建一个虚拟控制台,用于捕获输出,同时可以选择性地将输出传递给真实的控制台。
18
- *
19
- * @extends {Console}
20
- */
21
- export class VirtualConsole extends Console {
22
- /**
23
- * 在新的Async上下文中执行fn,并将fn上下文的控制台替换为此对象。
24
- * @template T
25
- * @overload
26
- * @param {() => T} fn - 在新的Async上下文中执行的函数。
27
- * @returns {Promise<T>} 返回 fn 函数的 Promise 结果。
28
- */
29
- /**
30
- * 将当前Async上下文中的控制台替换为此对象。
31
- * @overload
32
- * @returns {void}
33
- */
34
- /**
35
- * 若提供fn,则在新的Async上下文中执行fn,并将fn上下文的控制台替换为此对象。
36
- * 否则,将当前Async上下文中的控制台替换为此对象。
37
- * @param {(() => T) | undefined} [fn]
38
- * @returns {Promise<T> | void}
39
- */
40
- hookAsyncContext(fn) {
41
- if (fn) return consoleReflectRun(this, fn)
42
- else consoleReflectSet(this)
43
- }
44
- /** @type {string} - 捕获的所有输出 */
45
- outputs = ''
46
-
47
- /** @type {object} - 最终合并后的配置项 */
48
- options
49
-
50
- /** @type {Console} - 用于 realConsoleOutput 的底层控制台实例 */
51
- #base_console
52
-
53
- /** @private @type {string | null} - 用于 freshLine 功能,记录上一次 freshLine 的 ID */
54
- #loggedFreshLineId = null
55
-
56
- /**
57
- * @param {object} [options={}] - 配置选项。
58
- * @param {boolean} [options.realConsoleOutput=false] - 如果为 true,则在捕获输出的同时,也调用底层控制台进行实际输出。
59
- * @param {boolean} [options.recordOutput=true] - 如果为 true,则捕获输出并保存在 outputs 属性中。
60
- * @param {function(Error): void} [options.error_handler=null] - 一个专门处理单个 Error 对象的错误处理器。
61
- * @param {Console} [options.base_console=console] - 用于 realConsoleOutput 的底层控制台实例。
62
- */
63
- constructor(options = {}) {
64
- super(new Writable({ write: () => { } }), new Writable({ write: () => { } }))
65
-
66
- this.base_console = options.base_console || consoleReflect()
67
- delete options.base_console
68
- this.options = {
69
- realConsoleOutput: false,
70
- recordOutput: true,
71
- supportsAnsi: this.#base_console.options?.supportsAnsi || supportsAnsi,
72
- error_handler: null,
73
- ...options,
74
- }
75
- this.freshLine = this.freshLine.bind(this)
76
- this.clear = this.clear.bind(this)
77
- for (const method of ['log', 'info', 'warn', 'debug', 'error']) {
78
- if (!this[method]) continue
79
- const originalMethod = this[method]
80
- this[method] = (...args) => {
81
- if (method == 'error' && this.options.error_handler && args.length === 1 && args[0] instanceof Error) return this.options.error_handler(args[0])
82
- if (!this.options.realConsoleOutput || this.options.recordOutput) return originalMethod.apply(this, args)
83
- this.#loggedFreshLineId = null
84
- return this.#base_console[method](...args)
85
- }
86
- }
87
- }
88
-
89
- get base_console() {
90
- return this.#base_console
91
- }
92
-
93
- set base_console(value) {
94
- this.#base_console = value
95
-
96
- const createVirtualStream = (targetStream) => {
97
- const virtualStream = new Writable({
98
- write: (chunk, encoding, callback) => {
99
- this.#loggedFreshLineId = null
100
-
101
- if (this.options.recordOutput)
102
- this.outputs += chunk.toString()
103
- if (this.options.realConsoleOutput)
104
- targetStream.write(chunk, encoding, callback)
105
- else
106
- callback()
107
- },
108
- })
109
-
110
- if (targetStream.isTTY) {
111
- Object.defineProperties(virtualStream, {
112
- isTTY: { value: true, configurable: true, writable: false, enumerable: true },
113
- columns: { get: () => targetStream.columns, configurable: true, enumerable: true },
114
- rows: { get: () => targetStream.rows, configurable: true, enumerable: true },
115
- getColorDepth: { get: () => targetStream.getColorDepth.bind(targetStream), configurable: true, enumerable: true },
116
- hasColors: { get: () => targetStream.hasColors.bind(targetStream), configurable: true, enumerable: true },
117
- })
118
-
119
- const virtualStreamRef = new WeakRef(virtualStream)
120
-
121
- const resizeListener = () => {
122
- virtualStreamRef.deref()?.emit('resize')
123
- }
124
-
125
- targetStream.on?.('resize', resizeListener)
126
-
127
- cleanupRegistry.register(this, {
128
- stream: targetStream,
129
- listener: resizeListener,
130
- }, this)
131
- }
132
-
133
- return virtualStream
134
- }
135
-
136
- this._stdout = createVirtualStream(this.#base_console?._stdout || process.stdout)
137
- this._stderr = createVirtualStream(this.#base_console?._stderr || process.stderr)
138
- }
139
-
140
- /**
141
- * 在终端中打印一行,如果前一次调用也是具有相同ID的freshLine,
142
- * 则会覆盖上一行而不是打印新行。
143
- * @param {string} id - 用于标识可覆盖行的唯一ID。
144
- * @param {...any} args - 要打印的内容。
145
- */
146
- freshLine(id, ...args) {
147
- if (this.options.supportsAnsi && this.#loggedFreshLineId === id)
148
- this._stdout.write(ansiEscapes.cursorUp(1) + ansiEscapes.eraseLine)
149
-
150
- this.log(...args)
151
- this.#loggedFreshLineId = id
152
- }
153
-
154
- clear() {
155
- this.#loggedFreshLineId = null
156
- this.outputs = ''
157
- if (this.options.realConsoleOutput)
158
- this.#base_console.clear()
159
- }
160
- }
161
-
162
- const originalConsole = globalThis.console
163
- export const defaultConsole = new VirtualConsole({ base_console: originalConsole, recordOutput: false, realConsoleOutput: true })
164
- export const globalConsoleAdditionalProperties = {}
165
- /** @type {() => VirtualConsole} */
166
- let consoleReflect = () => consoleAsyncStorage.getStore() ?? defaultConsole
167
- /** @type {(value: VirtualConsole) => void} */
168
- let consoleReflectSet = (v) => consoleAsyncStorage.enterWith(v)
169
- /** @type {(value: VirtualConsole, fn: () => T) => Promise<T>} */
170
- let consoleReflectRun = (v, fn) => consoleAsyncStorage.run(v, fn)
171
- /**
172
- * 设置全局控制台反射逻辑
173
- * @template T
174
- * @param {(console: Console) => Console} Reflect
175
- * @param {(value: Console) => void} ReflectSet
176
- * @param {(value: Console, fn: () => T) => Promise<T>} ReflectRun
177
- */
178
- export function setGlobalConsoleReflect(Reflect, ReflectSet, ReflectRun) {
179
- consoleReflect = () => Reflect(defaultConsole)
180
- consoleReflectSet = ReflectSet
181
- consoleReflectRun = ReflectRun
182
- }
183
- export function getGlobalConsoleReflect() {
184
- return {
185
- Reflect: consoleReflect,
186
- ReflectSet: consoleReflectSet,
187
- ReflectRun: consoleReflectRun
188
- }
189
- }
190
- export const console = globalThis.console = new FullProxy(() => Object.assign({}, globalConsoleAdditionalProperties, consoleReflect()), {
191
- set: (target, property, value) => {
192
- target = consoleReflect()
193
- if (property in target) return Reflect.set(target, property, value)
194
- globalConsoleAdditionalProperties[property] = value
195
- return true
196
- }
197
- })
1
+ const module = await import(globalThis.document ? './browser.mjs' : './node.mjs')
2
+
3
+ export const consoleAsyncStorage = module.consoleAsyncStorage
4
+ export const VirtualConsole = module.VirtualConsole
5
+ export const defaultConsole = module.defaultConsole
6
+ export const globalConsoleAdditionalProperties = module.globalConsoleAdditionalProperties
7
+ export const setGlobalConsoleReflect = module.setGlobalConsoleReflect
8
+ export const getGlobalConsoleReflect = module.getGlobalConsoleReflect
9
+ export const console = module.console
package/node.mjs ADDED
@@ -0,0 +1,197 @@
1
+ import { AsyncLocalStorage } from 'node:async_hooks'
2
+ import { Console } from 'node:console'
3
+ import process from 'node:process'
4
+ import { Writable } from 'node:stream'
5
+
6
+ import ansiEscapes from 'ansi-escapes'
7
+ import { FullProxy } from 'full-proxy'
8
+ import supportsAnsi from 'supports-ansi'
9
+
10
+ export const consoleAsyncStorage = new AsyncLocalStorage()
11
+ const cleanupRegistry = new FinalizationRegistry(cleanupToken => {
12
+ const { stream, listener } = cleanupToken
13
+ stream.off?.('resize', listener)
14
+ })
15
+
16
+ /**
17
+ * 创建一个虚拟控制台,用于捕获输出,同时可以选择性地将输出传递给真实的控制台。
18
+ *
19
+ * @extends {Console}
20
+ */
21
+ export class VirtualConsole extends Console {
22
+ /**
23
+ * 在新的Async上下文中执行fn,并将fn上下文的控制台替换为此对象。
24
+ * @template T
25
+ * @overload
26
+ * @param {() => T} fn - 在新的Async上下文中执行的函数。
27
+ * @returns {Promise<T>} 返回 fn 函数的 Promise 结果。
28
+ */
29
+ /**
30
+ * 将当前Async上下文中的控制台替换为此对象。
31
+ * @overload
32
+ * @returns {void}
33
+ */
34
+ /**
35
+ * 若提供fn,则在新的Async上下文中执行fn,并将fn上下文的控制台替换为此对象。
36
+ * 否则,将当前Async上下文中的控制台替换为此对象。
37
+ * @param {(() => T) | undefined} [fn]
38
+ * @returns {Promise<T> | void}
39
+ */
40
+ hookAsyncContext(fn) {
41
+ if (fn) return consoleReflectRun(this, fn)
42
+ else consoleReflectSet(this)
43
+ }
44
+ /** @type {string} - 捕获的所有输出 */
45
+ outputs = ''
46
+
47
+ /** @type {object} - 最终合并后的配置项 */
48
+ options
49
+
50
+ /** @type {Console} - 用于 realConsoleOutput 的底层控制台实例 */
51
+ #base_console
52
+
53
+ /** @private @type {string | null} - 用于 freshLine 功能,记录上一次 freshLine 的 ID */
54
+ #loggedFreshLineId = null
55
+
56
+ /**
57
+ * @param {object} [options={}] - 配置选项。
58
+ * @param {boolean} [options.realConsoleOutput=false] - 如果为 true,则在捕获输出的同时,也调用底层控制台进行实际输出。
59
+ * @param {boolean} [options.recordOutput=true] - 如果为 true,则捕获输出并保存在 outputs 属性中。
60
+ * @param {function(Error): void} [options.error_handler=null] - 一个专门处理单个 Error 对象的错误处理器。
61
+ * @param {Console} [options.base_console=console] - 用于 realConsoleOutput 的底层控制台实例。
62
+ */
63
+ constructor(options = {}) {
64
+ super(new Writable({ write: () => { } }), new Writable({ write: () => { } }))
65
+
66
+ this.base_console = options.base_console || consoleReflect()
67
+ delete options.base_console
68
+ this.options = {
69
+ realConsoleOutput: false,
70
+ recordOutput: true,
71
+ supportsAnsi: this.#base_console.options?.supportsAnsi || supportsAnsi,
72
+ error_handler: null,
73
+ ...options,
74
+ }
75
+ this.freshLine = this.freshLine.bind(this)
76
+ this.clear = this.clear.bind(this)
77
+ for (const method of ['log', 'info', 'warn', 'debug', 'error']) {
78
+ if (!this[method]) continue
79
+ const originalMethod = this[method]
80
+ this[method] = (...args) => {
81
+ if (method == 'error' && this.options.error_handler && args.length === 1 && args[0] instanceof Error) return this.options.error_handler(args[0])
82
+ if (!this.options.realConsoleOutput || this.options.recordOutput) return originalMethod.apply(this, args)
83
+ this.#loggedFreshLineId = null
84
+ return this.#base_console[method](...args)
85
+ }
86
+ }
87
+ }
88
+
89
+ get base_console() {
90
+ return this.#base_console
91
+ }
92
+
93
+ set base_console(value) {
94
+ this.#base_console = value
95
+
96
+ const createVirtualStream = (targetStream) => {
97
+ const virtualStream = new Writable({
98
+ write: (chunk, encoding, callback) => {
99
+ this.#loggedFreshLineId = null
100
+
101
+ if (this.options.recordOutput)
102
+ this.outputs += chunk.toString()
103
+ if (this.options.realConsoleOutput)
104
+ targetStream.write(chunk, encoding, callback)
105
+ else
106
+ callback()
107
+ },
108
+ })
109
+
110
+ if (targetStream.isTTY) {
111
+ Object.defineProperties(virtualStream, {
112
+ isTTY: { value: true, configurable: true, writable: false, enumerable: true },
113
+ columns: { get: () => targetStream.columns, configurable: true, enumerable: true },
114
+ rows: { get: () => targetStream.rows, configurable: true, enumerable: true },
115
+ getColorDepth: { get: () => targetStream.getColorDepth.bind(targetStream), configurable: true, enumerable: true },
116
+ hasColors: { get: () => targetStream.hasColors.bind(targetStream), configurable: true, enumerable: true },
117
+ })
118
+
119
+ const virtualStreamRef = new WeakRef(virtualStream)
120
+
121
+ const resizeListener = () => {
122
+ virtualStreamRef.deref()?.emit('resize')
123
+ }
124
+
125
+ targetStream.on?.('resize', resizeListener)
126
+
127
+ cleanupRegistry.register(this, {
128
+ stream: targetStream,
129
+ listener: resizeListener,
130
+ }, this)
131
+ }
132
+
133
+ return virtualStream
134
+ }
135
+
136
+ this._stdout = createVirtualStream(this.#base_console?._stdout || process.stdout)
137
+ this._stderr = createVirtualStream(this.#base_console?._stderr || process.stderr)
138
+ }
139
+
140
+ /**
141
+ * 在终端中打印一行,如果前一次调用也是具有相同ID的freshLine,
142
+ * 则会覆盖上一行而不是打印新行。
143
+ * @param {string} id - 用于标识可覆盖行的唯一ID。
144
+ * @param {...any} args - 要打印的内容。
145
+ */
146
+ freshLine(id, ...args) {
147
+ if (this.options.supportsAnsi && this.#loggedFreshLineId === id)
148
+ this._stdout.write(ansiEscapes.cursorUp(1) + ansiEscapes.eraseLine)
149
+
150
+ this.log(...args)
151
+ this.#loggedFreshLineId = id
152
+ }
153
+
154
+ clear() {
155
+ this.#loggedFreshLineId = null
156
+ this.outputs = ''
157
+ if (this.options.realConsoleOutput)
158
+ this.#base_console.clear()
159
+ }
160
+ }
161
+
162
+ const originalConsole = globalThis.console
163
+ export const defaultConsole = new VirtualConsole({ base_console: originalConsole, recordOutput: false, realConsoleOutput: true })
164
+ export const globalConsoleAdditionalProperties = {}
165
+ /** @type {() => VirtualConsole} */
166
+ let consoleReflect = () => consoleAsyncStorage.getStore() ?? defaultConsole
167
+ /** @type {(value: VirtualConsole) => void} */
168
+ let consoleReflectSet = (v) => consoleAsyncStorage.enterWith(v)
169
+ /** @type {(value: VirtualConsole, fn: () => T) => Promise<T>} */
170
+ let consoleReflectRun = (v, fn) => consoleAsyncStorage.run(v, fn)
171
+ /**
172
+ * 设置全局控制台反射逻辑
173
+ * @template T
174
+ * @param {(console: Console) => Console} Reflect
175
+ * @param {(value: Console) => void} ReflectSet
176
+ * @param {(value: Console, fn: () => T) => Promise<T>} ReflectRun
177
+ */
178
+ export function setGlobalConsoleReflect(Reflect, ReflectSet, ReflectRun) {
179
+ consoleReflect = () => Reflect(defaultConsole)
180
+ consoleReflectSet = ReflectSet
181
+ consoleReflectRun = ReflectRun
182
+ }
183
+ export function getGlobalConsoleReflect() {
184
+ return {
185
+ Reflect: consoleReflect,
186
+ ReflectSet: consoleReflectSet,
187
+ ReflectRun: consoleReflectRun
188
+ }
189
+ }
190
+ export const console = globalThis.console = new FullProxy(() => Object.assign({}, globalConsoleAdditionalProperties, consoleReflect()), {
191
+ set: (target, property, value) => {
192
+ target = consoleReflect()
193
+ if (property in target) return Reflect.set(target, property, value)
194
+ globalConsoleAdditionalProperties[property] = value
195
+ return true
196
+ }
197
+ })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@steve02081504/virtual-console",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
4
4
  "description": "A virtual console for capturing and manipulating terminal output.",
5
5
  "main": "main.mjs",
6
6
  "type": "module",