@naanlang/naan 1.4.4 → 1.6.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.
- package/LICENSE.md +2 -2
- package/README.md +4 -4
- package/bin/index.js +25 -3
- package/dist/env_web.js +164 -22
- package/dist/naan.min.js +5 -5
- package/frameworks/browser/browser.nlg +2 -2
- package/frameworks/browser/https_request.nlg +2 -2
- package/frameworks/browser/sworker.js +28 -26
- package/frameworks/browser/terminals.nlg +342 -700
- package/frameworks/browser/worker.nlg +255 -0
- package/frameworks/browser/workers.nlg +5 -5
- package/frameworks/browser/ws_client.nlg +15 -10
- package/frameworks/client/apiclient.nlg +204 -19
- package/frameworks/client/client.nlg +22 -2
- package/frameworks/client/psm_client.nlg +12 -3
- package/frameworks/client/relaycon.nlg +87 -161
- package/frameworks/common/events.nlg +101 -0
- package/frameworks/common/repltools.nlg +80 -1
- package/frameworks/node/apiserver.nlg +39 -246
- package/frameworks/node/filesystem.nlg +39 -12
- package/frameworks/node/gitter.nlg +9 -7
- package/frameworks/node/http_request.nlg +34 -13
- package/frameworks/node/https_request.nlg +8 -3
- package/frameworks/node/node.nlg +2 -0
- package/frameworks/node/pty.nlg +346 -0
- package/frameworks/node/shell.nlg +4 -4
- package/frameworks/node/worker.nlg +409 -539
- package/frameworks/node/ws_client.nlg +2 -2
- package/frameworks/project/build.nlg +1 -1
- package/frameworks/project/projects.nlg +2 -1
- package/frameworks/running/debugnub.nlg +17 -4
- package/frameworks/running/executors.nlg +27 -274
- package/frameworks/running/instances.nlg +393 -0
- package/frameworks/running/remote_req.nlg +143 -0
- package/frameworks/running/remote_res.nlg +89 -0
- package/frameworks/running/taskexec.nlg +2 -2
- package/frameworks/service/imp.nlg +736 -0
- package/frameworks/service/model.nlg +71 -0
- package/frameworks/service/nubnode.nlg +105 -0
- package/frameworks/service/nubweb.nlg +106 -0
- package/frameworks/service/service.nlg +28 -0
- package/frameworks/storage/dbt_pouch.nlg +26 -13
- package/frameworks/storage/psm.nlg +39 -5
- package/frameworks/storage/psm_dbtables.nlg +10 -2
- package/lib/browser/env_web.js +171 -29
- package/lib/browser/env_webworker.js +91 -5
- package/lib/core/naanlib.js +5 -5
- package/lib/env_node.js +184 -8
- package/lib/env_nodeworker.js +74 -1
- package/package.json +1 -1
- package/plugins/serviceAws/aws_cloudwatchlogs.nlg +2 -1
- package/plugins/serviceAws/aws_dynamo.nlg +1 -0
- package/plugins/serviceAws/aws_s3.nlg +3 -3
- package/plugins/serviceAws/aws_sqs.nlg +3 -1
- package/plugins/serviceAws/aws_utils.nlg +3 -1
- package/plugins/serviceAws/dbt_aws.nlg +22 -20
- package/plugins/serviceAws/psm_aws.nlg +2 -1
- package/plugins/serviceNexara/speechrec.nlg +10 -6
- package/test/test_02_context.nlg +24 -1
- package/test/test_03_jsinterop.nlg +32 -3
- package/plugins/serviceGC/russian_trusted_root_ca_pem.crt +0 -33
|
@@ -6,11 +6,16 @@
|
|
|
6
6
|
*
|
|
7
7
|
* column positioning: // // !
|
|
8
8
|
*
|
|
9
|
-
* Copyright (c) 2017-
|
|
9
|
+
* Copyright (c) 2017-2026 by Richard C. Zulch
|
|
10
10
|
*
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
|
|
14
|
+
// Globals
|
|
15
|
+
|
|
16
|
+
serviceMod;;
|
|
17
|
+
|
|
18
|
+
|
|
14
19
|
/*
|
|
15
20
|
* APIServer
|
|
16
21
|
*
|
|
@@ -25,18 +30,21 @@
|
|
|
25
30
|
* basePort: <number> // starting point for search
|
|
26
31
|
* gateway: <string> // websocket gateway URL
|
|
27
32
|
* statics: [<string>, ...] // search paths for static content
|
|
33
|
+
* debug: <boolean> // verbose logging
|
|
28
34
|
* }
|
|
29
35
|
*
|
|
30
36
|
*/
|
|
31
37
|
|
|
32
38
|
closure APIServer(options, local server, chroot) {
|
|
33
|
-
global(App, JSpath, js, bodyParser, express, psmServerMod, workerMod)
|
|
39
|
+
global(App, serviceMod, JSpath, js, bodyParser, express, psmServerMod, workerMod)
|
|
34
40
|
apisLoadLibs()
|
|
35
41
|
server = new(object, this)
|
|
42
|
+
server.imp = App.imp
|
|
36
43
|
server.app = express()
|
|
37
44
|
server.guid = options.guid
|
|
38
45
|
server.notifyq = []
|
|
39
46
|
server.responseq = []
|
|
47
|
+
server.instanceID = UUID()
|
|
40
48
|
options = merge({
|
|
41
49
|
relpath: ""
|
|
42
50
|
name: "NaanServer"
|
|
@@ -94,6 +102,7 @@ closure APIServer(options, local server, chroot) {
|
|
|
94
102
|
return (result)
|
|
95
103
|
}
|
|
96
104
|
updatePort(port)
|
|
105
|
+
|
|
97
106
|
if server.guid
|
|
98
107
|
server.oururl = server.oururl.concat("?guid=", server.guid)
|
|
99
108
|
App.api = server // used remotely by client/relaycon.nlg
|
|
@@ -107,6 +116,7 @@ closure APIServer(options, local server, chroot) {
|
|
|
107
116
|
}
|
|
108
117
|
} else // default is native websocket protocols
|
|
109
118
|
server.ws = WebSocketServer(server)
|
|
119
|
+
|
|
110
120
|
WesRelay(server) // registers as subsystem
|
|
111
121
|
server.psm = psmServerMod.MakeServerPSM(server, options.name)
|
|
112
122
|
`(error, hostfs) = server.psm.conns.NodeFS.connect("Host", "NideFS", list(""))
|
|
@@ -116,7 +126,9 @@ closure APIServer(options, local server, chroot) {
|
|
|
116
126
|
return (list(error))
|
|
117
127
|
}
|
|
118
128
|
server.hostfs = hostfs
|
|
119
|
-
server.workco = workerMod.WorkerController(
|
|
129
|
+
server.workco = workerMod.WorkerController()
|
|
130
|
+
server.Register(server.workco.serverID, server.workco)
|
|
131
|
+
|
|
120
132
|
list(false, port)
|
|
121
133
|
}
|
|
122
134
|
|
|
@@ -431,15 +443,8 @@ closure WebSocketConn(wsgate, connID, local wsconn) {
|
|
|
431
443
|
wsconn.msgin = closure msgin(message, local error) {
|
|
432
444
|
if wsgate.guid && message.guid != wsgate.guid
|
|
433
445
|
error = Error("unauthorized")
|
|
434
|
-
else if message.init
|
|
435
|
-
if message.init.destID {
|
|
436
|
-
wsconn.relay = true
|
|
437
|
-
wsgate.subsystems.Relay.wsChangeDestID(conn, wsconn.info.destID, message.init.destID)
|
|
438
|
-
}
|
|
446
|
+
else if message.init
|
|
439
447
|
wsconn.info = merge(wsconn.info, message.init)
|
|
440
|
-
}
|
|
441
|
-
else if message.respOp && wsgate.subsystems.Relay
|
|
442
|
-
wsgate.subsystems.Relay.wsReceive(message, wsconn) // with respOp, must be a relay
|
|
443
448
|
else if message.clientID && wsgate.subsystems[message.serverID]
|
|
444
449
|
wsgate.subsystems[message.serverID].wsReceive(message, wsconn)
|
|
445
450
|
else
|
|
@@ -510,11 +515,11 @@ closure WebSocketServer(server, local websocks) {
|
|
|
510
515
|
websocks.subsystems[serverID].wsOpen(conn) // notify of our openings
|
|
511
516
|
})
|
|
512
517
|
|
|
513
|
-
|
|
518
|
+
websocks.wss.on("error", function(error) {
|
|
514
519
|
ErrorDebuglog(Error("WebSocketServer: connection error", error))
|
|
515
520
|
})
|
|
516
521
|
|
|
517
|
-
|
|
522
|
+
websocks.wss.on("close", function() {
|
|
518
523
|
debuglog("WebSocketServer has closed")
|
|
519
524
|
websocks.closed = true // note that we are closed
|
|
520
525
|
})
|
|
@@ -557,37 +562,31 @@ closure WesCon(server, websocks, socket, request, local wescon) {
|
|
|
557
562
|
destID: UUID()
|
|
558
563
|
address: new(socket._socket.address())
|
|
559
564
|
}
|
|
565
|
+
|
|
566
|
+
wescon.impconn = server.imp.responder(function(data) {
|
|
567
|
+
if wescon.closed
|
|
568
|
+
return
|
|
569
|
+
socket.send(JSONstringify(data)),
|
|
570
|
+
list(false, { sent: true })
|
|
571
|
+
}, {
|
|
572
|
+
secret: server.guid
|
|
573
|
+
label: "api-client"
|
|
574
|
+
})
|
|
560
575
|
|
|
561
576
|
// on message
|
|
562
577
|
//
|
|
563
578
|
// Incoming data.
|
|
564
579
|
//
|
|
565
|
-
socket.on("message", closure
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
if message.init.destID {
|
|
572
|
-
wescon.relay = true
|
|
573
|
-
websocks.subsystems.Relay.wsChangeDestID(wescon, wescon.info.destID, message.init.destID)
|
|
574
|
-
}
|
|
575
|
-
wescon.info = merge(wescon.info, message.init)
|
|
576
|
-
}
|
|
577
|
-
else if message.respOp && websocks.subsystems.Relay
|
|
578
|
-
websocks.subsystems.Relay.wsReceive(message, wescon) // with respOp, must be a relay
|
|
579
|
-
else if message.clientID && websocks.subsystems[message.serverID]
|
|
580
|
-
websocks.subsystems[message.serverID].wsReceive(message, wescon)
|
|
581
|
-
else
|
|
582
|
-
error = Error("can't route message")
|
|
583
|
-
} catch {
|
|
584
|
-
if true
|
|
585
|
-
error = Error("can't decode message JSON", exception)
|
|
580
|
+
socket.on("message", closure(text, local error, message) {
|
|
581
|
+
`(error, message) = JsonParse(text)
|
|
582
|
+
if !error {
|
|
583
|
+
message = new(message)
|
|
584
|
+
if wescon.impconn.dispatch(message)
|
|
585
|
+
return
|
|
586
586
|
}
|
|
587
|
-
|
|
588
|
-
socket.send(JSONstringify([error]))
|
|
587
|
+
debuglog("socket message:", text)
|
|
589
588
|
})
|
|
590
|
-
|
|
589
|
+
|
|
591
590
|
// on error
|
|
592
591
|
//
|
|
593
592
|
// Error has occurred on this connection.
|
|
@@ -607,222 +606,15 @@ closure WesCon(server, websocks, socket, request, local wescon) {
|
|
|
607
606
|
code: code
|
|
608
607
|
reason: reason
|
|
609
608
|
}
|
|
609
|
+
wescon.impconn.destroy()
|
|
610
610
|
websocks.closeConn(wescon)
|
|
611
611
|
})
|
|
612
|
-
|
|
613
|
-
// send
|
|
614
|
-
//
|
|
615
|
-
// Send a dictionary, which must have a clientID defined.
|
|
616
|
-
//
|
|
617
|
-
wescon.send = function send(message) {
|
|
618
|
-
if !message.clientID || !message.serverID
|
|
619
|
-
throw("Wescon.send: client and server IDs required")
|
|
620
|
-
if wescon.closed
|
|
621
|
-
list(Error("Socket connection closed", wescon.closed))
|
|
622
|
-
socket.send(JSONstringify(message)),
|
|
623
|
-
[false, {ok: true}]
|
|
624
|
-
}
|
|
625
612
|
|
|
626
613
|
// finis
|
|
627
614
|
wescon
|
|
628
615
|
};
|
|
629
616
|
|
|
630
617
|
|
|
631
|
-
/*
|
|
632
|
-
* WesRelay
|
|
633
|
-
*
|
|
634
|
-
* Web socket relay controller. This allows apiserver clients to discover each other and connect
|
|
635
|
-
* for remote evaluation, etc. A source connection makes a request of a destination connection, and
|
|
636
|
-
* the response is routed back to the original connection.
|
|
637
|
-
*
|
|
638
|
-
* This uses the following message fields for the requesting source:
|
|
639
|
-
* guid: security token
|
|
640
|
-
* clientID: defined by the source, this string defines where replies should be sent
|
|
641
|
-
* serverID: "Relay"
|
|
642
|
-
* destID: the destination connection
|
|
643
|
-
* endServerID: the serverID to use at the destination
|
|
644
|
-
* The message is modified with a virtual clientID to send to the destination, and then changed back
|
|
645
|
-
* for return to the requester.
|
|
646
|
-
*
|
|
647
|
-
* To discover the available destination connections a source should use:
|
|
648
|
-
* clientID: requester
|
|
649
|
-
* serverID: "Relay"
|
|
650
|
-
* relayOp: "getdests"
|
|
651
|
-
* which returns:
|
|
652
|
-
* relayOp: "destlist"
|
|
653
|
-
* destinations: [
|
|
654
|
-
* destID: guid for the connection
|
|
655
|
-
* address: socket port / family / ip address
|
|
656
|
-
* name: <string>
|
|
657
|
-
* workerID: id of destination worker
|
|
658
|
-
* ]
|
|
659
|
-
* New and closed destinations are sent asynchronously to each source:
|
|
660
|
-
* relayOp: "opened" / "closed"
|
|
661
|
-
* info: {
|
|
662
|
-
* destID: guid for the connection
|
|
663
|
-
* address: socket port / family / ip address
|
|
664
|
-
* name: <string>
|
|
665
|
-
* workerID: id of destination worker
|
|
666
|
-
* }
|
|
667
|
-
*
|
|
668
|
-
*/
|
|
669
|
-
|
|
670
|
-
closure WesRelay(server, local welay) {
|
|
671
|
-
global()
|
|
672
|
-
welay = new(object, this)
|
|
673
|
-
welay.serverID = "Relay" // our serverID as a subsystem
|
|
674
|
-
welay.destIDs = { } // destination conns index
|
|
675
|
-
welay.virtualIDs = { } // virtual clientID index
|
|
676
|
-
welay.links = new(weakmap) // source and dest connections
|
|
677
|
-
server.Register(welay.serverID, welay)
|
|
678
|
-
|
|
679
|
-
// getVirtualID
|
|
680
|
-
// Return the virtual clientID for a requesting source/clientID and destination.
|
|
681
|
-
function getVirtualID(sconn, clientID, dconn, local sdict, ddict, vid) {
|
|
682
|
-
sdict = welay.links[sconn]
|
|
683
|
-
if !sdict
|
|
684
|
-
welay.links[sconn] = sdict = {
|
|
685
|
-
virtualIDs: { }
|
|
686
|
-
}
|
|
687
|
-
ddict = welay.links[dconn]
|
|
688
|
-
if !ddict
|
|
689
|
-
welay.links[dconn] = ddict = {
|
|
690
|
-
virtualIDs: { }
|
|
691
|
-
}
|
|
692
|
-
vid = ddict.virtualIDs[clientID]
|
|
693
|
-
if !vid {
|
|
694
|
-
sdict.virtualIDs[clientID] = vid = UUID()
|
|
695
|
-
welay.virtualIDs[vid] = {
|
|
696
|
-
sconn: sconn
|
|
697
|
-
dconn: dconn
|
|
698
|
-
clientID: clientID
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
|
-
vid
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
// addConnection
|
|
705
|
-
// Record a new connection as a possible destination.
|
|
706
|
-
function addConnection(conn) {
|
|
707
|
-
welay.destIDs[conn.info.destID] = conn
|
|
708
|
-
}
|
|
709
|
-
|
|
710
|
-
// removeConnection
|
|
711
|
-
// Cleanup for a closed connection, be it source or destination or both.
|
|
712
|
-
function removeConnection(conn, local vid, cdict, did, dconn) {
|
|
713
|
-
welay.links[conn] = undefined
|
|
714
|
-
for `(vid, cdict) in welay.virtualIDs
|
|
715
|
-
if cdict.sconn === conn || cdict.dconn === conn
|
|
716
|
-
welay.virtualIDs[vid] = undefined
|
|
717
|
-
for `(did, dconn) in welay.destIDs
|
|
718
|
-
if dconn === conn
|
|
719
|
-
welay.destIDs[did] = undefined
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
// destList
|
|
723
|
-
// Make an array of destination connections, excluding the specified one.
|
|
724
|
-
//
|
|
725
|
-
welay.destList = function destList(conn, local output, did, dconn) {
|
|
726
|
-
output = []
|
|
727
|
-
for `(did, dconn) in welay.destIDs
|
|
728
|
-
if dconn !== conn && dconn.relay
|
|
729
|
-
output.push(dconn.info)
|
|
730
|
-
output
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
// wsReceive
|
|
734
|
-
//
|
|
735
|
-
// Process a message from the websocket API.
|
|
736
|
-
//
|
|
737
|
-
welay.wsReceive = function wsReceive(message, sconn, local dconn, error, cdict) {
|
|
738
|
-
if message.relayOp == "getdests" { // get destination list
|
|
739
|
-
message.relayOp = "destlist"
|
|
740
|
-
message.destinations = destList(sconn)
|
|
741
|
-
sconn.send(message)
|
|
742
|
-
return
|
|
743
|
-
}
|
|
744
|
-
else if message.destID && message.endServerID { // request from a source
|
|
745
|
-
dconn = welay.destIDs[message.destID]
|
|
746
|
-
if !dconn
|
|
747
|
-
error = Error("destID or endServerID invalid", message.relayOp)
|
|
748
|
-
else {
|
|
749
|
-
message.serverID = message.endServerID
|
|
750
|
-
message.clientID = getVirtualID(sconn, message.clientID, dconn)
|
|
751
|
-
message.endServerID = undefined
|
|
752
|
-
message.destID = undefined
|
|
753
|
-
dconn.send(message)
|
|
754
|
-
return
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
else { // response from a destination
|
|
758
|
-
cdict = welay.virtualIDs[message.clientID]
|
|
759
|
-
dconn = cdict.sconn // this is original source
|
|
760
|
-
if !cdict || !dconn
|
|
761
|
-
error = Error("cannot route reply", message.relayOp)
|
|
762
|
-
else {
|
|
763
|
-
message.endServerID = message.serverID
|
|
764
|
-
message.serverID = welay.serverID
|
|
765
|
-
message.clientID = cdict.clientID
|
|
766
|
-
message.destID = dconn.info.destID
|
|
767
|
-
dconn.send(message)
|
|
768
|
-
return
|
|
769
|
-
}
|
|
770
|
-
}
|
|
771
|
-
sconn.send({
|
|
772
|
-
serverID: message.serverID
|
|
773
|
-
clientID: message.clientID
|
|
774
|
-
respOp: "error"
|
|
775
|
-
payload: [error]
|
|
776
|
-
})
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
// wsOpen
|
|
780
|
-
//
|
|
781
|
-
// Note that a new connection was opened.
|
|
782
|
-
//
|
|
783
|
-
welay.wsOpen = function wsOpen(conn) {
|
|
784
|
-
addConnection(conn)
|
|
785
|
-
}
|
|
786
|
-
|
|
787
|
-
// wsClose
|
|
788
|
-
//
|
|
789
|
-
// Note that an existing connection was closed.
|
|
790
|
-
//
|
|
791
|
-
welay.wsClose = function wsClose(conn, local destID, sconn) {
|
|
792
|
-
removeConnection(conn)
|
|
793
|
-
if conn.relay
|
|
794
|
-
for `(destID, sconn) in welay.destIDs
|
|
795
|
-
sconn.send({
|
|
796
|
-
serverID: welay.serverID
|
|
797
|
-
clientID: "*"
|
|
798
|
-
relayOp: "closed"
|
|
799
|
-
info: conn.info
|
|
800
|
-
})
|
|
801
|
-
}
|
|
802
|
-
|
|
803
|
-
// wsChangeDestID
|
|
804
|
-
//
|
|
805
|
-
// The destID has changed (via the client init message.)
|
|
806
|
-
//
|
|
807
|
-
welay.wsChangeDestID = function wsChangeDestID(conn, prevID, newID, local destID, sconn) {
|
|
808
|
-
welay.destIDs[prevID] = undefined
|
|
809
|
-
welay.destIDs[newID] = conn
|
|
810
|
-
conn.info.destID = newID
|
|
811
|
-
for `(destID, sconn) in welay.destIDs
|
|
812
|
-
if destID != newID
|
|
813
|
-
sconn.send({
|
|
814
|
-
serverID: welay.serverID
|
|
815
|
-
clientID: "*"
|
|
816
|
-
relayOp: "opened"
|
|
817
|
-
info: conn.info
|
|
818
|
-
})
|
|
819
|
-
}
|
|
820
|
-
|
|
821
|
-
// finis
|
|
822
|
-
welay
|
|
823
|
-
};
|
|
824
|
-
|
|
825
|
-
|
|
826
618
|
/*
|
|
827
619
|
* ApisInit
|
|
828
620
|
*
|
|
@@ -840,7 +632,7 @@ function apisLoadLibs() {
|
|
|
840
632
|
};
|
|
841
633
|
|
|
842
634
|
function ApisInit(local manifest) {
|
|
843
|
-
manifest = `(APIServer, WebSocketGatewayServer, WebSocketConn, WebSocketServer, WesCon,
|
|
635
|
+
manifest = `(APIServer, WebSocketGatewayServer, WebSocketConn, WebSocketServer, WesCon,
|
|
844
636
|
apisLoadLibs, ApisInit)
|
|
845
637
|
|
|
846
638
|
Naan.module.build(module.id, "apiserver", function(modobj, compobj) {
|
|
@@ -851,4 +643,5 @@ function ApisInit(local manifest) {
|
|
|
851
643
|
require("./filesystem.nlg")
|
|
852
644
|
psmServerMod = require("./psm_server.nlg")
|
|
853
645
|
workerMod = require("./worker.nlg")
|
|
646
|
+
serviceMod = require("naanlib:frameworks/service/imp.nlg")
|
|
854
647
|
}();
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* column positioning: // // !
|
|
8
8
|
*
|
|
9
|
-
* Copyright (c) 2017-
|
|
9
|
+
* Copyright (c) 2017-2026 by Richard C. Zulch
|
|
10
10
|
*
|
|
11
11
|
*/
|
|
12
12
|
|
|
@@ -977,16 +977,41 @@ closure Filesystem(rootpath, local files, badCreatePathRegEx) {
|
|
|
977
977
|
//
|
|
978
978
|
// shellOpen
|
|
979
979
|
//
|
|
980
|
-
// Execute the
|
|
980
|
+
// Execute the platform's open utility, returning a standard tuple: `(error, result) which opens
|
|
981
|
+
// the specified path as a browser URL or invokes a shell script. This is used by the NaaN build
|
|
982
|
+
// system's "run" function.
|
|
983
|
+
// The args option has a dictionary that can override the "runner" utility and set arguments
|
|
984
|
+
// for specific platform(s):
|
|
985
|
+
// {
|
|
986
|
+
// darwin: {
|
|
987
|
+
// runner: "open" // default for platforms not mentioned
|
|
988
|
+
// // args: ["--args", <prog arg>] // program arguments if desired
|
|
989
|
+
// }
|
|
990
|
+
// linux / android: {
|
|
991
|
+
// runner: "bash" // for <path>.sh or <path>.tool
|
|
992
|
+
// runner: "xdg-open" // all other types
|
|
993
|
+
// }
|
|
994
|
+
// }
|
|
981
995
|
//
|
|
982
|
-
files.shellOpen = closure shellOpen(path, args, callback) {
|
|
996
|
+
files.shellOpen = closure shellOpen(path, args, callback, local platform, runner) {
|
|
983
997
|
if !callback
|
|
984
998
|
return (syncAdapter(shellOpen, path, args))
|
|
985
|
-
|
|
986
|
-
|
|
999
|
+
platform = js.g.process.platform
|
|
1000
|
+
runner = args[platform].runner
|
|
1001
|
+
if runner
|
|
1002
|
+
{ } // explicit runner
|
|
1003
|
+
else if platform == "linux" || platform == "android" {
|
|
1004
|
+
if RegExp("[.]((sh)|(tool))$").test(path)
|
|
1005
|
+
runner = "bash" // execute scripts
|
|
1006
|
+
else
|
|
1007
|
+
runner = "xdg-open" // "open" other things
|
|
1008
|
+
} else
|
|
1009
|
+
runner = "open" // default runner
|
|
1010
|
+
if args[platform]
|
|
1011
|
+
args = [path].concat(args[platform]) // has custom args
|
|
987
1012
|
else
|
|
988
1013
|
args = [path]
|
|
989
|
-
nodecp.execFile(
|
|
1014
|
+
nodecp.execFile(runner,
|
|
990
1015
|
args, // command line args to open
|
|
991
1016
|
{ }, // execution options
|
|
992
1017
|
function(err, stdout, stderr) {
|
|
@@ -1041,6 +1066,8 @@ closure Filesystem(rootpath, local files, badCreatePathRegEx) {
|
|
|
1041
1066
|
cmdargs.push(options.pattern)
|
|
1042
1067
|
cmdargs.push(JSpath.resolve(rootpath, localpath))
|
|
1043
1068
|
execOptions = { cmdargs: cmdargs }
|
|
1069
|
+
if js.g.process.platform == "win32" // to make "-i" work on Windows
|
|
1070
|
+
execOptions.spawnops = { env: merge(js.g.process.env, { LC_ALL: 'en_US.UTF-8' }) }
|
|
1044
1071
|
result = []
|
|
1045
1072
|
lineRE = RegExp("([^:]*):([0-9]+):(.*)$") // <path>:<line-number>:<matching-text-line>
|
|
1046
1073
|
if integer(options.maxCount)
|
|
@@ -1075,7 +1102,7 @@ closure Filesystem(rootpath, local files, badCreatePathRegEx) {
|
|
|
1075
1102
|
// has completed. Options are:
|
|
1076
1103
|
// {
|
|
1077
1104
|
// cmdargs: -- array of string arguments to the command (see below)
|
|
1078
|
-
//
|
|
1105
|
+
// spawnops: -- optional spawn options like cwd
|
|
1079
1106
|
// errproc: -- callback for stderr output lines, or false
|
|
1080
1107
|
// errLE: -- string or RegExp for stderr line ending
|
|
1081
1108
|
// outproc: -- optional callback for stdout output lines
|
|
@@ -1100,7 +1127,7 @@ closure Filesystem(rootpath, local files, badCreatePathRegEx) {
|
|
|
1100
1127
|
//
|
|
1101
1128
|
|
|
1102
1129
|
files.execLines = closure execLines(cmdname, options, callback,
|
|
1103
|
-
local output,
|
|
1130
|
+
local output, spawnops, spawn, linerErr, linerOut) {
|
|
1104
1131
|
if !callback
|
|
1105
1132
|
return (syncAdapter(execLines, cmdname, options))
|
|
1106
1133
|
if !string(cmdname) || cmdname == ""
|
|
@@ -1109,11 +1136,11 @@ closure Filesystem(rootpath, local files, badCreatePathRegEx) {
|
|
|
1109
1136
|
output = options.output
|
|
1110
1137
|
else
|
|
1111
1138
|
output = {}
|
|
1112
|
-
if options.
|
|
1113
|
-
|
|
1139
|
+
if options.spawnops
|
|
1140
|
+
spawnops = options.spawnops
|
|
1114
1141
|
else
|
|
1115
|
-
|
|
1116
|
-
spawn = nodecp.spawn(cmdname, options.cmdargs,
|
|
1142
|
+
spawnops = { }
|
|
1143
|
+
spawn = nodecp.spawn(cmdname, options.cmdargs, spawnops)
|
|
1117
1144
|
|
|
1118
1145
|
// completed
|
|
1119
1146
|
// execute callback only once
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* column positioning: // // !
|
|
8
8
|
*
|
|
9
|
-
* Copyright (c) 2020-
|
|
9
|
+
* Copyright (c) 2020-2025 by Richard C. Zulch
|
|
10
10
|
*
|
|
11
11
|
*/
|
|
12
12
|
|
|
@@ -56,7 +56,7 @@ closure Gitter(fs, local gitt) {
|
|
|
56
56
|
asyncResult(callback, false, gitt.roots[repopath])
|
|
57
57
|
execOptions = {
|
|
58
58
|
cmdargs: ["rev-parse", "--show-toplevel"]
|
|
59
|
-
|
|
59
|
+
spawnops: {
|
|
60
60
|
cwd: repopath
|
|
61
61
|
}
|
|
62
62
|
outLE: "\x00" // lines delimited by nulls
|
|
@@ -117,7 +117,7 @@ closure Gitter(fs, local gitt) {
|
|
|
117
117
|
repopath = fs.path.resolve(fs.rootpath, repopath)
|
|
118
118
|
execOptions = {
|
|
119
119
|
cmdargs: ["log", "-z", "--pretty=format:%H|%an|%ae|%aI|%B"]
|
|
120
|
-
|
|
120
|
+
spawnops: {
|
|
121
121
|
cwd: repopath
|
|
122
122
|
}
|
|
123
123
|
outLE: "\x00" // lines delimited by nulls
|
|
@@ -168,10 +168,12 @@ closure Gitter(fs, local gitt) {
|
|
|
168
168
|
if !string(repopath)
|
|
169
169
|
return (asyncResult(callback, Error("invalid path:", repopath)))
|
|
170
170
|
repopath = fs.path.resolve(fs.rootpath, repopath)
|
|
171
|
-
|
|
171
|
+
bridge = fs.path.normalize(bridgePath(repopath)) // ensure it works for our FS
|
|
172
|
+
if bridge == "."
|
|
173
|
+
bridge = "" // no bridge in this case
|
|
172
174
|
execOptions = {
|
|
173
175
|
cmdargs: ["status", "-z", "--porcelain"]
|
|
174
|
-
|
|
176
|
+
spawnops: {
|
|
175
177
|
cwd: repopath
|
|
176
178
|
}
|
|
177
179
|
outLE: "\x00" // lines delimited by nulls
|
|
@@ -219,13 +221,13 @@ closure Gitter(fs, local gitt) {
|
|
|
219
221
|
repopath = fs.path.resolve(fs.rootpath, repopath)
|
|
220
222
|
execOptions = {
|
|
221
223
|
cmdargs: ["show", options.hash.concat(":", bridgePath(repopath), gitPath(options.pathspec))]
|
|
222
|
-
|
|
224
|
+
spawnops: {
|
|
223
225
|
cwd: repopath
|
|
224
226
|
}
|
|
225
227
|
}
|
|
226
228
|
nodecp.execFile("git",
|
|
227
229
|
execOptions.cmdargs,
|
|
228
|
-
execOptions.
|
|
230
|
+
execOptions.spawnops,
|
|
229
231
|
function(err, stdout, stderr) {
|
|
230
232
|
if err
|
|
231
233
|
callback(Error("git failed", err, stderr))
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* column positioning: // // !
|
|
7
7
|
*
|
|
8
|
-
* Copyright (c) 2024-
|
|
8
|
+
* Copyright (c) 2024-2026 by Richard C. Zulch
|
|
9
9
|
*
|
|
10
10
|
*/
|
|
11
11
|
|
|
@@ -19,8 +19,9 @@
|
|
|
19
19
|
* {
|
|
20
20
|
* method: <string> // HTTP method (defaults to GET)
|
|
21
21
|
* headers: <dictionary> // key/data pairs
|
|
22
|
-
* putdata: <data> // data to put
|
|
22
|
+
* putdata: <data> // data to put, either string or JSON
|
|
23
23
|
* json: <boolean> // ignore content-type of return and decode JSON
|
|
24
|
+
* debug: <boolean> // log errors and status results
|
|
24
25
|
* }
|
|
25
26
|
*
|
|
26
27
|
* The return is a result tuple comprising:
|
|
@@ -34,13 +35,31 @@
|
|
|
34
35
|
*/
|
|
35
36
|
|
|
36
37
|
closure HttpRequest(url, options,
|
|
37
|
-
local pending, chunks, params, request) {
|
|
38
|
+
local host, pending, chunks, params, request, headers, putdata) {
|
|
38
39
|
global(js, nodeHttp)
|
|
40
|
+
try {
|
|
41
|
+
host = xnew(js.g.URL, url).host
|
|
42
|
+
if host.length < 3
|
|
43
|
+
return (Error("httpRequest: host empty"))
|
|
44
|
+
} catch {
|
|
45
|
+
return (Error("httpRequest:", exception))
|
|
46
|
+
}
|
|
39
47
|
pending = new(nonce)
|
|
40
48
|
chunks = []
|
|
49
|
+
headers = new(options.headers || []) // modifiable copy or false
|
|
50
|
+
headers.push(list("Host", host))
|
|
51
|
+
putdata = options.putdata
|
|
52
|
+
if putdata {
|
|
53
|
+
if !string(putdata) {
|
|
54
|
+
putdata = JSONstringify(putdata)
|
|
55
|
+
headers.push(list('Content-Type', 'application/json'))
|
|
56
|
+
}
|
|
57
|
+
// set explicit content-length to avoid default chunked encoding
|
|
58
|
+
headers.push(list('Content-Length', putdata.length))
|
|
59
|
+
}
|
|
41
60
|
params = {
|
|
42
61
|
method: options.method || undefined
|
|
43
|
-
headers:
|
|
62
|
+
headers: headers || undefined
|
|
44
63
|
}
|
|
45
64
|
request = nodeHttp.request(url, params, closure (response) {
|
|
46
65
|
response.on("data", function (chunk) {
|
|
@@ -49,7 +68,12 @@ closure HttpRequest(url, options,
|
|
|
49
68
|
response.on("end", function (local content, error) {
|
|
50
69
|
content = js.g.Buffer.concat(chunks)
|
|
51
70
|
if response.statusCode < 200 || response.statusCode >= 299 { // 299 is "cancelled"
|
|
52
|
-
|
|
71
|
+
if options.debug {
|
|
72
|
+
debuglog("httpRequest status:", url, response.statusCode, "message:", response.statusMessage)
|
|
73
|
+
if string(putdata)
|
|
74
|
+
putdata = putdata.substr(0,250)
|
|
75
|
+
debuglog("httpRequest putdata:", putdata)
|
|
76
|
+
}
|
|
53
77
|
pending.signal(list(Error("httpRequest statusCode:", url, response.statusCode, {
|
|
54
78
|
status: response.statusCode
|
|
55
79
|
}), false, { status: response.statusCode }))
|
|
@@ -63,23 +87,20 @@ closure HttpRequest(url, options,
|
|
|
63
87
|
else
|
|
64
88
|
error = Error("httpRequest terminated prematurely:", url, response.statusCode)
|
|
65
89
|
pending.signal(list(error, content, {
|
|
66
|
-
headers: response.headers
|
|
90
|
+
headers: new(response.headers)
|
|
67
91
|
status: response.statusCode
|
|
68
92
|
contentType: response.headers["content-type"]
|
|
69
93
|
}))
|
|
70
94
|
})
|
|
71
95
|
})
|
|
72
96
|
request.on("error", function hre(error) {
|
|
73
|
-
|
|
97
|
+
if options.debug
|
|
98
|
+
debuglog("httpRequest error:", url, "error:", error)
|
|
74
99
|
error = Error("httpRequest failed:", url, error)
|
|
75
100
|
pending.signal(list(error))
|
|
76
101
|
})
|
|
77
|
-
if
|
|
78
|
-
|
|
79
|
-
request.write(options.putdata)
|
|
80
|
-
else
|
|
81
|
-
request.write(JSONstringify(options.putdata))
|
|
82
|
-
}
|
|
102
|
+
if putdata
|
|
103
|
+
request.write(putdata)
|
|
83
104
|
request.end()
|
|
84
105
|
pending.wait()
|
|
85
106
|
};
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* column positioning: // // !
|
|
7
7
|
*
|
|
8
|
-
* Copyright (c) 2021-
|
|
8
|
+
* Copyright (c) 2021-2026 by Richard C. Zulch
|
|
9
9
|
*
|
|
10
10
|
*/
|
|
11
11
|
|
|
@@ -123,12 +123,17 @@ closure httpsApiReqOnce(url, options,
|
|
|
123
123
|
else
|
|
124
124
|
content = js.g.Buffer.concat(chunks)
|
|
125
125
|
if response.statusCode < 200 || response.statusCode >= 299 { // 299 is "cancelled"
|
|
126
|
-
if options.debug
|
|
126
|
+
if options.debug {
|
|
127
|
+
content = content.toString()
|
|
127
128
|
debuglog("HttpsApiRequest status:", url, response.statusCode, "message:", content)
|
|
129
|
+
if string(putdata)
|
|
130
|
+
putdata = putdata.substr(0,250)
|
|
131
|
+
debuglog("HttpsApiRequest putdata:", putdata)
|
|
132
|
+
}
|
|
128
133
|
pending.signal(list(Error("HttpsApiRequest statusCode:", url, response.statusCode, {
|
|
129
134
|
status: response.statusCode
|
|
130
135
|
}), false, {
|
|
131
|
-
headers: response.headers
|
|
136
|
+
headers: new(response.headers)
|
|
132
137
|
status: response.statusCode
|
|
133
138
|
}))
|
|
134
139
|
return
|
package/frameworks/node/node.nlg
CHANGED
|
@@ -124,6 +124,7 @@ function nodrInit(local manifest) {
|
|
|
124
124
|
throw("module frameworks/node cannot be used from a browser")
|
|
125
125
|
Naan.module.build(module.id, "node", function(modobj, compobj) {
|
|
126
126
|
require("../common/").LiveImport()
|
|
127
|
+
commonWatching = require("../common/watching.nlg")
|
|
127
128
|
compobj.manifest = manifest
|
|
128
129
|
function nodrReload() {
|
|
129
130
|
nodecp = js.r("child_process")
|
|
@@ -139,5 +140,6 @@ function nodrInit(local manifest) {
|
|
|
139
140
|
module.reload = nodrReload
|
|
140
141
|
module.exports.NaanlangDir = NaanlangDir
|
|
141
142
|
module.exports.NaanlangRC = NaanlangRC
|
|
143
|
+
module.exports.NodeStreamLiner = NodeStreamLiner
|
|
142
144
|
})
|
|
143
145
|
} ();
|