@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,60 @@
1
+ import { Effect, Logger, Layer } from "effect";
2
+ import * as RivetkitLog from "@ohos-ports/rivetkit/log";
3
+ import {
4
+ BaseLogger,
5
+ getOrCreateBaseLogger,
6
+ makeLogger,
7
+ } from "./internal/logging.ts";
8
+
9
+ /**
10
+ * Builds an Effect logger from a custom Pino-compatible logger.
11
+ *
12
+ * Use this with Effect's `Logger.layer` when you only need to customize Effect
13
+ * log output. Use {@link layerFromPino} when RivetKit's underlying TypeScript SDK
14
+ * logs should use the same logger too.
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * import { Logger } from "effect"
19
+ * import { RivetLogger } from "@rivetkit/effect"
20
+ * import { pino } from "pino"
21
+ *
22
+ * const baseLogger = pino({ transport: { target: "pino-pretty" } })
23
+ * const LoggerLive = Logger.layer([RivetLogger.fromPino(baseLogger)])
24
+ * ```
25
+ */
26
+ export const fromPino = (
27
+ baseLogger: RivetkitLog.Logger,
28
+ ): Logger.Logger<unknown, void> => makeLogger(baseLogger);
29
+
30
+ /**
31
+ * Builds a logging layer from a custom Pino-compatible logger.
32
+ *
33
+ * The layer installs the matching Effect logger and configures the underlying
34
+ * RivetKit TypeScript SDK logs to go through the same logger. The Effect tracer
35
+ * logger is installed alongside the Pino adapter so log events remain attached
36
+ * to active traces.
37
+ */
38
+ export const layerFromPino = (
39
+ baseLogger: RivetkitLog.Logger,
40
+ options?: {
41
+ readonly mergeWithExisting?: boolean | undefined;
42
+ },
43
+ ): Layer.Layer<BaseLogger> =>
44
+ Layer.mergeAll(
45
+ Layer.succeed(BaseLogger, baseLogger),
46
+ Logger.layer([Logger.tracerLogger, fromPino(baseLogger)], {
47
+ mergeWithExisting: options?.mergeWithExisting,
48
+ }),
49
+ );
50
+
51
+ /**
52
+ * Default RivetKit Effect logging layer.
53
+ *
54
+ * The layer creates a base logger from RivetKit log configuration and installs
55
+ * the Effect logger adapter. Applications that want custom formatting or
56
+ * transports should provide {@link layerFromPino} instead.
57
+ */
58
+ export const defaultLayer: Layer.Layer<never> = Layer.unwrap(
59
+ Effect.map(getOrCreateBaseLogger, layerFromPino),
60
+ );
@@ -0,0 +1,340 @@
1
+ import { assert, describe, it } from "@effect/vitest";
2
+ import { State } from "@rivetkit/effect";
3
+ import { Effect, Exit, PubSub, Stream } from "effect";
4
+
5
+ // Helper: build a State backed by a plain mutable cell, with
6
+ // Effect-typed read/write closures. Mirrors how Registry wires
7
+ // `decodeUnknownEffect` / `encodeUnknownEffect` over `c.state`.
8
+ const makeCellState = <A>(initial: A) => {
9
+ const cell = { value: initial };
10
+ return State.make<A, never, never>(
11
+ () => Effect.sync(() => cell.value),
12
+ (v) =>
13
+ Effect.sync(() => {
14
+ cell.value = v;
15
+ }),
16
+ ).pipe(Effect.map((s) => ({ s, cell })));
17
+ };
18
+
19
+ describe("State", () => {
20
+ it.effect("get reflects the backing store", () =>
21
+ Effect.gen(function* () {
22
+ const { s, cell } = yield* makeCellState(42);
23
+ assert.strictEqual(yield* State.get(s), 42);
24
+
25
+ cell.value = 100;
26
+ assert.strictEqual(yield* State.get(s), 100);
27
+ }),
28
+ );
29
+
30
+ it.effect("set writes through to the backing store", () =>
31
+ Effect.gen(function* () {
32
+ const { s, cell } = yield* makeCellState(0);
33
+ yield* State.set(s, 7);
34
+ assert.strictEqual(cell.value, 7);
35
+ assert.strictEqual(yield* State.get(s), 7);
36
+ }),
37
+ );
38
+
39
+ it.effect(
40
+ "getAndSet returns the previous value and commits the new value",
41
+ () =>
42
+ Effect.gen(function* () {
43
+ const { s, cell } = yield* makeCellState(10);
44
+ const previous = yield* State.getAndSet(s, 15);
45
+ assert.strictEqual(previous, 10);
46
+ assert.strictEqual(cell.value, 15);
47
+ }),
48
+ );
49
+
50
+ it.effect("setAndGet returns the committed value", () =>
51
+ Effect.gen(function* () {
52
+ const { s, cell } = yield* makeCellState(10);
53
+ const next = yield* State.setAndGet(s, 15);
54
+ assert.strictEqual(next, 15);
55
+ assert.strictEqual(cell.value, 15);
56
+ }),
57
+ );
58
+
59
+ it.effect("update applies f over read/write", () =>
60
+ Effect.gen(function* () {
61
+ const { s, cell } = yield* makeCellState(10);
62
+ yield* State.update(s, (n) => n + 5);
63
+ assert.strictEqual(cell.value, 15);
64
+ }),
65
+ );
66
+
67
+ it.effect("updateEffect applies an Effectful f over read/write", () =>
68
+ Effect.gen(function* () {
69
+ const { s, cell } = yield* makeCellState(10);
70
+ yield* State.updateEffect(s, (n) => Effect.succeed(n + 5));
71
+ assert.strictEqual(cell.value, 15);
72
+ }),
73
+ );
74
+
75
+ it.effect(
76
+ "getAndUpdate returns the previous value and commits the new value",
77
+ () =>
78
+ Effect.gen(function* () {
79
+ const { s, cell } = yield* makeCellState(10);
80
+ const previous = yield* State.getAndUpdate(s, (n) => n + 5);
81
+ assert.strictEqual(previous, 10);
82
+ assert.strictEqual(cell.value, 15);
83
+ }),
84
+ );
85
+
86
+ it.effect(
87
+ "getAndUpdateEffect returns the previous value and commits the new value",
88
+ () =>
89
+ Effect.gen(function* () {
90
+ const { s, cell } = yield* makeCellState(10);
91
+ const previous = yield* State.getAndUpdateEffect(s, (n) =>
92
+ Effect.succeed(n + 5),
93
+ );
94
+ assert.strictEqual(previous, 10);
95
+ assert.strictEqual(cell.value, 15);
96
+ }),
97
+ );
98
+
99
+ it.effect("updateAndGet returns the new value and commits it", () =>
100
+ Effect.gen(function* () {
101
+ const { s, cell } = yield* makeCellState(10);
102
+ const next = yield* State.updateAndGet(s, (n) => n + 5);
103
+ assert.strictEqual(next, 15);
104
+ assert.strictEqual(cell.value, 15);
105
+ }),
106
+ );
107
+
108
+ it.effect("updateAndGetEffect returns the new value and commits it", () =>
109
+ Effect.gen(function* () {
110
+ const { s, cell } = yield* makeCellState(10);
111
+ const next = yield* State.updateAndGetEffect(s, (n) =>
112
+ Effect.succeed(n + 5),
113
+ );
114
+ assert.strictEqual(next, 15);
115
+ assert.strictEqual(cell.value, 15);
116
+ }),
117
+ );
118
+
119
+ it.effect("modify returns B and commits the new value", () =>
120
+ Effect.gen(function* () {
121
+ const { s, cell } = yield* makeCellState("a");
122
+ const b = yield* State.modify(
123
+ s,
124
+ (str) => [str.length, `${str}b`] as const,
125
+ );
126
+ assert.strictEqual(b, 1);
127
+ assert.strictEqual(cell.value, "ab");
128
+ }),
129
+ );
130
+
131
+ it.effect("modifyEffect returns B and commits the new value", () =>
132
+ Effect.gen(function* () {
133
+ const { s, cell } = yield* makeCellState("a");
134
+ const b = yield* State.modifyEffect(s, (str) =>
135
+ Effect.succeed([str.length, `${str}b`] as const),
136
+ );
137
+ assert.strictEqual(b, 1);
138
+ assert.strictEqual(cell.value, "ab");
139
+ }),
140
+ );
141
+
142
+ it.effect(
143
+ "update is atomic across concurrent fibers (no lost updates)",
144
+ () =>
145
+ Effect.gen(function* () {
146
+ const { s, cell } = yield* makeCellState(0);
147
+ yield* Effect.all(
148
+ Array.from({ length: 100 }, () =>
149
+ State.update(s, (n) => n + 1),
150
+ ),
151
+ { concurrency: "unbounded" },
152
+ );
153
+ assert.strictEqual(cell.value, 100);
154
+ }),
155
+ );
156
+
157
+ it.effect("changes replays the most recent published value", () =>
158
+ Effect.gen(function* () {
159
+ const { s } = yield* makeCellState(0);
160
+ const initial = yield* State.changes(s).pipe(
161
+ Stream.take(1),
162
+ Stream.runCollect,
163
+ );
164
+ assert.deepStrictEqual(initial, [0]);
165
+
166
+ yield* s[State.RuntimeTypeId].publishEffect(Effect.succeed(7));
167
+ const later = yield* State.changes(s).pipe(
168
+ Stream.take(1),
169
+ Stream.runCollect,
170
+ );
171
+ assert.deepStrictEqual(later, [7]);
172
+ }),
173
+ );
174
+
175
+ it.effect("publish pushes values to live subscribers", () =>
176
+ Effect.gen(function* () {
177
+ const { s } = yield* makeCellState(0);
178
+ yield* Effect.scoped(
179
+ Effect.gen(function* () {
180
+ const sub = yield* PubSub.subscribe(
181
+ s[State.RuntimeTypeId].pubsub,
182
+ );
183
+ assert.strictEqual(yield* PubSub.take(sub), 0);
184
+
185
+ yield* s[State.RuntimeTypeId].publishEffect(
186
+ Effect.succeed(1),
187
+ );
188
+ yield* s[State.RuntimeTypeId].publishEffect(
189
+ Effect.succeed(2),
190
+ );
191
+ assert.strictEqual(yield* PubSub.take(sub), 1);
192
+ assert.strictEqual(yield* PubSub.take(sub), 2);
193
+ }),
194
+ );
195
+ }),
196
+ );
197
+
198
+ it.effect("shuts down the backing pubsub when its scope closes", () =>
199
+ Effect.gen(function* () {
200
+ const pubsub = yield* Effect.scoped(
201
+ Effect.gen(function* () {
202
+ const { s } = yield* makeCellState(0);
203
+ return s[State.RuntimeTypeId].pubsub;
204
+ }),
205
+ );
206
+
207
+ assert.isTrue(yield* PubSub.isShutdown(pubsub));
208
+ }),
209
+ );
210
+
211
+ it.effect("set does NOT auto-publish — the runtime does", () =>
212
+ Effect.gen(function* () {
213
+ const { s } = yield* makeCellState(0);
214
+ yield* State.set(s, 99);
215
+ // replay should still hold the initial 0, not 99
216
+ const latest = yield* State.changes(s).pipe(
217
+ Stream.take(1),
218
+ Stream.runCollect,
219
+ );
220
+ assert.deepStrictEqual(latest, [0]);
221
+ }),
222
+ );
223
+
224
+ it.effect("isState discriminates", () =>
225
+ Effect.gen(function* () {
226
+ const { s } = yield* makeCellState(0);
227
+ assert.isTrue(State.isState(s));
228
+ assert.isFalse(State.isState({}));
229
+ assert.isFalse(State.isState(null));
230
+ assert.isFalse(State.isState(42));
231
+ }),
232
+ );
233
+
234
+ it.effect("supports .pipe()", () =>
235
+ Effect.gen(function* () {
236
+ const { s } = yield* makeCellState(0);
237
+ yield* s.pipe(State.set(5));
238
+ assert.strictEqual(yield* State.get(s), 5);
239
+
240
+ yield* s.pipe(State.update((n) => n * 2));
241
+ assert.strictEqual(yield* State.get(s), 10);
242
+
243
+ yield* s.pipe(State.updateEffect((n) => Effect.succeed(n + 1)));
244
+ assert.strictEqual(yield* State.get(s), 11);
245
+ }),
246
+ );
247
+
248
+ it.effect("supports instance methods", () =>
249
+ Effect.gen(function* () {
250
+ const { s, cell } = yield* makeCellState(0);
251
+ assert.strictEqual(yield* s.get, 0);
252
+
253
+ yield* s.set(5);
254
+ assert.strictEqual(cell.value, 5);
255
+
256
+ const beforeSet = yield* s.getAndSet(6);
257
+ assert.strictEqual(beforeSet, 5);
258
+ assert.strictEqual(cell.value, 6);
259
+
260
+ const afterSet = yield* s.setAndGet(7);
261
+ assert.strictEqual(afterSet, 7);
262
+ assert.strictEqual(cell.value, 7);
263
+
264
+ yield* s.update((n) => n * 2);
265
+ assert.strictEqual(yield* s.get, 14);
266
+
267
+ yield* s.updateEffect((n) => Effect.succeed(n + 1));
268
+ assert.strictEqual(yield* s.get, 15);
269
+
270
+ const previousUpdate = yield* s.getAndUpdate((n) => n + 1);
271
+ assert.strictEqual(previousUpdate, 15);
272
+ assert.strictEqual(yield* s.get, 16);
273
+
274
+ const previousEffectUpdate = yield* s.getAndUpdateEffect((n) =>
275
+ Effect.succeed(n + 1),
276
+ );
277
+ assert.strictEqual(previousEffectUpdate, 16);
278
+ assert.strictEqual(yield* s.get, 17);
279
+
280
+ const next = yield* s.updateAndGet((n) => n + 1);
281
+ assert.strictEqual(next, 18);
282
+
283
+ const effectNext = yield* s.updateAndGetEffect((n) =>
284
+ Effect.succeed(n + 1),
285
+ );
286
+ assert.strictEqual(effectNext, 19);
287
+
288
+ const previous = yield* s.modify((n) => [n, n + 1] as const);
289
+ assert.strictEqual(previous, 19);
290
+ assert.strictEqual(yield* s.get, 20);
291
+
292
+ const effectPrevious = yield* s.modifyEffect((n) =>
293
+ Effect.succeed([n, n + 1] as const),
294
+ );
295
+ assert.strictEqual(effectPrevious, 20);
296
+ assert.strictEqual(yield* s.get, 21);
297
+ }),
298
+ );
299
+
300
+ it.effect("read failure propagates through get", () =>
301
+ Effect.gen(function* () {
302
+ const reads = { count: 0 };
303
+ // Construction reads once to seed the pubsub; subsequent reads
304
+ // fail. Mirrors a schema mismatch on persisted state.
305
+ const s = yield* State.make<number, "boom", never>(
306
+ () =>
307
+ Effect.suspend(() => {
308
+ reads.count++;
309
+ if (reads.count === 1) return Effect.succeed(0);
310
+ return Effect.fail("boom" as const);
311
+ }),
312
+ () => Effect.void,
313
+ );
314
+ const exit = yield* Effect.exit(State.get(s));
315
+ assert.isTrue(Exit.isFailure(exit));
316
+ }),
317
+ );
318
+
319
+ it.effect("write failure propagates through set", () =>
320
+ Effect.gen(function* () {
321
+ const s = yield* State.make<number, "boom", never>(
322
+ () => Effect.succeed(0),
323
+ () => Effect.fail("boom" as const),
324
+ );
325
+ const exit = yield* Effect.exit(State.set(s, 1));
326
+ assert.isTrue(Exit.isFailure(exit));
327
+ }),
328
+ );
329
+
330
+ it.effect("Effectful update failure does not write", () =>
331
+ Effect.gen(function* () {
332
+ const { s, cell } = yield* makeCellState(0);
333
+ const exit = yield* Effect.exit(
334
+ State.updateEffect(s, () => Effect.fail("boom" as const)),
335
+ );
336
+ assert.isTrue(Exit.isFailure(exit));
337
+ assert.strictEqual(cell.value, 0);
338
+ }),
339
+ );
340
+ });