firefly-compiler 0.5.35 → 0.5.37

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 (225) hide show
  1. package/.hintrc +4 -4
  2. package/.vscode/settings.json +4 -4
  3. package/bin/Release.ff +157 -154
  4. package/bin/firefly.mjs +1 -1
  5. package/compiler/Builder.ff +275 -277
  6. package/compiler/Compiler.ff +234 -233
  7. package/compiler/Dependencies.ff +186 -187
  8. package/compiler/DependencyLock.ff +17 -17
  9. package/compiler/Deriver.ff +23 -31
  10. package/compiler/Dictionaries.ff +1 -1
  11. package/compiler/Inference.ff +43 -20
  12. package/compiler/JsEmitter.ff +1437 -1282
  13. package/compiler/LspHook.ff +202 -202
  14. package/compiler/Main.ff +25 -24
  15. package/compiler/ModuleCache.ff +178 -178
  16. package/compiler/Parser.ff +36 -109
  17. package/compiler/Resolver.ff +5 -8
  18. package/compiler/Substitution.ff +1 -1
  19. package/compiler/Syntax.ff +1 -16
  20. package/compiler/Token.ff +9 -0
  21. package/compiler/Tokenizer.ff +4 -0
  22. package/compiler/Workspace.ff +88 -88
  23. package/core/.firefly/include/package.json +5 -5
  24. package/core/.firefly/package.ff +2 -2
  25. package/core/Any.ff +26 -30
  26. package/core/Array.ff +298 -265
  27. package/core/Atomic.ff +63 -64
  28. package/core/Box.ff +7 -7
  29. package/core/BrowserSystem.ff +40 -40
  30. package/core/Buffer.ff +185 -152
  31. package/core/BuildSystem.ff +156 -148
  32. package/core/Channel.ff +95 -92
  33. package/core/Char.ff +3 -2
  34. package/core/Core.ff +16 -23
  35. package/core/Crypto.ff +94 -96
  36. package/core/Equal.ff +41 -36
  37. package/core/Error.ff +15 -10
  38. package/core/FileHandle.ff +45 -37
  39. package/core/Float.ff +176 -200
  40. package/core/HttpClient.ff +142 -148
  41. package/core/Instant.ff +6 -8
  42. package/core/Int.ff +40 -24
  43. package/core/IntMap.ff +61 -39
  44. package/core/Js.ff +305 -0
  45. package/core/JsSystem.ff +135 -114
  46. package/core/JsValue.ff +303 -159
  47. package/core/Json.ff +423 -443
  48. package/core/List.ff +482 -486
  49. package/core/Lock.ff +108 -144
  50. package/core/Log.ff +25 -14
  51. package/core/NodeSystem.ff +198 -191
  52. package/core/Ordering.ff +160 -161
  53. package/core/Path.ff +377 -409
  54. package/core/Queue.ff +90 -0
  55. package/core/Random.ff +140 -134
  56. package/core/RbMap.ff +216 -216
  57. package/core/Serializable.ff +16 -13
  58. package/core/Show.ff +44 -43
  59. package/core/SourceLocation.ff +68 -68
  60. package/core/Stream.ff +1 -1
  61. package/core/String.ff +224 -202
  62. package/core/StringMap.ff +58 -36
  63. package/core/Task.ff +165 -149
  64. package/experimental/benchmarks/ListGrab.ff +23 -23
  65. package/experimental/benchmarks/ListGrab.java +55 -55
  66. package/experimental/benchmarks/Pyrotek45.ff +30 -30
  67. package/experimental/benchmarks/Pyrotek45.java +64 -64
  68. package/experimental/bidirectional/Bidi.ff +88 -88
  69. package/experimental/lines/Main.ff +40 -0
  70. package/experimental/random/Index.ff +53 -53
  71. package/experimental/random/Process.ff +120 -120
  72. package/experimental/random/RunLength.ff +65 -65
  73. package/experimental/random/Scrape.ff +51 -51
  74. package/experimental/random/Symbols.ff +73 -73
  75. package/experimental/random/Tensor.ff +52 -52
  76. package/experimental/random/Units.ff +36 -36
  77. package/experimental/s3/S3TestAuthorizationHeader.ff +39 -39
  78. package/experimental/s3/S3TestPut.ff +16 -16
  79. package/experimental/tests/TestJson.ff +26 -26
  80. package/firefly.sh +0 -0
  81. package/fireflysite/.firefly/package.ff +4 -4
  82. package/fireflysite/CommunityOverview.ff +20 -20
  83. package/fireflysite/CountingButtonDemo.ff +58 -58
  84. package/fireflysite/DocumentParser.ff +325 -331
  85. package/fireflysite/ExamplesOverview.ff +40 -40
  86. package/fireflysite/FrontPage.ff +344 -344
  87. package/fireflysite/GettingStarted.ff +45 -45
  88. package/fireflysite/Guide.ff +456 -456
  89. package/fireflysite/Main.ff +163 -152
  90. package/fireflysite/MatchingPasswordsDemo.ff +82 -82
  91. package/fireflysite/PackagesOverview.ff +49 -49
  92. package/fireflysite/PostgresqlDemo.ff +34 -34
  93. package/fireflysite/ReferenceAll.ff +18 -18
  94. package/fireflysite/ReferenceIntroduction.ff +11 -11
  95. package/fireflysite/Styles.ff +567 -567
  96. package/fireflysite/Test.ff +121 -62
  97. package/fireflysite/assets/markdown/reference/BaseTypes.md +209 -209
  98. package/fireflysite/assets/markdown/reference/EmittedJavascript.md +65 -65
  99. package/fireflysite/assets/markdown/reference/Exceptions.md +101 -101
  100. package/fireflysite/assets/markdown/reference/FunctionsAndMethods.md +364 -364
  101. package/fireflysite/assets/markdown/reference/JavascriptInterop.md +235 -172
  102. package/fireflysite/assets/markdown/reference/ModulesAndPackages.md +162 -162
  103. package/fireflysite/assets/markdown/reference/OldStructuredConcurrency.md +48 -48
  104. package/fireflysite/assets/markdown/reference/PatternMatching.md +224 -224
  105. package/fireflysite/assets/markdown/reference/StatementsAndExpressions.md +86 -86
  106. package/fireflysite/assets/markdown/reference/StructuredConcurrency.md +99 -99
  107. package/fireflysite/assets/markdown/reference/TraitsAndInstances.md +100 -100
  108. package/fireflysite/assets/markdown/reference/UserDefinedTypes.md +184 -184
  109. package/fireflysite/assets/markdown/scratch/ControlFlow.md +136 -136
  110. package/fireflysite/assets/markdown/scratch/Toc.md +40 -40
  111. package/lsp/.firefly/package.ff +1 -1
  112. package/lsp/CompletionHandler.ff +827 -827
  113. package/lsp/Handler.ff +714 -714
  114. package/lsp/HoverHandler.ff +79 -79
  115. package/lsp/LanguageServer.ff +272 -272
  116. package/lsp/SignatureHelpHandler.ff +55 -55
  117. package/lsp/SymbolHandler.ff +181 -181
  118. package/lsp/TestReferences.ff +17 -17
  119. package/lsp/TestReferencesCase.ff +7 -7
  120. package/lsp/stderr.txt +1 -1
  121. package/lsp/stdout.txt +34 -34
  122. package/lux/.firefly/package.ff +1 -1
  123. package/lux/Css.ff +648 -648
  124. package/lux/CssTest.ff +48 -48
  125. package/lux/Lux.ff +608 -617
  126. package/lux/LuxEvent.ff +79 -116
  127. package/lux/Main.ff +123 -123
  128. package/lux/Main2.ff +143 -143
  129. package/lux/TestDry.ff +28 -28
  130. package/output/js/ff/compiler/Builder.mjs +72 -71
  131. package/output/js/ff/compiler/Compiler.mjs +19 -13
  132. package/output/js/ff/compiler/Dependencies.mjs +8 -7
  133. package/output/js/ff/compiler/DependencyLock.mjs +6 -4
  134. package/output/js/ff/compiler/Deriver.mjs +26 -24
  135. package/output/js/ff/compiler/Dictionaries.mjs +14 -18
  136. package/output/js/ff/compiler/Environment.mjs +6 -4
  137. package/output/js/ff/compiler/Inference.mjs +238 -164
  138. package/output/js/ff/compiler/JsEmitter.mjs +1160 -350
  139. package/output/js/ff/compiler/JsImporter.mjs +20 -18
  140. package/output/js/ff/compiler/LspHook.mjs +12 -10
  141. package/output/js/ff/compiler/Main.mjs +61 -41
  142. package/output/js/ff/compiler/ModuleCache.mjs +10 -8
  143. package/output/js/ff/compiler/Parser.mjs +153 -669
  144. package/output/js/ff/compiler/Patterns.mjs +12 -10
  145. package/output/js/ff/compiler/Resolver.mjs +52 -78
  146. package/output/js/ff/compiler/Substitution.mjs +12 -16
  147. package/output/js/ff/compiler/Syntax.mjs +50 -341
  148. package/output/js/ff/compiler/Token.mjs +126 -4
  149. package/output/js/ff/compiler/Tokenizer.mjs +62 -52
  150. package/output/js/ff/compiler/Unification.mjs +74 -90
  151. package/output/js/ff/compiler/Wildcards.mjs +4 -2
  152. package/output/js/ff/compiler/Workspace.mjs +26 -20
  153. package/output/js/ff/core/Any.mjs +20 -20
  154. package/output/js/ff/core/Array.mjs +268 -175
  155. package/output/js/ff/core/AssetSystem.mjs +8 -6
  156. package/output/js/ff/core/Atomic.mjs +84 -52
  157. package/output/js/ff/core/Bool.mjs +6 -4
  158. package/output/js/ff/core/BrowserSystem.mjs +38 -29
  159. package/output/js/ff/core/Buffer.mjs +285 -133
  160. package/output/js/ff/core/BuildSystem.mjs +36 -56
  161. package/output/js/ff/core/Channel.mjs +250 -97
  162. package/output/js/ff/core/Char.mjs +5 -3
  163. package/output/js/ff/core/Core.mjs +28 -34
  164. package/output/js/ff/core/Crypto.mjs +30 -52
  165. package/output/js/ff/core/Duration.mjs +4 -2
  166. package/output/js/ff/core/Equal.mjs +14 -12
  167. package/output/js/ff/core/Error.mjs +17 -11
  168. package/output/js/ff/core/FileHandle.mjs +76 -38
  169. package/output/js/ff/core/Float.mjs +92 -160
  170. package/output/js/ff/core/HttpClient.mjs +208 -76
  171. package/output/js/ff/core/Instant.mjs +8 -10
  172. package/output/js/ff/core/Int.mjs +36 -26
  173. package/output/js/ff/core/IntMap.mjs +79 -33
  174. package/output/js/ff/core/Js.mjs +751 -0
  175. package/output/js/ff/core/JsSystem.mjs +54 -60
  176. package/output/js/ff/core/JsValue.mjs +294 -143
  177. package/output/js/ff/core/Json.mjs +443 -253
  178. package/output/js/ff/core/List.mjs +262 -214
  179. package/output/js/ff/core/Lock.mjs +156 -125
  180. package/output/js/ff/core/Log.mjs +20 -10
  181. package/output/js/ff/core/Map.mjs +10 -8
  182. package/output/js/ff/core/NodeSystem.mjs +189 -123
  183. package/output/js/ff/core/Nothing.mjs +4 -2
  184. package/output/js/ff/core/Option.mjs +40 -38
  185. package/output/js/ff/core/Ordering.mjs +26 -20
  186. package/output/js/ff/core/Pair.mjs +4 -2
  187. package/output/js/ff/core/Path.mjs +517 -315
  188. package/output/js/ff/core/Queue.mjs +306 -0
  189. package/output/js/ff/core/Random.mjs +141 -77
  190. package/output/js/ff/core/RbMap.mjs +36 -34
  191. package/output/js/ff/core/Serializable.mjs +44 -28
  192. package/output/js/ff/core/Set.mjs +6 -4
  193. package/output/js/ff/core/Show.mjs +8 -6
  194. package/output/js/ff/core/SourceLocation.mjs +4 -2
  195. package/output/js/ff/core/Stream.mjs +30 -50
  196. package/output/js/ff/core/String.mjs +263 -172
  197. package/output/js/ff/core/StringMap.mjs +77 -31
  198. package/output/js/ff/core/Task.mjs +91 -76
  199. package/output/js/ff/core/Try.mjs +20 -18
  200. package/output/js/ff/core/Unit.mjs +4 -2
  201. package/package.json +1 -1
  202. package/postgresql/Pg.ff +53 -59
  203. package/rpc/.firefly/package.ff +1 -1
  204. package/rpc/Rpc.ff +70 -70
  205. package/s3/.firefly/package.ff +1 -1
  206. package/s3/S3.ff +92 -94
  207. package/vscode/LICENSE.txt +21 -21
  208. package/vscode/Prepublish.ff +15 -15
  209. package/vscode/README.md +16 -16
  210. package/vscode/client/package-lock.json +544 -544
  211. package/vscode/client/package.json +22 -22
  212. package/vscode/client/src/extension.ts +104 -104
  213. package/vscode/icons/firefly-icon.svg +10 -10
  214. package/vscode/language-configuration.json +61 -61
  215. package/vscode/package-lock.json +3623 -3623
  216. package/vscode/package.json +1 -1
  217. package/vscode/snippets.json +241 -241
  218. package/vscode/syntaxes/firefly-markdown-injection.json +45 -45
  219. package/webserver/.firefly/include/package.json +5 -5
  220. package/webserver/.firefly/package.ff +2 -2
  221. package/webserver/WebServer.ff +647 -685
  222. package/websocket/.firefly/package.ff +1 -1
  223. package/websocket/WebSocket.ff +100 -131
  224. package/core/UnsafeJs.ff +0 -42
  225. package/output/js/ff/core/UnsafeJs.mjs +0 -191
package/core/Path.ff CHANGED
@@ -1,409 +1,377 @@
1
- capability Path {}
2
- capability PathEntry {}
3
-
4
- extend self: Path {
5
-
6
- exists(checkReadable: Bool = False, checkWritable: Bool = False, checkExecutable: Bool = False): Bool
7
- target node async """
8
- import * as fs from 'fs'
9
- import * as fsPromises from 'fs/promises'
10
- const flags =
11
- (fs.constants.R_OK * checkReadable_) |
12
- (fs.constants.W_OK * checkWritable_) |
13
- (fs.constants.X_OK * checkExecutable_)
14
- try {
15
- await fsPromises.access(self_, flags === 0 ? fs.constants.F_OK : flags)
16
- return true
17
- } catch(e) {
18
- return false
19
- }
20
- """
21
-
22
- isReadable(): Bool {
23
- self.exists(checkReadable = True)
24
- }
25
-
26
- isWritable(): Bool {
27
- self.exists(checkWritable = True)
28
- }
29
-
30
- isExecutable(): Bool {
31
- self.exists(checkExecutable = True)
32
- }
33
-
34
- isDirectory(): Bool
35
- target node async """
36
- import * as fsPromises from 'fs/promises'
37
- try {
38
- return (await fsPromises.lstat(self_)).isDirectory();
39
- } catch(e) {
40
- return false;
41
- }
42
- """
43
-
44
- isFile(): Bool
45
- target node async """
46
- import * as fsPromises from 'fs/promises'
47
- try {
48
- return (await fsPromises.lstat(self_)).isFile();
49
- } catch(e) {
50
- return false;
51
- }
52
- """
53
-
54
- isSymbolicLink(): Bool
55
- target node async """
56
- import * as fsPromises from 'fs/promises'
57
- try {
58
- return (await fsPromises.lstat(self_)).isSymbolicLink();
59
- } catch(e) {
60
- return false;
61
- }
62
- """
63
-
64
- isInsideOf(path: Path): Bool
65
- target node async """
66
- import * as path from 'path'
67
- if(path_ === '/') return true
68
- const childPath = path.resolve(self_)
69
- const parentPath = path.resolve(path_)
70
- return childPath.startsWith(parentPath + path.sep) || childPath === parentPath
71
- """
72
-
73
- size(): Int
74
- target node async """
75
- return (await fs.promises.stat(file)).size
76
- """
77
-
78
- modified(): Instant
79
- target node async """
80
- return (await fs.promises.stat(file)).mtimeMs * 0.001
81
- """
82
-
83
- entries(): Stream[PathEntry]
84
- target node async """
85
- import * as fsPromises from 'fs/promises'
86
- let dir = null
87
- return ff_core_Stream.Stream(
88
- async () => {
89
- if(dir === null) dir = await fsPromises.opendir(self_, {bufferSize: 128})
90
- const entry = await dir.read()
91
- if(entry === null) return ff_core_Option.None()
92
- entry.ffPath = self_
93
- return ff_core_Option.Some(entry)
94
- },
95
- async () => {
96
- if(dir !== null) await dir.close()
97
- }
98
- )
99
- """
100
-
101
- absolute(): String
102
- target node async """
103
- import * as path from 'path'
104
- return path.resolve(self_)
105
- """
106
-
107
- relativeTo(path: Path): String
108
- target node async """
109
- import * as path from 'path';
110
- return path.relative(path_, self_);
111
- """
112
-
113
- endsWith(parts: List[String]): Bool {
114
- function go(pathOption: Option[Path], reversed: List[String]): Bool {
115
- | _, [] => True
116
- | Some(path), [p, ...ps] => path.base() == p && go(path.parent(), ps)
117
- | None, _ => False
118
- }
119
- go(Some(self), parts.reverse())
120
- }
121
-
122
- contains(parts: List[String]): Bool {
123
- self.endsWith(parts) || self.parent().any {_.contains(parts)}
124
- }
125
-
126
- base(): String
127
- target node async """
128
- import * as path from 'path'
129
- return path.basename(self_)
130
- """
131
-
132
- extension(): String
133
- target node async """
134
- import * as path from 'path'
135
- return path.extname(self_)
136
- """
137
-
138
- url(): String
139
- target node async """
140
- import * as url from 'url';
141
- return '' + url.pathToFileURL(self_);
142
- """
143
-
144
- delimiter(): String
145
- target node async """
146
- import * as path from 'path';
147
- return path.delimiter(self_);
148
- """
149
-
150
- separator(): String
151
- target node async """
152
- import * as path from 'path';
153
- return path.separator();
154
- """
155
-
156
- parent(): Option[Path]
157
- target node async """
158
- import * as path from 'path'
159
- const result = path.dirname(self_)
160
- return result !== "" && result !== self_
161
- ? ff_core_Option.Some(result)
162
- : ff_core_Option.None()
163
- """
164
-
165
- slash(relativePath: String): Path
166
- target node async """
167
- import * as path from 'path'
168
- return path.join(self_, relativePath_)
169
- """
170
-
171
- path(absoluteOrRelativePath: String): Path
172
- target node async """
173
- import * as path from 'path'
174
- return path.resolve(self_, absoluteOrRelativePath_)
175
- """
176
-
177
- copyTo(path: Path, retries: Int = 0, retryDelay: Int = 100) {
178
- if(self.isDirectory()) {
179
- if(path.exists()) {path.delete(retries, retryDelay)}
180
- path.createDirectory()
181
- self.entries().each {file =>
182
- file.path().copyTo(path.slash(file.path().relativeTo(self)), retries, retryDelay)
183
- }
184
- } elseIf {self.isSymbolicLink()} {
185
- path.createSymlinkTo(self.path(self.readSymbolicLink()), junction = True)
186
- } else {
187
- path.writeStream(self.readStream())
188
- }
189
- }
190
-
191
- createDirectory(createParentDirectories: Bool = False)
192
- target node async """
193
- import * as fsPromises from 'fs/promises'
194
- await fsPromises.mkdir(self_, {recursive: createParentDirectories_})
195
- """
196
-
197
- createSymlinkTo(path: Path, junction: Bool = False)
198
- target node async """
199
- import * as fsPromises from 'fs/promises'
200
- await fsPromises.symlink(path_, self_, junction_ ? 'junction' : null)
201
- """
202
-
203
- delete(retries: Int = 0, retryDelay: Int = 100)
204
- target node async """
205
- import * as fsPromises from 'fs/promises'
206
- await fsPromises.rm(self_, {recursive: true, retries: retries_, retryDelay: retryDelay_})
207
- """
208
-
209
- truncate(length: Int = 0)
210
- target node async """
211
- import * as fsPromises from 'fs/promises'
212
- await fsPromises.truncate(self_, length_)
213
- """
214
-
215
- renameTo(path: Path)
216
- target node async """
217
- import * as fsPromises from 'fs/promises'
218
- await fsPromises.rename(self_, path_)
219
- """
220
-
221
- readSymbolicLink(): String
222
- target node async """
223
- import * as fsPromises from 'fs/promises'
224
- return await fsPromises.readlink(self_)
225
- """
226
-
227
- readText(): String
228
- target node async """
229
- import * as fsPromises from 'fs/promises'
230
- try {
231
- return await fsPromises.readFile(self_, {encoding: 'UTF-8', signal: $task.controller.signal})
232
- } finally {
233
- if($task.controller.signal.aborted) $task.controller = new AbortController()
234
- }
235
- """
236
-
237
- writeText(text: String)
238
- target node async """
239
- import * as fsPromises from 'fs/promises'
240
- try {
241
- await fsPromises.writeFile(self_, text_, {encoding: 'UTF-8', signal: $task.controller.signal})
242
- } finally {
243
- if($task.controller.signal.aborted) $task.controller = new AbortController()
244
- }
245
- """
246
-
247
- appendText(text: String)
248
- target node async """
249
- import * as fsPromises from 'fs/promises'
250
- try {
251
- await fsPromises.appendFile(self_, text_, {encoding: 'UTF-8', signal: $task.controller.signal})
252
- } finally {
253
- if($task.controller.signal.aborted) $task.controller = new AbortController()
254
- }
255
- """
256
-
257
- readBuffer(): Buffer {
258
- self.readStream().toBuffer()
259
- }
260
-
261
- writeBuffer(buffer: Buffer) {
262
- self.writeStream([buffer].toStream())
263
- }
264
-
265
- appendBuffer(buffer: Buffer) {
266
- self.appendStream([buffer].toStream())
267
- }
268
-
269
- readStream(): Stream[Buffer]
270
- target node async """
271
- import * as fs from 'fs'
272
- return ff_core_Path.internalReadStream_$(() => fs.createReadStream(self_))
273
- """
274
-
275
- writeStream(stream: Stream[Buffer], createOnly: Bool = False)
276
- target node async """
277
- import * as fs from 'fs'
278
- let writeable = fs.createWriteStream(self_, {flags: createOnly_ ? 'wx' : 'w'})
279
- try {
280
- await ff_core_Stream.Stream_each$(stream_, async buffer => {
281
- if(!writeable.write(new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength))) {
282
- await new Promise((resolve, reject) => {
283
- $task.controller.signal.addEventListener('abort', reject)
284
- writeable.once('drain', () => {
285
- $task.controller.signal.removeEventListener('abort', reject)
286
- resolve()
287
- })
288
- })
289
- }
290
- }, $task)
291
- } finally {
292
- await new Promise((resolve, reject) => {
293
- writeable.close(err => {if(err) reject(err); else resolve();});
294
- });
295
- }
296
- """
297
-
298
- appendStream(stream: Stream[Buffer])
299
- target node async """
300
- import * as fs from 'fs'
301
- let writeable = fs.createWriteStream(self_, {flags: 'a'})
302
- try {
303
- await ff_core_Stream.Stream_each$(stream_, async buffer => {
304
- if(!writeable.write(new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength))) {
305
- await new Promise((resolve, reject) => {
306
- $task.controller.signal.addEventListener('abort', reject)
307
- writeable.once('drain', () => {
308
- $task.controller.signal.removeEventListener('abort', reject)
309
- resolve()
310
- })
311
- })
312
- }
313
- }, $task)
314
- } finally {
315
- await new Promise((resolve, reject) => {
316
- writeable.close(err => {if(err) reject(err); else resolve();});
317
- });
318
- }
319
- """
320
-
321
- readHandle(alsoWrite: Bool = False): FileHandle
322
- target node async """
323
- import * as fsPromises from 'fs/promises'
324
- return await fsPromises.open(self_, alsoWrite_ ? 'r+' : 'r')
325
- """
326
-
327
- writeHandle(alsoRead: Bool = False, mustCreate: Bool = False): FileHandle
328
- target node async """
329
- import * as fsPromises from 'fs/promises'
330
- return await fsPromises.open(self_, (mustCreate_ ? 'wx' : 'w') + (alsoRead_ ? '+' : ''))
331
- """
332
-
333
- appendHandle(alsoRead: Bool = False, mustCreate: Bool = False): FileHandle
334
- target node async """
335
- import * as fsPromises from 'fs/promises'
336
- return await fsPromises.open(self_, (mustCreate_ ? 'wx' : 'w') + (alsoRead_ ? '+' : ''))
337
- """
338
-
339
- }
340
-
341
- extend self: PathEntry {
342
-
343
- path(): Path
344
- target node async """
345
- import * as path from 'path'
346
- return path.join(self_.ffPath, self_.name)
347
- """
348
-
349
- isDirectory(): Bool
350
- target node async """
351
- return self_.isDirectory()
352
- """
353
-
354
- isFile(): Bool
355
- target node async """
356
- return self_.isFile()
357
- """
358
-
359
- isSymbolicLink(): Bool
360
- target node async """
361
- return self_.isSymbolicLink()
362
- """
363
-
364
- }
365
-
366
- internalReadStream(createReadStream: () => JsValue): Stream[Buffer]
367
- target node async """
368
- let task = null
369
- let readable = null
370
- let doResolve = null
371
- let doReject = null
372
- let seenError = null
373
- const abort = () => {
374
- if(task != null) {
375
- task.controller.signal.removeEventListener('abort', abort)
376
- readable.destroy()
377
- }
378
- }
379
- function open($task) {
380
- ff_core_Task.Task_throwIfAborted($task)
381
- task = $task
382
- readable = createReadStream_()
383
- readable.on('readable', () => {
384
- if(doResolve != null) doResolve()
385
- })
386
- readable.on('error', error => {
387
- task.controller.signal.removeEventListener('abort', abort)
388
- seenError = error
389
- if(doReject != null) doReject(error)
390
- })
391
- readable.on('close', () => {
392
- task.controller.signal.removeEventListener('abort', abort)
393
- if(doResolve != null) doResolve()
394
- })
395
- $task.controller.signal.addEventListener('abort', abort)
396
- }
397
- return ff_core_Stream.Stream(async function go($task) {
398
- if(task == null) open($task)
399
- let buffer = readable.read()
400
- if(buffer != null) return ff_core_Option.Some(new DataView(buffer.buffer, buffer.byteOffset, buffer.length))
401
- if(seenError != null) throw seenError
402
- if(readable.destroyed) return ff_core_Option.None()
403
- let promise = new Promise((resolve, reject) => {
404
- doResolve = () => {doResolve = null; doReject = null; resolve()}
405
- doReject = error => {doResolve = null; doReject = null; reject(error)}
406
- }).then(() => go($task))
407
- return await promise
408
- }, abort)
409
- """
1
+ capability Path(absolutePath: String)
2
+ capability PathEntry {}
3
+
4
+ extend self: Path {
5
+
6
+ exists(checkReadable: Bool = False, checkWritable: Bool = False, checkExecutable: Bool = False): Bool {
7
+ let fs = Js.import("fs")
8
+ let fsPromises = Js.import("fs/promises")
9
+ let flagsR: Int = (fs->constants->"R_OK" * checkReadable!)?
10
+ let flagsW: Int = (fs->constants->"W_OK" * checkWritable!)?
11
+ let flagsX: Int = (fs->constants->"X_OK" * checkExecutable!)?
12
+ let flags = flagsR.bitOr(flagsW.bitOr(flagsX))
13
+ try {
14
+ Js.await(
15
+ fsPromises->access(self.absolutePath, if(flags == 0) {fs->constants->"F_OK"} else {flags!})
16
+ )
17
+ True
18
+ } catchAny {_ =>
19
+ False
20
+ }
21
+ }
22
+
23
+ isReadable(): Bool {
24
+ self.exists(checkReadable = True)
25
+ }
26
+
27
+ isWritable(): Bool {
28
+ self.exists(checkWritable = True)
29
+ }
30
+
31
+ isExecutable(): Bool {
32
+ self.exists(checkExecutable = True)
33
+ }
34
+
35
+ isDirectory(): Bool {
36
+ let fsPromises = Js.import("fs/promises")
37
+ try {
38
+ Js.await(fsPromises->lstat(self.absolutePath))->isDirectory()?
39
+ } catchAny {_ =>
40
+ False
41
+ }
42
+ }
43
+
44
+ isFile(): Bool {
45
+ let fsPromises = Js.import("fs/promises")
46
+ try {
47
+ Js.await(fsPromises->lstat(self.absolutePath))->isFile()?
48
+ } catchAny {_ =>
49
+ False
50
+ }
51
+ }
52
+
53
+ isSymbolicLink(): Bool {
54
+ let fsPromises = Js.import("fs/promises")
55
+ try {
56
+ Js.await(fsPromises->lstat(self.absolutePath))->isSymbolicLink()?
57
+ } catchAny {_ =>
58
+ False
59
+ }
60
+ }
61
+
62
+ isInsideOf(path: Path): Bool {
63
+ let nodePath = Js.import("path")
64
+ if(path.absolutePath == "/") {True} else:
65
+ let childPath = self.absolutePath
66
+ let parentPath = path.absolutePath
67
+ childPath.startsWith(parentPath + nodePath->sep?) || childPath == parentPath
68
+ }
69
+
70
+ size(): Int {
71
+ let fs = Js.import("fs")
72
+ Js.await(fs->promises->stat(self.absolutePath))?
73
+ }
74
+
75
+ modified(): Instant {
76
+ let fs = Js.import("fs")
77
+ (Js.await(fs->promises->stat(self.absolutePath))->mtimeMs * 0.001!)?
78
+ }
79
+
80
+ entries(): Stream[PathEntry] {
81
+ let fsPromises = Js.import("fs/promises")
82
+ mutable dir = Js.null()
83
+ Stream {
84
+ if(dir.isNull()) {
85
+ dir = Js.await(fsPromises->opendir(self.absolutePath, Js->(bufferSize = 128)))
86
+ }
87
+ let entry = Js.await(dir->read())
88
+ if(!entry.isNull()) {
89
+ entry->ffPath = self.absolutePath
90
+ entry?
91
+ }
92
+ } {
93
+ if(!dir.isNull()) {
94
+ Js.await(dir->close())
95
+ }
96
+ }
97
+ }
98
+
99
+ absolute(): String {
100
+ self.absolutePath
101
+ }
102
+
103
+ relativeTo(path: Path): String {
104
+ let nodePath = Js.import("path")
105
+ nodePath->relative(path.absolutePath, self.absolutePath)?
106
+ }
107
+
108
+ endsWith(parts: List[String]): Bool {
109
+ function go(pathOption: Option[Path], reversed: List[String]): Bool {
110
+ | _, [] => True
111
+ | Some(path), [p, ...ps] => path.base() == p && go(path.parent(), ps)
112
+ | None, _ => False
113
+ }
114
+ go(Some(self), parts.reverse())
115
+ }
116
+
117
+ contains(parts: List[String]): Bool {
118
+ self.endsWith(parts) || self.parent().any {_.contains(parts)}
119
+ }
120
+
121
+ base(): String {
122
+ let path = Js.import("path")
123
+ path->basename(self.absolutePath)?
124
+ }
125
+
126
+ extension(): String {
127
+ let path = Js.import("path")
128
+ path->extname(self.absolutePath)?
129
+ }
130
+
131
+ url(): String {
132
+ let url = Js.import("url")
133
+ "" + url->pathToFileURL(self.absolutePath)?
134
+ }
135
+
136
+ delimiter(): String {
137
+ let path = Js.import("path")
138
+ path->delimiter(self.absolutePath)?
139
+ }
140
+
141
+ separator(): String {
142
+ let path = Js.import("path")
143
+ path->separator(self.absolutePath)?
144
+ }
145
+
146
+ parent(): Option[Path] {
147
+ let path = Js.import("path")
148
+ let result: String = path->dirname(self.absolutePath)?
149
+ if(result != "" && result != self.absolutePath) {Path(result)}
150
+ }
151
+
152
+ slash(relativePath: String): Path {
153
+ let path = Js.import("path")
154
+ Path(path->join(self.absolutePath, relativePath)?)
155
+ }
156
+
157
+ path(absoluteOrRelativePath: String): Path {
158
+ let path = Js.import("path")
159
+ Path(path->resolve(self.absolutePath, absoluteOrRelativePath)?)
160
+ }
161
+
162
+ copyTo(path: Path, retries: Int = 0, retryDelay: Int = 100) {
163
+ if(self.isDirectory()) {
164
+ if(path.exists()) {path.delete(retries, retryDelay)}
165
+ path.createDirectory()
166
+ self.entries().each {file =>
167
+ file.path().copyTo(path.slash(file.path().relativeTo(self)), retries, retryDelay)
168
+ }
169
+ } elseIf {self.isSymbolicLink()} {
170
+ path.createSymlinkTo(self.path(self.readSymbolicLink()), junction = True)
171
+ } else {
172
+ path.writeStream(self.readStream())
173
+ }
174
+ }
175
+
176
+ createDirectory(createParentDirectories: Bool = False) {
177
+ let fsPromises = Js.import("fs/promises")
178
+ Js.await(fsPromises->mkdir(self.absolutePath, Js->(recursive = createParentDirectories)))
179
+ }
180
+
181
+ createSymlinkTo(path: Path, junction: Bool = False) {
182
+ let fsPromises = Js.import("fs/promises")
183
+ Js.await(fsPromises->symlink(path.absolutePath, self.absolutePath, if(junction) {"junction"!} else {Js.null()}))
184
+ }
185
+
186
+ delete(retries: Int = 0, retryDelay: Int = 100) {
187
+ let fsPromises = Js.import("fs/promises")
188
+ Js.await(fsPromises->rm(self.absolutePath, Js->(recursive = True, retries = retries, retryDelay = retryDelay)))
189
+ }
190
+
191
+ truncate(length: Int = 0) {
192
+ let fsPromises = Js.import("fs/promises")
193
+ Js.await(fsPromises->truncate(self.absolutePath, length))
194
+ }
195
+
196
+ renameTo(path: Path) {
197
+ let fsPromises = Js.import("fs/promises")
198
+ Js.await(fsPromises->rename(self.absolutePath, path.absolutePath))
199
+ }
200
+
201
+ readSymbolicLink(): String {
202
+ let fsPromises = Js.import("fs/promises")
203
+ Js.await(fsPromises->readlink(self.absolutePath))?
204
+ }
205
+
206
+ readText(): String {
207
+ let fsPromises = Js.import("fs/promises")
208
+ Js.withSignal {signal =>
209
+ Js.await(fsPromises->readFile(self.absolutePath, Js->(encoding = "UTF-8", signal = signal)))?
210
+ }
211
+ }
212
+
213
+ writeText(text: String) {
214
+ let fsPromises = Js.import("fs/promises")
215
+ Js.withSignal {signal =>
216
+ Js.await(fsPromises->writeFile(self.absolutePath, text, Js->(encoding = "UTF-8", signal = signal)))
217
+ }
218
+ }
219
+
220
+ appendText(text: String) {
221
+ let fsPromises = Js.import("fs/promises")
222
+ Js.withSignal {signal =>
223
+ Js.await(fsPromises->appendFile(self.absolutePath, text, Js->(encoding = "UTF-8", signal = signal)))
224
+ }
225
+ }
226
+
227
+ readBuffer(): Buffer {
228
+ self.readStream().toBuffer()
229
+ }
230
+
231
+ writeBuffer(buffer: Buffer) {
232
+ self.writeStream([buffer].toStream())
233
+ }
234
+
235
+ appendBuffer(buffer: Buffer) {
236
+ self.appendStream([buffer].toStream())
237
+ }
238
+
239
+ readStream(): Stream[Buffer] {
240
+ let fs = Js.import("fs")
241
+ internalReadStream {fs->createReadStream(self.absolutePath)}
242
+ }
243
+
244
+ writeStream(stream: Stream[Buffer], createOnly: Bool = False) {
245
+ internalWriteStream(self, stream, if(createOnly) {"wx"} else {"w"})
246
+ }
247
+
248
+ appendStream(stream: Stream[Buffer]) {
249
+ internalWriteStream(self, stream, "a")
250
+ }
251
+
252
+ readHandle(alsoWrite: Bool = False): FileHandle {
253
+ let fsPromises = Js.import("fs/promises")
254
+ Js.await(fsPromises->open(self.absolutePath, if(alsoWrite) {"r+"} else {"r"}))?
255
+ }
256
+
257
+ writeHandle(alsoRead: Bool = False, mustCreate: Bool = False): FileHandle {
258
+ let fsPromises = Js.import("fs/promises")
259
+ let flags = if(mustCreate) {"wx"} else {"w"} + if(alsoRead) {"+"} else {""}
260
+ Js.await(fsPromises->open(self.absolutePath, flags))?
261
+ }
262
+
263
+ appendHandle(alsoRead: Bool = False, mustCreate: Bool = False): FileHandle {
264
+ let fsPromises = Js.import("fs/promises")
265
+ let flags = if(mustCreate) {"ax"} else {"a"} + if(alsoRead) {"+"} else {""}
266
+ Js.await(fsPromises->open(self.absolutePath, flags))?
267
+ }
268
+
269
+ }
270
+
271
+ extend self: PathEntry {
272
+
273
+ path(): Path {
274
+ let path = Js.import("path")
275
+ Path(path->join(self!->ffPath, self!->name)?)
276
+ }
277
+
278
+ isDirectory(): Bool {
279
+ self!->isDirectory()?
280
+ }
281
+
282
+ isFile(): Bool {
283
+ self!->isFile()?
284
+ }
285
+
286
+ isSymbolicLink(): Bool {
287
+ self!->isSymbolicLink()?
288
+ }
289
+
290
+ }
291
+
292
+ internalReadStream(createReadStream: () => JsValue): Stream[Buffer] {
293
+ mutable readable = None
294
+ mutable seenError = Js.null()
295
+ let emptyResolve = {}
296
+ let emptyReject = {_ => }
297
+ mutable doResolve = emptyResolve
298
+ mutable doReject = emptyReject
299
+ let open = {
300
+ let newReadable = createReadStream()
301
+ newReadable->on("readable", Js->{
302
+ doResolve()
303
+ })
304
+ newReadable->on("error", Js->{error =>
305
+ seenError = error
306
+ doReject(error)
307
+ })
308
+ newReadable->on("close", Js->{
309
+ doResolve()
310
+ })
311
+ readable = Some(newReadable)
312
+ newReadable
313
+ }
314
+ Stream {
315
+ Js.throwIfCancelled()
316
+ let jsStream = readable.else(open)
317
+ function go(): Option[Buffer] {
318
+ let jsBuffer = jsStream->read()
319
+ if(!jsBuffer.isNullOrUndefined()) {
320
+ let buffer: Buffer = Js->DataView->(jsBuffer->buffer, jsBuffer->byteOffset, jsBuffer->length)?
321
+ Some(buffer)
322
+ } else:
323
+ if(!seenError.isNullOrUndefined()) {
324
+ throwAny(seenError?)
325
+ } else:
326
+ if(jsStream->destroyed?) {None} else:
327
+ Js.withSignal {signal =>
328
+ let promise = Js->Promise->(Js->{resolve, reject =>
329
+ let jsDoReject = Js->{doReject(_)}
330
+ doResolve = {
331
+ signal->removeEventListener("abort", jsDoReject)
332
+ doResolve = emptyResolve
333
+ doReject = emptyReject
334
+ resolve.callValue0()
335
+ }
336
+ doReject = {error =>
337
+ signal->removeEventListener("abort", jsDoReject)
338
+ doResolve = emptyResolve
339
+ doReject = emptyReject
340
+ reject.callValue1(error)
341
+ }
342
+ signal->addEventListener("abort", jsDoReject)
343
+ })
344
+ Js.await(promise)
345
+ }
346
+ tailcall go()
347
+ }
348
+ go()
349
+ } {
350
+ readable.each {_->destroy()}
351
+ }
352
+ }
353
+
354
+ internalWriteStream(path: Path, stream: Stream[Buffer], flags: String) {
355
+ let fs = Js.import("fs")
356
+ let writable = fs->createWriteStream(path.absolutePath, Js->(flags = flags))
357
+ try {
358
+ stream.each {buffer =>
359
+ if(!writable->write(Js->Uint8Array->(buffer!->buffer, buffer!->byteOffset, buffer!->byteLength))?) {
360
+ Js.withSignal {signal =>
361
+ Js.await(Js->Promise->(Js->{resolve, reject =>
362
+ signal->addEventListener("abort", reject)
363
+ writable->once("drain", Js->{
364
+ signal->removeEventListener("abort", reject)
365
+ resolve.callValue0()
366
+ })
367
+ }))
368
+ }
369
+ }
370
+ }
371
+ } finally {
372
+ Js.await(Js->Promise->(Js->{resolve, reject =>
373
+ writable->close(Js->{err => if(err?) {reject.callValue1(err)} else {resolve.callValue0()}})
374
+ }))
375
+ }
376
+ }
377
+