@zio.dev/zio-blocks 0.0.33 → 0.0.51
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.
- package/guides/compile-time-resource-safety-with-scope.md +16 -17
- package/guides/getting-started-with-mux.md +1507 -0
- package/guides/query-dsl-extending.md +161 -102
- package/guides/query-dsl-fluent-builder.md +217 -157
- package/guides/query-dsl-reified-optics.md +12 -10
- package/guides/query-dsl-sql.md +246 -165
- package/guides/telemetry-guide.md +1069 -0
- package/guides/zio-schema-migration.md +29 -22
- package/index.md +292 -50
- package/package.json +1 -1
- package/plans/config-follow-up-prs.md +188 -0
- package/plans/config-pr-assessment-roadmap.md +310 -0
- package/reference/MuxDataFlow.jsx +250 -0
- package/reference/async.md +651 -0
- package/reference/chunk.md +3533 -308
- package/reference/codegen/case-class.md +436 -0
- package/reference/codegen/emitter-config.md +383 -0
- package/reference/codegen/examples.md +664 -0
- package/reference/codegen/field.md +316 -0
- package/reference/codegen/index.md +317 -0
- package/reference/codegen/scala-emitter.md +392 -0
- package/reference/codegen/scala-file.md +276 -0
- package/reference/codegen/sealed-trait.md +408 -0
- package/reference/codegen/type-definition.md +340 -0
- package/reference/codegen/type-ref.md +201 -0
- package/reference/combinators.md +347 -117
- package/reference/config.md +158 -0
- package/reference/context.md +4 -4
- package/reference/datastar.md +346 -0
- package/reference/docs.md +1461 -345
- package/reference/endpoint/auth-type.md +146 -0
- package/reference/endpoint/endpoint.md +297 -0
- package/reference/endpoint/http-codec.md +249 -0
- package/reference/endpoint/index.md +825 -0
- package/reference/endpoint/path-codec.md +237 -0
- package/reference/endpoint/route-pattern.md +196 -0
- package/reference/endpoint/route-tree.md +111 -0
- package/reference/endpoint/segment-codec.md +212 -0
- package/reference/html.md +1120 -0
- package/reference/htmx/attribute-values.md +359 -0
- package/reference/htmx/hx-encoding.md +111 -0
- package/reference/htmx/hx-params.md +204 -0
- package/reference/htmx/hx-swap.md +276 -0
- package/reference/htmx/hx-sync.md +251 -0
- package/reference/htmx/hx-target.md +314 -0
- package/reference/htmx/hx-trigger.md +457 -0
- package/reference/htmx/hx-url-update.md +239 -0
- package/reference/htmx/index.md +855 -0
- package/reference/http-model/index.md +47 -0
- package/reference/http-model/model.md +1481 -0
- package/reference/http-model/schema.md +747 -0
- package/reference/maybe.md +826 -0
- package/reference/media-type.md +2 -2
- package/reference/mux.mdx +823 -0
- package/reference/openapi.md +1351 -0
- package/reference/resource-management/defer-handle.md +1 -1
- package/reference/resource-management/resource.md +31 -2
- package/reference/resource-management/scope.md +28 -12
- package/reference/resource-management/wire.md +3 -7
- package/reference/ringbuffer/MpmcDiagram.jsx +717 -0
- package/reference/ringbuffer/MpscDiagram.jsx +618 -0
- package/reference/ringbuffer/SpmcDiagram.jsx +680 -0
- package/reference/ringbuffer/SpscDiagram.jsx +677 -0
- package/reference/ringbuffer/advanced.mdx +109 -0
- package/reference/ringbuffer/index.mdx +145 -0
- package/reference/ringbuffer/mpmc.mdx +151 -0
- package/reference/ringbuffer/mpsc.mdx +132 -0
- package/reference/ringbuffer/spmc.mdx +108 -0
- package/reference/ringbuffer/spsc.mdx +344 -0
- package/reference/{allows.md → schema/allows.md} +4 -4
- package/reference/{binding-resolver.md → schema/binding-resolver.md} +1 -1
- package/reference/{binding.md → schema/binding.md} +2 -3
- package/reference/schema/built-in-codecs/avro.md +451 -0
- package/reference/schema/built-in-codecs/bson.md +480 -0
- package/reference/schema/built-in-codecs/csv.md +564 -0
- package/reference/schema/built-in-codecs/index.md +77 -0
- package/reference/schema/built-in-codecs/json/index.md +295 -0
- package/reference/schema/built-in-codecs/json/json-config.md +217 -0
- package/reference/{json-patch.md → schema/built-in-codecs/json/json-patch.md} +5 -5
- package/reference/{json-schema.md → schema/built-in-codecs/json/json-schema.md} +14 -47
- package/reference/schema/built-in-codecs/json/json-selection.md +322 -0
- package/reference/{json.md → schema/built-in-codecs/json/json.md} +32 -64
- package/reference/schema/built-in-codecs/messagepack.md +508 -0
- package/reference/schema/built-in-codecs/thrift.md +433 -0
- package/reference/schema/built-in-codecs/toon.md +1078 -0
- package/reference/{xml.md → schema/built-in-codecs/xml.md} +13 -9
- package/reference/schema/built-in-codecs/yaml.md +552 -0
- package/reference/{codec.md → schema/codec.md} +10 -10
- package/reference/{dynamic-optic.md → schema/dynamic-optic.md} +151 -5
- package/reference/{dynamic-schema.md → schema/dynamic-schema.md} +8 -8
- package/reference/schema/format.md +92 -0
- package/reference/schema/index.md +50 -0
- package/reference/schema/migration.md +297 -0
- package/reference/{modifier.md → schema/modifier.md} +58 -7
- package/reference/{optics.md → schema/optics.md} +2 -2
- package/reference/{patch.md → schema/patch.md} +1 -1
- package/{path-interpolator.md → reference/schema/path-interpolator.md} +165 -72
- package/reference/{schema-evolution → schema/schema-evolution}/as.md +8 -8
- package/reference/{schema-evolution → schema/schema-evolution}/index.md +2 -2
- package/reference/{schema-evolution → schema/schema-evolution}/into.md +8 -8
- package/reference/{schema-expr.md → schema/schema-expr.md} +110 -175
- package/reference/{schema.md → schema/schema.md} +12 -0
- package/reference/{structural-types.md → schema/structural-types.md} +1 -1
- package/reference/{type-class-derivation.md → schema/type-class-derivation.md} +63 -1
- package/reference/smithy.md +533 -0
- package/reference/sql/db-codec-deriver.md +71 -0
- package/reference/sql/db-codec.md +687 -0
- package/reference/sql/db-con.md +271 -0
- package/reference/sql/db-connection.md +153 -0
- package/reference/sql/db-param-writer.md +77 -0
- package/reference/sql/db-param.md +66 -0
- package/reference/sql/db-result-reader.md +146 -0
- package/reference/sql/db-tx.md +82 -0
- package/reference/sql/db-value.md +41 -0
- package/reference/sql/ddl.md +85 -0
- package/reference/sql/frag.md +254 -0
- package/reference/sql/index.md +341 -0
- package/reference/sql/repo.md +600 -0
- package/reference/sql/sql-dialect.md +73 -0
- package/reference/sql/sql-logger.md +62 -0
- package/reference/sql/sql-name-mapper.md +70 -0
- package/reference/sql/table-metadata.md +134 -0
- package/reference/sql/table.md +448 -0
- package/reference/sql/transactor-zio.md +399 -0
- package/reference/sql/transactor.md +353 -0
- package/reference/sql-zio.md +112 -0
- package/reference/streams/concurrent-operators.md +106 -0
- package/reference/streams/index.md +653 -0
- package/reference/streams/pipeline.md +718 -0
- package/reference/streams/reader.md +1284 -0
- package/reference/streams/scala-2-compatibility.md +55 -0
- package/reference/streams/sink.md +1426 -0
- package/reference/streams/stream.md +2526 -0
- package/reference/streams/writer.md +1045 -0
- package/reference/streams/zero-boxing.md +275 -0
- package/reference/telemetry.md +693 -0
- package/reference/typeid.md +5 -19
- package/sidebars.js +238 -43
- package/reference/formats.md +0 -694
- package/reference/http-model.md +0 -1716
- package/reference/streams.md +0 -989
- package/ringbuffer.md +0 -249
- /package/reference/{json-differ.md → schema/built-in-codecs/json/json-differ.md} +0 -0
- /package/reference/{dynamic-value.md → schema/dynamic-value.md} +0 -0
- /package/reference/{lazy.md → schema/lazy.md} +0 -0
- /package/reference/{reflect.md → schema/reflect.md} +0 -0
- /package/reference/{registers.md → schema/registers.md} +0 -0
- /package/reference/{schema-error.md → schema/schema-error.md} +0 -0
- /package/reference/{syntax.md → schema/syntax.md} +0 -0
- /package/reference/{validation.md → schema/validation.md} +0 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: config
|
|
3
|
+
title: "Config"
|
|
4
|
+
sidebar_label: "Config"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
`zio.blocks.config` provides typed configuration loading, feature flags, provenance tracking, rollout selection, and source adapters for YAML, JSON, and HOCON. The module is synchronous and zero-dependency: configuration is loaded from `ConfigSource`, flags are read through `FlagSource`, and typed decoding is derived from `Schema[A]`.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```scala
|
|
12
|
+
libraryDependencies += "dev.zio" %% "zio-blocks-config" % "0.0.51"
|
|
13
|
+
libraryDependencies += "dev.zio" %% "zio-blocks-config-yaml" % "0.0.51"
|
|
14
|
+
libraryDependencies += "dev.zio" %% "zio-blocks-config-json" % "0.0.51"
|
|
15
|
+
libraryDependencies += "dev.zio" %% "zio-blocks-config-hocon" % "0.0.51"
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
For Scala.js:
|
|
19
|
+
|
|
20
|
+
```scala
|
|
21
|
+
libraryDependencies += "dev.zio" %%% "zio-blocks-config" % "0.0.51"
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Supported Scala versions: 2.13.x and 3.x.
|
|
25
|
+
|
|
26
|
+
## Core Types
|
|
27
|
+
|
|
28
|
+
At the center of the module are two source abstractions:
|
|
29
|
+
|
|
30
|
+
```scala
|
|
31
|
+
import zio.blocks.maybe.Maybe
|
|
32
|
+
|
|
33
|
+
trait FlagSource {
|
|
34
|
+
def sourceId: String
|
|
35
|
+
def get(name: String): Maybe[SourceValue[String]]
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
trait ConfigSource extends FlagSource {
|
|
39
|
+
def get(key: String): Maybe[SourceValue[String]]
|
|
40
|
+
def all(prefix: String): Map[String, SourceValue[String]]
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
`ConfigSource` extends `FlagSource`, so the same source can power both typed config loading and simple scalar flags.
|
|
45
|
+
|
|
46
|
+
## Loading Typed Configuration
|
|
47
|
+
|
|
48
|
+
Use `Config.load[A]` when you want a typed result and explicit errors:
|
|
49
|
+
|
|
50
|
+
```scala
|
|
51
|
+
import zio.blocks.config._
|
|
52
|
+
import zio.blocks.scope.Unscoped
|
|
53
|
+
|
|
54
|
+
final case class AppConfig(host: String, port: Int) derives Schema, Unscoped
|
|
55
|
+
|
|
56
|
+
val source = ConfigSource.fromMap(
|
|
57
|
+
Map("app.host" -> "localhost", "app.port" -> "8080"),
|
|
58
|
+
"example"
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
val loaded = Config.load[AppConfig](source.prefix("app"))
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
The main entry points are:
|
|
65
|
+
|
|
66
|
+
```scala
|
|
67
|
+
Config.load[A](source)
|
|
68
|
+
Config.loadOrThrow[A](source)
|
|
69
|
+
Config.loadWithProvenance[A](source)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
`Config.loadWithProvenance` returns a `ProvenanceMap[A]`, which lets you inspect where resolved values came from.
|
|
73
|
+
|
|
74
|
+
## Wiring Config into Dependency Graphs
|
|
75
|
+
|
|
76
|
+
For application wiring, prefer `Config.wire[A]` or `Config.wire[A](prefix)` so decoding stays inside the dependency graph instead of being done manually at startup:
|
|
77
|
+
|
|
78
|
+
```scala
|
|
79
|
+
Config.wire[AppConfig]
|
|
80
|
+
Config.wire[AppConfig]("app")
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
That keeps `ConfigSource` as the injected input and the typed config as the derived output.
|
|
84
|
+
|
|
85
|
+
## Working with Sources
|
|
86
|
+
|
|
87
|
+
`ConfigSource` supports composition and key transformation:
|
|
88
|
+
|
|
89
|
+
```scala
|
|
90
|
+
val defaults = ConfigSource.fromMap(Map("db.host" -> "localhost"), "defaults")
|
|
91
|
+
val env = ConfigSource.fromMap(Map("db.port" -> "5432"), "env")
|
|
92
|
+
|
|
93
|
+
val combined = env.orElse(defaults)
|
|
94
|
+
val scoped = combined.prefix("db")
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Common adapters:
|
|
98
|
+
|
|
99
|
+
- `ConfigSource.fromMap(...)`
|
|
100
|
+
- `ConfigSource.fromYaml(...)` (requires `config-yaml` dependency)
|
|
101
|
+
- `ConfigSource.fromJson(...)` (requires `config-json` dependency)
|
|
102
|
+
- `ConfigSource.fromHocon(...)` (requires `config-hocon` dependency)
|
|
103
|
+
|
|
104
|
+
## Static and Dynamic Flags
|
|
105
|
+
|
|
106
|
+
`StaticFlag[A]` resolves once, during object initialization:
|
|
107
|
+
|
|
108
|
+
```scala
|
|
109
|
+
import zio.blocks.config._
|
|
110
|
+
|
|
111
|
+
object poolSize extends StaticFlag[Int](10)
|
|
112
|
+
|
|
113
|
+
val size: Int = poolSize()
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
`DynamicFlag[A]` keeps an updatable rollout expression and evaluates it on demand.
|
|
117
|
+
|
|
118
|
+
`StaticFlag` names are derived from the Scala object's fully qualified name, so custom `FlagSource` registrations must use that exact key.
|
|
119
|
+
|
|
120
|
+
`FlagSource.Registry` is consulted in registration order, so the first registered source wins when multiple sources provide the same flag.
|
|
121
|
+
|
|
122
|
+
:::note
|
|
123
|
+
Register `FlagSource`s before the first reference to a `StaticFlag` object. Once a static flag object is initialized, later registrations do not retroactively change its resolved value.
|
|
124
|
+
:::
|
|
125
|
+
|
|
126
|
+
## Rollout DSL
|
|
127
|
+
|
|
128
|
+
`Rollout` selects values based on a path and an optional percentage bucket:
|
|
129
|
+
|
|
130
|
+
```scala
|
|
131
|
+
import zio.blocks.config._
|
|
132
|
+
|
|
133
|
+
val bucket = Rollout.bucketFor("user-123")
|
|
134
|
+
val choice = Rollout.select("true@prod/50%;false", "prod", bucket)
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
The percentage uses slash-separated syntax (`prod/50%`). For the example above, `choice` is `Maybe.present("true")` for roughly half of the `prod` buckets and `Maybe.present("false")` otherwise. Bare values act as catch-all fallbacks and should come last.
|
|
138
|
+
|
|
139
|
+
## Provenance
|
|
140
|
+
|
|
141
|
+
Every resolved value carries provenance information through `SourceValue` and `Provenance`:
|
|
142
|
+
|
|
143
|
+
```scala
|
|
144
|
+
val source = ConfigSource.fromMap(Map("db.host" -> "localhost"), "defaults")
|
|
145
|
+
val host = source.get("db.host")
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
`host` contains both the raw value and a `Provenance.Resolved` entry that records the source id and key.
|
|
149
|
+
|
|
150
|
+
## Format Adapters
|
|
151
|
+
|
|
152
|
+
The format modules flatten structured documents into dot-separated keys:
|
|
153
|
+
|
|
154
|
+
- YAML: nested mappings become `a.b.c`
|
|
155
|
+
- JSON: arrays are indexed as `items.0`, `items.1`, ...
|
|
156
|
+
- HOCON: substitutions are resolved before flattening
|
|
157
|
+
|
|
158
|
+
Use these adapters when you want the convenience of file formats but still want a single `ConfigSource` API for decoding, composition, and provenance.
|
package/reference/context.md
CHANGED
|
@@ -116,7 +116,7 @@ val config = ctx.get[Config] // Compile-time proof it exists
|
|
|
116
116
|
Add the ZIO Blocks Context module to your `build.sbt`:
|
|
117
117
|
|
|
118
118
|
```scala
|
|
119
|
-
libraryDependencies += "dev.zio" %% "zio-blocks-context" % "0.0.
|
|
119
|
+
libraryDependencies += "dev.zio" %% "zio-blocks-context" % "0.0.51"
|
|
120
120
|
```
|
|
121
121
|
|
|
122
122
|
## Construction
|
|
@@ -544,7 +544,7 @@ cd zio-blocks
|
|
|
544
544
|
package context
|
|
545
545
|
|
|
546
546
|
import zio.blocks.context._
|
|
547
|
-
import
|
|
547
|
+
import zio.sbt.ExprEval.show
|
|
548
548
|
|
|
549
549
|
// Context.empty creates an empty, type-safe dependency container.
|
|
550
550
|
// Use Context.apply(...) to construct contexts with 1–10 values.
|
|
@@ -621,7 +621,7 @@ sbt "schema-examples/runMain context.ContextConstructionExample"
|
|
|
621
621
|
package context
|
|
622
622
|
|
|
623
623
|
import zio.blocks.context._
|
|
624
|
-
import
|
|
624
|
+
import zio.sbt.ExprEval.show
|
|
625
625
|
|
|
626
626
|
// Context#get[A] retrieves a value by type with compile-time proof of existence.
|
|
627
627
|
// Context#getOption[A] retrieves a value if present, returning None if missing.
|
|
@@ -706,7 +706,7 @@ sbt "schema-examples/runMain context.ContextRetrievalExample"
|
|
|
706
706
|
package context
|
|
707
707
|
|
|
708
708
|
import zio.blocks.context._
|
|
709
|
-
import
|
|
709
|
+
import zio.sbt.ExprEval.show
|
|
710
710
|
|
|
711
711
|
// Context is immutable; modification methods return new contexts.
|
|
712
712
|
// Context#add expands the context with a new value (or replaces if type exists).
|
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: datastar
|
|
3
|
+
title: "Datastar"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
`zio-blocks-datastar` provides a type-safe Scala SDK for [Datastar](https://data-star.dev/), the hypermedia framework that brings reactive UIs via server-sent events (SSE). It builds on `zio-blocks-html` for DOM construction and `zio-blocks-schema` for JSON serialization.
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
```scala
|
|
11
|
+
// JVM
|
|
12
|
+
libraryDependencies += "dev.zio" %% "zio-blocks-datastar" % "0.0.51"
|
|
13
|
+
|
|
14
|
+
// Scala.js
|
|
15
|
+
libraryDependencies += "dev.zio" %%% "zio-blocks-datastar" % "0.0.51"
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Signals
|
|
19
|
+
|
|
20
|
+
A `Signal[A]` represents a named reactive signal on the client. Use `:=` to pair it with a value, producing a `SignalUpdate` ready for SSE transmission:
|
|
21
|
+
|
|
22
|
+
```scala
|
|
23
|
+
import zio.http.datastar._
|
|
24
|
+
import zio.blocks.schema.Schema
|
|
25
|
+
|
|
26
|
+
case class User(name: String, age: Int)
|
|
27
|
+
object User {
|
|
28
|
+
implicit val schema: Schema[User] = Schema.derived
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
val count = Signal[Int]("count")
|
|
32
|
+
val username = Signal[String]("username")
|
|
33
|
+
|
|
34
|
+
// Create signal updates (serializes via Schema's cached JSON codec)
|
|
35
|
+
val update1 = count := 0
|
|
36
|
+
val update2 = username := "Alice"
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## SSE Events
|
|
40
|
+
|
|
41
|
+
`DatastarEvent` is the sealed trait representing events sent to the browser. Use the companion object's builder methods:
|
|
42
|
+
|
|
43
|
+
### patchSignals
|
|
44
|
+
|
|
45
|
+
Send signal updates to the client:
|
|
46
|
+
|
|
47
|
+
```scala
|
|
48
|
+
val sse = DatastarEvent
|
|
49
|
+
.patchSignals(count := 42, username := "Bob")
|
|
50
|
+
.renderSSE
|
|
51
|
+
|
|
52
|
+
// With options:
|
|
53
|
+
val sseWithOptions = DatastarEvent
|
|
54
|
+
.patchSignals(count := 1)
|
|
55
|
+
.withOnlyIfMissing
|
|
56
|
+
.withEventId("evt-1")
|
|
57
|
+
.withRetry(5000)
|
|
58
|
+
.renderSSE
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### patchElements
|
|
62
|
+
|
|
63
|
+
Send DOM fragments to the client:
|
|
64
|
+
|
|
65
|
+
```scala
|
|
66
|
+
import zio.blocks.html._
|
|
67
|
+
|
|
68
|
+
val sse = DatastarEvent
|
|
69
|
+
.patchElements(div(id := "status")("Online"))
|
|
70
|
+
.withSelector(CssSelector.id("status"))
|
|
71
|
+
.withMode(ElementPatchMode.Inner)
|
|
72
|
+
.withViewTransition
|
|
73
|
+
.renderSSE
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### executeScript
|
|
77
|
+
|
|
78
|
+
Run JavaScript on the client:
|
|
79
|
+
|
|
80
|
+
```scala
|
|
81
|
+
val sse = DatastarEvent
|
|
82
|
+
.executeScript(js"console.log('hello')")
|
|
83
|
+
.renderSSE
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### removeElements
|
|
87
|
+
|
|
88
|
+
Remove elements matching a CSS selector:
|
|
89
|
+
|
|
90
|
+
```scala
|
|
91
|
+
val sse = DatastarEvent
|
|
92
|
+
.removeElements(CssSelector.id("old-item"))
|
|
93
|
+
.renderSSE
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Attribute DSL
|
|
97
|
+
|
|
98
|
+
Import `zio.http.datastar._` to get all `data-*` attribute constructors. These integrate with the `zio-blocks-html` DSL:
|
|
99
|
+
|
|
100
|
+
```scala
|
|
101
|
+
import zio.blocks.html._
|
|
102
|
+
import zio.http.datastar._
|
|
103
|
+
|
|
104
|
+
val count = Signal[Int]("count")
|
|
105
|
+
val username = Signal[String]("username")
|
|
106
|
+
|
|
107
|
+
div(
|
|
108
|
+
dataSignals(count := 0),
|
|
109
|
+
dataText := count
|
|
110
|
+
)(
|
|
111
|
+
span()("Count: "),
|
|
112
|
+
button(dataOn.click := js"$count++")("Increment")
|
|
113
|
+
)
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Datastar expression positions are intentionally stricter than generic HTML/JS templating.
|
|
117
|
+
Raw `String` values are rejected in Datastar expression attributes; use `js"..."`
|
|
118
|
+
for expressions, or typed values like `Signal`, `SignalUpdate`, and `DatastarRef`.
|
|
119
|
+
|
|
120
|
+
```scala
|
|
121
|
+
dataText := count
|
|
122
|
+
dataText := count.ref
|
|
123
|
+
dataOn.click := js"$count++"
|
|
124
|
+
|
|
125
|
+
// does not compile:
|
|
126
|
+
// dataOn.click := "$count++"
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Available Attributes
|
|
130
|
+
|
|
131
|
+
| Method | Datastar Attribute | Description |
|
|
132
|
+
|--------|-------------------|-------------|
|
|
133
|
+
| `dataSignals(signal)` | `data-signals:name` | Declare one keyed signal with an initial value |
|
|
134
|
+
| `dataSignals(update, updates...)` | `data-signals` | Patch multiple signals with one object expression |
|
|
135
|
+
| `dataBind(signal)` | `data-bind:name` | Two-way bind an input to a signal |
|
|
136
|
+
| `dataText` | `data-text` | Set element text content |
|
|
137
|
+
| `dataShow` | `data-show` | Conditionally show/hide element |
|
|
138
|
+
| `dataClass("name")` | `data-class:name` | Toggle CSS class |
|
|
139
|
+
| `dataOn.click` | `data-on:click` | Event handler |
|
|
140
|
+
| `dataComputed(signal)` | `data-computed:name` | Computed signal |
|
|
141
|
+
| `dataEffect` | `data-effect` | Side-effect expression |
|
|
142
|
+
| `dataIndicator(signal)` | `data-indicator:name` | Loading indicator signal |
|
|
143
|
+
| `dataRef("name")` | `data-ref:name` | Element reference |
|
|
144
|
+
| `dataInit` | `data-init` | Initialization expression |
|
|
145
|
+
|
|
146
|
+
### Event Modifiers
|
|
147
|
+
|
|
148
|
+
Chain modifiers on `dataOn` before assigning a handler:
|
|
149
|
+
|
|
150
|
+
```scala
|
|
151
|
+
// Debounce input by 300ms
|
|
152
|
+
dataOn.input.debounce(300) := username
|
|
153
|
+
|
|
154
|
+
// Click with prevent default, only once
|
|
155
|
+
dataOn.click.prevent.once := js"handleSubmit()"
|
|
156
|
+
|
|
157
|
+
// Throttle scroll, listen on window
|
|
158
|
+
dataOn.scroll.throttle(100).window := js"onScroll()"
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Available modifiers: `debounce`, `debounceLeading`, `throttle`, `throttleLeading`, `delay`, `once`, `passive`, `capture`, `stop`, `prevent`, `outside`, `window`, `document`, `viewTransition`.
|
|
162
|
+
|
|
163
|
+
### Case Modifiers
|
|
164
|
+
|
|
165
|
+
`dataOn` and `dataSignals(signal)` builders expose `.camel`, `.kebab`, `.snake`, and `.pascal` to control how the attribute key is cased. The default for `dataOn` is kebab; for `dataSignals(signal)` it is camel.
|
|
166
|
+
|
|
167
|
+
```scala
|
|
168
|
+
// Renders data-on:myCustomEvent (camel, explicit)
|
|
169
|
+
dataOn("myCustomEvent").camel := js"handleIt()"
|
|
170
|
+
|
|
171
|
+
// Renders data-signals:my-signal__case.kebab (kebab override)
|
|
172
|
+
dataSignals(count := 0).kebab
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
The suffix `__case.<modifier>` is appended to the attribute name only when the chosen case differs from the builder's default.
|
|
176
|
+
|
|
177
|
+
### Advanced Triggers
|
|
178
|
+
|
|
179
|
+
#### dataOnIntersect
|
|
180
|
+
|
|
181
|
+
Fires when the element enters (or exits) the viewport. Modifiers map directly to Datastar's intersection observer options:
|
|
182
|
+
|
|
183
|
+
```scala
|
|
184
|
+
// Fire once when 50% of the element is visible
|
|
185
|
+
div(dataOnIntersect.half.once := js"loadContent()")()
|
|
186
|
+
|
|
187
|
+
// Fire when element exits the viewport, debounced
|
|
188
|
+
div(dataOnIntersect.exit.debounce(200) := js"cleanup()")()
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Available modifiers: `once`, `half`, `full`, `exit`, `threshold(pct: Double)`, `delay(millis)`, `debounce(millis)`, `throttle(millis)`, `viewTransition`.
|
|
192
|
+
|
|
193
|
+
#### dataOnInterval
|
|
194
|
+
|
|
195
|
+
Runs an expression on a repeating timer:
|
|
196
|
+
|
|
197
|
+
```scala
|
|
198
|
+
// Poll every second
|
|
199
|
+
div(dataOnInterval.duration(1000) := js"$count++")(count.ref.text)
|
|
200
|
+
|
|
201
|
+
// Leading-edge interval with view transitions
|
|
202
|
+
div(dataOnInterval.durationLeading(500).viewTransition := js"refresh()")()
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
#### dataOnSignalPatch / dataOnSignalPatchFilter
|
|
206
|
+
|
|
207
|
+
`dataOnSignalPatch` runs whenever the server patches signals. Use `dataOnSignalPatchFilter` to restrict which patches trigger the handler:
|
|
208
|
+
|
|
209
|
+
```scala
|
|
210
|
+
div(
|
|
211
|
+
dataOnSignalPatch.debounce(100) := js"onPatch()",
|
|
212
|
+
dataOnSignalPatchFilter := js"$count > 0"
|
|
213
|
+
)()
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Available modifiers on `dataOnSignalPatch`: `delay(millis)`, `debounce(millis)`, `throttle(millis)`.
|
|
217
|
+
|
|
218
|
+
### Style and Attribute Binding
|
|
219
|
+
|
|
220
|
+
#### dataStyle
|
|
221
|
+
|
|
222
|
+
Bind inline styles, either as a whole-object expression or per-property:
|
|
223
|
+
|
|
224
|
+
```scala
|
|
225
|
+
// Keyed: sets a single CSS property
|
|
226
|
+
div(dataStyle("color") := js"$isDark ? 'white' : 'black'")()
|
|
227
|
+
|
|
228
|
+
// Unkeyed: bind an entire style object
|
|
229
|
+
div(dataStyle := js"{color: $color, fontSize: $size + 'px'}")()
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
#### dataAttr
|
|
233
|
+
|
|
234
|
+
Bind arbitrary HTML attributes:
|
|
235
|
+
|
|
236
|
+
```scala
|
|
237
|
+
div(dataAttr("aria-label") := js"$label")()
|
|
238
|
+
div(dataAttr("tabindex") := js"$isActive ? 0 : -1")()
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Morph Control
|
|
242
|
+
|
|
243
|
+
Mark elements so Datastar's morphing algorithm treats them specially:
|
|
244
|
+
|
|
245
|
+
```scala
|
|
246
|
+
// Exclude element and its children from morphing
|
|
247
|
+
div(dataIgnore)()
|
|
248
|
+
|
|
249
|
+
// Exclude only the element itself, not its children
|
|
250
|
+
div(dataIgnoreSelf)()
|
|
251
|
+
|
|
252
|
+
// Prevent the element from being morphed (keep existing DOM node)
|
|
253
|
+
div(dataIgnoreMorph)()
|
|
254
|
+
|
|
255
|
+
// Preserve a specific attribute across morphs
|
|
256
|
+
input(dataPreserveAttr("value"))()
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### JSON Signals
|
|
260
|
+
|
|
261
|
+
`dataJsonSignals` passes a raw JSON string as the signals expression, useful when signals are serialized server-side:
|
|
262
|
+
|
|
263
|
+
```scala
|
|
264
|
+
div(dataJsonSignals := js"""{"count":0,"username":""}""")()
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
## ElementPatchMode
|
|
268
|
+
|
|
269
|
+
Controls how `patchElements` applies DOM content to the target:
|
|
270
|
+
|
|
271
|
+
| Mode | Description |
|
|
272
|
+
|------|-------------|
|
|
273
|
+
| `Outer` | Morphs the target element in place (default, omitted from SSE) |
|
|
274
|
+
| `Inner` | Replaces the target's children |
|
|
275
|
+
| `Replace` | Replaces the target element |
|
|
276
|
+
| `Prepend` | Inserts before the first child |
|
|
277
|
+
| `Append` | Inserts after the last child |
|
|
278
|
+
| `Before` | Inserts before the target element |
|
|
279
|
+
| `After` | Inserts after the target element |
|
|
280
|
+
| `Remove` | Removes the target element |
|
|
281
|
+
|
|
282
|
+
```scala
|
|
283
|
+
DatastarEvent
|
|
284
|
+
.patchElements(li(id := "new")("item"))
|
|
285
|
+
.withSelector(CssSelector.id("list"))
|
|
286
|
+
.withMode(ElementPatchMode.Append)
|
|
287
|
+
.renderSSE
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
## CssSelector
|
|
291
|
+
|
|
292
|
+
`CssSelector` (from `zio.blocks.html`) constructs CSS selectors with type-safe combinators:
|
|
293
|
+
|
|
294
|
+
```scala
|
|
295
|
+
import zio.blocks.html._
|
|
296
|
+
|
|
297
|
+
CssSelector.id("main") // #main
|
|
298
|
+
CssSelector.`class`("active") // .active
|
|
299
|
+
CssSelector.element("div") // div
|
|
300
|
+
CssSelector.raw(".foo > .bar") // .foo > .bar
|
|
301
|
+
CssSelector.universal // *
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
Selectors compose with operators:
|
|
305
|
+
|
|
306
|
+
```scala
|
|
307
|
+
val sel = CssSelector.element("ul") > CssSelector.element("li") // ul > li
|
|
308
|
+
val grouped = CssSelector.id("a") | CssSelector.id("b") // #a, #b
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
## Signal.dynamic
|
|
312
|
+
|
|
313
|
+
`Signal[A]("name")` validates the signal name at compile time for string literals. Use `Signal.dynamic[A](name)` when the name is only known at runtime:
|
|
314
|
+
|
|
315
|
+
```scala
|
|
316
|
+
val fieldName = computeFieldName() // runtime string
|
|
317
|
+
val sig = Signal.dynamic[String](fieldName) // validated at runtime
|
|
318
|
+
|
|
319
|
+
sig := "hello" // works like any other Signal
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
`dynamic` throws `IllegalArgumentException` if the name is invalid. The same validation rules apply: dot-separated JavaScript identifiers, no `__`.
|
|
323
|
+
|
|
324
|
+
## SSE Streaming
|
|
325
|
+
|
|
326
|
+
`DatastarEvent` has no ZIO or HTTP framework dependency. It just produces strings. To stream events, set the response content-type to `text/event-stream` and write each `renderSSE` result to the response body:
|
|
327
|
+
|
|
328
|
+
```scala
|
|
329
|
+
// Framework-agnostic pseudocode
|
|
330
|
+
response.setHeader("Content-Type", "text/event-stream")
|
|
331
|
+
response.setHeader("Cache-Control", "no-cache")
|
|
332
|
+
|
|
333
|
+
// Send an initial signal patch
|
|
334
|
+
response.write(DatastarEvent.patchSignals(count := 0).renderSSE)
|
|
335
|
+
|
|
336
|
+
// Stream DOM updates as data changes
|
|
337
|
+
val fragment = div(id := "result")(computedContent)
|
|
338
|
+
response.write(
|
|
339
|
+
DatastarEvent
|
|
340
|
+
.patchElements(fragment)
|
|
341
|
+
.withSelector(CssSelector.id("result"))
|
|
342
|
+
.renderSSE
|
|
343
|
+
)
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
Each `renderSSE` call returns a self-contained SSE block — multiple events can be written sequentially to the same stream.
|