Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Zig SDK Changes #718

Merged
merged 7 commits into from
Feb 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions sdk/zig/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Run `zig build test`.
Install with `zig fetch --save git+https://github.com/starfederation/datastar-zig`

```zig
const datastar = @import("datastar");
const datastar = @import("datastar").httpz;
// Creates a new `ServerSentEventGenerator`.
var sse = try datastar.ServerSentEventGenerator.init(res);
Expand All @@ -20,7 +20,7 @@ var sse = try datastar.ServerSentEventGenerator.init(res);
try sse.mergeFragments("<div id='question'>What do you put in a toaster?</div>", .{});
// Merges signals into the signals.
try sse.mergeSignals("{response: '', answer: 'bread'}", .{});
try sse.mergeSignals(.{ .response = "", .answer = "bread" }, .{});
```

Full examples at https://github.com/starfederation/datastar/tree/main/examples/zig
2 changes: 1 addition & 1 deletion sdk/zig/build.zig.zon
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.{
.name = "datastar",
.version = "1.0.0-beta.2",
.version = "1.0.0-beta.3",
.dependencies = .{
.httpz = .{
.url = "git+https://github.com/karlseguin/http.zig?ref=master#f16b296a2772be97e48a57259c004aa6584a02c6",
Expand Down
177 changes: 71 additions & 106 deletions sdk/zig/src/ServerSentEventGenerator.zig
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ const default_execute_script_attributes: []const []const u8 = &[_][]const u8{con

allocator: std.mem.Allocator,
writer: std.net.Stream.Writer,
mutex: std.Thread.Mutex = .{},

pub const ExecuteScriptOptions = struct {
/// `event_id` can be used by the backend to replay events.
Expand Down Expand Up @@ -81,15 +80,12 @@ pub const RemoveSignalsOptions = struct {
fn send(
self: *@This(),
event: consts.EventType,
data: []const []const u8,
data: []const u8,
options: struct {
event_id: ?[]const u8 = null,
retry_duration: u32 = consts.default_sse_retry_duration,
},
) !void {
self.mutex.lock();
defer self.mutex.unlock();

try self.writer.print("event: {}\n", .{event});

if (options.event_id) |id| {
Expand All @@ -100,14 +96,16 @@ fn send(
try self.writer.print("retry: {d}\n", .{options.retry_duration});
}

for (data) |line| {
var iter = std.mem.splitScalar(u8, data, '\n');
while (iter.next()) |line| {
if (line.len == 0) continue;
try self.writer.print("data: {s}\n", .{line});
}

try self.writer.writeAll("\n\n");
}

/// `ExecuteScript` executes JavaScript in the browser
/// `executeScript` executes JavaScript in the browser
///
/// See the [Datastar documentation](https://data-star.dev/reference/sse_events#datastar-execute-script) for more information.
pub fn executeScript(
Expand All @@ -116,52 +114,42 @@ pub fn executeScript(
script: []const u8,
options: ExecuteScriptOptions,
) !void {
var data = std.ArrayList([]const u8).init(self.allocator);
var data = std.ArrayList(u8).init(self.allocator);
errdefer data.deinit();
const writer = data.writer();

if (options.attributes.len != 1 or !std.mem.eql(
u8,
default_execute_script_attributes[0],
options.attributes[0],
)) {
for (options.attributes) |attribute| {
const line = try std.fmt.allocPrint(
self.allocator,
"{s} {s}",
try writer.print(
consts.attributes_dataline_literal ++ " {s}\n",
.{
consts.attributes_dataline_literal,
attribute,
},
);

try data.append(line);
}
}

if (options.auto_remove != consts.default_execute_script_auto_remove) {
const line = try std.fmt.allocPrint(
self.allocator,
"{s} {s}",
try writer.print(
consts.auto_remove_dataline_literal ++ " {}\n",
.{
consts.auto_remove_dataline_literal,
if (options.auto_remove) "true" else "false",
options.auto_remove,
},
);

try data.append(line);
}

var iter = std.mem.splitScalar(u8, script, '\n');
while (iter.next()) |elem| {
const line = try std.fmt.allocPrint(
self.allocator,
"{s} {s}",
try writer.print(
consts.script_dataline_literal ++ " {s}\n",
.{
consts.script_dataline_literal,
elem,
},
);

try data.append(line);
}

try self.send(
Expand All @@ -174,7 +162,7 @@ pub fn executeScript(
);
}

/// `MergeFragments` merges one or more fragments into the DOM. By default,
/// `mergeFragments` merges one or more fragments into the DOM. By default,
/// Datastar merges fragments using Idiomorph, which matches top level elements based on their ID.
///
/// See the [Datastar documentation](https://data-star.dev/reference/sse_events#datastar-merge-fragments) for more information.
Expand All @@ -184,72 +172,54 @@ pub fn mergeFragments(
fragments: []const u8,
options: MergeFragmentsOptions,
) !void {
var data = std.ArrayList([]const u8).init(self.allocator);
var data = std.ArrayList(u8).init(self.allocator);
errdefer data.deinit();
const writer = data.writer();

if (options.selector) |selector| {
const line = try std.fmt.allocPrint(
self.allocator,
"{s} {s}",
try writer.print(
consts.selector_dataline_literal ++ " {s}\n",
.{
consts.selector_dataline_literal,
selector,
},
);

try data.append(line);
}

if (options.merge_mode != consts.default_fragment_merge_mode) {
const line = try std.fmt.allocPrint(
self.allocator,
"{s} {}",
try writer.print(
consts.merge_mode_dataline_literal ++ " {}\n",
.{
consts.merge_mode_dataline_literal,
options.merge_mode,
},
);

try data.append(line);
}

if (options.settle_duration != consts.default_fragments_settle_duration) {
const line = try std.fmt.allocPrint(
self.allocator,
"{s} {d}",
try writer.print(
consts.settle_duration_dataline_literal ++ " {d}\n",
.{
consts.settle_duration_dataline_literal,
options.settle_duration,
},
);

try data.append(line);
}

if (options.use_view_transition != consts.default_fragments_use_view_transitions) {
const line = try std.fmt.allocPrint(
self.allocator,
"{s} {}",
try writer.print(
consts.use_view_transition_dataline_literal ++ " {}\n",
.{
consts.use_view_transition_dataline_literal,
options.use_view_transition,
},
);

try data.append(line);
}

var iter = std.mem.splitScalar(u8, fragments, '\n');
while (iter.next()) |elem| {
const line = try std.fmt.allocPrint(
self.allocator,
"{s} {s}",
try writer.print(
consts.fragments_dataline_literal ++ " {s}\n",
.{
consts.fragments_dataline_literal,
elem,
},
);

try data.append(line);
}

try self.send(
Expand All @@ -262,39 +232,31 @@ pub fn mergeFragments(
);
}

/// `MergeSignals` sends one or more signals to the browser to be merged into the signals.
/// `mergeSignals` sends one or more signals to the browser to be merged into the signals.
/// This function takes in `anytype` as the signals to merge, which can be any type that can be serialized to JSON.
///
/// See the [Datastar documentation](https://data-star.dev/reference/sse_events#datastar-merge-signals) for more information.
pub fn mergeSignals(
self: *@This(),
signals: []const u8,
signals: anytype,
options: MergeSignalsOptions,
) !void {
var data = std.ArrayList([]const u8).init(self.allocator);
var data = std.ArrayList(u8).init(self.allocator);
errdefer data.deinit();
const writer = data.writer();

if (options.only_if_missing != consts.default_merge_signals_only_if_missing) {
const line = try std.fmt.allocPrint(
self.allocator,
"{s} {}",
try writer.print(
consts.only_if_missing_dataline_literal ++ " {}\n",
.{
consts.only_if_missing_dataline_literal,
options.only_if_missing,
},
);

try data.append(line);
}

const line = try std.fmt.allocPrint(
self.allocator,
"{s} {s}",
.{
consts.signals_dataline_literal,
signals,
},
);

try data.append(line);
try writer.writeAll(consts.signals_dataline_literal ++ " ");
try std.json.stringify(signals, .{}, writer);
try writer.writeByte('\n');

try self.send(
.merge_signals,
Expand All @@ -306,53 +268,43 @@ pub fn mergeSignals(
);
}

/// `RemoveFragments` sends a selector to the browser to remove HTML fragments from the DOM.
/// `removeFragments` sends a selector to the browser to remove HTML fragments from the DOM.
///
/// See the [Datastar documentation](https://data-star.dev/reference/sse_events#datastar-remove-fragments) for more information.
pub fn removeFragments(
self: *@This(),
selector: []const u8,
options: RemoveFragmentsOptions,
) !void {
var data = std.ArrayList([]const u8).init(self.allocator);
var data = std.ArrayList(u8).init(self.allocator);
errdefer data.deinit();
const writer = data.writer();

if (options.settle_duration != consts.default_fragments_settle_duration) {
const line = try std.fmt.allocPrint(
self.allocator,
"{s} {d}",
try writer.print(
consts.settle_duration_dataline_literal ++ " {d}\n",
.{
consts.settle_duration_dataline_literal,
options.settle_duration,
},
);

try data.append(line);
}

if (options.use_view_transition != consts.default_fragments_use_view_transitions) {
const line = try std.fmt.allocPrint(
self.allocator,
"{s} {}",
try writer.print(
consts.use_view_transition_dataline_literal ++ " {}\n",
.{
consts.use_view_transition_dataline_literal,
options.use_view_transition,
},
);

try data.append(line);
}

const line = try std.fmt.allocPrint(
self.allocator,
"{s} {s}",
try writer.print(
consts.selector_dataline_literal ++ " {s}\n",
.{
consts.selector_dataline_literal,
selector,
},
);

try data.append(line);

try self.send(
.remove_fragments,
try data.toOwnedSlice(),
Expand All @@ -363,27 +315,25 @@ pub fn removeFragments(
);
}

/// `RemoveSignals` sends signals to the browser to be removed from the signals.
/// `removeSignals` sends signals to the browser to be removed from the signals.
///
/// See the [Datastar documentation](https://data-star.dev/reference/sse_events#datastar-remove-signals) for more information.
pub fn removeSignals(
self: *@This(),
paths: []const []const u8,
options: RemoveSignalsOptions,
) !void {
var data = std.ArrayList([]const u8).init(self.allocator);
var data = std.ArrayList(u8).init(self.allocator);
errdefer data.deinit();
const writer = data.writer();

for (paths) |path| {
const line = try std.fmt.allocPrint(
self.allocator,
"{s} {s}",
try writer.print(
consts.paths_dataline_literal ++ " {s}\n",
.{
consts.paths_dataline_literal,
path,
},
);

try data.append(line);
}

try self.send(
Expand All @@ -395,3 +345,18 @@ pub fn removeSignals(
},
);
}

/// `redirect` sends an `executeScript` event to redirect the user to a new URL.
pub fn redirect(
self: *@This(),
url: []const u8,
options: ExecuteScriptOptions,
) !void {
const script = try std.fmt.allocPrint(
self.allocator,
"setTimeout(() => window.location.href = '{s}')",
.{url},
);
errdefer self.allocator.free(script);
try self.executeScript(script, options);
}
Loading