@naanlang/naan 1.0.6 → 1.0.8

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 +3 -4
  2. package/README.md +20 -2
  3. package/bin/index.js +2 -2
  4. package/dist/env_web.js +91 -18
  5. package/dist/naan.min.js +7 -7
  6. package/frameworks/browser/browser.nlg +92 -3
  7. package/frameworks/browser/https_request.nlg +2 -2
  8. package/frameworks/browser/sworker.js +17 -17
  9. package/frameworks/browser/terminals.nlg +14 -12
  10. package/frameworks/browser/workers.nlg +13 -6
  11. package/frameworks/client/psm_client.nlg +1 -0
  12. package/frameworks/common/common.nlg +39 -8
  13. package/frameworks/node/filesystem.nlg +32 -26
  14. package/frameworks/node/gitter.nlg +9 -4
  15. package/frameworks/node/https_request.nlg +19 -14
  16. package/frameworks/project/build.nlg +187 -74
  17. package/frameworks/project/projects.nlg +42 -17
  18. package/frameworks/running/running.nlg +33 -11
  19. package/frameworks/running/taskexec.nlg +1 -1
  20. package/frameworks/storage/file_manager.nlg +2 -1
  21. package/frameworks/storage/psm.nlg +20 -4
  22. package/frameworks/storage/psm_dbtables.nlg +13 -3
  23. package/lib/browser/env_web.js +93 -20
  24. package/lib/browser/env_webworker.js +10 -0
  25. package/lib/core/naanlib.js +7 -7
  26. package/package.json +2 -1
  27. package/plugins/serviceAws/aws/aws-sdk-node.min.js +1 -1
  28. package/plugins/serviceAws/aws/aws-sdk.min.js +1 -1
  29. package/plugins/serviceAws/aws/lambda_ws_init.nlg +2 -1
  30. package/plugins/serviceAws/aws_dynamo.nlg +45 -26
  31. package/plugins/serviceAws/aws_dynextra.nlg +157 -32
  32. package/plugins/serviceAws/aws_lambda.nlg +4 -8
  33. package/plugins/serviceAws/aws_s3.nlg +74 -28
  34. package/plugins/serviceAws/aws_sqs.nlg +113 -0
  35. package/plugins/serviceAws/dbt_aws.nlg +73 -24
  36. package/plugins/serviceAws/psm_aws.nlg +30 -11
  37. package/plugins/serviceAws/serviceAws.nlg +2 -4
  38. package/plugins/serviceYC/generic_api.yaml +36 -0
  39. package/plugins/serviceYC/naanide-relay-index.js +148 -0
  40. package/plugins/serviceYC/serviceYC.nlg +59 -0
  41. package/plugins/serviceYC/yc_function.nlg +161 -0
  42. package/test/test_01_core.nlg +2 -2
  43. package/test/test_02_context.nlg +2 -2
  44. package/test/test_05_strings.nlg +11 -1
  45. package/test/test_06_lingoparse.nlg +43 -14
@@ -23,6 +23,7 @@
23
23
  * contentType: <string> // sets Content-Type header, otherwise auto-determined
24
24
  * range: <string> // sets range header
25
25
  * headers: [`(key, value)] // add header(s) to the request, overriding defaults
26
+ * debug: <boolean> // log errors and status results
26
27
  * }
27
28
  *
28
29
  */
@@ -52,25 +53,27 @@ closure HttpsApiRequest(url, options, local urlq, reqOptions, putdata, item, pen
52
53
  if error
53
54
  return (list(Error("HttpsApiRequest encode:", url)))
54
55
  }
55
- fetchOptions.body = putdata
56
+ reqOptions.body = putdata
56
57
  }
57
58
  if array(options.headers)
58
59
  for item in options.headers
59
60
  reqOptions.headers[item.0] = item.1
60
61
  pending = new(nonce)
61
62
  chunks = []
62
- request = nodeHttps.request(url, reqOptions, function (response) {
63
- if response.statusCode < 200 || response.statusCode >= 300 {
64
- pending.signal(list(Error("HttpsApiRequest statusCode:", url, response.statusCode, {
65
- status: response.statusCode
66
- })))
67
- return
68
- }
63
+ request = nodeHttps.request(url, reqOptions, closure (response) {
69
64
  response.on("data", function (chunk) {
70
65
  chunks.push(chunk)
71
66
  })
72
67
  response.on("end", function (local content, error) {
73
- content = Buffer.concat(chunks)
68
+ content = js.g.Buffer.concat(chunks)
69
+ if response.statusCode < 200 || response.statusCode >= 300 {
70
+ if options.debug
71
+ debuglog("HttpsApiRequest status:", url, response.statusCode, "message:", content)
72
+ pending.signal(list(Error("HttpsApiRequest statusCode:", url, response.statusCode, {
73
+ status: response.statusCode
74
+ })))
75
+ return
76
+ }
74
77
  if response.complete {
75
78
  if response.headers["content-type"].startsWith("application/json") {
76
79
  `(error, content) = JsonParse(content.toString())
@@ -86,16 +89,18 @@ closure HttpsApiRequest(url, options, local urlq, reqOptions, putdata, item, pen
86
89
  }
87
90
  }
88
91
  else
89
- error = Error("HttpsApiRequest terminated prematurely:", url)
92
+ error = Error("HttpsApiRequest terminated prematurely:", url, response.statusCode)
90
93
  pending.signal(list(error, content))
91
94
  })
92
95
  })
93
- request.on("error", function(error) {
96
+ request.on("error", function hre(error) {
97
+ if options.debug
98
+ debuglog("HttpsApiRequest error:", url, "error:", error)
94
99
  error = Error("HttpsApiRequest failed:", url, error)
95
100
  pending.signal(list(error))
96
101
  })
97
- if data
98
- request.write(data)
102
+ if putdata
103
+ request.write(putdata)
99
104
  request.end()
100
105
  pending.wait()
101
106
  };
@@ -109,7 +114,7 @@ closure HttpsApiRequest(url, options, local urlq, reqOptions, putdata, item, pen
109
114
  */
110
115
 
111
116
  function https_nodeInit(local manifest) {
112
- manifest = `(HttpsGetJson, HttpsPutJson, https_nodeInit)
117
+ manifest = `(HttpsApiRequest, https_nodeInit)
113
118
 
114
119
  Naan.module.build(module.id, "https_request", function(modobj, compobj) {
115
120
  require("./node.nlg")
@@ -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-2023 by Richard C. Zulch
10
10
  *
11
11
  */
12
12
 
@@ -47,6 +47,20 @@
47
47
  * srcpath - the source path in common to all sources
48
48
  * destpath - the destination path in common to all outputs
49
49
  *
50
+ * Special paths:
51
+ * srcpath - the Build object looks for all sources within the srcpath specified when it's
52
+ * instantiated. For example, the files in a group are located in the join of the
53
+ * paths: <srcpath> / <group.input> / <srcname.xx>.
54
+ * destpath - the Build object writes all output files within the destpath specified when
55
+ * it's instantiated. For example, the files output from a group are located in
56
+ * the join of the paths: <destpath> / <group.output> / <srcname.xx>. Note that
57
+ * group.input is used to locate the files, but is not part of the output.
58
+ * @ - the @ character stands in for a persistent, temporary directory within the
59
+ * build folder that replaces srcpath or destpath. It can be used in fields that
60
+ * are otherwise absolute: group.input, group.output, and template paths. Build
61
+ * detects piplines formed from temporary files and processes them in order from
62
+ * beginning to end.
63
+ *
50
64
  * Templates:
51
65
  * A dictionary where each key is a destfile as described above, and the data for the key
52
66
  * is the path to a template file relative to srcpath. When a template file exists for multiple-
@@ -76,6 +90,7 @@
76
90
  * licfile: <license file, path relative to srcpath>
77
91
  * verfile: <version file, path relative to srcpath>
78
92
  * buildno: <build number file, path relative to srcpath>
93
+ * xvrfile: <external version file, path relative to srcpath>
79
94
  * run: {
80
95
  * "open": "Nide_LaunchMac.tool",
81
96
  * "args": []
@@ -129,6 +144,7 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
129
144
  build = new(object, this)
130
145
  build.srcpath = srcpath
131
146
  build.destpath = destpath
147
+ build.intpath = JSpath.join(destpath, "tmp")
132
148
  build.tasks = { // all tasks counter
133
149
  copy: 0,
134
150
  version: 0,
@@ -152,11 +168,16 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
152
168
  build.verfilepath = JSpath.join(build.srcpath, rules.verfile)
153
169
  if rules.buildno
154
170
  build.buildnopath = JSpath.join(build.srcpath, rules.buildno)
171
+ if rules.xvrfile
172
+ build.xvrfilepath = JSpath.join(build.srcpath, rules.xvrfile)
155
173
  if rules.templates { // templates for catenates
156
174
  let (tpath, writepath) {
157
175
  build.tepaths = { } // template for each catenate output
158
176
  for tpath in rules.templates {
159
- writepath = JSpath.join(build.destpath, tpath)
177
+ if tpath.startsWith("@")
178
+ writepath = JSpath.join(build.intpath, tpath.substring(1))
179
+ else
180
+ writepath = JSpath.join(build.destpath, tpath)
160
181
  build.tepaths[writepath] = JSpath.join(build.srcpath, rules.templates[tpath])
161
182
  }
162
183
  build.testats = { }
@@ -616,7 +637,7 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
616
637
  }
617
638
 
618
639
  groups = new(build.groups) // copy so we can append more groups as needed
619
- sourcemax = groups.reduce(function(acc, group) { Math.max(acc, group.sources.length) }, 0)
640
+ sourcemax = 10000
620
641
  catenates = { } // catenate groups
621
642
  pending = new(nonce)
622
643
  pending.active = 0
@@ -635,11 +656,18 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
635
656
  groupVerStat, source, sdex) {
636
657
  ++pending.active
637
658
  future(function test1() {
638
- if group.input
659
+ if group.input.startsWith("@") { // intermediate directory
660
+ group.intSource = true
661
+ indirpath = JSpath.join(build.intpath, group.input.substring(1))
662
+ }
663
+ else if group.input
639
664
  indirpath = JSpath.join(build.srcpath, group.input)
640
665
  else
641
666
  indirpath = build.srcpath
642
- outdirpath = JSpath.join(build.destpath, group.output)
667
+ if group.output.startsWith("@")
668
+ outdirpath = JSpath.join(build.intpath, group.output.substring(1))
669
+ else
670
+ outdirpath = JSpath.join(build.destpath, group.output)
643
671
  destcheck = new(group.destcheck).reverse() // get next destination to check
644
672
  groupVerStat = verfileStat
645
673
  if group.depend_buildno {
@@ -655,11 +683,7 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
655
683
  else
656
684
  outfile = JSpath.join(outdirpath, source)
657
685
  fs.info(infile, false, function(error, instat, local listing, addgroup, file) {
658
- if error
659
- build.errorlist.push(error)
660
- else if instat.type != "directory"
661
- addEntry(infile, outfile, instat, sdex) // add execution entry if needed
662
- else { // add a new group for directory source
686
+ if !error && instat.type == "directory" {
663
687
  `(error, listing) = fs.dirList(infile, { stat: true })
664
688
  if error
665
689
  build.errorlist.push(error)
@@ -681,6 +705,10 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
681
705
  groups.push(addgroup)
682
706
  }
683
707
  }
708
+ else if !error || group.intSource
709
+ addEntry(infile, outfile, instat, sdex) // add execution entry
710
+ else
711
+ build.errorlist.push(error)
684
712
  --pending.active
685
713
  doMore()
686
714
  })
@@ -692,11 +720,17 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
692
720
  // Add an entry to execution list if it's needed.
693
721
  //
694
722
  closure addEntry(infile, outfile, instat, sdex, local error, outstat, entry) {
723
+ if sdex >= sourcemax {
724
+ build.errorlist.push(Error("Too many sources in one group:", groupno.group))
725
+ return
726
+ }
695
727
  entry = {
696
728
  readpath: infile,
697
729
  writepath: outfile
698
730
  }
699
- if group.input // insource is the name within zip/cat
731
+ if group.input.startsWith("@")
732
+ entry.insource = JSpath.join(group.input.substring(1), group.sources[sdex])
733
+ else if group.input // insource is the name within zip/cat
700
734
  entry.insource = JSpath.join(group.input, group.sources[sdex])
701
735
  else
702
736
  entry.insource = group.sources[sdex]
@@ -716,6 +750,10 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
716
750
  entry.unzip = true
717
751
  entry.writepath = outdirpath
718
752
  }
753
+ if group.tasks.indexOf("catenate") >= 0
754
+ entry.catenate = true
755
+ if group.tasks.indexOf("zip") >= 0
756
+ entry.zip = true
719
757
  if group.tasks.indexOf("naanpack") >= 0 || group.tasks.indexOf("jspack") >= 0 || group.tasks.indexOf("syntax") >= 0 {
720
758
  let (inpar, outpar, extension) { // change extension for package output
721
759
  inpar = JSpath.parse(infile)
@@ -740,12 +778,12 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
740
778
  inpar.base = inpar.name.concat(extension)
741
779
  entry.insource = JSpath.format(inpar)
742
780
  }
743
- } else
781
+ } else if !entry.zip && !entry.catenate
744
782
  entry.copy = true // fall back to copy
745
783
  } ()
746
784
  }
747
785
  if group.tasks.indexOf("minimize") >= 0 {
748
- if infile.endsWith(".js") {
786
+ if infile.endsWith(".js") || outfile.endsWith(".js") {
749
787
  entry.minimize = true
750
788
  if group.minprefix
751
789
  entry.minprefix = group.minprefix // use group minify prefix
@@ -753,10 +791,7 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
753
791
  entry.minprefix = build.minprefix // use generic minify prefix
754
792
  }
755
793
  }
756
- if group.tasks.indexOf("catenate") >= 0
757
- entry.catenate = true
758
- if group.tasks.indexOf("zip") >= 0 {
759
- entry.zip = true
794
+ if entry.zip {
760
795
  if !entry.writepath.toLowerCase().endsWith(".zip")
761
796
  entry.writepath = entry.writepath.concat(".zip") // always ends with .zip
762
797
  outfile = entry.writepath
@@ -770,23 +805,24 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
770
805
  ++pending.active
771
806
  if destcheck { // destcheck is special target for determining if task needed
772
807
  entry.destcheck = JSpath.join(outdirpath, destcheck.pop())
773
- fs.info(entry.destcheck, false, addIfOutOfDate)
808
+ fs.info(entry.destcheck, false, addOne)
774
809
  } else
775
- fs.info(outfile, false, addIfOutOfDate)
810
+ fs.info(outfile, false, addOne)
776
811
 
777
- function addIfOutOfDate(error, outstat, local entryop) {
812
+ function addOne(error, outstat, local entryop) {
778
813
  if error || toint(instat.mtimeMs) > toint(outstat.mtimeMs) {
814
+ entry.outOfDate = true
779
815
  catenates[entry.writepath] = true // this entire group needed
780
- build.filelist.push(entry) // destination is out of date
816
+ build.filelist.push(entry)
781
817
  entryop = "make:"
782
818
  } else if entry.catorder {
783
819
  build.filelist.push(entry) // needed if any in group out of date
784
- entryop = "maybe-needed:"
820
+ entryop = "cat-check:"
785
821
  }
786
822
  else
787
823
  entryop = "up-to-date:"
788
824
  if debugMake
789
- debuglog(entryop, entry.*, instat.mtimeMs, outstat.mtimeMs, entry.readpath, entry.writepath)
825
+ debuglog(entryop, entry.readpath, entry.*, instat.mtimeMs, outstat.mtimeMs, entry.writepath)
790
826
  --pending.active
791
827
  doMore()
792
828
  }
@@ -807,6 +843,32 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
807
843
  doMore()
808
844
  pending.wait()
809
845
 
846
+ //
847
+ // Cross-check inputs from intermediate files that will be built
848
+ //
849
+
850
+ let (outint, entry) {
851
+ outint = {}
852
+ for entry in build.filelist
853
+ if entry.outOfDate && entry.writepath.startsWith(build.intpath) {
854
+ outint[entry.writepath] = entry // at least one component out of date
855
+ break
856
+ }
857
+ for entry in build.filelist
858
+ if outint[entry.writepath].catorder > entry.catorder
859
+ outint[entry.writepath] = entry // find first component
860
+ for entry in build.filelist
861
+ if outint[entry.readpath] {
862
+ entry.outOfDate = true
863
+ catenates[entry.writepath] = true // this entire group needed
864
+ entry.depends = outint[entry.readpath]
865
+ if !entry.depends.done
866
+ entry.depends.done = new(nonce)
867
+ if debugMake
868
+ debuglog("add-make:", entry.readpath, entry.*, entry.writepath)
869
+ }
870
+ } ()
871
+
810
872
  //
811
873
  // Remove unneeded catenate entries and index the remainder
812
874
  //
@@ -819,11 +881,16 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
819
881
  else if catenates[entry.writepath] {
820
882
  if !renumber[entry.writepath]
821
883
  renumber[entry.writepath] = []
822
- renumber[entry.writepath].push(entry) // this catenate is needed
884
+ if renumber[entry.writepath].indexOf(entry) < 0
885
+ renumber[entry.writepath].push(entry) // this catenate is needed
886
+ entry.outOfDate = true // this entry is needed
823
887
  true
824
888
  } else
825
889
  false // this one is not
826
890
  })
891
+ build.filelist = build.filelist.filter(function(entry) {
892
+ entry.outOfDate
893
+ })
827
894
  for wpath in renumber { // assign entries in catorder
828
895
  renumber[wpath].sort(function(a,b) { a.catorder <=> b.catorder })
829
896
  catindex = 0
@@ -844,7 +911,8 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
844
911
  // checked and any that have all the parts are written out. Catenates that don't have all their
845
912
  // parts are reported as an error in addition to the error that caused them to be incomplete.
846
913
  //
847
- closure execute(local error, entries, pending, licFileDefs, buildno, verFileDefs, baddestpaths, catenates) {
914
+ closure execute(local error, licFileDefs, buildno, verFileDefs, xvrFileDefs,
915
+ entries, pending, baddestpaths, catenates) {
848
916
  if build.tasks.license > 0
849
917
  `(error, licFileDefs) = readSubstitutionFile(build.licfilepath)
850
918
  if build.tasks.version > 0 {
@@ -855,9 +923,17 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
855
923
  } else
856
924
  build.buildno = buildno = toint(buildno.trim())
857
925
  build.buildno = buildno
858
- `(error, verFileDefs) = readSubstitutionFile(build.verfilepath, {
859
- BuildNumber: buildno
860
- })
926
+ if build.xvrfilepath {
927
+ `(error, xvrFileDefs) = readSubstitutionFile(build.xvrfilepath)
928
+ if error
929
+ build.errorlist.push(error)
930
+ }
931
+ if !xvrFileDefs
932
+ xvrFileDefs = { }
933
+ xvrFileDefs.BuildNumber = buildno
934
+ `(error, verFileDefs) = readSubstitutionFile(build.verfilepath, xvrFileDefs)
935
+ build.version = verFileDefs["Version"]
936
+ verFileDefs["CacheBuster"] = HashMD5(build.version.concat(Math.random()))
861
937
  }
862
938
  if build.tasks.catenate > 0 || build.tasks.zip > 0 {
863
939
  build.templates = { }
@@ -885,6 +961,8 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
885
961
  closure processOneFile(entry, local result, content, error) {
886
962
  ++pending.active
887
963
  future(function build1(local dirpath) {
964
+ if entry.depends
965
+ entry.depends.done.wait() // wait for our precursor
888
966
  dirpath = JSpath.dirname(entry.writepath)
889
967
  if baddestpaths[dirpath]
890
968
  { } // error already reported
@@ -900,7 +978,7 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
900
978
  erase: true // not really needed
901
979
  overwrite: true // overwrite files
902
980
  force: true // ignore modify date
903
- preserve: true // preserve attributes
981
+ idmode: true // preserve ids and mode
904
982
  })
905
983
  if error // deepcopy returns a list of errors
906
984
  error = error.0 // but we're only doing one item
@@ -932,10 +1010,20 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
932
1010
  if !catenates[entry.writepath]
933
1011
  catenates[entry.writepath] = new(Array(entry.catcount))
934
1012
  entry.content = content
935
- catenates[entry.writepath][entry.catindex] = entry }
936
- else
1013
+ catenates[entry.writepath][entry.catindex] = entry
1014
+ if catReady(entry.writepath) {
1015
+ `(error, result) = writeCatenate(entry.writepath)
1016
+ if error
1017
+ build.errorlist.push(error)
1018
+ catenates[entry.writepath].0.done.signal(error)
1019
+ }
1020
+ }
1021
+ else {
937
1022
  `(error, result) = fs.writeFile(entry.writepath, content)
938
- }
1023
+ entry.done.signal(error)
1024
+ }
1025
+ } else
1026
+ entry.done.signal(error)
939
1027
  }
940
1028
  if error
941
1029
  build.errorlist.push(error)
@@ -956,52 +1044,76 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
956
1044
  if pending.remaining > 0
957
1045
  pending.wait() // don't wait if nothing to do
958
1046
 
1047
+ // catReady
959
1048
  //
960
- // Write out any completed catenates
1049
+ // Return true iff the catenate has all its elements.
961
1050
  //
1051
+ function catReady(writepath, local entry) {
1052
+ for entry in catenates[writepath]
1053
+ if !entry.content
1054
+ return (false)
1055
+ true
1056
+ }
962
1057
 
963
- let (writepath, template, entry, content, catsub, dozip, docat, fsoptions, zipmap, error, result) {
964
- for :out1 writepath in catenates {
965
- template = build.templates[writepath] // optional
966
- catsub = { }
967
- for entry in catenates[writepath] { // error check and substitution build
968
- if !entry.content {
969
- build.errorlist.push(Error("Catenate incomplete:", writepath))
970
- continue :out1 }
971
- catsub[entry.insource] = entry.content
972
- if entry.zip
973
- dozip = true
974
- if entry.catenate
975
- docat = true
1058
+ // writeCatenate
1059
+ //
1060
+ // Write out a catenate, returning a standard result tuple.
1061
+ //
1062
+ function writeCatenate(writepath,
1063
+ local template, entry, content, catsub, dozip, docat, fsoptions, zipmap, error, result) {
1064
+ template = build.templates[writepath] // optional in some cases ### should warn in others
1065
+ catsub = { }
1066
+ for entry in catenates[writepath] { // error check and substitution build
1067
+ if !entry.content
1068
+ return (list(Error("Incomplete zip/cat:", writepath)))
1069
+ catsub[entry.insource] = entry.content
1070
+ if entry.zip
1071
+ dozip = true
1072
+ if entry.catenate
1073
+ docat = true
1074
+ }
1075
+ if docat { // catenate the files
1076
+ if dozip
1077
+ return (list(Error("Cannot combine zip/cat into:", writepath)))
1078
+ if template {
1079
+ `(content, result) = applySubstitutions(template, catsub)
1080
+ if length(catsub) != result
1081
+ return (list(Error("Matched ", result, " of ", length(catsub),
1082
+ "substitutions in template:", build.tepaths[writepath])))
976
1083
  }
977
- if docat { // catenate the files
978
- if template {
979
- `(content, result) = applySubstitutions(template, catsub)
980
- if length(catsub) != result
981
- build.errorlist.push(Error("Matched ", result, " of ", length(catsub),
982
- "substitutions in template:", build.tepaths[writepath]))
983
- }
984
- else
985
- content = catenates[writepath].map(function(entry){entry.content}).join("\n")
986
- if dozip {
987
- zipmap = { }
988
- zipmap[JSpath.basename(writepath)] = content
989
- `(error, content) = zip(zipmap) // compress the catenated file
990
- }
991
- } else {
992
- if template
993
- zipmap = applyZipTemplate(template, catsub)
994
- else
995
- zipmap = trimZipPaths(catsub)
996
- `(error, content) = zip(zipmap) // compress each of the files
1084
+ else
1085
+ content = catenates[writepath].map(function(entry){entry.content}).join("\n")
1086
+ if dozip {
1087
+ zipmap = { }
1088
+ zipmap[JSpath.basename(writepath)] = content
1089
+ `(error, content) = zip(zipmap) // compress the catenated file
997
1090
  }
998
- if dozip
999
- fsoptions = { encoding: "base64" }
1000
- if !error
1001
- `(error, result) = fs.writeFile(writepath, content, fsoptions)
1002
- if error
1003
- build.errorlist.push(error)
1091
+ } else {
1092
+ if template
1093
+ zipmap = applyZipTemplate(template, catsub)
1094
+ else
1095
+ zipmap = trimZipPaths(catsub)
1096
+ `(error, content) = zip(zipmap) // compress each of the files
1004
1097
  }
1098
+ if dozip
1099
+ fsoptions = { encoding: "base64" }
1100
+ if !error
1101
+ `(error, result) = fs.writeFile(writepath, content, fsoptions)
1102
+ if error
1103
+ list(error)
1104
+ else
1105
+ list(false, { ok: true })
1106
+ }
1107
+
1108
+ //
1109
+ // Check for any incomplete catenates.
1110
+ //
1111
+
1112
+ let (writepath, entry) {
1113
+ for writepath in catenates
1114
+ for entry in catenates[writepath]
1115
+ if !entry.content
1116
+ build.errorlist.push(Error("Missing zip/cat element", writepath, "--", entry.readpath))
1005
1117
  } ()
1006
1118
  }
1007
1119
 
@@ -1054,7 +1166,7 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
1054
1166
  "\nfrom: ", JSpath.join(fs.rootpath, build.srcpath),
1055
1167
  "\n to: ", build.destpath,
1056
1168
  "\ntype: ", options.stage,
1057
- "\nvers: ", options.buildnum,
1169
+ "\nvers: ", build.version,
1058
1170
  "\n", sink.taketext())
1059
1171
  `(error) = fs.writeFile(JSpath.join(build.destpath, "build_log.txt"), outext)
1060
1172
  if error
@@ -1082,7 +1194,7 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
1082
1194
  `(error, executor) = track.spawn(build.runway.spawn.0, build.runway.spawn.1, "Build-".concat(build.buildnum), {
1083
1195
  startup: {
1084
1196
  initcmds: maintext
1085
- dirpath: JSpath.join(fs.rootpath, build.destpath)
1197
+ dirpath: fs.path.resolve(fs.path.sep, fs.rootpath, build.destpath)
1086
1198
  }
1087
1199
  })
1088
1200
  if error
@@ -1132,6 +1244,7 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
1132
1244
  `(error, site) = track.spawn("V-Site", build.runway."v-site", fs, vpath)
1133
1245
  if error
1134
1246
  return (list(Error("Builder: cannot create virtual site", error)))
1247
+ sleep(1) // needed on Windows or it won't open, fiik why
1135
1248
  window = js.w.open(url, title, features)
1136
1249
  list(false, { ok: true }) // we don't know the executor
1137
1250
  }