firefly-compiler 0.4.5 → 0.4.7

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/.hintrc CHANGED
@@ -1,5 +1,5 @@
1
- {
2
- "extends": [
3
- "development"
4
- ]
1
+ {
2
+ "extends": [
3
+ "development"
4
+ ]
5
5
  }
package/README.md CHANGED
@@ -13,7 +13,7 @@ A straightforward language for full-stack development.
13
13
 
14
14
  ## Writing your first webapp
15
15
 
16
- Install the [VSCode extension](https://marketplace.visualstudio.com/items?itemName=firefly-team.firefly-lang) (requires the upcoming [VSCode 1.82](https://code.visualstudio.com/insiders/)).
16
+ Install the [VSCode extension](https://marketplace.visualstudio.com/items?itemName=firefly-team.firefly-lang).
17
17
 
18
18
  Create a `WebApp.ff` file.
19
19
 
@@ -27,7 +27,7 @@ extend self: HttpClient {
27
27
  try {
28
28
  const options = {headers: {}, signal: $task.controller.signal}
29
29
  options.method = method_
30
- ff_core_List.List_each(headers_, pair => {options.headers[pair.key_] = pair.value_})
30
+ ff_core_List.List_each(headers_, pair => {options.headers[pair.first_] = pair.second_})
31
31
  if(body_.value_) options.body = body_.value_
32
32
  if(redirect_.RedirectError) options.redirect = "error"
33
33
  else if(redirect_.RedirectManual) options.redirect = "manual"
@@ -17,17 +17,26 @@ capability RequestMessage(
17
17
  parameters: Map[String, JsValue]
18
18
  )
19
19
 
20
+ logMessages = False
20
21
 
21
22
  main(system: NodeSystem) {
23
+ let logDirectory = if(logMessages) {
24
+ let directory = system.path(".").slash("messages")
25
+ if(directory.isDirectory()) {directory.delete()}
26
+ directory.createDirectory()
27
+ directory
28
+ }
29
+
22
30
  let fireflyPath = system.path(".")
23
31
  let handler = Handler(fireflyPath, None, Map.empty(), [].toSet(), Map.empty(), Map.empty())
24
32
  try {
25
33
  mutable input = system.readStream()
26
- let responseChannel: Channel[JsValue] = system.mainTask().channel()
34
+ let responseChannel: Channel[Pair[JsValue, String]] = system.mainTask().channel()
27
35
  system.mainTask().spawn {_ =>
28
36
  while {True} {
29
37
  let body = responseChannel.read()
30
- let json = body.toJson(Some(" ")).toBuffer()
38
+ let json = body.first.toJson(Some(" ")).toBuffer()
39
+ logDirectory.each {logMessage(_, "response", body.first, body.second)}
31
40
  system.writeText("Content-Length: " + json.size() + "\r\n\r\n")
32
41
  system.writeBuffer(json)
33
42
  }
@@ -38,13 +47,14 @@ main(system: NodeSystem) {
38
47
  input = requestPair.second
39
48
  let request = requestPair.first
40
49
  let message = parseRequestMessage(request.object)
50
+ logDirectory.each {logMessage(_, "request", request.object, message.method)}
41
51
  spawn(message.id) {_ =>
42
52
  if(message.method == "$/cancelRequest") {
43
53
  abort(Some(parseMessageId(message.parameters.grab("id"))))
44
54
  }
45
55
  try {
46
56
  handleRequestMessage(system, handler, message).each {body =>
47
- responseChannel.write(body)
57
+ responseChannel.write(Pair(body, message.method))
48
58
  }
49
59
  } catchAny {error =>
50
60
  let problem = if(error.name() == "AbortError") {
@@ -55,7 +65,8 @@ main(system: NodeSystem) {
55
65
  Error(-32603, "Internal error")
56
66
  }
57
67
  message.id.each {id =>
58
- responseChannel.write(makeResponseMessage(system.js(), id, problem))
68
+ let body = makeResponseMessage(system.js(), id, problem)
69
+ responseChannel.write(Pair(body, message.method))
59
70
  }
60
71
  } grab()
61
72
  }
@@ -74,17 +85,17 @@ taskGroup[K: Order, R](
74
85
  mutable tasks = [].toMap()
75
86
  function spawn(key: K, body: Task => Unit): Task {
76
87
  let body2 = {task =>
77
- lock.do {tasks = tasks.add(key, task)}
88
+ lock.do(reentrant = True) {tasks = tasks.add(key, task)}
78
89
  try {
79
90
  body(task)
80
91
  } finally {
81
- lock.do {tasks = tasks.remove(key)}
92
+ lock.do(reentrant = True) {tasks = tasks.remove(key)}
82
93
  } grab()
83
94
  }
84
95
  parentTask.spawn(body2)
85
96
  }
86
97
  function abort(key: K): Unit {
87
- lock.do {tasks.get(key).each {_.abort()}}
98
+ lock.do(reentrant = True) {tasks.get(key).each {_.abort()}}
88
99
  }
89
100
  groupBody(spawn, abort)
90
101
  }
@@ -227,3 +238,17 @@ makeResponseMessage(js: JsSystem, id: MessageId, result: ResultOrError): JsValue
227
238
  }
228
239
  o
229
240
  }
241
+
242
+ logMessage(directory: Path, messageType: String, body: JsValue, method: String) {
243
+ let id = if(method == "$/cancelRequest") {
244
+ Some(body.get("params").get("id").grabInt())
245
+ } else {
246
+ body.getOwn("id").map {_.grabInt()}
247
+ }
248
+ let number = directory.entries().toList().size()
249
+ let idName = id.map {_ + ""}.else {"_"}
250
+ let methodName = method.replace("$", "notification").replace("/", "_")
251
+ let fileName = number + "-id-" + idName + "-" + messageType + "-" + methodName + ".json"
252
+ let path = directory.slash(fileName)
253
+ path.writeText(body.toJson(Some(" ")))
254
+ }
package/lsp/stderr.txt ADDED
@@ -0,0 +1 @@
1
+ End of input while parsing request headers
package/lsp/stdin.txt ADDED
@@ -0,0 +1,11 @@
1
+ Content-Length: 5836
2
+
3
+ {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":43727,"clientInfo":{"name":"Visual Studio Code","version":"1.76.2"},"locale":"en","rootPath":"/Users/ahnfelt/Downloads/firefly-boot","rootUri":"file:///Users/ahnfelt/Downloads/firefly-boot","capabilities":{"workspace":{"applyEdit":true,"workspaceEdit":{"documentChanges":true,"resourceOperations":["create","rename","delete"],"failureHandling":"textOnlyTransactional","normalizesLineEndings":true,"changeAnnotationSupport":{"groupsOnLabel":true}},"configuration":true,"didChangeWatchedFiles":{"dynamicRegistration":true,"relativePatternSupport":true},"symbol":{"dynamicRegistration":true,"symbolKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]},"tagSupport":{"valueSet":[1]},"resolveSupport":{"properties":["location.range"]}},"codeLens":{"refreshSupport":true},"executeCommand":{"dynamicRegistration":true},"didChangeConfiguration":{"dynamicRegistration":true},"workspaceFolders":true,"semanticTokens":{"refreshSupport":true},"fileOperations":{"dynamicRegistration":true,"didCreate":true,"didRename":true,"didDelete":true,"willCreate":true,"willRename":true,"willDelete":true},"inlineValue":{"refreshSupport":true},"inlayHint":{"refreshSupport":true},"diagnostics":{"refreshSupport":true}},"textDocument":{"publishDiagnostics":{"relatedInformation":true,"versionSupport":false,"tagSupport":{"valueSet":[1,2]},"codeDescriptionSupport":true,"dataSupport":true},"synchronization":{"dynamicRegistration":true,"willSave":true,"willSaveWaitUntil":true,"didSave":true},"completion":{"dynamicRegistration":true,"contextSupport":true,"completionItem":{"snippetSupport":true,"commitCharactersSupport":true,"documentationFormat":["markdown","plaintext"],"deprecatedSupport":true,"preselectSupport":true,"tagSupport":{"valueSet":[1]},"insertReplaceSupport":true,"resolveSupport":{"properties":["documentation","detail","additionalTextEdits"]},"insertTextModeSupport":{"valueSet":[1,2]},"labelDetailsSupport":true},"insertTextMode":2,"completionItemKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25]},"completionList":{"itemDefaults":["commitCharacters","editRange","insertTextFormat","insertTextMode"]}},"hover":{"dynamicRegistration":true,"contentFormat":["markdown","plaintext"]},"signatureHelp":{"dynamicRegistration":true,"signatureInformation":{"documentationFormat":["markdown","plaintext"],"parameterInformation":{"labelOffsetSupport":true},"activeParameterSupport":true},"contextSupport":true},"definition":{"dynamicRegistration":true,"linkSupport":true},"references":{"dynamicRegistration":true},"documentHighlight":{"dynamicRegistration":true},"documentSymbol":{"dynamicRegistration":true,"symbolKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]},"hierarchicalDocumentSymbolSupport":true,"tagSupport":{"valueSet":[1]},"labelSupport":true},"codeAction":{"dynamicRegistration":true,"isPreferredSupport":true,"disabledSupport":true,"dataSupport":true,"resolveSupport":{"properties":["edit"]},"codeActionLiteralSupport":{"codeActionKind":{"valueSet":["","quickfix","refactor","refactor.extract","refactor.inline","refactor.rewrite","source","source.organizeImports"]}},"honorsChangeAnnotations":false},"codeLens":{"dynamicRegistration":true},"formatting":{"dynamicRegistration":true},"rangeFormatting":{"dynamicRegistration":true},"onTypeFormatting":{"dynamicRegistration":true},"rename":{"dynamicRegistration":true,"prepareSupport":true,"prepareSupportDefaultBehavior":1,"honorsChangeAnnotations":true},"documentLink":{"dynamicRegistration":true,"tooltipSupport":true},"typeDefinition":{"dynamicRegistration":true,"linkSupport":true},"implementation":{"dynamicRegistration":true,"linkSupport":true},"colorProvider":{"dynamicRegistration":true},"foldingRange":{"dynamicRegistration":true,"rangeLimit":5000,"lineFoldingOnly":true,"foldingRangeKind":{"valueSet":["comment","imports","region"]},"foldingRange":{"collapsedText":false}},"declaration":{"dynamicRegistration":true,"linkSupport":true},"selectionRange":{"dynamicRegistration":true},"callHierarchy":{"dynamicRegistration":true},"semanticTokens":{"dynamicRegistration":true,"tokenTypes":["namespace","type","class","enum","interface","struct","typeParameter","parameter","variable","property","enumMember","event","function","method","macro","keyword","modifier","comment","string","number","regexp","operator","decorator"],"tokenModifiers":["declaration","definition","readonly","static","deprecated","abstract","async","modification","documentation","defaultLibrary"],"formats":["relative"],"requests":{"range":true,"full":{"delta":true}},"multilineTokenSupport":false,"overlappingTokenSupport":false,"serverCancelSupport":true,"augmentsSyntaxTokens":true},"linkedEditingRange":{"dynamicRegistration":true},"typeHierarchy":{"dynamicRegistration":true},"inlineValue":{"dynamicRegistration":true},"inlayHint":{"dynamicRegistration":true,"resolveSupport":{"properties":["tooltip","textEdits","label.tooltip","label.location","label.command"]}},"diagnostic":{"dynamicRegistration":true,"relatedDocumentSupport":false}},"window":{"showMessage":{"messageActionItem":{"additionalPropertiesSupport":true}},"showDocument":{"support":true},"workDoneProgress":true},"general":{"staleRequestSupport":{"cancel":true,"retryOnContentModified":["textDocument/semanticTokens/full","textDocument/semanticTokens/range","textDocument/semanticTokens/full/delta"]},"regularExpressions":{"engine":"ECMAScript","version":"ES2020"},"markdown":{"parser":"marked","version":"1.1.0"},"positionEncodings":["utf-16"]},"notebookDocument":{"synchronization":{"dynamicRegistration":true,"executionSummarySupport":true}}},"trace":"off","workspaceFolders":[{"uri":"file:///Users/ahnfelt/Downloads/firefly-boot","name":"firefly-boot"}]}}Content-Length: 52
4
+
5
+ {"jsonrpc":"2.0","method":"initialized","params":{}}Content-Length: 9421
6
+
7
+ {"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///Users/ahnfelt/Downloads/firefly-boot/lsp/LanguageServer.ff","languageId":"firefly","version":1,"text":"class LanguageServer(\n mutable openFiles: Map[String, String]\n)\n\ndata BadRequestException(reason: String)\n\ncapability Request(\n headers: Map[String, String]\n object: JsValue\n)\n\ncapability RequestMessage(\n id: Option[MessageId] // None when the request is a Notification Message\n method: String\n parameters: Map[String, JsValue]\n)\n\ndata MessageId {\n MessageIdInt(id: Int)\n MessageIdString(id: String)\n}\n\ncapability ResultOrError {\n Result(result: JsValue)\n Error(code: Int, message: String)\n}\n\n\nmain(system: NodeSystem) {\n try {\n mutable input = system.readStream()\n while {True} {\n let requestPair = parseRequest(system, input)\n input = requestPair.second\n let request = requestPair.first\n let message = parseRequestMessage(request.object)\n handleRequestMessage(system.js(), message).each {body =>\n system.writeText(makeResponse(body))\n }\n //system.writeText(makeResponse(makeDiagnoseNotification(system.js())))\n }\n } catch {| BadRequestException(reason), error =>\n system.writeErrorText(reason + \"\\n\")\n } grab()\n}\n \nparseRequest(system: NodeSystem, input: Stream[Buffer]): Pair[Request, Stream[Buffer]] {\n let headersPair = parseRequestHeaders(input)\n let headers = headersPair.first\n let contentLength = headers\n .get(\"content-length\").else {throw(BadRequestException(\"'content-length' header is missing\"))}\n .getInt().else {throw(BadRequestException(\"Value for 'content-length' is not an integer\"))}\n let bodyPair = parseRequestBody(system.js(), contentLength, headersPair.second)\n bodyPair.mapFirst {body => Request(headers, body)}\n}\n\nparseRequestHeaders(input: Stream[Buffer]): Pair[Map[String, String], Stream[Buffer]] {\n let buffers = Stack.make()\n mutable buffer = input.next().else {\n throw(BadRequestException(\"End of input while parsing request headers\"))\n }\n mutable offset = 0\n mutable droppingInitialNewlines = True\n mutable lastWasNewline = False\n mutable done = False\n while {!done} {\n if(offset == buffer.size()) {\n offset = 0\n buffers.push(buffer)\n buffer = input.next().else {\n throw(BadRequestException(\"End of input while parsing request headers\"))\n }\n }\n let byte = buffer.grabUint8(offset)\n if(droppingInitialNewlines && (byte == '\\n'.codeUnit || byte == '\\r'.codeUnit)) {\n // Skip\n } elseIf {byte == '\\n'.codeUnit} {\n if(lastWasNewline) {\n done = True\n } else {\n lastWasNewline = True\n }\n } elseIf {byte != '\\r'.codeUnit && lastWasNewline} {\n lastWasNewline = False\n } else {\n droppingInitialNewlines = False\n }\n offset += 1\n }\n buffers.push(buffer.view(0, offset))\n let headers = Buffer.fromBufferArray(buffers.drain()).toString()\n let map = headers.lines().pairs().filter {_.second.size() != 0}.map {| Pair(i, line) =>\n line.splitFirst(':').else {\n throw(BadRequestException(\"Invalid header at line \" + i + \" '\" + line + \"'\"))\n }.mapFirst {_.lower()} //.mapSecond {_.trim()}\n }.toMap()\n Pair(map, [buffer.view(offset, buffer.size())].toStream().addAll(input))\n}\n\nparseRequestBody(js: JsSystem, contentLength: Int, input: Stream[Buffer]): Pair[JsValue, Stream[Buffer]] {\n let bodyPair = try {\n input.readBytes(contentLength) // Should Stream.readBytes return an option?\n } catchAny {error =>\n throw(BadRequestException(\"End of input while parsing request body\"))\n } grab()\n let body = Buffer.fromBufferArray(bodyPair.first).toString()\n let json = try {\n js.parseJson(body)\n } catchAny {error =>\n throw(BadRequestException(\"Invalid JSON in request body: \" + body))\n } grab()\n Pair(json, bodyPair.second)\n}\n\nparseRequestMessage(object: JsValue): RequestMessage {\n let id = object.getOwn(\"id\").map {id =>\n if(id.isInt()) {\n MessageIdInt(id.grabInt())\n } elseIf {id.isString()} {\n MessageIdString(id.grabString())\n } else {\n throw(BadRequestException(\"Bad JSON-RPC id, int or string expected\"))\n }\n }\n\n let method = object.getOwn(\"method\").{\n | None => throw(BadRequestException(\"Bad JSON-RPC, missing method\"))\n | Some(m) {!m.isString()} => throw(BadRequestException(\"Bad JSON-RPC method, string expected\"))\n | Some(m) => m.grabString()\n }\n\n let parameters = object.getOwn(\"params\").{\n | None => [].toMap()\n | Some(o) {!o.isObject()} => throw(BadRequestException(\"Bad JSON-RPC params, object expected\"))\n | Some(o) => o.grabMap()\n }\n\n RequestMessage(id, method, parameters)\n}\n\nhandleRequestMessage(js: JsSystem, message: RequestMessage): Option[JsValue] {\n message.id.{\n // Notification Message\n | None =>\n message.method.{\n | \"initialized\" =>\n | _ =>\n }\n None\n // Request Message\n | Some(id) =>\n let result = message.method.{\n | \"initialize\" => handleInitialize(js, message.parameters)\n | \"textDocument/diagnostic\" => handleDiagnostic(js, message.parameters)\n | \"hover\" => handleHover(js, message.parameters)\n | _ => handleUnsupported(js)\n }\n Some(makeResponseMessage(js, id, result))\n }\n}\n\nmakeResponse(body: JsValue): String {\n let json = body.toJson(Some(\" \"))\n let length = json.size()\n \"Content-Length: \" + length + \"\\r\\n\" +\n \"\\r\\n\" +\n json\n}\n\nmakeResponseMessage(js: JsSystem, id: MessageId, result: ResultOrError): JsValue {\n let o = js.object()\n o.set(\"jsonrpc\", \"2.0\")\n id.{\n | MessageIdInt(id) => o.set(\"id\", id)\n | MessageIdString(id) => o.set(\"id\", id)\n }\n result.{\n | Result(result) =>\n o.set(\"result\", result)\n | Error(code, message) =>\n let e = js.object()\n e.set(\"code\", code)\n e.set(\"message\", message)\n o.set(\"error\", e)\n }\n o\n}\n\nhandleUnsupported(js: JsSystem): ResultOrError {\n Error(1234, \"Unsupported method\")\n}\n\nhandleInitialize(js: JsSystem, parameters: Map[String, JsValue]): ResultOrError {\n let anyFireflyFile = js.object()\n .with(\"filters\", [\n js.object()\n .with(\"pattern\", js.object()\n .with(\"glob\", \"**/*.ff\")\n .with(\"matches\", \"file\")\n )\n ].toArray())\n\n let o = js.object()\n .with(\"capabilities\", js.object()\n .with(\"textDocumentSync\", js.object()\n .with(\"openClose\", True)\n .with(\"change\", 1 /* TextDocumentSyncKind.Full */)\n )\n .with(\"hoverProvider\", False)\n //.with(\"definitionProvider\", True)\n //.with(\"typeDefinitionProvider\", True)\n .with(\"diagnosticProvider\", js.object()\n .with(\"interFileDependencies\", True)\n .with(\"workspaceDiagnostics\", False)\n )\n /*.with(\"workspace\", js.object()\n .with(\"workspaceFolders\", js.object()\n .with(\"supported\", True)\n .with(\"changeNotifications\", True)\n )\n .with(\"fileOperations\", js.object()\n .with(\"didCreate\", anyFireflyFile)\n .with(\"didRename\", anyFireflyFile)\n .with(\"didDelete\", anyFireflyFile)\n )\n )*/\n )\n .with(\"serverInfo\", js.object()\n .with(\"name\", \"Firefly Language Server\")\n .with(\"version\", \"0.0.0\")\n )\n Result(o)\n}\n\nhandleDiagnostic(js: JsSystem, parameters: Map[String, JsValue]): ResultOrError {\n let diagnostic = js.object()\n .with(\"range\", js.object()\n .with(\"start\", js.object()\n .with(\"line\", 0).with(\"character\", 6)\n )\n .with(\"end\", js.object()\n .with(\"line\", 0).with(\"character\", 20)\n )\n )\n .with(\"severity\", 1 /* Error */)\n .with(\"message\", \"Hurray!\")\n\n let o = js.object()\n .with(\"kind\", \"full\")\n .with(\"items\", [/*diagnostic*/].toArray())\n\n Result(o)\n}\n\nhandleHover(js: JsSystem, parameters: Map[String, JsValue]): ResultOrError {\n let o = js.object().with(\"contents\", \"TODO\")\n Result(o)\n}\n\nmakeNotificationMessage(js: JsSystem, method: String, params: JsValue): JsValue {\n js.object()\n .with(\"jsonrpc\", \"2.0\")\n .with(\"method\", method)\n .with(\"params\", params)\n}\n"}}}Content-Length: 162
8
+
9
+ {"jsonrpc":"2.0","id":1,"method":"textDocument/diagnostic","params":{"textDocument":{"uri":"file:///Users/ahnfelt/Downloads/firefly-boot/lsp/LanguageServer.ff"}}}Content-Length: 44
10
+
11
+ {"jsonrpc":"2.0","id":2,"method":"shutdown"}
package/lsp/stdout.txt ADDED
@@ -0,0 +1,41 @@
1
+ Content-Length: 500
2
+
3
+ {
4
+ "jsonrpc": "2.0",
5
+ "id": 0,
6
+ "result": {
7
+ "capabilities": {
8
+ "textDocumentSync": {
9
+ "openClose": true,
10
+ "change": 1
11
+ },
12
+ "hoverProvider": false,
13
+ "diagnosticProvider": {
14
+ "interFileDependencies": true,
15
+ "workspaceDiagnostics": false
16
+ }
17
+ },
18
+ "serverInfo": {
19
+ "name": "Firefly Language Server",
20
+ "version": "0.0.0"
21
+ }
22
+ }
23
+ }Content-Length: 104
24
+
25
+ {
26
+ "jsonrpc": "2.0",
27
+ "id": 1,
28
+ "result": {
29
+ "kind": "full",
30
+ "items": []
31
+ }
32
+ }Content-Length: 121
33
+
34
+ {
35
+ "jsonrpc": "2.0",
36
+ "id": 2,
37
+ "error": {
38
+ "code": 1234,
39
+ "message": "Unsupported method"
40
+ }
41
+ }
package/lux/Main.ff CHANGED
@@ -3,6 +3,10 @@ import HttpServer from ff:httpserver
3
3
  import Lux
4
4
  import LuxEvent
5
5
 
6
+ data ChatEntry(
7
+ question: String
8
+ answer: Option[String]
9
+ )
6
10
 
7
11
  mainComponent(lux: Lux, http: HttpClient) {
8
12
  lux.useState([]): chat, setChat =>
@@ -46,6 +50,7 @@ browserMain(system: BrowserSystem) {
46
50
  }
47
51
 
48
52
  nodeMain(system: NodeSystem) {
53
+ let openAiKey = system.arguments().first()
49
54
  HttpServer.listen(system, "localhost", 8080) {request, response =>
50
55
  if(request.path() == "/") {
51
56
  response.setHeader("Content-Type", ["text/html; charset=UTF-8"])
@@ -56,15 +61,64 @@ nodeMain(system: NodeSystem) {
56
61
  response.setHeader("Content-Type", ["text/javascript; charset=UTF-8"])
57
62
  response.writeText(system.assets().readText(request.path()))
58
63
  } elseIf {request.path() == "/chat"} {
59
- system.mainTask().sleep(Duration(request.readText().size().toFloat()))
64
+ let question = request.readText()
60
65
  response.setHeader("Content-Type", ["text/plain; charset=UTF-8"])
61
- response.writeText("Hello there!")
66
+ openAiKey.{
67
+ | None =>
68
+ system.mainTask().sleep(Duration(question.size().toFloat()))
69
+ response.writeText("Hi! You need to give an OpenAI secret key at the command line.")
70
+ | Some(key) =>
71
+ let chatJson = encodeChat(system.js(), question)
72
+ let answer = fetchAnswer(system.httpClient(), system.js(), key, chatJson)
73
+ response.writeText(answer)
74
+ }
62
75
  } else {
63
76
  response.writeStatus(404, Some("Not found"))
64
77
  }
65
78
  }
66
79
  }
67
80
 
81
+ fetchAnswer(httpClient: HttpClient, js: JsSystem, key: String, question: JsValue): String {
82
+ let json = httpClient.fetch(
83
+ url = "https://api.openai.com/v1/chat/completions"
84
+ method = "POST"
85
+ headers = [
86
+ Pair("Authorization", "Bearer " + key)
87
+ Pair("Content-Type", "application/json")
88
+ ]
89
+ body = Some(HttpClient.bodyText(question.toJson()))
90
+ ).readText()
91
+ js.parseJson(json).get("choices").get(0).get("message").get("content").grabString()
92
+ }
93
+
94
+ encodeChat(js: JsSystem, question: String): JsValue {
95
+ let systemJson = js.object()
96
+ .with("role", "system")
97
+ .with("content", "You are a helpful assistant.")
98
+ let questionJson = js.object()
99
+ .with("role", "user")
100
+ .with("content", question)
101
+ let messagesJson = js.array([systemJson, questionJson])
102
+ js.object()
103
+ .with("model", "gpt-3.5-turbo")
104
+ .with("messages", messagesJson)
105
+ }
106
+
107
+
108
+ /*
109
+ let
110
+ encodeMessage role content =
111
+ E.object [ ("role", E.string role), ("content", E.string content) ]
112
+ encodeEntry question maybeAnswer = case maybeAnswer of
113
+ Nothing -> [ encodeMessage "user" question ]
114
+ Just answer -> [ encodeMessage "user" question, encodeMessage "assistant" answer ]
115
+ encodedMessages = encodeMessage "system" "You are a helpful assistant."
116
+ :: List.concatMap (\entry -> encodeEntry entry.question entry.answer) chat
117
+ in E.object
118
+ [ ("model", E.string "gpt-3.5-turbo")
119
+ , ("messages", E.list (\x -> x) encodedMessages)
120
+ ]
121
+ */
68
122
  buildMain(system: BuildSystem) {
69
123
  let browser = system.compileForBrowser("Main.ff")
70
124
  let assets = AssetSystem.create().addAssets("/js", browser.assets())
@@ -0,0 +1,6 @@
1
+ main(system: NodeSystem) {
2
+
3
+ let list = ["Meetup", "MF#K"]
4
+ list.map {a => a.}
5
+
6
+ }
@@ -138,7 +138,7 @@ export async function HttpClient_fetch$(self_, url_, method_ = "GET", headers_ =
138
138
  try {
139
139
  const options = {headers: {}, signal: $task.controller.signal}
140
140
  options.method = method_
141
- ff_core_List.List_each(headers_, pair => {options.headers[pair.key_] = pair.value_})
141
+ ff_core_List.List_each(headers_, pair => {options.headers[pair.first_] = pair.second_})
142
142
  if(body_.value_) options.body = body_.value_
143
143
  if(redirect_.RedirectError) options.redirect = "error"
144
144
  else if(redirect_.RedirectManual) options.redirect = "manual"
package/package.json CHANGED
@@ -1,29 +1,29 @@
1
- {
2
- "name": "firefly-compiler",
3
- "displayName": "Firefly Compiler",
4
- "description": "Firefly compiler",
5
- "author": "Firefly team",
6
- "license": "MIT",
7
- "version": "0.4.5",
8
- "repository": {
9
- "type": "git",
10
- "url": "https://github.com/Ahnfelt/firefly-boot"
11
- },
12
- "publisher": "firefly-team",
13
- "categories": [
14
- "Programming Languages"
15
- ],
16
- "engines": {
17
- "node": "^18.0.0"
18
- },
19
- "main": "./vscode/client/out/extension",
20
- "icon": "./vscode/icons/firefly-logo.png",
21
- "dependencies": {
22
- "esbuild": "^0.14.54",
23
- "pkg": "^5.8.0",
24
- "tar": "^6.1.11"
25
- },
26
- "bin": {
27
- "firefly": "./bin/firefly.mjs"
28
- }
29
- }
1
+ {
2
+ "name": "firefly-compiler",
3
+ "displayName": "Firefly Compiler",
4
+ "description": "Firefly compiler",
5
+ "author": "Firefly team",
6
+ "license": "MIT",
7
+ "version": "0.4.7",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/Ahnfelt/firefly-boot"
11
+ },
12
+ "publisher": "firefly-team",
13
+ "categories": [
14
+ "Programming Languages"
15
+ ],
16
+ "engines": {
17
+ "node": "^18.0.0"
18
+ },
19
+ "main": "./vscode/client/out/extension",
20
+ "icon": "./vscode/icons/firefly-logo.png",
21
+ "dependencies": {
22
+ "esbuild": "^0.14.54",
23
+ "pkg": "^5.8.0",
24
+ "tar": "^6.1.11"
25
+ },
26
+ "bin": {
27
+ "firefly": "./bin/firefly.mjs"
28
+ }
29
+ }
@@ -1,12 +1,12 @@
1
1
  {
2
- "name": "firefly",
3
- "version": "0.4.0",
2
+ "name": "firefly-lang",
3
+ "version": "0.4.4",
4
4
  "lockfileVersion": 2,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
- "name": "firefly",
9
- "version": "0.4.0",
8
+ "name": "firefly-lang",
9
+ "version": "0.4.4",
10
10
  "hasInstallScript": true,
11
11
  "license": "MIT",
12
12
  "devDependencies": {
@@ -19,7 +19,7 @@
19
19
  "typescript": "^4.9.5"
20
20
  },
21
21
  "engines": {
22
- "vscode": "^1.75.0"
22
+ "vscode": "^1.82.0"
23
23
  }
24
24
  },
25
25
  "node_modules/@eslint-community/eslint-utils": {
@@ -4,7 +4,7 @@
4
4
  "description": "Firefly language support",
5
5
  "author": "Firefly team",
6
6
  "license": "MIT",
7
- "version": "0.4.4",
7
+ "version": "0.4.7",
8
8
  "repository": {
9
9
  "type": "git",
10
10
  "url": "https://github.com/Ahnfelt/firefly-boot"