@arcticnotes/node-wsh 0.0.9 → 0.0.10
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/README.md +3 -3
- package/lib/node/node-wsh.js +104 -100
- package/lib/wsh/host.js +6 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -18,10 +18,10 @@ In JavaScript code:
|
|
|
18
18
|
```javascript
|
|
19
19
|
import {WindowsScriptingHost} from '@arcticnotes/node-wsh';
|
|
20
20
|
|
|
21
|
-
const
|
|
22
|
-
const WScript =
|
|
21
|
+
const WSH = await WindowsScriptingHost.connect();
|
|
22
|
+
const WScript = WSH.global( 'WScript');
|
|
23
23
|
console.log(WScript.Version);
|
|
24
|
-
await
|
|
24
|
+
await WSH.disconnect();
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
## Dependencies
|
package/lib/node/node-wsh.js
CHANGED
|
@@ -5,8 +5,6 @@ import { Syncline} from "@arcticnotes/syncline";
|
|
|
5
5
|
const COMMAND = 'cscript.exe';
|
|
6
6
|
const ARGS = [ '//E:jscript', '//NoLogo'];
|
|
7
7
|
const SCRIPT_FILE = PATH.join( PATH.dirname( import.meta.dirname), 'wsh', 'host.js');
|
|
8
|
-
const PROXY = Symbol();
|
|
9
|
-
const TRACE_REF = 1;
|
|
10
8
|
|
|
11
9
|
export class WindowsScriptingHost extends EventEmitter {
|
|
12
10
|
|
|
@@ -15,83 +13,36 @@ export class WindowsScriptingHost extends EventEmitter {
|
|
|
15
13
|
const args = options.args || ARGS;
|
|
16
14
|
const scriptFile = options.scriptFile || SCRIPT_FILE;
|
|
17
15
|
const trace = options.trace || 0;
|
|
18
|
-
return new WindowsScriptingHost( await Syncline.spawn( command, [ ...args, scriptFile], { trace})
|
|
16
|
+
return new WindowsScriptingHost( await Syncline.spawn( command, [ ...args, scriptFile], { trace}));
|
|
19
17
|
}
|
|
20
18
|
|
|
21
19
|
#syncline;
|
|
22
|
-
#proxies;
|
|
23
|
-
#WScript;
|
|
24
|
-
#GetObject;
|
|
25
|
-
#Enumerator;
|
|
26
|
-
|
|
27
|
-
constructor( syncline, options) {
|
|
28
|
-
super();
|
|
29
|
-
this.#syncline = syncline;
|
|
30
|
-
this.#syncline.on( 'stderr', line => console.log( 'wsh:', line));
|
|
31
|
-
this.#syncline.on( 'stdout', line => console.log( 'wsh:', line));
|
|
32
|
-
this.#proxies = new Proxies( syncline, options, this);
|
|
33
|
-
this.#WScript = this.#proxies.getOrCreateObject( 0);
|
|
34
|
-
this.#GetObject = this.#proxies.getOrCreateFunction( 1);
|
|
35
|
-
this.#Enumerator = this.#proxies.getOrCreateFunction( 2);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
get remoteObjects() {
|
|
39
|
-
const proxies = this.#proxies;
|
|
40
|
-
return {
|
|
41
|
-
get count() {
|
|
42
|
-
return proxies.count;
|
|
43
|
-
},
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
get WScript() {
|
|
48
|
-
return this.#WScript;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
get GetObject() {
|
|
52
|
-
return this.#GetObject;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
get Enumerator() {
|
|
56
|
-
return this.#Enumerator;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
async disconnect() {
|
|
60
|
-
await this.#syncline.close();
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
class Proxies {
|
|
65
|
-
|
|
66
|
-
#syncline;
|
|
67
|
-
#trace;
|
|
68
|
-
#eventEmitter;
|
|
69
20
|
#finalizer = new FinalizationRegistry( this.#finalized.bind( this));
|
|
70
21
|
#ref2proxy = new Map();
|
|
71
|
-
#proxy2ref = new
|
|
22
|
+
#proxy2ref = new WeakMap();
|
|
72
23
|
|
|
73
24
|
#objectHandler = {
|
|
74
25
|
|
|
75
|
-
|
|
26
|
+
wsh: this,
|
|
76
27
|
|
|
77
28
|
get( target, prop) {
|
|
78
29
|
if( prop === Symbol.toPrimitive)
|
|
79
|
-
return () => `ref#${
|
|
80
|
-
const encodedTarget = this.
|
|
81
|
-
const encodedProp = this.
|
|
82
|
-
const output = JSON.parse( this.
|
|
30
|
+
return () => `ref#${ target.ref}`;
|
|
31
|
+
const encodedTarget = this.wsh.#encode( target.proxy);
|
|
32
|
+
const encodedProp = this.wsh.#encode( prop);
|
|
33
|
+
const output = JSON.parse( this.wsh.#syncline.exchange( JSON.stringify( [ 'get', encodedTarget, encodedProp])));
|
|
83
34
|
switch( output[ 0]) {
|
|
84
|
-
case 'value': return this.
|
|
35
|
+
case 'value': return this.wsh.#decode( output[ 1]);
|
|
85
36
|
case 'error': throw new Error( output[ 1]);
|
|
86
37
|
default: throw new Error( `unknown status: ${ output[ 0]}`);
|
|
87
38
|
}
|
|
88
39
|
},
|
|
89
40
|
|
|
90
41
|
set( target, prop, value) {
|
|
91
|
-
const encodedTarget = this.
|
|
92
|
-
const encodedProp = this.
|
|
93
|
-
const encodedValue = this.
|
|
94
|
-
const output = JSON.parse( this.
|
|
42
|
+
const encodedTarget = this.wsh.#encode( target.proxy);
|
|
43
|
+
const encodedProp = this.wsh.#encode( prop);
|
|
44
|
+
const encodedValue = this.wsh.#encode( value);
|
|
45
|
+
const output = JSON.parse( this.wsh.#syncline.exchange( JSON.stringify( [ 'set', encodedTarget, encodedProp, encodedValue])));
|
|
95
46
|
switch( output[ 0]) {
|
|
96
47
|
case 'set': return;
|
|
97
48
|
case 'error': throw new Error( output[ 1]);
|
|
@@ -102,85 +53,96 @@ class Proxies {
|
|
|
102
53
|
|
|
103
54
|
#functionHandler = {
|
|
104
55
|
|
|
105
|
-
|
|
56
|
+
wsh: this,
|
|
106
57
|
|
|
107
58
|
get( target, prop) {
|
|
108
59
|
if( prop === Symbol.toPrimitive)
|
|
109
|
-
return () => `ref#${
|
|
60
|
+
return () => `ref#${ target.ref}`;
|
|
110
61
|
return undefined;
|
|
111
62
|
},
|
|
112
63
|
|
|
113
64
|
apply( target, thisArg, argumentList) {
|
|
114
|
-
const encodedTarget = this.
|
|
115
|
-
const encodedThisArg = this.
|
|
116
|
-
const encodedArgumentList = this.
|
|
117
|
-
const output = JSON.parse( this.
|
|
65
|
+
const encodedTarget = this.wsh.#encode( target.proxy);
|
|
66
|
+
const encodedThisArg = this.wsh.#encode( thisArg);
|
|
67
|
+
const encodedArgumentList = this.wsh.#encode( argumentList);
|
|
68
|
+
const output = JSON.parse( this.wsh.#syncline.exchange( JSON.stringify( [ 'apply', encodedTarget, encodedThisArg, encodedArgumentList])));
|
|
118
69
|
switch( output[ 0]) {
|
|
119
|
-
case 'value': return this.
|
|
70
|
+
case 'value': return this.wsh.#decode( output[ 1]);
|
|
120
71
|
case 'error': throw new Error( output[ 1]);
|
|
121
72
|
default: throw new Error( `unknown status: ${ output[ 0]}`);
|
|
122
73
|
}
|
|
123
74
|
},
|
|
124
75
|
|
|
125
76
|
construct( target, argumentList) {
|
|
126
|
-
const encodedTarget = this.
|
|
127
|
-
const encodedArgumentList = this.
|
|
128
|
-
const output = JSON.parse( this.
|
|
77
|
+
const encodedTarget = this.wsh.#encode( target.proxy);
|
|
78
|
+
const encodedArgumentList = this.wsh.#encode( argumentList);
|
|
79
|
+
const output = JSON.parse( this.wsh.#syncline.exchange( JSON.stringify( [ 'construct', encodedTarget, encodedArgumentList])));
|
|
129
80
|
switch( output[ 0]) {
|
|
130
|
-
case 'value': return this.
|
|
81
|
+
case 'value': return this.wsh.#decode( output[ 1]);
|
|
131
82
|
case 'error': throw new Error( output[ 1]);
|
|
132
83
|
default: throw new Error( `unknown status: ${ output[ 0]}`);
|
|
133
84
|
}
|
|
134
85
|
},
|
|
135
86
|
};
|
|
136
87
|
|
|
137
|
-
constructor( syncline
|
|
88
|
+
constructor( syncline) {
|
|
89
|
+
super();
|
|
138
90
|
this.#syncline = syncline;
|
|
139
|
-
this.#
|
|
140
|
-
this.#
|
|
91
|
+
this.#syncline.on( 'stderr', line => console.log( 'wsh:', line));
|
|
92
|
+
this.#syncline.on( 'stdout', line => console.log( 'wsh:', line));
|
|
141
93
|
}
|
|
142
94
|
|
|
143
|
-
get
|
|
144
|
-
|
|
95
|
+
get remoteObjects() {
|
|
96
|
+
const proxies = this;
|
|
97
|
+
return {
|
|
98
|
+
get count() {
|
|
99
|
+
return proxies.#ref2proxy.size;
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
global( name) {
|
|
105
|
+
const output = JSON.parse( this.#syncline.exchange( JSON.stringify( [ 'global', name])));
|
|
106
|
+
switch( output[ 0]) {
|
|
107
|
+
case 'value': return this.#decode( output[ 1]);
|
|
108
|
+
case 'error': throw new Error( output[ 1]);
|
|
109
|
+
default: throw new Error( `unknown status: ${ output[ 0]}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async disconnect() {
|
|
114
|
+
await this.#syncline.close();
|
|
145
115
|
}
|
|
146
116
|
|
|
147
|
-
getOrCreateObject( ref) {
|
|
117
|
+
#getOrCreateObject( ref) {
|
|
148
118
|
return this.#getOrCreate( ref, this.#objectHandler);
|
|
149
119
|
}
|
|
150
120
|
|
|
151
|
-
getOrCreateFunction( ref) {
|
|
121
|
+
#getOrCreateFunction( ref) {
|
|
152
122
|
return this.#getOrCreate( ref, this.#functionHandler);
|
|
153
123
|
}
|
|
154
124
|
|
|
155
125
|
#getOrCreate( ref, handler) {
|
|
156
|
-
const
|
|
126
|
+
const existingWeakRef = this.#ref2proxy.get( ref);
|
|
127
|
+
const existingProxy = existingWeakRef && existingWeakRef.deref();
|
|
157
128
|
if( existingProxy)
|
|
158
129
|
return existingProxy;
|
|
159
130
|
|
|
160
|
-
const target = handler === this.#objectHandler? new RemoteObject():
|
|
131
|
+
const target = handler === this.#objectHandler? new RemoteObject( ref): new RemoteFunction( ref);
|
|
161
132
|
const newProxy = new Proxy( target, handler);
|
|
162
|
-
target
|
|
163
|
-
this.#ref2proxy.set( ref, newProxy);
|
|
133
|
+
target.proxy = newProxy;
|
|
134
|
+
this.#ref2proxy.set( ref, new WeakRef( newProxy)); // may be overwriting a dead WeakRef
|
|
164
135
|
this.#proxy2ref.set( newProxy, ref);
|
|
165
136
|
this.#finalizer.register( newProxy, ref);
|
|
166
|
-
this
|
|
137
|
+
this.emit( 'ref', ref, newProxy);
|
|
167
138
|
return newProxy;
|
|
168
139
|
}
|
|
169
140
|
|
|
170
141
|
#finalized( ref) {
|
|
142
|
+
if( this.#ref2proxy.get( ref).deref() === undefined) // otherwise, it's overwritten by a refreshed proxy
|
|
143
|
+
this.#ref2proxy.delete( ref);
|
|
171
144
|
const output = this.#syncline.exchange( JSON.stringify( [ 'unref', ref]));
|
|
172
|
-
|
|
173
|
-
switch( output[ 0]) {
|
|
174
|
-
case 'error':
|
|
175
|
-
console.log( `failed to unref: ${ ref}`);
|
|
176
|
-
break;
|
|
177
|
-
case 'done':
|
|
178
|
-
console.log( `unreferenced: ${ ref}`);
|
|
179
|
-
break;
|
|
180
|
-
default:
|
|
181
|
-
console.log( `unknown response: ${ output[ 0]}`);
|
|
182
|
-
}
|
|
183
|
-
this.#eventEmitter.emit( 'unref', ref);
|
|
145
|
+
this.emit( 'unref', ref);
|
|
184
146
|
}
|
|
185
147
|
|
|
186
148
|
#encode( decoded) {
|
|
@@ -202,7 +164,7 @@ class Proxies {
|
|
|
202
164
|
}
|
|
203
165
|
if( decoded instanceof RemoteObject) {
|
|
204
166
|
const objref = this.#proxy2ref.get( decoded);
|
|
205
|
-
if( objref === undefined)
|
|
167
|
+
if( objref === undefined) // not because garbage-collected, because clearly `decoded` is still alive
|
|
206
168
|
throw new Error( `remote object reference not found: ${ decoded}`);
|
|
207
169
|
return { type: 'objref', value: objref};
|
|
208
170
|
}
|
|
@@ -212,7 +174,7 @@ class Proxies {
|
|
|
212
174
|
return encoded;
|
|
213
175
|
case 'function':
|
|
214
176
|
const funref = this.#proxy2ref.get( decoded);
|
|
215
|
-
if( funref === undefined)
|
|
177
|
+
if( funref === undefined) // not because garbage-collected, because clearly `decoded` is still alive
|
|
216
178
|
throw new Error( `functions from node are disallowed: ${ decoded}`);
|
|
217
179
|
return { type: 'funref', value: funref};
|
|
218
180
|
case 'bigint':
|
|
@@ -246,9 +208,9 @@ class Proxies {
|
|
|
246
208
|
decoded[ name] = this.#decode( value);
|
|
247
209
|
return decoded;
|
|
248
210
|
case 'objref':
|
|
249
|
-
return this
|
|
211
|
+
return this.#getOrCreateObject( encoded.value);
|
|
250
212
|
case 'funref':
|
|
251
|
-
return this
|
|
213
|
+
return this.#getOrCreateFunction( encoded.value);
|
|
252
214
|
default:
|
|
253
215
|
throw new Error( `illegal value: ${ encoded}`);
|
|
254
216
|
}
|
|
@@ -263,4 +225,46 @@ class Proxies {
|
|
|
263
225
|
}
|
|
264
226
|
|
|
265
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 {
|
|
250
|
+
|
|
251
|
+
#ref;
|
|
252
|
+
#proxy;
|
|
253
|
+
|
|
254
|
+
constructor( ref) {
|
|
255
|
+
super();
|
|
256
|
+
this.#ref = ref;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
get ref() {
|
|
260
|
+
return this.#ref;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
get proxy() {
|
|
264
|
+
return this.#proxy;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
set proxy( proxy) {
|
|
268
|
+
this.#proxy = proxy;
|
|
269
|
+
}
|
|
266
270
|
}
|
package/lib/wsh/host.js
CHANGED
|
@@ -2,12 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
var FSO = new ActiveXObject( 'Scripting.FileSystemObject');
|
|
4
4
|
var OBJECT_TOSTRING = Object.toString();
|
|
5
|
-
var
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
'2': { type: 'fun', value: Enumerator}
|
|
9
|
-
};
|
|
10
|
-
var nextRefId = 3;
|
|
5
|
+
var GLOBAL = this;
|
|
6
|
+
var REFERENCES = {}
|
|
7
|
+
var nextRefId = 0;
|
|
11
8
|
|
|
12
9
|
eval( FSO.OpenTextFile( FSO.BuildPath( FSO.GetParentFolderName( WScript.ScriptFullName), 'json2.js')).ReadAll());
|
|
13
10
|
|
|
@@ -145,6 +142,9 @@ function decodePotentialMethod( encoded) {
|
|
|
145
142
|
try {
|
|
146
143
|
input = JSON.parse( WScript.StdIn.ReadLine());
|
|
147
144
|
switch( input[ 0]) {
|
|
145
|
+
case 'global': // [ 'global', name] => [ 'value', value]
|
|
146
|
+
output = [ 'value', encode( GLOBAL[ input[ 1]])];
|
|
147
|
+
break;
|
|
148
148
|
case 'unref': // [ 'unref', ref] => [ 'done']
|
|
149
149
|
if( REFERENCES[ input[ 1]] === undefined)
|
|
150
150
|
throw new Error( 'unknown ref: ' + input[ 1]);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arcticnotes/node-wsh",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.10",
|
|
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",
|