@arcticnotes/node-wsh 0.0.10 → 0.0.11
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/lib/node/node-wsh.js +26 -70
- package/lib/wsh/host.js +23 -28
- package/lib/wsh/host.vbs +9 -0
- package/lib/wsh/host.wsf +4 -0
- package/package.json +1 -1
package/lib/node/node-wsh.js
CHANGED
|
@@ -3,8 +3,8 @@ import PATH from 'node:path';
|
|
|
3
3
|
import { Syncline} from "@arcticnotes/syncline";
|
|
4
4
|
|
|
5
5
|
const COMMAND = 'cscript.exe';
|
|
6
|
-
const ARGS = [ '//
|
|
7
|
-
const SCRIPT_FILE = PATH.join( PATH.dirname( import.meta.dirname), 'wsh', 'host.
|
|
6
|
+
const ARGS = [ '//NoLogo'];
|
|
7
|
+
const SCRIPT_FILE = PATH.join( PATH.dirname( import.meta.dirname), 'wsh', 'host.wsf');
|
|
8
8
|
|
|
9
9
|
export class WindowsScriptingHost extends EventEmitter {
|
|
10
10
|
|
|
@@ -17,11 +17,11 @@ export class WindowsScriptingHost extends EventEmitter {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
#syncline;
|
|
20
|
+
#closed = false;
|
|
20
21
|
#finalizer = new FinalizationRegistry( this.#finalized.bind( this));
|
|
21
22
|
#ref2proxy = new Map();
|
|
22
23
|
#proxy2ref = new WeakMap();
|
|
23
|
-
|
|
24
|
-
#objectHandler = {
|
|
24
|
+
#handler = {
|
|
25
25
|
|
|
26
26
|
wsh: this,
|
|
27
27
|
|
|
@@ -49,22 +49,11 @@ export class WindowsScriptingHost extends EventEmitter {
|
|
|
49
49
|
default: throw new Error( `unknown status: ${ output[ 0]}`);
|
|
50
50
|
}
|
|
51
51
|
},
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
#functionHandler = {
|
|
55
52
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
get( target, prop) {
|
|
59
|
-
if( prop === Symbol.toPrimitive)
|
|
60
|
-
return () => `ref#${ target.ref}`;
|
|
61
|
-
return undefined;
|
|
62
|
-
},
|
|
63
|
-
|
|
64
|
-
apply( target, thisArg, argumentList) {
|
|
53
|
+
apply( target, thisArg, argumentsList) {
|
|
65
54
|
const encodedTarget = this.wsh.#encode( target.proxy);
|
|
66
55
|
const encodedThisArg = this.wsh.#encode( thisArg);
|
|
67
|
-
const encodedArgumentList = this.wsh.#encode(
|
|
56
|
+
const encodedArgumentList = this.wsh.#encode( [ ...argumentsList]); // argumentsList may not be instanceof Array
|
|
68
57
|
const output = JSON.parse( this.wsh.#syncline.exchange( JSON.stringify( [ 'apply', encodedTarget, encodedThisArg, encodedArgumentList])));
|
|
69
58
|
switch( output[ 0]) {
|
|
70
59
|
case 'value': return this.wsh.#decode( output[ 1]);
|
|
@@ -73,9 +62,9 @@ export class WindowsScriptingHost extends EventEmitter {
|
|
|
73
62
|
}
|
|
74
63
|
},
|
|
75
64
|
|
|
76
|
-
construct( target,
|
|
65
|
+
construct( target, argumentsList) {
|
|
77
66
|
const encodedTarget = this.wsh.#encode( target.proxy);
|
|
78
|
-
const encodedArgumentList = this.wsh.#encode(
|
|
67
|
+
const encodedArgumentList = this.wsh.#encode( [ ...argumentsList]); // argumentsList may not be instanceof Array
|
|
79
68
|
const output = JSON.parse( this.wsh.#syncline.exchange( JSON.stringify( [ 'construct', encodedTarget, encodedArgumentList])));
|
|
80
69
|
switch( output[ 0]) {
|
|
81
70
|
case 'value': return this.wsh.#decode( output[ 1]);
|
|
@@ -111,25 +100,18 @@ export class WindowsScriptingHost extends EventEmitter {
|
|
|
111
100
|
}
|
|
112
101
|
|
|
113
102
|
async disconnect() {
|
|
103
|
+
this.#closed = true;
|
|
114
104
|
await this.#syncline.close();
|
|
115
105
|
}
|
|
116
106
|
|
|
117
|
-
#
|
|
118
|
-
return this.#getOrCreate( ref, this.#objectHandler);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
#getOrCreateFunction( ref) {
|
|
122
|
-
return this.#getOrCreate( ref, this.#functionHandler);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
#getOrCreate( ref, handler) {
|
|
107
|
+
#getOrCreate( ref) {
|
|
126
108
|
const existingWeakRef = this.#ref2proxy.get( ref);
|
|
127
109
|
const existingProxy = existingWeakRef && existingWeakRef.deref();
|
|
128
110
|
if( existingProxy)
|
|
129
111
|
return existingProxy;
|
|
130
112
|
|
|
131
|
-
const target =
|
|
132
|
-
const newProxy = new Proxy( target, handler);
|
|
113
|
+
const target = new RemoteObject( ref);
|
|
114
|
+
const newProxy = new Proxy( target, this.#handler);
|
|
133
115
|
target.proxy = newProxy;
|
|
134
116
|
this.#ref2proxy.set( ref, new WeakRef( newProxy)); // may be overwriting a dead WeakRef
|
|
135
117
|
this.#proxy2ref.set( newProxy, ref);
|
|
@@ -141,7 +123,8 @@ export class WindowsScriptingHost extends EventEmitter {
|
|
|
141
123
|
#finalized( ref) {
|
|
142
124
|
if( this.#ref2proxy.get( ref).deref() === undefined) // otherwise, it's overwritten by a refreshed proxy
|
|
143
125
|
this.#ref2proxy.delete( ref);
|
|
144
|
-
|
|
126
|
+
if( !this.#closed)
|
|
127
|
+
this.#syncline.exchange( JSON.stringify( [ 'unref', ref]));
|
|
145
128
|
this.emit( 'unref', ref);
|
|
146
129
|
}
|
|
147
130
|
|
|
@@ -156,27 +139,24 @@ export class WindowsScriptingHost extends EventEmitter {
|
|
|
156
139
|
case 'object':
|
|
157
140
|
if( decoded === null)
|
|
158
141
|
return decoded;
|
|
159
|
-
if( decoded
|
|
142
|
+
if( Array.isArray( decoded)) {
|
|
160
143
|
const encoded = [];
|
|
161
144
|
for( const item of decoded)
|
|
162
145
|
encoded.push( this.#encode( item));
|
|
163
146
|
return encoded;
|
|
164
147
|
}
|
|
165
|
-
if( decoded instanceof RemoteObject) {
|
|
166
|
-
const objref = this.#proxy2ref.get( decoded);
|
|
167
|
-
if( objref === undefined) // not because garbage-collected, because clearly `decoded` is still alive
|
|
168
|
-
throw new Error( `remote object reference not found: ${ decoded}`);
|
|
169
|
-
return { type: 'objref', value: objref};
|
|
170
|
-
}
|
|
171
148
|
const encoded = { type: 'object', value: {}};
|
|
172
149
|
for( const [ name, value] of Object.entries( decoded))
|
|
173
150
|
encoded.value[ name] = this.#encode( value);
|
|
174
151
|
return encoded;
|
|
175
152
|
case 'function':
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
153
|
+
if( decoded instanceof RemoteObject) {
|
|
154
|
+
const ref = this.#proxy2ref.get( decoded);
|
|
155
|
+
if( ref === undefined) // not because garbage-collected, because clearly `decoded` is still alive
|
|
156
|
+
throw new Error( `remote object reference not found: ${ decoded}`);
|
|
157
|
+
return { type: 'ref', value: ref};
|
|
158
|
+
}
|
|
159
|
+
throw new Error( `functions from node cannot be sent: ${ decoded}`);
|
|
180
160
|
case 'bigint':
|
|
181
161
|
case 'symbol':
|
|
182
162
|
default:
|
|
@@ -193,7 +173,7 @@ export class WindowsScriptingHost extends EventEmitter {
|
|
|
193
173
|
case 'object':
|
|
194
174
|
if( encoded === null)
|
|
195
175
|
return encoded;
|
|
196
|
-
if( encoded
|
|
176
|
+
if( Array.isArray( encoded)) {
|
|
197
177
|
const decoded = [];
|
|
198
178
|
for( const item of encoded)
|
|
199
179
|
decoded.push( this.#decode( item));
|
|
@@ -207,10 +187,8 @@ export class WindowsScriptingHost extends EventEmitter {
|
|
|
207
187
|
for( const [ name, value] of Object.entries( encoded.value))
|
|
208
188
|
decoded[ name] = this.#decode( value);
|
|
209
189
|
return decoded;
|
|
210
|
-
case '
|
|
211
|
-
return this.#
|
|
212
|
-
case 'funref':
|
|
213
|
-
return this.#getOrCreateFunction( encoded.value);
|
|
190
|
+
case 'ref':
|
|
191
|
+
return this.#getOrCreate( encoded.value);
|
|
214
192
|
default:
|
|
215
193
|
throw new Error( `illegal value: ${ encoded}`);
|
|
216
194
|
}
|
|
@@ -224,29 +202,7 @@ export class WindowsScriptingHost extends EventEmitter {
|
|
|
224
202
|
}
|
|
225
203
|
}
|
|
226
204
|
|
|
227
|
-
class RemoteObject {
|
|
228
|
-
|
|
229
|
-
#ref;
|
|
230
|
-
#proxy;
|
|
231
|
-
|
|
232
|
-
constructor( ref) {
|
|
233
|
-
this.#ref = ref;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
get ref() {
|
|
237
|
-
return this.#ref;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
get proxy() {
|
|
241
|
-
return this.#proxy;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
set proxy( proxy) {
|
|
245
|
-
this.#proxy = proxy;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
class RemoteFunction extends Function {
|
|
205
|
+
class RemoteObject extends Function {
|
|
250
206
|
|
|
251
207
|
#ref;
|
|
252
208
|
#proxy;
|
package/lib/wsh/host.js
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
// This file is in JScript, not in JavaScript. It is executed in Windows Scripting Host (WSH).
|
|
2
2
|
|
|
3
|
+
function CreateVBArray( jsArray) {
|
|
4
|
+
var dict = new ActiveXObject( 'Scripting.Dictionary');
|
|
5
|
+
for( var i = 0; i < jsArray.length; i++)
|
|
6
|
+
dict.Add( i, jsArray[ i]);
|
|
7
|
+
dict.Add( 'length', jsArray.length);
|
|
8
|
+
return Dict2VBArray( dict);
|
|
9
|
+
}
|
|
10
|
+
|
|
3
11
|
var FSO = new ActiveXObject( 'Scripting.FileSystemObject');
|
|
4
12
|
var OBJECT_TOSTRING = Object.toString();
|
|
5
13
|
var GLOBAL = this;
|
|
@@ -34,21 +42,12 @@ function decode( encoded) {
|
|
|
34
42
|
for( i in encoded.value)
|
|
35
43
|
decoded[ i] = decode( encoded.value[ i]);
|
|
36
44
|
return decoded;
|
|
37
|
-
case '
|
|
38
|
-
item = REFERENCES[ encoded.value];
|
|
39
|
-
if( item === undefined)
|
|
40
|
-
throw new Error( 'reference not found: ' + encoded.value);
|
|
41
|
-
if( item.type !== 'obj')
|
|
42
|
-
throw new Error( 'reference type mismatch: ' + encoded.value);
|
|
43
|
-
return item.value;
|
|
44
|
-
case 'funref':
|
|
45
|
+
case 'ref':
|
|
45
46
|
item = REFERENCES[ encoded.value];
|
|
46
47
|
if( item === undefined)
|
|
47
48
|
throw new Error( 'reference not found: ' + encoded.value);
|
|
48
49
|
if( item.type === 'potential-method')
|
|
49
50
|
throw new Error( 'potentially a method, cannot be evaluated standalone: ' + encoded.value);
|
|
50
|
-
if( item.type !== 'fun')
|
|
51
|
-
throw new Error( 'reference type mismatch: ' + encoded.value);
|
|
52
51
|
return item.value;
|
|
53
52
|
default:
|
|
54
53
|
throw new Error( 'unknown object type: ' + encoded.type);
|
|
@@ -87,19 +86,15 @@ function encode( decoded) {
|
|
|
87
86
|
encoded.value[ i] = encode( decoded[ i]);
|
|
88
87
|
return encoded;
|
|
89
88
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
return { type: 'objref', value: i};
|
|
93
|
-
i = '' + nextRefId++;
|
|
94
|
-
REFERENCES[ i] = { type: 'obj', value: decoded};
|
|
95
|
-
return { type: 'objref', value: i};
|
|
89
|
+
// warning: intentional fall-through here!
|
|
90
|
+
case 'unknown':
|
|
96
91
|
case 'function':
|
|
97
92
|
for( i in REFERENCES)
|
|
98
93
|
if( REFERENCES[ i].value === decoded)
|
|
99
|
-
return { type: '
|
|
94
|
+
return { type: 'ref', value: i};
|
|
100
95
|
i = '' + nextRefId++;
|
|
101
|
-
REFERENCES[ i] = {
|
|
102
|
-
return { type: '
|
|
96
|
+
REFERENCES[ i] = { value: decoded};
|
|
97
|
+
return { type: 'ref', value: i};
|
|
103
98
|
case 'symbol':
|
|
104
99
|
case 'bigint':
|
|
105
100
|
default:
|
|
@@ -112,22 +107,22 @@ function encodePotentialMethod( target, prop) {
|
|
|
112
107
|
var item;
|
|
113
108
|
for( i in REFERENCES) {
|
|
114
109
|
item = REFERENCES[ i];
|
|
115
|
-
if( item.type === 'potential-method' && item.target === target
|
|
116
|
-
return { type: '
|
|
110
|
+
if( item.type === 'potential-method' && item.target === target && item.prop === prop)
|
|
111
|
+
return { type: 'ref', value: i};
|
|
117
112
|
}
|
|
118
113
|
i = '' + nextRefId++;
|
|
119
114
|
REFERENCES[ i] = { type: 'potential-method', target: target, prop: prop};
|
|
120
|
-
return { type: '
|
|
115
|
+
return { type: 'ref', value: i};
|
|
121
116
|
}
|
|
122
117
|
|
|
123
118
|
function decodePotentialMethod( encoded) {
|
|
124
119
|
var item;
|
|
125
|
-
if( typeof encoded === 'object' && encoded.type === '
|
|
120
|
+
if( typeof encoded === 'object' && encoded.type === 'ref') {
|
|
126
121
|
item = REFERENCES[ encoded.value];
|
|
127
122
|
if( item.type === 'potential-method')
|
|
128
|
-
return
|
|
123
|
+
return item;
|
|
129
124
|
}
|
|
130
|
-
return { '
|
|
125
|
+
return { type: 'regular', value: decode( encoded)};
|
|
131
126
|
}
|
|
132
127
|
|
|
133
128
|
( function() {
|
|
@@ -174,11 +169,11 @@ function decodePotentialMethod( encoded) {
|
|
|
174
169
|
target = decodePotentialMethod( input[ 1]);
|
|
175
170
|
thisArg = decode( input[ 2]);
|
|
176
171
|
args = decode( input[ 3]);
|
|
177
|
-
if( target
|
|
172
|
+
if( target.type === 'potential-method') {
|
|
178
173
|
if( thisArg === undefined)
|
|
179
|
-
throw new Error( 'potentially a method,
|
|
174
|
+
throw new Error( 'potentially a method, use with a "this"');
|
|
180
175
|
if( thisArg !== target.target)
|
|
181
|
-
throw new Error( 'potentially a method,
|
|
176
|
+
throw new Error( 'potentially a method, "this" has changed');
|
|
182
177
|
switch( args.length) {
|
|
183
178
|
case 0: output = [ 'value', encode( target.target[ target.prop]())]; break;
|
|
184
179
|
case 1: output = [ 'value', encode( target.target[ target.prop]( args[ 0]))]; break;
|
package/lib/wsh/host.vbs
ADDED
package/lib/wsh/host.wsf
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arcticnotes/node-wsh",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.11",
|
|
4
4
|
"description": "A Node.js package that runs Windows Scripting Host (WSH) as a child process and exposes the resources from the WSH world to the Node.js world",
|
|
5
5
|
"author": "Paul <paul@arcticnotes.com>",
|
|
6
6
|
"license": "MIT",
|