@ohos-ports/rivetkit-effect 2.3.17-beta.0

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 (89) hide show
  1. package/dist/Action.d.ts +104 -0
  2. package/dist/Action.d.ts.map +1 -0
  3. package/dist/Action.js +50 -0
  4. package/dist/Action.js.map +1 -0
  5. package/dist/Actor.d.ts +132 -0
  6. package/dist/Actor.d.ts.map +1 -0
  7. package/dist/Actor.js +104 -0
  8. package/dist/Actor.js.map +1 -0
  9. package/dist/Client.d.ts +31 -0
  10. package/dist/Client.d.ts.map +1 -0
  11. package/dist/Client.js +100 -0
  12. package/dist/Client.js.map +1 -0
  13. package/dist/Registry.d.ts +134 -0
  14. package/dist/Registry.d.ts.map +1 -0
  15. package/dist/Registry.js +178 -0
  16. package/dist/Registry.js.map +1 -0
  17. package/dist/RivetError.d.ts +480 -0
  18. package/dist/RivetError.d.ts.map +1 -0
  19. package/dist/RivetError.js +985 -0
  20. package/dist/RivetError.js.map +1 -0
  21. package/dist/RivetLogger.d.ts +41 -0
  22. package/dist/RivetLogger.d.ts.map +1 -0
  23. package/dist/RivetLogger.js +41 -0
  24. package/dist/RivetLogger.js.map +1 -0
  25. package/dist/State.d.ts +159 -0
  26. package/dist/State.d.ts.map +1 -0
  27. package/dist/State.js +98 -0
  28. package/dist/State.js.map +1 -0
  29. package/dist/internal/ActionDispatcher.d.ts +14 -0
  30. package/dist/internal/ActionDispatcher.d.ts.map +1 -0
  31. package/dist/internal/ActionDispatcher.js +100 -0
  32. package/dist/internal/ActionDispatcher.js.map +1 -0
  33. package/dist/internal/ActionErrorEnvelope.d.ts +11 -0
  34. package/dist/internal/ActionErrorEnvelope.d.ts.map +1 -0
  35. package/dist/internal/ActionErrorEnvelope.js +14 -0
  36. package/dist/internal/ActionErrorEnvelope.js.map +1 -0
  37. package/dist/internal/ActorInstanceManager.d.ts +28 -0
  38. package/dist/internal/ActorInstanceManager.d.ts.map +1 -0
  39. package/dist/internal/ActorInstanceManager.js +51 -0
  40. package/dist/internal/ActorInstanceManager.js.map +1 -0
  41. package/dist/internal/ActorStateAdapter.d.ts +18 -0
  42. package/dist/internal/ActorStateAdapter.d.ts.map +1 -0
  43. package/dist/internal/ActorStateAdapter.js +24 -0
  44. package/dist/internal/ActorStateAdapter.js.map +1 -0
  45. package/dist/internal/StateOptions.d.ts +13 -0
  46. package/dist/internal/StateOptions.d.ts.map +1 -0
  47. package/dist/internal/StateOptions.js +2 -0
  48. package/dist/internal/StateOptions.js.map +1 -0
  49. package/dist/internal/logging.d.ts +22 -0
  50. package/dist/internal/logging.d.ts.map +1 -0
  51. package/dist/internal/logging.js +138 -0
  52. package/dist/internal/logging.js.map +1 -0
  53. package/dist/internal/tracing.d.ts +23 -0
  54. package/dist/internal/tracing.d.ts.map +1 -0
  55. package/dist/internal/tracing.js +30 -0
  56. package/dist/internal/tracing.js.map +1 -0
  57. package/dist/internal/utils.d.ts +7 -0
  58. package/dist/internal/utils.d.ts.map +1 -0
  59. package/dist/internal/utils.js +7 -0
  60. package/dist/internal/utils.js.map +1 -0
  61. package/dist/mod.d.ts +8 -0
  62. package/dist/mod.d.ts.map +1 -0
  63. package/dist/mod.js +8 -0
  64. package/dist/mod.js.map +1 -0
  65. package/package.json +56 -0
  66. package/src/Action.ts +231 -0
  67. package/src/Actor.test-d.ts +641 -0
  68. package/src/Actor.test.ts +209 -0
  69. package/src/Actor.ts +575 -0
  70. package/src/Client.test.ts +206 -0
  71. package/src/Client.ts +218 -0
  72. package/src/Registry.test-d.ts +126 -0
  73. package/src/Registry.test.ts +400 -0
  74. package/src/Registry.ts +304 -0
  75. package/src/RivetError.test.ts +199 -0
  76. package/src/RivetError.ts +1163 -0
  77. package/src/RivetLogger.ts +60 -0
  78. package/src/State.test.ts +340 -0
  79. package/src/State.ts +420 -0
  80. package/src/internal/ActionDispatcher.ts +189 -0
  81. package/src/internal/ActionErrorEnvelope.ts +19 -0
  82. package/src/internal/ActorInstanceManager.ts +137 -0
  83. package/src/internal/ActorStateAdapter.ts +82 -0
  84. package/src/internal/StateOptions.ts +21 -0
  85. package/src/internal/logging.test.ts +274 -0
  86. package/src/internal/logging.ts +197 -0
  87. package/src/internal/tracing.ts +42 -0
  88. package/src/internal/utils.ts +12 -0
  89. package/src/mod.ts +7 -0
@@ -0,0 +1,641 @@
1
+ import { Action, Actor, Client, type State } from "@rivetkit/effect";
2
+ import {
3
+ Context,
4
+ Effect,
5
+ type Layer,
6
+ Schema,
7
+ SchemaTransformation,
8
+ } from "effect";
9
+ import type { RawAccess } from "@ohos-ports/rivetkit/db";
10
+ import { db } from "@ohos-ports/rivetkit/db";
11
+ import { describe, expectTypeOf, it, test } from "@effect/vitest";
12
+
13
+ class SomeDep extends Context.Service<SomeDep, { readonly x: number }>()(
14
+ "SomeDep",
15
+ ) {}
16
+
17
+ const Ping = Action.make("Ping", {
18
+ success: Schema.Number,
19
+ error: Schema.String,
20
+ });
21
+
22
+ const TestActor = Actor.make("TestActor", {
23
+ actions: [Ping],
24
+ });
25
+
26
+ const TestState = {
27
+ schema: Schema.Struct({
28
+ count: Schema.Number,
29
+ }),
30
+ initialValue: () => ({ count: 0 }),
31
+ };
32
+
33
+ const TagsCsv = Schema.String.pipe(
34
+ Schema.decodeTo(
35
+ Schema.Array(Schema.String),
36
+ SchemaTransformation.transform({
37
+ decode: (s: string): ReadonlyArray<string> => s.split(","),
38
+ encode: (arr: ReadonlyArray<string>) => arr.join(","),
39
+ }),
40
+ ),
41
+ );
42
+
43
+ const ServiceDependentNumber = Schema.Number.pipe(
44
+ Schema.decodeTo(
45
+ Schema.Number,
46
+ SchemaTransformation.transformOrFail({
47
+ decode: (n: number) =>
48
+ Effect.gen(function* () {
49
+ const dep = yield* SomeDep;
50
+ return n + dep.x;
51
+ }),
52
+ encode: (n: number) =>
53
+ Effect.gen(function* () {
54
+ const dep = yield* SomeDep;
55
+ return n - dep.x;
56
+ }),
57
+ }),
58
+ ),
59
+ );
60
+
61
+ class ServiceDependentError extends Schema.TaggedErrorClass<ServiceDependentError>()(
62
+ "ServiceDependentError",
63
+ {
64
+ limit: ServiceDependentNumber,
65
+ message: Schema.String,
66
+ },
67
+ ) {}
68
+
69
+ const ServiceDependentAction = Action.make("ServiceDependentAction", {
70
+ payload: { amount: ServiceDependentNumber },
71
+ success: ServiceDependentNumber,
72
+ error: ServiceDependentError,
73
+ });
74
+
75
+ const ServiceDependentActor = Actor.make("ServiceDependentActor", {
76
+ actions: [ServiceDependentAction],
77
+ });
78
+
79
+ const ServiceDependentState = {
80
+ schema: Schema.Struct({
81
+ count: ServiceDependentNumber,
82
+ }),
83
+ initialValue: () => ({ count: 0 }),
84
+ };
85
+
86
+ const TransformedState = {
87
+ schema: Schema.Struct({
88
+ when: Schema.DateFromString,
89
+ url: Schema.URLFromString,
90
+ id: Schema.BigIntFromString,
91
+ bytes: Schema.Uint8ArrayFromBase64,
92
+ tags: TagsCsv,
93
+ history: Schema.Array(
94
+ Schema.Struct({
95
+ at: Schema.DateFromString,
96
+ payload: Schema.Uint8ArrayFromBase64,
97
+ }),
98
+ ),
99
+ }),
100
+ initialValue: () => ({
101
+ when: new Date("2024-01-15T10:30:00.000Z"),
102
+ url: new URL("https://rivet.dev/docs"),
103
+ id: 1n,
104
+ bytes: new Uint8Array([1, 2, 3]),
105
+ tags: ["alpha", "beta"],
106
+ history: [
107
+ {
108
+ at: new Date("2024-01-15T10:30:00.000Z"),
109
+ payload: new Uint8Array([4, 5, 6]),
110
+ },
111
+ ],
112
+ }),
113
+ };
114
+
115
+ describe("Actor.make", () => {
116
+ test("preserves the name literal", () => {
117
+ expectTypeOf(TestActor.name).toEqualTypeOf<"TestActor">();
118
+ });
119
+ });
120
+
121
+ describe("Actor.make(...).toLayer", () => {
122
+ test("is a function", () => {
123
+ expectTypeOf(TestActor.toLayer).toBeFunction();
124
+ });
125
+
126
+ test("accepts a plain action handlers object", () => {
127
+ expectTypeOf(TestActor.toLayer).toBeCallableWith({
128
+ Ping: () => Effect.succeed(0),
129
+ });
130
+ });
131
+
132
+ test("accepts an effect of action handlers", () => {
133
+ expectTypeOf(TestActor.toLayer).toBeCallableWith(
134
+ Effect.gen(function* () {
135
+ return {
136
+ Ping: () => Effect.succeed(0),
137
+ };
138
+ }),
139
+ );
140
+ });
141
+
142
+ test("accepts a function returning a plain action handlers object", () => {
143
+ expectTypeOf(TestActor.toLayer).toBeCallableWith(
144
+ (_wakeOptions: any) => ({
145
+ Ping: () => Effect.succeed(0),
146
+ }),
147
+ );
148
+ });
149
+
150
+ test("wake options omit state without a configured state type", () => {
151
+ TestActor.toLayer((wakeOptions) => {
152
+ // @ts-expect-error: stateless actors do not expose wakeOptions.state
153
+ wakeOptions.state;
154
+ expectTypeOf(
155
+ wakeOptions.rawRivetkitContext.state,
156
+ ).toEqualTypeOf<never>();
157
+
158
+ return {
159
+ Ping: () => Effect.succeed(0),
160
+ };
161
+ });
162
+
163
+ TestActor.toLayer((wakeOptions) => {
164
+ // @ts-expect-error: actors without a state option do not expose wakeOptions.state
165
+ wakeOptions.state;
166
+
167
+ expectTypeOf(
168
+ wakeOptions.rawRivetkitContext.state,
169
+ ).toEqualTypeOf<never>();
170
+
171
+ return {
172
+ Ping: () => Effect.succeed(0),
173
+ };
174
+ }, {});
175
+ });
176
+
177
+ test("wake options carry the configured state type", () => {
178
+ TestActor.toLayer(
179
+ (wakeOptions) => {
180
+ expectTypeOf(wakeOptions.state).toEqualTypeOf<
181
+ State.State<{ readonly count: number }, Schema.SchemaError>
182
+ >();
183
+
184
+ return {
185
+ Ping: () => Effect.succeed(0),
186
+ };
187
+ },
188
+ { state: TestState },
189
+ );
190
+ });
191
+
192
+ test("state schema services surface in State effects and actor layers", () => {
193
+ const layer = TestActor.toLayer(
194
+ (wakeOptions) => {
195
+ type StateGetServices = Effect.Services<
196
+ typeof wakeOptions.state.get
197
+ >;
198
+
199
+ expectTypeOf<SomeDep>().toExtend<StateGetServices>();
200
+
201
+ return {
202
+ Ping: () =>
203
+ wakeOptions.state.get.pipe(
204
+ Effect.map((current) => current.count),
205
+ Effect.orDie,
206
+ ),
207
+ };
208
+ },
209
+ { state: ServiceDependentState },
210
+ );
211
+ type LayerServices = Layer.Services<typeof layer>;
212
+ type LayerHasSomeDep = SomeDep extends LayerServices ? true : false;
213
+
214
+ expectTypeOf<LayerHasSomeDep>().toEqualTypeOf<true>();
215
+ });
216
+
217
+ test("wake options carry the transformed state type", () => {
218
+ TestActor.toLayer(
219
+ (wakeOptions) => {
220
+ expectTypeOf(wakeOptions.state).toEqualTypeOf<
221
+ State.State<
222
+ {
223
+ readonly when: Date;
224
+ readonly url: URL;
225
+ readonly id: bigint;
226
+ readonly bytes: Uint8Array;
227
+ readonly tags: ReadonlyArray<string>;
228
+ readonly history: ReadonlyArray<{
229
+ readonly at: Date;
230
+ readonly payload: Uint8Array;
231
+ }>;
232
+ },
233
+ Schema.SchemaError
234
+ >
235
+ >();
236
+
237
+ return {
238
+ Ping: () => Effect.succeed(0),
239
+ };
240
+ },
241
+ { state: TransformedState },
242
+ );
243
+ });
244
+
245
+ test("wake options carry the raw RivetKit context with the encoded configured state type", () => {
246
+ TestActor.toLayer(
247
+ (wakeOptions) => {
248
+ expectTypeOf(
249
+ wakeOptions.rawRivetkitContext.state,
250
+ ).toEqualTypeOf<{ readonly count: number }>();
251
+
252
+ return {
253
+ Ping: () => Effect.succeed(0),
254
+ };
255
+ },
256
+ { state: TestState },
257
+ );
258
+ });
259
+
260
+ test("wake options carry the raw RivetKit context with the encoded transformed state type", () => {
261
+ TestActor.toLayer(
262
+ (wakeOptions) => {
263
+ expectTypeOf(
264
+ wakeOptions.rawRivetkitContext.state,
265
+ ).toEqualTypeOf<{
266
+ readonly when: string;
267
+ readonly url: string;
268
+ readonly id: string;
269
+ readonly bytes: string;
270
+ readonly tags: string;
271
+ readonly history: ReadonlyArray<{
272
+ readonly at: string;
273
+ readonly payload: string;
274
+ }>;
275
+ }>();
276
+
277
+ return {
278
+ Ping: () => Effect.succeed(0),
279
+ };
280
+ },
281
+ { state: TransformedState },
282
+ );
283
+ });
284
+
285
+ test("wake options carry the configured database client type", () => {
286
+ TestActor.toLayer(
287
+ (wakeOptions) => {
288
+ expectTypeOf(
289
+ wakeOptions.rawRivetkitContext.db,
290
+ ).toEqualTypeOf<RawAccess>();
291
+ expectTypeOf(
292
+ wakeOptions.rawRivetkitContext.db.transaction(
293
+ async () => {},
294
+ { name: "effect-operation", timeout: 1_000 },
295
+ ),
296
+ ).toEqualTypeOf<Promise<void>>();
297
+
298
+ return {
299
+ Ping: () => Effect.succeed(0),
300
+ };
301
+ },
302
+ { db: db() },
303
+ );
304
+ });
305
+
306
+ test("accepts a function returning an effect of action handlers", () => {
307
+ expectTypeOf(TestActor.toLayer).toBeCallableWith((_wakeOptions: any) =>
308
+ Effect.gen(function* () {
309
+ return {
310
+ Ping: () => Effect.succeed(0),
311
+ };
312
+ }),
313
+ );
314
+ });
315
+
316
+ test("accepts an effect that resolves to a wake function", () => {
317
+ expectTypeOf(TestActor.toLayer).toBeCallableWith(
318
+ Effect.gen(function* () {
319
+ // Allow for initialization logic before the per-entity wake function is called
320
+
321
+ return (_wakeOptions: any) =>
322
+ Effect.gen(function* () {
323
+ return {
324
+ Ping: () => Effect.succeed(0),
325
+ };
326
+ });
327
+ }),
328
+ );
329
+ });
330
+
331
+ test("accepts an Effect.fn returning action handlers", () => {
332
+ expectTypeOf(TestActor.toLayer).toBeCallableWith(
333
+ Effect.fn("wake")(function* (_wakeOptions) {
334
+ return {
335
+ Ping: () => Effect.succeed(0),
336
+ };
337
+ }),
338
+ );
339
+ });
340
+
341
+ test("returns a Layer", () => {
342
+ expectTypeOf(TestActor.toLayer).returns.toExtend<Layer.Any>();
343
+ });
344
+
345
+ test("action handler's envelope is typed against the action", () => {
346
+ TestActor.toLayer({
347
+ Ping: (envelope) => {
348
+ expectTypeOf(envelope._tag).toEqualTypeOf<"Ping">();
349
+ expectTypeOf(envelope.action).toExtend<Action.Any>();
350
+ return Effect.succeed(0);
351
+ },
352
+ });
353
+ });
354
+
355
+ test("action handler return success is type checked", () => {
356
+ // Plain action handlers object.
357
+ expectTypeOf(TestActor.toLayer).toBeCallableWith({
358
+ Ping: () => Effect.succeed(0),
359
+ });
360
+
361
+ TestActor.toLayer({
362
+ // @ts-expect-error: Ping must return the declared number success type.
363
+ Ping: () => Effect.succeed("not a number"),
364
+ });
365
+
366
+ // Effect of action handlers.
367
+ expectTypeOf(TestActor.toLayer).toBeCallableWith(
368
+ Effect.gen(function* () {
369
+ return {
370
+ Ping: () => Effect.succeed(0),
371
+ };
372
+ }),
373
+ );
374
+
375
+ TestActor.toLayer(
376
+ // @ts-expect-error: Ping must return the declared number success type.
377
+ Effect.gen(function* () {
378
+ return {
379
+ Ping: () => Effect.succeed("not a number"),
380
+ };
381
+ }),
382
+ );
383
+
384
+ // Function returning a plain action handlers object.
385
+ expectTypeOf(TestActor.toLayer).toBeCallableWith(() => ({
386
+ Ping: () => Effect.succeed(0),
387
+ }));
388
+
389
+ // @ts-expect-error: Ping must return the declared number success type.
390
+ TestActor.toLayer(() => ({
391
+ Ping: () => Effect.succeed("not a number"),
392
+ }));
393
+
394
+ // Function returning an effect of action handlers.
395
+ expectTypeOf(TestActor.toLayer).toBeCallableWith(() =>
396
+ Effect.gen(function* () {
397
+ return {
398
+ Ping: () => Effect.succeed(0),
399
+ };
400
+ }),
401
+ );
402
+
403
+ // @ts-expect-error: Ping must return the declared number success type.
404
+ TestActor.toLayer(() =>
405
+ Effect.gen(function* () {
406
+ return {
407
+ Ping: () => Effect.succeed("not a number"),
408
+ };
409
+ }),
410
+ );
411
+
412
+ // Effect that resolves to a wake function.
413
+ expectTypeOf(TestActor.toLayer).toBeCallableWith(
414
+ Effect.gen(function* () {
415
+ return () => ({
416
+ Ping: () => Effect.succeed(0),
417
+ });
418
+ }),
419
+ );
420
+
421
+ TestActor.toLayer(
422
+ // @ts-expect-error: Ping must return the declared number success type.
423
+ Effect.gen(function* () {
424
+ return () => ({
425
+ Ping: () => Effect.succeed("not a number"),
426
+ });
427
+ }),
428
+ );
429
+
430
+ // Effect.fn returning action handlers.
431
+ expectTypeOf(TestActor.toLayer).toBeCallableWith(
432
+ Effect.fn("wake")(function* () {
433
+ return {
434
+ Ping: () => Effect.succeed(0),
435
+ };
436
+ }),
437
+ );
438
+
439
+ TestActor.toLayer(
440
+ // @ts-expect-error: Ping must return the declared number success type.
441
+ Effect.fn("wake")(function* () {
442
+ return {
443
+ Ping: () => Effect.succeed("not a number"),
444
+ };
445
+ }),
446
+ );
447
+ });
448
+
449
+ test("action handler return error is type checked", () => {
450
+ // Plain action handlers object.
451
+ expectTypeOf(TestActor.toLayer).toBeCallableWith({
452
+ Ping: () => Effect.succeed(0),
453
+ });
454
+
455
+ TestActor.toLayer({
456
+ // @ts-expect-error: Ping can only fail with its declared action error type.
457
+ // @effect-diagnostics effect/missingEffectError:off
458
+ Ping: () => Effect.fail(1),
459
+ });
460
+
461
+ // Effect of action handlers.
462
+ expectTypeOf(TestActor.toLayer).toBeCallableWith(
463
+ Effect.gen(function* () {
464
+ return {
465
+ Ping: () => Effect.succeed(0),
466
+ };
467
+ }),
468
+ );
469
+
470
+ TestActor.toLayer(
471
+ // @ts-expect-error: Ping can only fail with its declared action error type.
472
+ Effect.gen(function* () {
473
+ return {
474
+ // @effect-diagnostics effect/missingEffectError:off
475
+ Ping: () => Effect.fail(1),
476
+ };
477
+ }),
478
+ );
479
+
480
+ // Function returning a plain action handlers object.
481
+ expectTypeOf(TestActor.toLayer).toBeCallableWith(() => ({
482
+ Ping: () => Effect.succeed(0),
483
+ }));
484
+
485
+ // @ts-expect-error: Ping can only fail with its declared action error type.
486
+ TestActor.toLayer(() => ({
487
+ // @effect-diagnostics effect/missingEffectError:off
488
+ Ping: () => Effect.fail(1),
489
+ }));
490
+
491
+ // Function returning an effect of action handlers.
492
+ expectTypeOf(TestActor.toLayer).toBeCallableWith(() =>
493
+ Effect.gen(function* () {
494
+ return {
495
+ Ping: () => Effect.succeed(0),
496
+ };
497
+ }),
498
+ );
499
+
500
+ // @ts-expect-error: Ping can only fail with its declared action error type.
501
+ TestActor.toLayer(() =>
502
+ Effect.gen(function* () {
503
+ return {
504
+ // @effect-diagnostics effect/missingEffectError:off
505
+ Ping: () => Effect.fail(1),
506
+ };
507
+ }),
508
+ );
509
+
510
+ // Effect that resolves to a wake function.
511
+ expectTypeOf(TestActor.toLayer).toBeCallableWith(
512
+ Effect.gen(function* () {
513
+ return () => ({
514
+ Ping: () => Effect.succeed(0),
515
+ });
516
+ }),
517
+ );
518
+
519
+ TestActor.toLayer(
520
+ // @ts-expect-error: Ping can only fail with its declared action error type.
521
+ Effect.gen(function* () {
522
+ return () => ({
523
+ // @effect-diagnostics effect/missingEffectError:off
524
+ Ping: () => Effect.fail(1),
525
+ });
526
+ }),
527
+ );
528
+
529
+ // Effect.fn returning action handlers.
530
+ expectTypeOf(TestActor.toLayer).toBeCallableWith(
531
+ Effect.fn("wake")(function* () {
532
+ return {
533
+ Ping: () => Effect.succeed(0),
534
+ };
535
+ }),
536
+ );
537
+
538
+ TestActor.toLayer(
539
+ // @ts-expect-error: Ping can only fail with its declared action error type.
540
+ Effect.fn("wake")(function* () {
541
+ return {
542
+ // @effect-diagnostics effect/missingEffectError:off
543
+ Ping: () => Effect.fail(1),
544
+ };
545
+ }),
546
+ );
547
+ });
548
+
549
+ test("missing action handler is rejected", () => {
550
+ // @ts-expect-error: Ping handler is required
551
+ TestActor.toLayer({});
552
+ });
553
+
554
+ test.todo("unknown action handler key is rejected", () => {
555
+ TestActor.toLayer({
556
+ Ping: () => Effect.succeed(0),
557
+ // TODO: toLayer should reject unknown action handler keys
558
+ Unknown: () => Effect.void,
559
+ });
560
+ });
561
+
562
+ test.todo("wake-effect requirements surface in the Layer", () => {
563
+ const layer = TestActor.toLayer(
564
+ Effect.gen(function* () {
565
+ yield* SomeDep;
566
+ return { Ping: () => Effect.succeed(0) };
567
+ }),
568
+ );
569
+ type Reqs =
570
+ typeof layer extends Layer.Layer<any, any, infer R> ? R : never;
571
+ // @ts-expect-error: TODO - expectTypeOf<T>() no-arg generic form not resolving
572
+ expectTypeOf<SomeDep>().toExtend<Reqs>();
573
+ });
574
+ });
575
+
576
+ describe("Actor.make(...).of", () => {
577
+ test("preserves the action handlers object type", () => {
578
+ const handlers = {
579
+ Ping: () => Effect.succeed(0),
580
+ };
581
+
582
+ expectTypeOf(TestActor.of(handlers)).toEqualTypeOf<typeof handlers>();
583
+ });
584
+
585
+ test("action handler's envelope is typed against the action", () => {
586
+ TestActor.of({
587
+ Ping: (envelope) => {
588
+ expectTypeOf(envelope._tag).toEqualTypeOf<"Ping">();
589
+ expectTypeOf(envelope.action).toEqualTypeOf<typeof Ping>();
590
+ return Effect.succeed(0);
591
+ },
592
+ });
593
+ });
594
+
595
+ test("action handler return success is type checked", () => {
596
+ expectTypeOf(TestActor.of).toBeCallableWith({
597
+ Ping: () => Effect.succeed(0),
598
+ });
599
+
600
+ TestActor.of({
601
+ // @ts-expect-error: Ping must return the declared number success type.
602
+ Ping: () => Effect.succeed("not a number"),
603
+ });
604
+ });
605
+
606
+ test("action handler return error is type checked", () => {
607
+ expectTypeOf(TestActor.of).toBeCallableWith({
608
+ Ping: () => Effect.succeed(0),
609
+ });
610
+
611
+ TestActor.of({
612
+ // @ts-expect-error: Ping can only fail with its declared action error type.
613
+ // @effect-diagnostics effect/missingEffectError:off
614
+ Ping: () => Effect.fail(1),
615
+ });
616
+ });
617
+ });
618
+
619
+ describe("Actor.make(...).client", () => {
620
+ test("yields a typed Accessor", () => {
621
+ expectTypeOf(TestActor.client).toEqualTypeOf<
622
+ Effect.Effect<
623
+ Actor.Accessor<(typeof TestActor.actions)[number]>,
624
+ never,
625
+ Client.Client
626
+ >
627
+ >();
628
+ });
629
+
630
+ it.effect("handle calls require client-side schema services", () =>
631
+ Effect.gen(function* () {
632
+ const actor = (yield* ServiceDependentActor.client).getOrCreate(
633
+ "t-service-dependent",
634
+ );
635
+ const actionEffect = actor.ServiceDependentAction({ amount: 10 });
636
+ type ActionClientServices = Effect.Services<typeof actionEffect>;
637
+
638
+ expectTypeOf<SomeDep>().toExtend<ActionClientServices>();
639
+ }).pipe(Effect.provide(Client.layer())),
640
+ );
641
+ });