@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.
@@ -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 = [ '//E:jscript', '//NoLogo'];
7
- const SCRIPT_FILE = PATH.join( PATH.dirname( import.meta.dirname), 'wsh', 'host.js');
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
- wsh: this,
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( argumentList);
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, argumentList) {
65
+ construct( target, argumentsList) {
77
66
  const encodedTarget = this.wsh.#encode( target.proxy);
78
- const encodedArgumentList = this.wsh.#encode( argumentList);
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
- #getOrCreateObject( ref) {
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 = handler === this.#objectHandler? new RemoteObject( ref): new RemoteFunction( ref);
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
- const output = this.#syncline.exchange( JSON.stringify( [ 'unref', ref]));
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 instanceof Array) {
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
- const funref = this.#proxy2ref.get( decoded);
177
- if( funref === undefined) // not because garbage-collected, because clearly `decoded` is still alive
178
- throw new Error( `functions from node are disallowed: ${ decoded}`);
179
- return { type: 'funref', value: funref};
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 instanceof Array) {
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 'objref':
211
- return this.#getOrCreateObject( encoded.value);
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 'objref':
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
- for( i in REFERENCES)
91
- if( REFERENCES[ i].value === decoded)
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: 'funref', value: i};
94
+ return { type: 'ref', value: i};
100
95
  i = '' + nextRefId++;
101
- REFERENCES[ i] = { type: 'fun', value: decoded};
102
- return { type: 'funref', value: i};
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, item.prop === prop)
116
- return { type: 'funref', value: i};
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: 'funref', value: i};
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 === 'funref') {
120
+ if( typeof encoded === 'object' && encoded.type === 'ref') {
126
121
  item = REFERENCES[ encoded.value];
127
122
  if( item.type === 'potential-method')
128
- return { 'potential-method': true, target: item.target, prop: item.prop};
123
+ return item;
129
124
  }
130
- return { 'potential-method': false, value: decode( encoded)}
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[ 'potential-method']) {
172
+ if( target.type === 'potential-method') {
178
173
  if( thisArg === undefined)
179
- throw new Error( 'potentially a method, can only be used as such');
174
+ throw new Error( 'potentially a method, use with a "this"');
180
175
  if( thisArg !== target.target)
181
- throw new Error( 'potentially a method, can only be used as such');
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;
@@ -0,0 +1,9 @@
1
+ Function Dict2VBArray( dict)
2
+ Dim vbArray()
3
+ ReDim vbArray( dict.Item( "length") - 1)
4
+ Dim I
5
+ For I = 0 To dict.Item( "length") - 1
6
+ vbArray( I) = dict.Item( I)
7
+ Next
8
+ Dict2VBArray = vbArray
9
+ End Function
@@ -0,0 +1,4 @@
1
+ <job>
2
+ <script language="VBScript" src="host.vbs" />
3
+ <script language="JScript" src="host.js" />
4
+ </job>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arcticnotes/node-wsh",
3
- "version": "0.0.10",
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",