@naanlang/naan 1.3.2 → 1.4.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 (36) hide show
  1. package/LICENSE.md +1 -1
  2. package/README.md +1 -1
  3. package/bin/index.js +2 -2
  4. package/dist/naan.min.js +4 -4
  5. package/frameworks/browser/sworker.js +23 -23
  6. package/frameworks/browser/terminals.nlg +25 -10
  7. package/frameworks/browser/ws_client.nlg +1 -1
  8. package/frameworks/client/psm_client.nlg +1 -1
  9. package/frameworks/client/relaycon.nlg +2 -1
  10. package/frameworks/common/preferences.nlg +1 -1
  11. package/frameworks/node/apiserver.nlg +3 -3
  12. package/frameworks/node/worker.nlg +103 -45
  13. package/frameworks/project/build.nlg +0 -1
  14. package/frameworks/project/jszip.js +0 -0
  15. package/frameworks/project/projects.nlg +1 -1
  16. package/frameworks/running/executors.nlg +64 -28
  17. package/frameworks/running/running.nlg +85 -3
  18. package/frameworks/running/taskexec.nlg +24 -4
  19. package/frameworks/storage/dbt_pouch.nlg +1 -1
  20. package/frameworks/storage/psm.nlg +11 -1
  21. package/lib/browser/env_web.js +6 -6
  22. package/lib/browser/env_webworker.js +2 -0
  23. package/lib/core/naanlib.js +4 -4
  24. package/lib/env_nodeworker.js +2 -0
  25. package/package.json +1 -1
  26. package/plugins/gitClient/gitutils.nlg +2 -2
  27. package/plugins/nodeLib/node_https.nlg +2 -2
  28. package/plugins/serviceAws/lambda_naan_control.nlg +3 -3
  29. package/plugins/serviceGC/gc_api.nlg +1 -1
  30. package/plugins/serviceGitHub/gitops.nlg +4 -3
  31. package/plugins/serviceGitHub/psm_github.nlg +1 -1
  32. package/plugins/serviceGitLab/gitops.nlg +2 -1
  33. package/plugins/serviceGitLab/psm_gitlab.nlg +1 -1
  34. package/plugins/serviceYC/yc_vm.nlg +1 -1
  35. package/test/test_02_context.nlg +1 -1
  36. package/test/test_07_lingo.nlg +3 -3
@@ -24,19 +24,20 @@ closure ExecutorBase(track, type, name, local exec) {
24
24
  exec.name = name
25
25
 
26
26
  // execReset
27
+ //
27
28
  // Initialize remote communications for a new or restarted instance.
28
-
29
+ //
29
30
  exec.execReset = function execReset() {
30
31
  exec.pending = { }
31
32
  exec.nextSendID = toint(RandomBytes(3)) // keep our sequence code unique for shared servers
32
- exec.nextContextID = 1
33
33
  }
34
34
  execReset()
35
35
 
36
36
  // execSend
37
+ //
37
38
  // Send a message to the running instance and return a nonce to wait on. If data is not a
38
39
  // dictionary then it is ignored.
39
-
40
+ //
40
41
  exec.execSend = closure execSend(msg, data, local pending, error) {
41
42
  if !dictionary(data)
42
43
  data = { }
@@ -52,8 +53,9 @@ closure ExecutorBase(track, type, name, local exec) {
52
53
  }
53
54
 
54
55
  // execReceived
56
+ //
55
57
  // The specified message was received from the instance, so signal it.
56
-
58
+ //
57
59
  exec.execReceived = closure execReceived(data, local pending) {
58
60
  pending = exec.pending[data.xid]
59
61
  if !pending { // not waiting on message
@@ -68,15 +70,17 @@ closure ExecutorBase(track, type, name, local exec) {
68
70
  }
69
71
 
70
72
  // execSetStatus
73
+ //
71
74
  // Received a status update from the instance.
72
-
75
+ //
73
76
  exec.execSetStatus = function execSetStatus(status) {
74
77
  exec.xstatus = status
75
78
  }
76
79
 
77
80
  // execFailed
81
+ //
78
82
  // Communication has failed.
79
-
83
+ //
80
84
  exec.execFailed = closure execFailed(error, local xid, pending) {
81
85
  exec.pending.error = error
82
86
  for `(xid, pending) in exec.pending
@@ -84,8 +88,10 @@ closure ExecutorBase(track, type, name, local exec) {
84
88
  }
85
89
 
86
90
  // execClosed
91
+ //
92
+ //
87
93
  // Note that the connection has closed, so signal all the waiters.
88
-
94
+ //
89
95
  exec.execClosed = closure execClosed(error, local xid, pending) {
90
96
  for `(xid, pending) in exec.pending {
91
97
  pending.signal(list(error))
@@ -94,10 +100,13 @@ closure ExecutorBase(track, type, name, local exec) {
94
100
  }
95
101
 
96
102
  // context
97
- // Return a context object for the instance, or an error, in a result tuple. Currently this does
98
- // not ever reclaim old contexts on the instance end of things, but it should. ###
99
-
100
- exec.context = closure context(local error, retries, ms, delta, excon) {
103
+ //
104
+ // Return a context object for the instance, or an error, in a result tuple. The optional state
105
+ // dictionary argument initializes the context with the state of a previous one to facilitate
106
+ // persistent reconnection. Currently this does not ever reclaim old contexts on the instance
107
+ // end of things, but it should. ###
108
+ //
109
+ exec.context = closure context(state, local error, retries, ms, delta, excon) {
101
110
  retries = 0
102
111
  ms = milliseconds()
103
112
  delta = 1
@@ -114,13 +123,14 @@ closure ExecutorBase(track, type, name, local exec) {
114
123
  sleep(delta)
115
124
  if delta < 100
116
125
  delta *= 2 }
117
- excon = ExecutorContext(exec, exec.nextContextID++)
126
+ excon = ExecutorContext(exec, state)
118
127
  excon.register()
119
128
  }
120
129
 
121
130
  // attention
131
+ //
122
132
  // Focus the UI on this instance because something needs attention.
123
-
133
+ //
124
134
  exec.attention = function attention() {
125
135
  track.attention(exec)
126
136
  }
@@ -141,20 +151,37 @@ closure ExecutorBase(track, type, name, local exec) {
141
151
  * ability to track namespace changes as the result of evaluating an expression. They do not override
142
152
  * normal namespace protections. The contexts exposed here preserve the active namespace after the
143
153
  * expression is evaluated.
154
+ * The optional state argument preserves the context state so that it can be restored for
155
+ * resuming persistent connections.
144
156
  *
145
157
  */
146
158
 
147
- closure ExecutorContext(exec, contextID, local context, util) {
159
+ closure ExecutorContext(exec, state, local context, coder, util) {
148
160
  global(apply)
149
161
  context = new(object, this)
162
+ if !state || !state.contextID
163
+ state = {
164
+ contextID: UUID()
165
+ }
166
+ context.state = state
167
+ coder = packageCoder(evalf)
150
168
  util = modlist().Util.exports
151
169
 
170
+ // getState
171
+ //
172
+ // Get context state for future initialization
173
+ //
174
+ context.getState = function getState() {
175
+ context.state
176
+ }
177
+
152
178
  // register
179
+ //
153
180
  // Register or re-register our context with the worker.
154
-
181
+ //
155
182
  context.register = closure register(local pending, result) {
156
183
  pending = exec.execSend("register", {
157
- contextID: contextID
184
+ contextID: state.contextID
158
185
  })
159
186
  result = pending.wait()
160
187
  if result.1
@@ -164,10 +191,11 @@ closure ExecutorContext(exec, contextID, local context, util) {
164
191
  }
165
192
 
166
193
  // eval
194
+ //
167
195
  // Evaluate the specified expression in the current worker context. The optional put argument
168
196
  // if non-false is a tuple of symbols that should be transfered to the remote end, while get is
169
197
  // an optional tuple of symbols to retrieve after evaluation.
170
-
198
+ //
171
199
  context.eval = closure evalf(expr, put, get, local roots, pkg, data, pending, result) {
172
200
  if tuple(put)
173
201
  roots = cons(`expr, put)
@@ -175,9 +203,11 @@ closure ExecutorContext(exec, contextID, local context, util) {
175
203
  roots = `(expr)
176
204
  pkg = util.pkgSave(`expr, roots, {
177
205
  nsignore: true
178
- comments: true })
206
+ comments: true
207
+ objectEncoder: coder.encodeObject
208
+ })
179
209
  data = {
180
- contextID: contextID
210
+ contextID: state.contextID
181
211
  expr: pkg }
182
212
  if tuple(get)
183
213
  data.get = get
@@ -190,22 +220,25 @@ closure ExecutorContext(exec, contextID, local context, util) {
190
220
  localroots: false
191
221
  intern: false
192
222
  execute: false
223
+ objectDecoder: coder.decodeObject
193
224
  })))
194
225
  else
195
226
  result
196
227
  }
197
-
228
+
198
229
  // evalq
230
+ //
199
231
  // Evaluate the specified QUOTED expression in the current worker context.
200
-
232
+ //
201
233
  context.evalq = macro evalq(expr, put, get) {
202
234
  evalf(expr, eval(put), eval(get))
203
235
  }
204
-
236
+
205
237
  // apply
238
+ //
206
239
  // Apply list of arguments to the specified procure evaluated remotely. If the procedure is a
207
240
  // not a tuple then this uses the tuple definition if there is one.
208
-
241
+ //
209
242
  context.apply = closure applyf(proc, args, put, get) {
210
243
  if !tuple(proc) && tuple(proc.proc)
211
244
  proc = proc.proc
@@ -213,9 +246,10 @@ closure ExecutorContext(exec, contextID, local context, util) {
213
246
  }
214
247
 
215
248
  // call
249
+ //
216
250
  // Call the specified function remotely using local arguments:
217
251
  // context.call(proc, args...)
218
-
252
+ //
219
253
  context.call = macro callf args {
220
254
  closure (proc, arglist, local eargs) {
221
255
  while arglist
@@ -225,13 +259,14 @@ closure ExecutorContext(exec, contextID, local context, util) {
225
259
  }
226
260
 
227
261
  // rpc
262
+ //
228
263
  // Execute a remote procedure call without package translation, to reduce overhead. This will not
229
264
  // properly transfer Naan-specific types like symbols, and the results will be native JavaScript.
230
265
  // The procedure must be specified as a symbol or string.
231
-
266
+ //
232
267
  context.rpc = closure rpc(proc, args, local data, pending, result) {
233
268
  data = {
234
- contextID: contextID
269
+ contextID: state.contextID
235
270
  proc: proc
236
271
  args: args }
237
272
  pending = exec.execSend("rpc", data)
@@ -647,7 +682,7 @@ closure ExecutorTracker(local xtrak) {
647
682
  */
648
683
 
649
684
  closure execLambda(track, url, name, workerID, local target, nlregex, clientID, serverID) {
650
- global()
685
+ global(js)
651
686
  target = ExecutorBase(track, "Lambda", name)
652
687
  clientID = "DebugWorkers"
653
688
  serverID = "Workers"
@@ -896,7 +931,8 @@ closure execLambda(track, url, name, workerID, local target, nlregex, clientID,
896
931
  */
897
932
 
898
933
  function execInit(local manifest) {
899
- manifest = `(ExecutorBase, ExecutorContext, ExecutorParallel, ExecutorTracker, execLambda, execInit)
934
+ manifest = `(ExecutorBase, ExecutorContext, ExecutorParallel, ExecutorTracker, execLambda,
935
+ execInit)
900
936
 
901
937
  Naan.module.build(module.id, "executors", function(modobj, compobj) {
902
938
  require("./running.nlg")
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * column positioning: // // !
8
8
  *
9
- * Copyright (c) 2021-2023 by Richard C. Zulch
9
+ * Copyright (c) 2021-2024 by Richard C. Zulch
10
10
  *
11
11
  */
12
12
 
@@ -19,6 +19,7 @@
19
19
  */
20
20
 
21
21
  function rootproc(procdef, local parent) {
22
+ global()
22
23
  loop { // can only find out proc
23
24
  parent = procdef.1@\.parent
24
25
  if !parent
@@ -38,6 +39,7 @@ function rootproc(procdef, local parent) {
38
39
  */
39
40
 
40
41
  function baseproc(procdef) {
42
+ global()
41
43
  if procdef.1@\.base.proc
42
44
  { }
43
45
  else
@@ -58,6 +60,7 @@ function baseproc(procdef) {
58
60
  */
59
61
 
60
62
  function pathmatch(parent, path, local segment, nth, child) {
63
+ global()
61
64
  if empty(path)
62
65
  return (parent)
63
66
  segment = pop(path)
@@ -83,6 +86,7 @@ function pathmatch(parent, path, local segment, nth, child) {
83
86
  */
84
87
 
85
88
  function procpath(procdef, local path, segment, parent, peers, nth, peer) {
89
+ global()
86
90
  loop {
87
91
  segment = procdef.1
88
92
  parent = segment@\.parent
@@ -114,6 +118,7 @@ function procpath(procdef, local path, segment, parent, peers, nth, peer) {
114
118
  */
115
119
 
116
120
  function procname(name, local parent) {
121
+ global()
117
122
  parent = name@\.parent
118
123
  name = tostring(name)
119
124
  if parent
@@ -136,6 +141,7 @@ function procname(name, local parent) {
136
141
  */
137
142
 
138
143
  function procstring(procdef, options, local name, output, parm) {
144
+ global()
139
145
  if options.fullname || !defined(options, `fullname)
140
146
  name = procname(procdef.1) // default is full name
141
147
  else
@@ -156,6 +162,82 @@ function procstring(procdef, options, local name, output, parm) {
156
162
  }
157
163
 
158
164
 
165
+ /*
166
+ * packageCoder
167
+ *
168
+ * Encode and decode objects for the NaaN packager to establish proxies for remote access.
169
+ *
170
+ */
171
+
172
+ closure packageCoder(evalf, local coder) {
173
+ global()
174
+ coder = new(object, this)
175
+ coder.wmapEncoding = new(weakmap)
176
+ coder.mapDecoding = { }
177
+
178
+ // encodeObject
179
+ //
180
+ // Encode an object into the ident dictionary so that the remote end can create a proxy.
181
+ //
182
+ coder.encodeObject = closure encodeObject(obj, local objectID) {
183
+ if coder.wmapEncoding[obj]
184
+ { }
185
+ else {
186
+ objectID = UUID()
187
+ coder.mapDecoding[objectID] = obj
188
+ coder.wmapEncoding[obj] = {
189
+ name: obj@\.closure || undefined
190
+ objectID: objectID
191
+ }
192
+ }
193
+ }
194
+
195
+ // decodeObject
196
+ //
197
+ // Decode the ident dictionary into a proxy object allowing method invocation.
198
+ //
199
+ coder.decodeObject = closure decodeObject(ident, local x, obj) {
200
+ if coder.mapDecoding[ident.objectID]
201
+ { }
202
+ else if ident.name {
203
+ putproc(ident.name, false)
204
+ obj = coder.mapDecoding[ident.objectID] = closure(nn, objectID) {
205
+ call(enclose(list(`closure, nn, list(`local, `x),
206
+ quote(x=new(object, nn)),
207
+ quote(x@\.tostring = function() { "{proxy ${nn}}" }),
208
+ quote(x@\.undefined = closure undef args {
209
+ let (result) {
210
+ result = evalf(list(`\.execute\-method, objectID, args))
211
+ if result.0
212
+ throw("remoting failure: ${ErrorString(result.0)}")
213
+ else
214
+ result.1
215
+ } ()
216
+ }), `x)))
217
+ } (ident.name, ident.objectID)
218
+ coder.wmapEncoding[obj] = ident
219
+ obj
220
+ } else
221
+ coder.mapDecoding[ident.objectID] = new(object)
222
+ }
223
+
224
+ // invokeMethod
225
+ //
226
+ // Invoke a local object method from the remote proxy, returning a result tuple.
227
+ //
228
+ coder.invokeMethod = closure invokeMethod(objectID, args, local obj) {
229
+ obj = coder.mapDecoding[objectID]
230
+ if obj
231
+ list(false, eval(list(deref, obj, pop(args), args)))
232
+ else
233
+ list(Error("object ${objectID} not found"))
234
+ }
235
+
236
+ // finis
237
+ coder
238
+ };
239
+
240
+
159
241
  /*
160
242
  * runnInit
161
243
  *
@@ -164,8 +246,8 @@ function procstring(procdef, options, local name, output, parm) {
164
246
  */
165
247
 
166
248
  function runnInit(local manifest) {
167
- manifest = `(rootproc, baseproc, pathmatch, procpath, procname, procstring, runnInit)
168
-
249
+ manifest = `(rootproc, baseproc, pathmatch, procpath, procname, procstring, packageCoder,
250
+ runnInit)
169
251
  Naan.module.build(module.id, "running", closure(modobj, compobj) {
170
252
  require("../common").LiveImport()
171
253
  compobj.manifest = manifest
@@ -19,12 +19,27 @@
19
19
  *
20
20
  */
21
21
 
22
- closure TaskDispatcher(replier, local contexts, gohome, util) {
22
+ closure TaskDispatcher(replier, local contexts, gohome, coder, util) {
23
23
  global()
24
24
  contexts = { }
25
25
  gohome = makeActivator()
26
+ coder = packageCoder(evalf)
26
27
  util = modlist().Util.exports
27
- closure(data, local reply, restorens, context, expr, roots, proc, args, result) {
28
+
29
+ // sendEval
30
+ //
31
+ // Send an evaluation request to the original execution context.
32
+ //
33
+ closure evalf(expr) {
34
+ debuglog("TaskDispatcher doesn't yet implement reverse proxy objects")
35
+ list(Error("### reverse object proxy not implemented"))
36
+ }
37
+
38
+ // dispatcher
39
+ //
40
+ // Return a new task dispatcher.
41
+ //
42
+ closure dispatcher(data, local reply, restorens, context, expr, roots, proc, args, result) {
28
43
  reply = {
29
44
  xmsg: "error"
30
45
  xid: data.xid
@@ -70,10 +85,14 @@ closure TaskDispatcher(replier, local contexts, gohome, util) {
70
85
  intern: true // intern symbols
71
86
  localroots: true // make roots local if needed to intern
72
87
  execute: true // execute auto-running procedures
88
+ objectDecoder: coder.decodeObject
73
89
  })
74
90
  expr = caar(expr)
91
+ if expr.0 == `\.execute\-method
92
+ expr = coder.invokeMethod(expr.1, expr.2) // object method, args
93
+ else
94
+ expr = evalactive(expr) // evaluate expression
75
95
  reply.error = undefined
76
- expr = evalactive(expr)
77
96
  if data.get
78
97
  roots = new(data.get).map(function(sym){ // new() to get symbol from compress
79
98
  compress(sym)
@@ -84,7 +103,8 @@ closure TaskDispatcher(replier, local contexts, gohome, util) {
84
103
  roots = `(expr)
85
104
  reply.result = util.pkgSave(`expr, roots, {
86
105
  nsignore: true // don't report namespace errors
87
- comments: true }) // include comments
106
+ comments: true
107
+ objectEncoder: coder.encodeObject}) // include comments
88
108
  if context.namespace !== nsactive().0
89
109
  { // evaluation changed namespace
90
110
  context.namespace = nsactive().0
@@ -508,7 +508,7 @@ closure ListBrowserDBs(local pending, output, item) {
508
508
  */
509
509
 
510
510
  closure DbConnector(psm, local connClassID, connector, watch) {
511
- global()
511
+ global(js)
512
512
  connClassID = "PouchDB"
513
513
  connector = new(object, this)
514
514
  connector.psm = psm
@@ -260,6 +260,16 @@ closure MakePSMutil(fs, local util) {
260
260
 
261
261
  util.deepcopy = closure deepcopy(inpath, sources, outfs, outpath, options, callback,
262
262
  local errors, copies, fsopt, folders) {
263
+
264
+ // fixpath
265
+ // Fix the path to match the destination.
266
+ function fixpath(path) {
267
+ if fs.path.sep == outfs.path.sep
268
+ path
269
+ else
270
+ path.replaceAll(fs.path.sep, outfs.path.sep)
271
+ }
272
+
263
273
  if !callback
264
274
  return (syncAdapter(deepcopy, inpath, sources, outfs, outpath, options))
265
275
  if sources && !array(sources) || !string(inpath) || !string(outpath)
@@ -281,7 +291,7 @@ closure MakePSMutil(fs, local util) {
281
291
  asyncArray(sources, 10, closure mklist(entry,
282
292
  local srcpath, destpath, epath, error, stat, folder, node) {
283
293
  srcpath = fs.path.join(inpath, entry)
284
- destpath = outfs.path.join(outpath, entry)
294
+ destpath = fixpath(outfs.path.join(outpath, entry))
285
295
  if destpath == "."
286
296
  destpath = "" // joined empty strings
287
297
  `(error, stat) = fs.info(srcpath, fsopt)
@@ -536,7 +536,7 @@ exports.NaanControllerWeb = function() {
536
536
  statedoc.curversion = kStateCurrentVersion;
537
537
  statedoc.firstver = kStateFirstVersion;
538
538
  statedoc.licensee = "MIT-License";
539
- statedoc.verstring = "1.3.2+1";
539
+ statedoc.verstring = "1.4.0+1";
540
540
  statedoc.date = new Date().toISOString();
541
541
  statedoc.prefs = prefs;
542
542
  statedoc.naan = naanlib.saveState(false); // true to optimize, which is a bit slower
@@ -554,7 +554,7 @@ exports.NaanControllerWeb = function() {
554
554
  || statedoc.firstver > kStateCurrentVersion
555
555
  || statedoc.curversion < kStateFirstVersion
556
556
  || statedoc.licensee != "MIT-License"
557
- || statedoc.verstring != "1.3.2+1")
557
+ || statedoc.verstring != "1.4.0+1")
558
558
  {
559
559
  localStorage.removeItem("NaanState_Hosted");
560
560
  return (false);
@@ -613,7 +613,7 @@ exports.NaanControllerWeb = function() {
613
613
  op: "VsiteOpen",
614
614
  name: vsiteName,
615
615
  naancont: contSelf,
616
- title: vsiteName + " 1.3.2+1"
616
+ title: vsiteName + " 1.4.0+1"
617
617
  });
618
618
  }
619
619
  } catch (e) {
@@ -627,7 +627,7 @@ exports.NaanControllerWeb = function() {
627
627
  op: "VsiteClose",
628
628
  name: vsiteName,
629
629
  naancont: contSelf,
630
- title: vsiteName + " 1.3.2+1"
630
+ title: vsiteName + " 1.4.0+1"
631
631
  });
632
632
  termTextOut("\x1b[90m\x1b[3m".concat("\nwindow closed", "\x1b[0m\n"));
633
633
  }
@@ -642,8 +642,8 @@ exports.NaanControllerWeb = function() {
642
642
  naanlib.banner();
643
643
  var hostpath = window.location.origin.concat(naanlib.js.r("path").dirname(window.location.pathname));
644
644
  naanlib.start({
645
- cmd: 'App.version = "1.3.2+1";;\r\n'
646
- + 'App.cache = "a8f6e5b80d3b14e762d56efbef3ae771";;\r\n'
645
+ cmd: 'App.version = "1.4.0+1";;\r\n'
646
+ + 'App.cache = "233c1fed240ea5c0dd1c14562f30021b";;\r\n'
647
647
  + 'Naan.module.requireQuery({ naanver: App.cache });;\r\n'
648
648
  + 'Naan.module.webparse("naan_init.nlg", "' + hostpath + '", { naanver: App.cache });;\r\n'
649
649
  });
@@ -145,6 +145,8 @@ exports.NaanControllerWebWorker = function NaanControllerWebWorker() {
145
145
  naanlib.textLine(msg.text); // keyboard text from terminal
146
146
  else if (msg.id == "start")
147
147
  { // start execution with optional state
148
+ targSelf.workerID = msg.workerID;
149
+ targSelf.workerGUID = msg.workerGUID;
148
150
  naanlib.banner();
149
151
  naanlib.start({
150
152
  state: msg.state,