@naanlang/naan 1.0.16 → 1.2.0

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 (45) hide show
  1. package/LICENSE.md +2 -2
  2. package/README.md +13 -11
  3. package/bin/index.js +4 -4
  4. package/dist/naan.min.js +14 -14
  5. package/frameworks/browser/https_request.nlg +26 -10
  6. package/frameworks/browser/sworker.js +26 -25
  7. package/frameworks/browser/terminals.nlg +118 -55
  8. package/frameworks/browser/ws_client.nlg +124 -0
  9. package/frameworks/client/apiclient.nlg +82 -116
  10. package/frameworks/client/psm_client.nlg +37 -16
  11. package/frameworks/client/relaycon.nlg +308 -0
  12. package/frameworks/common/common.nlg +1 -1
  13. package/frameworks/common/utils.nlg +40 -2
  14. package/frameworks/node/apiserver.nlg +342 -103
  15. package/frameworks/node/https_request.nlg +32 -6
  16. package/frameworks/node/node.nlg +60 -2
  17. package/frameworks/node/psm_server.nlg +11 -7
  18. package/frameworks/node/worker.nlg +22 -9
  19. package/frameworks/node/ws_client.nlg +127 -0
  20. package/frameworks/project/build.nlg +3 -2
  21. package/frameworks/project/projects.nlg +8 -2
  22. package/frameworks/running/debugnub.nlg +2 -2
  23. package/frameworks/running/debugutil.nlg +1 -1
  24. package/frameworks/running/executors.nlg +69 -17
  25. package/frameworks/running/sourcecode.nlg +2 -2
  26. package/frameworks/running/taskexec.nlg +25 -25
  27. package/frameworks/storage/dbt_pouch.nlg +8 -6
  28. package/frameworks/storage/file_manager.nlg +6 -3
  29. package/frameworks/storage/psm.nlg +5 -3
  30. package/frameworks/storage/psm_dbtables.nlg +75 -53
  31. package/frameworks/storage/resources.nlg +4 -2
  32. package/lib/browser/env_web.js +6 -6
  33. package/lib/browser/env_webworker.js +1 -1
  34. package/lib/core/naanlib.js +14 -14
  35. package/lib/env_node.js +97 -8
  36. package/lib/env_nodeworker.js +22 -4
  37. package/package.json +1 -1
  38. package/plugins/serviceAws/aws/lambda_rest_index.js +5 -1
  39. package/plugins/serviceAws/aws_dynamo.nlg +91 -32
  40. package/plugins/serviceAws/dbt_aws.nlg +5 -5
  41. package/plugins/serviceAws/psm_aws.nlg +2 -2
  42. package/test/harness.nlg +1 -1
  43. package/test/test_01_core.nlg +30 -4
  44. package/test/test_02_context.nlg +2 -2
  45. package/test/test_07_lingo.nlg +3 -0
@@ -1,11 +1,11 @@
1
1
  /*
2
- * https_browser.nlg
2
+ * https_request.nlg
3
3
  *
4
4
  * Https operations for the browser.
5
5
  *
6
6
  * column positioning: // // !
7
7
  *
8
- * Copyright (c) 2021-2023 by Richard C. Zulch
8
+ * Copyright (c) 2021-2024 by Richard C. Zulch
9
9
  *
10
10
  */
11
11
 
@@ -24,6 +24,7 @@
24
24
  * encoding: <string> // "binary" to receive binary data as arrayBuffer
25
25
  * range: <string> // sets range header
26
26
  * headers: [`(key, value)] // add header(s) to the request, overriding defaults
27
+ * debug: <boolean> // log errors and status results
27
28
  * }
28
29
  *
29
30
  * The return is a result tuple comprising:
@@ -82,15 +83,29 @@ closure HttpsApiRequest(url, options,
82
83
  //
83
84
  // perform the fetch and return when complete
84
85
  //
85
- `(error, response) = await(window.fetch(urlq, fetchOptions))
86
- if error
87
- return (list(Error("HttpsApiRequest fetch failed:", error, url)))
86
+ loop { // loop for retries
87
+ `(error, response) = await(window.fetch(urlq, fetchOptions))
88
+ if error {
89
+ if options.debug
90
+ debuglog("HttpsApiRequest error:", url, "error:", error)
91
+ return (list(Error("HttpsApiRequest fetch failed:", error, url)))
92
+ }
93
+ if response.status < 200 || response.status >= 299 { // 299 is "cancelled"
94
+ if options.debug
95
+ debuglog("HttpsApiRequest status:", url, response.statusCode)
96
+ error = Error("HttpsApiRequest status:", url, response.status, {
97
+ status: response.status
98
+ })
99
+ if options.retrier(error)
100
+ continue // optional retry/delay logic
101
+ else
102
+ return (list(error, false, { status: response.status }))
103
+ } else
104
+ break
105
+ }
88
106
  contentType = response.headers.get("content-type")
89
- if response.status < 200 || response.status >= 300
90
- return (list(Error("HttpsApiRequest status:", url, response.status, {
91
- status: response.status
92
- }), false, { status: response.status }))
93
- if contentType.startsWith("application/json") {
107
+ if contentType.startsWith("application/json")
108
+ || contentType.startsWith("application/octet-stream") && options.encoding == "json" {
94
109
  `(error, content) = await(response.text())
95
110
  if !error
96
111
  `(error, content) = JsonParse(content)
@@ -115,6 +130,7 @@ closure HttpsApiRequest(url, options,
115
130
  list(error, content, {
116
131
  headers: headers
117
132
  status: response.status
133
+ contentType: contentType
118
134
  })
119
135
  };
120
136
 
@@ -32,11 +32,11 @@
32
32
  *
33
33
  * column positioning: // // !
34
34
  *
35
- * Copyright (c) 2020-2023 by Richard C. Zulch
35
+ * Copyright (c) 2020-2024 by Richard C. Zulch
36
36
  *
37
37
  */
38
38
 
39
- var CurrentCacheName = "Naanlang-1.0.16+1";
39
+ var CurrentCacheName = "Naanlang-1.2.0+1";
40
40
 
41
41
 
42
42
  //
@@ -92,18 +92,18 @@ var terminated; // flag that this service worker
92
92
  var pubVersion = event.data.hereIsMyVersion;
93
93
  var sourceId = event.source.id; // client id of sender of message
94
94
  msgPorts[sourceId] = msgport;
95
- console.log("[1.0.16+1] received new msgport for", sourceId, pubID, "-", pubVersion);
96
- if (pubVersion != "1.0.16+1") {
95
+ console.log("[1.2.0+1] received new msgport for", sourceId, pubID, "-", pubVersion);
96
+ if (pubVersion != "1.2.0+1") {
97
97
  if (upgradeMsgPorts) { // if not activated
98
- console.log("[1.0.16+1] update pending for", sourceId);
98
+ console.log("[1.2.0+1] update pending for", sourceId);
99
99
  upgradeMsgPorts[sourceId] = msgport;
100
100
  }
101
101
  else {
102
102
  msgport.postMessage({ // notify new version available
103
103
  id: "upgrade",
104
- version: "1.0.16+1"
104
+ version: "1.2.0+1"
105
105
  });
106
- console.log("[1.0.16+1] update sent for:", sourceId);
106
+ console.log("[1.2.0+1] update sent for:", sourceId);
107
107
  }
108
108
  }
109
109
  Reaper(); // clean up obsolete info
@@ -134,10 +134,10 @@ var terminated; // flag that this service worker
134
134
  msgport.onmessage = function(msg) {
135
135
  msg = msg.data;
136
136
  if (msg.id == "skipWaiting") { // try to activate this SW now
137
- console.log("[1.0.16+1] attempting skipWaiting");
137
+ console.log("[1.2.0+1] attempting skipWaiting");
138
138
  self.skipWaiting();
139
139
  } else if (msg.id == "terminate") { // termiante this SW now
140
- console.log("[1.0.16+1] terminated");
140
+ console.log("[1.2.0+1] terminated");
141
141
  terminate();
142
142
  } else if (msg.id == "abortURL") { // abort an I/O transaction
143
143
  var href = new URL(msg.url).href;
@@ -150,7 +150,7 @@ var terminated; // flag that this service worker
150
150
  } else if (msg.id == "response") // response from fetch request
151
151
  processResponse(msg);
152
152
  else if (msg.id == "text") // just log some text
153
- console.log("[1.0.16+1] msg received:", msg.text);
153
+ console.log("[1.2.0+1] msg received:", msg.text);
154
154
  };
155
155
 
156
156
  // send text to IDE log
@@ -161,7 +161,7 @@ var terminated; // flag that this service worker
161
161
  // don't clutter the log
162
162
  msgport.postMessage({
163
163
  id: "text",
164
- text: "port received by 1.0.16+1",
164
+ text: "port received by 1.2.0+1",
165
165
  });
166
166
  */
167
167
  if (--pending === 0)
@@ -178,7 +178,7 @@ var terminated; // flag that this service worker
178
178
  includeUncontrolled: true
179
179
  }).then(function(clientList) {
180
180
  clientList.every(function(client) {
181
- console.log("[1.0.16+1] requesting new msgport for", client.id);
181
+ console.log("[1.2.0+1] requesting new msgport for", client.id);
182
182
  ++pending;
183
183
  client.postMessage({ // tell client(s) we need this fetch source
184
184
  msg: "Naan_need_fetch_port",
@@ -224,12 +224,12 @@ function Reaper() {
224
224
  clients[clientList[clidex].id] = clientList[clidex];
225
225
  for (var sourceId in msgPorts)
226
226
  if (!clients[sourceId]) {
227
- console.log("[1.0.16+1] source gone:", sourceId);
227
+ console.log("[1.2.0+1] source gone:", sourceId);
228
228
  delete msgPorts[sourceId]; // no longer a source
229
229
  }
230
230
  for (var clientId in fetchPorts)
231
231
  if (!clients[clientId]) {
232
- console.log("[1.0.16+1] client gone:", clientId);
232
+ console.log("[1.2.0+1] client gone:", clientId);
233
233
  delete fetchPorts[clientId]; // no longer a client
234
234
  }
235
235
  for (var fqdex = 0; fqdex < fetchQueue.length; ++fqdex) {
@@ -263,16 +263,16 @@ function ClearCaches() {
263
263
  return (Promise.all(
264
264
  cacheNames.map(function(cacheName) {
265
265
  if (cacheName != CurrentCacheName) {
266
- console.log('[1.0.16+1] deleting old cache:', cacheName);
266
+ console.log('[1.2.0+1] deleting old cache:', cacheName);
267
267
  return (caches.delete(cacheName));
268
268
  }
269
269
  })
270
270
  ));
271
271
  }).then(function() { // claim all clients
272
- console.log('[1.0.16+1] claiming clients');
272
+ console.log('[1.2.0+1] claiming clients');
273
273
  return (self.clients.claim());
274
274
  }).then(function() {
275
- console.log('[1.0.16+1] clients claimed');
275
+ console.log('[1.2.0+1] clients claimed');
276
276
  return (Promise.resolve(true));
277
277
  });
278
278
  return (promise);
@@ -315,7 +315,7 @@ function GetClientResponse(event, urlpath) {
315
315
  msgport.postMessage({
316
316
  id: "fetch",
317
317
  seq: seqno,
318
- version: "1.0.16+1",
318
+ version: "1.2.0+1",
319
319
  request: {
320
320
  method: event.request.method,
321
321
  url: event.request.url
@@ -363,7 +363,7 @@ function GetClientResponse(event, urlpath) {
363
363
  */
364
364
 
365
365
  self.addEventListener('install', function(event) {
366
- console.log("[1.0.16+1] install");
366
+ console.log("[1.2.0+1] install");
367
367
  self.skipWaiting();
368
368
  });
369
369
 
@@ -376,20 +376,20 @@ self.addEventListener('install', function(event) {
376
376
  */
377
377
 
378
378
  self.addEventListener('activate', function(event) {
379
- console.log("[1.0.16+1] activate");
379
+ console.log("[1.2.0+1] activate");
380
380
  self.clients.matchAll({ // for debugging, list controlled clients
381
381
  includeUncontrolled: true
382
382
  }).then(function(clientList) {
383
383
  var urls = clientList.map(function(client) {
384
384
  return (client.url);
385
385
  });
386
- console.log('[1.0.16+1] matching clients:', urls.join(', '));
386
+ console.log('[1.2.0+1] matching clients:', urls.join(', '));
387
387
  });
388
388
  var promise = ClearCaches().then(function() {
389
389
  for (var sourceId in upgradeMsgPorts) {
390
390
  upgradeMsgPorts[sourceId].postMessage({ // notify new version available
391
391
  id: "upgrade",
392
- version: "1.0.16+1"
392
+ version: "1.2.0+1"
393
393
  });
394
394
  }
395
395
  upgradeMsgPorts = false;
@@ -410,7 +410,7 @@ self.addEventListener('fetch', function(event) {
410
410
  event.respondWith(
411
411
  caches.match(event.request).then(function(response)
412
412
  {
413
- if (response)
413
+ if (response && response.status != 299)
414
414
  return (response);
415
415
  if (terminated)
416
416
  return (new Response(undefined, {
@@ -421,7 +421,7 @@ self.addEventListener('fetch', function(event) {
421
421
  var promise;
422
422
  var url = new URL(request.url);
423
423
  var nocache = request.method != "GET"
424
- || url.search.length !== 0 && url.searchParams.get("naanver") !== "b2535090f4e111466794eea676c7d7aa"
424
+ || url.search.length !== 0 && url.searchParams.get("naanver") !== "d20959c47030d94a5b0694e7b9df2dc1"
425
425
  || request.headers.get('range');
426
426
  if (url.pathname.startsWith("/run/")) {
427
427
  nocache = true;
@@ -449,12 +449,13 @@ self.addEventListener('fetch', function(event) {
449
449
  return (item !== abortItem);
450
450
  });
451
451
  if (abortItem.aborted) {
452
+ nocache = true; // don't cache the 299!
452
453
  return (new Response(undefined, {
453
454
  status: 299,
454
455
  statusText: "Request Cancelled"
455
456
  }));
456
457
  }
457
- console.log("[1.0.16+1] fetch failed", request.url, e);
458
+ console.log("[1.2.0+1] fetch failed", request.url, e);
458
459
  return (new Response(undefined, {
459
460
  status: 404,
460
461
  statusText: "Fetch Failed"
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * column positioning: // // !
8
8
  *
9
- * Copyright (c) 2019-2022 by Richard C. Zulch
9
+ * Copyright (c) 2019-2024 by Richard C. Zulch
10
10
  *
11
11
  */
12
12
 
@@ -14,42 +14,77 @@
14
14
  /*
15
15
  * TermHost
16
16
  *
17
- * Make a host debugger target.
17
+ * Make an Nide API Server debugger target.
18
+ *
19
+ * Options:
20
+ * {
21
+ * type: <string> // target category, e.g. "Host", "api.yyy.com"
22
+ * workerID: <string> // kind of worker in server subsystem
23
+ * dispatcher: <tuple> // dispatcher function for incoming messages
24
+ * startup: <dictionary> // startup options
25
+ * clientID: <string> // (rare) our identification on client
26
+ * serverID: <string> // (rare) server subsystem for us
27
+ * }
18
28
  *
19
29
  */
20
30
 
21
- closure TermHost(track, api, name, workerID, options,
22
- local target, nlregex, clientID, serverID)
31
+ closure TermHost(track, api, name, options, local target, nlregex, xtarg)
23
32
  {
24
- target = require("../running/executors.nlg").ExecutorBase(track, "Host", name)
25
- clientID = "DebugWorkers"
26
- serverID = "Workers"
33
+ global(window)
34
+ options = merge({
35
+ type: "Host"
36
+ workerID: "myDebugWorker"
37
+ clientID: "DebugWorkers"
38
+ serverID: "Workers"
39
+ }, options)
40
+ target = require("../running/executors.nlg").ExecutorBase(track, options.type, name)
27
41
  nlregex = RegExp("\\n", "g")
28
- if !workerID
29
- workerID = "myDebugWorker"
30
- target.workerID = workerID
42
+ target.workerID = options.workerID
31
43
  target.msgQueue = []
32
-
44
+ target.dispatcher = call(options.dispatcher, function(reply) { reply })
45
+
46
+ // findWorker
47
+ //
48
+ function findWorker(testID, local instance) {
49
+ for instance in track.instances {
50
+ if instance.executor.hostWorkerMatch(api, testID)
51
+ return (instance.executor)
52
+ }
53
+ false
54
+ }
55
+
33
56
  // connect
34
57
  // Connect with the host if not already connected.
35
- closure connect(local pending) {
58
+ closure connect(local host, pending) {
36
59
  if target.ws
37
- return
38
- target.ws = xnew(window.WebSocket,"ws://".concat(window.location.host)) // ### use api parameter
60
+ return(list(false, { connected: true }))
61
+ host = xnew(window.URL, api.url).host
62
+ if !host || host == ""
63
+ return(list(Error("invalid URL ${api.url}")))
64
+ target.ws = xnew(window.WebSocket,"ws://${host}")
39
65
  pending = new(nonce)
40
-
66
+
41
67
  // onopen
42
68
  // The WebSocket connection is now open.
43
- target.ws.onopen = function onopen(e, local spawner) {
44
- pending.signal(list(false, { ok: true }))
45
- spawner = new(options.startup)
69
+ target.ws.onopen = function onopen(e, local info, spawner) {
70
+ info = {
71
+ name: name
72
+ workerID: target.workerID
73
+ }
74
+ if options.dispatcher
75
+ info.destID = api.instanceID // valid destination for relay
76
+ target.ws.send(window.JSON.stringify({
77
+ guid: api.guid
78
+ init: info
79
+ }))
80
+ spawner = new(options.startup) || { }
46
81
  spawner.name = name
47
82
  sendControl("spawn", spawner)
83
+ pending.signal(list(false, { ok: true }))
48
84
  }
49
85
 
50
86
  // onmessage
51
87
  // A message was received from the worker via WebSockets.
52
-
53
88
  target.ws.onmessage = function onmessage(e, message, local msg) {
54
89
  try {
55
90
  message = new(window.JSON.parse(e.data))
@@ -77,10 +112,29 @@ closure TermHost(track, api, name, workerID, options,
77
112
  if !target.written
78
113
  target.term.WriteLn("(reconnected)")
79
114
  }, 1000).run()
80
- else if message.respOp
81
- debuglog("unknown host response message:", message.respOp)
115
+ else if message.workerOp == "msgin" && message.payload.id == "targetin"
116
+ dispatcher(message) // request from Host
117
+ else if message.respOp || message.id
118
+ debuglog("unknown host response message:", message.respOp || message.id)
82
119
  }
83
-
120
+
121
+ // dispatcher
122
+ // Respond to a host request while preserving context to permit reply routing
123
+ closure dispatcher(message, local reply) {
124
+ reply = target.dispatcher(message.payload.data)
125
+ target.ws.send(window.JSON.stringify({
126
+ guid: api.guid
127
+ clientID: message.clientID
128
+ serverID: message.serverID
129
+ workerID: message.workerID
130
+ respOp: "msgout"
131
+ payload: {
132
+ id: "targetout",
133
+ data: reply
134
+ }
135
+ }))
136
+ }
137
+
84
138
  // onerror
85
139
  // An error occurred on the WebSocket connection.
86
140
  target.ws.onerror = function onerror(e) {
@@ -91,7 +145,8 @@ closure TermHost(track, api, name, workerID, options,
91
145
  // onclose
92
146
  // The WebSocket connection was closed.
93
147
  target.ws.onclose = function onclose(e) {
94
- debuglog("ws close:", e.code, e.reason)
148
+ if e.code != 1000 && e.code != 1001 && e.code != 1005 && e.code != 1006
149
+ debuglog("ws close:", e.code, e.reason)
95
150
  target.execClosed(Error("socket closed", e.code, e.reason))
96
151
  target.ws = false
97
152
  }
@@ -104,28 +159,17 @@ closure TermHost(track, api, name, workerID, options,
104
159
  //
105
160
  // New workers created/discovered on this host.
106
161
  //
107
- function hostWorkerUpdate(msg, local localID) {
108
-
109
- // findWorker
110
- //
111
- function findWorker(testID, local instance) {
112
- for instance in track.instances {
113
- if instance.executor.hostWorkerMatch(api, testID)
114
- return (instance.executor)
115
- }
116
- false
117
- }
118
-
162
+ function hostWorkerUpdate(msg, local localID, worker) {
119
163
  if msg.id == "spawned" {
120
164
  if !findWorker(msg.workerID)
121
- TermHost(track, api, msg.name, msg.workerID)
165
+ TermHost(track, api, msg.name, { type: options.type, workerID: msg.workerID })
122
166
  }
123
167
  else if msg.id == "termination"
124
168
  track.delete(findWorker(msg.workerID))
125
- else {
126
- for localID in msg.workerIDs
169
+ else if msg.id == "workerlist" {
170
+ for `(localID, worker) in msg.workers
127
171
  if !findWorker(localID)
128
- TermHost(track, api, localID, localID)
172
+ TermHost(track, api, worker.name, { type: options.type, workerID: localID })
129
173
  }
130
174
  }
131
175
 
@@ -135,7 +179,7 @@ closure TermHost(track, api, name, workerID, options,
135
179
  // True iff the specified information matches us.
136
180
  //
137
181
  target.hostWorkerMatch = function hostWorkerMatch(testApi, testID) {
138
- testApi === api && testID == workerID
182
+ testApi === api && testID == target.workerID
139
183
  }
140
184
 
141
185
  //
@@ -171,8 +215,8 @@ closure TermHost(track, api, name, workerID, options,
171
215
  target.sendControl = function sendControl(workerOp, payload) {
172
216
  target.ws.send(window.JSON.stringify({
173
217
  guid: api.guid
174
- clientID: clientID
175
- serverID: serverID
218
+ clientID: options.clientID
219
+ serverID: options.serverID
176
220
  workerID: target.workerID
177
221
  workerOp: workerOp
178
222
  payload: payload
@@ -243,12 +287,15 @@ closure TermHost(track, api, name, workerID, options,
243
287
  // Post a message to the target.
244
288
  //
245
289
 
246
- target.PostMessage = function PostMessage(data) {
247
- connect()
290
+ target.PostMessage = function PostMessage(data, local result) {
291
+ result = connect()
292
+ if result.0
293
+ return (result)
248
294
  send({ // send data to worker
249
295
  id: "targetin",
250
296
  data: data
251
297
  })
298
+ list(false, { posted: true })
252
299
  }
253
300
 
254
301
  //
@@ -339,6 +386,9 @@ closure TermHost(track, api, name, workerID, options,
339
386
  target.ws.close(1000) // 1000: normal close
340
387
  }
341
388
 
389
+ xtarg = findWorker(target.workerID)
390
+ if xtarg
391
+ return (list(false, xtarg)) // already exists
342
392
  track.add(target)
343
393
  connect() // connect right away to start receiving messages
344
394
 
@@ -353,26 +403,34 @@ closure TermHost(track, api, name, workerID, options,
353
403
  *
354
404
  * Make a local debugger target, returning a standard (error, object) tuple.
355
405
  *
406
+ * Options:
407
+ * {
408
+ * workerID: <string> // kind of worker in server subsystem
409
+ * // "NaanIDE" for local IDE
410
+ * // "NaanIDE-Debug" for browser debug target
411
+ * startup: <dictionary> // startup options
412
+ * type: <string> // (rare) target category, e.g. "Local"
413
+ * }
414
+ *
356
415
  */
357
416
 
358
- closure TermLocal(track, name, workerID, options,
359
- local target, nlregex, listener)
417
+ closure TermLocal(track, name, options, local target, nlregex, listener)
360
418
  {
361
- if workerID == "NaanIDE" {
419
+ global(js, window)
420
+ options = merge({
421
+ type: "Local"
422
+ }, options)
423
+ if options.workerID == "NaanIDE" {
362
424
  if !js.t
363
425
  return (list(Error("Cannot access Local IDE executor")))
364
426
  termInstallVirtualWatcher(track, js.t)
365
- return (termIDE(track, name, workerID, js.t)) // connect to IDE execution instance itself
427
+ return (termIDE(track, name, options.workerID, js.t)) // connect to IDE execution instance itself
366
428
  }
367
- if dictionary(options)
368
- options = new(options)
369
- else
370
- options = { }
371
- target = require("../running/executors.nlg").ExecutorBase(track, "Local", name)
429
+ target = require("../running/executors.nlg").ExecutorBase(track, options.type, name)
372
430
  nlregex = RegExp("\\n", "g")
373
- target.workerID = workerID
431
+ target.workerID = options.workerID
374
432
  target.msgQueue = []
375
- if workerID == "NaanIDE-Debug" {
433
+ if options.workerID == "NaanIDE-Debug" {
376
434
  target.worker = window.open(window.location.href, "_blank")
377
435
  target.worker.addEventListener("beforeunload", function(e) { // our target is closing
378
436
  destroy()
@@ -500,6 +558,7 @@ closure TermLocal(track, name, workerID, options,
500
558
  id: "targetin",
501
559
  data: data
502
560
  })
561
+ list(false, { posted: true })
503
562
  }
504
563
 
505
564
  //
@@ -591,6 +650,7 @@ closure TermLocal(track, name, workerID, options,
591
650
  */
592
651
 
593
652
  closure findIDEtarget(track, name, workerID, naancont, local target) {
653
+ global()
594
654
  for target in track.enumlist()
595
655
  if target.name == name && target.workerID == workerID && (!target.naancont || target.naancont === naancont)
596
656
  return (target)
@@ -608,6 +668,7 @@ closure findIDEtarget(track, name, workerID, naancont, local target) {
608
668
  */
609
669
 
610
670
  closure termIDE(track, name, workerID, naancont, title, local target, connected) {
671
+ global()
611
672
  target = findIDEtarget(track, name, workerID, naancont)
612
673
  if target { // new naancont on existing target
613
674
  target.updateController(naancont)
@@ -640,6 +701,7 @@ closure termIDE(track, name, workerID, naancont, title, local target, connected)
640
701
 
641
702
  target.PostMessage = function PostMessage(data) {
642
703
  naancont.DispatchMessage(data)
704
+ list(false, { posted: true })
643
705
  }
644
706
 
645
707
  // termAttach
@@ -770,6 +832,7 @@ closure termIDE(track, name, workerID, naancont, title, local target, connected)
770
832
  */
771
833
 
772
834
  closure termInstallVirtualWatcher(track, naancont) {
835
+ global()
773
836
 
774
837
  function watchVsites(msg, local error, target) {
775
838
  if msg.op == "VsiteOpen" {