firefly-compiler 0.4.70 → 0.4.71

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.
@@ -91,11 +91,12 @@ extend self: Dependencies {
91
91
  dependencyLock.do(donePath.absolute()) {
92
92
  if(!donePath.exists()) {
93
93
  Log.trace("Fetching " + location)
94
- let response = httpClient.fetch(location, throw = False)
95
- if(!response.ok()) {
96
- panic("Could not download dependency: " + location)
94
+ let buffer = httpClient.get(location, []) {response =>
95
+ if(!response.ok()) {
96
+ panic("Could not download dependency: " + location)
97
+ }
98
+ response.readBuffer()
97
99
  }
98
- let buffer = response.readBuffer()
99
100
  if(dependencyPath.exists()) {
100
101
  dependencyPath.delete()
101
102
  }
@@ -3,7 +3,7 @@ capability BrowserSystem {}
3
3
  extend self: BrowserSystem {
4
4
 
5
5
  httpClient(): HttpClient
6
- target js async "return null"
6
+ target js async "return typeof globalThis !== 'undefined' ? globalThis : window"
7
7
 
8
8
  mainTask(): Task
9
9
  target js async "return self_.task_"
@@ -6,82 +6,145 @@ data FetchRedirect {
6
6
  RedirectError
7
7
  RedirectManual
8
8
  }
9
+ data FetchOptions(
10
+ redirect: FetchRedirect = RedirectFollow
11
+ referrer: Option[String] = None
12
+ integrity: Option[String] = None
13
+ mode: Option[String] = None
14
+ credentials: Option[String] = None
15
+ cache: Option[String] = None
16
+ )
9
17
 
10
18
  extend self: HttpClient {
19
+
20
+ get[T](
21
+ url: String
22
+ headers: List[Pair[String, String]]
23
+ body: FetchResponse => T
24
+ ): T {
25
+ self.fetch("GET", url, headers.toMap(), None, FetchOptions(), body)
26
+ }
27
+
28
+ post[T](
29
+ url: String
30
+ headers: List[Pair[String, String]]
31
+ payload: Buffer
32
+ body: FetchResponse => T
33
+ ): T {
34
+ self.fetch("POST", url, headers.toMap(), Some(payload), FetchOptions(), body)
35
+ }
36
+
37
+ put[T](
38
+ url: String
39
+ headers: List[Pair[String, String]]
40
+ payload: Buffer
41
+ body: FetchResponse => T
42
+ ): T {
43
+ self.fetch("PUT", url, headers.toMap(), Some(payload), FetchOptions(), body)
44
+ }
45
+
46
+ delete[T](
47
+ url: String
48
+ headers: List[Pair[String, String]]
49
+ body: FetchResponse => T
50
+ ): T {
51
+ self.fetch("DELETE", url, headers.toMap(), None, FetchOptions(), body)
52
+ }
11
53
 
12
- fetch(
54
+ fetch[T](
55
+ method: String
13
56
  url: String
14
- method: String = "GET"
15
- headers: List[Pair[String, String]] = emptyList
16
- body: Option[FetchBody] = None
17
- redirect: FetchRedirect = RedirectFollow
18
- // The rest of the options are only respected by browsers - see the MDN fetch() documentation
19
- referrer: Option[String] = None
20
- integrity: Option[String] = None
21
- mode: Option[String] = None
22
- credentials: Option[String] = None
23
- cache: Option[String] = None
24
- throw: Bool = True
25
- ): FetchResponse
57
+ headers: Map[String, String] = Map.new()
58
+ payload: Option[Buffer] = None
59
+ options: FetchOptions = FetchOptions()
60
+ body: FetchResponse => T
61
+ ): T
26
62
  target js async """
63
+ const fetchResponse = {response: null, statusChecked: false};
27
64
  try {
28
- const options = {headers: {}, signal: $task.controller.signal}
29
- options.method = method_
30
- headers_.forEach(pair => {options.headers[pair.first_] = pair.second_})
31
- if(body_.value_) options.body = body_.value_
32
- if(redirect_.RedirectError) options.redirect = "error"
33
- else if(redirect_.RedirectManual) options.redirect = "manual"
34
- if(referrer_.value_) options.referrer = referrer_.value_
35
- if(integrity_.value_) options.integrity = integrity_.value_
36
- if(mode_.value_) options.mode = mode_.value_
37
- if(credentials_.value_) options.credentials = credentials_.value_
38
- if(cache_.value_) options.cache = cache_.value_
39
- let result = await fetch(url_, options)
40
- if(throw_ && !result.ok) throw new Error("Unexpected HTTP status code: " + result.status)
41
- return result
65
+ const options = {headers: {}, signal: $task.controller.signal};
66
+ options.method = method_;
67
+ headers_.forEach(pair => {options.headers[pair.first_] = pair.second_});
68
+ if(body_.value_) options.body = body_.value_;
69
+ if(redirect_.RedirectError) options.redirect = "error";
70
+ else if(redirect_.RedirectManual) options.redirect = "manual";
71
+ if(referrer_.value_) options.referrer = referrer_.value_;
72
+ if(integrity_.value_) options.integrity = integrity_.value_;
73
+ if(mode_.value_) options.mode = mode_.value_;
74
+ if(credentials_.value_) options.credentials = credentials_.value_;
75
+ if(cache_.value_) options.cache = cache_.value_;
76
+ fetchResponse.response = await self_.fetch(url_, options);
77
+ const result = await body_(fetchResponse, $task);
78
+ if(!statusChecked && !fetchResponse.response.ok) {
79
+ throw new Error("Unexpected HTTP status code: " + fetchResponse.response.status);
80
+ }
81
+ return result;
42
82
  } finally {
83
+ fetchResponse.response = null;
43
84
  if($task.controller.signal.aborted) $task.controller = new AbortController()
44
85
  }
45
86
  """
46
87
 
47
88
  }
48
89
 
49
- emptyList: List[Pair[String, String]] = [] // TODO: Why won't this type check when inlined? Probably some dangling unification variable?
50
-
51
- bodyText(body: String): FetchBody
52
- target js sync "return body_"
53
- target js async "return body_"
54
-
55
- bodyBuffer(body: Buffer): FetchBody
56
- target js sync "return body_"
57
- target js async "return body_"
58
-
59
90
  extend self: FetchResponse {
60
91
 
61
92
  ok(): Bool
62
- target js async "return self_.ok"
93
+ target js async """
94
+ self_.statusChecked = true;
95
+ internalCheck_(self_);
96
+ return self_.response.ok;
97
+ """
63
98
 
64
99
  status(): Int
65
- target js async "return self_.status"
100
+ target js async """
101
+ self_.statusChecked = true;
102
+ internalCheck_(self_);
103
+ return self_.response.status;
104
+ """
66
105
 
67
106
  statusText(): String
68
- target js async "return self_.statusText"
107
+ target js async """
108
+ self_.statusChecked = true;
109
+ internalCheck_(self_);
110
+ return self_.response.statusText;
111
+ """
69
112
 
70
113
  header(name: String): Option[String]
71
114
  target js async """
72
- const header = self_.headers.get(name_)
115
+ internalCheck_(self_);
116
+ const header = self_.response.headers.get(name_);
73
117
  return header != null
74
118
  ? ff_core_Option.Some(header)
75
- : ff_core_Option.None()
119
+ : ff_core_Option.None();
76
120
  """
77
121
 
78
122
  readText(): String
79
- target js async "return await self_.text()"
123
+ target js async """
124
+ internalCheck_(self_);
125
+ return await self_.response.text();
126
+ """
80
127
 
81
128
  readJson(): Json
82
- target js async "return await self_.json()"
129
+ target js async """
130
+ internalCheck_(self_);
131
+ return await self_.response.json();
132
+ """
83
133
 
84
134
  readBuffer(): Buffer
85
- target js async "return new DataView(await self_.arrayBuffer())"
135
+ target js async """
136
+ internalCheck_(self_);
137
+ return new DataView(await self_.response.arrayBuffer());
138
+ """
86
139
 
87
140
  }
141
+
142
+ internalCheck(fetchResponse: FetchResponse)
143
+ target js sync """
144
+ if(fetchResponse_.response === null) {
145
+ throw new Error("Response closed");
146
+ }
147
+ if(!fetchResponse_.statusChecked && !fetchResponse_.response.ok) {
148
+ throw new Error("Unchecked HTTP status code: " + fetchResponse_.response.status);
149
+ }
150
+ """
@@ -40,7 +40,7 @@ extend self: NodeSystem {
40
40
  """
41
41
 
42
42
  httpClient(): HttpClient
43
- target js async "return null"
43
+ target js async "return typeof globalThis !== 'undefined' ? globalThis : window"
44
44
 
45
45
  mainTask(): Task
46
46
  target js async "return self_.task_"
@@ -11,7 +11,7 @@ nodeMain(system: NodeSystem) {
11
11
  1.to(1000).each {_ =>
12
12
  system.mainTask().spawn {task =>
13
13
  let url = urlChannel.read()
14
- let html = system.httpClient().fetch(url).readText()
14
+ let html = system.httpClient().get(url, []) {_.readText()}
15
15
  htmlChannel.write(html)
16
16
  }
17
17
  }
@@ -11,12 +11,8 @@ browserMain(system: BrowserSystem): Unit {
11
11
  let handler = system.js().function1 {event =>
12
12
  let text = input.get("value").grabString()
13
13
  let serialized = Serializable.serialize(text)
14
- let result = system.httpClient().fetch(
15
- url = "http://localhost:8080/rhyme"
16
- method = "POST"
17
- body = Some(HttpClient.bodyBuffer(serialized))
18
- )
19
- output.set("innerText", result.readText())
14
+ let innerText = system.httpClient().post("http://localhost:8080/rhyme", [], serialized) {_.readText()}
15
+ output.set("innerText", innerText)
20
16
  }
21
17
 
22
18
  button.call2("addEventListener", "click", handler)
@@ -41,9 +37,8 @@ nodeMain(system: NodeSystem): Unit {
41
37
 
42
38
  let buffer = request.readBuffer()
43
39
  let deserialized = Serializable.deserialize(buffer)
44
- let result = system.httpClient().fetch("https://api.datamuse.com/words?rel_rhy=" + deserialized)
45
- let json = system.js().parseJson(result.readText())
46
- let rhyme = json.get(0).get("word").grabString()
40
+ let json = system.httpClient().get("https://api.datamuse.com/words?rel_rhy=" + deserialized, []) {_.readJson()}
41
+ let rhyme = json.index(0).field("word").grabString()
47
42
  response.setHeader("Access-Control-Allow-Origin", ["*"])
48
43
  response.writeText(rhyme)
49
44
 
@@ -1,13 +1,15 @@
1
- dependency ff:httpserver:0.0.0
2
- import HttpServer from ff:httpserver
1
+ dependency ff:webserver:0.0.0
2
+ import WebServer from ff:webserver
3
3
 
4
4
  nodeMain(system: NodeSystem): Unit {
5
5
  let host = system.arguments().grab(0)
6
6
  let port = system.arguments().grab(1).grabInt()
7
- HttpServer.listen(system, host, port) {request, response =>
8
- let parameters = request.parameters().map {"?" + _}.else {""}
9
- response.setHeader("Location", ["https://www.firefly-lang.org" + request.path() + parameters])
10
- response.writeStatus(302, Some("Found"))
7
+ WebServer.new(system, host, port).listen {request =>
8
+ let parameters = if(request.readRawQueryString().size() == 0) {""} else {
9
+ "?" + request.readRawQueryString()
10
+ }
11
+ request.writeHeader("Location", "https://www.firefly-lang.org" + request.readPath() + parameters)
12
+ request.writeStatus("302 Found")
11
13
  }
12
14
  }
13
15
 
@@ -7,6 +7,7 @@ import Handler
7
7
  data CompletionInfo(
8
8
  label: String
9
9
  extra: String
10
+ more: String
10
11
  snippet: String
11
12
  member: Bool
12
13
  type: Type
@@ -75,7 +76,7 @@ handleCompletion(lspHook: LspHook, toplevel: Bool, followedByOpenBracket: Bool):
75
76
  let fieldNames = n.split('$').dropFirst(1)
76
77
  let fieldCompletions = fieldNames.zip(ts).map {| Pair(name, t) =>
77
78
  let t2 = h.unification.substitute(t)
78
- CompletionInfo(name, "", name, True, t2, "(...)." + name + ": " + t2.show([]), Some(h.expected))
79
+ CompletionInfo(name, "", "", name, True, t2, "(...)." + name + ": " + t2.show([]), Some(h.expected))
79
80
  }
80
81
  [...fieldCompletions, ...completion(h.unification, h.environment, n, None, h.expected)]
81
82
  | InferArgumentHook h {
@@ -194,6 +195,7 @@ completionsToJson(completions: List[CompletionInfo]): Json {
194
195
  .with("label", i.label)
195
196
  .with("labelDetails", Json.object()
196
197
  .with("detail", i.extra + if(shownType == "") {""} else {": " + shownType})
198
+ .with("description", i.more)
197
199
  )
198
200
  .with("insertText", i.snippet)
199
201
  .with("insertTextFormat", 2 /* Snippet */)
@@ -213,7 +215,7 @@ typeCompletion(types: Map[String, String], typeGenerics: Map[String, List[String
213
215
  let label = typeName
214
216
  let extra = if(realGenerics.isEmpty()) {""} else {"[" + realGenerics.join(", ") + "]"}
215
217
  let snippet = typeName + if(realGenerics.isEmpty()) {""} else {"[$0]"}
216
- CompletionInfo(label, extra, snippet, False, TConstructor(Location("", 0, 0), "type", []), full, None)
218
+ CompletionInfo(label, extra, "", snippet, False, TConstructor(Location("", 0, 0), "type", []), full, None)
217
219
  }
218
220
  completions
219
221
  }
@@ -384,6 +386,7 @@ makeCompletion(
384
386
  CompletionInfo(
385
387
  label = shortName
386
388
  extra = pair.first
389
+ more = ""
387
390
  snippet = shortName + pair.second
388
391
  member = member && !copy
389
392
  type = returnType
@@ -398,6 +401,7 @@ exhaustiveMatchCompletion(environment: Environment, prefix: String, inside: Bool
398
401
  [CompletionInfo(
399
402
  label = curly.first + "| "
400
403
  extra = "[] => ... | [first, ...rest] => ..." + curly.second
404
+ more = ""
401
405
  snippet = curly.first + "\n | [] => $0\n | [first, ...rest] =>\n" + curly.second
402
406
  member = True
403
407
  type = TConstructor(Location("", 0, 0), "exhaustive match", [])
@@ -439,6 +443,7 @@ exhaustiveMatchCompletion(environment: Environment, prefix: String, inside: Bool
439
443
  [CompletionInfo(
440
444
  label = label.slice(0, 2)
441
445
  extra = label.dropFirst(2)
446
+ more = ""
442
447
  snippet = snippet
443
448
  member = True
444
449
  type = TConstructor(Location("", 0, 0), "exhaustive match", [])
@@ -454,16 +459,16 @@ patternCompletion(unification: Unification, environment: Environment, expected:
454
459
  }
455
460
  if(typeName == "") {[]} else:
456
461
  if(typeName == "ff:core/List.List") {
457
- [CompletionInfo("[...]", "", "[$0]", False, expected, "// List pattern", Some(expected))]
462
+ [CompletionInfo("[...]", "", "", "[$0]", False, expected, "// List pattern", Some(expected))]
458
463
  } else:
459
464
  if(typeName == "ff:core/String.String") {
460
- [CompletionInfo("\"...\"", "", "\"$0\"", False, expected, "// String pattern", Some(expected))]
465
+ [CompletionInfo("\"...\"", "", "", "\"$0\"", False, expected, "// String pattern", Some(expected))]
461
466
  } else:
462
467
  if(typeName == "ff:core/Char.Char") {
463
- [CompletionInfo("'...'", "", "'$0'", False, expected, "// Char pattern", Some(expected))]
468
+ [CompletionInfo("'...'", "", "", "'$0'", False, expected, "// Char pattern", Some(expected))]
464
469
  } else:
465
470
  if(typeName == "ff:core/Int.Int") {
466
- [CompletionInfo("0", "", "0", False, expected, "// Int pattern", Some(expected))]
471
+ [CompletionInfo("0", "", "", "0", False, expected, "// Int pattern", Some(expected))]
467
472
  } else:
468
473
  let variants = do {
469
474
  environment.symbols.toList().filter {s =>
@@ -492,6 +497,7 @@ patternCompletion(unification: Unification, environment: Environment, expected:
492
497
  CompletionInfo(
493
498
  label = shortName
494
499
  extra = extra
500
+ more = ""
495
501
  snippet = shortName + if(scheme.signature.parameters.isEmpty()) {""} else {"($0)"}
496
502
  member = False
497
503
  type = expected
@@ -544,7 +550,7 @@ missingCompletion(
544
550
  | TVariable _ => name + ": "
545
551
  }
546
552
  let documentation = "// Add missing parameter\n" + name + ": " + t.show([])
547
- [CompletionInfo(name, "", snippet, False, t, documentation, Some(t))]
553
+ [CompletionInfo(name, "", "", snippet, False, t, documentation, Some(t))]
548
554
  | Some(as) {isParameter} =>
549
555
  let noEffect = TConstructor(instantiated.scheme.signature.at, "ff:core/Nothing.Nothing", [])
550
556
  let t = unification.substitute(TConstructor(
@@ -557,12 +563,12 @@ missingCompletion(
557
563
  ]
558
564
  ))
559
565
  let documentation = "// Add missing parameter\n" + name + ": " + t.show([])
560
- [CompletionInfo(name, "", name + ": " + t.show([]), False, t, documentation, Some(t))]
566
+ [CompletionInfo(name, "", "", name + ": " + t.show([]), False, t, documentation, Some(t))]
561
567
  | None =>
562
568
  let t = unification.substitute(instantiated.scheme.signature.returnType)
563
569
  let snippet = name + " = "
564
570
  let documentation = "// Define missing variable\n" + name + ": " + t.show([])
565
- [CompletionInfo(snippet, "...", snippet, False, t, documentation, Some(t))]
571
+ [CompletionInfo(snippet, "...", "", snippet, False, t, documentation, Some(t))]
566
572
  | Some(as) =>
567
573
  mutable remainingParameters = instantiated.scheme.signature.parameters
568
574
  mutable usedNames = [].toSet()
@@ -603,7 +609,7 @@ missingCompletion(
603
609
  let snippet = if(shortSnippet.size() <= 100) {shortSnippet} else {longSnippet}
604
610
  let documentation = "// Define missing function\n" +
605
611
  if(shortSnippet.size() < 30) {shortSnippet} else {longSnippet}
606
- [CompletionInfo(name, extra, snippet + " {\n $0\n}", False, returnType, documentation, None)]
612
+ [CompletionInfo(name, extra, "", snippet + " {\n $0\n}", False, returnType, documentation, None)]
607
613
  }
608
614
  if(keyword && arguments.isEmpty()) {
609
615
  ["let ", "mutable "].flatMap {k => completions.map {c =>
@@ -622,6 +628,7 @@ namedParameterCompletion(parameter: Parameter, index: Int, preselect: Bool): Com
622
628
  CompletionInfo(
623
629
  label = parameter.name + " = "
624
630
  extra = "..."
631
+ more = ""
625
632
  snippet = parameter.name + " = "
626
633
  member = False
627
634
  type = parameter.valueType
@@ -636,6 +643,7 @@ toplevelCompletion(lspHook: LspHook): List[CompletionInfo] {
636
643
  CompletionInfo(
637
644
  label = "package",
638
645
  extra = " some:package:0.0.0",
646
+ more = ""
639
647
  snippet = "package ",
640
648
  member = False,
641
649
  type = TConstructor(lspHook.at, "", []),
@@ -645,6 +653,7 @@ toplevelCompletion(lspHook: LspHook): List[CompletionInfo] {
645
653
  CompletionInfo(
646
654
  label = "dependency",
647
655
  extra = " some:package:0.0.0",
656
+ more = ""
648
657
  snippet = "dependency ",
649
658
  member = False,
650
659
  type = TConstructor(lspHook.at, "", []),
@@ -654,6 +663,7 @@ toplevelCompletion(lspHook: LspHook): List[CompletionInfo] {
654
663
  CompletionInfo(
655
664
  label = "import",
656
665
  extra = " Module from some:package",
666
+ more = ""
657
667
  snippet = "import ",
658
668
  member = False,
659
669
  type = TConstructor(lspHook.at, "", []),
@@ -663,6 +673,7 @@ toplevelCompletion(lspHook: LspHook): List[CompletionInfo] {
663
673
  CompletionInfo(
664
674
  label = "extend",
665
675
  extra = " self[T]: SomeType[T] {...}",
676
+ more = ""
666
677
  snippet = "extend ",
667
678
  member = False,
668
679
  type = TConstructor(lspHook.at, "", []),
@@ -672,6 +683,7 @@ toplevelCompletion(lspHook: LspHook): List[CompletionInfo] {
672
683
  CompletionInfo(
673
684
  label = "data",
674
685
  extra = " SomeType[T](...) {...}",
686
+ more = ""
675
687
  snippet = "data ",
676
688
  member = False,
677
689
  type = TConstructor(lspHook.at, "", []),
@@ -681,6 +693,7 @@ toplevelCompletion(lspHook: LspHook): List[CompletionInfo] {
681
693
  CompletionInfo(
682
694
  label = "class",
683
695
  extra = " SomeType[T](...) {...}",
696
+ more = ""
684
697
  snippet = "class ",
685
698
  member = False,
686
699
  type = TConstructor(lspHook.at, "", []),
@@ -690,6 +703,7 @@ toplevelCompletion(lspHook: LspHook): List[CompletionInfo] {
690
703
  CompletionInfo(
691
704
  label = "capability",
692
705
  extra = " SomeType[T](...) {...}",
706
+ more = ""
693
707
  snippet = "capability ",
694
708
  member = False,
695
709
  type = TConstructor(lspHook.at, "", []),
@@ -699,6 +713,7 @@ toplevelCompletion(lspHook: LspHook): List[CompletionInfo] {
699
713
  CompletionInfo(
700
714
  label = "trait",
701
715
  extra = " T: SomeTrait {...}",
716
+ more = ""
702
717
  snippet = "trait ",
703
718
  member = False,
704
719
  type = TConstructor(lspHook.at, "", []),
@@ -708,6 +723,7 @@ toplevelCompletion(lspHook: LspHook): List[CompletionInfo] {
708
723
  CompletionInfo(
709
724
  label = "instance",
710
725
  extra = " SomeType: SomeTrait {...}",
726
+ more = ""
711
727
  snippet = "instance ",
712
728
  member = False,
713
729
  type = TConstructor(lspHook.at, "", []),
@@ -717,6 +733,7 @@ toplevelCompletion(lspHook: LspHook): List[CompletionInfo] {
717
733
  CompletionInfo(
718
734
  label = "nodeMain",
719
735
  extra = "(system: NodeSystem) {...}",
736
+ more = ""
720
737
  snippet = "nodeMain(system: NodeSystem) {\n $0\n}",
721
738
  member = False,
722
739
  type = TConstructor(lspHook.at, "", []),
@@ -726,6 +743,7 @@ toplevelCompletion(lspHook: LspHook): List[CompletionInfo] {
726
743
  CompletionInfo(
727
744
  label = "browserMain",
728
745
  extra = "(system: BrowserSystem) {...}",
746
+ more = ""
729
747
  snippet = "browserMain(system: BrowserSystem) {\n $0\n}",
730
748
  member = False,
731
749
  type = TConstructor(lspHook.at, "", []),
@@ -735,6 +753,7 @@ toplevelCompletion(lspHook: LspHook): List[CompletionInfo] {
735
753
  CompletionInfo(
736
754
  label = "buildMain",
737
755
  extra = "(system: BuildSystem) {...}",
756
+ more = ""
738
757
  snippet = "buildMain(system: NodeSystem) {\n $0\n}",
739
758
  member = False,
740
759
  type = TConstructor(lspHook.at, "", []),
@@ -744,14 +763,15 @@ toplevelCompletion(lspHook: LspHook): List[CompletionInfo] {
744
763
  CompletionInfo(
745
764
  label = "webapp",
746
765
  extra = " with frontend and backend",
766
+ more = ""
747
767
  snippet = [
748
768
  "dependency ff:webserver:0.0.0"
749
769
  "import WebServer from ff:webserver"
750
770
  ""
751
771
  "browserMain(system: BrowserSystem): Unit {"
752
- " let response = system.httpClient().fetch(\"http://localhost:8080/hello\")"
772
+ " let message = system.httpClient().get(\"http://localhost:8080/hello\", []) {_.readText()}"
753
773
  " let window = system.js().global().get(\"window\")"
754
- " window.call1(\"alert\", response.readText())"
774
+ " window.call1(\"alert\", message)"
755
775
  "}"
756
776
  ""
757
777
  "nodeMain(system: NodeSystem): Unit {"
package/lux/Main.ff CHANGED
@@ -39,11 +39,7 @@ questionComponent(lux: Lux, http: HttpClient, question: String) {
39
39
  lux.div {lux.text("User: " + question)}
40
40
  lux.useLazy1(question): _ =>
41
41
  lux.useSuspense {lux.div {lux.text("Assistant typing...")}}: lux =>
42
- let answer = http.fetch(
43
- url = "/chat"
44
- method = "POST"
45
- body = Some(HttpClient.bodyText(question))
46
- ).readText()
42
+ let answer = http.post("/chat", [], question.toBuffer()) {_.readText()}
47
43
  lux.div {lux.text("Assistant: " + answer)}
48
44
  }
49
45
  }
@@ -82,15 +78,14 @@ nodeMain(system: NodeSystem) {
82
78
  }
83
79
 
84
80
  fetchAnswer(httpClient: HttpClient, key: String, question: Json): String {
85
- let json = httpClient.fetch(
81
+ let json = httpClient.post(
86
82
  url = "https://api.openai.com/v1/chat/completions"
87
- method = "POST"
88
83
  headers = [
89
84
  Pair("Authorization", "Bearer " + key)
90
85
  Pair("Content-Type", "application/json")
91
86
  ]
92
- body = Some(HttpClient.bodyText(question.write()))
93
- ).readJson()
87
+ body = question.write().toBuffer()
88
+ ) {_.readJson()}
94
89
  json.field("choices").index(0).field("message").field("content").grabString()
95
90
  }
96
91
 
package/lux/Main2.ff CHANGED
@@ -92,7 +92,7 @@ rhymeComponent(lux: Lux, system: BrowserSystem) {
92
92
 
93
93
 
94
94
  rhyme(system: BrowserSystem, text: String): String {
95
- let result = system.httpClient().fetch("https://api.datamuse.com/words?rel_rhy=" + text)
95
+ let result = system.httpClient().get("https://api.datamuse.com/words?rel_rhy=" + text, [])
96
96
  let json = system.js().parseJson(result.readText())
97
97
  if(json.get(0).isNullOrUndefined()) {"?"} else {json.get(0).get("word").grabString()}
98
98
  }
@@ -256,11 +256,12 @@ ff_compiler_DependencyLock.DependencyLock_do(dependencyLock_, ff_core_Path.Path_
256
256
  if((!ff_core_Path.Path_exists(donePath_, false, false, false))) {
257
257
  return ff_core_Option.Some((function() {
258
258
  ff_core_Log.trace_(("Fetching " + location_));
259
- const response_ = ff_core_HttpClient.HttpClient_fetch(httpClient_, location_, "GET", ff_core_HttpClient.emptyList_, ff_core_Option.None(), ff_core_HttpClient.RedirectFollow(), ff_core_Option.None(), ff_core_Option.None(), ff_core_Option.None(), ff_core_Option.None(), ff_core_Option.None(), false);
259
+ const buffer_ = ff_core_HttpClient.HttpClient_get(httpClient_, location_, [], ((response_) => {
260
260
  if((!ff_core_HttpClient.FetchResponse_ok(response_))) {
261
261
  ff_core_Core.panic_(("Could not download dependency: " + location_))
262
262
  };
263
- const buffer_ = ff_core_HttpClient.FetchResponse_readBuffer(response_);
263
+ return ff_core_HttpClient.FetchResponse_readBuffer(response_)
264
+ }));
264
265
  if(ff_core_Path.Path_exists(dependencyPath_, false, false, false)) {
265
266
  ff_core_Path.Path_delete(dependencyPath_, 0, 100)
266
267
  };
@@ -360,11 +361,12 @@ if((!(await ff_core_Path.Path_exists$(donePath_, false, false, false, $task))))
360
361
  if((!(await ff_core_Path.Path_exists$(donePath_, false, false, false, $task)))) {
361
362
  return ff_core_Option.Some((await (async function() {
362
363
  ff_core_Log.trace_(("Fetching " + location_));
363
- const response_ = (await ff_core_HttpClient.HttpClient_fetch$(httpClient_, location_, "GET", ff_core_HttpClient.emptyList_, ff_core_Option.None(), ff_core_HttpClient.RedirectFollow(), ff_core_Option.None(), ff_core_Option.None(), ff_core_Option.None(), ff_core_Option.None(), ff_core_Option.None(), false, $task));
364
+ const buffer_ = (await ff_core_HttpClient.HttpClient_get$(httpClient_, location_, [], (async (response_, $task) => {
364
365
  if((!(await ff_core_HttpClient.FetchResponse_ok$(response_, $task)))) {
365
366
  ff_core_Core.panic_(("Could not download dependency: " + location_))
366
367
  };
367
- const buffer_ = (await ff_core_HttpClient.FetchResponse_readBuffer$(response_, $task));
368
+ return (await ff_core_HttpClient.FetchResponse_readBuffer$(response_, $task))
369
+ }), $task));
368
370
  if((await ff_core_Path.Path_exists$(dependencyPath_, false, false, false, $task))) {
369
371
  (await ff_core_Path.Path_delete$(dependencyPath_, 0, 100, $task))
370
372
  };
@@ -132,7 +132,7 @@ throw new Error('Function BrowserSystem_urlFragment is missing on this target in
132
132
  }
133
133
 
134
134
  export async function BrowserSystem_httpClient$(self_, $task) {
135
- return null
135
+ return typeof globalThis !== 'undefined' ? globalThis : window
136
136
  }
137
137
 
138
138
  export async function BrowserSystem_mainTask$(self_, $task) {
@@ -113,46 +113,87 @@ export function RedirectManual() {
113
113
  return RedirectManual$;
114
114
  }
115
115
 
116
- export const emptyList_ = [];
116
+ // type FetchOptions
117
+ export function FetchOptions(redirect_, referrer_, integrity_, mode_, credentials_, cache_) {
118
+ return {redirect_, referrer_, integrity_, mode_, credentials_, cache_};
119
+ }
120
+
121
+
122
+
123
+ export function internalCheck_(fetchResponse_) {
124
+
125
+ if(fetchResponse_.response === null) {
126
+ throw new Error("Response closed");
127
+ }
128
+ if(!fetchResponse_.statusChecked && !fetchResponse_.response.ok) {
129
+ throw new Error("Unchecked HTTP status code: " + fetchResponse_.response.status);
130
+ }
131
+
132
+ }
133
+
134
+ export async function internalCheck_$(fetchResponse_, $task) {
135
+ throw new Error('Function internalCheck is missing on this target in async context.');
136
+ }
117
137
 
118
- export function bodyText_(body_) {
119
- return body_
138
+ export function HttpClient_get(self_, url_, headers_, body_) {
139
+ return ff_core_HttpClient.HttpClient_fetch(self_, "GET", url_, ff_core_List.List_toMap(headers_, ff_core_Ordering.ff_core_Ordering_Order$ff_core_String_String), ff_core_Option.None(), ff_core_HttpClient.FetchOptions(ff_core_HttpClient.RedirectFollow(), ff_core_Option.None(), ff_core_Option.None(), ff_core_Option.None(), ff_core_Option.None(), ff_core_Option.None()), body_)
120
140
  }
121
141
 
122
- export function bodyBuffer_(body_) {
123
- return body_
142
+ export function HttpClient_post(self_, url_, headers_, payload_, body_) {
143
+ return ff_core_HttpClient.HttpClient_fetch(self_, "POST", url_, ff_core_List.List_toMap(headers_, ff_core_Ordering.ff_core_Ordering_Order$ff_core_String_String), ff_core_Option.Some(payload_), ff_core_HttpClient.FetchOptions(ff_core_HttpClient.RedirectFollow(), ff_core_Option.None(), ff_core_Option.None(), ff_core_Option.None(), ff_core_Option.None(), ff_core_Option.None()), body_)
124
144
  }
125
145
 
126
- export async function bodyText_$(body_, $task) {
127
- return body_
146
+ export function HttpClient_put(self_, url_, headers_, payload_, body_) {
147
+ return ff_core_HttpClient.HttpClient_fetch(self_, "PUT", url_, ff_core_List.List_toMap(headers_, ff_core_Ordering.ff_core_Ordering_Order$ff_core_String_String), ff_core_Option.Some(payload_), ff_core_HttpClient.FetchOptions(ff_core_HttpClient.RedirectFollow(), ff_core_Option.None(), ff_core_Option.None(), ff_core_Option.None(), ff_core_Option.None(), ff_core_Option.None()), body_)
128
148
  }
129
149
 
130
- export async function bodyBuffer_$(body_, $task) {
131
- return body_
150
+ export function HttpClient_delete(self_, url_, headers_, body_) {
151
+ return ff_core_HttpClient.HttpClient_fetch(self_, "DELETE", url_, ff_core_List.List_toMap(headers_, ff_core_Ordering.ff_core_Ordering_Order$ff_core_String_String), ff_core_Option.None(), ff_core_HttpClient.FetchOptions(ff_core_HttpClient.RedirectFollow(), ff_core_Option.None(), ff_core_Option.None(), ff_core_Option.None(), ff_core_Option.None(), ff_core_Option.None()), body_)
132
152
  }
133
153
 
134
- export function HttpClient_fetch(self_, url_, method_ = "GET", headers_ = ff_core_HttpClient.emptyList_, body_ = ff_core_Option.None(), redirect_ = ff_core_HttpClient.RedirectFollow(), referrer_ = ff_core_Option.None(), integrity_ = ff_core_Option.None(), mode_ = ff_core_Option.None(), credentials_ = ff_core_Option.None(), cache_ = ff_core_Option.None(), throw_ = true) {
154
+ export function HttpClient_fetch(self_, method_, url_, headers_ = ff_core_Map.new_(), payload_ = ff_core_Option.None(), options_ = ff_core_HttpClient.FetchOptions(), body_) {
135
155
  throw new Error('Function HttpClient_fetch is missing on this target in sync context.');
136
156
  }
137
157
 
138
- export async function HttpClient_fetch$(self_, url_, method_ = "GET", headers_ = ff_core_HttpClient.emptyList_, body_ = ff_core_Option.None(), redirect_ = ff_core_HttpClient.RedirectFollow(), referrer_ = ff_core_Option.None(), integrity_ = ff_core_Option.None(), mode_ = ff_core_Option.None(), credentials_ = ff_core_Option.None(), cache_ = ff_core_Option.None(), throw_ = true, $task) {
158
+ export async function HttpClient_get$(self_, url_, headers_, body_, $task) {
159
+ return (await ff_core_HttpClient.HttpClient_fetch$(self_, "GET", url_, ff_core_List.List_toMap(headers_, ff_core_Ordering.ff_core_Ordering_Order$ff_core_String_String), ff_core_Option.None(), ff_core_HttpClient.FetchOptions(ff_core_HttpClient.RedirectFollow(), ff_core_Option.None(), ff_core_Option.None(), ff_core_Option.None(), ff_core_Option.None(), ff_core_Option.None()), body_, $task))
160
+ }
161
+
162
+ export async function HttpClient_post$(self_, url_, headers_, payload_, body_, $task) {
163
+ return (await ff_core_HttpClient.HttpClient_fetch$(self_, "POST", url_, ff_core_List.List_toMap(headers_, ff_core_Ordering.ff_core_Ordering_Order$ff_core_String_String), ff_core_Option.Some(payload_), ff_core_HttpClient.FetchOptions(ff_core_HttpClient.RedirectFollow(), ff_core_Option.None(), ff_core_Option.None(), ff_core_Option.None(), ff_core_Option.None(), ff_core_Option.None()), body_, $task))
164
+ }
165
+
166
+ export async function HttpClient_put$(self_, url_, headers_, payload_, body_, $task) {
167
+ return (await ff_core_HttpClient.HttpClient_fetch$(self_, "PUT", url_, ff_core_List.List_toMap(headers_, ff_core_Ordering.ff_core_Ordering_Order$ff_core_String_String), ff_core_Option.Some(payload_), ff_core_HttpClient.FetchOptions(ff_core_HttpClient.RedirectFollow(), ff_core_Option.None(), ff_core_Option.None(), ff_core_Option.None(), ff_core_Option.None(), ff_core_Option.None()), body_, $task))
168
+ }
169
+
170
+ export async function HttpClient_delete$(self_, url_, headers_, body_, $task) {
171
+ return (await ff_core_HttpClient.HttpClient_fetch$(self_, "DELETE", url_, ff_core_List.List_toMap(headers_, ff_core_Ordering.ff_core_Ordering_Order$ff_core_String_String), ff_core_Option.None(), ff_core_HttpClient.FetchOptions(ff_core_HttpClient.RedirectFollow(), ff_core_Option.None(), ff_core_Option.None(), ff_core_Option.None(), ff_core_Option.None(), ff_core_Option.None()), body_, $task))
172
+ }
173
+
174
+ export async function HttpClient_fetch$(self_, method_, url_, headers_ = ff_core_Map.new_(), payload_ = ff_core_Option.None(), options_ = ff_core_HttpClient.FetchOptions(), body_, $task) {
139
175
 
176
+ const fetchResponse = {response: null, statusChecked: false};
140
177
  try {
141
- const options = {headers: {}, signal: $task.controller.signal}
142
- options.method = method_
143
- headers_.forEach(pair => {options.headers[pair.first_] = pair.second_})
144
- if(body_.value_) options.body = body_.value_
145
- if(redirect_.RedirectError) options.redirect = "error"
146
- else if(redirect_.RedirectManual) options.redirect = "manual"
147
- if(referrer_.value_) options.referrer = referrer_.value_
148
- if(integrity_.value_) options.integrity = integrity_.value_
149
- if(mode_.value_) options.mode = mode_.value_
150
- if(credentials_.value_) options.credentials = credentials_.value_
151
- if(cache_.value_) options.cache = cache_.value_
152
- let result = await fetch(url_, options)
153
- if(throw_ && !result.ok) throw new Error("Unexpected HTTP status code: " + result.status)
154
- return result
178
+ const options = {headers: {}, signal: $task.controller.signal};
179
+ options.method = method_;
180
+ headers_.forEach(pair => {options.headers[pair.first_] = pair.second_});
181
+ if(body_.value_) options.body = body_.value_;
182
+ if(redirect_.RedirectError) options.redirect = "error";
183
+ else if(redirect_.RedirectManual) options.redirect = "manual";
184
+ if(referrer_.value_) options.referrer = referrer_.value_;
185
+ if(integrity_.value_) options.integrity = integrity_.value_;
186
+ if(mode_.value_) options.mode = mode_.value_;
187
+ if(credentials_.value_) options.credentials = credentials_.value_;
188
+ if(cache_.value_) options.cache = cache_.value_;
189
+ fetchResponse.response = await self_.fetch(url_, options);
190
+ const result = await body_(fetchResponse, $task);
191
+ if(!statusChecked && !fetchResponse.response.ok) {
192
+ throw new Error("Unexpected HTTP status code: " + fetchResponse.response.status);
193
+ }
194
+ return result;
155
195
  } finally {
196
+ fetchResponse.response = null;
156
197
  if($task.controller.signal.aborted) $task.controller = new AbortController()
157
198
  }
158
199
 
@@ -187,36 +228,58 @@ throw new Error('Function FetchResponse_readBuffer is missing on this target in
187
228
  }
188
229
 
189
230
  export async function FetchResponse_ok$(self_, $task) {
190
- return self_.ok
231
+
232
+ self_.statusChecked = true;
233
+ internalCheck_(self_);
234
+ return self_.response.ok;
235
+
191
236
  }
192
237
 
193
238
  export async function FetchResponse_status$(self_, $task) {
194
- return self_.status
239
+
240
+ self_.statusChecked = true;
241
+ internalCheck_(self_);
242
+ return self_.response.status;
243
+
195
244
  }
196
245
 
197
246
  export async function FetchResponse_statusText$(self_, $task) {
198
- return self_.statusText
247
+
248
+ self_.statusChecked = true;
249
+ internalCheck_(self_);
250
+ return self_.response.statusText;
251
+
199
252
  }
200
253
 
201
254
  export async function FetchResponse_header$(self_, name_, $task) {
202
255
 
203
- const header = self_.headers.get(name_)
256
+ internalCheck_(self_);
257
+ const header = self_.response.headers.get(name_);
204
258
  return header != null
205
259
  ? ff_core_Option.Some(header)
206
- : ff_core_Option.None()
260
+ : ff_core_Option.None();
207
261
 
208
262
  }
209
263
 
210
264
  export async function FetchResponse_readText$(self_, $task) {
211
- return await self_.text()
265
+
266
+ internalCheck_(self_);
267
+ return await self_.response.text();
268
+
212
269
  }
213
270
 
214
271
  export async function FetchResponse_readJson$(self_, $task) {
215
- return await self_.json()
272
+
273
+ internalCheck_(self_);
274
+ return await self_.response.json();
275
+
216
276
  }
217
277
 
218
278
  export async function FetchResponse_readBuffer$(self_, $task) {
219
- return new DataView(await self_.arrayBuffer())
279
+
280
+ internalCheck_(self_);
281
+ return new DataView(await self_.response.arrayBuffer());
282
+
220
283
  }
221
284
 
222
285
 
@@ -317,7 +317,7 @@ export async function NodeSystem_pathFromUrl$(self_, url_, $task) {
317
317
  }
318
318
 
319
319
  export async function NodeSystem_httpClient$(self_, $task) {
320
- return null
320
+ return typeof globalThis !== 'undefined' ? globalThis : window
321
321
  }
322
322
 
323
323
  export async function NodeSystem_mainTask$(self_, $task) {
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "description": "Firefly compiler",
5
5
  "author": "Firefly team",
6
6
  "license": "MIT",
7
- "version": "0.4.70",
7
+ "version": "0.4.71",
8
8
  "repository": {
9
9
  "type": "git",
10
10
  "url": "https://github.com/Ahnfelt/firefly-boot"
package/rpc/Rpc.ff CHANGED
@@ -59,11 +59,12 @@ newClient(
59
59
  method: String = "POST"
60
60
  ): RpcClient {
61
61
  RpcClient {name, buffer =>
62
- let response = httpClient.fetch(
63
- url = prefix + name,
64
- method = method,
65
- body = Some(HttpClient.bodyBuffer(buffer))
66
- )
67
- response.readBuffer()
62
+ httpClient.fetch(
63
+ method = method
64
+ url = prefix + name
65
+ headers = Map.new()
66
+ payload = Some(buffer)
67
+ options = FetchOptions()
68
+ ) {_.readBuffer()}
68
69
  }
69
70
  }
package/s3/S3.ff CHANGED
@@ -23,7 +23,7 @@ put(
23
23
  let allHeaders = [...canonicalHeaders, Pair("Authorization", authenticationHeader)]
24
24
 
25
25
  let url = "https://" + host + "/" + encodedKey
26
- system.httpClient().fetch(url, method = "PUT", headers = allHeaders, body = Some(HttpClient.bodyBuffer(body)), throw = True)
26
+ system.httpClient().put(url, allHeaders, body) {_ => }
27
27
  }
28
28
 
29
29
  makeS3AuthorizationHeader(
@@ -4,7 +4,7 @@
4
4
  "description": "Firefly language support",
5
5
  "author": "Firefly team",
6
6
  "license": "MIT",
7
- "version": "0.4.70",
7
+ "version": "0.4.71",
8
8
  "repository": {
9
9
  "type": "git",
10
10
  "url": "https://github.com/Ahnfelt/firefly-boot"