elm-pages 3.0.0-beta.17 → 3.0.0-beta.18

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.
@@ -75,7 +75,7 @@ console.elmlog = (str) => logs.push(str + "\n");
75
75
  const { Elm } = require("./Runner.elm.js");
76
76
 
77
77
  // Start the Elm app
78
- const flags = { initialSeed: 4015861069, fuzzRuns: 100, filter: null };
78
+ const flags = { initialSeed: 3582516438, fuzzRuns: 100, filter: null };
79
79
  const app = Elm.Runner.init({ flags: flags });
80
80
 
81
81
  // Record the timing at which we received the last "runTest" message
@@ -82,7 +82,7 @@ const verbosity = 0;
82
82
  // Create a long lived reporter worker
83
83
  const { Elm } = require("./Reporter.elm.js");
84
84
  const flags = {
85
- initialSeed: 4015861069,
85
+ initialSeed: 3582516438,
86
86
  fuzzRuns: 100,
87
87
  mode: "consoleNoColor",
88
88
  verbosity: verbosity,
@@ -75,7 +75,7 @@ console.elmlog = (str) => logs.push(str + "\n");
75
75
  const { Elm } = require("./Runner.elm.js");
76
76
 
77
77
  // Start the Elm app
78
- const flags = { initialSeed: 1374716823, fuzzRuns: 100, filter: null };
78
+ const flags = { initialSeed: 955237713, fuzzRuns: 100, filter: null };
79
79
  const app = Elm.Runner.init({ flags: flags });
80
80
 
81
81
  // Record the timing at which we received the last "runTest" message
@@ -82,7 +82,7 @@ const verbosity = 0;
82
82
  // Create a long lived reporter worker
83
83
  const { Elm } = require("./Reporter.elm.js");
84
84
  const flags = {
85
- initialSeed: 1374716823,
85
+ initialSeed: 955237713,
86
86
  fuzzRuns: 100,
87
87
  mode: "consoleNoColor",
88
88
  verbosity: verbosity,
@@ -16,8 +16,6 @@ process.on("unhandledRejection", (error) => {
16
16
  console.error(error);
17
17
  });
18
18
  let foundErrors;
19
- let pendingBackendTaskResponses = new Map();
20
- let pendingBackendTaskCount;
21
19
 
22
20
  module.exports = { render, runGenerator };
23
21
 
@@ -44,8 +42,6 @@ async function render(
44
42
  const { fs, resetInMemoryFs } = require("./request-cache-fs.js")(hasFsAccess);
45
43
  resetInMemoryFs();
46
44
  foundErrors = false;
47
- pendingBackendTaskResponses = new Map();
48
- pendingBackendTaskCount = 0;
49
45
  // since init/update are never called in pre-renders, and BackendTask.Http is called using pure NodeJS HTTP fetching
50
46
  // we can provide a fake HTTP instead of xhr2 (which is otherwise needed for Elm HTTP requests from Node)
51
47
  XMLHttpRequest = {};
@@ -74,8 +70,6 @@ async function runGenerator(cliOptions, portsFile, elmModule) {
74
70
  const { fs, resetInMemoryFs } = require("./request-cache-fs.js")(true);
75
71
  resetInMemoryFs();
76
72
  foundErrors = false;
77
- pendingBackendTaskResponses = new Map();
78
- pendingBackendTaskCount = 0;
79
73
  // since init/update are never called in pre-renders, and BackendTask.Http is called using pure NodeJS HTTP fetching
80
74
  // we can provide a fake HTTP instead of xhr2 (which is otherwise needed for Elm HTTP requests from Node)
81
75
  XMLHttpRequest = {};
@@ -166,33 +160,39 @@ function runGeneratorAppHelp(
166
160
  );
167
161
  }
168
162
  } else if (fromElm.tag === "DoHttp") {
169
- const requestHash = fromElm.args[0];
170
- const requestToPerform = fromElm.args[1];
171
- if (
172
- requestToPerform.url !== "elm-pages-internal://port" &&
173
- requestToPerform.url.startsWith("elm-pages-internal://")
174
- ) {
175
- runInternalJob(
176
- requestHash,
177
- app,
178
- mode,
179
- requestToPerform,
180
- fs,
181
- hasFsAccess,
182
- patternsToWatch
183
- );
184
- } else {
185
- runHttpJob(
186
- requestHash,
187
- portsFile,
188
- app,
189
- mode,
190
- requestToPerform,
191
- fs,
192
- hasFsAccess,
193
- fromElm.args[1]
194
- );
195
- }
163
+ app.ports.gotBatchSub.send(
164
+ Object.fromEntries(
165
+ await Promise.all(
166
+ fromElm.args[0].map(([requestHash, requestToPerform]) => {
167
+ if (
168
+ requestToPerform.url !== "elm-pages-internal://port" &&
169
+ requestToPerform.url.startsWith("elm-pages-internal://")
170
+ ) {
171
+ return runInternalJob(
172
+ requestHash,
173
+ app,
174
+ mode,
175
+ requestToPerform,
176
+ fs,
177
+ hasFsAccess,
178
+ patternsToWatch
179
+ );
180
+ } else {
181
+ return runHttpJob(
182
+ requestHash,
183
+ portsFile,
184
+ app,
185
+ mode,
186
+ requestToPerform,
187
+ fs,
188
+ hasFsAccess,
189
+ requestToPerform
190
+ );
191
+ }
192
+ })
193
+ )
194
+ )
195
+ );
196
196
  } else if (fromElm.tag === "Errors") {
197
197
  foundErrors = true;
198
198
  reject(fromElm.args[0].errorsJson);
@@ -309,33 +309,39 @@ function runElmApp(
309
309
  );
310
310
  }
311
311
  } else if (fromElm.tag === "DoHttp") {
312
- const requestHash = fromElm.args[0];
313
- const requestToPerform = fromElm.args[1];
314
- if (
315
- requestToPerform.url !== "elm-pages-internal://port" &&
316
- requestToPerform.url.startsWith("elm-pages-internal://")
317
- ) {
318
- runInternalJob(
319
- requestHash,
320
- app,
321
- mode,
322
- requestToPerform,
323
- fs,
324
- hasFsAccess,
325
- patternsToWatch
326
- );
327
- } else {
328
- runHttpJob(
329
- requestHash,
330
- portsFile,
331
- app,
332
- mode,
333
- requestToPerform,
334
- fs,
335
- hasFsAccess,
336
- fromElm.args[1]
337
- );
338
- }
312
+ app.ports.gotBatchSub.send(
313
+ Object.fromEntries(
314
+ await Promise.all(
315
+ fromElm.args[0].map(([requestHash, requestToPerform]) => {
316
+ if (
317
+ requestToPerform.url !== "elm-pages-internal://port" &&
318
+ requestToPerform.url.startsWith("elm-pages-internal://")
319
+ ) {
320
+ return runInternalJob(
321
+ requestHash,
322
+ app,
323
+ mode,
324
+ requestToPerform,
325
+ fs,
326
+ hasFsAccess,
327
+ patternsToWatch
328
+ );
329
+ } else {
330
+ return runHttpJob(
331
+ requestHash,
332
+ portsFile,
333
+ app,
334
+ mode,
335
+ requestToPerform,
336
+ fs,
337
+ hasFsAccess,
338
+ requestToPerform
339
+ );
340
+ }
341
+ })
342
+ )
343
+ )
344
+ );
339
345
  } else if (fromElm.tag === "Errors") {
340
346
  foundErrors = true;
341
347
  reject(fromElm.args[0].errorsJson);
@@ -404,7 +410,6 @@ async function runHttpJob(
404
410
  hasFsAccess,
405
411
  useCache
406
412
  ) {
407
- pendingBackendTaskCount += 1;
408
413
  try {
409
414
  const lookupResponse = await lookupOrPerform(
410
415
  portsFile,
@@ -416,25 +421,29 @@ async function runHttpJob(
416
421
 
417
422
  if (lookupResponse.kind === "cache-response-path") {
418
423
  const responseFilePath = lookupResponse.value;
419
- pendingBackendTaskResponses.set(requestHash, {
420
- request: requestToPerform,
421
- response: JSON.parse(
422
- (await fs.promises.readFile(responseFilePath, "utf8")).toString()
423
- ),
424
- });
424
+ return [
425
+ requestHash,
426
+ {
427
+ request: requestToPerform,
428
+ response: JSON.parse(
429
+ (await fs.promises.readFile(responseFilePath, "utf8")).toString()
430
+ ),
431
+ },
432
+ ];
425
433
  } else if (lookupResponse.kind === "response-json") {
426
- pendingBackendTaskResponses.set(requestHash, {
427
- request: requestToPerform,
428
- response: lookupResponse.value,
429
- });
434
+ return [
435
+ requestHash,
436
+ {
437
+ request: requestToPerform,
438
+ response: lookupResponse.value,
439
+ },
440
+ ];
430
441
  } else {
431
442
  throw `Unexpected kind ${lookupResponse}`;
432
443
  }
433
444
  } catch (error) {
434
- sendError(app, error);
435
- } finally {
436
- pendingBackendTaskCount -= 1;
437
- flushIfDone(app);
445
+ console.log("@@@ERROR", error);
446
+ // sendError(app, error);
438
447
  }
439
448
  }
440
449
 
@@ -461,43 +470,29 @@ async function runInternalJob(
461
470
  patternsToWatch
462
471
  ) {
463
472
  try {
464
- pendingBackendTaskCount += 1;
465
-
466
473
  if (requestToPerform.url === "elm-pages-internal://log") {
467
- pendingBackendTaskResponses.set(
468
- requestHash,
469
- await runLogJob(requestToPerform)
470
- );
474
+ return [requestHash, await runLogJob(requestToPerform)];
471
475
  } else if (requestToPerform.url === "elm-pages-internal://read-file") {
472
- pendingBackendTaskResponses.set(
476
+ return [
473
477
  requestHash,
474
- await readFileJobNew(requestToPerform, patternsToWatch)
475
- );
478
+ await readFileJobNew(requestToPerform, patternsToWatch),
479
+ ];
476
480
  } else if (requestToPerform.url === "elm-pages-internal://glob") {
477
- pendingBackendTaskResponses.set(
478
- requestHash,
479
- await runGlobNew(requestToPerform, patternsToWatch)
480
- );
481
+ return [requestHash, await runGlobNew(requestToPerform, patternsToWatch)];
481
482
  } else if (requestToPerform.url === "elm-pages-internal://env") {
482
- pendingBackendTaskResponses.set(
483
- requestHash,
484
- await runEnvJob(requestToPerform, patternsToWatch)
485
- );
483
+ return [requestHash, await runEnvJob(requestToPerform, patternsToWatch)];
486
484
  } else if (requestToPerform.url === "elm-pages-internal://encrypt") {
487
- pendingBackendTaskResponses.set(
485
+ return [
488
486
  requestHash,
489
- await runEncryptJob(requestToPerform, patternsToWatch)
490
- );
487
+ await runEncryptJob(requestToPerform, patternsToWatch),
488
+ ];
491
489
  } else if (requestToPerform.url === "elm-pages-internal://decrypt") {
492
- pendingBackendTaskResponses.set(
490
+ return [
493
491
  requestHash,
494
- await runDecryptJob(requestToPerform, patternsToWatch)
495
- );
492
+ await runDecryptJob(requestToPerform, patternsToWatch),
493
+ ];
496
494
  } else if (requestToPerform.url === "elm-pages-internal://write-file") {
497
- pendingBackendTaskResponses.set(
498
- requestHash,
499
- await runWriteFileJob(requestToPerform)
500
- );
495
+ return [requestHash, await runWriteFileJob(requestToPerform)];
501
496
  } else {
502
497
  throw `Unexpected internal BackendTask request format: ${kleur.yellow(
503
498
  JSON.stringify(2, null, requestToPerform)
@@ -505,9 +500,6 @@ async function runInternalJob(
505
500
  }
506
501
  } catch (error) {
507
502
  sendError(app, error);
508
- } finally {
509
- pendingBackendTaskCount -= 1;
510
- flushIfDone(app);
511
503
  }
512
504
  }
513
505
 
@@ -628,27 +620,6 @@ async function runDecryptJob(req, patternsToWatch) {
628
620
  }
629
621
  }
630
622
 
631
- function flushIfDone(app) {
632
- if (foundErrors) {
633
- pendingBackendTaskResponses = new Map();
634
- } else if (pendingBackendTaskCount === 0) {
635
- // console.log(
636
- // `Flushing ${pendingBackendTaskResponses.length} items in ${timeUntilThreshold}ms`
637
- // );
638
-
639
- flushQueue(app);
640
- }
641
- }
642
-
643
- function flushQueue(app) {
644
- // TODO - could the case where flush is called with size 0 be avoided on the Elm side?
645
- // if (pendingBackendTaskResponses.size > 0) {
646
- // console.log("@@@ FLUSHING", pendingBackendTaskResponses.size);
647
- app.ports.gotBatchSub.send(Object.fromEntries(pendingBackendTaskResponses));
648
- pendingBackendTaskResponses = new Map();
649
- // }
650
- }
651
-
652
623
  /**
653
624
  * @param {{ ports: { fromJsPort: { send: (arg0: { tag: string; data: any; }) => void; }; }; }} app
654
625
  * @param {{ message: string; title: string; }} error
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "elm-pages",
3
- "version": "3.0.0-beta.17",
3
+ "version": "3.0.0-beta.18",
4
4
  "homepage": "https://elm-pages.com",
5
5
  "moduleResolution": "node",
6
6
  "description": "Type-safe static sites, written in pure elm with your own custom elm-markup syntax.",
@@ -59,7 +59,7 @@ ${Object.keys(process.env).join("\n")}
59
59
 
60
60
  ## Performance
61
61
 
62
- As with any JavaScript or NodeJS code, avoid doing blocking IO operations. For example, avoid using `fs.readFileSync`, because blocking IO can slow down your elm-pages builds and dev server. `elm-pages` performances all `BackendTask`'s in parallel whenever possible.
62
+ As with any JavaScript or NodeJS code, avoid doing blocking IO operations. For example, avoid using `fs.readFileSync`, because blocking IO can slow down your elm-pages builds and dev server. `elm-pages` performs all `BackendTask`'s in parallel whenever possible.
63
63
  So if you do `BackendTask.map2 Tuple.pair myHttpBackendTask myCustomBackendTask`, it will resolve those two in parallel. NodeJS performs best when you take advantage of its ability to do non-blocking I/O (file reads, HTTP requests, etc.). If you use `BackendTask.andThen`,
64
64
  it will need to resolve them in sequence rather than in parallel, but it's still best to avoid blocking IO operations in your Custom BackendTask definitions.
65
65
 
@@ -229,7 +229,7 @@ import BackendTask exposing (BackendTask)
229
229
  import BackendTask.Http
230
230
  import BackendTask.Internal.Glob exposing (Glob(..))
231
231
  import BackendTask.Internal.Request
232
- import FatalError exposing (FatalError, Recoverable)
232
+ import FatalError exposing (FatalError)
233
233
  import Json.Decode as Decode
234
234
  import Json.Encode as Encode
235
235
  import List.Extra
@@ -1054,7 +1054,9 @@ so it's ideal to make this kind of assertion rather than having fallback behavio
1054
1054
  issues (like if we had instead ignored the case where there are two or more matching blog post files).
1055
1055
 
1056
1056
  -}
1057
- expectUniqueMatch : Glob a -> BackendTask (Recoverable String) a
1057
+ expectUniqueMatch :
1058
+ Glob a
1059
+ -> BackendTask { fatal : FatalError, recoverable : String } a
1058
1060
  expectUniqueMatch glob =
1059
1061
  glob
1060
1062
  |> toBackendTask
@@ -104,7 +104,7 @@ import Base64
104
104
  import Bytes exposing (Bytes)
105
105
  import Bytes.Decode
106
106
  import Dict exposing (Dict)
107
- import FatalError exposing (FatalError, Recoverable)
107
+ import FatalError exposing (FatalError)
108
108
  import Json.Decode
109
109
  import Json.Encode as Encode
110
110
  import Pages.Internal.StaticHttpBody as Body
@@ -170,7 +170,7 @@ type alias Body =
170
170
  getJson :
171
171
  String
172
172
  -> Json.Decode.Decoder a
173
- -> BackendTask (Recoverable Error) a
173
+ -> BackendTask { fatal : FatalError, recoverable : Error } a
174
174
  getJson url decoder =
175
175
  getWithOptions
176
176
  { url = url
@@ -231,7 +231,7 @@ getWithOptions :
231
231
  , timeoutInMs : Maybe Int
232
232
  , cachePath : Maybe String
233
233
  }
234
- -> BackendTask (Recoverable Error) a
234
+ -> BackendTask { fatal : FatalError, recoverable : Error } a
235
235
  getWithOptions request__ =
236
236
  let
237
237
  request_ : HashRequest.Request
@@ -258,7 +258,7 @@ post :
258
258
  String
259
259
  -> Body
260
260
  -> Expect a
261
- -> BackendTask (Recoverable Error) a
261
+ -> BackendTask { fatal : FatalError, recoverable : Error } a
262
262
  post url body expect =
263
263
  request
264
264
  { url = url
@@ -397,7 +397,7 @@ request :
397
397
  , timeoutInMs : Maybe Int
398
398
  }
399
399
  -> Expect a
400
- -> BackendTask (Recoverable Error) a
400
+ -> BackendTask { fatal : FatalError, recoverable : Error } a
401
401
  request request__ expect =
402
402
  let
403
403
  request_ : HashRequest.Request
@@ -475,7 +475,7 @@ with this as a low-level detail, or you can use functions like [BackendTask.Http
475
475
  requestRaw :
476
476
  HashRequest.Request
477
477
  -> Expect a
478
- -> BackendTask (Recoverable Error) a
478
+ -> BackendTask { fatal : FatalError, recoverable : Error } a
479
479
  requestRaw request__ expect =
480
480
  let
481
481
  request_ : HashRequest.Request
@@ -1,7 +1,4 @@
1
- module FatalError exposing
2
- ( FatalError, fromString, recoverable
3
- , Recoverable
4
- )
1
+ module FatalError exposing (FatalError, fromString, recoverable)
5
2
 
6
3
  {-| The Elm language doesn't have the concept of exceptions or special control flow for errors. It just has
7
4
  Custom Types, and by convention types like `Result` and the `Err` variant are used to represent possible failure states
@@ -59,20 +56,11 @@ when these errors occur.
59
56
 
60
57
  @docs FatalError, fromString, recoverable
61
58
 
62
- @docs Recoverable
63
-
64
59
  -}
65
60
 
66
61
  import Pages.Internal.FatalError
67
62
 
68
63
 
69
- {-| -}
70
- type alias Recoverable error =
71
- { fatal : FatalError
72
- , recoverable : error
73
- }
74
-
75
-
76
64
  {-| -}
77
65
  type alias FatalError =
78
66
  Pages.Internal.FatalError.FatalError
@@ -94,7 +82,7 @@ fromString string =
94
82
 
95
83
 
96
84
  {-| -}
97
- recoverable : { title : String, body : String } -> value -> Recoverable value
85
+ recoverable : { title : String, body : String } -> error -> { fatal : FatalError, recoverable : error }
98
86
  recoverable info value =
99
87
  { fatal = build info
100
88
  , recoverable = value
@@ -209,8 +209,13 @@ perform site renderRequest config effect =
209
209
  Effect.Batch list ->
210
210
  flatten site renderRequest config list
211
211
 
212
- Effect.FetchHttp unmasked ->
213
- ToJsPayload.DoHttp (Pages.StaticHttp.Request.hash unmasked) unmasked
212
+ Effect.FetchHttp requests ->
213
+ requests
214
+ |> List.map
215
+ (\request ->
216
+ ( Pages.StaticHttp.Request.hash request, request )
217
+ )
218
+ |> ToJsPayload.DoHttp
214
219
  |> Codec.encoder (ToJsPayload.successCodecNew2 canonicalSiteUrl "")
215
220
  |> config.toJsPort
216
221
  |> Cmd.map never
@@ -817,10 +822,7 @@ nextStepToEffect model nextStep =
817
822
  ( { model
818
823
  | staticResponses = updatedStaticResponsesModel
819
824
  }
820
- , (httpRequests
821
- |> List.map Effect.FetchHttp
822
- )
823
- |> Effect.Batch
825
+ , Effect.FetchHttp httpRequests
824
826
  )
825
827
 
826
828
  StaticResponses.FinishedWithErrors errors ->
@@ -7,7 +7,7 @@ import Pages.StaticHttp.Request as StaticHttp
7
7
 
8
8
  type Effect
9
9
  = NoEffect
10
- | FetchHttp StaticHttp.Request
10
+ | FetchHttp (List StaticHttp.Request)
11
11
  | Batch (List Effect)
12
12
  | SendSinglePage ToJsSuccessPayloadNewCombined
13
13
  | SendSinglePageNew Bytes ToJsSuccessPayloadNewCombined
@@ -183,8 +183,13 @@ perform config effect =
183
183
  Effect.Batch list ->
184
184
  flatten config list
185
185
 
186
- Effect.FetchHttp unmasked ->
187
- ToJsPayload.DoHttp (Pages.StaticHttp.Request.hash unmasked) unmasked
186
+ Effect.FetchHttp requests ->
187
+ requests
188
+ |> List.map
189
+ (\request ->
190
+ ( Pages.StaticHttp.Request.hash request, request )
191
+ )
192
+ |> ToJsPayload.DoHttp
188
193
  |> Codec.encoder (ToJsPayload.successCodecNew2 canonicalSiteUrl "")
189
194
  |> config.toJsPort
190
195
  |> Cmd.map never
@@ -349,10 +354,7 @@ nextStepToEffect model nextStep =
349
354
  ( { model
350
355
  | staticResponses = updatedStaticResponsesModel
351
356
  }
352
- , (httpRequests
353
- |> List.map Effect.FetchHttp
354
- )
355
- |> Effect.Batch
357
+ , Effect.FetchHttp httpRequests
356
358
  )
357
359
 
358
360
  StaticResponses.Finish () ->
@@ -89,7 +89,7 @@ headCodec canonicalSiteUrl currentPagePath =
89
89
  type ToJsSuccessPayloadNewCombined
90
90
  = PageProgress ToJsSuccessPayloadNew
91
91
  | SendApiResponse { body : Json.Encode.Value, staticHttpCache : Dict String String, statusCode : Int }
92
- | DoHttp String Pages.StaticHttp.Request.Request
92
+ | DoHttp (List ( String, Pages.StaticHttp.Request.Request ))
93
93
  | Port String
94
94
  | Errors (List BuildError)
95
95
  | ApiResponse
@@ -109,8 +109,8 @@ successCodecNew2 canonicalSiteUrl currentPagePath =
109
109
  PageProgress payload ->
110
110
  success payload
111
111
 
112
- DoHttp hash requestUrl ->
113
- vDoHttp hash requestUrl
112
+ DoHttp hashRequestPairs ->
113
+ vDoHttp hashRequestPairs
114
114
 
115
115
  SendApiResponse record ->
116
116
  vSendApiResponse record
@@ -121,10 +121,7 @@ successCodecNew2 canonicalSiteUrl currentPagePath =
121
121
  |> Codec.variant1 "Errors" Errors errorCodec
122
122
  |> Codec.variant0 "ApiResponse" ApiResponse
123
123
  |> Codec.variant1 "PageProgress" PageProgress (successCodecNew canonicalSiteUrl currentPagePath)
124
- |> Codec.variant2 "DoHttp"
125
- DoHttp
126
- Codec.string
127
- Pages.StaticHttp.Request.codec
124
+ |> Codec.variant1 "DoHttp" DoHttp (Codec.list (Codec.tuple Codec.string Pages.StaticHttp.Request.codec))
128
125
  |> Codec.variant1 "ApiResponse"
129
126
  SendApiResponse
130
127
  (Codec.object (\body staticHttpCache statusCode -> { body = body, staticHttpCache = staticHttpCache, statusCode = statusCode })
@@ -34,7 +34,7 @@ import BackendTask.Http
34
34
  import BackendTask.Internal.Request
35
35
  import Cli.OptionsParser as OptionsParser
36
36
  import Cli.Program as Program
37
- import FatalError exposing (FatalError, Recoverable)
37
+ import FatalError exposing (FatalError)
38
38
  import Json.Decode as Decode
39
39
  import Json.Encode as Encode
40
40
  import Pages.Internal.Script
@@ -52,7 +52,7 @@ type Error
52
52
 
53
53
 
54
54
  {-| -}
55
- writeFile : { path : String, body : String } -> BackendTask (Recoverable Error) ()
55
+ writeFile : { path : String, body : String } -> BackendTask { fatal : FatalError, recoverable : Error } ()
56
56
  writeFile { path, body } =
57
57
  BackendTask.Internal.Request.request
58
58
  { name = "write-file"