@effect-app/vue 2.52.4 → 2.52.5

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 (49) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/experimental/commander.d.ts +120 -0
  3. package/dist/experimental/commander.d.ts.map +1 -0
  4. package/dist/experimental/commander.js +206 -0
  5. package/dist/experimental/confirm.d.ts +12 -0
  6. package/dist/experimental/confirm.d.ts.map +1 -0
  7. package/dist/experimental/confirm.js +14 -0
  8. package/dist/experimental/intl.d.ts +32 -0
  9. package/dist/experimental/intl.d.ts.map +1 -0
  10. package/dist/experimental/intl.js +4 -0
  11. package/dist/experimental/makeExperimental.d.ts +59 -26
  12. package/dist/experimental/makeExperimental.d.ts.map +1 -1
  13. package/dist/experimental/makeExperimental.js +8 -16
  14. package/dist/experimental/toast.d.ts +54 -0
  15. package/dist/experimental/toast.d.ts.map +1 -0
  16. package/dist/experimental/toast.js +4 -0
  17. package/dist/experimental/withToast.d.ts +17 -0
  18. package/dist/experimental/withToast.d.ts.map +1 -0
  19. package/dist/experimental/withToast.js +36 -0
  20. package/dist/makeClient.js +6 -6
  21. package/dist/makeIntl.d.ts +1 -1
  22. package/dist/makeIntl.d.ts.map +1 -1
  23. package/dist/makeIntl.js +9 -4
  24. package/package.json +19 -11
  25. package/src/experimental/commander.ts +567 -0
  26. package/src/experimental/confirm.ts +20 -0
  27. package/src/experimental/intl.ts +5 -0
  28. package/src/experimental/makeExperimental.ts +7 -25
  29. package/src/experimental/toast.ts +14 -0
  30. package/src/experimental/withToast.ts +58 -0
  31. package/src/makeClient.ts +5 -5
  32. package/src/makeIntl.ts +8 -3
  33. package/test/Mutation.test.ts +21 -41
  34. package/test/dist/stubs.d.ts +58 -21
  35. package/test/dist/stubs.d.ts.map +1 -1
  36. package/test/dist/stubs.js +28 -22
  37. package/test/stubs.ts +65 -61
  38. package/dist/experimental/useCommand.d.ts +0 -75
  39. package/dist/experimental/useCommand.d.ts.map +0 -1
  40. package/dist/experimental/useCommand.js +0 -198
  41. package/dist/experimental/useConfirm.d.ts +0 -7
  42. package/dist/experimental/useConfirm.d.ts.map +0 -1
  43. package/dist/experimental/useConfirm.js +0 -9
  44. package/dist/experimental/useWithToast.d.ts +0 -23
  45. package/dist/experimental/useWithToast.d.ts.map +0 -1
  46. package/dist/experimental/useWithToast.js +0 -31
  47. package/src/experimental/useCommand.ts +0 -564
  48. package/src/experimental/useConfirm.ts +0 -17
  49. package/src/experimental/useWithToast.ts +0 -65
@@ -0,0 +1,567 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import { asResult, reportRuntimeError } from "@effect-app/vue"
3
+ import { reportMessage } from "@effect-app/vue/errorReporter"
4
+ import { type Result } from "@effect-atom/atom/Result"
5
+ import { Cause, Context, Effect, type Exit, flow, Match, Option, Runtime, S } from "effect-app"
6
+ import { SupportedErrors } from "effect-app/client"
7
+ import { OperationFailure, OperationSuccess } from "effect-app/Operations"
8
+ import { type RuntimeFiber } from "effect/Fiber"
9
+ import { type NoInfer } from "effect/Types"
10
+ import { type YieldWrap } from "effect/Utils"
11
+ import { computed, type ComputedRef } from "vue"
12
+ import { ConfirmSvc } from "./confirm.js"
13
+ import { IntlSvc } from "./intl.js"
14
+ import { WithToastSvc } from "./withToast.js"
15
+
16
+ export const DefaultIntl = {
17
+ de: {
18
+ "handle.confirmation": "{action} bestätigen?",
19
+ "handle.waiting": "{action} wird ausgeführt...",
20
+ "handle.success": "{action} erfolgreich",
21
+ "handle.with_errors": "{action} fehlgeschlagen",
22
+ "handle.with_warnings": "{action} erfolgreich, mit Warnungen",
23
+ "handle.error_response":
24
+ "Die Anfrage war nicht erfolgreich:\n{error}\nWir wurden benachrichtigt und werden das Problem in Kürze beheben.",
25
+ "handle.response_error": "Die Antwort konnte nicht verarbeitet werden:\n{error}",
26
+ "handle.request_error": "Die Anfrage konnte nicht gesendet werden:\n{error}",
27
+ "handle.unexpected_error2": "{action} unerwarteter Fehler, probieren sie es in kurze nochmals."
28
+ },
29
+ en: {
30
+ "handle.confirmation": "Confirm {action}?",
31
+ "handle.waiting": "{action} executing...",
32
+ "handle.success": "{action} Success",
33
+ "handle.with_errors": "{action} Failed",
34
+ "handle.with_warnings": "{action}, with warnings",
35
+ "handle.error_response":
36
+ "There was an error in processing the response:\n{error}\nWe have been notified and will fix the problem shortly.",
37
+ "handle.request_error": "There was an error in the request:\n{error}",
38
+ "handle.response_error": "The request was not successful:\n{error}",
39
+ "handle.unexpected_error2": "{action} unexpected error, please try again shortly."
40
+ }
41
+ }
42
+
43
+ export class CommandContext extends Context.Tag("CommandContext")<
44
+ CommandContext,
45
+ { action: string }
46
+ >() {}
47
+
48
+ export interface CommandProps<A, E> {
49
+ action: string
50
+ result: Result<A, E>
51
+ waiting: boolean
52
+ }
53
+ // @effect-diagnostics-next-line missingEffectServiceDependency:off
54
+ export class Commander extends Effect.Service<Commander>()("Commander", {
55
+ dependencies: [WithToastSvc.Default, ConfirmSvc.Default],
56
+ effect: Effect.gen(function*() {
57
+ const { intl } = yield* IntlSvc
58
+ const withToast = yield* WithToastSvc
59
+ const { confirmOrInterrupt } = yield* ConfirmSvc
60
+
61
+ type CommandOut<Args extends Array<any>, A, E> = ComputedRef<
62
+ ((...a: Args) => RuntimeFiber<Exit.Exit<A, E>, never>) & {
63
+ action: string
64
+ result: Result<A, E>
65
+ waiting: boolean
66
+ }
67
+ >
68
+
69
+ type CommandOutHelper<Args extends Array<any>, Eff extends Effect.Effect<any, any, any>> = CommandOut<
70
+ Args,
71
+ Effect.Effect.Success<Eff>,
72
+ Effect.Effect.Error<Eff>
73
+ >
74
+
75
+ type Gen<RT> = {
76
+ <Eff extends YieldWrap<Effect.Effect<any, any, RT | CommandContext>>, AEff, Args extends Array<any>>(
77
+ body: (...args: Args) => Generator<Eff, AEff, never>
78
+ ): CommandOut<
79
+ Args,
80
+ AEff,
81
+ [Eff] extends [never] ? never
82
+ : [Eff] extends [YieldWrap<Effect.Effect<infer _A, infer E, infer _R>>] ? E
83
+ : never
84
+ >
85
+ <
86
+ Eff extends YieldWrap<Effect.Effect<any, any, any>>,
87
+ AEff,
88
+ Args extends Array<any>,
89
+ A extends Effect.Effect<any, any, RT | CommandContext>
90
+ >(
91
+ body: (...args: Args) => Generator<Eff, AEff, never>,
92
+ a: (
93
+ _: Effect.Effect<
94
+ AEff,
95
+ [Eff] extends [never] ? never
96
+ : [Eff] extends [YieldWrap<Effect.Effect<infer _A, infer E, infer _R>>] ? E
97
+ : never,
98
+ [Eff] extends [never] ? never
99
+ : [Eff] extends [YieldWrap<Effect.Effect<infer _A, infer _E, infer R>>] ? R
100
+ : never
101
+ >,
102
+ ...args: NoInfer<Args>
103
+ ) => A
104
+ ): CommandOutHelper<Args, A>
105
+ <
106
+ Eff extends YieldWrap<Effect.Effect<any, any, any>>,
107
+ AEff,
108
+ Args extends Array<any>,
109
+ A,
110
+ B extends Effect.Effect<any, any, RT | CommandContext>
111
+ >(
112
+ body: (...args: Args) => Generator<Eff, AEff, never>,
113
+ a: (
114
+ _: Effect.Effect<
115
+ AEff,
116
+ [Eff] extends [never] ? never
117
+ : [Eff] extends [YieldWrap<Effect.Effect<infer _A, infer E, infer _R>>] ? E
118
+ : never,
119
+ [Eff] extends [never] ? never
120
+ : [Eff] extends [YieldWrap<Effect.Effect<infer _A, infer _E, infer R>>] ? R
121
+ : never
122
+ >,
123
+ ...args: NoInfer<Args>
124
+ ) => A,
125
+ b: (_: A, ...args: NoInfer<Args>) => B
126
+ ): CommandOutHelper<Args, B>
127
+ <
128
+ Eff extends YieldWrap<Effect.Effect<any, any, any>>,
129
+ AEff,
130
+ Args extends Array<any>,
131
+ A,
132
+ B,
133
+ C extends Effect.Effect<any, any, RT | CommandContext>
134
+ >(
135
+ body: (...args: Args) => Generator<Eff, AEff, never>,
136
+ a: (
137
+ _: Effect.Effect<
138
+ AEff,
139
+ [Eff] extends [never] ? never
140
+ : [Eff] extends [YieldWrap<Effect.Effect<infer _A, infer E, infer _R>>] ? E
141
+ : never,
142
+ [Eff] extends [never] ? never
143
+ : [Eff] extends [YieldWrap<Effect.Effect<infer _A, infer _E, infer R>>] ? R
144
+ : never
145
+ >,
146
+ ...args: NoInfer<Args>
147
+ ) => A,
148
+ b: (_: A, ...args: NoInfer<Args>) => B,
149
+ c: (_: B, ...args: NoInfer<Args>) => C
150
+ ): CommandOutHelper<Args, C>
151
+ <
152
+ Eff extends YieldWrap<Effect.Effect<any, any, any>>,
153
+ AEff,
154
+ Args extends Array<any>,
155
+ A,
156
+ B,
157
+ C,
158
+ D extends Effect.Effect<any, any, RT | CommandContext>
159
+ >(
160
+ body: (...args: Args) => Generator<Eff, AEff, never>,
161
+ a: (
162
+ _: Effect.Effect<
163
+ AEff,
164
+ [Eff] extends [never] ? never
165
+ : [Eff] extends [YieldWrap<Effect.Effect<infer _A, infer E, infer _R>>] ? E
166
+ : never,
167
+ [Eff] extends [never] ? never
168
+ : [Eff] extends [YieldWrap<Effect.Effect<infer _A, infer _E, infer R>>] ? R
169
+ : never
170
+ >,
171
+ ...args: NoInfer<Args>
172
+ ) => A,
173
+ b: (_: A, ...args: NoInfer<Args>) => B,
174
+ c: (_: B, ...args: NoInfer<Args>) => C,
175
+ d: (_: C, ...args: NoInfer<Args>) => D
176
+ ): CommandOutHelper<Args, D>
177
+ <
178
+ Eff extends YieldWrap<Effect.Effect<any, any, any>>,
179
+ AEff,
180
+ Args extends Array<any>,
181
+ A,
182
+ B,
183
+ C,
184
+ D,
185
+ E extends Effect.Effect<any, any, RT | CommandContext>
186
+ >(
187
+ body: (...args: Args) => Generator<Eff, AEff, never>,
188
+ a: (
189
+ _: Effect.Effect<
190
+ AEff,
191
+ [Eff] extends [never] ? never
192
+ : [Eff] extends [YieldWrap<Effect.Effect<infer _A, infer E, infer _R>>] ? E
193
+ : never,
194
+ [Eff] extends [never] ? never
195
+ : [Eff] extends [YieldWrap<Effect.Effect<infer _A, infer _E, infer R>>] ? R
196
+ : never
197
+ >,
198
+ ...args: NoInfer<Args>
199
+ ) => A,
200
+ b: (_: A, ...args: NoInfer<Args>) => B,
201
+ c: (_: B, ...args: NoInfer<Args>) => C,
202
+ d: (_: C, ...args: NoInfer<Args>) => D,
203
+ e: (_: D, ...args: NoInfer<Args>) => E
204
+ ): CommandOutHelper<Args, E>
205
+ <
206
+ Eff extends YieldWrap<Effect.Effect<any, any, any>>,
207
+ AEff,
208
+ Args extends Array<any>,
209
+ A,
210
+ B,
211
+ C,
212
+ D,
213
+ E,
214
+ F extends Effect.Effect<any, any, RT | CommandContext>
215
+ >(
216
+ body: (...args: Args) => Generator<Eff, AEff, never>,
217
+ a: (
218
+ _: Effect.Effect<
219
+ AEff,
220
+ [Eff] extends [never] ? never
221
+ : [Eff] extends [YieldWrap<Effect.Effect<infer _A, infer E, infer _R>>] ? E
222
+ : never,
223
+ [Eff] extends [never] ? never
224
+ : [Eff] extends [YieldWrap<Effect.Effect<infer _A, infer _E, infer R>>] ? R
225
+ : never
226
+ >,
227
+ ...args: NoInfer<Args>
228
+ ) => A,
229
+ b: (_: A, ...args: NoInfer<Args>) => B,
230
+ c: (_: B, ...args: NoInfer<Args>) => C,
231
+ d: (_: C, ...args: NoInfer<Args>) => D,
232
+ e: (_: D, ...args: NoInfer<Args>) => E,
233
+ f: (_: E, ...args: NoInfer<Args>) => F
234
+ ): CommandOutHelper<Args, F>
235
+ <
236
+ Eff extends YieldWrap<Effect.Effect<any, any, any>>,
237
+ AEff,
238
+ Args extends Array<any>,
239
+ A,
240
+ B,
241
+ C,
242
+ D,
243
+ E,
244
+ F,
245
+ G extends Effect.Effect<any, any, RT | CommandContext>
246
+ >(
247
+ body: (...args: Args) => Generator<Eff, AEff, never>,
248
+ a: (
249
+ _: Effect.Effect<
250
+ AEff,
251
+ [Eff] extends [never] ? never
252
+ : [Eff] extends [YieldWrap<Effect.Effect<infer _A, infer E, infer _R>>] ? E
253
+ : never,
254
+ [Eff] extends [never] ? never
255
+ : [Eff] extends [YieldWrap<Effect.Effect<infer _A, infer _E, infer R>>] ? R
256
+ : never
257
+ >,
258
+ ...args: NoInfer<Args>
259
+ ) => A,
260
+ b: (_: A, ...args: NoInfer<Args>) => B,
261
+ c: (_: B, ...args: NoInfer<Args>) => C,
262
+ d: (_: C, ...args: NoInfer<Args>) => D,
263
+ e: (_: D, ...args: NoInfer<Args>) => E,
264
+ f: (_: E, ...args: NoInfer<Args>) => F,
265
+ g: (_: F, ...args: NoInfer<Args>) => G
266
+ ): CommandOutHelper<Args, G>
267
+ <
268
+ Eff extends YieldWrap<Effect.Effect<any, any, any>>,
269
+ AEff,
270
+ Args extends Array<any>,
271
+ A,
272
+ B,
273
+ C,
274
+ D,
275
+ E,
276
+ F,
277
+ G,
278
+ H extends Effect.Effect<any, any, RT | CommandContext>
279
+ >(
280
+ body: (...args: Args) => Generator<Eff, AEff, never>,
281
+ a: (
282
+ _: Effect.Effect<
283
+ AEff,
284
+ [Eff] extends [never] ? never
285
+ : [Eff] extends [YieldWrap<Effect.Effect<infer _A, infer E, infer _R>>] ? E
286
+ : never,
287
+ [Eff] extends [never] ? never
288
+ : [Eff] extends [YieldWrap<Effect.Effect<infer _A, infer _E, infer R>>] ? R
289
+ : never
290
+ >,
291
+ ...args: NoInfer<Args>
292
+ ) => A,
293
+ b: (_: A, ...args: NoInfer<Args>) => B,
294
+ c: (_: B, ...args: NoInfer<Args>) => C,
295
+ d: (_: C, ...args: NoInfer<Args>) => D,
296
+ e: (_: D, ...args: NoInfer<Args>) => E,
297
+ f: (_: E, ...args: NoInfer<Args>) => F,
298
+ g: (_: F, ...args: NoInfer<Args>) => G,
299
+ h: (_: G, ...args: NoInfer<Args>) => H
300
+ ): CommandOutHelper<Args, H>
301
+ <
302
+ Eff extends YieldWrap<Effect.Effect<any, any, any>>,
303
+ AEff,
304
+ Args extends Array<any>,
305
+ A,
306
+ B,
307
+ C,
308
+ D,
309
+ E,
310
+ F,
311
+ G,
312
+ H,
313
+ I extends Effect.Effect<any, any, RT | CommandContext>
314
+ >(
315
+ body: (...args: Args) => Generator<Eff, AEff, never>,
316
+ a: (
317
+ _: Effect.Effect<
318
+ AEff,
319
+ [Eff] extends [never] ? never
320
+ : [Eff] extends [YieldWrap<Effect.Effect<infer _A, infer E, infer _R>>] ? E
321
+ : never,
322
+ [Eff] extends [never] ? never
323
+ : [Eff] extends [YieldWrap<Effect.Effect<infer _A, infer _E, infer R>>] ? R
324
+ : never
325
+ >,
326
+ ...args: NoInfer<Args>
327
+ ) => A,
328
+ b: (_: A, ...args: NoInfer<Args>) => B,
329
+ c: (_: B, ...args: NoInfer<Args>) => C,
330
+ d: (_: C, ...args: NoInfer<Args>) => D,
331
+ e: (_: D, ...args: NoInfer<Args>) => E,
332
+ f: (_: E, ...args: NoInfer<Args>) => F,
333
+ g: (_: F, ...args: NoInfer<Args>) => G,
334
+ h: (_: G, ...args: NoInfer<Args>) => H,
335
+ i: (_: H, ...args: NoInfer<Args>) => I
336
+ ): CommandOutHelper<Args, I>
337
+ }
338
+
339
+ const makeCommand =
340
+ <RT>(runtime: Runtime.Runtime<RT>) =>
341
+ (actionName: string, errorDef?: Error) =>
342
+ <Args extends ReadonlyArray<any>, A, E, R extends RT | CommandContext>(
343
+ handler: (...args: Args) => Effect.Effect<A, E, R>
344
+ ) => {
345
+ const limit = Error.stackTraceLimit
346
+ Error.stackTraceLimit = 2
347
+ const localErrorDef = new Error()
348
+ Error.stackTraceLimit = limit
349
+ if (!errorDef) {
350
+ errorDef = localErrorDef
351
+ }
352
+ const action = intl.formatMessage({
353
+ id: `action.${actionName}`,
354
+ defaultMessage: actionName
355
+ })
356
+ const context = { action }
357
+
358
+ const errorReporter = <A, E, R>(self: Effect.Effect<A, E, R>) =>
359
+ self.pipe(
360
+ Effect.tapErrorCause(
361
+ Effect.fnUntraced(function*(cause) {
362
+ if (Cause.isInterruptedOnly(cause)) {
363
+ console.info(`Interrupted while trying to ${actionName}`)
364
+ return
365
+ }
366
+
367
+ const fail = Cause.failureOption(cause)
368
+ if (Option.isSome(fail)) {
369
+ // if (fail.value._tag === "SuppressErrors") {
370
+ // console.info(
371
+ // `Suppressed error trying to ${action}`,
372
+ // fail.value,
373
+ // )
374
+ // return
375
+ // }
376
+ const message = `Failure trying to ${actionName}`
377
+ yield* reportMessage(message, {
378
+ action: actionName,
379
+ error: fail.value
380
+ })
381
+ return
382
+ }
383
+
384
+ const extra = {
385
+ action,
386
+ message: `Unexpected Error trying to ${actionName}`
387
+ }
388
+ yield* reportRuntimeError(cause, extra)
389
+ })
390
+ )
391
+ )
392
+
393
+ const theHandler = flow(
394
+ handler,
395
+ // all must be within the Effect.fn to fit within the Span
396
+ Effect.provideService(CommandContext, context),
397
+ (_) => Effect.annotateCurrentSpan({ action }).pipe(Effect.zipRight(_)),
398
+ errorReporter
399
+ )
400
+
401
+ const [result, mut] = asResult(theHandler)
402
+
403
+ return computed(() =>
404
+ Object.assign(
405
+ (...args: Args) => {
406
+ const limit = Error.stackTraceLimit
407
+ Error.stackTraceLimit = 2
408
+ const errorCall = new Error()
409
+ Error.stackTraceLimit = limit
410
+
411
+ let cache: false | string = false
412
+ const captureStackTrace = () => {
413
+ if (cache !== false) {
414
+ return cache
415
+ }
416
+ if (errorCall.stack) {
417
+ const stackDef = errorDef!.stack!.trim().split("\n")
418
+ const stackCall = errorCall.stack.trim().split("\n")
419
+ let endStackDef = stackDef.slice(2).join("\n").trim()
420
+ if (!endStackDef.includes(`(`)) {
421
+ endStackDef = endStackDef.replace(/at (.*)/, "at ($1)")
422
+ }
423
+ let endStackCall = stackCall.slice(2).join("\n").trim()
424
+ if (!endStackCall.includes(`(`)) {
425
+ endStackCall = endStackCall.replace(/at (.*)/, "at ($1)")
426
+ }
427
+ cache = `${endStackDef}\n${endStackCall}`
428
+ return cache
429
+ }
430
+ }
431
+ return Runtime.runFork(runtime)(Effect.withSpan(mut(...args), actionName, { captureStackTrace }))
432
+ }, /* make sure always create a new one, or the state won't properly propagate */
433
+ {
434
+ action,
435
+ result: result.value,
436
+ waiting: result.value.waiting
437
+ } as CommandProps<A, E>
438
+ )
439
+ )
440
+ }
441
+
442
+ return {
443
+ /** Version of confirmOrInterrupt that automatically includes the action name in the default messages */
444
+ confirmOrInterrupt: Effect.fnUntraced(function*(
445
+ message: string | undefined = undefined
446
+ ) {
447
+ const context = yield* CommandContext
448
+ yield* confirmOrInterrupt(
449
+ message
450
+ ?? intl.formatMessage(
451
+ { id: "handle.confirmation" },
452
+ { action: context.action }
453
+ )
454
+ )
455
+ }),
456
+ /** Version of withDefaultToast that automatically includes the action name in the default messages and uses intl */
457
+ withDefaultToast: <A, E>(errorRenderer?: (e: E) => string | undefined) =>
458
+ (
459
+ self: Effect.Effect<A, E, CommandContext>
460
+ ) =>
461
+ Effect.gen(function*() {
462
+ const { action } = yield* CommandContext
463
+
464
+ const defaultWarnMessage = intl.formatMessage(
465
+ { id: "handle.with_warnings" },
466
+ { action }
467
+ )
468
+ const defaultErrorMessage = intl.formatMessage(
469
+ { id: "handle.with_errors" },
470
+ { action }
471
+ )
472
+ function renderError(e: E): string {
473
+ if (errorRenderer) {
474
+ const m = errorRenderer(e)
475
+ if (m) {
476
+ return m
477
+ }
478
+ }
479
+ if (!S.is(SupportedErrors)(e) && !S.ParseResult.isParseError(e)) {
480
+ if (typeof e === "object" && e !== null) {
481
+ if ("message" in e) {
482
+ return `${e.message}`
483
+ }
484
+ if ("_tag" in e) {
485
+ return `${e._tag}`
486
+ }
487
+ }
488
+ return ""
489
+ }
490
+ const e2: SupportedErrors | S.ParseResult.ParseError = e
491
+ return Match.value(e2).pipe(
492
+ Match.tags({
493
+ ParseError: (e) => {
494
+ console.warn(e.toString())
495
+ return intl.formatMessage({ id: "validation.failed" })
496
+ }
497
+ }),
498
+ Match.orElse((e) => `${e.message ?? e._tag ?? e}`)
499
+ )
500
+ }
501
+
502
+ return yield* self.pipe(
503
+ withToast({
504
+ onWaiting: intl.formatMessage(
505
+ { id: "handle.waiting" },
506
+ { action }
507
+ ),
508
+ onSuccess: (a) =>
509
+ intl.formatMessage({ id: "handle.success" }, { action })
510
+ + (S.is(OperationSuccess)(a) && a.message ? "\n" + a.message : ""),
511
+ onFailure: Option.match({
512
+ onNone: () =>
513
+ intl.formatMessage(
514
+ { id: "handle.unexpected_error2" },
515
+ {
516
+ action,
517
+ error: "" // TODO consider again Cause.pretty(cause), // will be reported to Sentry/Otel anyway.. and we shouldn't bother users with error dumps?
518
+ }
519
+ ),
520
+ onSome: (e) =>
521
+ S.is(OperationFailure)(e)
522
+ ? {
523
+ level: "warn",
524
+ message: defaultWarnMessage + e.message ? "\n" + e.message : ""
525
+ }
526
+ : `${defaultErrorMessage}:\n` + renderError(e)
527
+ })
528
+ })
529
+ )
530
+ }),
531
+ /**
532
+ * Define a Command
533
+ * @param actionName The internal name of the action. will be used as Span. will be used to lookup user facing name via intl. `action.${actionName}`
534
+ * @returns A function that can be called to execute the mutation, like directly in a `@click` handler. Error reporting is built-in.
535
+ * the Effects **only** have access to the `CommandContext` service, which contains the user-facing action name.
536
+ * The function also has the following properties:
537
+ * - action: The user-facing name of the action, as defined in the intl messages. Can be used e.g as Button label.
538
+ * - result: The Result of the mutation
539
+ * - waiting: Whether the mutation is currently in progress. (shorthand for .result.waiting). Can be used e.g as Button loading/disabled state.
540
+ * Reporting status to the user is recommended to use the `withDefaultToast` helper, or render the .result inline
541
+ */
542
+ fn: <RT>(runtime: Runtime.Runtime<RT>) => {
543
+ const make = makeCommand(runtime)
544
+ return (actionName: string): Gen<RT> =>
545
+ // TODO constrain/type combinators
546
+ (
547
+ fn: any,
548
+ // TODO: combinators can freely take A, E, R and change it to whatever they want, as long as the end result Requires not more than CommandContext | R
549
+ ...combinators: any[]
550
+ ): any => {
551
+ const limit = Error.stackTraceLimit
552
+ Error.stackTraceLimit = 2
553
+ const errorDef = new Error()
554
+ Error.stackTraceLimit = limit
555
+
556
+ return make(actionName, errorDef)(Effect.fnUntraced(fn, ...combinators as [any]) as any)
557
+ }
558
+ },
559
+
560
+ alt: makeCommand as <RT>(runtime: Runtime.Runtime<RT>) => (
561
+ actionName: string
562
+ ) => <Args extends ReadonlyArray<any>, A, E, R extends RT | CommandContext>(
563
+ handler: (...args: Args) => Effect.Effect<A, E, R>
564
+ ) => ComputedRef<((...a: Args) => RuntimeFiber<Exit.Exit<A, E>, never>) & CommandProps<A, E>>
565
+ }
566
+ })
567
+ }) {}
@@ -0,0 +1,20 @@
1
+ import { Effect } from "effect-app"
2
+ import { IntlSvc } from "./intl.js"
3
+
4
+ // @effect-diagnostics-next-line missingEffectServiceDependency:off
5
+ export class ConfirmSvc extends Effect.Service<ConfirmSvc>()("ConfirmSvc", {
6
+ effect: Effect.gen(function*() {
7
+ const { intl } = yield* IntlSvc
8
+
9
+ const getDefaultMessage = () => intl.formatMessage({ id: "confirm.default", defaultMessage: "Sind sie Sicher?" })
10
+
11
+ const confirm = (message = getDefaultMessage()) => Effect.sync(() => window.confirm(message))
12
+
13
+ const confirmOrInterrupt = (message = getDefaultMessage()) =>
14
+ confirm(message).pipe(
15
+ Effect.flatMap((result) => (result ? Effect.void : Effect.interrupt))
16
+ )
17
+
18
+ return { confirm, confirmOrInterrupt }
19
+ })
20
+ }) {}
@@ -0,0 +1,5 @@
1
+ import { Context } from "effect-app"
2
+ import { type MakeIntlReturn } from "../makeIntl.js"
3
+
4
+ export class IntlSvc extends Context.TagId("IntlSvc")<IntlSvc, ReturnType<MakeIntlReturn<string>["useIntl"]>>() {
5
+ }
@@ -1,27 +1,9 @@
1
- import { type Runtime } from "effect-app"
2
- import { type MakeIntlReturn } from "../makeIntl.js"
3
- import { makeUseCommand } from "./useCommand.js"
4
- import { makeUseConfirm } from "./useConfirm.js"
5
- import { makeUseWithToast, type UseToast } from "./useWithToast.js"
1
+ import { Effect } from "effect-app"
2
+ import { Commander } from "./commander.js"
6
3
 
7
- export const makeExperimental = <Locale extends string, R>(
8
- // NOTE: underscores to not collide with auto exports in nuxt apps
9
- _useIntl: MakeIntlReturn<Locale>["useIntl"],
10
- _useToast: UseToast,
11
- runtime: Runtime.Runtime<R>
12
- ) => {
13
- const _useConfirm = makeUseConfirm(_useIntl)
14
- const _useWithToast = makeUseWithToast(_useToast)
15
- const _useCommand = makeUseCommand(
16
- _useIntl,
17
- _useConfirm,
18
- _useWithToast,
19
- runtime
20
- )
4
+ export const makeExperimental = Effect.fnUntraced(function*<R = never>() {
5
+ const cmndr = yield* Commander
6
+ const runtime = yield* Effect.runtime<R>()
21
7
 
22
- return {
23
- useConfirm: _useConfirm,
24
- useCommand: _useCommand,
25
- useWithToast: _useWithToast
26
- }
27
- }
8
+ return { ...cmndr, alt: cmndr.alt(runtime), fn: cmndr.fn(runtime) }
9
+ })
@@ -0,0 +1,14 @@
1
+ import { Context } from "effect-app"
2
+
3
+ export type ToastId = string | number
4
+ export type ToastOpts = { id?: ToastId; timeout?: number }
5
+
6
+ export type UseToast = () => {
7
+ error: (message: string, options?: ToastOpts) => ToastId
8
+ warning: (message: string, options?: ToastOpts) => ToastId
9
+ success: (message: string, options?: ToastOpts) => ToastId
10
+ info: (message: string, options?: ToastOpts) => ToastId
11
+ dismiss: (id: ToastId) => void
12
+ }
13
+
14
+ export class ToastSvc extends Context.TagId("ToastSvc")<ToastSvc, ReturnType<UseToast>>() {}