# Datastar SDK for Raku

This is a data-oriented Datastar SDK written in Raku. The SDK implements the
Datastar SDK ADR with zero runtime dependencies.
[Raku++](https://github.com/ash/rakupp.git) is the primary compiler used by
this project to develop, test, and build it.

Install the distribution from the zef ecosystem:

```sh
zef install 'Datastar::SDK:auth<zef:ealvar3z>'
```

Then [install](https://github.com/ash/rakupp#install) the `rakupp` compiler to
run it with Raku++.

The [Rakudoc package guide](docs/README.rakudoc) contains the same documentation
for the zef distribution.

## Quick start

Create an event as native Raku data, then encode it for an SSE response:

```raku
use Datastar::SDK;

my %event = patch-elements(
    '<section id="status">Saved</section>',
);

say encode-sse(%event);
```

## Specification

The core API follows Datastar's
[SDK architecture decision record](https://github.com/starfederation/datastar/blob/develop/sdk/ADR.md)
and the constants, defaults, literals, and enums in the
[v1 SDK configuration](https://github.com/starfederation/datastar/blob/develop/sdk/datastar-sdk-config-v1.json).
The configuration's shape is described by its
[JSON Schema](https://github.com/starfederation/datastar/blob/develop/sdk/datastar-sdk-config-v1.schema.json).
Scoped view transitions use the ADR's newer `viewTransitionSelector` option.

## Architecture

The API follows a data-oriented flow:

```text
HTTP request adapter
  -> native Raku signals
  -> event hash
  -> SSE encoding
  -> HTTP response adapter
```

`ServerSentEventGenerator` is the only stateful SDK object. It owns the
response lifecycle, serializes complete event writes with a `Lock`, and flushes
after each event. Event constructors and JSON handling operate on native Raku
data.

## Usage

Implement the small request and response roles for your HTTP server:

```raku
use Datastar::SDK;

class MyRequest does RequestAdapter {
    method request-method(--> Str) { ... }
    method query-parameter(Str:D $name) { ... }
    method body-text(--> Str) { ... }
    method protocol-version(--> Str) { ... }
}

class MyResponse does ResponseAdapter {
    method set-header(Str:D $name, Str:D $value) { ... }
    method write(Str:D $chunk) { ... }
    method flush() { ... }
}

my $sse = ServerSentEventGenerator.new(
    request => $request,
    response => $response,
);

my %signals = read-signals($request);

$sse.patch-elements(
    '<section id="status">Saved</section>',
);

$sse.patch-signals(%(
    pending => False,
    error => JSON-NULL,
));
```

For `GET` and `DELETE` requests, `query-parameter` must return the URL-decoded
value of the requested query parameter. `read-signals` asks for the configured
`datastar` parameter and parses its JSON value. For `POST`, `PUT`, and `PATCH`,
it parses `body-text` directly.

## Options

Raku-style kebab-case option names correspond to the ADR's Go-style names:

| Raku | ADR |
|---|---|
| `event-id` | `eventId` |
| `retry-duration` | `retryDuration` |
| `use-view-transition` | `useViewTransition` |
| `view-transition-selector` | `viewTransitionSelector` |
| `only-if-missing` | `onlyIfMissing` |
| `auto-remove` | `autoRemove` |

`patch-signals` accepts either native associative data or a JSON string. The
string form preserves formatting when multiline JSON must be streamed exactly.
Use the exported `JSON-NULL` sentinel to retain JSON merge-patch removal values
inside native Raku data.

## Examples

The [examples guide](examples/README.md) contains Raku ports of Datastar's Go
examples:

- A Hello World server that streams a complete element for every message
  prefix.
- A zero-dependency hot-reload server driven by a long-lived SSE request.

Run either example from the repository root:

```sh
rakupp -Ilib -Iexamples/lib examples/helloworld/main.raku
rakupp -Ilib -Iexamples/lib examples/hotreload/main.raku
```

## Testing

Run the unit tests from the repository root:

```sh
for test in t/*.rakutest; do rakupp -Ilib "$test"; done
RAKUPP_PARALLEL=1 rakupp -Ilib t/03-generator.rakutest
```

Run the conformance server from the repository root:

```sh
rakupp -Ilib t/conformance-server.raku
```

In another terminal:

```sh
go run github.com/starfederation/datastar/sdk/tests/cmd/datastar-sdk-tests@latest \
  -server http://127.0.0.1:7331 -v
```

Verify that the conformance server can be transpiled to native C++:

```sh
RAKULIB=lib rakupp --cpp t/conformance-server.raku >/dev/null
```

An exit status of `0` means native code generation is available. Status `5`
means `--exe` would fall back to AOT or bundling.

Compile and run the native conformance server:

```sh
sdk_tmp=$(mktemp -d)
RAKULIB=lib \
  rakupp --exe t/conformance-server.raku \
  -o "$sdk_tmp/datastar-raku-conformance"
RAKULIB=lib "$sdk_tmp/datastar-raku-conformance"
```

The compile step must report `Compiled (native)` without a fallback notice.
Run the same Go conformance command against the compiled server.

The conformance server is test-only. Production integrations should adapt the
roles to their HTTP stack so writes and flush errors propagate naturally.

## Author

[E. Alvarez](mailto:mail@alvar3z.com)

## Copyright and license

Copyright 2026 E. Alvarez.

This software is licensed under the
[Artistic License 2.0](https://opensource.org/license/artistic-2-0).
