@arcticnotes/node-wsh 0.0.11 → 0.0.12
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/index.js +1 -0
- package/lib/node/node-wsh.js +144 -133
- package/lib/node/utils.js +24 -0
- package/package.json +2 -2
package/lib/index.js
CHANGED
package/lib/node/node-wsh.js
CHANGED
|
@@ -12,73 +12,24 @@ export class WindowsScriptingHost extends EventEmitter {
|
|
|
12
12
|
const command = options.command || COMMAND;
|
|
13
13
|
const args = options.args || ARGS;
|
|
14
14
|
const scriptFile = options.scriptFile || SCRIPT_FILE;
|
|
15
|
-
|
|
16
|
-
return new WindowsScriptingHost( await Syncline.spawn( command, [ ...args, scriptFile], { trace}));
|
|
15
|
+
return new WindowsScriptingHost( await Syncline.spawn( command, [ ...args, scriptFile], options.syncline), options);
|
|
17
16
|
}
|
|
18
17
|
|
|
19
18
|
#syncline;
|
|
20
19
|
#closed = false;
|
|
21
20
|
#finalizer = new FinalizationRegistry( this.#finalized.bind( this));
|
|
22
|
-
#ref2proxy = new Map();
|
|
23
|
-
#proxy2ref = new WeakMap();
|
|
24
|
-
#handler = {
|
|
25
|
-
|
|
26
|
-
wsh: this,
|
|
27
|
-
|
|
28
|
-
get( target, prop) {
|
|
29
|
-
if( prop === Symbol.toPrimitive)
|
|
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])));
|
|
34
|
-
switch( output[ 0]) {
|
|
35
|
-
case 'value': return this.wsh.#decode( output[ 1]);
|
|
36
|
-
case 'error': throw new Error( output[ 1]);
|
|
37
|
-
default: throw new Error( `unknown status: ${ output[ 0]}`);
|
|
38
|
-
}
|
|
39
|
-
},
|
|
40
|
-
|
|
41
|
-
set( target, prop, value) {
|
|
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])));
|
|
46
|
-
switch( output[ 0]) {
|
|
47
|
-
case 'set': return;
|
|
48
|
-
case 'error': throw new Error( output[ 1]);
|
|
49
|
-
default: throw new Error( `unknown status: ${ output[ 0]}`);
|
|
50
|
-
}
|
|
51
|
-
},
|
|
52
|
-
|
|
53
|
-
apply( target, thisArg, argumentsList) {
|
|
54
|
-
const encodedTarget = this.wsh.#encode( target.proxy);
|
|
55
|
-
const encodedThisArg = this.wsh.#encode( thisArg);
|
|
56
|
-
const encodedArgumentList = this.wsh.#encode( [ ...argumentsList]); // argumentsList may not be instanceof Array
|
|
57
|
-
const output = JSON.parse( this.wsh.#syncline.exchange( JSON.stringify( [ 'apply', encodedTarget, encodedThisArg, encodedArgumentList])));
|
|
58
|
-
switch( output[ 0]) {
|
|
59
|
-
case 'value': return this.wsh.#decode( output[ 1]);
|
|
60
|
-
case 'error': throw new Error( output[ 1]);
|
|
61
|
-
default: throw new Error( `unknown status: ${ output[ 0]}`);
|
|
62
|
-
}
|
|
63
|
-
},
|
|
64
|
-
|
|
65
|
-
construct( target, argumentsList) {
|
|
66
|
-
const encodedTarget = this.wsh.#encode( target.proxy);
|
|
67
|
-
const encodedArgumentList = this.wsh.#encode( [ ...argumentsList]); // argumentsList may not be instanceof Array
|
|
68
|
-
const output = JSON.parse( this.wsh.#syncline.exchange( JSON.stringify( [ 'construct', encodedTarget, encodedArgumentList])));
|
|
69
|
-
switch( output[ 0]) {
|
|
70
|
-
case 'value': return this.wsh.#decode( output[ 1]);
|
|
71
|
-
case 'error': throw new Error( output[ 1]);
|
|
72
|
-
default: throw new Error( `unknown status: ${ output[ 0]}`);
|
|
73
|
-
}
|
|
74
|
-
},
|
|
75
|
-
};
|
|
21
|
+
#ref2proxy = new Map(); // Map< string, WeakRef< Proxy | <custom-mapped>>>
|
|
22
|
+
#proxy2ref = new WeakMap(); // Map< Proxy | <custom-mapped>, string>
|
|
76
23
|
|
|
77
|
-
constructor( syncline) {
|
|
24
|
+
constructor( syncline, options) {
|
|
78
25
|
super();
|
|
79
26
|
this.#syncline = syncline;
|
|
80
|
-
this.#syncline.on( 'stderr', line => console.log( 'wsh:', line));
|
|
81
|
-
this.#syncline.on( 'stdout', line => console.log( 'wsh:', line));
|
|
27
|
+
this.#syncline.on( 'stderr', line => console.log( 'wsh-stderr:', line));
|
|
28
|
+
this.#syncline.on( 'stdout', line => console.log( 'wsh-stdout:', line));
|
|
29
|
+
if( options.printSynclineIO) {
|
|
30
|
+
this.#syncline.on( 'input', line => console.log( 'syncline-input:', line));
|
|
31
|
+
this.#syncline.on( 'output', line => console.log( 'syncline-output', line));
|
|
32
|
+
}
|
|
82
33
|
}
|
|
83
34
|
|
|
84
35
|
get remoteObjects() {
|
|
@@ -90,12 +41,12 @@ export class WindowsScriptingHost extends EventEmitter {
|
|
|
90
41
|
};
|
|
91
42
|
}
|
|
92
43
|
|
|
93
|
-
global( name) {
|
|
44
|
+
global( name, mapper = undefined) {
|
|
94
45
|
const output = JSON.parse( this.#syncline.exchange( JSON.stringify( [ 'global', name])));
|
|
95
46
|
switch( output[ 0]) {
|
|
96
|
-
case 'value': return this.#decode( output[ 1]);
|
|
47
|
+
case 'value': return this.#decode( output[ 1], mapper);
|
|
97
48
|
case 'error': throw new Error( output[ 1]);
|
|
98
|
-
default: throw new Error( `unknown status: ${ output[ 0]}`);
|
|
49
|
+
default: throw new Error( `unknown status: ${ output[ 0]}`); // bug
|
|
99
50
|
}
|
|
100
51
|
}
|
|
101
52
|
|
|
@@ -104,67 +55,7 @@ export class WindowsScriptingHost extends EventEmitter {
|
|
|
104
55
|
await this.#syncline.close();
|
|
105
56
|
}
|
|
106
57
|
|
|
107
|
-
#
|
|
108
|
-
const existingWeakRef = this.#ref2proxy.get( ref);
|
|
109
|
-
const existingProxy = existingWeakRef && existingWeakRef.deref();
|
|
110
|
-
if( existingProxy)
|
|
111
|
-
return existingProxy;
|
|
112
|
-
|
|
113
|
-
const target = new RemoteObject( ref);
|
|
114
|
-
const newProxy = new Proxy( target, this.#handler);
|
|
115
|
-
target.proxy = newProxy;
|
|
116
|
-
this.#ref2proxy.set( ref, new WeakRef( newProxy)); // may be overwriting a dead WeakRef
|
|
117
|
-
this.#proxy2ref.set( newProxy, ref);
|
|
118
|
-
this.#finalizer.register( newProxy, ref);
|
|
119
|
-
this.emit( 'ref', ref, newProxy);
|
|
120
|
-
return newProxy;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
#finalized( ref) {
|
|
124
|
-
if( this.#ref2proxy.get( ref).deref() === undefined) // otherwise, it's overwritten by a refreshed proxy
|
|
125
|
-
this.#ref2proxy.delete( ref);
|
|
126
|
-
if( !this.#closed)
|
|
127
|
-
this.#syncline.exchange( JSON.stringify( [ 'unref', ref]));
|
|
128
|
-
this.emit( 'unref', ref);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
#encode( decoded) {
|
|
132
|
-
switch( typeof decoded) {
|
|
133
|
-
case 'boolean':
|
|
134
|
-
case 'number':
|
|
135
|
-
case 'string':
|
|
136
|
-
return decoded;
|
|
137
|
-
case 'undefined':
|
|
138
|
-
return { type: 'undefined'};
|
|
139
|
-
case 'object':
|
|
140
|
-
if( decoded === null)
|
|
141
|
-
return decoded;
|
|
142
|
-
if( Array.isArray( decoded)) {
|
|
143
|
-
const encoded = [];
|
|
144
|
-
for( const item of decoded)
|
|
145
|
-
encoded.push( this.#encode( item));
|
|
146
|
-
return encoded;
|
|
147
|
-
}
|
|
148
|
-
const encoded = { type: 'object', value: {}};
|
|
149
|
-
for( const [ name, value] of Object.entries( decoded))
|
|
150
|
-
encoded.value[ name] = this.#encode( value);
|
|
151
|
-
return encoded;
|
|
152
|
-
case 'function':
|
|
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}`);
|
|
160
|
-
case 'bigint':
|
|
161
|
-
case 'symbol':
|
|
162
|
-
default:
|
|
163
|
-
throw new Error( `unsupported value: ${ decoded}`);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
#decode( encoded) {
|
|
58
|
+
#decode( encoded, mapper) {
|
|
168
59
|
switch( typeof encoded) {
|
|
169
60
|
case 'boolean':
|
|
170
61
|
case 'number':
|
|
@@ -176,7 +67,7 @@ export class WindowsScriptingHost extends EventEmitter {
|
|
|
176
67
|
if( Array.isArray( encoded)) {
|
|
177
68
|
const decoded = [];
|
|
178
69
|
for( const item of encoded)
|
|
179
|
-
decoded.push( this.#decode( item));
|
|
70
|
+
decoded.push( this.#decode( item, mapper));
|
|
180
71
|
return decoded;
|
|
181
72
|
}
|
|
182
73
|
switch( encoded.type) {
|
|
@@ -185,10 +76,10 @@ export class WindowsScriptingHost extends EventEmitter {
|
|
|
185
76
|
case 'object':
|
|
186
77
|
const decoded = {};
|
|
187
78
|
for( const [ name, value] of Object.entries( encoded.value))
|
|
188
|
-
decoded[ name] = this.#decode( value);
|
|
79
|
+
decoded[ name] = this.#decode( value, mapper);
|
|
189
80
|
return decoded;
|
|
190
81
|
case 'ref':
|
|
191
|
-
return this.#getOrCreate( encoded.value);
|
|
82
|
+
return this.#getOrCreate( encoded.value, mapper);
|
|
192
83
|
default:
|
|
193
84
|
throw new Error( `illegal value: ${ encoded}`);
|
|
194
85
|
}
|
|
@@ -200,27 +91,147 @@ export class WindowsScriptingHost extends EventEmitter {
|
|
|
200
91
|
throw new Error( `illegal value: ${ encoded}`);
|
|
201
92
|
}
|
|
202
93
|
}
|
|
94
|
+
|
|
95
|
+
#encode( decoded) {
|
|
96
|
+
switch( typeof decoded) {
|
|
97
|
+
case 'boolean':
|
|
98
|
+
case 'number':
|
|
99
|
+
case 'string':
|
|
100
|
+
return decoded;
|
|
101
|
+
case 'undefined':
|
|
102
|
+
return { type: 'undefined'};
|
|
103
|
+
case 'object': {
|
|
104
|
+
if( decoded === null)
|
|
105
|
+
return decoded;
|
|
106
|
+
const ref = this.#proxy2ref.get( decoded);
|
|
107
|
+
if( ref !== undefined)
|
|
108
|
+
return { type: 'ref', value: ref};
|
|
109
|
+
if( Array.isArray( decoded)) {
|
|
110
|
+
const encoded = [];
|
|
111
|
+
for( const item of decoded)
|
|
112
|
+
encoded.push( this.#encode( item));
|
|
113
|
+
return encoded;
|
|
114
|
+
}
|
|
115
|
+
const encoded = { type: 'object', value: {}};
|
|
116
|
+
for( const [ name, value] of Object.entries( decoded))
|
|
117
|
+
encoded.value[ name] = this.#encode( value);
|
|
118
|
+
return encoded;
|
|
119
|
+
}
|
|
120
|
+
case 'function': {
|
|
121
|
+
const ref = this.#proxy2ref.get( decoded);
|
|
122
|
+
if( ref !== undefined)
|
|
123
|
+
return { type: 'ref', value: ref};
|
|
124
|
+
throw new Error( `functions from node cannot be sent: ${ decoded}`);
|
|
125
|
+
}
|
|
126
|
+
case 'bigint':
|
|
127
|
+
case 'symbol':
|
|
128
|
+
default:
|
|
129
|
+
throw new Error( `unsupported value: ${ decoded}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
#getOrCreate( ref, mapper) {
|
|
134
|
+
const existingWeakRef = this.#ref2proxy.get( ref);
|
|
135
|
+
const existingProxy = existingWeakRef && existingWeakRef.deref();
|
|
136
|
+
if( existingProxy)
|
|
137
|
+
return existingProxy;
|
|
138
|
+
|
|
139
|
+
const newProxy = new RemoteObject( this.#syncline, this.#encode.bind( this), this.#decode.bind( this), ref, mapper).proxy;
|
|
140
|
+
if( this.#proxy2ref.has( newProxy)) // sanity check, doesn't catch all misbehaving mappers
|
|
141
|
+
throw new Error( `mapper must return new objects: ${ newProxy}`);
|
|
142
|
+
this.#ref2proxy.set( ref, new WeakRef( newProxy)); // may be overwriting a dead WeakRef
|
|
143
|
+
this.#proxy2ref.set( newProxy, ref);
|
|
144
|
+
this.#finalizer.register( newProxy, ref);
|
|
145
|
+
this.emit( 'ref', ref, newProxy);
|
|
146
|
+
return newProxy;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
#finalized( ref) {
|
|
150
|
+
if( this.#ref2proxy.get( ref).deref() === undefined) // otherwise, it's overwritten by a refreshed proxy
|
|
151
|
+
this.#ref2proxy.delete( ref);
|
|
152
|
+
if( !this.#closed)
|
|
153
|
+
this.#syncline.exchange( JSON.stringify( [ 'unref', ref]));
|
|
154
|
+
this.emit( 'unref', ref);
|
|
155
|
+
}
|
|
203
156
|
}
|
|
204
157
|
|
|
205
158
|
class RemoteObject extends Function {
|
|
206
159
|
|
|
160
|
+
static #handler = {
|
|
161
|
+
get: ( ticket, prop) => ticket.get( prop, undefined),
|
|
162
|
+
set: ( ticket, prop, value) => ticket.set( prop, value),
|
|
163
|
+
apply: ( ticket, thisArg, argumentsList) => ticket.apply( thisArg, argumentsList, undefined),
|
|
164
|
+
construct: ( ticket, argumentsList) => ticket.construct( argumentsList, undefined),
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
#syncline;
|
|
168
|
+
#encode;
|
|
169
|
+
#decode;
|
|
207
170
|
#ref;
|
|
208
171
|
#proxy;
|
|
209
172
|
|
|
210
|
-
constructor( ref) {
|
|
173
|
+
constructor( syncline, encode, decode, ref, mapper) {
|
|
211
174
|
super();
|
|
175
|
+
this.#syncline = syncline;
|
|
176
|
+
this.#encode = encode;
|
|
177
|
+
this.#decode = decode;
|
|
212
178
|
this.#ref = ref;
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
get ref() {
|
|
216
|
-
return this.#ref;
|
|
179
|
+
this.#proxy = mapper? mapper( this): this.newProxy();
|
|
217
180
|
}
|
|
218
181
|
|
|
219
182
|
get proxy() {
|
|
220
183
|
return this.#proxy;
|
|
221
184
|
}
|
|
222
185
|
|
|
223
|
-
|
|
224
|
-
this.#
|
|
186
|
+
newProxy() {
|
|
187
|
+
return new Proxy( this, RemoteObject.#handler);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
get( prop, mapper) {
|
|
191
|
+
if( prop === Symbol.toPrimitive)
|
|
192
|
+
return () => `ref#${ this.#ref}`;
|
|
193
|
+
const encodedTarget = this.#encode( this.#proxy);
|
|
194
|
+
const encodedProp = this.#encode( prop);
|
|
195
|
+
const output = JSON.parse( this.#syncline.exchange( JSON.stringify( [ 'get', encodedTarget, encodedProp])));
|
|
196
|
+
switch( output[ 0]) {
|
|
197
|
+
case 'value': return this.#decode( output[ 1], mapper);
|
|
198
|
+
case 'error': throw new Error( output[ 1]);
|
|
199
|
+
default: throw new Error( `unknown status: ${ output[ 0]}`);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
set( prop, value) {
|
|
204
|
+
const encodedTarget = this.#encode( this.#proxy);
|
|
205
|
+
const encodedProp = this.#encode( prop);
|
|
206
|
+
const encodedValue = this.#encode( value);
|
|
207
|
+
const output = JSON.parse( this.#syncline.exchange( JSON.stringify( [ 'set', encodedTarget, encodedProp, encodedValue])));
|
|
208
|
+
switch( output[ 0]) {
|
|
209
|
+
case 'set': return true;
|
|
210
|
+
case 'error': throw new Error( output[ 1]);
|
|
211
|
+
default: throw new Error( `unknown status: ${ output[ 0]}`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
apply( thisArg, argumentsList, mapper) {
|
|
216
|
+
const encodedTarget = this.#encode( this.#proxy);
|
|
217
|
+
const encodedThisArg = this.#encode( thisArg);
|
|
218
|
+
const encodedArgumentList = this.#encode( [ ...argumentsList]); // argumentsList may not be instanceof Array
|
|
219
|
+
const output = JSON.parse( this.#syncline.exchange( JSON.stringify( [ 'apply', encodedTarget, encodedThisArg, encodedArgumentList])));
|
|
220
|
+
switch( output[ 0]) {
|
|
221
|
+
case 'value': return this.#decode( output[ 1], mapper);
|
|
222
|
+
case 'error': throw new Error( output[ 1]);
|
|
223
|
+
default: throw new Error( `unknown status: ${ output[ 0]}`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
construct( argumentsList, mapper) {
|
|
228
|
+
const encodedTarget = this.#encode( this.#proxy);
|
|
229
|
+
const encodedArgumentList = this.#encode( [ ...argumentsList]); // argumentsList may not be instanceof Array
|
|
230
|
+
const output = JSON.parse( this.#syncline.exchange( JSON.stringify( [ 'construct', encodedTarget, encodedArgumentList])));
|
|
231
|
+
switch( output[ 0]) {
|
|
232
|
+
case 'value': return this.#decode( output[ 1], mapper);
|
|
233
|
+
case 'error': throw new Error( output[ 1]);
|
|
234
|
+
default: throw new Error( `unknown status: ${ output[ 0]}`);
|
|
235
|
+
}
|
|
225
236
|
}
|
|
226
237
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export class Collection {
|
|
2
|
+
|
|
3
|
+
#ticket;
|
|
4
|
+
#type;
|
|
5
|
+
|
|
6
|
+
constructor( ticket, type) {
|
|
7
|
+
this.#ticket = ticket;
|
|
8
|
+
this.#type = type;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
get length() {
|
|
12
|
+
return this.#ticket.get( 'Count');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
get( indexBase0) {
|
|
16
|
+
return this.#ticket.get( 'Item', t2 => t2.apply.bind( t2))( this, [ indexBase0 + 1], t3 => new this.#type( t3));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
*[ Symbol.iterator]() {
|
|
20
|
+
const length = this.length; // expect anomoly if the underlying collect is changing
|
|
21
|
+
for( let i = 0; i < length; i++)
|
|
22
|
+
yield this.get( i);
|
|
23
|
+
}
|
|
24
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arcticnotes/node-wsh",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.12",
|
|
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",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
".": "./lib/index.js"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@arcticnotes/syncline": "0.0.
|
|
24
|
+
"@arcticnotes/syncline": "0.0.4"
|
|
25
25
|
},
|
|
26
26
|
"files": [
|
|
27
27
|
"/lib/"
|