├── .gitattributes ├── .github ├── CODEOWNERS ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug-report.yaml │ ├── config.yml │ ├── feature-request.yaml │ └── support-request.yaml └── workflows │ ├── build-docker-images.yml │ ├── clojure-sdk.yml │ ├── deploy-site.yml │ ├── enforce-branch-policy.yml │ ├── java-sdk-unit-tests.yml │ ├── sdk-dotnet-nuget.yml │ └── tests.yml ├── .gitignore ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── BUILDING.md ├── CHANGELOG.md ├── CONTRIBUTING.md ├── DOCKER.md ├── Dockerfile ├── Dockerfile-dev ├── LICENSE ├── Makefile ├── README.md ├── Taskfile.yml ├── VERSION ├── biome.json ├── build ├── .gitignore ├── cmd │ └── build │ │ └── main.go ├── consts.go ├── consts_clojure.qtpl ├── consts_datastar_client.qtpl ├── consts_datastar_readme.qtpl ├── consts_fsharp.qtpl ├── consts_go.qtpl ├── consts_haskell.qtpl ├── consts_hello_world.qtpl ├── consts_java.qtpl ├── consts_php.qtpl ├── consts_python.qtpl ├── consts_ruby.qtpl ├── consts_rust.qtpl ├── consts_typescript.qtpl ├── consts_zig.qtpl └── run.go ├── bundles ├── datastar-aliased.js ├── datastar-aliased.js.map ├── datastar-core.js ├── datastar-core.js.map ├── datastar.js └── datastar.js.map ├── examples ├── clojure │ └── hello-world │ │ ├── .gitignore │ │ ├── README.md │ │ ├── deps.edn │ │ ├── resources │ │ └── public │ │ │ └── hello-world.html │ │ └── src │ │ ├── dev │ │ └── user.clj │ │ └── main │ │ └── example │ │ ├── core.clj │ │ ├── main.clj │ │ ├── server.clj │ │ └── utils.clj ├── dotnet │ ├── .gitignore │ └── csharp │ │ └── HelloWorld │ │ ├── HelloWorld.csproj │ │ ├── Program.cs │ │ ├── Properties │ │ └── launchSettings.json │ │ ├── appsettings.Development.json │ │ ├── appsettings.json │ │ └── wwwroot │ │ └── hello-world.html ├── go │ └── hello-world │ │ ├── go.mod │ │ ├── go.sum │ │ ├── hello-world.html │ │ └── main.go ├── haskell │ └── hello-world │ │ ├── Main.hs │ │ └── hello-world.html ├── java │ └── hello-world │ │ ├── .gitignore │ │ ├── pom.xml │ │ └── src │ │ ├── main │ │ ├── java │ │ │ └── org │ │ │ │ └── example │ │ │ │ ├── Main.java │ │ │ │ └── servlets │ │ │ │ ├── HelloWorldServlet.java │ │ │ │ └── HtmlServlet.java │ │ └── resources │ │ │ └── hello-world.html │ │ └── test │ │ └── java │ │ └── org │ │ └── example │ │ └── AppTest.java ├── php │ └── hello-world │ │ ├── composer.json │ │ └── public │ │ ├── hello-world.html │ │ └── hello-world.php ├── python │ ├── django │ │ ├── README.md │ │ ├── datastar │ │ │ ├── __init__.py │ │ │ ├── asgi.py │ │ │ ├── settings.py │ │ │ ├── urls.py │ │ │ └── wsgi.py │ │ ├── db.sqlite3 │ │ ├── ds │ │ │ ├── __init__.py │ │ │ ├── admin.py │ │ │ ├── apps.py │ │ │ ├── migrations │ │ │ │ └── __init__.py │ │ │ ├── models.py │ │ │ ├── tests.py │ │ │ └── views.py │ │ ├── manage.py │ │ └── pyproject.toml │ ├── fastapi │ │ └── app.py │ ├── fasthtml │ │ ├── advanced.py │ │ └── simple.py │ ├── litestar │ │ └── app.py │ ├── pyproject.toml │ ├── quart │ │ └── app.py │ └── sanic │ │ └── app.py ├── ruby │ ├── hello-world │ │ ├── Gemfile │ │ ├── Gemfile.lock │ │ ├── hello-world.html │ │ └── hello-world.ru │ └── threads │ │ ├── Gemfile │ │ ├── Gemfile.lock │ │ └── threads.ru ├── rust │ ├── axum │ │ └── hello-world │ │ │ ├── .gitignore │ │ │ ├── Cargo.toml │ │ │ ├── Makefile │ │ │ ├── hello-world.html │ │ │ └── src │ │ │ └── main.rs │ ├── makefile.defs │ ├── rama │ │ └── hello-world │ │ │ ├── .gitignore │ │ │ ├── Cargo.toml │ │ │ ├── Makefile │ │ │ ├── hello-world.html │ │ │ └── src │ │ │ └── main.rs │ └── rocket │ │ └── hello-world │ │ ├── .gitignore │ │ ├── Cargo.toml │ │ ├── Makefile │ │ ├── hello-world.html │ │ └── src │ │ └── main.rs └── zig │ ├── httpz │ └── hello-world │ │ ├── .gitignore │ │ ├── build.zig │ │ ├── build.zig.zon │ │ └── src │ │ ├── hello-world.html │ │ └── main.zig │ └── tokamak │ └── hello-world │ ├── .gitignore │ ├── build.zig │ ├── build.zig.zon │ ├── hello-world.html │ └── src │ └── main.zig ├── fly.toml ├── go.mod ├── go.sum ├── library ├── .gitignore ├── README.md ├── package.json ├── pnpm-lock.yaml ├── src │ ├── bundles │ │ ├── datastar-aliased.ts │ │ ├── datastar-core.ts │ │ └── datastar.ts │ ├── engine │ │ ├── consts.ts │ │ ├── engine.ts │ │ ├── errors.ts │ │ ├── index.ts │ │ ├── signals.ts │ │ └── types.ts │ ├── index.ts │ ├── plugins │ │ ├── index.ts │ │ └── official │ │ │ ├── backend │ │ │ ├── actions │ │ │ │ ├── delete.ts │ │ │ │ ├── get.ts │ │ │ │ ├── patch.ts │ │ │ │ ├── post.ts │ │ │ │ ├── put.ts │ │ │ │ └── sse.ts │ │ │ ├── attributes │ │ │ │ └── indicator.ts │ │ │ ├── shared.ts │ │ │ └── watchers │ │ │ │ ├── executeScript.ts │ │ │ │ ├── mergeFragments.ts │ │ │ │ ├── mergeSignals.ts │ │ │ │ ├── removeFragments.ts │ │ │ │ └── removeSignals.ts │ │ │ ├── browser │ │ │ ├── actions │ │ │ │ └── clipboard.ts │ │ │ └── attributes │ │ │ │ ├── customValidity.ts │ │ │ │ ├── onIntersect.ts │ │ │ │ ├── onInterval.ts │ │ │ │ ├── onLoad.ts │ │ │ │ ├── onRaf.ts │ │ │ │ ├── onSignalChange.ts │ │ │ │ ├── persist.ts │ │ │ │ ├── replaceUrl.ts │ │ │ │ ├── scrollIntoView.ts │ │ │ │ └── viewTransition.ts │ │ │ ├── core │ │ │ └── attributes │ │ │ │ ├── computed.ts │ │ │ │ ├── signals.ts │ │ │ │ └── star.ts │ │ │ ├── dom │ │ │ └── attributes │ │ │ │ ├── attr.ts │ │ │ │ ├── bind.ts │ │ │ │ ├── class.ts │ │ │ │ ├── on.ts │ │ │ │ ├── ref.ts │ │ │ │ ├── show.ts │ │ │ │ └── text.ts │ │ │ └── logic │ │ │ └── actions │ │ │ ├── fit.ts │ │ │ ├── setAll.ts │ │ │ └── toggleAll.ts │ ├── utils │ │ ├── dom.ts │ │ ├── paths.ts │ │ ├── tags.ts │ │ ├── text.ts │ │ ├── timing.ts │ │ └── view-transtions.ts │ └── vendored │ │ ├── fetch-event-source.ts │ │ ├── idiomorph.esm.d.ts │ │ ├── idiomorph.esm.js │ │ └── preact-core.ts └── tsconfig.json ├── sdk ├── README.md ├── clojure │ ├── .clj-kondo │ │ ├── config.edn │ │ └── hooks │ │ │ └── test_hooks.clj │ ├── .gitignore │ ├── CHANGELOG.md │ ├── README.md │ ├── adapter-http-kit │ │ ├── README.md │ │ ├── build.clj │ │ ├── deps.edn │ │ └── src │ │ │ └── main │ │ │ └── starfederation │ │ │ └── datastar │ │ │ └── clojure │ │ │ └── adapter │ │ │ ├── http_kit.clj │ │ │ └── http_kit │ │ │ └── impl.clj │ ├── adapter-ring │ │ ├── README.md │ │ ├── build.clj │ │ ├── deps.edn │ │ └── src │ │ │ └── main │ │ │ └── starfederation │ │ │ └── datastar │ │ │ └── clojure │ │ │ └── adapter │ │ │ ├── ring.clj │ │ │ └── ring │ │ │ └── impl.clj │ ├── bb.edn │ ├── deps.edn │ ├── doc │ │ ├── SSE-design-notes.md │ │ ├── Write-profiles.md │ │ ├── implementing-adapters.md │ │ └── maintainers-guide.md │ ├── malli-schemas │ │ ├── README.md │ │ ├── deps.edn │ │ └── src │ │ │ └── main │ │ │ └── starfederation │ │ │ └── datastar │ │ │ └── clojure │ │ │ ├── adapter │ │ │ ├── common_schemas.clj │ │ │ ├── http_kit_schemas.clj │ │ │ └── ring_schemas.clj │ │ │ ├── api │ │ │ ├── common_schemas.clj │ │ │ ├── fragments_schemas.clj │ │ │ ├── scripts_schemas.clj │ │ │ ├── signals_schemas.clj │ │ │ └── sse_schemas.clj │ │ │ └── api_schemas.clj │ ├── sdk-tests │ │ ├── README.md │ │ ├── deps.edn │ │ └── src │ │ │ ├── dev │ │ │ └── user.clj │ │ │ └── main │ │ │ └── starfederation │ │ │ └── datastar │ │ │ └── clojure │ │ │ └── sdk_test │ │ │ ├── core.clj │ │ │ └── main.clj │ ├── sdk │ │ ├── README.md │ │ ├── build.clj │ │ ├── deps.edn │ │ ├── resources │ │ │ └── clj-kondo.exports │ │ │ │ └── starfederation.datastar │ │ │ │ └── clojure │ │ │ │ └── config.edn │ │ └── src │ │ │ └── main │ │ │ └── starfederation │ │ │ └── datastar │ │ │ └── clojure │ │ │ ├── adapter │ │ │ ├── common.clj │ │ │ └── test.clj │ │ │ ├── api.clj │ │ │ ├── api │ │ │ ├── common.clj │ │ │ ├── fragments.clj │ │ │ ├── scripts.clj │ │ │ ├── signals.clj │ │ │ └── sse.clj │ │ │ ├── consts.clj │ │ │ ├── protocols.clj │ │ │ └── utils.clj │ └── src │ │ ├── bb │ │ └── tasks.clj │ │ ├── dev │ │ ├── examples │ │ │ ├── animation_gzip.clj │ │ │ ├── animation_gzip │ │ │ │ ├── animation.clj │ │ │ │ ├── brotli.clj │ │ │ │ ├── handlers.clj │ │ │ │ ├── rendering.clj │ │ │ │ ├── state.clj │ │ │ │ └── style.css │ │ │ ├── broadcast_http_kit.clj │ │ │ ├── broadcast_ring.clj │ │ │ ├── common.clj │ │ │ ├── data_dsl.clj │ │ │ ├── faulty_event.clj │ │ │ ├── form_behavior.clj │ │ │ ├── http_kit_disconnect.clj │ │ │ ├── jetty_disconnect.clj │ │ │ ├── malli.clj │ │ │ ├── multiple_fragments.clj │ │ │ ├── redirect.clj │ │ │ ├── remove_fragments.clj │ │ │ ├── scripts.clj │ │ │ ├── snippets.clj │ │ │ ├── snippets │ │ │ │ ├── load_more.clj │ │ │ │ ├── polling1.clj │ │ │ │ ├── polling2.clj │ │ │ │ ├── redirect1.clj │ │ │ │ ├── redirect2.clj │ │ │ │ └── redirect3.clj │ │ │ ├── tiny_gzip.clj │ │ │ └── utils.clj │ │ └── user.clj │ │ └── test │ │ ├── adapter-common │ │ └── test │ │ │ ├── common.clj │ │ │ ├── examples │ │ │ ├── common.clj │ │ │ ├── counter.clj │ │ │ └── form.clj │ │ │ └── utils.clj │ │ ├── adapter-http-kit │ │ ├── starfederation │ │ │ └── datastar │ │ │ │ └── clojure │ │ │ │ └── adapter │ │ │ │ └── http_kit │ │ │ │ └── impl_test.clj │ │ └── test │ │ │ ├── examples │ │ │ └── http_kit_handler.clj │ │ │ └── http_kit_test.clj │ │ ├── adapter-ring-jetty │ │ └── test │ │ │ └── ring_jetty_test.clj │ │ ├── adapter-ring │ │ ├── starfederation │ │ │ └── datastar │ │ │ │ └── clojure │ │ │ │ └── adapter │ │ │ │ └── ring │ │ │ │ └── impl_test.clj │ │ └── test │ │ │ └── examples │ │ │ └── ring_handler.clj │ │ ├── adapter-rj9a │ │ └── test │ │ │ └── rj9a_test.clj │ │ ├── core-sdk │ │ └── starfederation │ │ │ └── datastar │ │ │ └── clojure │ │ │ ├── adapter │ │ │ └── common_test.clj │ │ │ └── api_test.clj │ │ └── malli-schemas │ │ └── test │ │ └── api_schemas_test.clj ├── dotnet │ ├── .gitignore │ ├── Build.ps1 │ ├── README.md │ ├── assets │ │ └── datastar_icon.png │ └── fsharp │ │ └── src │ │ ├── .gitattributes │ │ ├── Consts.fs │ │ ├── Datastar.FSharp.fsproj │ │ ├── DependencyInjection │ │ ├── ServerSentEventScriptExtensions.fs │ │ ├── Services.fs │ │ └── ServicesProvider.fs │ │ ├── HttpHandlers.fs │ │ ├── ModelBinding │ │ ├── FromSignalAttribute.fs │ │ ├── MvcServiceProvider.fs │ │ └── SignalsModelBinder.fs │ │ ├── Scripts │ │ ├── BrowserConsole.fs │ │ └── Redirect.fs │ │ ├── ServerSentEventGenerator.fs │ │ ├── Types.fs │ │ └── Utility.fs ├── go │ ├── README.md │ ├── datastar │ │ ├── .gitattributes │ │ ├── consts.go │ │ ├── execute-script-sugar.go │ │ ├── execute.go │ │ ├── fragments-sugar.go │ │ ├── fragments.go │ │ ├── fragments_test.go │ │ ├── signals-sugar.go │ │ ├── signals.go │ │ ├── sse-compression.go │ │ ├── sse.go │ │ └── types.go │ └── examples │ │ ├── basic │ │ └── main.go │ │ └── hotreload │ │ └── main.go ├── haskell │ ├── CHANGELOG.md │ ├── LICENSE.md │ ├── Makefile │ ├── README.md │ ├── cabal.project │ ├── datastar.cabal │ ├── run │ └── src │ │ ├── ServerSentEventGenerator.hs │ │ ├── ServerSentEventGenerator │ │ ├── Class.hs │ │ ├── Constants.hs │ │ ├── Internal.hs │ │ ├── Server │ │ │ └── Snap.hs │ │ └── Types.hs │ │ ├── demo │ │ ├── Main.hs │ │ └── www │ │ │ ├── index.html │ │ │ └── keats.txt │ │ └── test │ │ └── Main.hs ├── java │ ├── README.md │ ├── core │ │ ├── pom.xml │ │ └── src │ │ │ ├── main │ │ │ └── java │ │ │ │ └── starfederation │ │ │ │ └── datastar │ │ │ │ ├── Consts.java │ │ │ │ ├── adapters │ │ │ │ ├── request │ │ │ │ │ ├── AbstractRequestAdapter.java │ │ │ │ │ └── RequestAdapter.java │ │ │ │ └── response │ │ │ │ │ ├── AbstractResponseAdapter.java │ │ │ │ │ └── ResponseAdapter.java │ │ │ │ ├── enums │ │ │ │ ├── EventType.java │ │ │ │ └── FragmentMergeMode.java │ │ │ │ ├── events │ │ │ │ ├── AbstractBuilder.java │ │ │ │ ├── AbstractDatastarEvent.java │ │ │ │ ├── CustomEvent.java │ │ │ │ ├── DatastarEvent.java │ │ │ │ ├── ExecuteScript.java │ │ │ │ ├── MergeFragments.java │ │ │ │ ├── MergeSignals.java │ │ │ │ ├── RemoveFragments.java │ │ │ │ └── RemoveSignals.java │ │ │ │ └── utils │ │ │ │ ├── DataStore.java │ │ │ │ ├── ServerSentEventGenerator.java │ │ │ │ └── SignalReader.java │ │ │ └── test │ │ │ └── java │ │ │ └── starfederation │ │ │ └── datastar │ │ │ └── unit │ │ │ ├── DataStoreTest.java │ │ │ ├── ExecuteScriptTest.java │ │ │ ├── MergeFragmentsTest.java │ │ │ ├── MergeSignalsTest.java │ │ │ ├── RemoveFragmentsTest.java │ │ │ ├── RemoveSignalsTest.java │ │ │ ├── ServerSentEventGeneratorTest.java │ │ │ └── SignalReaderTest.java │ ├── datastar-java-sdk-jaxrs │ │ ├── pom.xml │ │ └── src │ │ │ ├── main │ │ │ └── java │ │ │ │ └── starfederation │ │ │ │ └── datastar │ │ │ │ └── adapters │ │ │ │ ├── request │ │ │ │ ├── HttpServletRequestAdapter.java │ │ │ │ └── JaxRsRequestAdapter.java │ │ │ │ └── response │ │ │ │ ├── HttpServletResponseAdapter.java │ │ │ │ └── JaxRsResponseAdapter.java │ │ │ └── test │ │ │ └── java │ │ │ └── starfederation │ │ │ └── datastar │ │ │ └── adapter │ │ │ └── unit │ │ │ └── JaxRsRequestAdapterTest.java │ └── pom.xml ├── php │ ├── LICENSE.md │ ├── README.md │ ├── composer.json │ └── src │ │ ├── .gitattributes │ │ ├── Consts.php │ │ ├── ServerSentEventData.php │ │ ├── ServerSentEventGenerator.php │ │ ├── enums │ │ ├── EventType.php │ │ └── FragmentMergeMode.php │ │ └── events │ │ ├── EventInterface.php │ │ ├── EventTrait.php │ │ ├── ExecuteScript.php │ │ ├── MergeFragments.php │ │ ├── MergeSignals.php │ │ ├── RemoveFragments.php │ │ └── RemoveSignals.php ├── python │ ├── README.md │ ├── pyproject.toml │ └── src │ │ └── datastar_py │ │ ├── __init__.py │ │ ├── attributes.py │ │ ├── consts.py │ │ ├── django.py │ │ ├── fastapi.py │ │ ├── fasthtml.py │ │ ├── litestar.py │ │ ├── py.typed │ │ ├── quart.py │ │ ├── sanic.py │ │ ├── sse.py │ │ └── starlette.py ├── ruby │ ├── .gitignore │ ├── .rspec │ ├── Gemfile │ ├── Gemfile.lock │ ├── LICENSE.md │ ├── README.md │ ├── Rakefile │ ├── bin │ │ ├── console │ │ └── setup │ ├── datastar.gemspec │ ├── examples │ │ └── test.ru │ ├── lib │ │ ├── datastar.rb │ │ └── datastar │ │ │ ├── async_executor.rb │ │ │ ├── configuration.rb │ │ │ ├── consts.rb │ │ │ ├── dispatcher.rb │ │ │ ├── rails_async_executor.rb │ │ │ ├── rails_thread_executor.rb │ │ │ ├── railtie.rb │ │ │ ├── server_sent_event_generator.rb │ │ │ └── version.rb │ ├── sig │ │ └── datastar.rbs │ └── spec │ │ ├── dispatcher_spec.rb │ │ ├── spec_helper.rb │ │ └── support │ │ └── dispatcher_examples.rb ├── rust │ ├── .gitignore │ ├── Cargo.toml │ ├── Makefile │ ├── README.md │ └── src │ │ ├── axum.rs │ │ ├── consts.rs │ │ ├── execute_script.rs │ │ ├── lib.rs │ │ ├── merge_fragments.rs │ │ ├── merge_signals.rs │ │ ├── rama.rs │ │ ├── remove_fragments.rs │ │ ├── remove_signals.rs │ │ ├── rocket.rs │ │ └── testing.rs ├── test │ ├── .gitignore │ ├── README.md │ ├── get-cases │ │ ├── executeScriptWithAllOptions │ │ │ ├── input.json │ │ │ └── output.txt │ │ ├── executeScriptWithDefaults │ │ │ ├── input.json │ │ │ └── output.txt │ │ ├── executeScriptWithMultilineScript │ │ │ ├── input.json │ │ │ └── output.txt │ │ ├── executeScriptWithoutDefaults │ │ │ ├── input.json │ │ │ └── output.txt │ │ ├── mergeFragmentsWithAllOptions │ │ │ ├── input.json │ │ │ └── output.txt │ │ ├── mergeFragmentsWithDefaults │ │ │ ├── input.json │ │ │ └── output.txt │ │ ├── mergeFragmentsWithMultilineFragments │ │ │ ├── input.json │ │ │ └── output.txt │ │ ├── mergeFragmentsWithoutDefaults │ │ │ ├── input.json │ │ │ └── output.txt │ │ ├── mergeSignalsWithAllOptions │ │ │ ├── input.json │ │ │ └── output.txt │ │ ├── mergeSignalsWithDefaults │ │ │ ├── input.json │ │ │ └── output.txt │ │ ├── mergeSignalsWithMultilineSignals │ │ │ ├── input.json │ │ │ └── output.txt │ │ ├── mergeSignalsWithoutDefaults │ │ │ ├── input.json │ │ │ └── output.txt │ │ ├── removeFragmentsWithAllOptions │ │ │ ├── input.json │ │ │ └── output.txt │ │ ├── removeFragmentsWithDefaults │ │ │ ├── input.json │ │ │ └── output.txt │ │ ├── removeFragmentsWithoutDefaults │ │ │ ├── input.json │ │ │ └── output.txt │ │ ├── removeSignalsWithAllOptions │ │ │ ├── input.json │ │ │ └── output.txt │ │ ├── removeSignalsWithDefaults │ │ │ ├── input.json │ │ │ └── output.txt │ │ └── sendTwoEvents │ │ │ ├── input.json │ │ │ └── output.txt │ ├── normalize.sh │ ├── post-cases │ │ └── readSignalsFromBody │ │ │ ├── input.json │ │ │ └── output.txt │ ├── test-all.sh │ ├── test-get.sh │ └── test-post.sh ├── typescript │ ├── .gitignore │ ├── README.md │ ├── build.ts │ ├── deno.json │ ├── deno.lock │ ├── examples │ │ ├── deno.ts │ │ └── node.js │ ├── package.json │ ├── pnpm-lock.yaml │ ├── src │ │ ├── abstractServerSentEventGenerator.ts │ │ ├── consts.ts │ │ ├── node │ │ │ ├── node.ts │ │ │ └── serverSentEventGenerator.ts │ │ ├── types.ts │ │ └── web │ │ │ ├── deno.ts │ │ │ └── serverSentEventGenerator.ts │ └── tsconfig.json └── zig │ ├── .github │ └── PULL_REQUEST_TEMPLATE.md │ ├── .gitignore │ ├── README.md │ ├── build.zig │ ├── build.zig.zon │ ├── src │ ├── ServerSentEventGenerator.zig │ ├── consts.zig │ ├── httpz │ │ ├── ServerSentEventGenerator.zig │ │ └── root.zig │ ├── root.zig │ ├── testing.zig │ └── tokamak │ │ ├── ServerSentEventGenerator.zig │ │ └── root.zig │ └── test_runner.zig ├── site ├── .gitignore ├── cmd │ ├── asciiconvertor │ │ └── main.go │ └── site │ │ ├── .gitignore │ │ └── main.go ├── postcss.config.js ├── router.go ├── routes_bundler.go ├── routes_bundler.qtpl ├── routes_bundler.templ ├── routes_errors.go ├── routes_errors.templ ├── routes_errors_init.templ ├── routes_errors_internal.templ ├── routes_errors_runtime.templ ├── routes_essays.go ├── routes_examples.go ├── routes_examples_active_search.go ├── routes_examples_active_search.templ ├── routes_examples_animations.go ├── routes_examples_animations.templ ├── routes_examples_bad_apple.go ├── routes_examples_bulk_update.go ├── routes_examples_bulk_update.templ ├── routes_examples_click_to_edit.go ├── routes_examples_click_to_edit.templ ├── routes_examples_click_to_load.go ├── routes_examples_click_to_load.templ ├── routes_examples_csrf.go ├── routes_examples_csrf.templ ├── routes_examples_custom_validity.go ├── routes_examples_dbmon.go ├── routes_examples_dbmon.templ ├── routes_examples_delete_row.go ├── routes_examples_delete_row.templ ├── routes_examples_dialogs_browser.go ├── routes_examples_dialogs_browser.templ ├── routes_examples_disable_button.go ├── routes_examples_dispatch_custom_event.go ├── routes_examples_edit_row.go ├── routes_examples_edit_row.templ ├── routes_examples_execute_script.go ├── routes_examples_file_upload.go ├── routes_examples_file_upload.templ ├── routes_examples_form_data.go ├── routes_examples_indicator.go ├── routes_examples_indicator.templ ├── routes_examples_infinite_scroll.go ├── routes_examples_infinite_scroll.templ ├── routes_examples_inline_validation.go ├── routes_examples_inline_validation.templ ├── routes_examples_lazy_load.go ├── routes_examples_lazy_load.templ ├── routes_examples_lazy_tabs.go ├── routes_examples_lazy_tabs.templ ├── routes_examples_merge_options.go ├── routes_examples_merge_options.templ ├── routes_examples_model_bindings.go ├── routes_examples_model_bindings.templ ├── routes_examples_offline_sync.go ├── routes_examples_on_load.go ├── routes_examples_on_load.templ ├── routes_examples_polling.go ├── routes_examples_prefetch.go ├── routes_examples_prefetch.templ ├── routes_examples_progress_bar.go ├── routes_examples_progress_bar.templ ├── routes_examples_quick_primer_go.go ├── routes_examples_quick_primer_go.templ ├── routes_examples_quiz.go ├── routes_examples_quiz_slow.go ├── routes_examples_redirects.go ├── routes_examples_redirects.templ ├── routes_examples_replace_url.go ├── routes_examples_scroll_into_view.go ├── routes_examples_scroll_into_view.templ ├── routes_examples_signals_changed.go ├── routes_examples_signals_ifmissing.go ├── routes_examples_templ_counter.go ├── routes_examples_templ_counter.templ ├── routes_examples_title_update_backend.go ├── routes_examples_toggle_visibility.go ├── routes_examples_toggle_visibility.templ ├── routes_examples_update_signals.go ├── routes_examples_value_select.go ├── routes_examples_value_select.templ ├── routes_examples_view_transition_api.go ├── routes_examples_view_transition_api.templ ├── routes_guide.go ├── routes_home.go ├── routes_home.templ ├── routes_how_tos.go ├── routes_how_tos_load_more.go ├── routes_how_tos_polling.go ├── routes_how_tos_redirect.go ├── routes_memes.go ├── routes_memes.templ ├── routes_reference.go ├── routes_tests.go ├── routes_tests_indicator.go ├── routes_tests_indicator_element_removed.go ├── routes_tests_merge_fragment.go ├── routes_tests_merge_fragment_input_value.go ├── routes_tests_merge_fragment_on_load.go ├── routes_tests_merge_fragment_outer_multiple_targets.go ├── routes_tests_merge_fragment_signals.go ├── routes_tests_merge_fragment_whitespace.go ├── routes_tests_on_load.go ├── routes_tests_remove_fragment.go ├── routes_tests_remove_initiating_fragment.go ├── routes_tests_sse_error_event.go ├── routes_tests_sse_events.go ├── routes_videos.go ├── routes_videos.templ ├── shared.templ ├── shared_partials.go ├── smoketests │ ├── .gitignore │ ├── active_search_test.go │ ├── aliased_test.go │ ├── animations_test.go │ ├── attr_false_test.go │ ├── attr_object_false_test.go │ ├── bad_apple_test.go │ ├── bind_keys_test.go │ ├── bulk_update_test.go │ ├── checkbox_array_test.go │ ├── checkbox_boolean_checked_test.go │ ├── checkbox_boolean_test.go │ ├── checkbox_value_checked_test.go │ ├── checkbox_value_test.go │ ├── classes_test.go │ ├── click_to_edit_test.go │ ├── click_to_load_test.go │ ├── cloak_test.go │ ├── csrf_test.go │ ├── custom_events_test.go │ ├── custom_plugin_test.go │ ├── dbmon_test.go │ ├── delete_row_test.go │ ├── dialogs_browser_test.go │ ├── disable_button_test.go │ ├── dispatch_custom_event_test.go │ ├── edit_row_test.go │ ├── execute_script_test.go │ ├── file_upload_test.go │ ├── helpers.go │ ├── img_src_bind_test.go │ ├── indicator_element_removed_test.go │ ├── indicator_test.go │ ├── infinite_scroll_test.go │ ├── inline_validation_test.go │ ├── input_array_test.go │ ├── input_signal_test.go │ ├── input_value_test.go │ ├── key_casing_test.go │ ├── lazy_load_test.go │ ├── lazy_tabs_test.go │ ├── local_signals_test.go │ ├── merge_fragment_input_value_test.go │ ├── merge_fragment_on_load_test.go │ ├── merge_fragment_signals_test.go │ ├── merge_fragment_test.go │ ├── merge_fragment_whitespace_test.go │ ├── merge_options_test.go │ ├── model_binding_test.go │ ├── multi_select_test.go │ ├── multiline_expressions_test.go │ ├── multiline_signals_test.go │ ├── offline_sync_test.go │ ├── on_load_delay_test.go │ ├── on_load_test.go │ ├── on_signal_change_path_once_test.go │ ├── on_signal_change_path_test.go │ ├── on_signal_change_path_wildcard_test.go │ ├── on_signal_change_test.go │ ├── persist_signals_path_test.go │ ├── persist_signals_path_wildcard_test.go │ ├── persist_signals_test.go │ ├── persist_test.go │ ├── plugin_name_prefix_test.go │ ├── prefetch_test.go │ ├── progress_bar_test.go │ ├── radio_value_test.go │ ├── raf_update_test.go │ ├── redirects_test.go │ ├── ref_test.go │ ├── refs_test.go │ ├── remove_fragment_test.go │ ├── remove_initiating_fragment_test.go │ ├── replace_url_from_backend_test.go │ ├── replace_url_from_signals_test.go │ ├── scroll_into_view_test.go │ ├── select_multiple_test.go │ ├── select_single_test.go │ ├── session_storage_test.go │ ├── set_all_path_test.go │ ├── set_all_path_wildcard_test.go │ ├── set_all_paths_test.go │ ├── setup_test.go │ ├── signals_ifmissing_test.go │ ├── sortable_test.go │ ├── sse_error_event_test.go │ ├── sse_events_test.go │ ├── templ_counter_test.go │ ├── title_update_backend_test.go │ ├── todos_test.go │ ├── toggle_all_path_test.go │ ├── toggle_visibility_test.go │ ├── update_signals_test.go │ ├── value_select_test.go │ ├── view_transition_api_test.go │ └── web_component_test.go ├── src │ └── css │ │ └── site.css ├── static │ ├── code_snippets │ │ ├── getting_started │ │ │ ├── multiple_events.clojuresnippet │ │ │ ├── multiple_events.csharpsnippet │ │ │ ├── multiple_events.gosnippet │ │ │ ├── multiple_events.haskellsnippet │ │ │ ├── multiple_events.phpsnippet │ │ │ ├── multiple_events.rubysnippet │ │ │ ├── multiple_events.rustsnippet │ │ │ ├── multiple_events.typescriptsnippet │ │ │ ├── multiple_events.zigsnippet │ │ │ ├── setup.clojuresnippet │ │ │ ├── setup.csharpsnippet │ │ │ ├── setup.gosnippet │ │ │ ├── setup.haskellsnippet │ │ │ ├── setup.phpsnippet │ │ │ ├── setup.rubysnippet │ │ │ ├── setup.rustsnippet │ │ │ ├── setup.typescriptsnippet │ │ │ └── setup.zigsnippet │ │ ├── going_deeper │ │ │ ├── multiple_events.clojuresnippet │ │ │ ├── multiple_events.csharpsnippet │ │ │ ├── multiple_events.gosnippet │ │ │ ├── multiple_events.haskellsnippet │ │ │ ├── multiple_events.phpsnippet │ │ │ ├── multiple_events.rubysnippet │ │ │ ├── multiple_events.rustsnippet │ │ │ ├── multiple_events.typescriptsnippet │ │ │ └── multiple_events.zigsnippet │ │ └── how_tos │ │ │ ├── load_more.clojuresnippet │ │ │ ├── load_more.csharpsnippet │ │ │ ├── load_more.gosnippet │ │ │ ├── load_more.phpsnippet │ │ │ ├── polling_1.clojuresnippet │ │ │ ├── polling_1.csharpsnippet │ │ │ ├── polling_1.gosnippet │ │ │ ├── polling_1.haskellsnippet │ │ │ ├── polling_1.phpsnippet │ │ │ ├── polling_1.rubysnippet │ │ │ ├── polling_1.rustsnippet │ │ │ ├── polling_1.typescriptsnippet │ │ │ ├── polling_1.zigsnippet │ │ │ ├── polling_2.clojuresnippet │ │ │ ├── polling_2.csharpsnippet │ │ │ ├── polling_2.gosnippet │ │ │ ├── polling_2.haskellsnippet │ │ │ ├── polling_2.phpsnippet │ │ │ ├── polling_2.rubysnippet │ │ │ ├── polling_2.rustsnippet │ │ │ ├── polling_2.typescriptsnippet │ │ │ ├── polling_2.zigsnippet │ │ │ ├── redirect_1.clojuresnippet │ │ │ ├── redirect_1.csharpsnippet │ │ │ ├── redirect_1.gosnippet │ │ │ ├── redirect_1.haskellsnippet │ │ │ ├── redirect_1.phpsnippet │ │ │ ├── redirect_1.rubysnippet │ │ │ ├── redirect_1.rustsnippet │ │ │ ├── redirect_1.typescriptsnippet │ │ │ ├── redirect_1.zigsnippet │ │ │ ├── redirect_2.clojuresnippet │ │ │ ├── redirect_2.csharpsnippet │ │ │ ├── redirect_2.gosnippet │ │ │ ├── redirect_2.haskellsnippet │ │ │ ├── redirect_2.phpsnippet │ │ │ ├── redirect_2.rubysnippet │ │ │ ├── redirect_2.rustsnippet │ │ │ ├── redirect_2.typescriptsnippet │ │ │ ├── redirect_2.zigsnippet │ │ │ ├── redirect_3.clojuresnippet │ │ │ ├── redirect_3.csharpsnippet │ │ │ ├── redirect_3.gosnippet │ │ │ ├── redirect_3.phpsnippet │ │ │ ├── redirect_3.rubysnippet │ │ │ └── redirect_3.zigsnippet │ ├── css │ │ └── .gitignore │ ├── favicon │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-512x512.png │ │ ├── apple-touch-icon.png │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── favicon.ico │ │ └── site.webmanifest │ ├── images │ │ ├── badapple.zst │ │ ├── datastar.svg │ │ ├── datastar_icon.svg │ │ ├── essays │ │ │ ├── datastar_dependencies.png │ │ │ └── fullstack.jpg │ │ ├── examples │ │ │ └── tokyo.png │ │ ├── fujs.svg │ │ ├── memes │ │ │ ├── bender.png │ │ │ ├── bigsky.png │ │ │ ├── both.png │ │ │ ├── club.png │ │ │ ├── compile_time.png │ │ │ ├── crap.png │ │ │ ├── force_feed.png │ │ │ ├── history.png │ │ │ ├── hypermedia.png │ │ │ ├── one.png │ │ │ ├── only_clicks.png │ │ │ ├── polling.png │ │ │ ├── remember.png │ │ │ ├── rule7b.png │ │ │ ├── sse_vs_xhr.png │ │ │ ├── target_id_not_found.png │ │ │ └── use_datastar.png │ │ ├── rocket-circle.png │ │ ├── rocket-social-preview-white.png │ │ ├── rocket-social-preview.png │ │ ├── rocket.gif │ │ ├── rocket.png │ │ ├── rocket.svg │ │ └── rocket.webp │ ├── js │ │ ├── .gitignore │ │ ├── sortable.js │ │ └── web_component.js │ └── md │ │ ├── essays │ │ ├── another_dependency.md │ │ ├── event_streams_all_the_way_down.md │ │ ├── grugs_around_fire.md │ │ ├── haikus.md │ │ ├── htmx_sucks.md │ │ ├── i_am_a_teapot.md │ │ ├── the_road_to_v1.md │ │ ├── why_another_framework.md │ │ └── yes_you_want_a_build_step.md │ │ ├── examples │ │ ├── active_search.md │ │ ├── animations.md │ │ ├── bad_apple.md │ │ ├── bind_keys.md │ │ ├── bulk_update.md │ │ ├── classes.md │ │ ├── click_outside.md │ │ ├── click_to_edit.md │ │ ├── click_to_load.md │ │ ├── cloak.md │ │ ├── csrf.md │ │ ├── custom_events.md │ │ ├── custom_validity.md │ │ ├── dbmon.md │ │ ├── debounce_and_throttle.md │ │ ├── delete_row.md │ │ ├── dialogs_browser.md │ │ ├── disable_button.md │ │ ├── dispatch_custom_event.md │ │ ├── edit_row.md │ │ ├── execute_script.md │ │ ├── file_upload.md │ │ ├── form_data.md │ │ ├── ignore_attributes.md │ │ ├── img_src_bind.md │ │ ├── indicator.md │ │ ├── infinite_scroll.md │ │ ├── inline_validation.md │ │ ├── invalid_signals.md │ │ ├── key_casing.md │ │ ├── lazy_load.md │ │ ├── lazy_tabs.md │ │ ├── merge_options.md │ │ ├── model_binding.md │ │ ├── multi_select.md │ │ ├── multiline_expressions.md │ │ ├── multiline_signals.md │ │ ├── offline_sync.md │ │ ├── on_load.md │ │ ├── persist.md │ │ ├── plugin_order.md │ │ ├── polling.md │ │ ├── prefetch.md │ │ ├── progress_bar.md │ │ ├── quick_primer_go.md │ │ ├── raf_update.md │ │ ├── redirects.md │ │ ├── refs.md │ │ ├── regex.md │ │ ├── replace_url_from_backend.md │ │ ├── replace_url_from_signals.md │ │ ├── scroll_into_view.md │ │ ├── session_storage.md │ │ ├── signals_change.md │ │ ├── signals_ifmissing.md │ │ ├── signals_ifmissing_onload.md │ │ ├── sortable.md │ │ ├── templ_counter.md │ │ ├── timing.md │ │ ├── title_update_backend.md │ │ ├── toggle_visibility.md │ │ ├── update_signals.md │ │ ├── value_select.md │ │ ├── view_transition_api.md │ │ ├── view_transition_on_click.md │ │ └── web_component.md │ │ ├── guide │ │ ├── datastar_expressions.md │ │ ├── getting_started.md │ │ ├── going_deeper.md │ │ └── stop_overcomplicating_it.md │ │ ├── how_tos │ │ ├── how_to_bind_keydown_events_to_specific_keys.md │ │ ├── how_to_load_more_list_items.md │ │ ├── how_to_poll_the_backend_at_regular_intervals.md │ │ ├── how_to_redirect_the_page_from_the_backend.md │ │ └── how_to_stream_sse_events_with_a_user_defined_delay.md │ │ ├── reference │ │ ├── action_plugins.md │ │ ├── attribute_plugins.md │ │ ├── custom_builds.md │ │ ├── overview.md │ │ ├── sdks.md │ │ ├── security.md │ │ └── sse_events.md │ │ └── tests │ │ ├── aliased.md │ │ ├── attr_false.md │ │ ├── attr_object_false.md │ │ ├── checkbox_array.md │ │ ├── checkbox_boolean.md │ │ ├── checkbox_boolean_checked.md │ │ ├── checkbox_value.md │ │ ├── checkbox_value_checked.md │ │ ├── custom_plugin.md │ │ ├── indicator.md │ │ ├── indicator_element_removed.md │ │ ├── input_array.md │ │ ├── input_signal.md │ │ ├── input_value.md │ │ ├── key_casing.md │ │ ├── local_signals.md │ │ ├── merge_fragment.md │ │ ├── merge_fragment_containing_on_event.md │ │ ├── merge_fragment_input_value.md │ │ ├── merge_fragment_on_load.md │ │ ├── merge_fragment_outer_multiple_targets.md │ │ ├── merge_fragment_signals.md │ │ ├── merge_fragment_whitespace.md │ │ ├── on_load.md │ │ ├── on_load_delay.md │ │ ├── on_signal_change.md │ │ ├── on_signal_change_path.md │ │ ├── on_signal_change_path_once.md │ │ ├── on_signal_change_path_wildcard.md │ │ ├── persist_signals.md │ │ ├── persist_signals_path.md │ │ ├── persist_signals_path_wildcard.md │ │ ├── plugin_name_prefix.md │ │ ├── radio_value.md │ │ ├── ref.md │ │ ├── remove_fragment.md │ │ ├── remove_initiating_fragment.md │ │ ├── select_multiple.md │ │ ├── select_single.md │ │ ├── set_all_path.md │ │ ├── set_all_path_wildcard.md │ │ ├── set_all_paths.md │ │ ├── sse_error_event.md │ │ ├── sse_events.md │ │ └── toggle_all_path.md └── tailwind.config.js └── tools ├── intellij-plugin ├── .github │ ├── dependabot.yml │ └── workflows │ │ ├── build.yml │ │ ├── release.yml │ │ └── run-ui-tests.yml ├── .gitignore ├── .run │ ├── Run Plugin.run.xml │ ├── Run Tests.run.xml │ └── Run Verifications.run.xml ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── build.gradle.kts ├── gradle.properties ├── gradle │ ├── libs.versions.toml │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── qodana.yml ├── resources │ └── img │ │ └── datastar-intellij-plugin.png ├── schema.json ├── settings.gradle.kts ├── src │ └── main │ │ └── resources │ │ ├── META-INF │ │ ├── plugin.xml │ │ └── pluginIcon.svg │ │ ├── datastar-attributes.web-types.json │ │ ├── datastar-icon.png │ │ └── messages │ │ └── MyBundle.properties ├── test.css └── test.html └── vscode-extension ├── .gitignore ├── LICENSE.md ├── README.md ├── package-lock.json ├── package.json └── src ├── data-attributes.json └── icon.png /.gitattributes: -------------------------------------------------------------------------------- 1 | 2 | bundles linguist-generated=true 3 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @delaneyj @bencroker 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: starfederation 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | -------------------------------------------------------------------------------- /.github/workflows/enforce-branch-policy.yml: -------------------------------------------------------------------------------- 1 | name: Enforce Branch Policy 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | push: 8 | branches: 9 | - main 10 | workflow_dispatch: 11 | jobs: 12 | enforce-branch-policy: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Enforce main branch pull request policy 16 | if: github.event_name == 'pull_request' && github.ref == 'refs/heads/main' 17 | run: | 18 | echo "Pull requests to the main branch are not allowed." 19 | exit 1 20 | - name: Enforce main branch push policy 21 | run: | 22 | echo "Push events to the main branch are allowed." 23 | exit 0 -------------------------------------------------------------------------------- /.github/workflows/java-sdk-unit-tests.yml: -------------------------------------------------------------------------------- 1 | name: Java SDK Unit Tests 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - 'sdk/java/**' 7 | 8 | jobs: 9 | test-java: 10 | runs-on: ubuntu-latest 11 | defaults: 12 | run: 13 | working-directory: ./sdk/java 14 | steps: 15 | - name: Checkout repository 16 | uses: actions/checkout@v4 17 | 18 | - name: Set up Java 17 19 | uses: actions/setup-java@v4 20 | with: 21 | distribution: 'temurin' 22 | java-version: '17' 23 | 24 | - name: Build and run tests 25 | 26 | run: mvn --batch-mode verify 27 | 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | tailwindcli 2 | datastar_site 3 | data 4 | .task 5 | .idea 6 | .DS_Store 7 | node_modules 8 | datastar-website 9 | *_bin 10 | *.qtpl.go 11 | */java/*/target/ 12 | *.pyc 13 | __pycache__ 14 | __debug_bin* 15 | 16 | # search index 17 | /data-star.bleve -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "jdinabox.quicktemplate-vscode", 4 | "golang.go", 5 | "a-h.templ" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "go.testTimeout": "200s", 3 | "go.coverOnSingleTestFile": true, 4 | "go.coverOnSingleTest": true, 5 | "editor.foldingStrategy": "indentation", 6 | "makefile.configureOnOpen": false, 7 | "editor.formatOnSave": true, 8 | "[typescript]": { 9 | "editor.defaultFormatter": "biomejs.biome" 10 | }, 11 | "[json]": { 12 | "editor.defaultFormatter": "biomejs.biome" 13 | }, 14 | "[html]": { 15 | "editor.formatOnSave": false 16 | }, 17 | "[markdown]": { 18 | "editor.formatOnSave": false 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "shell", 6 | "label": "build datastar", 7 | "command": "task", 8 | "args": [ 9 | "support" 10 | ] 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM docker.io/golang:1.24.2-alpine AS build 2 | 3 | RUN apk add --no-cache upx 4 | ENV PORT=8080 5 | 6 | WORKDIR /src 7 | COPY . . 8 | RUN go mod download 9 | COPY site ./site 10 | RUN --mount=type=cache,target=/root/.cache/go-build \ 11 | go build -ldflags="-s" -o /out/site site/cmd/site/main.go 12 | RUN upx -9 -k /out/site 13 | 14 | FROM alpine 15 | RUN chmod a=rwx,u+t /tmp 16 | COPY --from=build /out/site / 17 | ENTRYPOINT ["/site"] -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 1.0.0-beta.11 -------------------------------------------------------------------------------- /build/.gitignore: -------------------------------------------------------------------------------- 1 | *_templ.go 2 | twcli -------------------------------------------------------------------------------- /build/cmd/build/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "time" 6 | 7 | build "github.com/starfederation/datastar/build" 8 | ) 9 | 10 | func main() { 11 | start := time.Now() 12 | log.Print("Datastar built in TS compiler!") 13 | defer func() { 14 | log.Printf("Datastar built in %s", time.Since(start)) 15 | }() 16 | 17 | if err := build.Build(); err != nil { 18 | log.Fatal(err) 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /examples/clojure/hello-world/.gitignore: -------------------------------------------------------------------------------- 1 | .nrepl-port 2 | .cpcache 3 | -------------------------------------------------------------------------------- /examples/clojure/hello-world/README.md: -------------------------------------------------------------------------------- 1 | # Hello world example 2 | 3 | ## Running the example 4 | 5 | - repl: 6 | 7 | ``` 8 | clojure -M:repl -m nrepl.cmdline --middleware "[cider.nrepl/cider-middleware]" 9 | ``` 10 | 11 | - main: 12 | 13 | ``` 14 | clojure -M -m example.main 15 | ``` 16 | -------------------------------------------------------------------------------- /examples/clojure/hello-world/src/dev/user.clj: -------------------------------------------------------------------------------- 1 | (ns user 2 | (:require 3 | [clj-reload.core :as reload])) 4 | 5 | 6 | (alter-var-root #'*warn-on-reflection* (constantly true)) 7 | 8 | 9 | (reload/init 10 | {:no-reload ['user]}) 11 | 12 | 13 | (defn reload! [] 14 | (reload/reload)) 15 | 16 | 17 | (comment 18 | (reload!) 19 | *e) 20 | 21 | 22 | -------------------------------------------------------------------------------- /examples/clojure/hello-world/src/main/example/main.clj: -------------------------------------------------------------------------------- 1 | (ns example.main 2 | (:require 3 | [example.core :as c] 4 | [example.server :as server])) 5 | 6 | 7 | (defn -main [& _] 8 | (let [server (server/start! c/handler)] 9 | (.addShutdownHook (Runtime/getRuntime) 10 | (Thread. (fn [] 11 | (server/stop! server) 12 | (shutdown-agents)))))) 13 | -------------------------------------------------------------------------------- /examples/clojure/hello-world/src/main/example/utils.clj: -------------------------------------------------------------------------------- 1 | (ns example.utils 2 | (:require 3 | [charred.api :as charred] 4 | [starfederation.datastar.clojure.api :as d*])) 5 | 6 | 7 | (def ^:private bufSize 1024) 8 | (def read-json (charred/parse-json-fn {:async? false :bufsize bufSize})) 9 | 10 | (defn get-signals [req] 11 | (-> req d*/get-signals read-json)) 12 | 13 | 14 | -------------------------------------------------------------------------------- /examples/dotnet/csharp/HelloWorld/HelloWorld.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /examples/dotnet/csharp/HelloWorld/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/dotnet/csharp/HelloWorld/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*" 9 | } 10 | -------------------------------------------------------------------------------- /examples/java/hello-world/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | !**/src/main/**/target/ 4 | !**/src/test/**/target/ 5 | 6 | ### IntelliJ IDEA ### 7 | .idea/modules.xml 8 | .idea/jarRepositories.xml 9 | .idea/compiler.xml 10 | .idea/libraries/ 11 | *.iws 12 | *.iml 13 | *.ipr 14 | 15 | ### Eclipse ### 16 | .apt_generated 17 | .classpath 18 | .factorypath 19 | .project 20 | .settings 21 | .springBeans 22 | .sts4-cache 23 | 24 | ### NetBeans ### 25 | /nbproject/private/ 26 | /nbbuild/ 27 | /dist/ 28 | /nbdist/ 29 | /.nb-gradle/ 30 | build/ 31 | !**/src/main/**/build/ 32 | !**/src/test/**/build/ 33 | 34 | ### VS Code ### 35 | .vscode/ 36 | 37 | ### Mac OS ### 38 | .DS_Store -------------------------------------------------------------------------------- /examples/java/hello-world/src/test/java/org/example/AppTest.java: -------------------------------------------------------------------------------- 1 | package org.example; 2 | 3 | 4 | /** 5 | * Unit test for simple App. 6 | */ 7 | public class AppTest 8 | { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /examples/php/hello-world/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require": { 3 | "starfederation/datastar-php": "1.0.0-beta.7" 4 | }, 5 | "minimum-stability": "dev", 6 | "prefer-stable": true 7 | } 8 | -------------------------------------------------------------------------------- /examples/php/hello-world/public/hello-world.php: -------------------------------------------------------------------------------- 1 | mergeFragments('
' 15 | . substr($message, 0, $i + 1) 16 | . '
' 17 | ); 18 | 19 | // Sleep for the provided delay in milliseconds. 20 | usleep($delay * 1000); 21 | } 22 | -------------------------------------------------------------------------------- /examples/python/django/datastar/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starfederation/datastar/03164b47af392a8e25275e14c46ce2a9a1935a37/examples/python/django/datastar/__init__.py -------------------------------------------------------------------------------- /examples/python/django/datastar/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for datastar project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/4.2/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.asgi import get_asgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "datastar.settings") 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /examples/python/django/db.sqlite3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starfederation/datastar/03164b47af392a8e25275e14c46ce2a9a1935a37/examples/python/django/db.sqlite3 -------------------------------------------------------------------------------- /examples/python/django/ds/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starfederation/datastar/03164b47af392a8e25275e14c46ce2a9a1935a37/examples/python/django/ds/__init__.py -------------------------------------------------------------------------------- /examples/python/django/ds/admin.py: -------------------------------------------------------------------------------- 1 | # Register your models here. 2 | -------------------------------------------------------------------------------- /examples/python/django/ds/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class DsConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "ds" 7 | -------------------------------------------------------------------------------- /examples/python/django/ds/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starfederation/datastar/03164b47af392a8e25275e14c46ce2a9a1935a37/examples/python/django/ds/migrations/__init__.py -------------------------------------------------------------------------------- /examples/python/django/ds/models.py: -------------------------------------------------------------------------------- 1 | # Create your models here. 2 | -------------------------------------------------------------------------------- /examples/python/django/ds/tests.py: -------------------------------------------------------------------------------- 1 | # Create your tests here. 2 | -------------------------------------------------------------------------------- /examples/python/django/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "datastar-django-example" 3 | version = "1.0.0" 4 | description = "Datastar Example Django Project" 5 | readme = "README.md" 6 | requires-python = ">=3.10" 7 | dependencies = [ 8 | "daphne>=4.2.0", 9 | "datastar-py>=0.4.4", 10 | "django>=5.2.1", 11 | ] 12 | 13 | [tool.uv.sources] 14 | datastar-py = { path = "../../../sdk/python" } -------------------------------------------------------------------------------- /examples/python/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.ruff] 2 | line-length = 99 3 | exclude = [ 4 | "src/datastar_py/consts.py", 5 | ] 6 | [tool.ruff.lint] 7 | select = [ 8 | # pycodestyle 9 | "E", 10 | # Pyflakes 11 | "F", 12 | # pyupgrade 13 | "UP", 14 | # flake8-bugbear 15 | "B", 16 | # flake8-simplify 17 | "SIM", 18 | # isort 19 | "I", 20 | ] 21 | extend-ignore = [ 22 | "E501", # line too long 23 | ] 24 | fixable = ["ALL"] 25 | -------------------------------------------------------------------------------- /examples/ruby/hello-world/Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source 'https://rubygems.org' 4 | 5 | gem 'puma' 6 | gem 'rack' 7 | # gem 'datastar' 8 | gem 'datastar', path: '../../../sdk/ruby' 9 | -------------------------------------------------------------------------------- /examples/ruby/hello-world/Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: ../../../sdk/ruby 3 | specs: 4 | datastar (1.0.0.beta.1) 5 | rack (~> 3.0) 6 | 7 | GEM 8 | remote: https://rubygems.org/ 9 | specs: 10 | nio4r (2.7.4) 11 | puma (6.6.0) 12 | nio4r (~> 2.0) 13 | rack (3.1.9) 14 | 15 | PLATFORMS 16 | arm64-darwin-24 17 | ruby 18 | 19 | DEPENDENCIES 20 | datastar! 21 | puma 22 | rack 23 | 24 | BUNDLED WITH 25 | 2.6.3 26 | -------------------------------------------------------------------------------- /examples/ruby/threads/Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source 'https://rubygems.org' 4 | 5 | gem 'puma' 6 | gem 'rack' 7 | # gem 'datastar' 8 | gem 'datastar', path: '../../../sdk/ruby' 9 | -------------------------------------------------------------------------------- /examples/ruby/threads/Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: ../../../sdk/ruby 3 | specs: 4 | datastar (1.0.0.beta.1) 5 | rack (~> 3.0) 6 | 7 | GEM 8 | remote: https://rubygems.org/ 9 | specs: 10 | nio4r (2.7.4) 11 | puma (6.6.0) 12 | nio4r (~> 2.0) 13 | rack (3.1.9) 14 | 15 | PLATFORMS 16 | arm64-darwin-24 17 | ruby 18 | 19 | DEPENDENCIES 20 | datastar! 21 | puma 22 | rack 23 | 24 | BUNDLED WITH 25 | 2.6.3 26 | -------------------------------------------------------------------------------- /examples/rust/axum/hello-world/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock -------------------------------------------------------------------------------- /examples/rust/axum/hello-world/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2024" 3 | name = "hello_world" 4 | version = "0.1.0" 5 | rust-version = "1.85.0" 6 | 7 | [dependencies] 8 | async-stream = "0.3.6" 9 | axum = "0.8.3" 10 | datastar = { path = "../../../../sdk/rust", features = ["axum"] } 11 | futures = "0.3.31" 12 | serde = { version = "1.0.219", features = ["derive"] } 13 | tokio = { version = "1.44.2", features = ["full"] } 14 | tokio-stream = "0.1.17" 15 | tracing = "0.1.41" 16 | tracing-subscriber = { version = "0.3.19", features = ["env-filter"] } 17 | -------------------------------------------------------------------------------- /examples/rust/axum/hello-world/Makefile: -------------------------------------------------------------------------------- 1 | include ../../makefile.defs 2 | -------------------------------------------------------------------------------- /examples/rust/rama/hello-world/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock -------------------------------------------------------------------------------- /examples/rust/rama/hello-world/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2024" 3 | name = "hello_world" 4 | version = "0.1.0" 5 | rust-version = "1.85.0" 6 | 7 | [dependencies] 8 | async-stream = "0.3.6" 9 | datastar = { path = "../../../../sdk/rust", features = ["rama"] } 10 | futures = "0.3.31" 11 | rama = { version = "0.2", features = ["http-full"] } 12 | serde = { version = "1.0.219", features = ["derive"] } 13 | tokio = { version = "1.44.2", features = ["full"] } 14 | tokio-stream = "0.1.17" 15 | tracing = "0.1.41" 16 | tracing-subscriber = { version = "0.3.19", features = ["env-filter"] } 17 | -------------------------------------------------------------------------------- /examples/rust/rama/hello-world/Makefile: -------------------------------------------------------------------------------- 1 | include ../../makefile.defs 2 | -------------------------------------------------------------------------------- /examples/rust/rocket/hello-world/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock -------------------------------------------------------------------------------- /examples/rust/rocket/hello-world/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2024" 3 | name = "hello_world" 4 | version = "0.1.0" 5 | rust-version = "1.85.0" 6 | 7 | [dependencies] 8 | datastar = { path = "../../../../sdk/rust", features = ["rocket"] } 9 | rocket = { version = "0.5.1", default-features = false, features = ["json"] } 10 | -------------------------------------------------------------------------------- /examples/rust/rocket/hello-world/Makefile: -------------------------------------------------------------------------------- 1 | include ../../makefile.defs 2 | -------------------------------------------------------------------------------- /examples/zig/httpz/hello-world/.gitignore: -------------------------------------------------------------------------------- 1 | .zig-cache 2 | zig-out -------------------------------------------------------------------------------- /examples/zig/httpz/hello-world/build.zig.zon: -------------------------------------------------------------------------------- 1 | .{ 2 | .name = .hello_world, 3 | .fingerprint = 0xf73eae91535aa5fa, 4 | .version = "0.0.0", 5 | .dependencies = .{ 6 | .httpz = .{ 7 | .url = "git+https://github.com/karlseguin/http.zig?ref=master#56258131ef4505543fef5484451867c13c5ff322", 8 | .hash = "httpz-0.0.0-PNVzrJSuBgDFvO7mtd2qDzaq8_hXIu1BqFuL1jwAV8Ac", 9 | }, 10 | .datastar = .{ 11 | .path = "../../../../sdk/zig/", 12 | }, 13 | }, 14 | .paths = .{ 15 | "build.zig", 16 | "build.zig.zon", 17 | "src", 18 | }, 19 | } 20 | -------------------------------------------------------------------------------- /examples/zig/tokamak/hello-world/.gitignore: -------------------------------------------------------------------------------- 1 | .zig-cache 2 | zig-out -------------------------------------------------------------------------------- /examples/zig/tokamak/hello-world/build.zig.zon: -------------------------------------------------------------------------------- 1 | .{ 2 | .name = .hello_world, 3 | .fingerprint = 0xf73eae91cce00cf1, 4 | .version = "0.0.0", 5 | .dependencies = .{ 6 | .tokamak = .{ 7 | .url = "git+https://github.com/cztomsik/tokamak#c9491b312eabbd5683bc236950fd2455c7b07752", 8 | .hash = "tokamak-2.0.0-FbnSeSbfAQAIiCUmj1RRpAio0RAHWvRoMFozHcnbeeMb", 9 | }, 10 | .datastar = .{ 11 | .path = "../../../../sdk/zig", 12 | }, 13 | }, 14 | .paths = .{ 15 | "build.zig", 16 | "build.zig.zon", 17 | "src", 18 | }, 19 | } 20 | -------------------------------------------------------------------------------- /fly.toml: -------------------------------------------------------------------------------- 1 | app = "datastar" 2 | primary_region = "den" 3 | 4 | [env] 5 | PORT = "8080" 6 | 7 | [http_service] 8 | internal_port = 8080 9 | force_https = true 10 | auto_stop_machines = true 11 | auto_start_machines = true 12 | min_machines_running = 0 13 | 14 | [[http_handlers]] 15 | action = "redirect" 16 | status_code = 301 17 | url = "https://data-star.dev" 18 | 19 | [[vm]] 20 | memory = "512mb" -------------------------------------------------------------------------------- /library/.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules -------------------------------------------------------------------------------- /library/pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '9.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | importers: 8 | 9 | .: 10 | devDependencies: 11 | typescript: 12 | specifier: ^5.6.3 13 | version: 5.7.2 14 | 15 | packages: 16 | 17 | typescript@5.7.2: 18 | resolution: {integrity: sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==} 19 | engines: {node: '>=14.17'} 20 | hasBin: true 21 | 22 | snapshots: 23 | 24 | typescript@5.7.2: {} 25 | -------------------------------------------------------------------------------- /library/src/bundles/datastar-core.ts: -------------------------------------------------------------------------------- 1 | import { apply, load, setAlias } from '../engine' 2 | 3 | apply() 4 | 5 | export { apply, load, setAlias } 6 | -------------------------------------------------------------------------------- /library/src/engine/index.ts: -------------------------------------------------------------------------------- 1 | import { DSP } from '../engine/consts' 2 | // @ts-ignore 3 | const _ = DSP // This is to force the import of DSP first in the compiled code 4 | 5 | import { Computed } from '../plugins/official/core/attributes/computed' 6 | import { Signals } from '../plugins/official/core/attributes/signals' 7 | import { Star } from '../plugins/official/core/attributes/star' 8 | import { apply, load, setAlias } from './engine' 9 | 10 | load(Star, Signals, Computed) 11 | 12 | export { apply, load, setAlias } 13 | -------------------------------------------------------------------------------- /library/src/index.ts: -------------------------------------------------------------------------------- 1 | // We don't use these exports, they are purely for access via package managers like NPM 2 | export * from './engine' 3 | -------------------------------------------------------------------------------- /library/src/plugins/official/backend/actions/delete.ts: -------------------------------------------------------------------------------- 1 | // Icon: material-symbols:delete-outline 2 | // Slug: Use a DELETE request to fetch data from a server using Server-Sent Events matching the Datastar SDK interface 3 | // Description: Remember, SSE is just a regular SSE request but with the ability to send 0-inf messages to the client. 4 | 5 | import { 6 | type ActionPlugin, 7 | PluginType, 8 | type RuntimeContext, 9 | } from '../../../../engine/types' 10 | import { type SSEArgs, sse } from './sse' 11 | 12 | export const DELETE: ActionPlugin = { 13 | type: PluginType.Action, 14 | name: 'delete', 15 | fn: async (ctx: RuntimeContext, url: string, args: SSEArgs) => { 16 | return sse(ctx, 'DELETE', url, { ...args }) 17 | }, 18 | } 19 | -------------------------------------------------------------------------------- /library/src/plugins/official/backend/actions/get.ts: -------------------------------------------------------------------------------- 1 | // Icon: ic:baseline-get-app 2 | // Slug: Use a GET request to fetch data from a server using Server-Sent Events matching the Datastar SDK interface 3 | // Description: Remember, SSE is just a regular SSE request but with the ability to send 0-inf messages to the client. 4 | 5 | import { 6 | type ActionPlugin, 7 | PluginType, 8 | type RuntimeContext, 9 | } from '../../../../engine/types' 10 | import { type SSEArgs, sse } from './sse' 11 | 12 | export const GET: ActionPlugin = { 13 | type: PluginType.Action, 14 | name: 'get', 15 | fn: async (ctx: RuntimeContext, url: string, args: SSEArgs) => { 16 | return sse(ctx, 'GET', url, { ...args }) 17 | }, 18 | } 19 | -------------------------------------------------------------------------------- /library/src/plugins/official/backend/actions/patch.ts: -------------------------------------------------------------------------------- 1 | // Icon: fluent:patch-24-filled 2 | // Slug: Use a PATCH request to fetch data from a server using Server-Sent Events matching the Datastar SDK interface 3 | // Description: Remember, SSE is just a regular SSE request but with the ability to send 0-inf messages to the client. 4 | 5 | import { 6 | type ActionPlugin, 7 | PluginType, 8 | type RuntimeContext, 9 | } from '../../../../engine/types' 10 | import { type SSEArgs, sse } from './sse' 11 | 12 | export const PATCH: ActionPlugin = { 13 | type: PluginType.Action, 14 | name: 'patch', 15 | fn: async (ctx: RuntimeContext, url: string, args: SSEArgs) => { 16 | return sse(ctx, 'PATCH', url, { ...args }) 17 | }, 18 | } 19 | -------------------------------------------------------------------------------- /library/src/plugins/official/backend/actions/post.ts: -------------------------------------------------------------------------------- 1 | // Icon: ri:signpost-fill 2 | // Slug: Use a POST request to fetch data from a server using Server-Sent Events matching the Datastar SDK interface 3 | // Description: Remember, SSE is just a regular SSE request but with the ability to send 0-inf messages to the client. 4 | 5 | import { 6 | type ActionPlugin, 7 | PluginType, 8 | type RuntimeContext, 9 | } from '../../../../engine/types' 10 | import { type SSEArgs, sse } from './sse' 11 | 12 | export const POST: ActionPlugin = { 13 | type: PluginType.Action, 14 | name: 'post', 15 | fn: async (ctx: RuntimeContext, url: string, args: SSEArgs) => { 16 | return sse(ctx, 'POST', url, { ...args }) 17 | }, 18 | } 19 | -------------------------------------------------------------------------------- /library/src/plugins/official/backend/actions/put.ts: -------------------------------------------------------------------------------- 1 | // Icon: material-symbols:arrows-input 2 | // Slug: Use a PUT request to fetch data from a server using Server-Sent Events matching the Datastar SDK interface 3 | // Description: Remember, SSE is just a regular SSE request but with the ability to send 0-inf messages to the client. 4 | 5 | import { 6 | type ActionPlugin, 7 | PluginType, 8 | type RuntimeContext, 9 | } from '../../../../engine/types' 10 | import { type SSEArgs, sse } from './sse' 11 | 12 | export const PUT: ActionPlugin = { 13 | type: PluginType.Action, 14 | name: 'put', 15 | fn: async (ctx: RuntimeContext, url: string, args: SSEArgs) => { 16 | return sse(ctx, 'PUT', url, { ...args }) 17 | }, 18 | } 19 | -------------------------------------------------------------------------------- /library/src/plugins/official/browser/actions/clipboard.ts: -------------------------------------------------------------------------------- 1 | // Authors: Delaney Gillilan 2 | // Icon: mdi:clipboard 3 | // Slug: Copy text to the clipboard 4 | // Description: This action copies text to the clipboard using the Clipboard API. 5 | 6 | import { runtimeErr } from '../../../../engine/errors' 7 | import { type ActionPlugin, PluginType } from '../../../../engine/types' 8 | 9 | export const Clipboard: ActionPlugin = { 10 | type: PluginType.Action, 11 | name: 'clipboard', 12 | fn: (ctx, text) => { 13 | if (!navigator.clipboard) { 14 | throw runtimeErr('ClipboardNotAvailable', ctx) 15 | } 16 | navigator.clipboard.writeText(text) 17 | }, 18 | } 19 | -------------------------------------------------------------------------------- /library/src/plugins/official/core/attributes/computed.ts: -------------------------------------------------------------------------------- 1 | import { 2 | type AttributePlugin, 3 | PluginType, 4 | Requirement, 5 | } from '../../../../engine/types' 6 | import { modifyCasing } from '../../../../utils/text' 7 | 8 | const name = 'computed' 9 | export const Computed: AttributePlugin = { 10 | type: PluginType.Attribute, 11 | name, 12 | keyReq: Requirement.Must, 13 | valReq: Requirement.Must, 14 | onLoad: ({ key, mods, signals, genRX }) => { 15 | key = modifyCasing(key, mods) 16 | const rx = genRX() 17 | signals.setComputed(key, rx) 18 | }, 19 | } 20 | -------------------------------------------------------------------------------- /library/src/plugins/official/core/attributes/star.ts: -------------------------------------------------------------------------------- 1 | import { 2 | type AttributePlugin, 3 | PluginType, 4 | Requirement, 5 | } from '../../../../engine/types' 6 | 7 | export const Star: AttributePlugin = { 8 | type: PluginType.Attribute, 9 | name: 'star', 10 | keyReq: Requirement.Denied, 11 | valReq: Requirement.Denied, 12 | onLoad: () => { 13 | alert('YOU ARE PROBABLY OVERCOMPLICATING IT') 14 | }, 15 | } 16 | -------------------------------------------------------------------------------- /library/src/plugins/official/logic/actions/setAll.ts: -------------------------------------------------------------------------------- 1 | // Authors: Delaney Gillilan 2 | // Icon: ion:checkmark-round 3 | // Slug: Set all signals that match the signal path 4 | // Description: Set all signals that match one or more space-separated paths in which `*` can be used as a wildcard 5 | 6 | import { type ActionPlugin, PluginType } from '../../../../engine/types' 7 | import { getMatchingSignalPaths } from '../../../../utils/paths' 8 | 9 | export const SetAll: ActionPlugin = { 10 | type: PluginType.Action, 11 | name: 'setAll', 12 | fn: ({ signals }, paths: string, newValue) => { 13 | const signalPaths = getMatchingSignalPaths(signals, paths) 14 | for (const path of signalPaths) { 15 | signals.setValue(path, newValue) 16 | } 17 | }, 18 | } 19 | -------------------------------------------------------------------------------- /library/src/utils/tags.ts: -------------------------------------------------------------------------------- 1 | export function tagToMs(args: Set) { 2 | if (!args || args.size <= 0) return 0 3 | for (const arg of args) { 4 | if (arg.endsWith('ms')) { 5 | return Number(arg.replace('ms', '')) 6 | } 7 | if (arg.endsWith('s')) { 8 | return Number(arg.replace('s', '')) * 1000 9 | } 10 | try { 11 | return Number.parseFloat(arg) 12 | } catch (e) {} 13 | } 14 | return 0 15 | } 16 | 17 | export function tagHas(tags: Set, tag: string, defaultValue = false) { 18 | if (!tags) return defaultValue 19 | return tags.has(tag.toLowerCase()) 20 | } 21 | -------------------------------------------------------------------------------- /library/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2023", 4 | "experimentalDecorators": true, 5 | "useDefineForClassFields": false, 6 | "module": "ESNext", 7 | "lib": ["ES2023", "DOM", "DOM.Iterable"], 8 | "skipLibCheck": true, 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "resolveJsonModule": true, 12 | "isolatedModules": true, 13 | /* Linting */ 14 | "allowJs": true, 15 | "strict": true, 16 | "noUnusedLocals": true, 17 | "noUnusedParameters": true, 18 | "noFallthroughCasesInSwitch": true, 19 | "declaration": true, 20 | "outDir": "dist", 21 | }, 22 | "include": ["src/**/*.ts", "src/**/*.js"] 23 | } 24 | -------------------------------------------------------------------------------- /sdk/clojure/.clj-kondo/config.edn: -------------------------------------------------------------------------------- 1 | {:lint-as {fr.jeremyschoffen.datastar.utils/defroutes clojure.core/def 2 | starfederation.datastar.clojure.utils/transient-> clojure.core/->} 3 | :hooks 4 | {:analyze-call 5 | {test.utils/with-server hooks.test-hooks/with-server}} 6 | :linters {:redundant-ignore {:exclude [:clojure-lsp/unused-public-var]}}} 7 | -------------------------------------------------------------------------------- /sdk/clojure/.clj-kondo/hooks/test_hooks.clj: -------------------------------------------------------------------------------- 1 | (ns hooks.test-hooks 2 | (:require 3 | [clj-kondo.hooks-api :as api])) 4 | 5 | 6 | (defn with-server [{:keys [node] :as exp}] 7 | (let [[s-name handler opts & body] (-> node :children rest) 8 | underscore (api/token-node '_) 9 | new-children (list* 10 | (api/token-node 'let) 11 | (api/vector-node 12 | [s-name handler 13 | underscore opts]) 14 | body) 15 | new-node (assoc node :children new-children)] 16 | (assoc exp :node new-node))) 17 | 18 | -------------------------------------------------------------------------------- /sdk/clojure/.gitignore: -------------------------------------------------------------------------------- 1 | .cpcache 2 | .nrepl-port 3 | .lsp 4 | .clj-kondo/** 5 | !.clj-kondo/config.edn 6 | !.clj-kondo/hooks** 7 | **/target 8 | test-resources/test.config.edn 9 | /.lsp-root 10 | /.nfnl.fnl 11 | /.nvim.fnl 12 | /.nvim.lua 13 | -------------------------------------------------------------------------------- /sdk/clojure/adapter-http-kit/README.md: -------------------------------------------------------------------------------- 1 | # Datastar http-kit adapter 2 | 3 | ## Installation 4 | 5 | For now the SDK and adapters are distributed as git dependencies using a `deps.edn` file. 6 | 7 | ```clojure 8 | {datastar/sdk {:git/url "https://github.com/starfederation/datastar/" 9 | :git/sha "LATEST SHA" 10 | :deps/root "sdk/clojure/sdk"} 11 | 12 | datastar/http-kit {:git/url "https://github.com/starfederation/datastar/" 13 | :git/sha "LATEST SHA" 14 | :deps/root "sdk/clojure/adapter-http-kit"}} 15 | ``` 16 | 17 | > [!important] 18 | > Replace `LATEST_SHA` in the git coordinates below by the actual latest commit sha of the repository. 19 | -------------------------------------------------------------------------------- /sdk/clojure/adapter-http-kit/deps.edn: -------------------------------------------------------------------------------- 1 | ;; NOTE: Track the next release of http-kit to switch to maven dep 2 | {:paths ["src/main"] 3 | :deps {http-kit/http-kit {:mvn/version "2.9.0-alpha4"}} 4 | :aliases {:build {:deps {io.github.clojure/tools.build {:git/tag "v0.10.9" 5 | :git/sha "e405aac"} 6 | slipset/deps-deploy {:mvn/version "0.2.2"}} 7 | :ns-default build} 8 | :neil {:project {:name dev.data-star/http-kit 9 | :version "1.0.0-beta.11"}}}} 10 | -------------------------------------------------------------------------------- /sdk/clojure/adapter-ring/deps.edn: -------------------------------------------------------------------------------- 1 | {:paths ["src/main"] 2 | :deps {org.ring-clojure/ring-core-protocols {:mvn/version "1.14.1"}} 3 | :aliases {:build {:deps {io.github.clojure/tools.build {:git/tag "v0.10.9" 4 | :git/sha "e405aac"} 5 | slipset/deps-deploy {:mvn/version "0.2.2"}} 6 | :ns-default build} 7 | :neil {:project {:name dev.data-star/ring 8 | :version "1.0.0-beta.11"}}}} 9 | -------------------------------------------------------------------------------- /sdk/clojure/malli-schemas/deps.edn: -------------------------------------------------------------------------------- 1 | {:paths ["src/main"] 2 | :deps {metosin/malli {:mvn/version "0.17.0"}}} 3 | -------------------------------------------------------------------------------- /sdk/clojure/malli-schemas/src/main/starfederation/datastar/clojure/adapter/ring_schemas.clj: -------------------------------------------------------------------------------- 1 | (ns starfederation.datastar.clojure.adapter.ring-schemas 2 | (:require 3 | [malli.core :as m] 4 | [starfederation.datastar.clojure.adapter.ring] 5 | [starfederation.datastar.clojure.adapter.common-schemas :as cs])) 6 | 7 | 8 | (m/=> starfederation.datastar.clojure.adapter.ring/->sse-response 9 | [:-> :map cs/->sse-response-options-schema :any]) 10 | 11 | 12 | -------------------------------------------------------------------------------- /sdk/clojure/malli-schemas/src/main/starfederation/datastar/clojure/api/scripts_schemas.clj: -------------------------------------------------------------------------------- 1 | (ns starfederation.datastar.clojure.api.scripts-schemas 2 | (:require 3 | [malli.core :as m] 4 | [starfederation.datastar.clojure.api.common-schemas :as cs] 5 | [starfederation.datastar.clojure.api.scripts])) 6 | 7 | (m/=> starfederation.datastar.clojure.api.scripts/->script 8 | [:-> cs/script-content-schema cs/execute-script-options-schemas cs/data-lines-schema]) 9 | 10 | -------------------------------------------------------------------------------- /sdk/clojure/malli-schemas/src/main/starfederation/datastar/clojure/api/signals_schemas.clj: -------------------------------------------------------------------------------- 1 | (ns starfederation.datastar.clojure.api.signals-schemas 2 | (:require 3 | [malli.core :as m] 4 | [starfederation.datastar.clojure.api.common-schemas :as cs] 5 | [starfederation.datastar.clojure.api.signals])) 6 | 7 | 8 | (m/=> starfederation.datastar.clojure.api.signals/->merge-signals 9 | [:-> cs/signals-schema cs/merge-signals-options-schemas cs/data-lines-schema]) 10 | 11 | 12 | (m/=> starfederation.datastar.clojure.api.signals/->remove-signals 13 | [:-> cs/signal-paths-schema cs/data-lines-schema]) 14 | -------------------------------------------------------------------------------- /sdk/clojure/malli-schemas/src/main/starfederation/datastar/clojure/api/sse_schemas.clj: -------------------------------------------------------------------------------- 1 | (ns starfederation.datastar.clojure.api.sse-schemas 2 | (:require 3 | [malli.core :as m] 4 | [starfederation.datastar.clojure.api.common-schemas :as cs] 5 | [starfederation.datastar.clojure.api.sse])) 6 | 7 | 8 | (m/=> starfederation.datastar.clojure.api.sse/send-event! 9 | [:function 10 | [:-> cs/sse-gen-schema cs/event-type-schema cs/data-lines-schema :any] 11 | [:-> cs/sse-gen-schema cs/event-type-schema cs/data-lines-schema cs/sse-options-schema :any]]) 12 | -------------------------------------------------------------------------------- /sdk/clojure/sdk-tests/README.md: -------------------------------------------------------------------------------- 1 | # SDK tests 2 | 3 | This is where the code for the [generic tests](/sdk/test) lives. 4 | 5 | ## Running the test app 6 | 7 | - repl: 8 | 9 | ``` 10 | clojure -M:repl -m nrepl.cmdline --middleware "[cider.nrepl/cider-middleware]" 11 | ``` 12 | 13 | - main: 14 | 15 | ``` 16 | clojure -M -m starfederation.datastar.clojure.sdk-test.main 17 | ``` 18 | -------------------------------------------------------------------------------- /sdk/clojure/sdk-tests/src/dev/user.clj: -------------------------------------------------------------------------------- 1 | (ns user 2 | (:require 3 | [clj-reload.core :as reload])) 4 | 5 | 6 | (alter-var-root #'*warn-on-reflection* (constantly true)) 7 | 8 | ;(rcf/enable!) 9 | 10 | 11 | (reload/init 12 | {:no-reload ['user]}) 13 | 14 | 15 | (defn reload! [] 16 | (reload/reload)) 17 | 18 | 19 | 20 | 21 | (defn clear-terminal! [] 22 | (binding [*out* (java.io.PrintWriter. System/out)] 23 | (print "\033c") 24 | (flush))) 25 | 26 | 27 | -------------------------------------------------------------------------------- /sdk/clojure/sdk/README.md: -------------------------------------------------------------------------------- 1 | # Generic Clojure SDK for Datastar 2 | 3 | This is where the code for the Generic SDK lives. 4 | 5 | ## Installation 6 | 7 | For now the SDK and adapters are distributed as git dependencies using a `deps.edn` file. 8 | If you roll your own adapter you only need: 9 | 10 | ```clojure 11 | {datastar/sdk {:git/url "https://github.com/starfederation/datastar/tree/develop" 12 | :git/sha "LATEST SHA" 13 | :deps/root "sdk/clojure/sdk"}} 14 | ``` 15 | 16 | > [!important] 17 | > This project is new and there isn't a release process yet other than using git shas. 18 | > Replace `LATEST_SHA` in the git coordinates below by the actual latest commit sha of the repository. 19 | -------------------------------------------------------------------------------- /sdk/clojure/sdk/deps.edn: -------------------------------------------------------------------------------- 1 | {:paths ["src/main" "resources"] 2 | :aliases {:build {:deps {io.github.clojure/tools.build {:git/tag "v0.10.9" 3 | :git/sha "e405aac"} 4 | slipset/deps-deploy {:mvn/version "0.2.2"}} 5 | :ns-default build} 6 | :neil {:project {:name dev.data-star/sdk 7 | :version "1.0.0-beta.11"}}}} 8 | -------------------------------------------------------------------------------- /sdk/clojure/sdk/resources/clj-kondo.exports/starfederation.datastar/clojure/config.edn: -------------------------------------------------------------------------------- 1 | {:lint-as 2 | {starfederation.datastar.clojure.utils/def-clone clojure.core/def}} 3 | 4 | -------------------------------------------------------------------------------- /sdk/clojure/sdk/src/main/starfederation/datastar/clojure/protocols.clj: -------------------------------------------------------------------------------- 1 | (ns starfederation.datastar.clojure.protocols) 2 | 3 | 4 | (defprotocol SSEGenerator 5 | (send-event! [this event-type data-lines opts] "Send sse event.") 6 | (get-lock [this] "Access to the lock used in the generator.") 7 | (close-sse! [this] "Close connection.") 8 | (sse-gen? [this] "Test wheter a value is a SSEGenerator.")) 9 | 10 | 11 | (extend-protocol SSEGenerator 12 | nil 13 | (sse-gen? [_] false) 14 | 15 | Object 16 | (sse-gen? [_] false)) 17 | 18 | 19 | -------------------------------------------------------------------------------- /sdk/clojure/src/dev/examples/common.clj: -------------------------------------------------------------------------------- 1 | (ns examples.common 2 | (:require 3 | [dev.onionpancakes.chassis.core :as h] 4 | [dev.onionpancakes.chassis.compiler :as hc] 5 | [starfederation.datastar.clojure.consts :as consts])) 6 | 7 | 8 | (def cdn-url 9 | (str "https://cdn.jsdelivr.net/gh/starfederation/datastar@" 10 | consts/version 11 | "/bundles/datastar.js")) 12 | 13 | 14 | (defn page-scaffold [body] 15 | (hc/compile 16 | [[h/doctype-html5] 17 | [:html 18 | [:head 19 | [:meta {:charset "UTF-8"}] 20 | [:script {:type "module" 21 | :src cdn-url}]] 22 | [:body body]]])) 23 | -------------------------------------------------------------------------------- /sdk/dotnet/assets/datastar_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starfederation/datastar/03164b47af392a8e25275e14c46ce2a9a1935a37/sdk/dotnet/assets/datastar_icon.png -------------------------------------------------------------------------------- /sdk/dotnet/fsharp/src/.gitattributes: -------------------------------------------------------------------------------- 1 | Consts.fs linguist-generated=true -------------------------------------------------------------------------------- /sdk/dotnet/fsharp/src/DependencyInjection/ServerSentEventScriptExtensions.fs: -------------------------------------------------------------------------------- 1 | namespace StarFederation.Datastar.Scripts 2 | 3 | open System.Runtime.CompilerServices 4 | open StarFederation.Datastar.FSharp.Scripts 5 | open StarFederation.Datastar.DependencyInjection 6 | 7 | [] 8 | type ServerSentEventScriptExtensions() = 9 | 10 | [] 11 | static member Redirect (sse:IDatastarServerSentEventService, url:string) = 12 | Redirect.Redirect (sse.Handler, url) 13 | 14 | [] 15 | static member BrowserConsoleAction (sse:IDatastarServerSentEventService, consoleAction:BrowserConsoleAction) = 16 | BrowserConsole.BrowserConsoleAction (sse.Handler, consoleAction) 17 | -------------------------------------------------------------------------------- /sdk/dotnet/fsharp/src/Scripts/Redirect.fs: -------------------------------------------------------------------------------- 1 | namespace StarFederation.Datastar.FSharp.Scripts 2 | 3 | open StarFederation.Datastar.FSharp 4 | 5 | [] 6 | type Redirect = 7 | static member Redirect (env, url, ?options:EventOptions) = 8 | let options = options |> Option.defaultValue EventOptions.defaults 9 | let scriptOptions = { ExecuteScriptOptions.defaults with EventId = options.EventId; Retry = options.Retry } 10 | ServerSentEventGenerator.ExecuteScript (env, $"window.location.href = '%s{url}';", scriptOptions) 11 | -------------------------------------------------------------------------------- /sdk/go/datastar/.gitattributes: -------------------------------------------------------------------------------- 1 | consts.go linguist-generated=true -------------------------------------------------------------------------------- /sdk/go/datastar/types.go: -------------------------------------------------------------------------------- 1 | package datastar 2 | 3 | const ( 4 | NewLine = "\n" 5 | DoubleNewLine = "\n\n" 6 | ) 7 | 8 | var ( 9 | newLineBuf = []byte(NewLine) 10 | doubleNewLineBuf = []byte(DoubleNewLine) 11 | ) 12 | 13 | type flusher interface { 14 | Flush() error 15 | } 16 | -------------------------------------------------------------------------------- /sdk/haskell/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | 1.0.0-beta.1 2025-02-05 4 | -------------------------------------------------------------------------------- /sdk/haskell/cabal.project: -------------------------------------------------------------------------------- 1 | constraints: HsOpenSSL == 0.11.7.2 2 | packages: . 3 | 4 | -------------------------------------------------------------------------------- /sdk/haskell/run: -------------------------------------------------------------------------------- 1 | cabal run exe:datastar-demo 2>&1 -------------------------------------------------------------------------------- /sdk/haskell/src/demo/www/keats.txt: -------------------------------------------------------------------------------- 1 | Darkling I listen; and, for many a time 2 | I have been half in love with easeful Death, 3 | Call'd him soft names in many a mused rhyme, 4 | To take into the air my quiet breath; 5 | Now more than ever seems it rich to die, 6 | To cease upon the midnight with no pain, 7 | While thou art pouring forth thy soul abroad 8 | In such an ecstasy! 9 | Still wouldst thou sing, and I have ears in vain— 10 | To thy high requiem become a sod. 11 | -------------------------------------------------------------------------------- /sdk/java/core/src/main/java/starfederation/datastar/adapters/request/AbstractRequestAdapter.java: -------------------------------------------------------------------------------- 1 | package starfederation.datastar.adapters.request; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | 6 | public abstract class AbstractRequestAdapter implements RequestAdapter { 7 | protected BufferedReader reader; 8 | 9 | @Override 10 | public BufferedReader getReader() throws IOException { 11 | return reader; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /sdk/java/core/src/main/java/starfederation/datastar/events/AbstractBuilder.java: -------------------------------------------------------------------------------- 1 | package starfederation.datastar.events; 2 | 3 | public abstract class AbstractBuilder { 4 | abstract T build(); 5 | } 6 | -------------------------------------------------------------------------------- /sdk/java/core/src/main/java/starfederation/datastar/events/CustomEvent.java: -------------------------------------------------------------------------------- 1 | package starfederation.datastar.events; 2 | 3 | import starfederation.datastar.enums.EventType; 4 | 5 | import java.util.List; 6 | 7 | public abstract non-sealed class CustomEvent extends AbstractDatastarEvent { 8 | 9 | protected CustomEvent(EventType eventType, List dataLines) { 10 | super(eventType, dataLines); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /sdk/java/core/src/main/java/starfederation/datastar/events/DatastarEvent.java: -------------------------------------------------------------------------------- 1 | package starfederation.datastar.events; 2 | 3 | import starfederation.datastar.enums.EventType; 4 | 5 | sealed interface DatastarEvent permits AbstractDatastarEvent { 6 | 7 | /** 8 | * Returns the event type. 9 | */ 10 | EventType getEventType(); 11 | 12 | /** 13 | * Returns the data lines for the event. 14 | */ 15 | String[] getDataLines(); 16 | 17 | /** 18 | * Returns the builder for the event. 19 | */ 20 | 21 | static AbstractBuilder builder() { 22 | throw new IllegalStateException("the builder method should be overridden to use the appropriate builder"); 23 | } 24 | 25 | } 26 | 27 | -------------------------------------------------------------------------------- /sdk/php/src/.gitattributes: -------------------------------------------------------------------------------- 1 | Constants.php linguist-generated=true 2 | enums/EventType.php linguist-generated=true 3 | enums/FragmentMergeMode.php linguist-generated=true -------------------------------------------------------------------------------- /sdk/php/src/ServerSentEventData.php: -------------------------------------------------------------------------------- 1 | eventType = $eventType; 24 | $this->data = $data; 25 | $this->eventId = $eventId; 26 | $this->retryDuration = $retryDuration; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /sdk/python/src/datastar_py/fastapi.py: -------------------------------------------------------------------------------- 1 | from typing import Annotated, Any, Union 2 | 3 | from fastapi import Depends 4 | 5 | from .sse import SSE_HEADERS, ServerSentEventGenerator 6 | from .starlette import DatastarResponse, read_signals 7 | 8 | __all__ = [ 9 | "SSE_HEADERS", 10 | "DatastarResponse", 11 | "ReadSignals", 12 | "ServerSentEventGenerator", 13 | "read_signals", 14 | ] 15 | 16 | 17 | ReadSignals = Annotated[Union[dict[str, Any], None], Depends(read_signals)] 18 | -------------------------------------------------------------------------------- /sdk/python/src/datastar_py/fasthtml.py: -------------------------------------------------------------------------------- 1 | from .sse import SSE_HEADERS, ServerSentEventGenerator 2 | from .starlette import DatastarResponse, read_signals 3 | 4 | __all__ = [ 5 | "SSE_HEADERS", 6 | "DatastarResponse", 7 | "ServerSentEventGenerator", 8 | "read_signals", 9 | ] 10 | -------------------------------------------------------------------------------- /sdk/python/src/datastar_py/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starfederation/datastar/03164b47af392a8e25275e14c46ce2a9a1935a37/sdk/python/src/datastar_py/py.typed -------------------------------------------------------------------------------- /sdk/ruby/.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /_yardoc/ 4 | /coverage/ 5 | /doc/ 6 | /pkg/ 7 | /spec/reports/ 8 | /tmp/ 9 | 10 | # rspec failure tracking 11 | .rspec_status 12 | -------------------------------------------------------------------------------- /sdk/ruby/.rspec: -------------------------------------------------------------------------------- 1 | --format documentation 2 | --color 3 | --require spec_helper 4 | -------------------------------------------------------------------------------- /sdk/ruby/Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source 'https://rubygems.org' 4 | 5 | # Specify your gem's dependencies in datastar.gemspec 6 | gemspec 7 | 8 | gem 'rake', '~> 13.0' 9 | 10 | gem 'rspec', '~> 3.0' 11 | 12 | gem 'debug' 13 | 14 | group :test do 15 | # Async to test Datastar::AsyncExecutor 16 | gem 'async' 17 | # Puma to host test server 18 | gem 'puma' 19 | end 20 | -------------------------------------------------------------------------------- /sdk/ruby/Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "bundler/gem_tasks" 4 | require "rspec/core/rake_task" 5 | 6 | RSpec::Core::RakeTask.new(:spec) 7 | 8 | task default: :spec 9 | -------------------------------------------------------------------------------- /sdk/ruby/bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | require "bundler/setup" 5 | require "datastar" 6 | 7 | # You can add fixtures and/or initialization code here to make experimenting 8 | # with your gem easier. You can also use a different console, if you like. 9 | 10 | require "irb" 11 | IRB.start(__FILE__) 12 | -------------------------------------------------------------------------------- /sdk/ruby/bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | set -vx 5 | 6 | bundle install 7 | 8 | # Do any other automated setup that you need to do here 9 | -------------------------------------------------------------------------------- /sdk/ruby/lib/datastar/rails_async_executor.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'datastar/async_executor' 4 | 5 | module Datastar 6 | class RailsAsyncExecutor < Datastar::AsyncExecutor 7 | def prepare(response) 8 | response.delete_header 'Connection' 9 | end 10 | 11 | def spawn(&block) 12 | Async do 13 | Rails.application.executor.wrap(&block) 14 | end 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /sdk/ruby/lib/datastar/rails_thread_executor.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Datastar 4 | # See https://guides.rubyonrails.org/threading_and_code_execution.html#wrapping-application-code 5 | class RailsThreadExecutor < Datastar::ThreadExecutor 6 | def spawn(&block) 7 | Thread.new do 8 | Rails.application.executor.wrap(&block) 9 | end 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /sdk/ruby/lib/datastar/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Datastar 4 | VERSION = '1.0.0.beta.3' 5 | end 6 | -------------------------------------------------------------------------------- /sdk/ruby/sig/datastar.rbs: -------------------------------------------------------------------------------- 1 | module Datastar 2 | VERSION: String 3 | # See the writing guide of rbs: https://github.com/ruby/rbs#guides 4 | end 5 | -------------------------------------------------------------------------------- /sdk/ruby/spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'datastar' 4 | require 'rack' 5 | require 'datastar/async_executor' 6 | require 'debug' 7 | require_relative './support/dispatcher_examples' 8 | 9 | RSpec.configure do |config| 10 | # Enable flags like --only-failures and --next-failure 11 | config.example_status_persistence_file_path = '.rspec_status' 12 | 13 | # Disable RSpec exposing methods globally on `Module` and `main` 14 | config.disable_monkey_patching! 15 | 16 | config.expect_with :rspec do |c| 17 | c.syntax = :expect 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /sdk/rust/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock -------------------------------------------------------------------------------- /sdk/test/.gitignore: -------------------------------------------------------------------------------- 1 | **/**/norm*.txt 2 | **/**/testOutput.txt 3 | -------------------------------------------------------------------------------- /sdk/test/get-cases/executeScriptWithAllOptions/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "events": [ 3 | { 4 | "type": "executeScript", 5 | "script": "console.log('hello');", 6 | "eventId": "event1", 7 | "retryDuration": 2000, 8 | "attributes": { 9 | "type": "text/javascript", 10 | "blocking": false 11 | }, 12 | "autoRemove": false 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /sdk/test/get-cases/executeScriptWithAllOptions/output.txt: -------------------------------------------------------------------------------- 1 | event: datastar-execute-script 2 | id: event1 3 | retry: 2000 4 | data: attributes type text/javascript 5 | data: attributes blocking false 6 | data: autoRemove false 7 | data: script console.log('hello'); 8 | 9 | 10 | -------------------------------------------------------------------------------- /sdk/test/get-cases/executeScriptWithDefaults/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "events": [ 3 | { 4 | "type": "executeScript", 5 | "script": "console.log('hello');", 6 | "autoRemove": true, 7 | "attributes": { 8 | "type": "module" 9 | } 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /sdk/test/get-cases/executeScriptWithDefaults/output.txt: -------------------------------------------------------------------------------- 1 | event: datastar-execute-script 2 | data: script console.log('hello'); 3 | 4 | 5 | -------------------------------------------------------------------------------- /sdk/test/get-cases/executeScriptWithMultilineScript/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "events": [ 3 | { 4 | "type": "executeScript", 5 | "script": "if (true) {\n console.log('hello');\n}" 6 | } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /sdk/test/get-cases/executeScriptWithMultilineScript/output.txt: -------------------------------------------------------------------------------- 1 | event: datastar-execute-script 2 | data: script if (true) { 3 | data: script console.log('hello'); 4 | data: script } 5 | 6 | 7 | -------------------------------------------------------------------------------- /sdk/test/get-cases/executeScriptWithoutDefaults/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "events": [ 3 | { 4 | "type": "executeScript", 5 | "script": "console.log('hello');" 6 | } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /sdk/test/get-cases/executeScriptWithoutDefaults/output.txt: -------------------------------------------------------------------------------- 1 | event: datastar-execute-script 2 | data: script console.log('hello'); 3 | 4 | 5 | -------------------------------------------------------------------------------- /sdk/test/get-cases/mergeFragmentsWithAllOptions/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "events": [ 3 | { 4 | "type": "mergeFragments", 5 | "fragments": "
Merge
", 6 | "eventId": "event1", 7 | "retryDuration": 2000, 8 | "selector": "div", 9 | "mergeMode": "append", 10 | "useViewTransition": true 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /sdk/test/get-cases/mergeFragmentsWithAllOptions/output.txt: -------------------------------------------------------------------------------- 1 | event: datastar-merge-fragments 2 | id: event1 3 | retry: 2000 4 | data: selector div 5 | data: mergeMode append 6 | data: useViewTransition true 7 | data: fragments
Merge
8 | 9 | 10 | -------------------------------------------------------------------------------- /sdk/test/get-cases/mergeFragmentsWithDefaults/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "events": [ 3 | { 4 | "type": "mergeFragments", 5 | "fragments": "
Merge
", 6 | "mergeMode": "morph", 7 | "useViewTransition": false 8 | } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /sdk/test/get-cases/mergeFragmentsWithDefaults/output.txt: -------------------------------------------------------------------------------- 1 | event: datastar-merge-fragments 2 | data: fragments
Merge
3 | 4 | 5 | -------------------------------------------------------------------------------- /sdk/test/get-cases/mergeFragmentsWithMultilineFragments/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "events": [ 3 | { 4 | "type": "mergeFragments", 5 | "fragments": "
\n Merge\n
" 6 | } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /sdk/test/get-cases/mergeFragmentsWithMultilineFragments/output.txt: -------------------------------------------------------------------------------- 1 | event: datastar-merge-fragments 2 | data: fragments
3 | data: fragments Merge 4 | data: fragments
5 | 6 | 7 | -------------------------------------------------------------------------------- /sdk/test/get-cases/mergeFragmentsWithoutDefaults/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "events": [ 3 | { 4 | "type": "mergeFragments", 5 | "fragments": "
Merge
" 6 | } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /sdk/test/get-cases/mergeFragmentsWithoutDefaults/output.txt: -------------------------------------------------------------------------------- 1 | event: datastar-merge-fragments 2 | data: fragments
Merge
3 | 4 | 5 | -------------------------------------------------------------------------------- /sdk/test/get-cases/mergeSignalsWithAllOptions/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "events": [ 3 | { 4 | "type": "mergeSignals", 5 | "signals": { 6 | "one": 1, 7 | "two": 2 8 | }, 9 | "eventId": "event1", 10 | "retryDuration": 2000, 11 | "onlyIfMissing": true 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /sdk/test/get-cases/mergeSignalsWithAllOptions/output.txt: -------------------------------------------------------------------------------- 1 | event: datastar-merge-signals 2 | id: event1 3 | retry: 2000 4 | data: onlyIfMissing true 5 | data: signals {"one":1,"two":2} 6 | 7 | 8 | -------------------------------------------------------------------------------- /sdk/test/get-cases/mergeSignalsWithDefaults/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "events": [ 3 | { 4 | "type": "mergeSignals", 5 | "signals": { 6 | "one": 1, 7 | "two": 2 8 | }, 9 | "onlyIfMissing": false 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /sdk/test/get-cases/mergeSignalsWithDefaults/output.txt: -------------------------------------------------------------------------------- 1 | event: datastar-merge-signals 2 | data: signals {"one":1,"two":2} 3 | 4 | 5 | -------------------------------------------------------------------------------- /sdk/test/get-cases/mergeSignalsWithMultilineSignals/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "events": [ 3 | { 4 | "type": "mergeSignals", 5 | "signals": { 6 | "one": "first\\n signal", 7 | "two": "second signal" 8 | } 9 | } 10 | ] 11 | } -------------------------------------------------------------------------------- /sdk/test/get-cases/mergeSignalsWithMultilineSignals/output.txt: -------------------------------------------------------------------------------- 1 | event: datastar-merge-signals 2 | data: signals {"one":"first\\n signal","two":"second signal"} 3 | 4 | 5 | -------------------------------------------------------------------------------- /sdk/test/get-cases/mergeSignalsWithoutDefaults/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "events": [ 3 | { 4 | "type": "mergeSignals", 5 | "signals": { 6 | "one": 1, 7 | "two": 2 8 | } 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /sdk/test/get-cases/mergeSignalsWithoutDefaults/output.txt: -------------------------------------------------------------------------------- 1 | event: datastar-merge-signals 2 | data: signals {"one":1,"two":2} 3 | 4 | 5 | -------------------------------------------------------------------------------- /sdk/test/get-cases/removeFragmentsWithAllOptions/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "events": [ 3 | { 4 | "type": "removeFragments", 5 | "selector": "#target", 6 | "eventId": "event1", 7 | "retryDuration": 2000, 8 | "useViewTransition": true 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /sdk/test/get-cases/removeFragmentsWithAllOptions/output.txt: -------------------------------------------------------------------------------- 1 | event: datastar-remove-fragments 2 | id: event1 3 | retry: 2000 4 | data: useViewTransition true 5 | data: selector #target 6 | 7 | 8 | -------------------------------------------------------------------------------- /sdk/test/get-cases/removeFragmentsWithDefaults/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "events": [ 3 | { 4 | "type": "removeFragments", 5 | "selector": "#target", 6 | "useViewTransition": false 7 | } 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /sdk/test/get-cases/removeFragmentsWithDefaults/output.txt: -------------------------------------------------------------------------------- 1 | event: datastar-remove-fragments 2 | data: selector #target 3 | 4 | 5 | -------------------------------------------------------------------------------- /sdk/test/get-cases/removeFragmentsWithoutDefaults/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "events": [ 3 | { 4 | "type": "removeFragments", 5 | "selector": "#target" 6 | } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /sdk/test/get-cases/removeFragmentsWithoutDefaults/output.txt: -------------------------------------------------------------------------------- 1 | event: datastar-remove-fragments 2 | data: selector #target 3 | 4 | 5 | -------------------------------------------------------------------------------- /sdk/test/get-cases/removeSignalsWithAllOptions/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "events": [ 3 | { 4 | "type": "removeSignals", 5 | "paths": [ 6 | "one", 7 | "two.alpha" 8 | ], 9 | "eventId": "event1", 10 | "retryDuration": 2000 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /sdk/test/get-cases/removeSignalsWithAllOptions/output.txt: -------------------------------------------------------------------------------- 1 | event: datastar-remove-signals 2 | id: event1 3 | retry: 2000 4 | data: paths one 5 | data: paths two.alpha 6 | 7 | 8 | -------------------------------------------------------------------------------- /sdk/test/get-cases/removeSignalsWithDefaults/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "events": [ 3 | { 4 | "type": "removeSignals", 5 | "paths": [ 6 | "one" 7 | ] 8 | } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /sdk/test/get-cases/removeSignalsWithDefaults/output.txt: -------------------------------------------------------------------------------- 1 | event: datastar-remove-signals 2 | data: paths one 3 | 4 | 5 | -------------------------------------------------------------------------------- /sdk/test/get-cases/sendTwoEvents/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "events": [ 3 | { 4 | "type": "mergeFragments", 5 | "fragments": "
Merge
" 6 | }, 7 | { 8 | "type": "mergeFragments", 9 | "fragments": "
Merge 2
" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /sdk/test/get-cases/sendTwoEvents/output.txt: -------------------------------------------------------------------------------- 1 | event: datastar-merge-fragments 2 | data: fragments
Merge
3 | 4 | 5 | event: datastar-merge-fragments 6 | data: fragments
Merge 2
7 | 8 | 9 | -------------------------------------------------------------------------------- /sdk/test/normalize.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | awk ' 4 | BEGIN { RS = "\n\n\n"; FS = "\n"; } 5 | 6 | function flush_data(data) { 7 | if (data) { 8 | print data | "sort" 9 | close("sort") 10 | } 11 | return "" 12 | } 13 | 14 | { 15 | data = "" 16 | 17 | for (i = 1; i <= NF; i++) { 18 | if ($i ~ /^data: (fragments|script|path)/) { 19 | data = flush_data(data) 20 | print $i 21 | } else if ($i ~ /^data:/) { 22 | data = data $i "\n" 23 | } else { 24 | data = flush_data(data) 25 | print $i 26 | } 27 | } 28 | 29 | # flush remaining unordered data lines 30 | flush_data(data) 31 | } 32 | ' "$1" 33 | -------------------------------------------------------------------------------- /sdk/test/post-cases/readSignalsFromBody/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "events": [ 3 | { 4 | "type": "mergeFragments", 5 | "fragments": "
Merge
" 6 | } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /sdk/test/post-cases/readSignalsFromBody/output.txt: -------------------------------------------------------------------------------- 1 | event: datastar-merge-fragments 2 | data: fragments
Merge
3 | 4 | 5 | -------------------------------------------------------------------------------- /sdk/typescript/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | npm -------------------------------------------------------------------------------- /sdk/typescript/deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "imports": { 3 | "@deno/dnt": "jsr:@deno/dnt@^0.41.3" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /sdk/typescript/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "esModuleInterop": true, 4 | "skipLibCheck": true, 5 | "target": "es2022", 6 | "resolveJsonModule": true, 7 | "moduleDetection": "force", 8 | "isolatedModules": true, 9 | "strict": true, 10 | "noUncheckedIndexedAccess": true, 11 | "noImplicitOverride": true, 12 | 13 | "module": "commonjs", 14 | "outDir": "dist", 15 | "sourceMap": true, 16 | 17 | "declaration": true, 18 | "lib": ["es2022"] 19 | }, 20 | "include": ["src/**/*.ts"], 21 | "exclude": ["src/web/deno.ts"] 22 | } 23 | -------------------------------------------------------------------------------- /sdk/zig/.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | **Please do not submit any pull requests here – they will be closed!** 2 | 3 | This repository is a read-only subset of the Datastar monorepo. Please open your pull request there instead: 4 | https://github.com/starfederation/datastar 5 | -------------------------------------------------------------------------------- /sdk/zig/.gitignore: -------------------------------------------------------------------------------- 1 | .zig-cache 2 | zig-out -------------------------------------------------------------------------------- /sdk/zig/build.zig.zon: -------------------------------------------------------------------------------- 1 | .{ 2 | .name = .datastar, 3 | .fingerprint = 0xc03ca25b47dce68f, 4 | .version = "1.0.0-beta.5", 5 | .dependencies = .{ 6 | .httpz = .{ 7 | .url = "git+https://github.com/karlseguin/http.zig?ref=master#56258131ef4505543fef5484451867c13c5ff322", 8 | .hash = "httpz-0.0.0-PNVzrJSuBgDFvO7mtd2qDzaq8_hXIu1BqFuL1jwAV8Ac", 9 | }, 10 | .tokamak = .{ 11 | .url = "git+https://github.com/cztomsik/tokamak#c9491b312eabbd5683bc236950fd2455c7b07752", 12 | .hash = "tokamak-2.0.0-FbnSeSbfAQAIiCUmj1RRpAio0RAHWvRoMFozHcnbeeMb", 13 | }, 14 | }, 15 | .paths = .{ 16 | "build.zig", 17 | "build.zig.zon", 18 | "src", 19 | }, 20 | } 21 | -------------------------------------------------------------------------------- /sdk/zig/src/httpz/ServerSentEventGenerator.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const config = @import("config"); 3 | const httpz = @import("httpz"); 4 | const ServerSentEventGenerator = @import("../ServerSentEventGenerator.zig"); 5 | 6 | pub fn init(res: *httpz.Response) !ServerSentEventGenerator { 7 | res.content_type = .EVENTS; 8 | res.header("Cache-Control", "no-cache"); 9 | 10 | if (config.http1) { 11 | res.header("Connection", "keep-alive"); 12 | } 13 | 14 | try res.write(); 15 | 16 | const conn = res.conn; 17 | conn.handover = .close; 18 | 19 | return .{ 20 | .allocator = res.arena, 21 | .writer = conn.stream.writer(), 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /sdk/zig/src/root.zig: -------------------------------------------------------------------------------- 1 | const config = @import("config"); 2 | 3 | pub const consts = @import("consts.zig"); 4 | pub const ServerSentEventGenerator = @import("ServerSentEventGenerator.zig"); 5 | pub const httpz = switch (config.framework) { 6 | .httpz, .all => @import("httpz/root.zig"), 7 | else => undefined, 8 | }; 9 | pub const tk = switch (config.framework) { 10 | .tokamak, .all => @import("tokamak/root.zig"), 11 | else => undefined, 12 | }; 13 | 14 | test { 15 | @import("std").testing.refAllDecls(@This()); 16 | } 17 | -------------------------------------------------------------------------------- /sdk/zig/src/tokamak/ServerSentEventGenerator.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const config = @import("config"); 3 | const tk = @import("tokamak"); 4 | const ServerSentEventGenerator = @import("../ServerSentEventGenerator.zig"); 5 | 6 | pub fn init(res: *tk.Response) !ServerSentEventGenerator { 7 | res.content_type = .EVENTS; 8 | res.header("Cache-Control", "no-cache"); 9 | 10 | if (config.http1) { 11 | res.header("Connection", "keep-alive"); 12 | } 13 | 14 | try res.write(); 15 | 16 | const conn = res.conn; 17 | conn.handover = .close; 18 | 19 | return .{ 20 | .allocator = res.arena, 21 | .writer = conn.stream.writer(), 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /site/.gitignore: -------------------------------------------------------------------------------- 1 | *_templ.go 2 | librarySource -------------------------------------------------------------------------------- /site/cmd/site/.gitignore: -------------------------------------------------------------------------------- 1 | *_bin* 2 | data -------------------------------------------------------------------------------- /site/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [require('tailwindcss'), require('autoprefixer')], 3 | } 4 | -------------------------------------------------------------------------------- /site/routes_bundler.qtpl: -------------------------------------------------------------------------------- 1 | {% func bundlerContent(manifest PluginManifest) %} 2 | import { apply, load, setAlias } from '../engine' 3 | {%- for _, p := range manifest.Plugins -%} 4 | import { {%s p.Name %} } from "{%s p.Path %}" 5 | {%- endfor -%} 6 | 7 | {%- if manifest.Alias != "" -%} 8 | // You included an alias in your manifest 9 | // Please use this as a last resort as you will have to convert any issues or errors back to the original form before reporting them if you want help. 10 | setAlias('{%s manifest.Alias %}') 11 | {%- endif -%} 12 | 13 | load( 14 | {%- for _, p := range manifest.Plugins -%} 15 | {%s p.Name %}, 16 | {%- endfor -%} 17 | ) 18 | 19 | apply() 20 | export { apply, load, setAlias } 21 | {% endfunc %} -------------------------------------------------------------------------------- /site/routes_errors_internal.templ: -------------------------------------------------------------------------------- 1 | package site 2 | 3 | templ internalArgs(info *InternalErrorInfo) { 4 |
5 |
 6 | 			{ templ.JSONString(info) }
 7 | 		
8 |
9 | } 10 | 11 | templ InternalErrorView(name string, info *InternalErrorInfo) { 12 | @ErrorView("Internal", name, internalArgs(info)) { 13 |

Internal errors should never occur. If you encounter this error, please open a new GitHub issue, providing and explanation of how it occurred and the details above.

14 | } 15 | } 16 | -------------------------------------------------------------------------------- /site/routes_examples_csrf.templ: -------------------------------------------------------------------------------- 1 | 2 | package site 3 | 4 | import "fmt" 5 | 6 | templ CSRFDemo(csrf string) { 7 |
11 | 18 |
19 |
20 |

Responses

21 |
22 |
23 |
24 | } 25 | 26 | templ CSRFDemoResponse(response string) { 27 |
{ response }
28 | } 29 | -------------------------------------------------------------------------------- /site/routes_examples_custom_validity.go: -------------------------------------------------------------------------------- 1 | package site 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/go-chi/chi/v5" 7 | "github.com/starfederation/datastar/sdk/go/datastar" 8 | ) 9 | 10 | func setupExamplesCustomValidity(examplesRouter chi.Router) error { 11 | examplesRouter.Get("/custom_validity/data", func(w http.ResponseWriter, r *http.Request) { 12 | sse := datastar.NewSSE(w, r) 13 | sse.ExecuteScript(`alert('Form submitted')`) 14 | }) 15 | 16 | return nil 17 | } 18 | -------------------------------------------------------------------------------- /site/routes_examples_indicator.go: -------------------------------------------------------------------------------- 1 | package site 2 | 3 | import ( 4 | "net/http" 5 | "time" 6 | 7 | "github.com/go-chi/chi/v5" 8 | "github.com/starfederation/datastar/sdk/go/datastar" 9 | ) 10 | 11 | func setupExamplesIndicator(examplesRouter chi.Router) error { 12 | 13 | examplesRouter.Get("/fetch_indicator/greet", func(w http.ResponseWriter, r *http.Request) { 14 | sse := datastar.NewSSE(w, r) 15 | sse.MergeFragmentTempl(indicatorEmpty()) 16 | time.Sleep(2 * time.Second) 17 | sse.MergeFragmentTempl(indicatorGreeting()) 18 | }) 19 | 20 | return nil 21 | } 22 | -------------------------------------------------------------------------------- /site/routes_examples_indicator.templ: -------------------------------------------------------------------------------- 1 | package site 2 | 3 | import "time" 4 | 5 | templ indicatorEmpty() { 6 |
No data yet, please wait
7 | } 8 | 9 | templ indicatorGreeting() { 10 |
Hello, the time is { time.Now().Format(time.RFC3339) }
11 | } 12 | -------------------------------------------------------------------------------- /site/routes_examples_lazy_load.go: -------------------------------------------------------------------------------- 1 | package site 2 | 3 | import ( 4 | "net/http" 5 | "time" 6 | 7 | "github.com/go-chi/chi/v5" 8 | "github.com/starfederation/datastar/sdk/go/datastar" 9 | ) 10 | 11 | func setupExamplesLazyLoad(examplesRouter chi.Router) error { 12 | examplesRouter.Get("/lazy_load/data", func(w http.ResponseWriter, r *http.Request) { 13 | sse := datastar.NewSSE(w, r) 14 | sse.MergeFragmentTempl(lazyLoadLoader()) 15 | }) 16 | 17 | examplesRouter.Get("/lazy_load/graph", func(w http.ResponseWriter, r *http.Request) { 18 | time.Sleep(2 * time.Second) 19 | datastar.NewSSE(w, r).MergeFragmentTempl( 20 | lazyLoadGraph(), 21 | ) 22 | }) 23 | 24 | return nil 25 | } 26 | -------------------------------------------------------------------------------- /site/routes_examples_lazy_load.templ: -------------------------------------------------------------------------------- 1 | package site 2 | 3 | import "github.com/starfederation/datastar/sdk/go/datastar" 4 | 5 | templ lazyLoadLoader() { 6 |
7 |
8 | @icon("svg-spinners:blocks-wave") 9 | Loading... 10 |
11 |
12 | } 13 | 14 | templ lazyLoadGraph() { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /site/routes_examples_model_bindings.go: -------------------------------------------------------------------------------- 1 | package site 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/go-chi/chi/v5" 7 | "github.com/starfederation/datastar/sdk/go/datastar" 8 | ) 9 | 10 | func setupExamplesModelBinding(examplesRouter chi.Router) error { 11 | examplesRouter.Get("/model_binding/data", func(w http.ResponseWriter, r *http.Request) { 12 | sse := datastar.NewSSE(w, r) 13 | 14 | signals := &ModelBindingSignals{ 15 | BindText: "foo", 16 | BindNumber: 42, 17 | BindSelection: 1, 18 | BindBool: true, 19 | } 20 | 21 | sse.MergeFragmentTempl(ModelBindingView(7, signals)) 22 | }) 23 | 24 | return nil 25 | } 26 | -------------------------------------------------------------------------------- /site/routes_examples_on_load.templ: -------------------------------------------------------------------------------- 1 | package site 2 | 3 | import ( 4 | "time" 5 | "fmt" 6 | ) 7 | 8 | templ onLoadView(sessionKey string, sessionValues any) { 9 |
10 | Loaded at { time.Now().Format(time.RFC3339) } 11 |
Session name: { sessionKey }
12 |
Session contents: { fmt.Sprintf("%+v",sessionValues) }
13 |
14 | } 15 | -------------------------------------------------------------------------------- /site/routes_examples_redirects.templ: -------------------------------------------------------------------------------- 1 | package site 2 | 3 | import "github.com/starfederation/datastar/sdk/go/datastar" 4 | 5 | type RedirectsSignals struct { 6 | RedirectTo string `json:"redirectTo"` 7 | } 8 | 9 | templ redirectsView(signals *RedirectsSignals) { 10 |
11 | 15 | 21 |
22 | } 23 | -------------------------------------------------------------------------------- /site/routes_examples_toggle_visibility.go: -------------------------------------------------------------------------------- 1 | package site 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/go-chi/chi/v5" 7 | "github.com/starfederation/datastar/sdk/go/datastar" 8 | ) 9 | 10 | func setupExamplesToggleVisibility(examplesRouter chi.Router) error { 11 | examplesRouter.Get("/toggle_visibility/data", func(w http.ResponseWriter, r *http.Request) { 12 | sse := datastar.NewSSE(w, r) 13 | 14 | signals := &ShowSignals{ 15 | BindBool: false, 16 | } 17 | 18 | sse.MergeFragmentTempl(ToggleVisibilityView(signals)) 19 | }) 20 | 21 | return nil 22 | } 23 | -------------------------------------------------------------------------------- /site/routes_examples_toggle_visibility.templ: -------------------------------------------------------------------------------- 1 | package site 2 | 3 | type ShowSignals struct { 4 | BindBool bool `json:"bindBool"` 5 | } 6 | 7 | templ ToggleVisibilityView(signals *ShowSignals) { 8 |
13 | 19 |
20 | Hello! 21 |
22 |
23 | } 24 | -------------------------------------------------------------------------------- /site/routes_examples_view_transition_api.templ: -------------------------------------------------------------------------------- 1 | package site 2 | 3 | import "time" 4 | 5 | templ viewTransitionAPIUpdate(useSlide bool) { 6 |
10 |
11 |
The time is:
12 |
{ time.Now().Format(time.TimeOnly) }
18 |
19 | 23 |
24 | } 25 | -------------------------------------------------------------------------------- /site/routes_how_tos_redirect.go: -------------------------------------------------------------------------------- 1 | package site 2 | 3 | import ( 4 | "net/http" 5 | "time" 6 | 7 | "github.com/go-chi/chi/v5" 8 | "github.com/starfederation/datastar/sdk/go/datastar" 9 | ) 10 | 11 | func setupHowTosRedirect(howTosRedirect chi.Router) error { 12 | 13 | howTosRedirect.Route("/redirect/data", func(dataRouter chi.Router) { 14 | 15 | dataRouter.Get("/", func(w http.ResponseWriter, r *http.Request) { 16 | sse := datastar.NewSSE(w, r) 17 | sse.MergeFragments(` 18 | Redirecting in 3 seconds... 19 | `) 20 | time.Sleep(3 * time.Second) 21 | sse.Redirect("/guide") 22 | }) 23 | }) 24 | 25 | return nil 26 | } 27 | -------------------------------------------------------------------------------- /site/routes_memes.templ: -------------------------------------------------------------------------------- 1 | package site 2 | 3 | import "net/http" 4 | 5 | templ PageMemes(r *http.Request, memes ...string) { 6 | @Page("Hot fresh memes", "The most wasted of all days is one without laughter.", "/memes") { 7 | @header(r) 8 |
9 |
Memes
10 |
11 | for _, meme := range memes { 12 |
13 | 14 |
15 | } 16 |
17 |
18 | } 19 | } 20 | -------------------------------------------------------------------------------- /site/routes_tests_indicator.go: -------------------------------------------------------------------------------- 1 | package site 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/go-chi/chi/v5" 7 | "github.com/starfederation/datastar/sdk/go/datastar" 8 | ) 9 | 10 | func setupTestsIndicator(testsRouter chi.Router) error { 11 | 12 | testsRouter.Get("/indicator/data", func(w http.ResponseWriter, r *http.Request) { 13 | sse := datastar.NewSSE(w, r) 14 | sse.MergeFragments(`
`) 15 | }) 16 | 17 | return nil 18 | } 19 | -------------------------------------------------------------------------------- /site/routes_tests_indicator_element_removed.go: -------------------------------------------------------------------------------- 1 | package site 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/go-chi/chi/v5" 7 | "github.com/starfederation/datastar/sdk/go/datastar" 8 | ) 9 | 10 | func setupTestsIndicatorElementRemoved(testsRouter chi.Router) error { 11 | 12 | testsRouter.Get("/indicator_element_removed/data", func(w http.ResponseWriter, r *http.Request) { 13 | sse := datastar.NewSSE(w, r) 14 | sse.MergeFragments(`
`) 15 | }) 16 | 17 | return nil 18 | } 19 | -------------------------------------------------------------------------------- /site/routes_tests_merge_fragment.go: -------------------------------------------------------------------------------- 1 | package site 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/go-chi/chi/v5" 7 | "github.com/starfederation/datastar/sdk/go/datastar" 8 | ) 9 | 10 | func setupTestsMergeFragment(testsRouter chi.Router) error { 11 | 12 | testsRouter.Get("/merge_fragment/data", func(w http.ResponseWriter, r *http.Request) { 13 | sse := datastar.NewSSE(w, r) 14 | sse.MergeFragments(`1`) 15 | }) 16 | 17 | return nil 18 | } 19 | -------------------------------------------------------------------------------- /site/routes_tests_merge_fragment_input_value.go: -------------------------------------------------------------------------------- 1 | package site 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/go-chi/chi/v5" 7 | "github.com/starfederation/datastar/sdk/go/datastar" 8 | ) 9 | 10 | func setupTestsMergeFragmentInputValue(testsRouter chi.Router) error { 11 | 12 | testsRouter.Get("/merge_fragment_input_value/data", func(w http.ResponseWriter, r *http.Request) { 13 | sse := datastar.NewSSE(w, r) 14 | sse.MergeFragments(`
15 | 16 |
`) 17 | sse.ExecuteScript(`setTimeout(() => result.innerText = document.querySelector('[data-bind-result]').value === 'foo' ? 1 : 0, 0)`) 18 | }) 19 | 20 | return nil 21 | } 22 | -------------------------------------------------------------------------------- /site/routes_tests_merge_fragment_on_load.go: -------------------------------------------------------------------------------- 1 | package site 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/go-chi/chi/v5" 7 | "github.com/starfederation/datastar/sdk/go/datastar" 8 | ) 9 | 10 | func setupTestsMergeFragmentOnLoad(testsRouter chi.Router) error { 11 | 12 | testsRouter.Get("/merge_fragment_on_load/data", func(w http.ResponseWriter, r *http.Request) { 13 | sse := datastar.NewSSE(w, r) 14 | sse.MergeFragments(`
`) 15 | }) 16 | 17 | return nil 18 | } 19 | -------------------------------------------------------------------------------- /site/routes_tests_merge_fragment_outer_multiple_targets.go: -------------------------------------------------------------------------------- 1 | package site 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/go-chi/chi/v5" 7 | "github.com/starfederation/datastar/sdk/go/datastar" 8 | ) 9 | 10 | func setupTestsMergeFragmentOuterMuplipleTargets(testsRouter chi.Router) error { 11 | 12 | testsRouter.Get("/merge_fragment_outer_multiple_targets/data", func(w http.ResponseWriter, r *http.Request) { 13 | sse := datastar.NewSSE(w, r) 14 | sse.MergeFragments( 15 | `
`, 16 | datastar.WithSelector(".target"), 17 | datastar.WithMergeOuter(), 18 | ) 19 | }) 20 | 21 | return nil 22 | } 23 | -------------------------------------------------------------------------------- /site/routes_tests_merge_fragment_signals.go: -------------------------------------------------------------------------------- 1 | package site 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/go-chi/chi/v5" 7 | "github.com/starfederation/datastar/sdk/go/datastar" 8 | ) 9 | 10 | func setupTestsMergeFragmentSignals(testsRouter chi.Router) error { 11 | 12 | testsRouter.Get("/merge_fragment_signals/data", func(w http.ResponseWriter, r *http.Request) { 13 | sse := datastar.NewSSE(w, r) 14 | sse.MergeFragments(`
`) 15 | }) 16 | 17 | return nil 18 | } 19 | -------------------------------------------------------------------------------- /site/routes_tests_merge_fragment_whitespace.go: -------------------------------------------------------------------------------- 1 | package site 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/go-chi/chi/v5" 7 | "github.com/starfederation/datastar/sdk/go/datastar" 8 | ) 9 | 10 | func setupTestsMergeFragmentWhitespace(testsRouter chi.Router) error { 11 | 12 | testsRouter.Get("/merge_fragment_whitespace/data", func(w http.ResponseWriter, r *http.Request) { 13 | sse := datastar.NewSSE(w, r) 14 | sse.MergeFragments(`
foo
15 |     bar
`) 16 | }) 17 | 18 | return nil 19 | } 20 | -------------------------------------------------------------------------------- /site/routes_tests_on_load.go: -------------------------------------------------------------------------------- 1 | package site 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/go-chi/chi/v5" 7 | 8 | "github.com/starfederation/datastar/sdk/go/datastar" 9 | ) 10 | 11 | type OnLoadSignals struct { 12 | Fetching bool `json:"fetching"` 13 | } 14 | 15 | func setupTestsOnLoad(testsRouter chi.Router) error { 16 | 17 | testsRouter.Get("/on_load/data", func(w http.ResponseWriter, r *http.Request) { 18 | signals := &OnLoadSignals{} 19 | if err := datastar.ReadSignals(r, signals); err != nil { 20 | http.Error(w, err.Error(), http.StatusBadRequest) 21 | } 22 | 23 | sse := datastar.NewSSE(w, r) 24 | 25 | if signals.Fetching { 26 | sse.MergeFragments(`1`) 27 | } 28 | }) 29 | 30 | return nil 31 | } 32 | -------------------------------------------------------------------------------- /site/routes_tests_remove_fragment.go: -------------------------------------------------------------------------------- 1 | package site 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/go-chi/chi/v5" 7 | "github.com/starfederation/datastar/sdk/go/datastar" 8 | ) 9 | 10 | func setupTestsRemoveFragment(testsRouter chi.Router) error { 11 | 12 | testsRouter.Delete("/remove_fragment/data", func(w http.ResponseWriter, r *http.Request) { 13 | sse := datastar.NewSSE(w, r) 14 | sse.RemoveFragments("#remove") 15 | }) 16 | 17 | return nil 18 | } 19 | -------------------------------------------------------------------------------- /site/routes_tests_remove_initiating_fragment.go: -------------------------------------------------------------------------------- 1 | package site 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/go-chi/chi/v5" 7 | "github.com/starfederation/datastar/sdk/go/datastar" 8 | ) 9 | 10 | func setupTestsRemoveInitiatingFragment(testsRouter chi.Router) error { 11 | 12 | testsRouter.Delete("/remove_initiating_fragment/data", func(w http.ResponseWriter, r *http.Request) { 13 | sse := datastar.NewSSE(w, r) 14 | sse.RemoveFragments("#clickable") 15 | sse.MergeFragments(`1`) 16 | }) 17 | 18 | return nil 19 | } 20 | -------------------------------------------------------------------------------- /site/routes_tests_sse_error_event.go: -------------------------------------------------------------------------------- 1 | package site 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/go-chi/chi/v5" 7 | ) 8 | 9 | func setupTestsSseErrorEvent(testsRouter chi.Router) error { 10 | 11 | testsRouter.Get("/sse_error_event/data", func(w http.ResponseWriter, r *http.Request) { 12 | http.Error(w, "Service Unavailable", http.StatusServiceUnavailable) 13 | }) 14 | 15 | return nil 16 | } 17 | -------------------------------------------------------------------------------- /site/routes_tests_sse_events.go: -------------------------------------------------------------------------------- 1 | package site 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/go-chi/chi/v5" 7 | "github.com/starfederation/datastar/sdk/go/datastar" 8 | ) 9 | 10 | func setupTestsSseEvents(testsRouter chi.Router) error { 11 | 12 | testsRouter.Get("/sse_events/data", func(w http.ResponseWriter, r *http.Request) { 13 | datastar.NewSSE(w, r) 14 | }) 15 | 16 | return nil 17 | } 18 | -------------------------------------------------------------------------------- /site/smoketests/.gitignore: -------------------------------------------------------------------------------- 1 | *.png 2 | -------------------------------------------------------------------------------- /site/smoketests/aliased_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestUnitAliased(t *testing.T) { 8 | setupPageTestOnLoad(t, "tests/aliased") 9 | } 10 | -------------------------------------------------------------------------------- /site/smoketests/attr_false_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestUnitAttrFalse(t *testing.T) { 8 | setupPageTestOnLoad(t, "tests/attr_false") 9 | } 10 | -------------------------------------------------------------------------------- /site/smoketests/attr_object_false_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestUnitAttrObjectFalse(t *testing.T) { 8 | setupPageTestOnLoad(t, "tests/attr_object_false") 9 | } 10 | -------------------------------------------------------------------------------- /site/smoketests/checkbox_array_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestUnitCheckboxArray(t *testing.T) { 8 | setupPageTestOnClick(t, "tests/checkbox_array") 9 | } 10 | -------------------------------------------------------------------------------- /site/smoketests/checkbox_boolean_checked_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestUnitCheckboxBooleanChecked(t *testing.T) { 8 | setupPageTestOnClick(t, "tests/checkbox_boolean_checked") 9 | } 10 | -------------------------------------------------------------------------------- /site/smoketests/checkbox_boolean_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestUnitCheckboxDefault(t *testing.T) { 8 | setupPageTestOnClick(t, "tests/checkbox_boolean") 9 | } 10 | -------------------------------------------------------------------------------- /site/smoketests/checkbox_value_checked_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestUnitCheckboxValueChecked(t *testing.T) { 8 | setupPageTestOnClick(t, "tests/checkbox_value_checked") 9 | } 10 | -------------------------------------------------------------------------------- /site/smoketests/checkbox_value_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestUnitCheckboxValue(t *testing.T) { 8 | setupPageTestOnClick(t, "tests/checkbox_value") 9 | } 10 | -------------------------------------------------------------------------------- /site/smoketests/click_to_load_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/go-rod/rod" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestExampleClickToLoad(t *testing.T) { 11 | setupPageTest(t, "examples/click_to_load", func(runner runnerFn) { 12 | runner("click to load", func(t *testing.T, page *rod.Page) { 13 | table := page.MustElement(".table") 14 | 15 | rows := table.MustElements("tr") 16 | assert.Len(t, rows, 11) 17 | 18 | btn := page.MustElement("#more_btn") 19 | btn.MustClick() 20 | page.MustWaitStable() 21 | 22 | updatedRows := table.MustElements("tr") 23 | assert.Len(t, updatedRows, 21) 24 | }) 25 | }) 26 | } 27 | -------------------------------------------------------------------------------- /site/smoketests/cloak_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/go-rod/rod" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestExampleCloak(t *testing.T) { 11 | setupPageTest(t, "examples/cloak", func(runner runnerFn) { 12 | runner("cloak", func(t *testing.T, page *rod.Page) { 13 | element := page.MustElement("#cloak") 14 | initial, err := element.Attribute("class") 15 | if err != nil { 16 | t.Fatal("failed to get initial class: %w", err) 17 | } 18 | assert.Nil(t, initial) 19 | }) 20 | }) 21 | } 22 | -------------------------------------------------------------------------------- /site/smoketests/custom_plugin_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestUnitCustomPlugin(t *testing.T) { 8 | setupPageTestOnLoad(t, "tests/custom_plugin") 9 | } 10 | -------------------------------------------------------------------------------- /site/smoketests/dispatch_custom_event_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/go-rod/rod" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestExampleDispatchCustomEvent(t *testing.T) { 11 | setupPageTest(t, "examples/dispatch_custom_event", func(runner runnerFn) { 12 | runner("observe dispatched custom event", func(t *testing.T, page *rod.Page) { 13 | selector := "#container" 14 | el := page.MustElement(selector) 15 | initial := el.MustText() 16 | 17 | page.MustWait(`() => document.querySelector("` + selector + `").innerText !== ""`) 18 | 19 | result := el.MustText() 20 | 21 | assert.NotEqual(t, initial, result) 22 | }) 23 | }) 24 | } 25 | -------------------------------------------------------------------------------- /site/smoketests/file_upload_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "path/filepath" 5 | "testing" 6 | 7 | "github.com/go-rod/rod" 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestExampleFileUpload(t *testing.T) { 12 | setupPageTest(t, "examples/file_upload", func(runner runnerFn) { 13 | runner("upload a file", func(t *testing.T, page *rod.Page) { 14 | el := page.MustElement(`[type=file]`) 15 | el.MustSetFiles( 16 | filepath.FromSlash("site/smoketests/file_upload_test.go"), 17 | ) 18 | 19 | list := el.MustEval("() => Array.from(this.files).map(f => f.name)").Arr() 20 | assert.Len(t, list, 1) 21 | assert.Equal(t, "file_upload_test.go", list[0].String()) 22 | }) 23 | }) 24 | } 25 | -------------------------------------------------------------------------------- /site/smoketests/indicator_element_removed_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestUnitIndicatorElementRemoved(t *testing.T) { 8 | setupPageTestOnClick(t, "tests/indicator_element_removed") 9 | } 10 | -------------------------------------------------------------------------------- /site/smoketests/indicator_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestUnitIndicator(t *testing.T) { 8 | setupPageTestOnClick(t, "tests/indicator") 9 | } 10 | -------------------------------------------------------------------------------- /site/smoketests/input_array_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestUnitInputArray(t *testing.T) { 8 | setupPageTestOnPopulate(t, "tests/input_array", "bar") 9 | } 10 | -------------------------------------------------------------------------------- /site/smoketests/input_signal_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestUnitInputSignal(t *testing.T) { 8 | setupPageTestOnLoad(t, "tests/input_signal") 9 | } 10 | -------------------------------------------------------------------------------- /site/smoketests/input_value_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestUnitInputValue(t *testing.T) { 8 | setupPageTestOnLoad(t, "tests/input_value") 9 | } 10 | -------------------------------------------------------------------------------- /site/smoketests/key_casing_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestUnitKeyCasing(t *testing.T) { 8 | setupPageTestOnLoad(t, "tests/key_casing") 9 | } 10 | -------------------------------------------------------------------------------- /site/smoketests/lazy_load_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/go-rod/rod" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func DisabledTestExampleLazyLoad(t *testing.T) { 11 | setupPageTest(t, "examples/lazy_load", func(runner runnerFn) { 12 | runner("observe lazy load", func(t *testing.T, page *rod.Page) { 13 | selector := "#lazy_load" 14 | 15 | initial := page.MustElement(selector).MustText() 16 | assert.Equal(t, "Loading...", initial) 17 | 18 | page.MustWait(`() => document.querySelector("` + selector + `").innerText === ""`) 19 | 20 | src := page.MustElement(selector).MustAttribute("src") 21 | 22 | assert.NotNil(t, src) 23 | }) 24 | }) 25 | } 26 | -------------------------------------------------------------------------------- /site/smoketests/local_signals_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestUnitLocalSignals(t *testing.T) { 8 | setupPageTestOnLoad(t, "tests/local_signals") 9 | } 10 | -------------------------------------------------------------------------------- /site/smoketests/merge_fragment_input_value_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestUnitMergeFragmentInputValue(t *testing.T) { 8 | setupPageTestOnPopulateAndClick(t, "tests/merge_fragment_input_value", "foo") 9 | } 10 | -------------------------------------------------------------------------------- /site/smoketests/merge_fragment_on_load_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestUnitMergeFragmentOnLoad(t *testing.T) { 8 | setupPageTestOnClick(t, "tests/merge_fragment_on_load") 9 | } 10 | -------------------------------------------------------------------------------- /site/smoketests/merge_fragment_signals_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestUnitMergeFragmentSignals(t *testing.T) { 8 | setupPageTestOnClick(t, "tests/merge_fragment_signals") 9 | } 10 | -------------------------------------------------------------------------------- /site/smoketests/merge_fragment_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestUnitMergeFragment(t *testing.T) { 8 | setupPageTestOnClick(t, "tests/merge_fragment") 9 | } 10 | -------------------------------------------------------------------------------- /site/smoketests/merge_fragment_whitespace_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestUnitMergeFragmentWhitespace(t *testing.T) { 8 | setupPageTestOnClick(t, "tests/merge_fragment_whitespace") 9 | } 10 | -------------------------------------------------------------------------------- /site/smoketests/multiline_expressions_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/go-rod/rod" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestExampleMultilineExpressions(t *testing.T) { 11 | setupPageTest(t, "examples/multiline_expressions", func(runner runnerFn) { 12 | runner("observe change", func(t *testing.T, page *rod.Page) { 13 | selector := "article > div" 14 | initial := page.MustElement(selector).MustText() 15 | 16 | page.MustWait(`() => document.querySelector("` + selector + `").innerText !== "` + initial + `"`) 17 | result := page.MustElement(selector).MustText() 18 | 19 | assert.NotEqual(t, initial, result) 20 | }) 21 | }) 22 | } 23 | -------------------------------------------------------------------------------- /site/smoketests/on_load_delay_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestUnitOnLoadDelay(t *testing.T) { 8 | setupPageTestOnDelay(t, "tests/on_load_delay") 9 | } 10 | -------------------------------------------------------------------------------- /site/smoketests/on_load_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestUnitOnLoad(t *testing.T) { 8 | setupPageTestOnLoad(t, "tests/on_load") 9 | } 10 | -------------------------------------------------------------------------------- /site/smoketests/on_signal_change_path_once_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestUnitOnSignalChangePathOnce(t *testing.T) { 8 | setupPageTestOnLoad(t, "tests/on_signal_change_path_once") 9 | } 10 | -------------------------------------------------------------------------------- /site/smoketests/on_signal_change_path_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestUnitOnSignalChangePath(t *testing.T) { 8 | setupPageTestOnLoad(t, "tests/on_signal_change_path") 9 | } 10 | -------------------------------------------------------------------------------- /site/smoketests/on_signal_change_path_wildcard_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestUnitOnSignalChangePathWildcard(t *testing.T) { 8 | setupPageTestOnLoad(t, "tests/on_signal_change_path_wildcard") 9 | } 10 | -------------------------------------------------------------------------------- /site/smoketests/on_signal_change_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestUnitOnSignalChange(t *testing.T) { 8 | setupPageTestOnLoad(t, "tests/on_signal_change") 9 | } 10 | -------------------------------------------------------------------------------- /site/smoketests/persist_signals_path_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/go-rod/rod" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestPersistSignalsPathWildcard(t *testing.T) { 11 | setupPageTest(t, "tests/persist_signals_path", func(runner runnerFn) { 12 | runner("tests/persist_signals_path", func(t *testing.T, page *rod.Page) { 13 | page.MustWaitIdle() 14 | assert.Equal(t, "1", getLocalStoragePath(t, page, "foo")) 15 | assert.Equal(t, "1", getLocalStoragePath(t, page, "bar")) 16 | assert.Equal(t, "", getLocalStoragePath(t, page, "baz")) 17 | }) 18 | }) 19 | } 20 | -------------------------------------------------------------------------------- /site/smoketests/persist_signals_path_wildcard_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/go-rod/rod" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestPersistSignalsPath(t *testing.T) { 11 | setupPageTest(t, "tests/persist_signals_path_wildcard", func(runner runnerFn) { 12 | runner("tests/persist_signals_path_wildcard", func(t *testing.T, page *rod.Page) { 13 | page.MustWaitIdle() 14 | assert.Equal(t, "{bar:{baz:1}", getLocalStoragePath(t, page, "foo")) 15 | }) 16 | }) 17 | } 18 | -------------------------------------------------------------------------------- /site/smoketests/persist_signals_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/go-rod/rod" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestPersistSignals(t *testing.T) { 11 | setupPageTest(t, "tests/persist_signals", func(runner runnerFn) { 12 | runner("tests/persist_signals", func(t *testing.T, page *rod.Page) { 13 | page.MustWaitIdle() 14 | assert.Equal(t, "1", getLocalStoragePath(t, page, "foo")) 15 | assert.Equal(t, "1", getLocalStoragePath(t, page, "bar")) 16 | assert.Equal(t, "1", getLocalStoragePath(t, page, "baz")) 17 | }) 18 | }) 19 | } 20 | -------------------------------------------------------------------------------- /site/smoketests/plugin_name_prefix_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestUnitPluginNamePrefix(t *testing.T) { 8 | setupPageTestOnLoad(t, "tests/plugin_name_prefix") 9 | } 10 | -------------------------------------------------------------------------------- /site/smoketests/progress_bar_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/go-rod/rod" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestExampleProgressBar(t *testing.T) { 11 | setupPageTest(t, "examples/progress_bar", func(runner runnerFn) { 12 | runner("observe progress bar", func(t *testing.T, page *rod.Page) { 13 | selector := "#progress_bar" 14 | svg := page.MustElement(selector) 15 | 16 | initial := svg.MustHTML() 17 | 18 | page.MustWaitStable() 19 | 20 | result := page.MustElement(selector).MustHTML() 21 | 22 | assert.NotEqual(t, initial, result) 23 | }) 24 | }) 25 | } 26 | -------------------------------------------------------------------------------- /site/smoketests/radio_value_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestUnitRadioValue(t *testing.T) { 8 | setupPageTestOnClick(t, "tests/radio_value") 9 | } 10 | -------------------------------------------------------------------------------- /site/smoketests/raf_update_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/go-rod/rod" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestExampleRafUpdate(t *testing.T) { 11 | setupPageTest(t, "examples/raf_update", func(runner runnerFn) { 12 | runner("observe raf", func(t *testing.T, page *rod.Page) { 13 | initial := page.MustElement("pre").MustText() 14 | 15 | page.MustWait("() => document.querySelector(`pre`).innerText !== `" + initial + "`") 16 | 17 | result := page.MustElement("pre").MustText() 18 | 19 | assert.NotEqual(t, initial, result) 20 | }) 21 | }) 22 | } 23 | -------------------------------------------------------------------------------- /site/smoketests/redirects_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/go-rod/rod" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestExampleRedirects(t *testing.T) { 11 | setupPageTest(t, "examples/redirects", func(runner runnerFn) { 12 | runner("redirection", func(t *testing.T, page *rod.Page) { 13 | btn := page.MustElementR("button", "Redirect") 14 | btn.MustClick() 15 | 16 | waitForURLToContain(page, "grugs_around_fire") 17 | 18 | url := page.MustInfo().URL 19 | assert.Contains(t, url, "grugs_around_fire") 20 | }) 21 | }) 22 | } 23 | -------------------------------------------------------------------------------- /site/smoketests/ref_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestUnitRef(t *testing.T) { 8 | setupPageTestOnLoad(t, "tests/ref") 9 | } 10 | -------------------------------------------------------------------------------- /site/smoketests/refs_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/go-rod/rod" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestExampleRefs(t *testing.T) { 11 | setupPageTest(t, "examples/refs", func(runner runnerFn) { 12 | runner("observe ref", func(t *testing.T, page *rod.Page) { 13 | initial := page.MustElementR(".card-title", "I'm using content").MustText() 14 | assert.Contains(t, initial, "I'm a div that is getting referenced") 15 | }) 16 | }) 17 | } 18 | -------------------------------------------------------------------------------- /site/smoketests/remove_fragment_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestUnitRemoveFragment(t *testing.T) { 8 | setupPageTestOnClick(t, "tests/remove_fragment") 9 | } -------------------------------------------------------------------------------- /site/smoketests/remove_initiating_fragment_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestUnitRemoveInitiatingFragment(t *testing.T) { 8 | setupPageTestOnClick(t, "tests/remove_initiating_fragment") 9 | } -------------------------------------------------------------------------------- /site/smoketests/replace_url_from_backend_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/go-rod/rod" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestExampleReplaceUrlFromBackend(t *testing.T) { 11 | setupPageTest(t, "examples/replace_url_from_backend", func(runner runnerFn) { 12 | runner("observe url replacement", func(t *testing.T, page *rod.Page) { 13 | initial := page.MustInfo().URL 14 | 15 | page.MustWait(`() => window.location.href !== "` + initial + `"`) 16 | 17 | result := page.MustInfo().URL 18 | assert.NotEqual(t, initial, result) 19 | }) 20 | }) 21 | } 22 | -------------------------------------------------------------------------------- /site/smoketests/replace_url_from_signals_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/go-rod/rod" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestExampleReplaceUrlFromSignals(t *testing.T) { 11 | setupPageTest(t, "examples/replace_url_from_signals", func(runner runnerFn) { 12 | runner("observe url replacement", func(t *testing.T, page *rod.Page) { 13 | initial := page.MustInfo().URL 14 | 15 | page.MustWait(`() => window.location.href !== "` + initial + `"`) 16 | 17 | result := page.MustInfo().URL 18 | assert.NotEqual(t, initial, result) 19 | }) 20 | }) 21 | } 22 | -------------------------------------------------------------------------------- /site/smoketests/scroll_into_view_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/go-rod/rod" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestExampleScrollIntoView(t *testing.T) { 11 | setupPageTest(t, "examples/scroll_into_view", func(runner runnerFn) { 12 | runner("smooth", func(t *testing.T, page *rod.Page) { 13 | pos := page.Mouse.Position() 14 | assert.InDelta(t, 0, pos.X, 5.0) 15 | assert.InDelta(t, 0, pos.Y, 5.0) 16 | 17 | btn := page.MustElement("#scrollIntoViewButton") 18 | btn.MustClick() 19 | 20 | page.MustWaitIdle() 21 | 22 | pos = page.Mouse.Position() 23 | assert.Greater(t, pos.X, 500.0) 24 | assert.Greater(t, pos.Y, 200.0) 25 | }) 26 | }) 27 | } 28 | -------------------------------------------------------------------------------- /site/smoketests/select_multiple_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestUnitSelectMultipe(t *testing.T) { 8 | setupPageTestOnSelect(t, "tests/select_multiple") 9 | } 10 | -------------------------------------------------------------------------------- /site/smoketests/select_single_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestUnitSelectSingle(t *testing.T) { 8 | setupPageTestOnSelect(t, "tests/select_single") 9 | } 10 | -------------------------------------------------------------------------------- /site/smoketests/set_all_path_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestUnitSetAllPath(t *testing.T) { 8 | setupPageTestOnLoad(t, "tests/set_all_path") 9 | } 10 | -------------------------------------------------------------------------------- /site/smoketests/set_all_path_wildcard_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestUnitSetAllPathWildcard(t *testing.T) { 8 | setupPageTestOnLoad(t, "tests/set_all_path_wildcard") 9 | } 10 | -------------------------------------------------------------------------------- /site/smoketests/set_all_paths_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestUnitSetAllPaths(t *testing.T) { 8 | setupPageTestOnLoad(t, "tests/set_all_paths") 9 | } 10 | -------------------------------------------------------------------------------- /site/smoketests/signals_ifmissing_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/go-rod/rod" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestExampleSignalsIfmissing(t *testing.T) { 11 | setupPageTest(t, "examples/signals_ifmissing", func(runner runnerFn) { 12 | runner("check the signals", func(t *testing.T, page *rod.Page) { 13 | 14 | initial := page.MustElement("#placeholder").MustText() 15 | assert.Empty(t, initial) 16 | 17 | page.MustWait(`() => document.querySelector("#placeholder").innerText !== ""`) 18 | 19 | result := page.MustElement("#placeholder").MustText() 20 | assert.NotEqual(t, initial, result) 21 | assert.Equal(t, "1234", result) 22 | }) 23 | }) 24 | } 25 | -------------------------------------------------------------------------------- /site/smoketests/sse_error_event_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestUnitSseErrorEvent(t *testing.T) { 8 | setupPageTestOnLoad(t, "tests/sse_error_event") 9 | } 10 | -------------------------------------------------------------------------------- /site/smoketests/sse_events_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestUnitSseEvents(t *testing.T) { 8 | setupPageTestOnLoad(t, "tests/sse_events") 9 | } 10 | -------------------------------------------------------------------------------- /site/smoketests/title_update_backend_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/go-rod/rod" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestExampleTitleUpdateBackend(t *testing.T) { 11 | setupPageTest(t, "examples/title_update_backend", func(runner runnerFn) { 12 | runner("observe title change", func(t *testing.T, page *rod.Page) { 13 | initial := page.MustEval(`() => document.title`).Str() 14 | 15 | page.MustWait(`() => document.title !== "` + initial + `"`) 16 | 17 | result := page.MustEval(`() => document.title`).Str() 18 | 19 | assert.NotEqual(t, initial, result) 20 | }) 21 | }) 22 | } 23 | -------------------------------------------------------------------------------- /site/smoketests/todos_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/go-rod/rod" 7 | "github.com/go-rod/rod/lib/input" 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestTodos(t *testing.T) { 12 | setupPageTest(t, "", func(runner runnerFn) { 13 | runner("add a todo", func(t *testing.T, page *rod.Page) { 14 | assert.NotNil(t, page) 15 | el := page.MustElementR("input", "What needs to be done?").MustInput("testing!") 16 | el.MustType(input.Enter) 17 | }) 18 | }) 19 | 20 | } 21 | -------------------------------------------------------------------------------- /site/smoketests/toggle_all_path_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestUnitToggleAllPath(t *testing.T) { 8 | setupPageTestOnLoad(t, "tests/toggle_all_path") 9 | } 10 | -------------------------------------------------------------------------------- /site/smoketests/toggle_visibility_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/go-rod/rod" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestExampleToggleVisibility(t *testing.T) { 11 | setupPageTest(t, "examples/toggle_visibility", func(runner runnerFn) { 12 | runner("toggle", func(t *testing.T, page *rod.Page) { 13 | initial := page.MustElement("#container > div").MustAttribute("style") 14 | assert.Equal(t, "display: none;", *initial) 15 | 16 | btn := page.MustElementR("button", "Toggle") 17 | btn.MustClick() 18 | 19 | result := page.MustElement("#container > div").MustAttribute("style") 20 | assert.Equal(t, "", *result) 21 | }) 22 | }) 23 | } 24 | -------------------------------------------------------------------------------- /site/smoketests/web_component_test.go: -------------------------------------------------------------------------------- 1 | package smoketests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/go-rod/rod" 7 | "github.com/go-rod/rod/lib/input" 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestExampleWebComponent(t *testing.T) { 12 | setupPageTest(t, "examples/web_component", func(runner runnerFn) { 13 | runner("observe web component", func(t *testing.T, page *rod.Page) { 14 | page.MustElement("article > div > input").MustInput("tes") 15 | page.MustElement("article > div > input").MustType(input.KeyT) 16 | 17 | result := page.MustElement("div > span").MustText() 18 | 19 | assert.Equal(t, "tset", result) 20 | }) 21 | }) 22 | } 23 | -------------------------------------------------------------------------------- /site/static/code_snippets/getting_started/multiple_events.clojuresnippet: -------------------------------------------------------------------------------- 1 | (d*/merge-fragment! sse "
...
") 2 | (d*/merge-fragment! sse "
...
") 3 | (d*/merge-signals! sse "{answer: '...'}") 4 | (d*/merge-signals! sse "{prize: '...'}") 5 | -------------------------------------------------------------------------------- /site/static/code_snippets/getting_started/multiple_events.csharpsnippet: -------------------------------------------------------------------------------- 1 | sse.MergeFragmentsAsync(@"
...
"); 2 | sse.MergeFragmentsAsync(@"
...
"); 3 | sse.MergeSignalsAsync("{answer: '...'}"); 4 | sse.MergeSignalsAsync("{prize: '...'}"); 5 | -------------------------------------------------------------------------------- /site/static/code_snippets/getting_started/multiple_events.gosnippet: -------------------------------------------------------------------------------- 1 | sse.MergeFragments(`
...
`) 2 | sse.MergeFragments(`
...
`) 3 | sse.MergeSignals([]byte(`{answer: '...'}`)) 4 | sse.MergeSignals([]byte(`{prize: '...'}`)) 5 | -------------------------------------------------------------------------------- /site/static/code_snippets/getting_started/multiple_events.haskellsnippet: -------------------------------------------------------------------------------- 1 | import ServerSentEventGenerator 2 | import ServerSentEventGenerator.Server.Snap -- or whatever is appropriate 3 | 4 | send (withDefaults MergeFragments "
...
") 5 | send (withDefaults MergeFragments "
...
") 6 | send (withDefaults MergeFragments "{answer: '...'}") 7 | send (withDefaults MergeFragments "{prize: '...'}") 8 | 9 | -------------------------------------------------------------------------------- /site/static/code_snippets/getting_started/multiple_events.phpsnippet: -------------------------------------------------------------------------------- 1 | $sse->mergeFragments('
...
'); 2 | $sse->mergeFragments('
...
'); 3 | $sse->mergeSignals(['answer' => '...']); 4 | $sse->mergeSignals(['prize' => '...']); 5 | -------------------------------------------------------------------------------- /site/static/code_snippets/getting_started/multiple_events.rubysnippet: -------------------------------------------------------------------------------- 1 | datastar.stream do |sse| 2 | sse.merge_fragments('
...
') 3 | sse.merge_fragments('
...
') 4 | sse.merge_signals(answer: '...') 5 | sse.merge_signals(prize: '...') 6 | end 7 | -------------------------------------------------------------------------------- /site/static/code_snippets/getting_started/multiple_events.rustsnippet: -------------------------------------------------------------------------------- 1 | yield MergeFragments::new("
...
").into() 2 | yield MergeFragments::new("
...
").into() 3 | yield MergeSignals::new("{answer: '...'}").into() 4 | yield MergeSignals::new("{prize: '...'}").into() 5 | -------------------------------------------------------------------------------- /site/static/code_snippets/getting_started/multiple_events.typescriptsnippet: -------------------------------------------------------------------------------- 1 | stream.mergeFragments('
...
'); 2 | stream.mergeFragments('
...
'); 3 | stream.mergeSignals({'answer': '...'}); 4 | stream.mergeSignals({'prize': '...'}); 5 | -------------------------------------------------------------------------------- /site/static/code_snippets/getting_started/multiple_events.zigsnippet: -------------------------------------------------------------------------------- 1 | try sse.mergeFragments("
...
"), .{}; 2 | try sse.mergeFragments("
...
", .{}); 3 | try sse.mergeSignals(.{ .answer = "..." }, .{}); 4 | try sse.mergeSignals(.{ .prize = "..." }, .{}); 5 | -------------------------------------------------------------------------------- /site/static/code_snippets/getting_started/setup.clojuresnippet: -------------------------------------------------------------------------------- 1 | ;; Import the SDK's api and your adapter 2 | (require 3 | '[starfederation.datastar.clojure.api :as d*] 4 | '[starfederation.datastar.clojure.adapter.http-kit :refer [->sse-response on-open]]) 5 | 6 | 7 | ;; in a ring handler 8 | (defn handler [request] 9 | ;; Create a SSE response 10 | (->sse-response request 11 | {on-open 12 | (fn [sse] 13 | ;; Merge html fragments into the DOM 14 | (d*/merge-fragment! sse 15 | "
What do you put in a toaster?
") 16 | 17 | ;; Merge signals into the signals 18 | (d*/merge-signals! sse "{response: '', answer: 'bread'}"))})) 19 | -------------------------------------------------------------------------------- /site/static/code_snippets/getting_started/setup.csharpsnippet: -------------------------------------------------------------------------------- 1 | using StarFederation.Datastar.DependencyInjection; 2 | 3 | // Adds Datastar as a service 4 | builder.Services.AddDatastar(); 5 | 6 | app.MapGet("/", async (IDatastarServerSentEventService sse) => 7 | { 8 | // Merges HTML fragments into the DOM. 9 | await sse.MergeFragmentsAsync(@"
What do you put in a toaster?
"); 10 | 11 | // Merges signals into the signals. 12 | await sse.MergeSignalsAsync("{response: '', answer: 'bread'}"); 13 | }); 14 | -------------------------------------------------------------------------------- /site/static/code_snippets/getting_started/setup.gosnippet: -------------------------------------------------------------------------------- 1 | import ("github.com/starfederation/datastar/sdk/go/datastar") 2 | 3 | // Creates a new `ServerSentEventGenerator` instance. 4 | sse := datastar.NewSSE(w,r) 5 | 6 | // Merges HTML fragments into the DOM. 7 | sse.MergeFragments( 8 | `
What do you put in a toaster?
` 9 | ) 10 | 11 | // Merges signals into the signals. 12 | sse.MergeSignals([]byte(`{response: '', answer: 'bread'}`)) 13 | -------------------------------------------------------------------------------- /site/static/code_snippets/getting_started/setup.haskellsnippet: -------------------------------------------------------------------------------- 1 | import ServerSentEventGenerator 2 | import ServerSentEventGenerator.Server.Snap -- or whatever is appropriate 3 | 4 | -- Merges HTML fragments into the DOM. 5 | send (withDefaults mergeFragments "
What do you put in a toaster?
") 6 | 7 | -- Merges signals into the signals. 8 | send (withDefaults mergeSignals "{response: '', answer: 'bread'}") 9 | -------------------------------------------------------------------------------- /site/static/code_snippets/getting_started/setup.phpsnippet: -------------------------------------------------------------------------------- 1 | use starfederation\datastar\ServerSentEventGenerator; 2 | 3 | // Creates a new `ServerSentEventGenerator` instance. 4 | $sse = new ServerSentEventGenerator(); 5 | 6 | // Merges HTML fragments into the DOM. 7 | $sse->mergeFragments( 8 | '
What do you put in a toaster?
' 9 | ); 10 | 11 | // Merges signals into the signals. 12 | $sse->mergeSignals(['response' => '', 'answer' => 'bread']); 13 | -------------------------------------------------------------------------------- /site/static/code_snippets/getting_started/setup.rubysnippet: -------------------------------------------------------------------------------- 1 | require 'datastar' 2 | 3 | # Create a Datastar::Dispatcher instance 4 | 5 | datastar = Datastar.new(request:, response:) 6 | 7 | # In a Rack handler, you can instantiate from the Rack env 8 | # datastar = Datastar.from_rack_env(env) 9 | 10 | # Start a streaming response 11 | datastar.stream do |sse| 12 | # Merges fragment into the DOM 13 | sse.merge_fragments %(
What do you put in a toaster?
) 14 | 15 | # Merges signals 16 | sse.merge_signals(response: '', answer: 'bread') 17 | end 18 | -------------------------------------------------------------------------------- /site/static/code_snippets/getting_started/setup.rustsnippet: -------------------------------------------------------------------------------- 1 | use datastar::prelude::*; 2 | use async_stream::stream; 3 | 4 | Sse(stream! { 5 | // Merges HTML fragments into the DOM. 6 | yield MergeFragments::new("
What do you put in a toaster?
").into(); 7 | 8 | // Merges signals into the signals. 9 | yield MergeSignals::new("{response: '', answer: 'bread'}").into(); 10 | }) -------------------------------------------------------------------------------- /site/static/code_snippets/getting_started/setup.typescriptsnippet: -------------------------------------------------------------------------------- 1 | // Creates a new `ServerSentEventGenerator` instance (this also sends required headers) 2 | ServerSentEventGenerator.stream(req, res, (stream) => { 3 | // Merges HTML fragments into the DOM. 4 | stream.mergeFragments(`
What do you put in a toaster?
`); 5 | 6 | // Merges signals into the signals. 7 | stream.mergeSignals({'response': '', 'answer': 'bread'}); 8 | }); -------------------------------------------------------------------------------- /site/static/code_snippets/getting_started/setup.zigsnippet: -------------------------------------------------------------------------------- 1 | const datastar = @import("datastar").httpz; 2 | 3 | // Creates a new `ServerSentEventGenerator`. 4 | var sse = try datastar.ServerSentEventGenerator.init(res); 5 | 6 | // Merges HTML fragments into the DOM. 7 | try sse.mergeFragments("
What do you put in a toaster?
", .{}); 8 | 9 | // Merges signals into the signals. 10 | try sse.mergeSignals(.{ .response = "", .answer = "bread" }, .{}); -------------------------------------------------------------------------------- /site/static/code_snippets/going_deeper/multiple_events.clojuresnippet: -------------------------------------------------------------------------------- 1 | (require 2 | '[starfederation.datastar.clojure.api :as d*] 3 | '[starfederation.datastar.clojure.adapter.http-kit :refer [->sse-response on-open]]) 4 | 5 | 6 | (defn handler [request] 7 | (->sse-response request 8 | {on-open 9 | (fn [sse] 10 | (d*/merge-fragment! sse "
Hello, world!
") 11 | (d*/merge-signals! sse "{foo: {bar: 1}}") 12 | (d*/execute-script! sse "console.log('Success!')"))})) 13 | -------------------------------------------------------------------------------- /site/static/code_snippets/going_deeper/multiple_events.csharpsnippet: -------------------------------------------------------------------------------- 1 | using StarFederation.Datastar.DependencyInjection; 2 | 3 | // Adds Datastar as a service 4 | builder.Services.AddDatastar(); 5 | 6 | app.MapGet("/", async (IDatastarServerSentEventService sse) => 7 | { 8 | await sse.MergeFragmentsAsync(@"
What do you put in a toaster?
"); 9 | await sse.MergeSignalsAsync("{foo: {bar: 1}}"); 10 | await sse.ExecuteScriptAsync(@"console.log(""Success!"")"); 11 | }); 12 | -------------------------------------------------------------------------------- /site/static/code_snippets/going_deeper/multiple_events.gosnippet: -------------------------------------------------------------------------------- 1 | import ("github.com/starfederation/datastar/sdk/go/datastar") 2 | 3 | // Creates a new `ServerSentEventGenerator` instance. 4 | sse := datastar.NewSSE(w,r) 5 | 6 | sse.MergeFragments(`
Hello, world!
`) 7 | sse.MergeSignals([]byte(`{foo: {bar: 1}}`)) 8 | sse.ExecuteScript(`console.log('Success!')`) 9 | -------------------------------------------------------------------------------- /site/static/code_snippets/going_deeper/multiple_events.haskellsnippet: -------------------------------------------------------------------------------- 1 | import ServerSentEventGenerator 2 | import ServerSentEventGenerator.Server.Snap -- or whatever is appropriate 3 | 4 | send (withDefaults mergeFragments "
Hello, world!
" def def def def) 5 | send (withDefaults mergeSignals "{foo: {bar: 1}}" def def) 6 | send (withDefaults executeScript "console.log('Success!')" def def def) 7 | 8 | -------------------------------------------------------------------------------- /site/static/code_snippets/going_deeper/multiple_events.phpsnippet: -------------------------------------------------------------------------------- 1 | use starfederation\datastar\ServerSentEventGenerator; 2 | 3 | // Creates a new `ServerSentEventGenerator` instance. 4 | $sse = new ServerSentEventGenerator(); 5 | 6 | $sse->mergeFragments('
Hello, world!
'); 7 | $sse->mergeSignals(['foo' => ['bar' => 1]]); 8 | $sse->executeScript('console.log("Success!")'); -------------------------------------------------------------------------------- /site/static/code_snippets/going_deeper/multiple_events.rubysnippet: -------------------------------------------------------------------------------- 1 | require 'datastar' 2 | 3 | # Create a Datastar::Dispatcher instance 4 | 5 | datastar = Datastar.new(request:, response:) 6 | 7 | datastar.stream do |sse| 8 | sse.merge_fragments('
Hello, world!
') 9 | sse.merge_signals(foo: { bar: 1 }) 10 | sse.execute_script('console.log("Success!")') 11 | end 12 | -------------------------------------------------------------------------------- /site/static/code_snippets/going_deeper/multiple_events.rustsnippet: -------------------------------------------------------------------------------- 1 | use datastar::prelude::*; 2 | use async_stream::stream; 3 | 4 | Sse(stream! { 5 | yield MergeFragments::new("
Hello, world!
").into(); 6 | yield MergeSignals::new("{foo: {bar: 1}}").into(); 7 | yield ExecuteScript::new("console.log('Success!')).into(); 8 | }) 9 | -------------------------------------------------------------------------------- /site/static/code_snippets/going_deeper/multiple_events.typescriptsnippet: -------------------------------------------------------------------------------- 1 | ServerSentEventGenerator.stream(req, res, (stream) => { 2 | stream.mergeFragments('
Hello, world!
'); 3 | stream.mergeSignals({'foo': {'bar': 1}}); 4 | stream.executeScript('console.log("Success!")'); 5 | }); 6 | -------------------------------------------------------------------------------- /site/static/code_snippets/going_deeper/multiple_events.zigsnippet: -------------------------------------------------------------------------------- 1 | const datastar = @import("datastar").httpz; 2 | 3 | // Creates a new `ServerSentEventGenerator`. 4 | sse = try datastar.ServerSentEventGenerator.init(res); 5 | 6 | try sse.mergeFragments("
Hello, world!
", .{}); 7 | try sse.mergeSignals(.{ .foo = .{ .bar = 1 } }, .{}); 8 | try sse.executeScript("console.log('Success!')", .{}); -------------------------------------------------------------------------------- /site/static/code_snippets/how_tos/load_more.phpsnippet: -------------------------------------------------------------------------------- 1 | use starfederation\datastar\enums\FragmentMergeMode; 2 | use starfederation\datastar\ServerSentEventGenerator; 3 | 4 | $signals = ServerSentEventGenerator::readSignals(); 5 | 6 | $max = 5; 7 | $limit = 1; 8 | $offset = $signals['offset'] ?? 1; 9 | 10 | $sse = new ServerSentEventGenerator(); 11 | 12 | if ($offset < $max) { 13 | $newOffset = $offset + $limit; 14 | $sse->mergeFragments(`
Item $newOffset
`, [ 15 | 'selector' => '#list', 16 | 'mergeMode' => FragmentMergeMode::Append, 17 | ]); 18 | if (newOffset < $max) { 19 | $sse->mergeSignals(['offset' => $newOffset]); 20 | } else { 21 | $sse->removeFragments('#load-more'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /site/static/code_snippets/how_tos/polling_1.csharpsnippet: -------------------------------------------------------------------------------- 1 | using StarFederation.Datastar.DependencyInjection; 2 | 3 | app.MapGet("/endpoint", async (IDatastarServerSentEventService sse) => 4 | { 5 | var currentTime = DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss"); 6 | var fragment = $""" 7 |
8 | {currentTime} 9 |
10 | """; 11 | await sse.MergeFragmentsAsync(fragment); 12 | }); 13 | -------------------------------------------------------------------------------- /site/static/code_snippets/how_tos/polling_1.gosnippet: -------------------------------------------------------------------------------- 1 | import ( 2 | "time" 3 | "github.com/starfederation/datastar/sdk/go/datastar" 4 | ) 5 | 6 | currentTime := time.Now().Format("2006-01-02 15:04:05") 7 | 8 | sse := datastar.NewSSE(w, r) 9 | sse.MergeFragments(fmt.Sprintf(` 10 |
11 | %s 12 |
13 | `, currentTime)) -------------------------------------------------------------------------------- /site/static/code_snippets/how_tos/polling_1.haskellsnippet: -------------------------------------------------------------------------------- 1 | import ServerSentEventGenerator 2 | import ServerSentEventGenerator.Server.Snap -- or whatever is appropriate 3 | import Data.Time ( getCurrentTime ) 4 | import Data.Text ( pack ) 5 | 6 | now <- getCurrentTime 7 | let 8 | txt = mconcat [ 9 | "
" 10 | , (pack . show) now 11 | , "
" ] 12 | send $ mergeFragments txt def def def def 13 | 14 | 15 | -------------------------------------------------------------------------------- /site/static/code_snippets/how_tos/polling_1.phpsnippet: -------------------------------------------------------------------------------- 1 | use starfederation\datastar\ServerSentEventGenerator; 2 | 3 | $currentTime = date('Y-m-d H:i:s'); 4 | 5 | $sse = new ServerSentEventGenerator(); 6 | $sse->mergeFragments(` 7 |
10 | $currentTime 11 |
12 | `); -------------------------------------------------------------------------------- /site/static/code_snippets/how_tos/polling_1.rubysnippet: -------------------------------------------------------------------------------- 1 | datastar = Datastar.new(request:, response:) 2 | 3 | current_time = Time.now.strftime('%Y-%m-%d %H:%M:%S') 4 | 5 | datastar.merge_fragments <<~FRAGMENT 6 |
9 | #{current_time} 10 |
11 | FRAGMENT 12 | -------------------------------------------------------------------------------- /site/static/code_snippets/how_tos/polling_1.rustsnippet: -------------------------------------------------------------------------------- 1 | use datastar::prelude::*; 2 | use chrono::Local; 3 | use async_stream::stream; 4 | 5 | let current_time = Local::now().format("%Y-%m-%d %H:%M:%S").to_string(); 6 | 7 | Sse(stream! { 8 | yield MergeFragments::new( 9 | format!( 10 | "
{}
", 11 | current_time 12 | ) 13 | ).into(); 14 | }) -------------------------------------------------------------------------------- /site/static/code_snippets/how_tos/polling_1.typescriptsnippet: -------------------------------------------------------------------------------- 1 | import { createServer } from "node:http"; 2 | import { ServerSentEventGenerator } from "../npm/esm/node/serverSentEventGenerator.js"; 3 | 4 | const server = createServer(async (req, res) => { 5 | 6 | const currentTime = new Date().toISOString(); 7 | ServerSentEventGenerator.stream(req, res, (sse) => { 8 | sse.mergeFragments(` 9 |
12 | ${currentTime} 13 |
14 | `); 15 | }); 16 | }); 17 | 18 | -------------------------------------------------------------------------------- /site/static/code_snippets/how_tos/polling_1.zigsnippet: -------------------------------------------------------------------------------- 1 | const datastar = @import("datastar").httpz; 2 | const zdt = @import("zdt"); 3 | const std = @import("std"); 4 | 5 | var tz_chicago = try zdt.Timezone.fromTzdata("America/Chicago", res.arena); 6 | const datetime = try zdt.Datetime.fromISO8601("2006-01-02 15:04:05"); 7 | const current_time = try a_datetime.tzLocalize(.{ .tz = &tz_chicago }); 8 | 9 | var sse = try datastar.ServerSentEventGenerator.init(res); 10 | 11 | sse.mergeFragments( 12 | std.fmt.allocPrint( 13 | res.arena, 14 | "
{s}
", 15 | .{current_time}, 16 | ), 17 | .{}, 18 | ); -------------------------------------------------------------------------------- /site/static/code_snippets/how_tos/polling_2.csharpsnippet: -------------------------------------------------------------------------------- 1 | using StarFederation.Datastar.DependencyInjection; 2 | 3 | app.MapGet("/endpoint", async (IDatastarServerSentEventService sse) => 4 | { 5 | var currentTime = DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss"); 6 | var currentSeconds = DateTime.Now.Second; 7 | var duration = currentSeconds < 50 ? 5 : 1; 8 | var fragment = $""" 9 |
10 | {currentTime} 11 |
12 | """; 13 | await sse.MergeFragmentsAsync(fragment); 14 | }); -------------------------------------------------------------------------------- /site/static/code_snippets/how_tos/polling_2.gosnippet: -------------------------------------------------------------------------------- 1 | import ( 2 | "time" 3 | "github.com/starfederation/datastar/sdk/go/datastar" 4 | ) 5 | 6 | currentTime := time.Now().Format("2006-01-02 15:04:05") 7 | currentSeconds := time.Now().Format("05") 8 | duration := 1 9 | if currentSeconds < "50" { 10 | duration = 5 11 | } 12 | 13 | sse := datastar.NewSSE(w, r) 14 | sse.MergeFragments(fmt.Sprintf(` 15 |
16 | %s 17 |
18 | `, duration, currentTime)) -------------------------------------------------------------------------------- /site/static/code_snippets/how_tos/polling_2.phpsnippet: -------------------------------------------------------------------------------- 1 | use starfederation\datastar\ServerSentEventGenerator; 2 | 3 | $currentTime = date('Y-m-d H:i:s'); 4 | $currentSeconds = date('s'); 5 | $duration = $currentSeconds < 50 ? 5 : 1; 6 | 7 | $sse = new ServerSentEventGenerator(); 8 | $sse->mergeFragments(` 9 |
12 | $currentTime 13 |
14 | `); -------------------------------------------------------------------------------- /site/static/code_snippets/how_tos/polling_2.rubysnippet: -------------------------------------------------------------------------------- 1 | datastar = Datastar.new(request:, response:) 2 | 3 | now = Time.now 4 | current_time = now.strftime('%Y-%m-%d %H:%M:%S') 5 | current_seconds = now.strftime('%S').to_i 6 | duration = current_seconds < 50 ? 5 : 1 7 | 8 | datastar.merge_fragments <<~FRAGMENT 9 |
12 | #{current_time} 13 |
14 | FRAGMENT 15 | -------------------------------------------------------------------------------- /site/static/code_snippets/how_tos/polling_2.rustsnippet: -------------------------------------------------------------------------------- 1 | use datastar::prelude::*; 2 | use chrono::Local; 3 | use async_stream::stream; 4 | 5 | let current_time = Local::now().format("%Y-%m-%d %H:%M:%S").to_string(); 6 | let current_seconds = Local::now().second(); 7 | let duration = if current_seconds < 50 { 8 | 5 9 | } else { 10 | 1 11 | }; 12 | 13 | Sse(stream! { 14 | yield MergeFragments::new( 15 | format!( 16 | "
{}
", 17 | duration, 18 | current_time, 19 | ) 20 | ).into(); 21 | }) -------------------------------------------------------------------------------- /site/static/code_snippets/how_tos/polling_2.typescriptsnippet: -------------------------------------------------------------------------------- 1 | import { createServer } from "node:http"; 2 | import { ServerSentEventGenerator } from "../npm/esm/node/serverSentEventGenerator.js"; 3 | 4 | const server = createServer(async (req, res) => { 5 | const currentTime = new Date(); 6 | const duration = currentTime.getSeconds > 50 ? 5 : 1; 7 | 8 | ServerSentEventGenerator.stream(req, res, (sse) => { 9 | sse.mergeFragments(` 10 |
13 | ${currentTime.toISOString()} 14 |
15 | `); 16 | }); 17 | }); 18 | 19 | -------------------------------------------------------------------------------- /site/static/code_snippets/how_tos/redirect_1.clojuresnippet: -------------------------------------------------------------------------------- 1 | (require 2 | '[starfederation.datastar.clojure.api :as d*] 3 | '[starfederation.datastar.clojure.adapter.http-kit :refer [->sse-response on-open]] 4 | '[some.hiccup.library :refer [html]]) 5 | 6 | 7 | (defn handle [ring-request] 8 | (->sse-response ring-request 9 | {on-open 10 | (fn [sse] 11 | (d*/merge-fragment! sse 12 | (html [:div#indicator "Redirecting in 3 seconds..."])) 13 | (Thread/sleep 3000) 14 | (d*/execute-script! sse "window.location = \"/guide\"") 15 | (d*/close-sse! sse)})) 16 | 17 | -------------------------------------------------------------------------------- /site/static/code_snippets/how_tos/redirect_1.csharpsnippet: -------------------------------------------------------------------------------- 1 | using StarFederation.Datastar.DependencyInjection; 2 | 3 | app.MapGet("/redirect", async (IDatastarServerSentEventService sse) => 4 | { 5 | await sse.MergeFragmentsAsync("""
Redirecting in 3 seconds...
"""); 6 | await Task.Delay(TimeSpan.FromSeconds(3)); 7 | await sse.ExecuteScriptAsync("""window.location = "/guide";"""); 8 | }); -------------------------------------------------------------------------------- /site/static/code_snippets/how_tos/redirect_1.gosnippet: -------------------------------------------------------------------------------- 1 | import ( 2 | "time" 3 | "github.com/starfederation/datastar/sdk/go/datastar" 4 | ) 5 | 6 | sse := datastar.NewSSE(w, r) 7 | sse.MergeFragments(` 8 |
Redirecting in 3 seconds...
9 | `) 10 | time.Sleep(3 * time.Second) 11 | sse.ExecuteScript(` 12 | window.location = "/guide" 13 | `) -------------------------------------------------------------------------------- /site/static/code_snippets/how_tos/redirect_1.haskellsnippet: -------------------------------------------------------------------------------- 1 | import ServerSentEventGenerator 2 | import ServerSentEventGenerator.Server.Snap -- or whatever is appropriate 3 | 4 | send (withDefaults mergeFragments "
Redirecting in 3 seconds...
") 5 | threadDelay (3 * 1000 * 1000) 6 | send (withDefaults executeScript "window.location = \"/guide\"") 7 | -------------------------------------------------------------------------------- /site/static/code_snippets/how_tos/redirect_1.phpsnippet: -------------------------------------------------------------------------------- 1 | use starfederation\datastar\ServerSentEventGenerator; 2 | 3 | $sse = new ServerSentEventGenerator(); 4 | $sse->mergeFragments(` 5 |
Redirecting in 3 seconds...
6 | `); 7 | sleep(3); 8 | $sse->executeScript(` 9 | window.location = "/guide" 10 | `); -------------------------------------------------------------------------------- /site/static/code_snippets/how_tos/redirect_1.rubysnippet: -------------------------------------------------------------------------------- 1 | datastar = Datastar.new(request:, response:) 2 | 3 | datastar.stream do |sse| 4 | sse.merge_fragments '
Redirecting in 3 seconds...
' 5 | sleep 3 6 | sse.execute_script 'window.location = "/guide"' 7 | end 8 | -------------------------------------------------------------------------------- /site/static/code_snippets/how_tos/redirect_1.rustsnippet: -------------------------------------------------------------------------------- 1 | use datastar::prelude::*; 2 | use async_stream::stream; 3 | use core::time::Duration; 4 | 5 | Sse(stream! { 6 | yield MergeFragments::new("
Redirecting in 3 seconds...
").into(); 7 | tokio::time::sleep(core::time::Duration::from_secs(3)).await; 8 | yield ExecuteScript::new("window.location = '/guide'").into(); 9 | }); -------------------------------------------------------------------------------- /site/static/code_snippets/how_tos/redirect_1.typescriptsnippet: -------------------------------------------------------------------------------- 1 | import { createServer } from "node:http"; 2 | import { ServerSentEventGenerator } from "../npm/esm/node/serverSentEventGenerator.js"; 3 | 4 | function delay(milliseconds: number) { 5 | return new Promise((resolve) => { 6 | setTimeout(resolve, milliseconds); 7 | }); 8 | } 9 | 10 | const server = createServer(async (req, res) => { 11 | 12 | ServerSentEventGenerator.stream(req, res, async (sse) => { 13 | sse.mergeFragments(` 14 |
Redirecting in 3 seconds...
15 | `); 16 | 17 | await delay(3000); 18 | 19 | sse.executeScript(` 20 | window.location = "/guide" 21 | `); 22 | }); 23 | }); 24 | 25 | -------------------------------------------------------------------------------- /site/static/code_snippets/how_tos/redirect_1.zigsnippet: -------------------------------------------------------------------------------- 1 | const datastar = @import("datastar").httpz; 2 | const std = @import("std"); 3 | 4 | var sse = try datastar.ServerSentEventGenerator.init(res); 5 | 6 | sse.mergeFragments("
Redirecting in 3 seconds...
", .{}); 7 | std.Thread.sleep(std.time.ns_per_s * 3); 8 | sse.executeScript("window.location = '/guide'", .{}); -------------------------------------------------------------------------------- /site/static/code_snippets/how_tos/redirect_2.clojuresnippet: -------------------------------------------------------------------------------- 1 | (require 2 | '[starfederation.datastar.clojure.api :as d*] 3 | '[starfederation.datastar.clojure.adapter.http-kit :refer [->sse-response on-open]] 4 | '[some.hiccup.library :refer [html]]) 5 | 6 | 7 | (defn handle [ring-request] 8 | (->sse-response ring-request 9 | {on-open 10 | (fn [sse] 11 | (d*/merge-fragment! sse 12 | (html [:div#indicator "Redirecting in 3 seconds..."])) 13 | (Thread/sleep 3000) 14 | (d*/execute-script! sse 15 | "setTimeout(() => window.location = \"/guide\"") 16 | (d*/close-sse! sse))})) 17 | 18 | -------------------------------------------------------------------------------- /site/static/code_snippets/how_tos/redirect_2.csharpsnippet: -------------------------------------------------------------------------------- 1 | using StarFederation.Datastar.DependencyInjection; 2 | 3 | app.MapGet("/redirect", async (IDatastarServerSentEventService sse) => 4 | { 5 | await sse.MergeFragmentsAsync("""
Redirecting in 3 seconds...
"""); 6 | await Task.Delay(TimeSpan.FromSeconds(3)); 7 | await sse.ExecuteScriptAsync("""setTimeout(() => window.location = "/guide");"""); 8 | }); -------------------------------------------------------------------------------- /site/static/code_snippets/how_tos/redirect_2.gosnippet: -------------------------------------------------------------------------------- 1 | import ( 2 | "time" 3 | "github.com/starfederation/datastar/sdk/go/datastar" 4 | ) 5 | 6 | sse := datastar.NewSSE(w, r) 7 | sse.MergeFragments(` 8 |
Redirecting in 3 seconds...
9 | `) 10 | time.Sleep(3 * time.Second) 11 | sse.ExecuteScript(` 12 | setTimeout(() => window.location = "/guide") 13 | `) -------------------------------------------------------------------------------- /site/static/code_snippets/how_tos/redirect_2.haskellsnippet: -------------------------------------------------------------------------------- 1 | import ServerSentEventGenerator 2 | import ServerSentEventGenerator.Server.Snap -- or whatever is appropriate 3 | 4 | send (withDefaults mergeFragments "
Redirecting in 3 seconds...
") 5 | threadDelay (3 * 1000 * 1000) 6 | send (withDefaults executeScript "window.location = \"/guide\"") 7 | -------------------------------------------------------------------------------- /site/static/code_snippets/how_tos/redirect_2.phpsnippet: -------------------------------------------------------------------------------- 1 | use starfederation\datastar\ServerSentEventGenerator; 2 | 3 | $sse = new ServerSentEventGenerator(); 4 | $sse->mergeFragments(` 5 |
Redirecting in 3 seconds...
6 | `); 7 | sleep(3); 8 | $sse->executeScript(` 9 | setTimeout(() => window.location = "/guide") 10 | `); -------------------------------------------------------------------------------- /site/static/code_snippets/how_tos/redirect_2.rubysnippet: -------------------------------------------------------------------------------- 1 | datastar = Datastar.new(request:, response:) 2 | 3 | datastar.stream do |sse| 4 | sse.merge_fragments '
Redirecting in 3 seconds...
' 5 | 6 | sleep 3 7 | 8 | sse.execute_script <<~JS 9 | setTimeout(() => { 10 | window.location = '/guide' 11 | }) 12 | JS 13 | end 14 | -------------------------------------------------------------------------------- /site/static/code_snippets/how_tos/redirect_2.rustsnippet: -------------------------------------------------------------------------------- 1 | use datastar::prelude::*; 2 | use async_stream::stream; 3 | use core::time::Duration; 4 | 5 | Sse(stream! { 6 | yield MergeFragments::new("
Redirecting in 3 seconds...
").into(); 7 | tokio::time::sleep(core::time::Duration::from_secs(3)).await; 8 | yield ExecuteScript::new("setTimeout(() => window.location = '/guide')").into(); 9 | }); -------------------------------------------------------------------------------- /site/static/code_snippets/how_tos/redirect_2.typescriptsnippet: -------------------------------------------------------------------------------- 1 | import { createServer } from "node:http"; 2 | import { ServerSentEventGenerator } from "../npm/esm/node/serverSentEventGenerator.js"; 3 | 4 | function delay(milliseconds: number) { 5 | return new Promise((resolve) => { 6 | setTimeout(resolve, milliseconds); 7 | }); 8 | } 9 | 10 | const server = createServer(async (req, res) => { 11 | 12 | ServerSentEventGenerator.stream(req, res, async (sse) => { 13 | sse.mergeFragments(` 14 |
Redirecting in 3 seconds...
15 | `); 16 | 17 | await delay(3000); 18 | 19 | sse.executeScript(` 20 | setTimeout(() => window.location = "/guide") 21 | `); 22 | }); 23 | }); 24 | 25 | -------------------------------------------------------------------------------- /site/static/code_snippets/how_tos/redirect_2.zigsnippet: -------------------------------------------------------------------------------- 1 | const datastar = @import("datastar").httpz; 2 | const std = @import("std"); 3 | 4 | var sse = try datastar.ServerSentEventGenerator.init(res); 5 | 6 | sse.mergeFragments("
Redirecting in 3 seconds...
", .{}); 7 | std.Thread.sleep(std.time.ns_per_s * 3); 8 | sse.executeScript("setTimeout(() => window.location = '/guide')", .{}); -------------------------------------------------------------------------------- /site/static/code_snippets/how_tos/redirect_3.clojuresnippet: -------------------------------------------------------------------------------- 1 | (require 2 | '[starfederation.datastar.clojure.api :as d*] 3 | '[starfederation.datastar.clojure.adapter.http-kit :refer [->sse-response on-open]] 4 | '[some.hiccup.library :refer [html]]) 5 | 6 | 7 | (defn handler [ring-request] 8 | (->sse-response ring-request 9 | {on-open 10 | (fn [sse] 11 | (d*/merge-fragment! sse 12 | (html [:div#indicator "Redirecting in 3 seconds..."])) 13 | (Thread/sleep 3000) 14 | (d*/redirect! sse "/guide") 15 | (d*/close-sse! sse))})) 16 | 17 | -------------------------------------------------------------------------------- /site/static/code_snippets/how_tos/redirect_3.csharpsnippet: -------------------------------------------------------------------------------- 1 | using StarFederation.Datastar.DependencyInjection; 2 | using StarFederation.Datastar.Scripts; 3 | 4 | app.MapGet("/redirect", async (IDatastarServerSentEventService sse) => 5 | { 6 | await sse.MergeFragmentsAsync("""
Redirecting in 3 seconds...
"""); 7 | await Task.Delay(TimeSpan.FromSeconds(3)); 8 | await sse.Redirect("/guide"); 9 | }); -------------------------------------------------------------------------------- /site/static/code_snippets/how_tos/redirect_3.gosnippet: -------------------------------------------------------------------------------- 1 | import ( 2 | "time" 3 | "github.com/starfederation/datastar/sdk/go/datastar" 4 | ) 5 | 6 | sse := datastar.NewSSE(w, r) 7 | sse.MergeFragments(` 8 |
Redirecting in 3 seconds...
9 | `) 10 | time.Sleep(3 * time.Second) 11 | sse.Redirect("/guide") -------------------------------------------------------------------------------- /site/static/code_snippets/how_tos/redirect_3.phpsnippet: -------------------------------------------------------------------------------- 1 | use starfederation\datastar\ServerSentEventGenerator; 2 | 3 | $sse = new ServerSentEventGenerator(); 4 | $sse->mergeFragments(` 5 |
Redirecting in 3 seconds...
6 | `); 7 | sleep(3); 8 | $sse->location('/guide'); -------------------------------------------------------------------------------- /site/static/code_snippets/how_tos/redirect_3.rubysnippet: -------------------------------------------------------------------------------- 1 | datastar = Datastar.new(request:, response:) 2 | 3 | datastar.stream do |sse| 4 | sse.merge_fragments '
Redirecting in 3 seconds...
' 5 | 6 | sleep 3 7 | 8 | sse.redirect '/guide' 9 | end 10 | -------------------------------------------------------------------------------- /site/static/code_snippets/how_tos/redirect_3.zigsnippet: -------------------------------------------------------------------------------- 1 | const datastar = @import("datastar").httpz; 2 | const std = @import("std"); 3 | 4 | var sse = try datastar.ServerSentEventGenerator.init(res); 5 | 6 | sse.mergeFragments("
Redirecting in 3 seconds...
", .{}); 7 | std.Thread.sleep(std.time.ns_per_s * 3); 8 | sse.redirect("/guide", .{}); -------------------------------------------------------------------------------- /site/static/css/.gitignore: -------------------------------------------------------------------------------- 1 | site.css -------------------------------------------------------------------------------- /site/static/favicon/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starfederation/datastar/03164b47af392a8e25275e14c46ce2a9a1935a37/site/static/favicon/android-chrome-192x192.png -------------------------------------------------------------------------------- /site/static/favicon/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starfederation/datastar/03164b47af392a8e25275e14c46ce2a9a1935a37/site/static/favicon/android-chrome-512x512.png -------------------------------------------------------------------------------- /site/static/favicon/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starfederation/datastar/03164b47af392a8e25275e14c46ce2a9a1935a37/site/static/favicon/apple-touch-icon.png -------------------------------------------------------------------------------- /site/static/favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starfederation/datastar/03164b47af392a8e25275e14c46ce2a9a1935a37/site/static/favicon/favicon-16x16.png -------------------------------------------------------------------------------- /site/static/favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starfederation/datastar/03164b47af392a8e25275e14c46ce2a9a1935a37/site/static/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /site/static/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starfederation/datastar/03164b47af392a8e25275e14c46ce2a9a1935a37/site/static/favicon/favicon.ico -------------------------------------------------------------------------------- /site/static/favicon/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Datastar", 3 | "short_name": "", 4 | "icons": [ 5 | { 6 | "src": "/static/favicon/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/static/favicon/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#ffffff", 17 | "background_color": "#ffffff", 18 | "display": "standalone" 19 | } 20 | -------------------------------------------------------------------------------- /site/static/images/badapple.zst: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starfederation/datastar/03164b47af392a8e25275e14c46ce2a9a1935a37/site/static/images/badapple.zst -------------------------------------------------------------------------------- /site/static/images/essays/datastar_dependencies.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starfederation/datastar/03164b47af392a8e25275e14c46ce2a9a1935a37/site/static/images/essays/datastar_dependencies.png -------------------------------------------------------------------------------- /site/static/images/essays/fullstack.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starfederation/datastar/03164b47af392a8e25275e14c46ce2a9a1935a37/site/static/images/essays/fullstack.jpg -------------------------------------------------------------------------------- /site/static/images/examples/tokyo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starfederation/datastar/03164b47af392a8e25275e14c46ce2a9a1935a37/site/static/images/examples/tokyo.png -------------------------------------------------------------------------------- /site/static/images/memes/bender.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starfederation/datastar/03164b47af392a8e25275e14c46ce2a9a1935a37/site/static/images/memes/bender.png -------------------------------------------------------------------------------- /site/static/images/memes/bigsky.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starfederation/datastar/03164b47af392a8e25275e14c46ce2a9a1935a37/site/static/images/memes/bigsky.png -------------------------------------------------------------------------------- /site/static/images/memes/both.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starfederation/datastar/03164b47af392a8e25275e14c46ce2a9a1935a37/site/static/images/memes/both.png -------------------------------------------------------------------------------- /site/static/images/memes/club.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starfederation/datastar/03164b47af392a8e25275e14c46ce2a9a1935a37/site/static/images/memes/club.png -------------------------------------------------------------------------------- /site/static/images/memes/compile_time.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starfederation/datastar/03164b47af392a8e25275e14c46ce2a9a1935a37/site/static/images/memes/compile_time.png -------------------------------------------------------------------------------- /site/static/images/memes/crap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starfederation/datastar/03164b47af392a8e25275e14c46ce2a9a1935a37/site/static/images/memes/crap.png -------------------------------------------------------------------------------- /site/static/images/memes/force_feed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starfederation/datastar/03164b47af392a8e25275e14c46ce2a9a1935a37/site/static/images/memes/force_feed.png -------------------------------------------------------------------------------- /site/static/images/memes/history.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starfederation/datastar/03164b47af392a8e25275e14c46ce2a9a1935a37/site/static/images/memes/history.png -------------------------------------------------------------------------------- /site/static/images/memes/hypermedia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starfederation/datastar/03164b47af392a8e25275e14c46ce2a9a1935a37/site/static/images/memes/hypermedia.png -------------------------------------------------------------------------------- /site/static/images/memes/one.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starfederation/datastar/03164b47af392a8e25275e14c46ce2a9a1935a37/site/static/images/memes/one.png -------------------------------------------------------------------------------- /site/static/images/memes/only_clicks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starfederation/datastar/03164b47af392a8e25275e14c46ce2a9a1935a37/site/static/images/memes/only_clicks.png -------------------------------------------------------------------------------- /site/static/images/memes/polling.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starfederation/datastar/03164b47af392a8e25275e14c46ce2a9a1935a37/site/static/images/memes/polling.png -------------------------------------------------------------------------------- /site/static/images/memes/remember.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starfederation/datastar/03164b47af392a8e25275e14c46ce2a9a1935a37/site/static/images/memes/remember.png -------------------------------------------------------------------------------- /site/static/images/memes/rule7b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starfederation/datastar/03164b47af392a8e25275e14c46ce2a9a1935a37/site/static/images/memes/rule7b.png -------------------------------------------------------------------------------- /site/static/images/memes/sse_vs_xhr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starfederation/datastar/03164b47af392a8e25275e14c46ce2a9a1935a37/site/static/images/memes/sse_vs_xhr.png -------------------------------------------------------------------------------- /site/static/images/memes/target_id_not_found.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starfederation/datastar/03164b47af392a8e25275e14c46ce2a9a1935a37/site/static/images/memes/target_id_not_found.png -------------------------------------------------------------------------------- /site/static/images/memes/use_datastar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starfederation/datastar/03164b47af392a8e25275e14c46ce2a9a1935a37/site/static/images/memes/use_datastar.png -------------------------------------------------------------------------------- /site/static/images/rocket-circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starfederation/datastar/03164b47af392a8e25275e14c46ce2a9a1935a37/site/static/images/rocket-circle.png -------------------------------------------------------------------------------- /site/static/images/rocket-social-preview-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starfederation/datastar/03164b47af392a8e25275e14c46ce2a9a1935a37/site/static/images/rocket-social-preview-white.png -------------------------------------------------------------------------------- /site/static/images/rocket-social-preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starfederation/datastar/03164b47af392a8e25275e14c46ce2a9a1935a37/site/static/images/rocket-social-preview.png -------------------------------------------------------------------------------- /site/static/images/rocket.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starfederation/datastar/03164b47af392a8e25275e14c46ce2a9a1935a37/site/static/images/rocket.gif -------------------------------------------------------------------------------- /site/static/images/rocket.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starfederation/datastar/03164b47af392a8e25275e14c46ce2a9a1935a37/site/static/images/rocket.png -------------------------------------------------------------------------------- /site/static/images/rocket.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starfederation/datastar/03164b47af392a8e25275e14c46ce2a9a1935a37/site/static/images/rocket.webp -------------------------------------------------------------------------------- /site/static/js/.gitignore: -------------------------------------------------------------------------------- 1 | datastar* -------------------------------------------------------------------------------- /site/static/js/sortable.js: -------------------------------------------------------------------------------- 1 | import Sortable from 'https://cdn.jsdelivr.net/npm/sortablejs@1.15.3/+esm' 2 | 3 | const sortContainer = document.getElementById('sortContainer') 4 | 5 | new Sortable(sortContainer, { 6 | animation: 150, 7 | ghostClass: 'opacity-25', 8 | onEnd: (evt) => { 9 | sortContainer.dispatchEvent(new CustomEvent('reordered', {detail: {orderInfo: `Moved from ${evt.oldIndex} to ${evt.newIndex}`}})); 10 | } 11 | }) 12 | 13 | -------------------------------------------------------------------------------- /site/static/js/web_component.js: -------------------------------------------------------------------------------- 1 | class ReverseComponent extends HTMLElement { 2 | static get observedAttributes() { 3 | return ['name']; 4 | } 5 | 6 | attributeChangedCallback(name, oldValue, newValue) { 7 | const value = newValue.split('').reverse().join(''); 8 | this.dispatchEvent(new CustomEvent('reverse', {detail: {value}})); 9 | } 10 | } 11 | 12 | customElements.define('reverse-component', ReverseComponent); 13 | -------------------------------------------------------------------------------- /site/static/md/essays/haikus.md: -------------------------------------------------------------------------------- 1 | # Haikus 2 | 3 | ## HTMX 4 | 5 | [HTMX](https://htmx.org/) has a wonderful haiku in its footer 6 | 7 | > JavaScript fatigue 8 | 9 | > longing for a hypertext 10 | 11 | > already in hand 12 | 13 | ## Datastar 14 | 15 | I think this also applies to Datastar, but thought of a few that might match more to this project's goals. 16 | 17 | > Hypermedia 18 | 19 | > reactivity advanced 20 | 21 | > attributes enhance 22 | 23 | --- 24 | 25 | > Code compiles with ease, 26 | 27 | > Build steps streamline, bring release, 28 | 29 | > Dev's joy, future peace. 30 | 31 | --- 32 | 33 | > Standard tags convey, 34 | 35 | > No monkey patching today, 36 | 37 | > Clean hypertext, hooray! 38 | -------------------------------------------------------------------------------- /site/static/md/examples/bind_keys.md: -------------------------------------------------------------------------------- 1 | ## Bind Keys 2 | 3 | ## Demo 4 | 5 |

Press Ctrl+K

6 |

Press Enter

7 | 8 | ## Explanation 9 | 10 | ```html 11 |

Press Ctrl+K

12 |

Press Enter

13 | ``` 14 | 15 | Able to bind to any value on the `event`. Because of how `data-*` attributes are interpreted you'll need to use `kebab-case` for `camelCase` attributes. 16 | -------------------------------------------------------------------------------- /site/static/md/examples/bulk_update.md: -------------------------------------------------------------------------------- 1 | ## Bulk Update 2 | 3 | [Original HTMX Version](https://htmx.org/examples/bulk-update/) 4 | 5 | ## Demo 6 | 7 |
8 |
9 | 10 | ## Explanation 11 | 12 | This demo shows how to implement a common pattern where rows are selected and then bulk updated. This is accomplished by 13 | putting a form around a table, with checkboxes in the table, and then including the checked values in PUT’s to two 14 | different endpoints: `activate` and `deactivate`. 15 | 16 | The server will either activate or deactivate the checked users and then rerender the tbody tag with updated rows. -------------------------------------------------------------------------------- /site/static/md/examples/csrf.md: -------------------------------------------------------------------------------- 1 | ## CSRF 2 | 3 | ## Explanation 4 | 5 | Sometimes a backend framework need to set a header. Normally you should be using cookies to be more secure, but it 6 | depends on your backend. 7 | 8 | ```html 9 | 13 |
14 | 15 |
16 |
17 | ``` 18 | 19 | ## Demo 20 | 21 |
Update Me
-------------------------------------------------------------------------------- /site/static/md/examples/inline_validation.md: -------------------------------------------------------------------------------- 1 | ## Inline Validation 2 | 3 | [Original HTMX Version](https://htmx.org/examples/inline-validation/) 4 | 5 | ## Demo 6 | 7 | The only email that will be accepted is test@test.com. 8 | 9 |
10 |
11 | 12 | ## Explanation 13 | 14 | This example shows how to do inline field validation, in this case of an email address. To do this we need to create a 15 | form with an input that POSTs back to the server with the value to be validated and updates the DOM with the validation 16 | results. Since its easy to replace the whole form the logic for displaying the validation results is kept simple. -------------------------------------------------------------------------------- /site/static/md/examples/invalid_signals.md: -------------------------------------------------------------------------------- 1 | ## Invalid Signals 2 | 3 |
4 |
5 |
6 | 7 | ## Explanation 8 | 9 | In the following example, `$foo` is not a valid signal, since only the leaf nodes in namespaced signals are actually signals. Open the console and you’ll see a link to a helpful error message. 10 | 11 | ```html 12 |
13 |
14 |
15 | ``` -------------------------------------------------------------------------------- /site/static/md/examples/key_casing.md: -------------------------------------------------------------------------------- 1 | ## Key Casing 2 | 3 | ## Demo 4 | 5 |
11 |

12 | 
13 | 14 | ## Explanation 15 | 16 | ``` 17 |
23 |
24 | ``` -------------------------------------------------------------------------------- /site/static/md/examples/lazy_load.md: -------------------------------------------------------------------------------- 1 | ## Lazy Load 2 | 3 | [Original HTMX Version](https://htmx.org/examples/lazy-load/) 4 | 5 | ## Demo 6 | 7 |
8 |
9 | 10 | ## Explanation 11 | 12 | This example shows how to lazily load an element on a page. We start with an initial state that looks like this: 13 | 14 | ```html 15 |
16 | Loading... 17 |
18 | ``` 19 | 20 | Which shows a progress indicator as we are loading the graph. The graph is then loaded. -------------------------------------------------------------------------------- /site/static/md/examples/lazy_tabs.md: -------------------------------------------------------------------------------- 1 | ## Lazy Tabs 2 | 3 | [Original HTMX Version](https://htmx.org/examples/tabs-hateoas/) 4 | 5 | ## Demo 6 | 7 |
8 |
9 | 10 | ## Explanation 11 | 12 | This example shows how easy it is to implement tabs using Datastar. Following the principle of [Hypertext As The Engine 13 | Of Application State](https://en.wikipedia.org/wiki/HATEOAS), the selected tab is a part of the application state. 14 | Therefore, to display and select tabs in your application, simply include the tab markup in the returned HTML fragment. -------------------------------------------------------------------------------- /site/static/md/examples/merge_options.md: -------------------------------------------------------------------------------- 1 | ## Merge Options 2 | 3 | ## Demo 4 | 5 |
6 | 7 | ## Explanation 8 | 9 | Shows the current merge options available. For best understanding open up your dev tools and look at the contents of 10 | `#contents` to see how the merge options effect the DOM. 11 | 12 | ** Note:** This uses `#target` and can create multiple elements with the same ID (which is [not valid 13 | HTML](https://html.spec.whatwg.org/#the-id-attribute)) but is fine for this example. In actual use be sure to make IDs 14 | unique. -------------------------------------------------------------------------------- /site/static/md/examples/on_load.md: -------------------------------------------------------------------------------- 1 | ## On load 2 | 3 | ## Demo 4 | 5 |
6 | No session data 7 |
8 | 9 | ## Explanation 10 | 11 | ```html 12 |
13 | No session data 14 |
15 | ``` 16 | 17 | The `data-on-load` attribute is used to specify a fetch request that should be made when the element is loaded. The 18 | value 19 | of the attribute is a JavaScript expression that is evaluated when the element is loaded. 20 | 21 | The following example adds a delay of 3 seconds. 22 | 23 | ```html 24 |
25 | No session data 26 |
27 | ``` -------------------------------------------------------------------------------- /site/static/md/examples/quick_primer_go.md: -------------------------------------------------------------------------------- 1 | ## Quick Primer in Go 2 | 3 | ## Demo 4 | 5 |
6 | 7 | ## Explanation 8 | 9 | We have the [quick primer](/docs/getting_started), here is the same example ported to be part of the Go backend that runs the site. -------------------------------------------------------------------------------- /site/static/md/examples/redirects.md: -------------------------------------------------------------------------------- 1 | ## Redirects 2 | 3 | ## Demo 4 | 5 |
6 |
7 | 8 | ## Explanation 9 | 10 | As part of SSE updates you may want to redirect the user to a different page. The 11 | [`datastar-execute-script`](/reference/sse_events#datastar-execute-script) SSE event can be used to execute JavaScript 12 | on the client. 13 | 14 | ```html 15 | event: datastar-execute-script 16 | data: script window.location = "/essays/grugs_around_fire" 17 | ``` -------------------------------------------------------------------------------- /site/static/md/examples/regex.md: -------------------------------------------------------------------------------- 1 | ## Regex 2 | 3 |
4 |
5 |
6 |
7 |
8 |
9 | -------------------------------------------------------------------------------- /site/static/md/examples/signals_ifmissing_onload.md: -------------------------------------------------------------------------------- 1 | ## Signals If Missing on Load 2 | 3 | ## Demo 4 | 5 |
6 |
7 | Should always be 1234: 8 | 9 |
10 |
11 | 12 | ## Explanation 13 | 14 | ```html 15 |
16 |
17 | Should always be 1234: 18 | 19 |
20 |
21 | ``` -------------------------------------------------------------------------------- /site/static/md/examples/templ_counter.md: -------------------------------------------------------------------------------- 1 | ## Templ Counter 2 | 3 | [Original Templ Version](https://templ.guide/server-side-rendering/example-counter-application/) 4 | 5 | ## Demo 6 | 7 |
8 |
9 | 10 | ## Explanation 11 | 12 | Full explanation can be found on the [Templ website](https://templ.guide/server-side-rendering/datastar/) -------------------------------------------------------------------------------- /site/static/md/examples/timing.md: -------------------------------------------------------------------------------- 1 | ## Timing 2 | 3 | ## Demo 4 | 5 |
7 |
Count increments every 5s:
8 | 10 |
-------------------------------------------------------------------------------- /site/static/md/examples/toggle_visibility.md: -------------------------------------------------------------------------------- 1 | ## Toggle Visibility 2 | 3 | ## Demo 4 | 5 |
6 | 7 | ## Explanation 8 | 9 | This example displays how to toggle visibility using the `data-show` attribute. 10 | 11 | ```html 12 |
13 | 16 |
17 | Hello! 18 |
19 |
20 | ``` -------------------------------------------------------------------------------- /site/static/md/how_tos/how_to_stream_sse_events_with_a_user_defined_delay.md: -------------------------------------------------------------------------------- 1 | # How to stream SSE events with a user-defined delay 2 | 3 | -------------------------------------------------------------------------------- /site/static/md/tests/aliased.md: -------------------------------------------------------------------------------- 1 | # Aliased 2 | 3 | Tests the `data-star-*` alias. 4 | 5 |
6 | Result: 7 | 8 |
9 | Expected result on load: 1 10 |
-------------------------------------------------------------------------------- /site/static/md/tests/attr_false.md: -------------------------------------------------------------------------------- 1 | # Attr False 2 | 3 | Tests that `data-attr-*` removes the element attribute when the value is `false`. 4 | 5 |
6 | 7 |
8 | Result: 9 | 10 |
11 | Expected result on load: 1 12 |
-------------------------------------------------------------------------------- /site/static/md/tests/attr_object_false.md: -------------------------------------------------------------------------------- 1 | # Attr Object False 2 | 3 | Tests that `data-attr` removes the element attribute when the value is `false`. 4 | 5 |
6 | 7 |
8 | Result: 9 | 10 |
11 | Expected result on load: 1 12 |
-------------------------------------------------------------------------------- /site/static/md/tests/checkbox_array.md: -------------------------------------------------------------------------------- 1 | # Checkbox Array 2 | 3 | Tests that a checkbox input's value is added to a bound signal array when checked. 4 | 5 |
6 | foo 7 |
8 | bar 9 |
10 | baz 11 |

12 |   
13 | Result: 14 | 15 |
16 | Expected result on clicking `baz`: 1 17 |
-------------------------------------------------------------------------------- /site/static/md/tests/checkbox_boolean.md: -------------------------------------------------------------------------------- 1 | # Checkbox Boolean 2 | 3 | Tests that a checkbox input's bound signal is set to `false` when unchecked and `true` when checked. 4 | 5 |
6 | 7 | 8 |
9 | Result: 10 | 11 |
12 | Expected result on click: 1 13 |
-------------------------------------------------------------------------------- /site/static/md/tests/checkbox_boolean_checked.md: -------------------------------------------------------------------------------- 1 | # Checkbox Boolean Checked 2 | 3 | Tests that a checkbox input's bound signal is set to `false` when unchecked and `true` when checked. 4 | 5 |
6 | 7 | 8 |
9 | Result: 10 | 11 |
12 | Expected result on click: 1 13 |
-------------------------------------------------------------------------------- /site/static/md/tests/checkbox_value.md: -------------------------------------------------------------------------------- 1 | # Checkbox Value 2 | 3 | Tests that a checkbox input's bound signal is set to an empty string when unchecked and its value when checked. 4 | 5 |
6 | 7 | 8 |
9 | Result: 10 | 11 |
12 | Expected result on click: 1 13 |
-------------------------------------------------------------------------------- /site/static/md/tests/checkbox_value_checked.md: -------------------------------------------------------------------------------- 1 | # Checkbox Value Checked 2 | 3 | Tests that a checkbox input's bound signal is initially set to its value when initially checked. 4 | 5 |
6 | 7 | 8 |
9 | Result: 10 | 11 |
12 | Expected result on click: 1 13 |
-------------------------------------------------------------------------------- /site/static/md/tests/custom_plugin.md: -------------------------------------------------------------------------------- 1 | # Custom Plugin 2 | 3 | Tests loading a custom plugin. 4 | 5 | 16 | 17 |
18 | Result: 19 | 0 20 |
21 | Expected result on load: 1 22 |
-------------------------------------------------------------------------------- /site/static/md/tests/indicator.md: -------------------------------------------------------------------------------- 1 | # Indicator 2 | 3 | Tests that the indicator signal is set to `false` when a request completes. 4 | 5 |
6 |
7 | 8 |
9 | Result: 10 | 11 |
12 | Expected result on click: 1 13 |
-------------------------------------------------------------------------------- /site/static/md/tests/indicator_element_removed.md: -------------------------------------------------------------------------------- 1 | # Indicator Element Removed 2 | 3 | Tests that the indicator signal is set to `false` when a request completes and the element it was on is removed from the DOM. 4 | 5 |
6 |
7 |
8 | Result: 9 | 10 |
11 | Expected result on click: 1 12 |
-------------------------------------------------------------------------------- /site/static/md/tests/input_array.md: -------------------------------------------------------------------------------- 1 | # Input Array 2 | 3 | Tests that a text input's value is added to a bound signal array when changed. 4 | 5 |
6 | 7 |
8 | 9 |

10 |   
11 | Result: 12 | 13 |
14 | Expected result on entering `bar` in second input: 1 15 |
16 | -------------------------------------------------------------------------------- /site/static/md/tests/input_signal.md: -------------------------------------------------------------------------------- 1 | # Input Signal 2 | 3 | Tests that a text input's bound signal is not set to its value when a signal is defined. 4 | 5 |
6 | 7 |
8 | Result: 9 | 10 |
11 | Expected result on load: 1 12 |
-------------------------------------------------------------------------------- /site/static/md/tests/input_value.md: -------------------------------------------------------------------------------- 1 | # Input Value 2 | 3 | Tests that a text input's bound signal is set to its value when non-empty and no signal is defined. 4 | 5 |
6 | 7 |
8 | Result: 9 | 10 |
11 | Expected result on load: 1 12 |
-------------------------------------------------------------------------------- /site/static/md/tests/key_casing.md: -------------------------------------------------------------------------------- 1 | # Key Casing 2 | 3 | Tests that keys are correctly cased when using the `__case` modifier, and are assignable in expressions. 4 | 5 |
6 | Result: 7 | 8 |
9 | Expected result on load: 1 10 |
-------------------------------------------------------------------------------- /site/static/md/tests/local_signals.md: -------------------------------------------------------------------------------- 1 | # Local Signals 2 | 3 | Tests that local signals are assignable in expressions. 4 | 5 |
6 | Result: 7 | 8 |
9 | Expected result on load: 1 10 |
-------------------------------------------------------------------------------- /site/static/md/tests/merge_fragment.md: -------------------------------------------------------------------------------- 1 | # Merge Fragment 2 | 3 | Tests merging a fragment. 4 | 5 |
6 | 7 |
8 | Result: 9 | 0 10 |
11 | Expected result on click: 1 12 |
-------------------------------------------------------------------------------- /site/static/md/tests/merge_fragment_containing_on_event.md: -------------------------------------------------------------------------------- 1 | # Merge Fragment Containing On Event 2 | 3 | Tests merging a fragment containing an `on` event. 4 | 5 |
6 |
7 |
8 | 9 |

10 | 
-------------------------------------------------------------------------------- /site/static/md/tests/merge_fragment_input_value.md: -------------------------------------------------------------------------------- 1 | # Merge Fragment Input Value 2 | 3 | Tests merging a fragment containing a bound input value. 4 | 5 |
6 |
7 | 8 |
9 | Result: 10 | 0 11 |
12 | Expected result on entering `foo` in input and then click: 1 13 |
-------------------------------------------------------------------------------- /site/static/md/tests/merge_fragment_on_load.md: -------------------------------------------------------------------------------- 1 | # Merge Fragment On Load 2 | 3 | Tests merging a fragment containing `data-on-load`. 4 | 5 |
6 |
7 | 8 |
9 | Result: 10 | 11 |
12 | Expected result on click: 1 13 |
-------------------------------------------------------------------------------- /site/static/md/tests/merge_fragment_outer_multiple_targets.md: -------------------------------------------------------------------------------- 1 | # Merge Fragment Outer Multiple Targets 2 | 3 | Tests merging a fragment using the `outer` merge mode with multiple targets. 4 | 5 |
6 |
7 | 8 |
9 |
10 |
11 | Result: 12 | 13 |
14 | Expected result on click: 1 15 |
-------------------------------------------------------------------------------- /site/static/md/tests/merge_fragment_signals.md: -------------------------------------------------------------------------------- 1 | # Merge Fragment Signals 2 | 3 | Tests merging a fragment containing `data-signals-*`. 4 | 5 |
6 |
7 | 8 |
9 | Result: 10 | 11 |
12 | Expected result on click: 1 13 |
-------------------------------------------------------------------------------- /site/static/md/tests/merge_fragment_whitespace.md: -------------------------------------------------------------------------------- 1 | # Merge Fragment Whitespace 2 | 3 | Tests merging a fragment maintains whitespace. 4 | 5 |
6 | 7 |

 8 |   
9 | Result: 10 | 11 |
12 | Expected result on click: 1 13 |
-------------------------------------------------------------------------------- /site/static/md/tests/on_load.md: -------------------------------------------------------------------------------- 1 | # On Load 2 | 3 | Tests that the on load event is triggered after all other data attributes on the element are loaded, without forcing the 4 | user to specify the correct order. 5 | 6 |
7 | Result: 8 | 0 9 |
10 | Expected result on load: 1 11 |
-------------------------------------------------------------------------------- /site/static/md/tests/on_load_delay.md: -------------------------------------------------------------------------------- 1 | # On Load Delay 2 | 3 | Tests that the on load event is triggered with a delay. 4 | 5 |
6 | Result: 7 | 8 |
9 | Expected result on wait: 1 10 |
-------------------------------------------------------------------------------- /site/static/md/tests/on_signal_change.md: -------------------------------------------------------------------------------- 1 | # On Signal Change 2 | 3 | Tests detecting a signal change. 4 | 5 |
6 | Result: 7 | 8 |
9 | Expected result on click: 1 10 |
-------------------------------------------------------------------------------- /site/static/md/tests/on_signal_change_path.md: -------------------------------------------------------------------------------- 1 | # On Signal Change Path 2 | 3 | Tests detecting a signal change with a path. 4 | 5 |
6 | Result: 7 | 8 |
9 | Expected result on click: 1 10 |
-------------------------------------------------------------------------------- /site/static/md/tests/on_signal_change_path_once.md: -------------------------------------------------------------------------------- 1 | # On Signal Change Path Once 2 | 3 | Tests detecting a signal change with a path, and that the expression is called once. 4 | 5 |
6 | Result: 7 | 8 |
9 | Expected result on click: 1 10 |
-------------------------------------------------------------------------------- /site/static/md/tests/on_signal_change_path_wildcard.md: -------------------------------------------------------------------------------- 1 | # On Signal Change Path Wildcard 2 | 3 | Tests detecting a signal change with a path using a wildcard. 4 | 5 |
6 | Result: 7 | 8 |
9 | Expected result on click: 1 10 |
-------------------------------------------------------------------------------- /site/static/md/tests/persist_signals.md: -------------------------------------------------------------------------------- 1 | # Persist Signals 2 | 3 | Tests persisting signals. 4 | 5 |
6 | Expected value in local storage (in alphabetical order): 7 |
datastar: {"bar":1,"baz:1","foo":1}
8 |
-------------------------------------------------------------------------------- /site/static/md/tests/persist_signals_path.md: -------------------------------------------------------------------------------- 1 | # Persist Signals Path 2 | 3 | Tests persisting signals with a path. 4 | 5 |
6 | Expected value in local storage (in alphabetical order): 7 |
datastar: {"bar":1,"foo":1}
8 |
-------------------------------------------------------------------------------- /site/static/md/tests/persist_signals_path_wildcard.md: -------------------------------------------------------------------------------- 1 | # Persist Signals Wildcard 2 | 3 | Tests persisting signals with a path using a wildcard. 4 | 5 |
6 | Expected value in local storage (in alphabetical order): 7 |
datastar: {"foo":{"bar":{"baz":1}}}
8 |
-------------------------------------------------------------------------------- /site/static/md/tests/plugin_name_prefix.md: -------------------------------------------------------------------------------- 1 | # Plugin Name Prefix 2 | 3 | Tests that data attributes with plugin names in their prefix are not processed. In this test, `data-classes` should be ignored. 4 | 5 |
6 | Result: 7 | 0 8 |
9 | Expected result on load: 1 10 |
-------------------------------------------------------------------------------- /site/static/md/tests/radio_value.md: -------------------------------------------------------------------------------- 1 | # Radio Value 2 | 3 | Tests that a radio input's bound signal is assigned to the value when checked. 4 | 5 |
6 | foo 7 |
8 | bar 9 |
10 | Result: 11 | 12 |
13 | Expected result on click: 1 14 |
-------------------------------------------------------------------------------- /site/static/md/tests/ref.md: -------------------------------------------------------------------------------- 1 | # Ref 2 | 3 | Tests that `data-ref` is correctly applied. 4 | 5 |
6 | Result: 7 | 0 8 |
9 | Expected result on load: 1 10 |
-------------------------------------------------------------------------------- /site/static/md/tests/remove_fragment.md: -------------------------------------------------------------------------------- 1 | # Remove Fragment 2 | 3 | Tests removing a fragment. 4 | 5 |
6 | 7 |
8 | Result: 9 | 0--1 10 |
11 | Expected result on click: 1 12 |
-------------------------------------------------------------------------------- /site/static/md/tests/remove_initiating_fragment.md: -------------------------------------------------------------------------------- 1 | # Remove Initiating Fragment 2 | 3 | Tests removing the fragment that initiated the request. 4 | 5 |
6 | 7 |
8 | Result: 9 | 0 10 |
11 | Expected result on click: 1 12 |
-------------------------------------------------------------------------------- /site/static/md/tests/select_multiple.md: -------------------------------------------------------------------------------- 1 | # Select Multiple 2 | 3 | Tests that a multiple select element's bound signal is assigned to the selected option values. 4 | 5 |
6 | 7 |

 8 |   
9 | Result: 10 | 11 |
12 | Expected result on selecting `foo` and `bar`: 1 13 |
-------------------------------------------------------------------------------- /site/static/md/tests/select_single.md: -------------------------------------------------------------------------------- 1 | # Select Single 2 | 3 | Tests that a single select element's bound signal is assigned to the selected option value. 4 | 5 |
6 | 7 |
8 | Result: 9 | 10 |
11 | Expected result on selecting `bar`: 1 12 |
-------------------------------------------------------------------------------- /site/static/md/tests/set_all_path.md: -------------------------------------------------------------------------------- 1 | # Set All Path 2 | 3 | Tests the set all action on a single path. 4 | 5 |
6 | Result: 7 | 8 |
9 | Expected result on load: 1 10 |
-------------------------------------------------------------------------------- /site/static/md/tests/set_all_path_wildcard.md: -------------------------------------------------------------------------------- 1 | # Set All Path Wildcard 2 | 3 | Tests the set all action on a path using a wildcard. 4 | 5 |
6 | Result: 7 | 8 |
9 | Expected result on load: 1 10 |
-------------------------------------------------------------------------------- /site/static/md/tests/set_all_paths.md: -------------------------------------------------------------------------------- 1 | # Set All Path 2 | 3 | Tests the set all action on multiple paths. 4 | 5 |
6 | Result: 7 | 8 |
9 | Expected result on load: 1 10 |
-------------------------------------------------------------------------------- /site/static/md/tests/sse_error_event.md: -------------------------------------------------------------------------------- 1 | # SSE Error Event 2 | 3 | Tests that an SSE error event is dispatched. 4 | 5 |
7 |

 8 |   
9 | Result: 10 | 11 |
12 | Expected result on load: 1 13 |
-------------------------------------------------------------------------------- /site/static/md/tests/sse_events.md: -------------------------------------------------------------------------------- 1 | # SSE Events 2 | 3 | Tests that SSE events are dispatched. 4 | 5 |
6 |

 7 |   
8 | Result: 9 | 10 |
11 | Expected result on load: 1 12 |
-------------------------------------------------------------------------------- /site/static/md/tests/toggle_all_path.md: -------------------------------------------------------------------------------- 1 | # Toggle All Path 2 | 3 | Tests the toggle all action on a single path. 4 | 5 |
6 | Result: 7 | 8 |
9 | Expected result on load: 1 10 |
-------------------------------------------------------------------------------- /tools/intellij-plugin/.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Dependabot configuration: 2 | # https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/configuration-options-for-dependency-updates 3 | 4 | version: 2 5 | updates: 6 | # Maintain dependencies for Gradle dependencies 7 | - package-ecosystem: "gradle" 8 | directory: "/" 9 | schedule: 10 | interval: "daily" 11 | # Maintain dependencies for GitHub Actions 12 | - package-ecosystem: "github-actions" 13 | directory: "/" 14 | schedule: 15 | interval: "daily" 16 | -------------------------------------------------------------------------------- /tools/intellij-plugin/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | .idea 3 | .intellijPlatform 4 | .qodana 5 | build 6 | -------------------------------------------------------------------------------- /tools/intellij-plugin/gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | # libraries 3 | junit = "4.13.2" 4 | 5 | # plugins 6 | changelog = "2.2.1" 7 | intelliJPlatform = "2.1.0" 8 | kotlin = "1.9.25" 9 | kover = "0.8.3" 10 | qodana = "2024.2.3" 11 | 12 | [libraries] 13 | junit = { group = "junit", name = "junit", version.ref = "junit" } 14 | 15 | [plugins] 16 | changelog = { id = "org.jetbrains.changelog", version.ref = "changelog" } 17 | intelliJPlatform = { id = "org.jetbrains.intellij.platform", version.ref = "intelliJPlatform" } 18 | kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } 19 | kover = { id = "org.jetbrains.kotlinx.kover", version.ref = "kover" } 20 | qodana = { id = "org.jetbrains.qodana", version.ref = "qodana" } 21 | -------------------------------------------------------------------------------- /tools/intellij-plugin/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starfederation/datastar/03164b47af392a8e25275e14c46ce2a9a1935a37/tools/intellij-plugin/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /tools/intellij-plugin/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /tools/intellij-plugin/qodana.yml: -------------------------------------------------------------------------------- 1 | # Qodana configuration: 2 | # https://www.jetbrains.com/help/qodana/qodana-yaml.html 3 | 4 | version: 1.0 5 | linter: jetbrains/qodana-jvm-community:2024.2 6 | projectJDK: "17" 7 | profile: 8 | name: qodana.recommended 9 | exclude: 10 | - name: All 11 | paths: 12 | - .qodana 13 | -------------------------------------------------------------------------------- /tools/intellij-plugin/resources/img/datastar-intellij-plugin.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:f54ed26a49e3b8821cfc51cba79f8932048bf45682ff3f6b5fa4e2e1346923ee 3 | size 293668 4 | -------------------------------------------------------------------------------- /tools/intellij-plugin/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" 3 | } 4 | 5 | rootProject.name = "datastar-jetbrains-plugin" 6 | -------------------------------------------------------------------------------- /tools/intellij-plugin/src/main/resources/META-INF/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | com.github.starfederation.datastarjetbrainsplugin 4 | Datastar support 5 | starfederation 6 | com.intellij.modules.platform 7 | JavaScript 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /tools/intellij-plugin/src/main/resources/datastar-icon.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:b8ff04b4c8a902b8e895cc65dd25042b9eca2eeed0ccebccff145c70c57c6f26 3 | size 1927 4 | -------------------------------------------------------------------------------- /tools/intellij-plugin/src/main/resources/messages/MyBundle.properties: -------------------------------------------------------------------------------- 1 | projectService=Project service: {0} 2 | randomLabel=The random number is: {0} 3 | shuffle=Shuffle 4 | -------------------------------------------------------------------------------- /tools/intellij-plugin/test.css: -------------------------------------------------------------------------------- 1 | .orange-text { 2 | color: orange; 3 | font-weight: bold; 4 | } 5 | 6 | .blue-text { 7 | color: blue; 8 | font-weight: bold; 9 | } -------------------------------------------------------------------------------- /tools/intellij-plugin/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /tools/vscode-extension/.gitignore: -------------------------------------------------------------------------------- 1 | dist -------------------------------------------------------------------------------- /tools/vscode-extension/README.md: -------------------------------------------------------------------------------- 1 | # Datastar Extension for Visual Studio Code 2 | 3 | Adds autocomplete for [Datastar](https://data-star.dev/) to Visual Studio Code. 4 | 5 | ![screenshot-Zi09CjE7@2x](https://github.com/user-attachments/assets/5082d177-46d2-4683-b7ed-f68749c12c7b) 6 | 7 | ## License 8 | 9 | This plugin is licensed for free under the MIT License. 10 | 11 | ## Requirements 12 | 13 | This plugin requires Visual Studio Code version 1.63.0 or later. 14 | 15 | ## Installation 16 | 17 | Install the extension from the Visual Studio Code Marketplace or from the extensions panel by searching for “Datastar”. -------------------------------------------------------------------------------- /tools/vscode-extension/src/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starfederation/datastar/03164b47af392a8e25275e14c46ce2a9a1935a37/tools/vscode-extension/src/icon.png --------------------------------------------------------------------------------