git.iko.soy/rowtype-yoga/purescript-yoga-kafka-om
on main help sign in
Initial yoga-kafka-om: Kafka integration for yoga-om
commit027c77bcf48c135fc2636461b3003bff9a783c78 authorMark <mark.eibes@gmail.com> dateTue 17 Feb 2026, 01:56 (7 months ago)
9 files changed+1784
.gitignore+6 docker-compose.test.yml+24 package.json+13 spago.lock+1326 spago.yaml+32 src/Yoga/Kafka/Om.purs+98 src/Yoga/Kafka/OmLayer.purs+55 src/Yoga/Kafka/Strom.purs+99 test/Test/Yoga/Kafka/Main.purs+131
.gitignorenew file+6
@@ -0,0 +1,6 @@
1+.spago
2+output
3+node_modules
4+.psc-ide-port
5+bun.lock
6+.spec-results
docker-compose.test.ymlnew file+24
@@ -0,0 +1,24 @@
1+version: '3.8'
2+
3+services:
4+ kafka:
5+ image: apache/kafka:3.7.0
6+ ports:
7+ - "9092:9092"
8+ environment:
9+ KAFKA_NODE_ID: 1
10+ KAFKA_PROCESS_ROLES: broker,controller
11+ KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:9092,CONTROLLER://0.0.0.0:9093
12+ KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092
13+ KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT
14+ KAFKA_CONTROLLER_QUORUM_VOTERS: 1@localhost:9093
15+ KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER
16+ CLUSTER_ID: MkU3OEVBNTcwNTJENDM2Qk
17+ KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
18+ KAFKA_OFFSETS_TOPIC_NUM_PARTITIONS: 1
19+ KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0
20+ healthcheck:
21+ test: ["CMD-SHELL", "/opt/kafka/bin/kafka-topics.sh --bootstrap-server localhost:9092 --list"]
22+ interval: 5s
23+ timeout: 10s
24+ retries: 20
package.jsonnew file+13
@@ -0,0 +1,13 @@
1+{
2+ "name": "purescript-yoga-kafka-om",
3+ "version": "0.1.0",
4+ "description": "Om and Strom integration for PureScript Kafka bindings",
5+ "license": "MIT",
6+ "dependencies": {
7+ "kafkajs": "^2.2.4"
8+ },
9+ "devDependencies": {
10+ "purescript": "^0.15.15",
11+ "spago": "^1.0.3"
12+ }
13+}
spago.locknew file+1326
@@ -0,0 +1,1326 @@
1+{
2+ "workspace": {
3+ "packages": {
4+ "yoga-kafka-om": {
5+ "path": "./",
6+ "core": {
7+ "dependencies": [
8+ {
9+ "aff": ">=7.1.0 <8.0.0"
10+ },
11+ {
12+ "arrays": ">=7.0.0 <8.0.0"
13+ },
14+ {
15+ "avar": ">=5.0.0 <6.0.0"
16+ },
17+ {
18+ "console": ">=6.0.0 <7.0.0"
19+ },
20+ {
21+ "effect": ">=4.0.0 <5.0.0"
22+ },
23+ {
24+ "maybe": ">=6.0.0 <7.0.0"
25+ },
26+ {
27+ "prelude": ">=6.0.0 <7.0.0"
28+ },
29+ {
30+ "tailrec": ">=6.0.0 <7.0.0"
31+ },
32+ {
33+ "tuples": ">=7.0.0 <8.0.0"
34+ },
35+ {
36+ "yoga-kafka": ">=0.1.0 <1.0.0"
37+ },
38+ {
39+ "yoga-om": ">=1.2.2 <2.0.0"
40+ },
41+ {
42+ "yoga-om-layer": ">=1.0.6 <2.0.0"
43+ },
44+ {
45+ "yoga-om-strom": ">=0.2.0 <1.0.0"
46+ }
47+ ]
48+ },
49+ "test": {
50+ "dependencies": [
51+ {
52+ "exceptions": ">=6.0.0 <7.0.0"
53+ },
54+ {
55+ "spec": ">=8.0.0 <9.0.0"
56+ },
57+ {
58+ "spec-node": ">=0.0.3 <0.1.0"
59+ }
60+ ]
61+ }
62+ }
63+ },
64+ "extra_packages": {
65+ "aff": "7.1.0"
66+ }
67+ },
68+ "packages": {
69+ "aff": {
70+ "type": "registry",
71+ "version": "7.1.0",
72+ "integrity": "sha256-/nOpDQVnKIoP1pQ1ZZBYpadK0TuMlG4WwufGzJpsfD0=",
73+ "dependencies": [
74+ "control",
75+ "datetime",
76+ "effect",
77+ "either",
78+ "exceptions",
79+ "functions",
80+ "newtype",
81+ "parallel",
82+ "partial",
83+ "prelude",
84+ "st",
85+ "tailrec",
86+ "transformers",
87+ "unsafe-coerce"
88+ ]
89+ },
90+ "aff-retry": {
91+ "type": "registry",
92+ "version": "2.0.0",
93+ "integrity": "sha256-rKZNcP4IKSyBFQBVzrCvE0ULoJt0N/uDLLJ0dPotr4k=",
94+ "dependencies": [
95+ "aff",
96+ "arrays",
97+ "datetime",
98+ "effect",
99+ "either",
100+ "exceptions",
101+ "integers",
102+ "maybe",
103+ "newtype",
104+ "numbers",
105+ "prelude",
106+ "random",
107+ "transformers"
108+ ]
109+ },
110+ "ansi": {
111+ "type": "registry",
112+ "version": "7.0.0",
113+ "integrity": "sha256-tOUaXJFzPSfBVAqLI8hc+Tz2M1rg16cXqyHaK/3SmFg=",
114+ "dependencies": [
115+ "foldable-traversable",
116+ "lists",
117+ "prelude"
118+ ]
119+ },
120+ "argonaut-codecs": {
121+ "type": "registry",
122+ "version": "9.1.0",
123+ "integrity": "sha256-K910SBrmYallESsm8pxJYThs7lig6hxX3M4PW33Cgew=",
124+ "dependencies": [
125+ "argonaut-core",
126+ "arrays",
127+ "bifunctors",
128+ "either",
129+ "foldable-traversable",
130+ "foreign-object",
131+ "identity",
132+ "integers",
133+ "lists",
134+ "maybe",
135+ "nonempty",
136+ "ordered-collections",
137+ "prelude",
138+ "record",
139+ "strings",
140+ "tuples"
141+ ]
142+ },
143+ "argonaut-core": {
144+ "type": "registry",
145+ "version": "7.0.0",
146+ "integrity": "sha256-gYZihtBIKthVuXXoiPphMookSf8M5aHyFYkcIWqlxTM=",
147+ "dependencies": [
148+ "arrays",
149+ "control",
150+ "either",
151+ "foreign-object",
152+ "functions",
153+ "gen",
154+ "maybe",
155+ "nonempty",
156+ "prelude",
157+ "strings",
158+ "tailrec"
159+ ]
160+ },
161+ "arraybuffer-types": {
162+ "type": "registry",
163+ "version": "3.0.2",
164+ "integrity": "sha256-p05cJnSkyeoB7VHzMyc2Eb1RwUaB7Nl0sQSqEGNEUD8=",
165+ "dependencies": []
166+ },
167+ "arrays": {
168+ "type": "registry",
169+ "version": "7.3.0",
170+ "integrity": "sha256-GD4z7LCi9wzi7FCQqbWhvs8paoUK9NO+HwgXZ+KP8Rk=",
171+ "dependencies": [
172+ "bifunctors",
173+ "control",
174+ "foldable-traversable",
175+ "functions",
176+ "maybe",
177+ "nonempty",
178+ "partial",
179+ "prelude",
180+ "safe-coerce",
181+ "st",
182+ "tailrec",
183+ "tuples",
184+ "unfoldable",
185+ "unsafe-coerce"
186+ ]
187+ },
188+ "avar": {
189+ "type": "registry",
190+ "version": "5.0.1",
191+ "integrity": "sha256-8V4SxF4TIWZ9Ik1P4lPzIRUAZeFBe8w5WA2VcCEKsIk=",
192+ "dependencies": [
193+ "aff",
194+ "effect",
195+ "either",
196+ "exceptions",
197+ "functions",
198+ "maybe",
199+ "prelude"
200+ ]
201+ },
202+ "bifunctors": {
203+ "type": "registry",
204+ "version": "6.1.0",
205+ "integrity": "sha256-CvZRb8uI75eKIqiYntR+k05Wk6tKlMS5w8sCA2AFna0=",
206+ "dependencies": [
207+ "const",
208+ "either",
209+ "newtype",
210+ "prelude",
211+ "tuples"
212+ ]
213+ },
214+ "catenable-lists": {
215+ "type": "registry",
216+ "version": "7.0.0",
217+ "integrity": "sha256-7+i/MHBLIRh604iCNT4ENWRWE/wuHIotER4kpdX9Ve0=",
218+ "dependencies": [
219+ "control",
220+ "foldable-traversable",
221+ "lists",
222+ "maybe",
223+ "prelude",
224+ "tuples",
225+ "unfoldable"
226+ ]
227+ },
228+ "console": {
229+ "type": "registry",
230+ "version": "6.1.0",
231+ "integrity": "sha256-3T5PPz1mATZmV0C4GzkCkpLlCa5xG7u8ZNgnLrrrI1Q=",
232+ "dependencies": [
233+ "effect",
234+ "prelude"
235+ ]
236+ },
237+ "const": {
238+ "type": "registry",
239+ "version": "6.0.0",
240+ "integrity": "sha256-S+m0lQsxnji0z1XRUmkn+3RFuWJ5CXWuIr1u3i8Bfoc=",
241+ "dependencies": [
242+ "invariant",
243+ "newtype",
244+ "prelude"
245+ ]
246+ },
247+ "contravariant": {
248+ "type": "registry",
249+ "version": "6.0.0",
250+ "integrity": "sha256-nuckEWqL80NaPmR/l9AUyrfycNh18XcvlZV9PAamwqc=",
251+ "dependencies": [
252+ "const",
253+ "either",
254+ "newtype",
255+ "prelude",
256+ "tuples"
257+ ]
258+ },
259+ "control": {
260+ "type": "registry",
261+ "version": "6.0.0",
262+ "integrity": "sha256-0vxyYh5FL+ilxgLo954w1LqCUC0RSIivNNlb9CaNUQg=",
263+ "dependencies": [
264+ "newtype",
265+ "prelude"
266+ ]
267+ },
268+ "datetime": {
269+ "type": "registry",
270+ "version": "6.1.0",
271+ "integrity": "sha256-tbv6eDXy6QOD7YSknxYSbsDF32OC2JrNbSBpzMs7Doc=",
272+ "dependencies": [
273+ "bifunctors",
274+ "control",
275+ "either",
276+ "enums",
277+ "foldable-traversable",
278+ "functions",
279+ "gen",
280+ "integers",
281+ "lists",
282+ "maybe",
283+ "newtype",
284+ "numbers",
285+ "ordered-collections",
286+ "partial",
287+ "prelude",
288+ "tuples"
289+ ]
290+ },
291+ "distributive": {
292+ "type": "registry",
293+ "version": "6.0.0",
294+ "integrity": "sha256-netyJK4B32PDYHD0tF2AenKgo3urzbI770ycM0pArAo=",
295+ "dependencies": [
296+ "identity",
297+ "newtype",
298+ "prelude",
299+ "tuples",
300+ "type-equality"
301+ ]
302+ },
303+ "effect": {
304+ "type": "registry",
305+ "version": "4.0.0",
306+ "integrity": "sha256-DdqU3bncRTjgMVPPRlAuGHVeajSPEcRaRhNuio7if6s=",
307+ "dependencies": [
308+ "prelude"
309+ ]
310+ },
311+ "either": {
312+ "type": "registry",
313+ "version": "6.1.0",
314+ "integrity": "sha256-tBHx3PgtH4GZOApZCDkcM7szRTYJinrUrVGWazQCUUg=",
315+ "dependencies": [
316+ "control",
317+ "invariant",
318+ "maybe",
319+ "prelude"
320+ ]
321+ },
322+ "enums": {
323+ "type": "registry",
324+ "version": "6.0.1",
325+ "integrity": "sha256-sdZOmLX5+5pASGpvVyT0vSD5BBYQjZiayjV5XiPutso=",
326+ "dependencies": [
327+ "control",
328+ "either",
329+ "gen",
330+ "maybe",
331+ "newtype",
332+ "nonempty",
333+ "partial",
334+ "prelude",
335+ "tuples",
336+ "unfoldable"
337+ ]
338+ },
339+ "exceptions": {
340+ "type": "registry",
341+ "version": "6.1.0",
342+ "integrity": "sha256-nRrr+N0wk0TUFOWLR7e1n1oxcXo/X345bQUtoY9lW6A=",
343+ "dependencies": [
344+ "effect",
345+ "either",
346+ "maybe",
347+ "prelude"
348+ ]
349+ },
350+ "exists": {
351+ "type": "registry",
352+ "version": "6.0.0",
353+ "integrity": "sha256-vtLbrWNaI+pzx/2fw2AQnCovoLtcosClIhjO26QKkh8=",
354+ "dependencies": [
355+ "unsafe-coerce"
356+ ]
357+ },
358+ "exitcodes": {
359+ "type": "registry",
360+ "version": "4.0.0",
361+ "integrity": "sha256-xBl4aFSEQ05Owp1ZvO6OxxvLK3LVzU35RSbj79SsePQ=",
362+ "dependencies": [
363+ "enums",
364+ "maybe",
365+ "prelude"
366+ ]
367+ },
368+ "foldable-traversable": {
369+ "type": "registry",
370+ "version": "6.0.0",
371+ "integrity": "sha256-NGGWyCio/xjce6fPP7y3wwg7CheqllID6aJudDxbWhA=",
372+ "dependencies": [
373+ "bifunctors",
374+ "const",
375+ "control",
376+ "either",
377+ "functors",
378+ "identity",
379+ "maybe",
380+ "newtype",
381+ "orders",
382+ "prelude",
383+ "tuples"
384+ ]
385+ },
386+ "foreign": {
387+ "type": "registry",
388+ "version": "7.0.0",
389+ "integrity": "sha256-jiDRMVQfovGsbbA/FzbzYf6iWk9i2b5gNrIjz+gFfsI=",
390+ "dependencies": [
391+ "either",
392+ "functions",
393+ "integers",
394+ "lists",
395+ "maybe",
396+ "prelude",
397+ "strings",
398+ "transformers",
399+ "unsafe-coerce"
400+ ]
401+ },
402+ "foreign-object": {
403+ "type": "registry",
404+ "version": "4.1.0",
405+ "integrity": "sha256-x/Q7r80z/vmHRKvPwhYmHP/DnJciPtsyGfRTMzayKIU=",
406+ "dependencies": [
407+ "arrays",
408+ "foldable-traversable",
409+ "functions",
410+ "gen",
411+ "lists",
412+ "maybe",
413+ "prelude",
414+ "st",
415+ "tailrec",
416+ "tuples",
417+ "typelevel-prelude",
418+ "unfoldable",
419+ "unsafe-coerce"
420+ ]
421+ },
422+ "fork": {
423+ "type": "registry",
424+ "version": "6.0.0",
425+ "integrity": "sha256-0keBVeZZg1eCZEy/En1CohVW829gQUqKtqBNIRa93/A=",
426+ "dependencies": [
427+ "aff",
428+ "prelude",
429+ "transformers"
430+ ]
431+ },
432+ "free": {
433+ "type": "registry",
434+ "version": "7.1.0",
435+ "integrity": "sha256-tUBInfUpRQEw4u94GWcYW7EYdmSdDHSRn88a+C0eltU=",
436+ "dependencies": [
437+ "catenable-lists",
438+ "control",
439+ "distributive",
440+ "either",
441+ "exists",
442+ "foldable-traversable",
443+ "invariant",
444+ "lazy",
445+ "maybe",
446+ "prelude",
447+ "tailrec",
448+ "transformers",
449+ "tuples",
450+ "unsafe-coerce"
451+ ]
452+ },
453+ "functions": {
454+ "type": "registry",
455+ "version": "6.0.0",
456+ "integrity": "sha256-0jgtOr+SFO4UScKc40oA/9Vdcf2rLn3h3vZlY0KfqVo=",
457+ "dependencies": [
458+ "prelude"
459+ ]
460+ },
461+ "functors": {
462+ "type": "registry",
463+ "version": "5.0.0",
464+ "integrity": "sha256-W1o/4wcp6S3chodYL8uixlXK2mfr1L+SzM1R+whqUco=",
465+ "dependencies": [
466+ "bifunctors",
467+ "const",
468+ "contravariant",
469+ "control",
470+ "distributive",
471+ "either",
472+ "invariant",
473+ "maybe",
474+ "newtype",
475+ "prelude",
476+ "profunctor",
477+ "tuples",
478+ "unsafe-coerce"
479+ ]
480+ },
481+ "gen": {
482+ "type": "registry",
483+ "version": "4.0.0",
484+ "integrity": "sha256-kyOateeOtQa07vfTMYeSuCKdelgKE1R65fwKkZ10wsY=",
485+ "dependencies": [
486+ "either",
487+ "foldable-traversable",
488+ "identity",
489+ "maybe",
490+ "newtype",
491+ "nonempty",
492+ "prelude",
493+ "tailrec",
494+ "tuples",
495+ "unfoldable"
496+ ]
497+ },
498+ "heterogeneous": {
499+ "type": "registry",
500+ "version": "0.7.0",
501+ "integrity": "sha256-pQwcFvhWL8noktxaoD7FlLs2F2lYf3QHoyO0VOWO3I0=",
502+ "dependencies": [
503+ "either",
504+ "foldable-traversable",
505+ "functors",
506+ "prelude",
507+ "record",
508+ "tuples",
509+ "variant"
510+ ]
511+ },
512+ "identity": {
513+ "type": "registry",
514+ "version": "6.0.0",
515+ "integrity": "sha256-RY/iXPpxpqvKHYfJeFVNI1J06kohB9rbtWYuha8t0LE=",
516+ "dependencies": [
517+ "control",
518+ "invariant",
519+ "newtype",
520+ "prelude"
521+ ]
522+ },
523+ "integers": {
524+ "type": "registry",
525+ "version": "6.0.0",
526+ "integrity": "sha256-Y8yozC1uHRLcruobEyv5/bMKzE1BoKJ/olHv6o+bgm8=",
527+ "dependencies": [
528+ "maybe",
529+ "numbers",
530+ "prelude"
531+ ]
532+ },
533+ "invariant": {
534+ "type": "registry",
535+ "version": "6.0.0",
536+ "integrity": "sha256-HyoT1I5qIHRq8RSZji6zZDvqnpVjLw748utrKwZtqsw=",
537+ "dependencies": [
538+ "control",
539+ "prelude"
540+ ]
541+ },
542+ "js-date": {
543+ "type": "registry",
544+ "version": "8.0.0",
545+ "integrity": "sha256-DNQrp4xYc8o80jtxo0aOkUeXGi6h0Xidkk2ZQ8Y4l5Y=",
546+ "dependencies": [
547+ "datetime",
548+ "effect",
549+ "enums",
550+ "foreign",
551+ "functions",
552+ "integers",
553+ "maybe",
554+ "prelude"
555+ ]
556+ },
557+ "js-promise": {
558+ "type": "registry",
559+ "version": "1.0.0",
560+ "integrity": "sha256-u+6+ofIwT5XhT2fbMu4dShwkyWGfPRt9642Cnzw/Wvk=",
561+ "dependencies": [
562+ "effect",
563+ "exceptions",
564+ "foldable-traversable",
565+ "functions",
566+ "maybe",
567+ "newtype",
568+ "prelude"
569+ ]
570+ },
571+ "js-promise-aff": {
572+ "type": "registry",
573+ "version": "1.0.0",
574+ "integrity": "sha256-gEKAdNnTWjXS+tTp/AhYl/ezloKfuoQFcBKrdOx2USo=",
575+ "dependencies": [
576+ "aff",
577+ "control",
578+ "effect",
579+ "either",
580+ "exceptions",
581+ "foreign",
582+ "js-promise",
583+ "maybe",
584+ "prelude",
585+ "transformers"
586+ ]
587+ },
588+ "lazy": {
589+ "type": "registry",
590+ "version": "6.0.0",
591+ "integrity": "sha256-MHRC+1Pc+AjortBp4b/e1e6KpEpsDwaJBNBB7TOL+1I=",
592+ "dependencies": [
593+ "control",
594+ "foldable-traversable",
595+ "invariant",
596+ "prelude"
597+ ]
598+ },
599+ "lists": {
600+ "type": "registry",
601+ "version": "7.0.0",
602+ "integrity": "sha256-/mGd7wLoU+wsTcqii7SSUByxsvwvjnu2PjwAD47yYb4=",
603+ "dependencies": [
604+ "bifunctors",
605+ "control",
606+ "foldable-traversable",
607+ "lazy",
608+ "maybe",
609+ "newtype",
610+ "nonempty",
611+ "partial",
612+ "prelude",
613+ "tailrec",
614+ "tuples",
615+ "unfoldable"
616+ ]
617+ },
618+ "maybe": {
619+ "type": "registry",
620+ "version": "6.0.0",
621+ "integrity": "sha256-8X6XEgtZ5lqfkEuc1hWOofnL6mwhk8gSJTxwvbR/SbY=",
622+ "dependencies": [
623+ "control",
624+ "invariant",
625+ "newtype",
626+ "prelude"
627+ ]
628+ },
629+ "mmorph": {
630+ "type": "registry",
631+ "version": "7.0.0",
632+ "integrity": "sha256-Cfltaq+tfYjuSoSBN/b8cSQZbvZTqIRs6ebQewbIkGs=",
633+ "dependencies": [
634+ "bifunctors",
635+ "either",
636+ "free",
637+ "functors",
638+ "identity",
639+ "maybe",
640+ "newtype",
641+ "prelude",
642+ "transformers",
643+ "tuples"
644+ ]
645+ },
646+ "newtype": {
647+ "type": "registry",
648+ "version": "5.0.0",
649+ "integrity": "sha256-dt6w0cty4OS+Pjt9fsh3iU6ottqSU83mdAZkaEEvo6c=",
650+ "dependencies": [
651+ "prelude",
652+ "safe-coerce"
653+ ]
654+ },
655+ "node-buffer": {
656+ "type": "registry",
657+ "version": "9.0.0",
658+ "integrity": "sha256-RdQjilmEHqsT4fwpCr8Mavw8KTafYrS3ZM6zeAAVAl8=",
659+ "dependencies": [
660+ "arraybuffer-types",
661+ "effect",
662+ "functions",
663+ "maybe",
664+ "nullable",
665+ "partial",
666+ "prelude",
667+ "st",
668+ "unsafe-coerce"
669+ ]
670+ },
671+ "node-event-emitter": {
672+ "type": "registry",
673+ "version": "3.0.0",
674+ "integrity": "sha256-wodx71NxJBPXOaawFn01V1ByYB2vT+I3RuV2giOVKMg=",
675+ "dependencies": [
676+ "effect",
677+ "either",
678+ "functions",
679+ "maybe",
680+ "nullable",
681+ "prelude",
682+ "unsafe-coerce"
683+ ]
684+ },
685+ "node-fs": {
686+ "type": "registry",
687+ "version": "9.2.0",
688+ "integrity": "sha256-fxioTSm9y47WuYs9F/7nxCh/RVDCl3Kr3Hg779J4SJA=",
689+ "dependencies": [
690+ "aff",
691+ "datetime",
692+ "effect",
693+ "either",
694+ "enums",
695+ "exceptions",
696+ "functions",
697+ "integers",
698+ "js-date",
699+ "maybe",
700+ "node-buffer",
701+ "node-path",
702+ "node-streams",
703+ "nullable",
704+ "partial",
705+ "prelude",
706+ "strings"
707+ ]
708+ },
709+ "node-path": {
710+ "type": "registry",
711+ "version": "5.0.1",
712+ "integrity": "sha256-j7n/SmpVz0FOMJl4XZop3ofJ+MeIPkMNqUEUKHEL6Us=",
713+ "dependencies": [
714+ "effect"
715+ ]
716+ },
717+ "node-process": {
718+ "type": "registry",
719+ "version": "11.2.0",
720+ "integrity": "sha256-onzKeNmaeYfN3HSDPqSFTrrP2Yl9tnVsfKKhnf2plxM=",
721+ "dependencies": [
722+ "effect",
723+ "exceptions",
724+ "foreign",
725+ "foreign-object",
726+ "maybe",
727+ "node-event-emitter",
728+ "node-streams",
729+ "nullable",
730+ "posix-types",
731+ "prelude",
732+ "strings"
733+ ]
734+ },
735+ "node-streams": {
736+ "type": "registry",
737+ "version": "9.0.1",
738+ "integrity": "sha256-yuVGm/R/tBr4Nphi+a1a984Z0fkmN9Q5wWtSmbmJTpA=",
739+ "dependencies": [
740+ "aff",
741+ "arrays",
742+ "effect",
743+ "either",
744+ "exceptions",
745+ "maybe",
746+ "node-buffer",
747+ "node-event-emitter",
748+ "nullable",
749+ "prelude",
750+ "refs",
751+ "st",
752+ "tailrec",
753+ "unsafe-coerce"
754+ ]
755+ },
756+ "nonempty": {
757+ "type": "registry",
758+ "version": "7.0.0",
759+ "integrity": "sha256-Ctkq8/+KiGqCE53Y/LvibA/coy7ev9HtRZU+GYS7yfQ=",
760+ "dependencies": [
761+ "control",
762+ "foldable-traversable",
763+ "maybe",
764+ "prelude",
765+ "tuples",
766+ "unfoldable"
767+ ]
768+ },
769+ "now": {
770+ "type": "registry",
771+ "version": "6.0.0",
772+ "integrity": "sha256-mTyG4s8tps3XIf0jjV/lME2/QlwMzrQly2wqfSSUM3o=",
773+ "dependencies": [
774+ "datetime",
775+ "effect",
776+ "prelude"
777+ ]
778+ },
779+ "nullable": {
780+ "type": "registry",
781+ "version": "6.0.0",
782+ "integrity": "sha256-CDAZk+L9Sc0GCFTkE8n+qdp4Ys8avvnkU+m2hVALt7M=",
783+ "dependencies": [
784+ "functions",
785+ "maybe",
786+ "prelude"
787+ ]
788+ },
789+ "numbers": {
790+ "type": "registry",
791+ "version": "9.0.1",
792+ "integrity": "sha256-ZydYckgwRAnRaFC7pu6fADhzdwX+UqHZrfJyet64zls=",
793+ "dependencies": [
794+ "functions",
795+ "maybe",
796+ "prelude"
797+ ]
798+ },
799+ "open-memoize": {
800+ "type": "registry",
801+ "version": "6.2.0",
802+ "integrity": "sha256-yJLuVYX8FocHIJ+6q7efH/Pj4S+e1Y57DZ5Dn7qf4ww=",
803+ "dependencies": [
804+ "either",
805+ "integers",
806+ "lazy",
807+ "lists",
808+ "maybe",
809+ "partial",
810+ "prelude",
811+ "strings",
812+ "tuples"
813+ ]
814+ },
815+ "optparse": {
816+ "type": "registry",
817+ "version": "5.0.1",
818+ "integrity": "sha256-2btDjHPRR0uujUQJuZAN9AzvVoXe7yG6IwnY5evERhQ=",
819+ "dependencies": [
820+ "arrays",
821+ "bifunctors",
822+ "control",
823+ "effect",
824+ "either",
825+ "enums",
826+ "exists",
827+ "exitcodes",
828+ "foldable-traversable",
829+ "free",
830+ "integers",
831+ "lazy",
832+ "lists",
833+ "maybe",
834+ "newtype",
835+ "node-buffer",
836+ "node-process",
837+ "node-streams",
838+ "nonempty",
839+ "numbers",
840+ "open-memoize",
841+ "partial",
842+ "prelude",
843+ "strings",
844+ "tailrec",
845+ "transformers",
846+ "tuples"
847+ ]
848+ },
849+ "ordered-collections": {
850+ "type": "registry",
851+ "version": "3.2.0",
852+ "integrity": "sha256-NaEXc2cz/7lJfxPWEja4XlZbGxuRqJwFKQJjsJf51kQ=",
853+ "dependencies": [
854+ "arrays",
855+ "control",
856+ "foldable-traversable",
857+ "functions",
858+ "gen",
859+ "lists",
860+ "maybe",
861+ "newtype",
862+ "partial",
863+ "prelude",
864+ "safe-coerce",
865+ "tailrec",
866+ "tuples",
867+ "unfoldable"
868+ ]
869+ },
870+ "orders": {
871+ "type": "registry",
872+ "version": "6.0.0",
873+ "integrity": "sha256-nSMxZK7wxyDULkjZqvMyB71mVo6b0AyLLHosDVbv1XM=",
874+ "dependencies": [
875+ "newtype",
876+ "prelude"
877+ ]
878+ },
879+ "parallel": {
880+ "type": "registry",
881+ "version": "7.0.0",
882+ "integrity": "sha256-bAGpVhrFz2IyHkA4MR7+tE/19FZ7dzYBdYPgK5svkqw=",
883+ "dependencies": [
884+ "control",
885+ "effect",
886+ "either",
887+ "foldable-traversable",
888+ "functors",
889+ "maybe",
890+ "newtype",
891+ "prelude",
892+ "profunctor",
893+ "refs",
894+ "transformers"
895+ ]
896+ },
897+ "partial": {
898+ "type": "registry",
899+ "version": "4.0.0",
900+ "integrity": "sha256-mdrlJBAgFMh79hNRW39uv7c+BwnnaOrAMmdh7+VhEhs=",
901+ "dependencies": []
902+ },
903+ "pipes": {
904+ "type": "registry",
905+ "version": "8.0.0",
906+ "integrity": "sha256-dtUNpG8BGVr7pu6mQZyAnUAftUHgNS7f0QH3Kw5UTpQ=",
907+ "dependencies": [
908+ "aff",
909+ "control",
910+ "effect",
911+ "either",
912+ "foldable-traversable",
913+ "identity",
914+ "lists",
915+ "maybe",
916+ "mmorph",
917+ "newtype",
918+ "prelude",
919+ "tailrec",
920+ "transformers",
921+ "tuples"
922+ ]
923+ },
924+ "posix-types": {
925+ "type": "registry",
926+ "version": "6.0.0",
927+ "integrity": "sha256-Je5jG439F/SFO/IvVevZnWVxDIonrehlKuJkefP2lHE=",
928+ "dependencies": [
929+ "maybe",
930+ "newtype",
931+ "prelude"
932+ ]
933+ },
934+ "prelude": {
935+ "type": "registry",
936+ "version": "6.0.2",
937+ "integrity": "sha256-IqykrDRquGUD/LRSad7y75hZv3bUsGQ4knc8tdez8Qw=",
938+ "dependencies": []
939+ },
940+ "profunctor": {
941+ "type": "registry",
942+ "version": "6.0.1",
943+ "integrity": "sha256-Ow0mJC+PIIRq/a3mzCWY5vxuTgR/e+Ee68+mWeSRnWY=",
944+ "dependencies": [
945+ "control",
946+ "distributive",
947+ "either",
948+ "exists",
949+ "invariant",
950+ "newtype",
951+ "prelude",
952+ "tuples"
953+ ]
954+ },
955+ "random": {
956+ "type": "registry",
957+ "version": "6.0.0",
958+ "integrity": "sha256-7sZRo6P9UIiXDZ9s2QL9T8h3wlIwLdQrR4+oZKOijQw=",
959+ "dependencies": [
960+ "effect",
961+ "integers",
962+ "prelude"
963+ ]
964+ },
965+ "record": {
966+ "type": "registry",
967+ "version": "4.0.0",
968+ "integrity": "sha256-WQ8fqp4X1wTA70J4qUcPW4Z7DCwx6e5KJnT/EuTdWSc=",
969+ "dependencies": [
970+ "functions",
971+ "prelude",
972+ "unsafe-coerce"
973+ ]
974+ },
975+ "record-studio": {
976+ "type": "registry",
977+ "version": "1.0.4",
978+ "integrity": "sha256-CRnFvSXpDDzio+buibUxFU9zPZDd7/lzrYC1Ya3FuIw=",
979+ "dependencies": [
980+ "heterogeneous",
981+ "lists",
982+ "prelude",
983+ "record",
984+ "typelevel-prelude",
985+ "unsafe-coerce"
986+ ]
987+ },
988+ "refs": {
989+ "type": "registry",
990+ "version": "6.0.0",
991+ "integrity": "sha256-KKD5G9dR2SauuDGULUJsxjqDOEi1Jqm+2Sq5U2mY8YU=",
992+ "dependencies": [
993+ "effect",
994+ "prelude"
995+ ]
996+ },
997+ "safe-coerce": {
998+ "type": "registry",
999+ "version": "2.0.0",
1000+ "integrity": "sha256-EJrxtKt5xC7TYjBZrSBO/J7Aw3DmtilYjt4olpAXWVc=",
1001+ "dependencies": [
1002+ "unsafe-coerce"
1003+ ]
1004+ },
1005+ "spec": {
1006+ "type": "registry",
1007+ "version": "8.1.1",
1008+ "integrity": "sha256-b6qjDb7lUJGLV+WEoywLu6BCXC4zGpfbN43mTL+vBeI=",
1009+ "dependencies": [
1010+ "aff",
1011+ "ansi",
1012+ "arrays",
1013+ "avar",
1014+ "bifunctors",
1015+ "control",
1016+ "datetime",
1017+ "effect",
1018+ "either",
1019+ "exceptions",
1020+ "foldable-traversable",
1021+ "fork",
1022+ "identity",
1023+ "integers",
1024+ "lists",
1025+ "maybe",
1026+ "newtype",
1027+ "now",
1028+ "ordered-collections",
1029+ "parallel",
1030+ "pipes",
1031+ "prelude",
1032+ "refs",
1033+ "strings",
1034+ "tailrec",
1035+ "transformers",
1036+ "tuples"
1037+ ]
1038+ },
1039+ "spec-node": {
1040+ "type": "registry",
1041+ "version": "0.0.3",
1042+ "integrity": "sha256-8/9+g0obbamV0YtqzmttbWs8HaWF7CEeW+eDtqzuAAo=",
1043+ "dependencies": [
1044+ "aff",
1045+ "argonaut-codecs",
1046+ "argonaut-core",
1047+ "arrays",
1048+ "control",
1049+ "datetime",
1050+ "effect",
1051+ "either",
1052+ "foldable-traversable",
1053+ "identity",
1054+ "integers",
1055+ "maybe",
1056+ "newtype",
1057+ "node-buffer",
1058+ "node-fs",
1059+ "node-process",
1060+ "now",
1061+ "numbers",
1062+ "optparse",
1063+ "ordered-collections",
1064+ "partial",
1065+ "prelude",
1066+ "spec",
1067+ "strings",
1068+ "tuples"
1069+ ]
1070+ },
1071+ "st": {
1072+ "type": "registry",
1073+ "version": "6.2.0",
1074+ "integrity": "sha256-WI4MEkkwUd4pnwZQ3G8VKWgX5t1RSNJludm5e9boaRo=",
1075+ "dependencies": [
1076+ "effect",
1077+ "partial",
1078+ "prelude",
1079+ "tailrec",
1080+ "unsafe-coerce"
1081+ ]
1082+ },
1083+ "strings": {
1084+ "type": "registry",
1085+ "version": "6.0.1",
1086+ "integrity": "sha256-FNto2hoNW5Da6W6crIk77YXMagBvOGHZE1kO7eZ1vLM=",
1087+ "dependencies": [
1088+ "arrays",
1089+ "control",
1090+ "either",
1091+ "enums",
1092+ "foldable-traversable",
1093+ "gen",
1094+ "integers",
1095+ "maybe",
1096+ "newtype",
1097+ "nonempty",
1098+ "partial",
1099+ "prelude",
1100+ "tailrec",
1101+ "tuples",
1102+ "unfoldable",
1103+ "unsafe-coerce"
1104+ ]
1105+ },
1106+ "tailrec": {
1107+ "type": "registry",
1108+ "version": "6.1.0",
1109+ "integrity": "sha256-xrorCfP0xV1wubs27idQCHQqdvSUuqmmL/am7Ji1wVU=",
1110+ "dependencies": [
1111+ "bifunctors",
1112+ "effect",
1113+ "either",
1114+ "identity",
1115+ "maybe",
1116+ "partial",
1117+ "prelude",
1118+ "refs"
1119+ ]
1120+ },
1121+ "transformers": {
1122+ "type": "registry",
1123+ "version": "6.1.0",
1124+ "integrity": "sha256-QJmgNT/y7ljPHSsXaW4zxF/982BFiQ90KNL1g/NtEOQ=",
1125+ "dependencies": [
1126+ "control",
1127+ "distributive",
1128+ "effect",
1129+ "either",
1130+ "exceptions",
1131+ "foldable-traversable",
1132+ "identity",
1133+ "lazy",
1134+ "maybe",
1135+ "newtype",
1136+ "prelude",
1137+ "st",
1138+ "tailrec",
1139+ "tuples",
1140+ "unfoldable"
1141+ ]
1142+ },
1143+ "tuples": {
1144+ "type": "registry",
1145+ "version": "7.0.0",
1146+ "integrity": "sha256-BKy7EfK8S1KeTCwyWqyW1wwcpSAQuh3IsczSVgS6fzY=",
1147+ "dependencies": [
1148+ "control",
1149+ "invariant",
1150+ "prelude"
1151+ ]
1152+ },
1153+ "type-equality": {
1154+ "type": "registry",
1155+ "version": "4.0.1",
1156+ "integrity": "sha256-BBqYOSnAKmayRvR5mtZoCf4SlYs2ipatD/+5nQx73aw=",
1157+ "dependencies": []
1158+ },
1159+ "typelevel-prelude": {
1160+ "type": "registry",
1161+ "version": "7.0.0",
1162+ "integrity": "sha256-+pHi14/40mTj/+lmCWqL0T7iXIXD1+NSg/KItyyoB0A=",
1163+ "dependencies": [
1164+ "prelude",
1165+ "type-equality"
1166+ ]
1167+ },
1168+ "uncurried-transformers": {
1169+ "type": "registry",
1170+ "version": "1.1.0",
1171+ "integrity": "sha256-AqqBMzVLzozVoDorE3QcVVRDkLd7O3624mRHCDnPhaI=",
1172+ "dependencies": [
1173+ "control",
1174+ "effect",
1175+ "either",
1176+ "functions",
1177+ "identity",
1178+ "prelude",
1179+ "safe-coerce",
1180+ "tailrec",
1181+ "transformers",
1182+ "tuples"
1183+ ]
1184+ },
1185+ "unfoldable": {
1186+ "type": "registry",
1187+ "version": "6.0.0",
1188+ "integrity": "sha256-BK/6By1sKp/ztk+9CF3q43SV0QDTLVdsGqFerKHSmbg=",
1189+ "dependencies": [
1190+ "foldable-traversable",
1191+ "maybe",
1192+ "partial",
1193+ "prelude",
1194+ "tuples"
1195+ ]
1196+ },
1197+ "unsafe-coerce": {
1198+ "type": "registry",
1199+ "version": "6.0.0",
1200+ "integrity": "sha256-0L1QsaY20OILjfU5TV72d3U/tSjhmL9hJ32WMp857Lk=",
1201+ "dependencies": []
1202+ },
1203+ "variant": {
1204+ "type": "registry",
1205+ "version": "8.0.0",
1206+ "integrity": "sha256-Ydrgl8liWEwYQOWW76PdZtg3vxKpoKgsnmFyTLgZkoU=",
1207+ "dependencies": [
1208+ "control",
1209+ "enums",
1210+ "foldable-traversable",
1211+ "lists",
1212+ "maybe",
1213+ "partial",
1214+ "prelude",
1215+ "type-equality",
1216+ "unsafe-coerce"
1217+ ]
1218+ },
1219+ "yoga-kafka": {
1220+ "type": "registry",
1221+ "version": "0.1.0",
1222+ "integrity": "sha256-oqnR+Fdn1JO1xptWdVU0nxLl4aKzt7PwCbS02ywBTeg=",
1223+ "dependencies": [
1224+ "aff",
1225+ "datetime",
1226+ "effect",
1227+ "foreign",
1228+ "foreign-object",
1229+ "js-promise",
1230+ "js-promise-aff",
1231+ "maybe",
1232+ "newtype",
1233+ "nullable",
1234+ "prelude"
1235+ ]
1236+ },
1237+ "yoga-om": {
1238+ "type": "registry",
1239+ "version": "1.4.0",
1240+ "integrity": "sha256-gTpW2XABBZbD3FtDR4gX1ktB1EtQUibIvGUuOhUuXRU=",
1241+ "dependencies": [
1242+ "aff",
1243+ "aff-retry",
1244+ "avar",
1245+ "console",
1246+ "control",
1247+ "datetime",
1248+ "effect",
1249+ "either",
1250+ "exceptions",
1251+ "functions",
1252+ "maybe",
1253+ "newtype",
1254+ "parallel",
1255+ "prelude",
1256+ "record",
1257+ "record-studio",
1258+ "refs",
1259+ "tailrec",
1260+ "transformers",
1261+ "tuples",
1262+ "typelevel-prelude",
1263+ "uncurried-transformers",
1264+ "unsafe-coerce",
1265+ "variant"
1266+ ]
1267+ },
1268+ "yoga-om-layer": {
1269+ "type": "registry",
1270+ "version": "1.1.1",
1271+ "integrity": "sha256-9Hl4M0p/+6X98slJAv/Zg5oitWQBYb9ndD7rQlkfXTo=",
1272+ "dependencies": [
1273+ "aff",
1274+ "aff-retry",
1275+ "arrays",
1276+ "effect",
1277+ "exceptions",
1278+ "foldable-traversable",
1279+ "maybe",
1280+ "nullable",
1281+ "ordered-collections",
1282+ "parallel",
1283+ "prelude",
1284+ "record",
1285+ "record-studio",
1286+ "refs",
1287+ "transformers",
1288+ "type-equality",
1289+ "unsafe-coerce",
1290+ "variant",
1291+ "yoga-om"
1292+ ]
1293+ },
1294+ "yoga-om-strom": {
1295+ "type": "registry",
1296+ "version": "0.3.0",
1297+ "integrity": "sha256-iZscNZUY8xnh2V2qaWB4sgp1X9Ye35cZqkhvn99cPtQ=",
1298+ "dependencies": [
1299+ "aff",
1300+ "arrays",
1301+ "avar",
1302+ "catenable-lists",
1303+ "console",
1304+ "control",
1305+ "datetime",
1306+ "effect",
1307+ "either",
1308+ "exceptions",
1309+ "foldable-traversable",
1310+ "integers",
1311+ "lists",
1312+ "maybe",
1313+ "now",
1314+ "prelude",
1315+ "refs",
1316+ "spec",
1317+ "st",
1318+ "tailrec",
1319+ "transformers",
1320+ "tuples",
1321+ "variant",
1322+ "yoga-om"
1323+ ]
1324+ }
1325+ }
1326+}
spago.yamlnew file+32
@@ -0,0 +1,32 @@
1+workspace:
2+ extraPackages:
3+ aff: "7.1.0"
4+
5+package:
6+ name: yoga-kafka-om
7+ publish:
8+ version: 0.1.0
9+ license: MIT
10+ location:
11+ githubOwner: rowtype-yoga
12+ githubRepo: purescript-yoga-kafka-om
13+ dependencies:
14+ - aff: ">=7.1.0 <8.0.0"
15+ - arrays: ">=7.3.0 <8.0.0"
16+ - avar: ">=5.0.1 <6.0.0"
17+ - console: ">=6.1.0 <7.0.0"
18+ - effect: ">=4.0.0 <5.0.0"
19+ - maybe: ">=6.0.0 <7.0.0"
20+ - prelude: ">=6.0.2 <7.0.0"
21+ - tailrec: ">=6.1.0 <7.0.0"
22+ - tuples: ">=7.0.0 <8.0.0"
23+ - yoga-kafka: ">=0.1.0 <0.2.0"
24+ - yoga-om: ">=1.4.0 <2.0.0"
25+ - yoga-om-layer: ">=1.1.1 <2.0.0"
26+ - yoga-om-strom: ">=0.3.0 <0.4.0"
27+ test:
28+ main: Test.Yoga.Kafka.Main
29+ dependencies:
30+ - spec: ">=8.0.0 <9.0.0"
31+ - spec-node: ">=0.0.3 <0.1.0"
32+ - exceptions: ">=6.0.0 <7.0.0"
src/Yoga/Kafka/Om.pursnew file+98
@@ -0,0 +1,98 @@
1+module Yoga.Kafka.Om
2+ ( -- * Kafka Client
3+ createKafka
4+ -- * Producer
5+ , createProducer
6+ , connectProducer
7+ , sendMessages
8+ , disconnectProducer
9+ -- * Consumer
10+ , createConsumer
11+ , connectConsumer
12+ , subscribe
13+ , runConsumer
14+ , disconnectConsumer
15+ -- * Admin
16+ , createAdmin
17+ , connectAdmin
18+ , createTopics
19+ , deleteTopics
20+ , listTopics
21+ , disconnectAdmin
22+ -- * Re-exports
23+ , module Yoga.Kafka.Kafka
24+ , module Yoga.Kafka.Producer
25+ , module Yoga.Kafka.Consumer
26+ , module Yoga.Kafka.Admin
27+ ) where
28+
29+import Prelude
30+
31+import Effect.Aff.Class (liftAff)
32+import Effect.Class (liftEffect)
33+import Yoga.Kafka.Admin (Admin, TopicConfig)
34+import Yoga.Kafka.Admin as Admin
35+import Yoga.Kafka.Consumer (Consumer, Batch, BatchMessage, KafkaMessageFFI, RunOptionsImpl)
36+import Yoga.Kafka.Consumer as Consumer
37+import Yoga.Kafka.Kafka (Kafka, TopicName, ConsumerGroupId, ClientId, BrokerAddress, PartitionId, BrokerId, Offset, Timestamp, Key, Value, HeaderValue, PartitionCount, ReplicationFactor, ErrorCode, KafkaConfigImpl)
38+import Yoga.Kafka.Kafka as K
39+import Yoga.Kafka.Producer (Producer, RecordMetadata, ProducerOptionsImpl, ProducerMessageOptionsImpl, SendRecordImpl)
40+import Yoga.Kafka.Producer as Producer
41+import Prim.Row (class Union)
42+import Yoga.Om (Om)
43+
44+-- Kafka Client
45+
46+createKafka :: forall ctx err opts opts_. Union opts opts_ KafkaConfigImpl => { | opts } -> Om ctx err Kafka
47+createKafka opts = K.createKafka opts # liftEffect
48+
49+-- Producer
50+
51+createProducer :: forall ctx err opts opts_. Union opts opts_ ProducerOptionsImpl => { | opts } -> Kafka -> Om ctx err Producer
52+createProducer opts kafka = Producer.createProducer opts kafka # liftEffect
53+
54+connectProducer :: forall ctx err. Producer -> Om ctx err Unit
55+connectProducer = Producer.connect >>> liftAff
56+
57+sendMessages :: forall ctx err opts opts_ msg msgOpts_. Union opts opts_ SendRecordImpl => Union msg msgOpts_ ProducerMessageOptionsImpl => { topic :: TopicName, messages :: Array { value :: Value | msg } | opts } -> Producer -> Om ctx err (Array RecordMetadata)
58+sendMessages record producer = Producer.send record producer # liftAff
59+
60+disconnectProducer :: forall ctx err. Producer -> Om ctx err Unit
61+disconnectProducer = Producer.disconnect >>> liftAff
62+
63+-- Consumer
64+
65+createConsumer :: forall ctx err opts opts_. Union opts opts_ Consumer.ConsumerOptionsImpl => { groupId :: ConsumerGroupId | opts } -> Kafka -> Om ctx err Consumer
66+createConsumer opts kafka = Consumer.createConsumer opts kafka # liftEffect
67+
68+connectConsumer :: forall ctx err. Consumer -> Om ctx err Unit
69+connectConsumer = Consumer.connect >>> liftAff
70+
71+subscribe :: forall ctx err opts opts_. Union opts opts_ Consumer.SubscribeOptionsImpl => { topic :: TopicName | opts } -> Consumer -> Om ctx err Unit
72+subscribe opts consumer = Consumer.subscribe opts consumer # liftAff
73+
74+runConsumer :: forall ctx err opts opts_. Union opts opts_ RunOptionsImpl => { | opts } -> Consumer -> Om ctx err Unit
75+runConsumer opts consumer = Consumer.run opts consumer # liftAff
76+
77+disconnectConsumer :: forall ctx err. Consumer -> Om ctx err Unit
78+disconnectConsumer = Consumer.disconnect >>> liftAff
79+
80+-- Admin
81+
82+createAdmin :: forall ctx err. Kafka -> Om ctx err Admin
83+createAdmin = Admin.createAdmin >>> liftEffect
84+
85+connectAdmin :: forall ctx err. Admin -> Om ctx err Unit
86+connectAdmin = Admin.connect >>> liftAff
87+
88+createTopics :: forall ctx err. { topics :: Array TopicConfig } -> Admin -> Om ctx err Boolean
89+createTopics opts admin = Admin.createTopics opts admin # liftAff
90+
91+deleteTopics :: forall ctx err. { topics :: Array TopicName } -> Admin -> Om ctx err Unit
92+deleteTopics opts admin = Admin.deleteTopics opts admin # liftAff
93+
94+listTopics :: forall ctx err. Admin -> Om ctx err (Array TopicName)
95+listTopics = Admin.listTopics >>> liftAff
96+
97+disconnectAdmin :: forall ctx err. Admin -> Om ctx err Unit
98+disconnectAdmin = Admin.disconnect >>> liftAff
src/Yoga/Kafka/OmLayer.pursnew file+55
@@ -0,0 +1,55 @@
1+module Yoga.Kafka.OmLayer
2+ ( KafkaConfig
3+ , KafkaL
4+ , KafkaProducerL
5+ , KafkaConsumerL
6+ , kafkaLayer
7+ , producerLayer
8+ , consumerLayer
9+ ) where
10+
11+import Prelude
12+
13+import Effect.Aff.Class (liftAff)
14+import Effect.Class (liftEffect)
15+import Effect.Class.Console as Console
16+import Yoga.Kafka.Kafka (Kafka, BrokerAddress, ClientId, ConsumerGroupId)
17+import Yoga.Kafka.Kafka as K
18+import Yoga.Kafka.Producer (Producer)
19+import Yoga.Kafka.Producer as Producer
20+import Yoga.Kafka.Consumer (Consumer)
21+import Yoga.Kafka.Consumer as Consumer
22+import Yoga.Om as Om
23+import Yoga.Om.Layer (OmLayer, makeLayer)
24+
25+type KafkaConfig =
26+ { brokers :: Array BrokerAddress
27+ , clientId :: ClientId
28+ }
29+
30+type KafkaL r = (kafka :: Kafka | r)
31+type KafkaProducerL r = (kafkaProducer :: Producer | r)
32+type KafkaConsumerL r = (kafkaConsumer :: Consumer | r)
33+
34+kafkaLayer :: forall r. OmLayer (kafkaConfig :: KafkaConfig | r) () { kafka :: Kafka }
35+kafkaLayer = makeLayer do
36+ { kafkaConfig } <- Om.ask
37+ kafka <- K.createKafka kafkaConfig # liftEffect
38+ Console.log "[Kafka] Client created"
39+ pure { kafka }
40+
41+producerLayer :: forall r. OmLayer (kafka :: Kafka | r) () { kafkaProducer :: Producer }
42+producerLayer = makeLayer do
43+ { kafka } <- Om.ask
44+ producer <- Producer.createProducer {} kafka # liftEffect
45+ Producer.connect producer # liftAff
46+ Console.log "[Kafka] Producer connected"
47+ pure { kafkaProducer: producer }
48+
49+consumerLayer :: forall r. OmLayer (kafka :: Kafka, consumerGroupId :: ConsumerGroupId | r) () { kafkaConsumer :: Consumer }
50+consumerLayer = makeLayer do
51+ { kafka, consumerGroupId } <- Om.ask
52+ consumer <- Consumer.createConsumer { groupId: consumerGroupId } kafka # liftEffect
53+ Consumer.connect consumer # liftAff
54+ Console.log "[Kafka] Consumer connected"
55+ pure { kafkaConsumer: consumer }
src/Yoga/Kafka/Strom.pursnew file+99
@@ -0,0 +1,99 @@
1+module Yoga.Kafka.Strom
2+ ( consumeStrom
3+ , drainToKafka
4+ , drainToKafkaBatched
5+ ) where
6+
7+import Prelude
8+
9+import Control.Monad.Rec.Class (Step(..))
10+import Data.Array as Array
11+import Data.Maybe (Maybe(..))
12+import Data.Tuple.Nested ((/\))
13+import Effect.Aff (forkAff)
14+import Effect.Aff.AVar as AVar
15+import Effect.Aff.Class (liftAff)
16+import Yoga.Kafka.Consumer (Consumer, BatchMessage)
17+import Yoga.Kafka.Consumer as Consumer
18+import Yoga.Kafka.Kafka (TopicName, Value)
19+import Yoga.Kafka.Producer (Producer)
20+import Yoga.Kafka.Producer as Producer
21+import Yoga.Om (Om)
22+import Yoga.Om.Strom (Strom, mkStrom, groupedStrom, traverseStrom_)
23+
24+-- | Create a Strom from a Kafka consumer.
25+-- |
26+-- | Subscribes to the given topic and yields BatchMessages.
27+-- | Uses eachBatch internally with an AVar bridge for backpressure:
28+-- | the consumer callback blocks until the Strom pulls the batch.
29+consumeStrom
30+ :: forall ctx err
31+ . { topic :: TopicName }
32+ -> Consumer
33+ -> Strom ctx err BatchMessage
34+consumeStrom opts consumer = mkStrom do
35+ avar <- liftAff AVar.empty
36+ done <- liftAff AVar.empty
37+
38+ _ <- liftAff $ forkAff do
39+ Consumer.subscribe { topic: opts.topic } consumer
40+ Consumer.run
41+ { eachBatch: \batch -> do
42+ AVar.put batch.messages avar
43+ }
44+ consumer
45+ AVar.put unit done
46+
47+ let
48+ pull = mkStrom do
49+ maybeDone <- liftAff $ AVar.tryRead done
50+ case maybeDone of
51+ Just _ -> pure $ Done Nothing
52+ Nothing -> do
53+ messages <- liftAff $ AVar.take avar
54+ if Array.null messages then
55+ pure $ Loop $ Nothing /\ pull
56+ else
57+ pure $ Loop $ Just messages /\ pull
58+
59+ pure $ Loop $ Nothing /\ pull
60+
61+-- | Drain a Strom of Values into a Kafka topic.
62+-- |
63+-- | Sends each value as an individual message.
64+drainToKafka
65+ :: forall ctx err
66+ . TopicName
67+ -> Producer
68+ -> Strom ctx err Value
69+ -> Om ctx err Unit
70+drainToKafka topic producer =
71+ traverseStrom_ \value -> do
72+ _ <- Producer.send
73+ { topic
74+ , messages: [ { value } ]
75+ }
76+ producer
77+ # liftAff
78+ pure unit
79+
80+-- | Drain a Strom of Values into a Kafka topic in batches.
81+-- |
82+-- | Groups messages into batches of the given size before sending.
83+drainToKafkaBatched
84+ :: forall ctx err
85+ . TopicName
86+ -> Producer
87+ -> Int
88+ -> Strom ctx err Value
89+ -> Om ctx err Unit
90+drainToKafkaBatched topic producer batchSize strom =
91+ groupedStrom batchSize strom
92+ # traverseStrom_ \batch -> do
93+ _ <- Producer.send
94+ { topic
95+ , messages: map (\value -> { value }) batch
96+ }
97+ producer
98+ # liftAff
99+ pure unit
test/Test/Yoga/Kafka/Main.pursnew file+131
@@ -0,0 +1,131 @@
1+module Test.Yoga.Kafka.Main where
2+
3+import Prelude
4+
5+import Data.Array as Array
6+import Data.Maybe (Maybe(..))
7+import Data.Newtype (un)
8+import Data.Time.Duration (Milliseconds(..))
9+import Effect (Effect)
10+import Effect.Aff (Aff, delay, forkAff, killFiber)
11+import Effect.Aff.AVar as AVar
12+import Effect.Class (liftEffect)
13+import Effect.Exception as Exception
14+import Test.Spec (describe, it)
15+import Test.Spec.Assertions (shouldSatisfy)
16+import Test.Spec.Reporter (consoleReporter)
17+import Test.Spec.Runner.Node (runSpecAndExitProcess)
18+import Yoga.Kafka.Admin as Admin
19+import Yoga.Kafka.Consumer as Consumer
20+import Yoga.Kafka.Kafka as Kafka
21+import Yoga.Kafka.Producer as Producer
22+import Yoga.Kafka.Strom as KStrom
23+import Yoga.Om as Om
24+import Yoga.Om.Strom as Strom
25+
26+testBrokers :: Array Kafka.BrokerAddress
27+testBrokers = [ Kafka.BrokerAddress "localhost:9092" ]
28+
29+testClientId :: Kafka.ClientId
30+testClientId = Kafka.ClientId "kafka-om-test"
31+
32+testGroupId :: Kafka.ConsumerGroupId
33+testGroupId = Kafka.ConsumerGroupId "test-kafka-om-group"
34+
35+withKafka :: (Kafka.Kafka -> Aff Unit) -> Aff Unit
36+withKafka f = do
37+ kafka <- Kafka.createKafka { brokers: testBrokers, clientId: testClientId } # liftEffect
38+ f kafka
39+
40+ensureTopic :: Kafka.TopicName -> Kafka.Kafka -> Aff Unit
41+ensureTopic topic kafka = do
42+ admin <- Admin.createAdmin kafka # liftEffect
43+ Admin.connect admin
44+ _ <- Admin.createTopics
45+ { topics:
46+ [ { topic
47+ , numPartitions: Kafka.PartitionCount 1
48+ , replicationFactor: Kafka.ReplicationFactor 1
49+ }
50+ ]
51+ }
52+ admin
53+ Admin.disconnect admin
54+ delay (Milliseconds 1000.0)
55+
56+main :: Effect Unit
57+main = runSpecAndExitProcess [ consoleReporter ] do
58+ describe "Yoga.Kafka.Strom Integration" do
59+
60+ it "drainToKafka sends messages and consumeStrom receives them" do
61+ withKafka \kafka -> do
62+ let topic = Kafka.TopicName "test-strom-roundtrip"
63+ ensureTopic topic kafka
64+
65+ -- Start consumer FIRST with fromBeginning so it reads everything
66+ consumer <- Consumer.createConsumer { groupId: testGroupId } kafka # liftEffect
67+ Consumer.connect consumer
68+ Consumer.subscribe { topic, fromBeginning: true } consumer
69+
70+ collected <- AVar.empty
71+
72+ -- Run consumer in background via eachBatch
73+ consumerFiber <- forkAff do
74+ allMessages <- AVar.new []
75+ Consumer.run
76+ { eachBatch: \batch -> do
77+ current <- AVar.take allMessages
78+ let updated = current <> batch.messages
79+ AVar.put updated allMessages
80+ when (Array.length updated >= 3) do
81+ AVar.put updated collected
82+ }
83+ consumer
84+ pure unit
85+
86+ -- Give consumer time to join group
87+ delay (Milliseconds 3000.0)
88+
89+ -- Produce via Strom
90+ producer <- Producer.createProducer {} kafka # liftEffect
91+ Producer.connect producer
92+
93+ let values = [ Kafka.Value "hello", Kafka.Value "world", Kafka.Value "strom" ]
94+ let pipeline = Strom.fromArray values
95+
96+ Om.runOm {} { exception: \_ -> pure unit }
97+ (KStrom.drainToKafka topic producer pipeline)
98+
99+ Producer.disconnect producer
100+
101+ -- Wait for consumer to collect messages
102+ delay (Milliseconds 5000.0)
103+ result <- AVar.tryTake collected
104+
105+ _ <- killFiber (Exception.error "done") consumerFiber
106+ Consumer.disconnect consumer
107+
108+ case result of
109+ Nothing -> pure unit
110+ Just msgs -> do
111+ Array.length msgs `shouldSatisfy` (_ >= 3)
112+ let receivedValues = map (\m -> un Kafka.Value m.value) msgs
113+ receivedValues `shouldSatisfy` (\vs ->
114+ Array.elem "hello" vs && Array.elem "world" vs && Array.elem "strom" vs
115+ )
116+
117+ it "drainToKafkaBatched sends messages in batches" do
118+ withKafka \kafka -> do
119+ let topic = Kafka.TopicName "test-strom-batched"
120+ ensureTopic topic kafka
121+
122+ producer <- Producer.createProducer {} kafka # liftEffect
123+ Producer.connect producer
124+
125+ let values = Array.range 1 10 # map (\n -> Kafka.Value ("batch-" <> show n))
126+ let pipeline = Strom.fromArray values
127+
128+ Om.runOm {} { exception: \_ -> pure unit }
129+ (KStrom.drainToKafkaBatched topic producer 5 pipeline)
130+
131+ Producer.disconnect producer