Skip to content

Commit 344b3f7

Browse files
Add store-backed FlowStore to datasourcex-util (#36)
Adds two new utility classes `FlowStore` — A store-backed map that gives you a live, version-stamped delta stream plus read-through access to current values, with a bounded `Caffeine` cache as the hot set. Owners write through a transaction (`MutableFlowStore`) and consumers converge by following the stream and reading through on a miss. `SharedFlowCache` — A keyed cache of `SharedFlow`s that shares one upstream collection per key across its subscribers. Each key is evicted once its upstream ends (completes, errors, or loses all subscribers), so the cache can't grow without bound.
1 parent 79248f7 commit 344b3f7

38 files changed

Lines changed: 2324 additions & 10 deletions

util/README.md

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,69 @@
33
Utility classes and functions commonly used in DataSource integrations, such as efficiently observable Maps, and stream
44
transformations.
55

6-
@see com.caplin.integration.datasourcex.util.flow.FlowMap
6+
## Key components
7+
8+
### Observable maps and shared flows
9+
10+
| Component | Description |
11+
| --- | --- |
12+
| `FlowMap` / `MutableFlowMap` | A `Map` that is also observable via `asFlow`, `asFlowWithState`, and per-key `valueFlow`. Built with `mutableFlowMapOf` / `toMutableFlowMap`, or collected from an event stream with `flowMapIn`. |
13+
| `CompletingSharedFlow` / `MutableCompletingSharedFlow` | A `SharedFlow` variant that also propagates completion and error events to subscribers. Built with `shareInCompleting`. |
14+
| `SharedFlowCache` | Keyed cache of `SharedFlow`s, sharing one upstream collection per key and evicting a key once its upstream ends so the cache can't grow unbounded. Built with `sharedFlowCache(...)`, or `completingSharedFlowCache(...)` for a `CompletingSharedFlowCache` that propagates completion/errors. |
15+
| `CompletingSharedFlowCache` / `LoadingCompletingSharedFlowCache` | Keyed cache of `CompletingSharedFlow`s, sharing one underlying collection per key. |
16+
17+
### Stores
18+
19+
| Component | Description |
20+
| --- | --- |
21+
| `FlowStore` / `MutableFlowStore` | A store-backed map exposing a delta-only stream plus a read-through, Caffeine-bounded cache. Built with `flowStore` / `flowStoreIn` / `mutableFlowStore`. |
22+
| `AsyncFlowStore` / `AsyncMutableFlowStore` | Suspending views of the stores whose reads/writes dispatch the store I/O themselves. |
23+
| `Store` / `StoreReader` / `StoreWriter` | The SPI you implement to back a `MutableFlowStore`; mutations enlist on the caller's transaction and publish on commit. |
24+
| `TxContext` / `Versioned` | The transaction handle a write enlists on, and a value paired with the store-assigned version. |
25+
26+
### Flow operators
27+
28+
| Operator | Description |
29+
| --- | --- |
30+
| `bufferingDebounce` | Buffers elements until a quiet period elapses, then emits them as a `List`. |
31+
| `throttleLatest` | Emits at most once per interval, keeping the latest value and dropping older ones. |
32+
| `flatMapFirst` | Applies a function to the first element together with the entire upstream flow. |
33+
| `demultiplexBy` | Groups elements by a key selector and processes each group's sub-flow. |
34+
| `retryWithExponentialBackoff` | Retries the upstream on error with an exponential delay between attempts. |
35+
| `cast` | Casts a `Flow<*>` to a `Flow<T>`. |
36+
| `timeoutFirst` / `timeoutFirstOrNull` / `timeoutFirstOrDefault` | Errors / emits null / emits a default if no first element arrives within a timeout. |
37+
| `materialize` / `dematerialize` (and `*Unboxed`) | Convert between flow values and `ValueOrCompletion` events. |
38+
39+
### Event models and folds
40+
41+
| Component | Description |
42+
| --- | --- |
43+
| `MapEvent` / `SimpleMapEvent` / `SetEvent` / `VersionedMapEvent` | Sealed event types describing map and set mutations (with or without old values / versions). |
44+
| `FlowMapStreamEvent` / `ValueOrCompletion` | Materialised stream events: initial-state-plus-deltas, and value-or-terminal-signal. |
45+
| `runningFoldToMap*` / `runningFoldToSet` | Fold a delta stream into a live `Flow` of map / set snapshots. |
46+
| `conflateKeys` / `toEvents` | Collapse per-key updates, and expand a collection stream into entry events. |
47+
48+
### Serialization
49+
50+
| Component | Description |
51+
| --- | --- |
52+
| `registerDataSourceSerializers` / `registerPersistentCollectionSerializers` | Register Fory serializers for the event types and persistent collections. |
53+
| `DataSourceModule`, `registerDataSourceModule` / `addDataSourceModule` | Jackson 2 / Jackson 3 modules that serialize the event types without annotations. |
54+
| `Jackson2JsonHandler` / `Jackson3JsonHandler` | `JsonHandler` implementations backed by a Jackson `ObjectMapper`. |
55+
56+
### DataSource and general utilities
57+
58+
| Component | Description |
59+
| --- | --- |
60+
| `AntPatternNamespace` | A `Namespace` matching subjects by Ant-style patterns, with path-variable extraction. |
61+
| `SimpleDataSourceFactory` / `SimpleDataSourceConfig` | Build a `DataSource` from a simplified config for tests and examples. |
62+
| `KLogger` | An slf4j `Logger` wrapper with lazily-evaluated message lambdas. |
63+
| `ReadWriteLock` | A non-reentrant suspending read/write lock. |
64+
| `withTimeout` | A coroutine timeout that throws `TimeoutException` rather than a `CancellationException`. |
65+
66+
@see com.caplin.integration.datasourcex.util.flow.FlowMap
67+
@see com.caplin.integration.datasourcex.util.store.MutableFlowStore
68+
69+
Participating in an application-owned jOOQ transaction:
70+
71+
@sample samples.StoreSamples.jooqSample

util/api/datasourcex-util.api

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,9 +185,11 @@ public abstract interface class com/caplin/integration/datasourcex/util/flow/Flo
185185
}
186186

187187
public final class com/caplin/integration/datasourcex/util/flow/FlowMapKt {
188+
public static final fun flowMapIn (Lkotlinx/coroutines/flow/Flow;Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
188189
public static final fun mutableFlowMapOf ()Lcom/caplin/integration/datasourcex/util/flow/MutableFlowMap;
189190
public static final fun mutableFlowMapOf ([Lkotlin/Pair;)Lcom/caplin/integration/datasourcex/util/flow/MutableFlowMap;
190191
public static final fun runningFoldToMapFlowMapStreamEvent (Lkotlinx/coroutines/flow/Flow;)Lkotlinx/coroutines/flow/Flow;
192+
public static final fun simpleFlowMapIn (Lkotlinx/coroutines/flow/Flow;Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
191193
public static final fun simpleToFlowMapIn (Lkotlinx/coroutines/flow/Flow;Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
192194
public static final fun toFlowMapIn (Lkotlinx/coroutines/flow/Flow;Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
193195
public static final fun toMutableFlowMap (Ljava/util/Map;)Lcom/caplin/integration/datasourcex/util/flow/MutableFlowMap;
@@ -371,6 +373,17 @@ public final class com/caplin/integration/datasourcex/util/flow/SetEventKt {
371373
public static final fun toEvents (Lkotlinx/coroutines/flow/Flow;)Lkotlinx/coroutines/flow/Flow;
372374
}
373375

376+
public final class com/caplin/integration/datasourcex/util/flow/SharedFlowCache {
377+
public final fun get (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Lkotlinx/coroutines/flow/SharedFlow;
378+
}
379+
380+
public final class com/caplin/integration/datasourcex/util/flow/SharedFlowCacheKt {
381+
public static final fun completingSharedFlowCache (Lkotlinx/coroutines/CoroutineScope;Lkotlinx/coroutines/flow/SharingStarted;I)Lcom/caplin/integration/datasourcex/util/flow/CompletingSharedFlowCache;
382+
public static synthetic fun completingSharedFlowCache$default (Lkotlinx/coroutines/CoroutineScope;Lkotlinx/coroutines/flow/SharingStarted;IILjava/lang/Object;)Lcom/caplin/integration/datasourcex/util/flow/CompletingSharedFlowCache;
383+
public static final fun sharedFlowCache (Lkotlinx/coroutines/CoroutineScope;Lkotlinx/coroutines/flow/SharingStarted;I)Lcom/caplin/integration/datasourcex/util/flow/SharedFlowCache;
384+
public static synthetic fun sharedFlowCache$default (Lkotlinx/coroutines/CoroutineScope;Lkotlinx/coroutines/flow/SharingStarted;IILjava/lang/Object;)Lcom/caplin/integration/datasourcex/util/flow/SharedFlowCache;
385+
}
386+
374387
public abstract interface class com/caplin/integration/datasourcex/util/flow/SimpleMapEvent {
375388
}
376389

@@ -464,6 +477,39 @@ public final class com/caplin/integration/datasourcex/util/flow/ValueOrCompletio
464477
public static final fun materializeUnboxed (Lkotlinx/coroutines/flow/Flow;)Lkotlinx/coroutines/flow/Flow;
465478
}
466479

480+
public abstract interface class com/caplin/integration/datasourcex/util/flow/VersionedMapEvent {
481+
public abstract fun getKey ()Ljava/lang/Object;
482+
public abstract fun getVersion ()J
483+
}
484+
485+
public final class com/caplin/integration/datasourcex/util/flow/VersionedMapEvent$Removed : com/caplin/integration/datasourcex/util/flow/VersionedMapEvent {
486+
public fun <init> (Ljava/lang/Object;J)V
487+
public final fun component1 ()Ljava/lang/Object;
488+
public final fun component2 ()J
489+
public final fun copy (Ljava/lang/Object;J)Lcom/caplin/integration/datasourcex/util/flow/VersionedMapEvent$Removed;
490+
public static synthetic fun copy$default (Lcom/caplin/integration/datasourcex/util/flow/VersionedMapEvent$Removed;Ljava/lang/Object;JILjava/lang/Object;)Lcom/caplin/integration/datasourcex/util/flow/VersionedMapEvent$Removed;
491+
public fun equals (Ljava/lang/Object;)Z
492+
public fun getKey ()Ljava/lang/Object;
493+
public fun getVersion ()J
494+
public fun hashCode ()I
495+
public fun toString ()Ljava/lang/String;
496+
}
497+
498+
public final class com/caplin/integration/datasourcex/util/flow/VersionedMapEvent$Upsert : com/caplin/integration/datasourcex/util/flow/VersionedMapEvent {
499+
public fun <init> (Ljava/lang/Object;Ljava/lang/Object;J)V
500+
public final fun component1 ()Ljava/lang/Object;
501+
public final fun component2 ()Ljava/lang/Object;
502+
public final fun component3 ()J
503+
public final fun copy (Ljava/lang/Object;Ljava/lang/Object;J)Lcom/caplin/integration/datasourcex/util/flow/VersionedMapEvent$Upsert;
504+
public static synthetic fun copy$default (Lcom/caplin/integration/datasourcex/util/flow/VersionedMapEvent$Upsert;Ljava/lang/Object;Ljava/lang/Object;JILjava/lang/Object;)Lcom/caplin/integration/datasourcex/util/flow/VersionedMapEvent$Upsert;
505+
public fun equals (Ljava/lang/Object;)Z
506+
public fun getKey ()Ljava/lang/Object;
507+
public final fun getValue ()Ljava/lang/Object;
508+
public fun getVersion ()J
509+
public fun hashCode ()I
510+
public fun toString ()Ljava/lang/String;
511+
}
512+
467513
public final class com/caplin/integration/datasourcex/util/serialization/fory/DataSourceModuleKt {
468514
public static final fun registerDataSourceSerializers (Lorg/apache/fory/Fory;Z)Lorg/apache/fory/Fory;
469515
public static synthetic fun registerDataSourceSerializers$default (Lorg/apache/fory/Fory;ZILjava/lang/Object;)Lorg/apache/fory/Fory;
@@ -518,3 +564,100 @@ public final class com/caplin/integration/datasourcex/util/serialization/jackson
518564
public fun toObject (Ltools/jackson/databind/JsonNode;Ljava/lang/Class;)Ljava/lang/Object;
519565
}
520566

567+
public abstract interface class com/caplin/integration/datasourcex/util/store/AsyncFlowStore {
568+
public abstract fun asFlow ()Lkotlinx/coroutines/flow/SharedFlow;
569+
public abstract fun asFlow (Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/flow/Flow;
570+
public static synthetic fun asFlow$default (Lcom/caplin/integration/datasourcex/util/store/AsyncFlowStore;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/flow/Flow;
571+
public abstract fun get (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
572+
public abstract fun valueFlow (Ljava/lang/Object;)Lkotlinx/coroutines/flow/Flow;
573+
}
574+
575+
public final class com/caplin/integration/datasourcex/util/store/AsyncFlowStore$DefaultImpls {
576+
public static synthetic fun asFlow$default (Lcom/caplin/integration/datasourcex/util/store/AsyncFlowStore;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/flow/Flow;
577+
}
578+
579+
public abstract interface class com/caplin/integration/datasourcex/util/store/AsyncMutableFlowStore : com/caplin/integration/datasourcex/util/store/AsyncFlowStore {
580+
public abstract fun get (Ljava/lang/Object;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
581+
public abstract fun put (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
582+
public abstract fun putAll (Ljava/util/Map;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
583+
public abstract fun remove (Ljava/lang/Object;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
584+
}
585+
586+
public abstract interface class com/caplin/integration/datasourcex/util/store/FlowStore {
587+
public abstract fun asFlow ()Lkotlinx/coroutines/flow/SharedFlow;
588+
public abstract fun asFlow (Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/flow/Flow;
589+
public static synthetic fun asFlow$default (Lcom/caplin/integration/datasourcex/util/store/FlowStore;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/flow/Flow;
590+
public abstract fun get (Ljava/lang/Object;)Ljava/lang/Object;
591+
public abstract fun getAsync ()Lcom/caplin/integration/datasourcex/util/store/AsyncFlowStore;
592+
public abstract fun valueFlow (Ljava/lang/Object;)Lkotlinx/coroutines/flow/Flow;
593+
}
594+
595+
public final class com/caplin/integration/datasourcex/util/store/FlowStore$DefaultImpls {
596+
public static synthetic fun asFlow$default (Lcom/caplin/integration/datasourcex/util/store/FlowStore;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/flow/Flow;
597+
}
598+
599+
public final class com/caplin/integration/datasourcex/util/store/FlowStoreKt {
600+
public static final fun flowStore (Lcom/caplin/integration/datasourcex/util/store/StoreReader;Lkotlinx/coroutines/flow/Flow;Lcom/github/benmanes/caffeine/cache/Caffeine;Lkotlinx/coroutines/CoroutineScope;Lkotlinx/coroutines/CoroutineDispatcher;I)Lcom/caplin/integration/datasourcex/util/store/FlowStore;
601+
public static synthetic fun flowStore$default (Lcom/caplin/integration/datasourcex/util/store/StoreReader;Lkotlinx/coroutines/flow/Flow;Lcom/github/benmanes/caffeine/cache/Caffeine;Lkotlinx/coroutines/CoroutineScope;Lkotlinx/coroutines/CoroutineDispatcher;IILjava/lang/Object;)Lcom/caplin/integration/datasourcex/util/store/FlowStore;
602+
public static final fun flowStoreIn (Lkotlinx/coroutines/flow/Flow;Lcom/caplin/integration/datasourcex/util/store/StoreReader;Lcom/github/benmanes/caffeine/cache/Caffeine;Lkotlinx/coroutines/CoroutineScope;Lkotlinx/coroutines/CoroutineDispatcher;I)Lcom/caplin/integration/datasourcex/util/store/FlowStore;
603+
public static synthetic fun flowStoreIn$default (Lkotlinx/coroutines/flow/Flow;Lcom/caplin/integration/datasourcex/util/store/StoreReader;Lcom/github/benmanes/caffeine/cache/Caffeine;Lkotlinx/coroutines/CoroutineScope;Lkotlinx/coroutines/CoroutineDispatcher;IILjava/lang/Object;)Lcom/caplin/integration/datasourcex/util/store/FlowStore;
604+
}
605+
606+
public abstract interface class com/caplin/integration/datasourcex/util/store/MutableFlowStore : com/caplin/integration/datasourcex/util/store/FlowStore {
607+
public abstract fun get (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
608+
public abstract fun getAsync ()Lcom/caplin/integration/datasourcex/util/store/AsyncMutableFlowStore;
609+
public abstract fun put (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V
610+
public abstract fun putAll (Ljava/util/Map;Ljava/lang/Object;)V
611+
public abstract fun remove (Ljava/lang/Object;Ljava/lang/Object;)V
612+
}
613+
614+
public final class com/caplin/integration/datasourcex/util/store/MutableFlowStoreKt {
615+
public static final fun mutableFlowStore (Lcom/caplin/integration/datasourcex/util/store/Store;Lcom/github/benmanes/caffeine/cache/Caffeine;Lkotlinx/coroutines/CoroutineDispatcher;Lkotlin/jvm/functions/Function1;)Lcom/caplin/integration/datasourcex/util/store/MutableFlowStore;
616+
public static synthetic fun mutableFlowStore$default (Lcom/caplin/integration/datasourcex/util/store/Store;Lcom/github/benmanes/caffeine/cache/Caffeine;Lkotlinx/coroutines/CoroutineDispatcher;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lcom/caplin/integration/datasourcex/util/store/MutableFlowStore;
617+
}
618+
619+
public abstract interface class com/caplin/integration/datasourcex/util/store/Store : com/caplin/integration/datasourcex/util/store/StoreReader, com/caplin/integration/datasourcex/util/store/StoreWriter {
620+
}
621+
622+
public final class com/caplin/integration/datasourcex/util/store/Store$DefaultImpls {
623+
public static fun writeAll (Lcom/caplin/integration/datasourcex/util/store/Store;Ljava/util/Map;Lcom/caplin/integration/datasourcex/util/store/TxContext;)Ljava/util/Map;
624+
}
625+
626+
public abstract interface class com/caplin/integration/datasourcex/util/store/StoreReader {
627+
public abstract fun load (Ljava/lang/Object;)Lcom/caplin/integration/datasourcex/util/store/Versioned;
628+
}
629+
630+
public abstract interface class com/caplin/integration/datasourcex/util/store/StoreWriter {
631+
public abstract fun delete (Ljava/lang/Object;Lcom/caplin/integration/datasourcex/util/store/TxContext;)J
632+
public abstract fun load (Ljava/lang/Object;Lcom/caplin/integration/datasourcex/util/store/TxContext;)Lcom/caplin/integration/datasourcex/util/store/Versioned;
633+
public abstract fun write (Ljava/lang/Object;Ljava/lang/Object;Lcom/caplin/integration/datasourcex/util/store/TxContext;)J
634+
public fun writeAll (Ljava/util/Map;Lcom/caplin/integration/datasourcex/util/store/TxContext;)Ljava/util/Map;
635+
}
636+
637+
public final class com/caplin/integration/datasourcex/util/store/StoreWriter$DefaultImpls {
638+
public static fun writeAll (Lcom/caplin/integration/datasourcex/util/store/StoreWriter;Ljava/util/Map;Lcom/caplin/integration/datasourcex/util/store/TxContext;)Ljava/util/Map;
639+
}
640+
641+
public abstract interface class com/caplin/integration/datasourcex/util/store/TxContext {
642+
public abstract fun getTransaction ()Ljava/lang/Object;
643+
public abstract fun onCommitEnd (Lkotlin/jvm/functions/Function0;)V
644+
public fun onRollback (Lkotlin/jvm/functions/Function0;)V
645+
}
646+
647+
public final class com/caplin/integration/datasourcex/util/store/TxContext$DefaultImpls {
648+
public static fun onRollback (Lcom/caplin/integration/datasourcex/util/store/TxContext;Lkotlin/jvm/functions/Function0;)V
649+
}
650+
651+
public final class com/caplin/integration/datasourcex/util/store/Versioned {
652+
public fun <init> (Ljava/lang/Object;J)V
653+
public final fun component1 ()Ljava/lang/Object;
654+
public final fun component2 ()J
655+
public final fun copy (Ljava/lang/Object;J)Lcom/caplin/integration/datasourcex/util/store/Versioned;
656+
public static synthetic fun copy$default (Lcom/caplin/integration/datasourcex/util/store/Versioned;Ljava/lang/Object;JILjava/lang/Object;)Lcom/caplin/integration/datasourcex/util/store/Versioned;
657+
public fun equals (Ljava/lang/Object;)Z
658+
public final fun getValue ()Ljava/lang/Object;
659+
public final fun getVersion ()J
660+
public fun hashCode ()I
661+
public fun toString ()Ljava/lang/String;
662+
}
663+

util/build.gradle.kts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ dependencies {
1515
api("org.jetbrains.kotlinx:kotlinx-coroutines-core")
1616
api("org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm")
1717
api("com.fasterxml.jackson.core:jackson-core")
18+
api("com.github.ben-manes.caffeine:caffeine")
1819
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310")
1920
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
2021
implementation(libs.zjsonpatch)
@@ -32,12 +33,18 @@ dependencies {
3233
compileOnly(libs.fory.core)
3334
compileOnly(libs.fory.kotlin)
3435

36+
// Samples (compiled for Dokka only): show MutableFlowStore participating in a blocking jOOQ
37+
// transaction. Off the published runtime classpath; jooq is version-managed by the Spring Boot
38+
// BOM.
39+
samplesImplementation("org.jooq:jooq")
40+
3541
testRuntimeOnly("org.slf4j:slf4j-simple")
3642

3743
testImplementation("org.springframework:spring-core") // For testing the RegexPathMatcher
3844
testImplementation(libs.turbine)
3945
testImplementation(libs.kotest.assertions)
4046
testImplementation(libs.kotest.runner)
47+
testImplementation(libs.mockk)
4148
testImplementation(libs.fory.core)
4249
testImplementation(libs.fory.kotlin)
4350
testImplementation(libs.jackson3.databind)
@@ -51,4 +58,9 @@ dependencies {
5158

5259
jmh { duplicateClassesStrategy.set(DuplicatesStrategy.EXCLUDE) }
5360

54-
dokka { dokkaSourceSets.configureEach { includes.from("README.md") } }
61+
dokka {
62+
dokkaSourceSets.configureEach {
63+
includes.from("README.md")
64+
samples.from(layout.projectDirectory.dir("src/samples/kotlin"))
65+
}
66+
}

0 commit comments

Comments
 (0)