also bump version to 0.1.2
- add makefile with all commands for rust sdk developers
to run all relevant checks and test,
which also auto formatting of code and toml deps
- fixed broken clippy rules
(this also required fixing some stuff in consts)
Also fixed the docs.rs attributes to ensure _all_
modules and symbols get published in docs,
instead of only those that are available in default features
(which was missing the framework specific stuff in docs)
Only nitpick issue left: in generated code (consts.rs)
some descriptions can contain 'html' code which is not escaped,
pretty certain this html will be rendered in docs.rs
as rust docs renders docs as markdown (which includes html),
but given that's auto generated I have left that for now as-is.
ready for publish
- changelogs per sdk's is not done it seems,
so I did not add that
Once this is merged we should be able to run
> cargo publish
and all should be ok
The current link is a link to a channel in a server which only works if you are already in the discord. This PR Replaces it with the invite link <https://discord.com/invite/bnRNgZjgPh> which I found on the datastar website: <https://data-star.dev/>
The merge fragments call configuration struct had an EventID field and
logic that added it to the SSE call, but no option to configure it.
Added that missing option.
* externalize interface compatibility for two optional template engines
Templ and GoStar are currently module dependencies for Golang SDK. They
should not be required for 1.0 release. This step replaces two component
interfaces with copies that ensure compatibility without having to
include various optional engines as dependencies with each Datastar
deployment.
* update comments and documentation for Golang SDK
Added comments to `execute.go` file. I will make similar changes to
other files. I submit those as a way of getting feedback.
The only public API change is the removal of execute script options
struct. It is changed to private by letter case because the options
pattern is used to configure script execution. The struct serves no
purpose and pollutes the public API.
* add hot reload example to Golang SDK
Updated the Golang SDK README.md file to include a list of examples that
will be expanded in the future. A basic example is moved into its own
directory. A hot reload example is added alongside it.
* expand Golang SDK documentation by adding comments to every method
Added a comment to every public method. Removed inaccessible options
structs from public API by letter case change. Exposed one hidden option
to public API. Refactored fragment merge mode parsing and added a
matching validation test.
* reinstate ValidFragmentMergeTypes because the website documentation depends on it
* externalize interface compatibility for two optional template engines
Templ and GoStar are currently module dependencies for Golang SDK. They
should not be required for 1.0 release. This step replaces two component
interfaces with copies that ensure compatibility without having to
include various optional engines as dependencies with each Datastar
deployment.
* update comments and documentation for Golang SDK
Added comments to `execute.go` file. I will make similar changes to
other files. I submit those as a way of getting feedback.
The only public API change is the removal of execute script options
struct. It is changed to private by letter case because the options
pattern is used to configure script execution. The struct serves no
purpose and pollutes the public API.
* add hot reload example to Golang SDK
Updated the Golang SDK README.md file to include a list of examples that
will be expanded in the future. A basic example is moved into its own
directory. A hot reload example is added alongside it.
* update to go 1.24
* run go mod tidy
* make qtc a go tool
* remove tools installation from dockerfile dev
since it is now taken care of by the go tool command
* update makefile to use go tool task
* externalize interface compatibility for two optional template engines
Templ and GoStar are currently module dependencies for Golang SDK. They
should not be required for 1.0 release. This step replaces two component
interfaces with copies that ensure compatibility without having to
include various optional engines as dependencies with each Datastar
deployment.
* update comments and documentation for Golang SDK
Added comments to `execute.go` file. I will make similar changes to
other files. I submit those as a way of getting feedback.
The only public API change is the removal of execute script options
struct. It is changed to private by letter case because the options
pattern is used to configure script execution. The struct serves no
purpose and pollutes the public API.
* go get go@latest
* go get -tool github.com/go-task/task/v3/cmd/task@latest
* go get -tool github.com/a-h/templ/cmd/templ@latest
* upd task file to use go tool templ
Templ and GoStar are currently module dependencies for Golang SDK. They
should not be required for 1.0 release. This step replaces two component
interfaces with copies that ensure compatibility without having to
include various optional engines as dependencies with each Datastar
deployment.
* add compression
make compression work
update compression logic
* made compression configurable
* allow for compression strategy
cleanup api
---------
Co-authored-by: Delaney Gillilan <delaneygillilan@gmail.com>
* Allow passing arguments to the generator function in fastapi sdk
* Allow passing extra headers in FastAPI streaming response
* Keep method signature of upstream FastAPI streaming response
* Split out python sdk frameworks into their own modules
Make all python sdk frameworks mirror the native way of streaming responses
* Rename the sanic helper in python sdk to match sanic convention.
Add some type hinting to sanic helper
* ruff format new python sdk files
* fix: support connection header over WSGI
Per spec, WSGI doesn't allow the connection header:
https://peps.python.org/pep-0333/#other-http-features
This commit works around that limitation and permits the connection
header over a WSGI connection.
* feat: add WSGI views/urls to Django example
* build(py-sdk): add django/datastar_py/daphne pkgs
Other related changes:
* Added "daphne" to INSTALLED_APPS to provide us with an ASGI runserver.
* Changed a setting to make ASGI the default.
* docs: add a README to the example Django project
The docs explain why Django devs should use ASGI and explain how to use
WSGI if needed.
* Clarify that data-computed is a pure function
Also make explicit that `data-on-signals-change` is the alternative for
side effects.
* Update attribute_plugins.md
---------
Co-authored-by: Ben Croker <57572400+bencroker@users.noreply.github.com>
* Emphasize two of the most important facts about d* attributes.
* Expand the attribute casing documentation
The previous top-line message "data-* attributes are case-insensitive", while
true, was very confusing for new users because it is not immedietly clear that
sentence is referring to the HTML spec and not the library of the docs they are reading.
So I've clarified the top-line message, and then expanded the explanation in a
way I hope makes it easier to understand the first time.
* Explain how to use modifiers with `data-signals`
(the object notation variant)
* Clarify it is possible to set both data-signals__ifmissing= and data-signals on the same element
* Update attribute_plugins.md
* Update attribute_plugins.md
---------
Co-authored-by: Ben Croker <57572400+bencroker@users.noreply.github.com>
* Improve the datastar expression guide
After chatting with Anders, I got a list of "things to know" about D* expressions.
This commit expands upon the current docs, adding more facts, examples and some structure.
* Remove redundant heading
* Document usage of common js operators
* Update datastar_expressions.md
---------
Co-authored-by: Ben Croker <57572400+bencroker@users.noreply.github.com>
* Rename event and dispatch on element
* Add release note
* Upate docs
* Fix test
* Make events bubble up
* Default to kebab-case
* Kebab
* Fix docs
* Default to kebab-case for `data-class-*`
* Fix missing argument
* Finalise docs
* Feature: gzip
Docs: changelog, removed todos
Feature: locking macro for sse generators.
Feature: flowstorm setup
Refactor: the SSE event writing code is now generic
Chore: bumped http-kit version
Feature: new malli schemas + fixes
Docs: adding the how to load more list items doc
* Fix: forgotten closing call
* Docs: fixed text
* Refactor: using write profiles to configure compression and buffering
Docs: modified the docs to reflect the new write profile mechanism
Refactor: changed the malli schema to go with the write profiles
* Feature: better error handling
* Docs: fixed unbalanced brackets / typos
* Docs: fixed example
* Docs: better write profiles docs
* Refactor: consistent namespaced keyword for all option
`:on-open` and `:on-close` options are deprecated
Docs: all callbacks are documented in docstrings
Calling context, return values and exception behavior are documented
* Refactor: using the new callback name everywhere
* Docs: Readme is redone and more docstrings
* fix(python sdk): fasthtml simple example import from responses
* fasthtml examples: URLs use latest datastar version
Replace versioned CDN URLs with unversioned ones in FastHTML examples to
automatically use the latest Datastar bundle.
* fix(python examples): fasthtml use repo datastar bundles
---------
Co-authored-by: Lucian Knock <git@lucianknock.com>
* sdk/dotnet-v1 - simplified API surface with static members; fixed a bug with remove signals; fix to caching when retrieving signals mutiple times; removed Datastar prefix from filenames
* sdk/dotnet-v1-beta.4
---------
Co-authored-by: Greg Holden <SpiralOSS@users.noreply.github.com>
* Restore elUniqId, removeOnLoad
* Store cleanup function only if not removed
* Restore elUniqId (again)
* Fix empty value being replaced
* Rehash the cleanup functions
* Cleanup
* Fix persist example
* Use el.id
* Remove `removeOnLoad`
* Restore check for existing mutationObserver
* Move
* Add release notes
* Improve tests
* Bring typescript sdk in line with sdk api's
But still allow passing of stricter types when it makes sense
* fix bug introduced in https://github.com/starfederation/datastar/pull/698
we want the stream to be ended only if keepalive is not present
* Remove close method from typescript sdk and improve comments
The stream is closed automatically after the callback runs.
The only situation where we want to use keepalive is if we
want an infinite stream, in which case we dont want to close it.
* improve package.json for typescript sdk
* Use consts VERSION in typescript sdk tests
* Improve method comments in typescript sdk
this sdk is also used in javascript where there's less type information
* normalize output and test output and then compare
* separate ordered data lines from unordered data lines + only remove space after colon if its after a key at the beginning of the line
* don't remove space after colon
* only sort unordered fields
* update readme
* add gitignore to sdk/.test folder
* Replace legacy call to `applyAttributePlugin`
Fixes#699
* Ignore when alias prefix missing
* Remove merge fragment callbacks
* add bundler support
---------
Co-authored-by: Ben Croker <ben@putyourlightson.com>
* improvements to node sdk
Add abort handling to node sdk, make onError simpler and close stream by default
- closes https://github.com/starfederation/datastar/pull/667
- closes https://github.com/starfederation/datastar/issues/602
* Improvements to typescript sdk readme
Ben Croker wanted a mergeSignals example
* Make typescript sdk pass tests
* add missing typescript snippets and use same name as sdk folder
* update datastar version in typescript sdk test servers
* Update to 0.7.2
* Fix indicator and remove auto ID generation
* Setup
* Data-* attributes hash their contents
* Update release notes
* Revert star.ts and remove test
---------
Co-authored-by: Delaney Gillilan <delaneygillilan@gmail.com>
* Clojure sdk: Fixes, new website snippets
Docs: corrected the readme's example
Fix: changed return value in the jetty implementation
Chore: Changed dependency coordinate of http-kit to a newer maven dependency
Fix: using setTimeout in the redirect sugar function
Fix: mistake in http-kit management in examples
Docs: redirect example
Feature: added another testing utility
Docs: added missing snippets for the Datastar website
* Fix: typo
* sdk/dotnet - reduced abstraction. SSE header response moved out of constructor
* sdk/dotnet - removed support for .NET 6 and 7
* sdk/dotnet - IReadSignals.ReadSignals<T> will accept JsonSerializerOptions
* sdk/dotnet - bumped the version to 1.0.0-beta.3
---------
Co-authored-by: Greg Holden <SpiralOSS@users.noreply.github.com>
Link is 404 as all other languages live in /examples, while TS examples are in its own SDK folder. They should probably be moved with the others, but for now, at least links should point to where they are available currently.
* Add necessary dev-dependencies for testing
* Update some crate metadata
* Replace all implementations of `Into` with `From`
* Replace `write!` with `writeln!` when applicable
* Replace `futures` crate with slightly more minimal `futures_util` crate
* Clarify uses of `Default::default()`
* sdk/dotnet-updates - added ability to append custom headers to sse response
* sdk/dotnet-updates - updated README with Model Binding examples
* sdk/dotnet-updates - nuget package version should reflect properly. no release made in github
---------
Co-authored-by: Greg Holden <SpiralOSS@users.noreply.github.com>
* ready for pull request
* I had the consts_haskell.qtpl in the wrong place
* made changes requested by Ben
* made changes requested by Ben
* fixed license and version in cabal
* more changes per Ben
* renamed dstar.cabal to datastar.cabal
* Update LICENSE
* Rename LICENSE to LICENSE.md
* Update datastar.cabal
---------
Co-authored-by: Ben Croker <57572400+bencroker@users.noreply.github.com>
* Support Rails' #render_in(view_context) interface
So render arbitrary objects, including component libs like ViewComponent and Phlex Rails.
* Render anything that supports #to_s, including Rails' safe buffers
* Make sure Rails view_context is an ActionView::Base
* Document
* Add optional key to `data-on-signals-change`
Fixes#587
* support files
* Listen for changes to signal path
---------
Co-authored-by: Ben Croker <ben@putyourlightson.com>
* Add example to demonstrate
* upgrade to latest idiomorph
* Remove test file
* fix possible removal on apply
---------
Co-authored-by: Delaney Gillilan <delaneygillilan@gmail.com>
* WiP initial setup, ServerSentEventGenerator class
* WiP working merge_fragments in Rails
* #merge_fragments and #merge_signals
* Handle SSE vs Data* options
* Test that #merge_fragments works with a #call(view_context:) interface
* Test Dispatcher#stream
* #remove_fragments
* #remove_signals
* #execute_script
* execute_script with attributes Hash
* Connection: keep-alive
* Use 2 line-breaks as message end, plus last line's 1 line break (3 total)
* Connection callbacks. #on_connect, #on_disconnect, #on_error
* Dispatcher#signals
* Omit retry if using default value (1000)
* Omit defaults
* Multiline scripts
* Test Rack endpoint
* Document test Rack endpoint
* Add missing defaults
* Spawn multiple streams in threads, client_disconnect and server_disconnect handlers
* Move ThreadSpawner to configuration
* Configure a RailsThreadSpawner when Rails detected
* Move Railtie one dir up
* Global error callback
Datastar.config.on_error { |err| Sentry.notify(err) }
* Catch exception from stream threads in main thread
* Linearlize exception handling
* Refactor dispatcher to handle single stream in main thread, multi streams in separate threads
* spawner => executor. Rails Async executor using fibers.
* Support Async for fiber-based concurrency
* Finalize response for Rack and Rails
* test Rack app
* Threaded demo
* Test Dispatcher#sse?
Also do not check for SSE Accept on stream.
Leave it up to the user.
* Do not check Accept header in test app. Test scripts don't send it properly.
* Document code
* Example progress bar Rack app
* README
* Link to D* SSE docs
* See examples
* Document callbacks
* List Ruby SDK in SDKs.md
* Ruby struct in consts.go
* Document running tasks with arguments via Docker
* Code-gen Ruby constants from shared data via template
* Make test rely on constants
* Datastar.from_rack_env(env) => Datastar::Dispatcher
* Ruby example snippets
* #redirect(location)
* Ruby snippet using #redirect(new_path)
* Add X-Accel-Buffering: no header
To disable response buffering by NGinx and other proxies.
* Clarify linearisation of updates in Readme
* Tidy-up progress example
* Move examples to /examples/ruby
* Document Rails and Phlex
* Version 1.0.0.beta.1
* Version 1.0.0.beta.1
* Do not set Connection header if not HTTP/1.1
* Don't touch BUILDING.md docs in this PR
* Remove Changelog for now
* Sort Ruby alphabetically (just "ruby", not the entire line)
* Add hello world example, remove progress bar one.
* Add hello-world example to code-gen
* Typos
* update zig to work on master
* Updated deps, fixed to use tests.zig as root, add datastar as module, refactoring
* Update test_runner.zig. Move tests to root.zig.
* tokamak: use post0 when no body
Datastar responses are also valid SSE responses, and the D* client already sends `Accept: text/event-stream` to the server, so tests should replicate that.
Some servers might rely on this for content negotiation.
Co-authored-by: Ismael Celis <ismaelct@gmail.com>
* sdk/typescript add support for ResponseInit in web stream method
This allows setting custom headers
* sdk/ts elide datalines with default values
* sdk/ts run deno fmt
* sdk/typescript deno lint
* sdk/typescript better error handling for streams
* sdk/typescript improve deno request abortion support and async completion
* sdk/typescript fix type error in error usage
* Refactor: Ring jetty adapter is now a generic ring adapter
Minor changes:
- Properly eliding SSE retry duration
- Added a small utility in the main api.
- Added a new option to the ->sse-response functions to support
modifying the HTTP response status.
- Fixed typos and errors in the docs.
- using consts version number.
- Prepared tests for rj9a
- Properly killing webrivers when shutting down tests
* Docs: fixing the fixed typo
* sdk/dotnet-nuget_version - updated the nuget version to 1.0.0-beta.3
* sdk/dotnet-nuget_version - updated the nuget version to 1.0.0-beta.1
---------
Co-authored-by: Greg Holden <SpiralOSS@users.noreply.github.com>
* sdk/dotnet - more renaming, doco, and code shortening; caching of signals
* sdk/dotnet - added model binding
* sdk/dotnet-model_binding - FromSignals behaves more like other From* attributes
---------
Co-authored-by: Greg Holden <SpiralOSS@users.noreply.github.com>
* add description to the markdownRenders func
* add /api/search using bleve
* wrap search functionality in a basic feature flag
* remove some comments that are not needed
* add desktop styling for search
* undo add feature flag
* mobile styling
* fix: merge correct fragment
* fix: menu item being cut off on mobile
* Add keyboard shortcut to add focus
* fix: loading indicator jank
* Styling tweaks
* remove un-neccessary ids
* Update site.css
---------
Co-authored-by: Ben Croker <57572400+bencroker@users.noreply.github.com>
* Add polling
* Rename file
* Add `how_to_redirect_the_page_from_the_backend`
* Add Hello World example guide
* Add to nav, start on polling
* Remove `.leading` from code snippets
* Fix typo
* How to bind keydown events to specific keys
* How to redirect from the backend (#565)
* Add how tos with demos
* Fixes
* Fixes
* Add snippets
* Rename to location in snippet
* Wording
* Tweaks
* Prep for merging
* Improve wording
---------
Co-authored-by: Dominik Tarnowski <dom@tdom.dev>
* Revamp apply and cleanup to be based primarily on MutationObserver
* Improve `data-on` timing functions
Fixes#519
* inline cast
* Update library/src/engine/engine.ts
Co-authored-by: Thomas Threadgold <2460166+Regaez@users.noreply.github.com>
* cleanup and merge with develop
* Add timing example
* remove apply
---------
Co-authored-by: Thomas Threadgold <2460166+Regaez@users.noreply.github.com>
Co-authored-by: Ben Croker <ben@putyourlightson.com>
* Feature: added clojure sdk
Chore: rebasing on latest
(last commit from clojure-sdk d6259824908c)
* Fix: alphabetical order
* Docs: Actual broadcast example
* Test: adding shell test support
Fix: order of options in data-lines for execute scripts events
Fix: fixed signal handling / missing options
Refactor: using the generated constants for boolean options
* Test: passing shell tests
Chore: task build
* sdk/test improving formatting and handle defaults better
The test suite was not handling defaults in the right way, they're supposed
to be elided. Add new tests to check that the response is correct in cases
where all defaults are provided and cases where they are not.
Also formats the input's with jq(1) since it got too complicated to edit.
* sdk/test remove merge artefacts
* sdk/test fixed the last merge artefact
* fix scroll bar appearing
* use explicit height and width for img
* add `cursor-pointer` to button and darken text on hover
* remove unnecessary classes and do gradient in oklch
* text-gray-100
* Update consts_hello_world.qtpl
---------
Co-authored-by: Ben Croker <57572400+bencroker@users.noreply.github.com>
* fix bfcache causing data-bind signals to be out of sync
* changelog, comments, fix build
* Update CHANGELOG.md
---------
Co-authored-by: Ben Croker <57572400+bencroker@users.noreply.github.com>
* Fix recurring back through a ancestors to find an id to hash
When we recur through ancestors of a node to generate an id we were
skipping back two parent nodes instead of one on the most common
branch in the recursion.
This would mean html structure like this would not produce unique ids:
```
...
<div id="3">
<div>
<div>
<div id="datastar1pyvig">hello</div>
<div id="datastargves4o">hello</div>
</div>
</div>
</div>
<div id="4">
<div>
<div>
<div id="datastar1pyvig">hello</div>
<div id="datastargves4o">hello</div>
</div>
</div>
</div>
...
```
Where as html structure like this would (even layer of nesting):
```
...
<div id="1">
<div>
<div>
<div>
<div id="datastar1d">hello</div>
<div id="datastarsx">hello</div>
</div>
</div>
</div>
</div>
<div id="2">
<div>
<div>
<div>
<div id="datastar1t">hello</div>
<div id="datastarsr">hello</div>
</div>
</div>
</div>
</div>
...
```
Ideally we want to find any unique user generated id when recurring
back through ancestors to avoid duplicate ids. Which looks like the
original intention of this code (as that way users don't have to
manually give ids to inner elements like buttons).
* Add release notes
* Fix order of release notes
* IDs
---------
Co-authored-by: Ben Croker <57572400+bencroker@users.noreply.github.com>
* Add release note
* Add docs
* Add default value and trim
* Add multiline suggestion to error
* Join using semicolon only
* Revert removing line break
* Improve code comments
* Fix typo
* Fix
* Use button element
* Build
* rust sdk
* add settle duration an use view transition to `RemoveFragments`
* replace `std::time::Duration` with `core::time::Duration`
* add `settle_duration` and `use_view_transition` setters to `RemoveFragments`
* remove `workspace` from `Cargo.toml`
* update `Cargo.lock`
* align documentation with official documentation
* change description
* use consts
* fix typo in `consts.go`
* code snippets
* add semi colons to bool consts
* fix example
* always include `retry_duration` in event body
* fix typo
* add extra newline to every event
* remove unnecessary `.as_ref()`
* remove `datastar-request: true` validation from `ReadSignals`
* add retry and extra newline to doc tests
* add tests and update package files
This means we don't need to write a JS parser in regex and keeps
things simple.
I haven't updated the docs to mention this or added specific error
handling. Figured that could be done in a separate PR.
This PR does fix all the tests where \n was used as a statement delimiter
* site sdk/typescript bring snippets into line with other examples
* sdk/typescript remove mention of readSignals in getting started snippet
---------
Co-authored-by: Ben Croker <57572400+bencroker@users.noreply.github.com>
Adds `;` back to `stmts.join(';\n')` as sometimes javascript really
needs a `;`.
The following expression would cause an error due to the leading/trailing `;`
being removed.
```
:data-on-click "var somthing = 1 + 3
;(5) + 4;
@post('/send'); $message = ''"
```
This code would cause an error too:
```
:data-on-click "var somthing = 1 + 3;
(5) + 4;
@post('/send'); $message = ''"
```
By making sure we always add a semicolon we ensure this can't happen.
The `\n` has been kept for error message readability.
* Support nested regex and strings in expressions
This regex allows Datastar expressions to support nested
regex and strings that contain ; and/or \n without breaking.
Full regex for testing on regex101.com
```
((?:\/(?:\\\/|[^\/])*\/|"(?:\\"|[^\"])*"|'(?:\\'|[^'])*'|`(?:\\`|[^`])*`|[^;\n])*)([;\n]+)
```
Only valid ; and \n should be in the second capture group Each of these regex ignore a block type:
```
regex \/(?:\\\/|[^\/])*\/
double quotes "(?:\\"|[^\"])*"
single quotes '(?:\\'|[^'])*'
ticks `(?:\\`|[^`])*`
```
We want to ignore the non delimiter part of statements too:
`[^;\n]`
Once all the blocks we want to ignore are captured we then find
the statement delimiters in the second capture group:
`([;\n]+)`
Note: the reason why this regex isn't broken down into strings is that
doing so adds an extra layer of escapes which makes it even harder to
reason about, harder to check in external regex apps and leads to
worse compression.
The ignore block model allows us to extend this and validate each
component separately.
Effectively, an ignore block is a regex that skips the content of the
block. For example we want to skip the contents of a `''` block then
we need to match the open quote and the close quote and ignore the
contents including escaped quotes.
That's the theory anyway. You can go mad testing this, just remember
the it needs to be valid JS! A few times I thought I'd broken
it and it would have broken the JS parser.
I don't expect this to be perfect, but I do expect it to be easy to
fix any edge cases we find by design (what I'm aiming for with this).
There might be a combination of nested strings that break this.
I've run the test (sort is still failing but it was before) and
I've built and tested the todo app.
* Simplify regex
* Fix typo
* Support nested regex and strings in expressions #508
/(?:\/(?:\\\/|[^\/])*\/|"(?:\\"|[^\"])*"|'(?:\\'|[^'])*'|`(?:\\`|[^`])*`|[^;\n])+/gm
This regex allows Datastar expressions to support nested
regex and strings that contain ; and/or \n without breaking.
Each of these regex defines a block type we want to capture:
regex \/(?:\\\/|[^\/])*\/
double quotes "(?:\\"|[^\"])*"
single quotes '(?:\\'|[^'])*'
ticks `(?:\\`|[^`])*`
We want to capture the non delimiter part of statements too:
[^;\n]
The regex above will not work in regex101.com as javascript
regex handles single and double quotes for us.
The test cases bellow can be pasted into the developer console to
check the regex.
Note we need the `trim`, before we `match`.
```
var statementRe = /(?:\/(?:\\\/|[^\/])*\/|"(?:\\"|[^\"])*"|'(?:\\'|[^'])*'|`(?:\\`|[^`])*`|[^;\n])+/gm
var testStringSingle = '["foo", "\n", \';\', `"fo ; \' \\\' \\" bam"`]map((x) => x.match(/regex;/); "goat;" + y'
testStringSingle.match(statementRe)
var testStringDouble = "['foo', \"\\n\", ';',`'fo ; \" \\\" \\' bam'`]map((x) => x.match(/regex;/)); 'goat;' + y"
testStringDouble.match(statementRe)
// trailing space after a colon breaks this so we need to add trim
var testStringDoubleTrailingSpace = "['foo', \"\\n\", ';',`'fo ; \" \\\" \\' bam'`]map((x) => x.match(/regex;/)); 'goat;' + y; "
testStringDoubleTrailingSpace.match(statementRe)
testStringDoubleTrailingSpace.trim().match(statementRe)
```
p
* build the bundle
* Removed the ignore capture groups as they are not needed with match
* Use newline as separator for more readable error messages
---------
Co-authored-by: Delaney Gillilan <delaneygillilan@gmail.com>
From the conversation on discord `replace` can be used to simplify
making the last statement in an expression a return.
Note:
- This version doesn't do any trimming.
- This does not fix#497
Ran tests and build step locally, but wasn't sure if PRs
should only be the code change (rather than artefacts).
* sdk/ts add warning about http1
* sdk/ts improve readme example
and add snippets to go site
* sdk/ts fix type and import errors
* ts/sdk fix type error in node readme example
* sdk/ts improve snippets and examples
Slimmed down the snippets setup example and added it to the readme, then
moved the readme examples to their own files in a new examples folder to
make place for other examples.
Also fixed md links in readme.
* sdk/ts --unstable-sloppy-imports is no longer required
* sdk/ts fixed some type / import errors
* Rename sdk/ts to sdk/typescript
* sdk/typescript simplify setup snippet
* Ts sdk
Does not contain node code, working on that now. But all tests pass.
* Initial concrete impl of ts serverSentEventGenerator
This can do cross-runtime parsing of signals, but I havent figured out
yet how to do the streaming cross-runtime, the current code is for node only.
I will probably figure this out tomorrow.
* ts-sdk: make the sdk cross-runtime
this should support node, deno and most other runtime.
* Create concrete serverSentEventGenerator for node and web standard runtimes
They both get their own version of the class. Also move building from vite to esbuild.
Unit tests are still in vitest though. You can also run `node bundle/node/node.js` or
`deno run --allow-net --unstable-sloppy-imports sdk/ts/src/web/deno.ts` to test
them manually. Automatic curl based tests for these two are my next focus.
* new sh based test suite for sdk
along with changes to sdk/ts after testing.
some tests are still failing
node: reading post signals
deno: all get-cases (some problem with the deno buffering I think)
readme changes will come in a later commit
* Improve sdk/test/test-all.sh
following suggestions by King of Mods on discord
https://discord.com/channels/1296224603642925098/1299058221390102528/1325511650543013888
* sdk/test make get and post tests less noisy
* sdk/ts fix deno streaming
I wasnt closing the stream properly
* sdk/ts Fix parsing of json body in node sdk
using code from https://github.com/starfederation/datastar/pull/391 by https://github.com/jmcudd
* sdk/ts add jsdocs to public methods
* sdk/test Readme
* sdk/ts Readme
* sdk/ts fix error in controller code for web stream
* sdk/ts fix error with async handling in web standard sse
* sdk/ts fix same async problems with node
also remove the need to add res.end() after calling stream
* sdk/ts remove unit tests and vite
The sdk/test suite does a much better job of testing the sdk.
Sure we lose the fuzzing, but we can always add that into sdk/test
* sdk/ts improve typings and async usage
Most of the `unkwown` objects are not wholly unknown, we know that
they can be de/serialized from/to JSON.
Make non async streams easier to use.
* sdk/ts improve node readSignals method
less code duplication
* sdk/ts build the npm package with deno dnt
It's hard to write a library that typechecks and run for
both deno and node. The chief reason is that node esm
does not accept file extensions and deno requires them.
By using dnt, I can bypass this problem and have deno
package the esm version directly.
* sdk/ts format with deno fmt
* remove sdk/ts from esbuild
since we're using deno to package it anyway.
* First draft of renaming nest/ed to namespace/d
After a lengthy discussion on discord it was suggested that we rename
`nested` signals to `namespaced` signals. For a few reasons:
1. Nested signals are used for namespacing.
2. We don't support or plan to support root nodes being signals
3. Nested signals should not be used to recreate SPAs and are there
for namespacing convenience (bulk add/remove) not
lookup (read/watch).
4. The naming was causing confusion.
Not sure if we still need to specify that only nodes are signals now
that nested signals are called namespaced signals.
I haven't renamed the implementation's yet as that's an implementation
detail we can address later.
Link to discord conversation:
https://discord.com/channels/1296224603642925098/1296225503610671224/1327365393563783238
* Fix persist example test
* Update datastar_expressions.md
* Update attribute_plugins.md
---------
Co-authored-by: Ben Croker <57572400+bencroker@users.noreply.github.com>
* Updated README
* Added retryDuration to ServerSentEventGenerator
* Added options to readme
* Moved core sdk to submodule
* Moved core sdk to submodule
* Added more explanations to readme
* Fixed java directory structure in build/run.go
* Can .value be removed?
Fixes#428
* get dialog working
* add } to terminators
* Add fujs.svg
* whackamole
* sigh
* recursive regex tag template literals, lol
* Regex example
* (jacks|lack|of|complete|surprise)
* persist fixed
* fix current regex example
* current state
* Update all examples
* regex version of signals, actions and escapement
* Fix Go examples
* fix templates
* Add `@` to actions in examples
* Revert `.Value`
* frontpage
* all test pass
* move to @get/post/put/patch/delete
* Fix up docs and examples
* Add true client size to readme
* Add v1.0.0-beta.1 release notes
* @ → $
* Add space
* Fix more docs and examples
* missed files in merge
---------
Co-authored-by: Ben Croker <ben@putyourlightson.com>
- Rewrite the entire API to leverage JDK 17 features and implement a fluent API design.
- Updated the minimal JDK version requirement to 17.
- Temporarily removed examples as they are not yet aligned with the new API.
BREAKING CHANGE: The API has been completely redesigned and requires JDK 17 or higher.
BREAKING CHANGE: Previous examples and usages are no longer compatible.
* Readd manual building instructions
Useful for building with docker as well since it gives indications
about the main task commands.
* Improve wording of BUILDING.md
* Break up list in BUILDING.md
* Remove mention of git-lfs from BUILDING.md
* Fix typo
---------
Co-authored-by: Ben Croker <57572400+bencroker@users.noreply.github.com>
* Add folder
* feat: Initial plugin check-in under monorepo
* refactor: Add `test.css`
* refactor: Use latest `web-types` schema
* refactor: Better dynamic autocomplete for `data-on-*`, `data-class-*`, and `data-attributes-*`
Some of the other attributes are still a WIP
* refactor: Remove workflows that build/release/test the plugin
tbd if we want to figure out how they work and re-implement them in the future
* refactor: Remove vestigial tests
* refactor: Rename in preparation for multiple `web-types` files
* feat: Add `data-bind-*` autocomplete only on user input elements
* feat: Add singular completions
* feat: Add data-computed
* feat: Add data-signals-*
* feat: Add `raf` & `signal-change` events
* feat: Add `data-ref`
* feat: Add `data-indicator`
* feat: Finish attributes
* refactor: Code cleanup
* refactor: Namespace to `starfederation`
* feat: Add `data-on-*` modifiers
* feat: Add data-persist modifiers
* refactor: Make our attributes virtual
* refactor: Make modifiers sticky
* Revert "refactor: Remove workflows that build/release/test the plugin"
This reverts commit 126c2b33e0.
* docs: Add a description
* feat: Add plugin icon
* docs: Update docs to add Errata, add screenshot
* refactor: Version 0.21.0 to match the D* release
* docs: Show how to get the documentation window alongside the autocomplete
* refactor: Switch to `__` modifier separator, add full set of modifiers for all tags
---------
Co-authored-by: Andrew Welch <andrew@nystudio107.com>
* scratch no longer has a /tmp dir, move to alpine
we need all the files cause Docker COPY doesn't support glob
* Update routes_home.templ (#319)
Remove two typos
* toggleAll & setAll use path instead of regex (#318)
* toggleAll & setAll use path instead of regex
Fixes#317
* Fix wording
---------
Co-authored-by: Ben Croker <57572400+bencroker@users.noreply.github.com>
* Update release note
* Update go_deeper.md (#320)
Minor edits to Signals section, for grammar, clarity
* Make retries and backoff configurable (#316)
Fixes#190
* Fix links to SDKs
* Tweak intro code
* Improve getting started guide
* Fix and improve getting started guide
* Add web components
* evt.detail.value not working for third party libs (#326)
* evt.detail.value not working for third party libs
Fixes#324
* regex is never fun
* fix possible xss on errors
* Add circular logo
* fix clipboard usage on front page
* Support closing SSE connection from client side (#327)
Fixes#276
* Fix link in readme
* refactor: `nodejs`, `npm`, and `pnpm` are needed for the `libpub` task
* data-ref bug (#333)
* data-ref bug
Fixes#331
* Merge branch 'develop' of github.com:starfederation/datastar into delaneyj/issue331
* adds context.WithTimeout to site smoketests (#334)
removes got lib refs
* Java SDK (#321)
* Examples
* SDK and Readme
* Added build files
* Fixed Readme
* add SDKs to frontpage
* TODONE: BEN!
* Importing Datastar (#335)
Fixes#329
* bad link
* Rename default constant (#330)
* Rename default constant
* Fix PHP usage
* Build
* Remove Teleport from docs
* Bundler bug (#347)
Fixes#339
* make modifiers spec compliant (#346)
* make modifiers spec compliant
Fixes#345
* `_` for modifier args is now back to `.`. Please let it end
* remove unused error
* bump version
* Release notes and VSCode release
* Update VSCode version number
* Fix up modifier docs
---------
Co-authored-by: Wm Talcott <32972881+codetalcott@users.noreply.github.com>
Co-authored-by: Ben Croker <57572400+bencroker@users.noreply.github.com>
Co-authored-by: Ben Croker <ben@putyourlightson.com>
Co-authored-by: Andrew Welch <andrew@nystudio107.com>
Co-authored-by: zangster300 <nick0xbb8@gmail.com>
Co-authored-by: Peter Humulock <rphumulock@gmail.com>
* Fix newline in SDK protocol for paths. (#281)
* Fix newline in SDK protocol for paths.
Fixes#272
* Update PHP SDK
* Fix newline in SDK protocol for paths in Go SDK #272
* Tweak wording
---------
Co-authored-by: Ben Croker <ben@putyourlightson.com>
* Rename data-store to data-merge-signals. (#280)
* Make title dynamic in site (#277)
* Make title dynamic in site
Fixes#263
* Update meta tags
---------
Co-authored-by: Ben Croker <ben@putyourlightson.com>
* Add release note
* Rename data-store to data-merge-signals.
Fixes#269
* Fix backend reference docs and example
* feat: Add fly-deploy.yaml, manually triggered via GH only for now
* Rename data-store to data-merge-signals.
Fixes#269
* Update SDK #269
---------
Co-authored-by: Ben Croker <ben@putyourlightson.com>
Co-authored-by: Andrew Welch <andrew@nystudio107.com>
Co-authored-by: Ben Croker <57572400+bencroker@users.noreply.github.com>
* side effects
* Delaneyj/issue270 (#282)
* Start work on data-model removal #270
* move files #270
* move all test and examples to data-bind from data-model
* merge consts
* Add release note
---------
Co-authored-by: Ben Croker <ben@putyourlightson.com>
* Move to `@` for actions (#283)
* move to @ for actions
* Rename store to signals for #284 (#289)
* Rename store to signals #284 passes tests
---------
Co-authored-by: Delaney Gillilan <delaneygillilan@gmail.com>
* Update CHANGELOG.md
* Release notes
* Preprocessor to macro (#290)
* Preprocessor to macro Fixes#285
---------
Co-authored-by: Andrew Welch <andrew@nystudio107.com>
* Remove RefMacro
* Tweka wording
* Fix docs
* Support explicit syntax (#297)
* refactor: Use Docker-dev for the base image, and build all the things
* refactor: Log in to the registry
* refactor: Run on workflow changes
* chore: revert changes
* Improve homepage title
* delete everything first
* add top level sdk folder
move go SDK
add back bundles and stuff to make tsbuild work
* update go SDK url
* fix: Fix issue with `make task`
* fix: Fix multiple linux arch’s, add `support` as a `deploy` dep
* refactor: Install `flyctl`
* refactor: Output to the current directory
* refactor: ignore .out
* refactor: Run via Docker container
* refactor: Use `flyctl` instead of the alias
* refactor: Docker-in-Docker
As per “The Socket Solution” here: https://jpetazzo.github.io/2015/09/03/do-not-use-docker-in-docker-for-ci/
* refactor: Fix comment
* refactor: Make fly alias
* refactor: Use fly alias
* add vendored back in
* fix: Revert relative paths
* refactor: Revert ignoring `.out`
* refactor: Run flyctl in the CI container
* refactor: Remove flyctl
* refactor: Remove socket bind mount
* refactor: Add `flyctl` back in so `make task deploy` can be done locally
* add types and nested signals
* Add canonical meta tag
* add more stuff back in
* add ( i think ) the rest of the plugins
* lazy generation ReactiveExpresison, get click to edit working
* data-merge-signals to data-signals
start @action to action
* Add WIP release notes
* update more examples removing @ syntax and moving to sse action
* remove deprecated examples
* The great $ purge of 2024
* Add error codes
* working on v21 syntax #296
fix bind/intersect lazy reactiveExpression gen
* Fix todo demo
* Reorder plugins
* Start markdown errors
* Finish markdown errors
* Update release notes
* Add release note
* Update release note
* remove dup and require checks
* fix signals JSON usage
fix throttle/debounce lead/trail port
* make js obj helper
auto formatting
signal value needed explicit period prefix for value
data-signals lazy generates due to possible expressons
removeSignal applies after eval
* update test, 127/127
* Create VSCode extension (#295)
* Add attributes
* Finish attributes
* Fix repo URL
* Finish
* change attributes
* fix attributes
* VSCode extension improvements
* fix data-attributes example
* Fix a BEN!
* support individual data-class-foo calls
* error page
* Rework error pages
* Simplify
* Rework errors
* Fixes
* Delete `console.log` from library
* #296 persist back in
* Add missing error
* remove mouse move until I implement correctly
* i hate javascript
* . syntax for modifiers now :
* Extract plugin rules
* Add clarification comment
* support xor key or value for bind/ref/indicator
* Add KeyValRules
* Remove unused properties
* Update VSCode extension
* Update docs
* Improve getting started guide
* logic restored
* autogen error sidebar links
* fixed Restructure folders #287
* fix scroll test
* Prep docs for release
* Fixed check for exclusive requirements
* Build
* img diff
* add errors to nested signals
fix getting started
---------
Co-authored-by: Andrew Welch <andrew@nystudio107.com>
Co-authored-by: Ben Croker <ben@putyourlightson.com>
Co-authored-by: Ben Croker <57572400+bencroker@users.noreply.github.com>
* fix signals type elision
---------
Co-authored-by: Ben Croker <ben@putyourlightson.com>
Co-authored-by: Andrew Welch <andrew@nystudio107.com>
Co-authored-by: Ben Croker <57572400+bencroker@users.noreply.github.com>
* Issue getting throttling to work on event listeners #255
* Can't reproduce Issue getting throttling to work on event listeners, made /examples/debounce_and_throttle fixes#255
* Headers not merging correctly
Fixes#258
* add dep update
* Fix dialog test
Add helper for testing
* move csrf to a helper as well
* side effects
* deflake offline sync test
* cleanup merge options
* fix redirect test
* fix wait order for merge options
* fix scroll into view test, do not do float equality!
* fix lazy tabs and helpers
* add idle checks, cleanup helpers
* Add platforms to taskfile
Means that we download the correct version of tailwindcli for the
current platform.
* Remove comment
---------
Co-authored-by: Ben Croker <57572400+bencroker@users.noreply.github.com>
* feat: Initial docker-dev work
* refactor: Use -c
* refactor: do a fetch & pull too
* feat: Use the vite devserver when doing local development
* refactor: Add taskfile
* feat: Build the backend ahead of time so it’s cached
* feat: Clean up clean
* docs: Add documentation for Datastar Docker dev
* refactor: Add the package.json since the Taskfile tries to read it
* * remove _element suffix
* redircet, error, console are there own events
* fragments now take multi fragments
* delete removed from fragments
* fix backend plugin's delete to make test pass
* all tests pass
* cleanup old routes
* added default consts to backend.ts top of file
* Update docs for Backend Plugins (#154)
* Update plugins_backend.md
* remove bad limit for data-model on type
* add new discord server
* memes
* bump and cleanup
* remove _element from docs
* cleanup
---------
Co-authored-by: Ben Croker <57572400+bencroker@users.noreply.github.com>
* Trying to get the attribute evaluation inspection to work
* Improve inspector expression visualization
Keep expression snapshots on all state changes and fix idiomorph not
triggering attribute removals.
* inspector: Keep more events by default
* inspector: cleanup expression list on data-store removal
* Inspector: Improve dom viewer and add events
Work on events for plugin registration, the backend plugin and dom mutations.
Make a tree of Details viewer for the html.
* inspector-extension: improve types and allow devtools to message content
* inspector: add limit tree to rootId and add highlight on hover
* inspector: changes to version viewer
shows the versioned dom in an iframe and shows versioned dom references
to the store. Also allow highlight of ref element on hover (probably should happen
in the iframe instead though)
* commit current inspector work
* fix message for expr construction in inspector
* Replace TemplSignalStoreView with inspector component
Remove some things that werent satisfactory like the dom versioning in the iframe,
improve formatting of some messages and reduce the noise ratio by ignoring
certain datastar classes for animations and expr_construction messages (those show up in the expressions list anyways)
* add sidebar for inspector
cleanup css
---------
Co-authored-by: Delaney Gillilan <delaneygillilan@gmail.com>
Fixes#70
* Added modifiers to data-show
Added examples for modifiers and data-show
* Re-added show link
* Added requestAnimationFrame to show animation on first click
* Added better documentation. Added default value if duration value not provided.
* Tweaked duration time
---------
Co-authored-by: Delaney <delaneygillilan@gmail.com>
this commit fixes most of the remaining issues with the inspector as well as adding
better logging of state and a only showing state diffs in the event log.
* more styling
* move frontend packages to "packages"
* move large file out of static assets
* fix refs
* better mobile support
* add data-on-*.outside modifier
---------
Co-authored-by: Patrick Marchand <mail@patrickmarchand.com>
still some bugs in regards to the initial events and the store
inspector not being updated. The event logs also persists across
page changes. The extension console complains that
theres a cyclic object. Probably doesnt work on chrome yet, I've
only tested on firefox.
So you think you’ve discovered a bug in Datastar? To ensure this hasn’t already been reported, please first search [existing issues](https://github.com/starfederation/datastar/issues?q=is%3Aissue) and ensure you’re running the [latest release](https://github.com/starfederation/datastar/releases). The `#bug-report` channel in the [Discord server](https://discord.gg/bnRNgZjgPh) is also a great place to report bugs.
To rule out that this isn’t a setup or configuration issue, please read the [guide](https://data-star.dev/guide) and the [reference](https://data-star.dev/reference/attribute_plugins) docs.
If you still believe you’ve found a bug, please provide a clear and concise description, including:
- What is happening and what you expected to happen.
- Steps to reproduce the issue.
- Screenshots, if applicable.
- type:textarea
id:body
attributes:
label:Bug Report
description:Please provide a clear and concise description of the bug.
validations:
required:true
- type:input
id:version
attributes:
label:Datastar Version
description:Provide the Datastar version that this relates to, ideally the [latest release](https://github.com/starfederation/datastar/releases).
So you have a feature request for Datastar? To ensure this isn’t already on our radar, please first search [existing issues](https://github.com/starfederation/datastar/issues?q=is%3Aissue). The `#general` channel in the [Discord server](https://discord.gg/bnRNgZjgPh) is also a great place to suggest features.
If you still believe you have a new suggestion, please provide a clear and concise description, including:
- What problem you believe your feature request can solve.
- Any alternative solutions or features you have already considered.
- type:textarea
id:body
attributes:
label:Feature Request
description:Please provide a clear and concise suggestion for a feature.
validations:
required:true
- type:input
id:version
attributes:
label:Datastar Version
description:Provide the Datastar version that this relates to, ideally the [latest release](https://github.com/starfederation/datastar/releases).
So you have a question about Datastar? To ensure your question hasn’t already been answered, please first search [existing issues](https://github.com/starfederation/datastar/issues?q=is%3Aissue). The `#help` channel in the [Discord server](https://discord.gg/bnRNgZjgPh) is also a great place to ask questions.
If your question is still unanswered, please provide a clear and concise description. Note that we are diligent about documentation, so please check whether your question is answered in the [guide](https://data-star.dev/guide) or the [reference](https://data-star.dev/reference/attribute_plugins) docs before submitting.
- type:textarea
id:body
attributes:
label:Support Request
description:Please provide a clear and concise question.
validations:
required:true
- type:input
id:version
attributes:
label:Datastar Version
description:Provide the Datastar version that this relates to, ideally the [latest release](https://github.com/starfederation/datastar/releases).
Each tagged version of Datastar is accompanied by a release note. Read the [release notes »](https://github.com/starfederation/datastar/releases)
# WIP Release Notes
## v1.0.0-beta.11
### Added
- Added the `__debounce`, `__throttle` and `__viewtransition` modifiers to `data-on-intersect`.
- Added the `__debounce`, `__throttle` and `__viewtransition` modifiers to `data-on-signal-change`.
- Added the `__viewtransition` modifier to `data-on-interval`.
- Added the `__viewtransition` modifier to `data-on-load`.
- Added the `__viewtransition` modifier to `data-on-raf`.
### Changed
- The `datastar-sse` event is now dispatched on the `document` element, and using `data-on-datastar-sse` automatically listens for the event on the `document` ([#802](https://github.com/starfederation/datastar/issues/802)).
- The `data-on-signals-change-*` attribute key now accepts a path in which `*` matches a single path segment and `**` matches multiple path segments (`data-on-signals-change-foo.*.baz`, `data-on-signals-change-foo.**`).
- The `data-persist` attribute now accepts one or more space-separated paths in which `*` matches a single path segment and `**` matches multiple path segments (`data-persist="foo.*.baz"`, `data-persist="foo.**"`).
- The `@setAll` action now accepts one or more space-separated paths in which `*` matches a single path segment and `**` matches multiple path segments (`@setAll('foo.*.baz', true)`, `@setAll('foo.**', true)`) ([#793](https://github.com/starfederation/datastar/issues/793)).
- The `@toggleAll` action now accepts one or more space-separated paths in which `*` matches a single path segment and `**` matches multiple path segments (`@toggleAll('foo.*.baz')`, `@toggleAll('foo.**')`) ([#793](https://github.com/starfederation/datastar/issues/793)).
Thanks for thinking about contributing to Datastar 🚀
## Before You Contribute
Before you contribute, please consider that Datastar is a lightweight JavaScript framework that aims to simplify reactive web apps by using a minimalist, attribute-based approach for state management and event handling directly in HTML.
Anything outside of this scope may not be accepted. If you have a need for a feature that is not within the scope of Datastar, consider using a Web Component or writing your own Datastar plugin.
## Bug Reports & Feature Requests
Before submitting bug reports and feature requests, please search the [open issues](https://github.com/starfederation/datastar/issues) and the _#help_ channel in the [Discord server](https://discord.gg/bnRNgZjgPh) to see if it has already been addressed. When submitting a [new issue](https://github.com/starfederation/datastar/issues/new), please use a descriptive title and include a clear description and as much relevant information as possible.
## Pull Requests
Always open an issue to discuss your idea _before_ submitting a pull request. Pull requests must be made to the `develop` branch, have a descriptive title and clearly describe the problem and solution. If the pull request adds or changes behavior, documentation and examples should be updated accordingly.
## Documentation
Datastar’s documentation is written in markdown files that live in [this folder](https://github.com/starfederation/datastar/tree/develop/site/static/md). Improvements to them can be submitted via pull requests.
## SDKs
We are currently only accepting SDKs from people willing to contribute _and_ maintain them.
SDKs must follow the [Architecture Decision Record](https://github.com/starfederation/datastar/blob/develop/sdk/README.md). Pull requests for new SDKs must also include the following (use alphabetical order, where appropriate):
- A README file that explains how to use the SDK.
- An entry in the [SDKs reference guide](https://github.com/starfederation/datastar/blob/develop/site/static/md/reference/sdks.md).
- A value in `SDKLanguages` in [`consts.go`](https://github.com/starfederation/datastar/blob/develop/build/consts.go).
- A `consts_[language].qtpl` file for generating [constants and defaults](https://github.com/starfederation/datastar/blob/develop/build).
- Sample [code snippets](https://github.com/starfederation/datastar/tree/develop/site/static/code_snippets) in the language of your SDK.
- An implementation of each of the [examples](https://github.com/starfederation/datastar/tree/develop/examples).
- Template values for consts and examples in [`run.go`](https://github.com/starfederation/datastar/blob/develop/build/run.go).
## Building
Datastar comes with instructions for building, either manually or via Docker. Read the [Building Guidelines »](BUILDING.md)
Datastar comes with a development environment for developing Datastar via a Docker container.
It takes care of the setup listed in the for you in a Dockerized environment, which allows you to get it up and running quickly & easily.
The only requirement is that you have [Docker](https://www.docker.com/products/docker-desktop) installed (you do not need `golang`, `tmpl`, `npm`, `pnpm` or any other project dependencies installed locally).
## Why Docker?
Developers who have adopted [Docker](https://www.docker.com/) for a containerized approach to development are used to not having to install a matching development infrastructure each time they approach a project.
This allows you to "shrink-wrap" the devops needed to run a project in a container, which will run anywhere, on any machine, without having to do any meticulous setup.
It also allows you to easily swap between basic things like Go versions, without affecting your local computer.
## Quick Start
In terminal, `cd` to the `datastar/` directory, and then type:
```
make dev
```
The first time you run this command, it may take a bit of time to build the Docker image, and download all of the appropriate packages, and cache them locally.
Then just navigate to `http://localhost:8080` in your browser, and Datastar site from `site` will be up and running.
You can freely make changes to the `library` Datastar codebase, and the changes will be rebuilt and reload automatically.
You can also make changes to the `site` Datastar website backend, and the backend will be rebuilt and reload automatically.
## Using Datastar Docker Dev
Datastar Docker Dev uses the venerable `make` command to automate setup and access to the Docker containers used.
It uses `make` because it's available pre-installed on any development machine.
See the [Using Make & Makefiles to Automate your Frontend Workflow](https://nystudio107.com/blog/using-make-makefiles-to-automate-your-frontend-workflow) article for more on `make`.
The make tool is available for just about every platform you can imagine, and is installed with the [XCode CLI Tools](https://www.embarcadero.com/starthere/xe5/mobdevsetup/ios/en/installing_the_commandline_tools.html) on the Mac, and [WSL2](https://docs.microsoft.com/en-us/windows/wsl/install-win10) on Windows. Probably you have these installed already if you’re doing development.
Below are details and options available in each of the provided `make` commands:
* `make dev` - starts up the Go website server for the backend with hot reloading as you make changes
* `make test` - run the project tests inside the Docker container
* `make task xxx` - runs the specified task from the `Taskfile.yml` inside of the Docker container
* `make ssh` - gives you a shell inside of the Docker container
* `make image-build` - rebuilds the Docker image from scratch (you will probably never need this)
* `make clean` - removes all build artifacts, including the Docker image & volumes, to start from scratch
### CLI Arguments
You can pass in optional CLI arguments to override the default settings Datastar dev uses:
* `TAG=` (default is defined in `Dockerfile-dev`) - allows you to specify the official [golang Docker image](https://hub.docker.com/_/golang) tag that should be used. Using this, you can change the version of Go the container runs, e.g.: `make image-build TAG="1.24"` will use the latest patch version of Go 1.24 official Docker image.
### Terminating
To terminate the `datastar-dev` Docker container, enter `Control-C` in the terminal.
[](https://www.star-history.com/#starfederation/datastar&Date)
There are many things I find wonderful about Alpine.js and HTMX. However there are a few things that I think could be improved. The biggest challenge it both is plugins (called `directives`/`magic` in Alpine and `extensions` in HTMX). I tried writing in both of them as was disappointed by the breadth of monkey patching and lack of type safety. In general you don't have access to what other plugins do nor can you build a DAG of dependencies. This means you have to be very careful about the order you load plugins and what they do. This is especially true if you want to use a plugin in a plugin. I think this is a big problem and I wanted to solve it. Because Datastar started with the notion **_EVERYTHING_** is a plugin you could theoretically build both Alpine and HTMX inside of Datastar. I don't think that is a good idea but it is possible.
Beyond that I want the backend to be the _source of truth_ when it comes to state. [HATEOAS](https://en.wikipedia.org/wiki/HATEOAS) is a great way to structure your applications but you still need some client side state. Datstar does not use FormData; it lacks the ability to define nested data structures simply. So in general you send up JSON (automatically) and down HTML. This is more prescriptive than HTMX, but again this is with just the included plugins. You can build your own plugins to do whatever you want.
## Background
I love what [Caleb Porzio](https://calebporzio.com/) has done with [Alpine.js](https://alpinejs.dev/) and likewise with [Carson Gross](https://bigsky.software/cv/) with [HTMX](https://htmx.org/). However after trying to make extensions in both I got disappointed with the limitations. Both are pure Javascript which is fine but leads to having to either keep the whole state in your head and or be prepared for lots of runtime errors. Personally I think the best way to do this is to use Typescript and have the build step and compiler catch as many errors as possible. Plus it makes it easier to extend and create optimized builds.
### Why not just use Vue/React/Svelte?
[HTMX Essays](https://htmx.org/essays/) are a great place to start.
### Why not just use HTMX+Alpine?
There was a bit of a rant here before. TL;DR tried to show HTMX v2 could do all these things and it wasn't accepted by the community. I think HTMX is great and I will continue to recommend it. In general I think the some of the choices are throwing the baby out with the bathwater when it comes to things like Vite/TS/etc. Good news is that everything I, and some other lurkers, wanted are available in Datastar or can be added easily via plugins.
[Let's move on to the docs and see what Datastar can do](/docs)
## In response to the HTMX essay [No Build Step](https://htmx.org/essays/no-build-step/)
This is making the counter argument that a build step are extremely valuable when building a frontend framework. Lack of builds might be the right choice for HTMX but in my eyes its a self full-filling prophecy. The author concurrently wary of progress yet works on a library about extending the HTML spec to do more than it was designed for. The essay is written by a core contributor and not the author of HTMX. In general, its filled with so many fallacies but the real culprit is the _false dilemma fallacy_.
> This common fallacy misleads by presenting complex issues in terms of two inherently opposed sides. Instead of acknowledging that most (if not all) issues can be thought of on a spectrum of possibilities and stances, the false dilemma fallacy asserts that there are only two mutually exclusive outcomes.
> This fallacy is particularly problematic because it can lend false credence to extreme stances, ignoring opportunities for compromise or chances to re-frame the issue in a new way.
The whole discussion is framed as a false dilemma. Almost every point he makes is demonstrably false by the counter example of Datastar. Its smaller, faster, supports more features, isn't written in Javascript, targets multiple browser versions, and has a build step. If he is trying to prove his point it needs to be done with more rigor. In general I find the HTMX essays to be a beacon for well articulated arguments; this one is not and does a disservice to the community exploring hypermedia.
## TED Talk
In the first paragraph he says both:
> A recurring question from some htmx contributors is why htmx isn’t written in TypeScript, or, for that matter, why htmx **_lacks any build step at all_**. The full htmx source is a single 3,500-line JavaScript file; if you want to contribute to htmx, you do so by modifying the htmx.js file, the same file that gets sent to browsers in production, **_give or take minification and compression_**.
So before the essay starts he's given up the ghost. Even in as close to pure Javascript as you can get you still need a build step. Thank you, this has been my TED talk.
## Write once, run forever
> The best reason to write a library in plain JavaScript is that it lasts forever.
This is just not the world we live in. ES5 is dead, IE11 is dead, ECMAScript doesn't look anything like it did a decade ago, let alone 20 years ago. ECMAScript has been around for 25 years and has changed a lot in that time, let alone the APIs you are interfacing with. The web is a moving target and you need to keep up with it. If you don't you will be left behind.
## Reduction in labor
> Maintenance is a cost paid for with labor, and open-source code bases are the projects that can least afford to pay it. Opting not to use a build step drastically minimizes the labor required to keep htmx up-to-date.
No, just no. Much like CI/CD pipelines you set it up and largely forget about it. You can even automate the updates. The build step is a one time cost that pays dividends for the life of the project. If you don't have a build step you are going to be spending a lot of time manually updating your codebase. This is a false dichotomy. Datastar supports IIFE, UMD, & Modules and took all of adding the options to the build step. I've seen the crazy wrapper that HTMX uses to support all the different module types. It's a lot of code and a lot of maintenance. I'm not saying that HTMX is doing it wrong, just that it's a lot of work to support all the different module types. I'm not sure how much time it saves in the long run.
> But htmx is written in JavaScript, with no dependencies, so it will run unmodified for as long as web browsers remain relevant. Let the browser vendors do the hard work for you.
Every Datastar build or CDN version is a single Javascript file with no dependencies. The FUD here doesn't match reality at sets up a narrative of purity that is just not true. If fact oddly **because** of our build step we have full source mapping supported so you can debug your the production minified version in the browser looking at the original source and stack. This is a huge win for developers. I'm not sure why you would want to give that up.
## Source Maps and Hot Module Reloading
> Build step advocates point out that TypeScript can generate source maps, which tell your browser what TypeScript corresponds to what JavaScript, and that’s true! But now you have another thing to keep track of—the TypeScript you wrote, the JavaScript it generated, and the source map that connects these two. The hot-reloading development server you’re now dependent on will keep these up to date for you on localhost
Yes you have **_the same build folder_** with more static files that reference each other. This is not a big deal, a weird straw man, non-issue. You have to run a dev server to test your code anyway for things like CORS to work. This is just a weird argument, especially in light of things like hot module reloading for both code and styles. Modern tooling is now in the _sub milliseconds_ for rebuilds. Want to run your tests in parallel, well now you are just missing out on the real DX that modern tooling provides.
> ...but what about on your staging server? What about in production? Bugs that appear in these environments will be harder to track down, because you’ve lost a lot of information about where they come from.
Github actions for `bun build` or `vite build` are dead easy, and with weird arguments that are just not an issue nowadays. Like complaining about Makefiles when you have Visual Studio.
## The DX
> The htmx DX is very simple—your browser loads a single file, which in every environment is the exact same file you wrote. The tradeoffs required to maintain that experience are real, but they’re tradeoffs that make sense for this project.
The DX is why I personally moved on. The core monkey patches the DOM and that's just not a good DX. The core is not very extendable and you have to keep all 3000 LOC in your head to understand the ramification of your changes let alone the changes that other people make. You are giving up a lot of the DX that you get with a build step.
> Requiring that htmx remain in a single file (again, around 3,500 LOC) enforces a degree of intention on the library; there is a real pressure when working on the htmx source to justify the addition of new code, a pressure which maintains an equilibrium of relative simplicity.
Or... make everything an extension and allow them to rely on each other at there discretion. Making it a single file doesn't do anything but make harder to maintain.
## Amalgamation is a build step
> the lack of places for functionality to hide makes working on htmx a lot more approachable. Far, far more complex projects use aspects of this approach as well: SQLite3 compiles from a single-file source amalgamation (though they use separate files for development, they’re not crazy) which makes hacking on it significantly easier. You could never build the linux kernel this way—but htmx is not the linux kernel.
This is one of the worst takes in the whole essay. SQLite is **developed** as a set of files and **deployed** as an amalgamation. Just like Datastar, Alpine, HTMX, Zod, etc. You are conflating distribution with development and even making examples against you're own position.
> The htmx code does have to be written in JavaScript, though, because browsers run JavaScript. And as long as JavaScript is dynamically typed, the tradeoffs required to get true static typing in the htmx source are not worth it (htmx users can still take advantage of typed APIs, declared with .d.ts files).
The declaration (.d.ts) he point do are not accurate and aren't maintained last I checked. Also they are filled with `any` escape hatches which don't say anything. Separating definition into documentation seems to always cause this to happen. Conflating a build target with a language target. Like saying you have write in Assembly because x86 run assembly. Javascript can be a runtime target just like C can compile to assembly. You can write in Typescript and compile to Javascript. Another fallacy.
## Legacy targets stop progress
> Because htmx maintains support for Internet Explorer 11, and because it does not have a build step, every line of htmx has to be written in IE11-compatible JavaScript, which means no ES6.
This is certainly a choice and have seems as a hot button debate in the HTMX discord channel. If you had a build step, let alone used Typescript, you could target both **with the same codebase**. The false dilemma fallacy.
> This point is obvious, but it’s worth re-stating: the htmx source would be a lot tidier if it could be split it into modules. There are other factors that affect code quality besides tidiness, but to the extent that the htmx source is high-quality, it is not because it is tidy.
This is just a weird argument. You can split it into modules and still have tidiness. You can still split up into extensions, I know because I did it. To be clear, this isn't a declaration of minimal code sizes, but it _should_ be broken up into at least a per module/extension basis.
> Once you no longer have an enormous codebase of frontend JavaScript, there is far less pressure to adopt JavaScript on the backend. You can write backends in Python, Go, even NodeJS, and it doesn’t matter to htmx—every mainstream language has mature solutions for formatting HTML. This is the principle of Hypermedia On Whatever you’d Like (HOWL).
This essay is about HTMX build step, not the backend. It appears to me that not only is it clear you are throwing out the baby with the bathwater but demolishing the whole house. Just because SPA's are not the right tool for every job doesn't mean that you should throw out the whole tool box. We are talking about the best way to build a frontend library, not the backend.
## Conclusion
Even HTMX has a build step, and if they embraced it they could support multiple targets while still keeping their single un-tree shakeable amalgamation that they enjoy developing with.
The input issues a `GET` to `/active_search/data` with the input value bound to `$search`. The `debounce_1000ms` modifier ensures that the search is not issued on every keystroke, but only after the user has stopped typing for 1 second. This modifiers will be covered in more detail in the [reference section](/reference).
Datastar is designed to allow you to use CSS transitions to add smooth animations and transitions to your web page using only CSS and HTML. Below are a few examples of various animation techniques.
Datastar also allows you to use the new View Transitions API for creating animations.
The simplest animation technique in Datastar is to keep the id of an element stable across a content swap. If the id of an element is kept stable, htmx will swap it in such a way that CSS transitions can be written between the old version of the element and the new one.
The [Progress Bar](/examples/progress_bar) demo uses this basic CSS animation technique as well, by updating the `length` property of a progress bar element, allowing for a smooth animation.
## Swap Transitions
### Fade Out On Swap
<divid="fade_out_swap">Fade out swap Demo</div>
If you want to fade out an element that is going to be removed when the request ends, just send a SSE event with the opacity set to 0 and set a transition duration. This will fade out the element before it is removed.
## Settling Transitions
### Fade In On Addition
<divid="fade_me_in">Fade in Demo</div>
Building on the last example, we can fade in the new content the same way, starting from an opacity of 0 and transitioning to an opacity of 1.
## Request In Flight Animation
<divid="request_in_flight">Request in Flight Demo</div>
One of the nice features for reactivity is to show a spinner when a request is in flight. On any element that is using backend actions you can add a `data-indicator` attribute to show a spinner when the request is in flight. This can be done like so:
This demo shows how to implement a common pattern where rows are selected and then bulk updated. This is accomplished by putting a form around a table, with checkboxes in the table, and then including the checked values in PUT’s to two different endpoints: `activate` and `deactivate`:
Added to the page in this way:
```html
<style>
tr.deactivate.datastar-settling td {
background: lightcoral;
}
tr.activate.datastar-settling td {
background: darkseagreen;
}
tr td {
transition: all 1.2s;
}
</style>
```
The server will either activate or deactivate the checked users and then rerender the tbody tag with updated rows. It will apply the class activate or deactivate to rows that have been mutated. This allows us to use a bit of CSS to flash a color helping the user see what happened. The server events look like this:
```go
event: datastar-fragment
id: 129798448043016254
data: selector
data: merge upsert_attributes
data: settle 500
data: fragment ...
```
Notice the `settle` value is 500. This means the `datastar-settling` class will be added immediately and removed after 500ms. This is a nice way to show the user that something is happening.
The click to edit pattern provides a way to offer inline editing of all or part of a record without a page refresh. This pattern starts with a UI that shows the details of a contact. The div has a button that will get the editing UI for the contact from `/contact/1/edit`
If you compare to HTMX you'll notice there is no form, you can use one but its unnecessary. This is because you are already using signals and when you use a `PUT` to `/contact/1/edit`, the body is the entire contents of the store and its available to handle errors and validation holistically. There is also a profanity filter on the normal rendering of the contact that is not applied to the edit form. Controlling the rendering complete on the server allows you to have a single source of truth for the data and the rendering.
### There is no client validation
On the backend we've also added a quick santizer on the input to avoid bad actors (to some degree). You already have to deal with the data on the server so you might as well do the validation there. In this case its just modifying how the text is rendered when not editing. This is a simple example but you can see how you can extend it to more complex forms.
Dialogs can be triggered with the standard browser `prompt` and `confirm` within an expression. These are triggered by the user interaction that would trigger the fetch, but the request is only sent if the dialog is accepted.
```html
<button
id="dialogs"
data-store="{prompt:'foo',confirm:false}"
data-fetch-url=""
data-on-click="$prompt=prompt('Enter a string',$prompt);$confirm=confirm('Are you sure?');$confirm && $$get('/examples/dialogs___browser/sure')"
>
Click Me
</button>
```
The value provided by the user to the prompt dialog is fed back into the store, and the confirm dialog is used to determine whether the request should be sent.
This will tell trigger a whole table replacement as we are going to remove the `Edit` buttons from other rows as well as change out to inputs to allow editing. The `data-on-click` attribute sets a variable `$editRowIndex` to the index of the row that is being edited. This is used to determine which row to edit on the server side. Again we don't need alpine or hyperscript to do this, signals and actions are built-in.
Finally, here is what the row looks like when the data is being edited:
Here we have a few things going on, clicking `Cancel` will bring back the read-only version of the row. Finally, there is a save button that issues a `PATCH` to update the contact.
The `data-fetch-indicator` attribute is used to specify the elements that should be made visible when the fetch request is in progress. The value of the attribute is a CSS selector that can represent multiple elements. The same `data-fetch-indicator` selector can be used by different elements at the same time.
The `$$isFetching("#ind")` action returns a computed value that allows you to easily react to the state of the indicator.
**Note:** The contents of the `data-fetch-indicator` is an expression. In this case, the expression is a string literal, hence the single quotes around the CSS selector.
We don't need a form because everything is encoded as signals and automatically sent to the server.
We `POST` the form to /upload, since the `input` is using `data-model` the file will be automatically encoded as base64. If your store includes `${signalName}Mimes` and `${signalName}Names` then those will be sent as well. All three signals are arrays and files / metainfo will be appended in the order of selection.
## Differences from HTMX
Since HTMX uses standard forms the [file upload is lost](https://htmx.org/examples/file-upload-input/) when the form is submitted. This is a limitation of the browser and not HTMX. However, since we are using signals we can send the file back down to the client and hook up to the signal if desired.
This last element contains a listener which, when scrolled into view, will trigger a request. The result is then appended after it. The last element of the results will itself contain the listener to load the next page of results, and so on. `data-intersects` is a custom attribute that triggers a request when the element is scrolled into view. The `$offset` and `$limit` parameters are used to control the pagination of the results. The server sents back a new `more_btn` element with a new offset and the results to be appended.
```bash
event: datastar-fragment
data: selector #more_btn
data: merge morph_element
data: settle 500
data: fragment <divdata-intersects="$offset=10;$limit=10;$$get('/examples/infinite_scroll/data')"id="more_btn"><div>Loading...</div></div>
event: datastar-fragment
data: selector #click_to_load_rows
data: merge append_element
data: settle: 500
data: fragment <<trid=\"agent_60\"><td>Agent Smith 3c</td><td>void61@null.org</td><tdclass=\"uppercase\">39b02fcf39c047c5</td></tr>"
This example shows how to do inline field validation, in this case of an email address. To do this we need to create a form with an input that POSTs back to the server with the value to be validated and updates the DOM with the validation results. Since its easy to replace the whole form the logic for displaying the validation results is kept simple.
This example shows how easy it is to implement tabs using Datastar. Following the principle of [Hypertext As The Engine Of Application State](https://en.wikipedia.org/wiki/HATEOAS), the selected tab is a part of the application state. Therefore, to display and select tabs in your application, simply include the tab markup in the returned HTML fragment.
Shows the current merge options available. For best understanding open up your dev tools and look at the contents of `#contents` to see how the merge options effect the DOM.
** Note:** This uses `#target` and can create multiple elements with the same ID (which is [not valid HTML](https://html.spec.whatwg.org/#the-id-attribute)) but is fine for this example. In actual use be sure to make IDs unique.
As long as the fragment has a newline at the end of the line it will be treated as a multiline fragment. This is useful when you are writing a lot of text by hand.
```text
event: datastar-fragment
data: merge morph_element
data: settle 500
data: fragment
data: <divid="replaceMe">
data: <pre>
data: This is a multiline fragment.
data:
data: Used when you are writing a lot of text by hand
The `data-on-load` attribute is used to specify a fetch request that should be made when the element is loaded. The value of the attribute is a JavaScript expression that is evaluated when the element is loaded. This example comes from a [Github issue](https://github.com/delaneyj/datastar/issues/15) where the user wanted to load session data when the page was loaded.
**Note:** In this case its targeting the `#replaceMe` element but its up to the SSE returned to how the page is updated.
This example shows how to implement an updating progress graphic. Since Datastar is using SSE this is very easy to implement. The server sends a progress value every 500 milliseconds, and the client updates the progress bar accordingly sending down a new SVG. After the progress is complete, the server a button to restart the job.
In the [Title Update Backend](/examples/title_update_backend) example we showed how to update the title of the page using a server sent event fragment. In this example we show how to update the title of the page using a requestAnimationFrame event on the client side.
`data-on-raf` is a special event that is triggered on every requestAnimationFrame event. This is useful for updating the UI at maximum at the rendering refresh rate of the browser. In this example we update the currentTime store with a new Date object. This triggers a re-render of the currentTime span element. You can still use the `throttle` and `debounce` modifiers to control the rate of updates even further.
In this case we are updating the currentTime store with the current time. This triggers a re-render of the `currentTime` span element, however if you inspect with the browser debugger you will notice that `#time`'s `<span>` element is not updated every frame. This is because the signals are smartly updated only when the value changes. This is a performance optimization that is done by default.
As part of SSE updates you may want to redirect the user to a different page. This can be done by returning a `data: redirect` event from the server. The client will then redirect the user to the specified URL.
We are highlighting the middle paragraph of the page to make it easier to see the scroll into view effect. Depending on the size of your browser window, you may need to scroll to see the effect. In this specific example the options are sent to the server to then send down the scroll command but normally you would just send the scroll command directly from the server.
**Note:** The defaults are to have smooth centered scroll. If you want to also focus() the element just add the `.focus` modifier.
[Shoelace](https://shoelace.style/) is a wonderful webcomponent library. It is a great fit for Datastar, because it is designed to be used with plain HTML. All the Datastar built-in plugins just work with Shoelace.
Note this is using a `nested` data store just to verify that works. The store gets sent will get sent to the server as the whole context of the client should be available to the server.
`data-on-store-change` is a special event that is triggered when the store changes. This is useful for updating the UI when the store changes. In this example we update the `clicks` store with a new value. This triggers a re-render of the `clicks` span element. You can still use the `throttle` and `debounce` modifiers to control the rate of updates even further. In this case we are sending the store changes to the server to update the lifetime total clicks the server has seen.
**Note**: The `.remote` modifier is used to only trigger this event when remotely viewable signals are updated. This is useful for not sending data that is not needed to the server. To look at the details run `console.log(JSON.stringify(ds.store.value,null,2))` in the browser console. You should see something like
```json
{
"_dsPlugins": {
"on": {}
},
"_sidebarOpen": false,
"clicks": 0,
"_localState": {
"bar": 1234
},
"_anotherLocalVar": "hello"
}
```
Whereas if you look at the Network tab in the browser you should see the following request payload
```json
{ "clicks": 0 }
```
Any signal (or nested set of signals) starting with an underscore `_` is considered local and will not be sent to the server. In this example `_localState` and `_anotherLocalVar` are local only.
A user [in the Discord channel](https://discord.com/channels/725789699527933952/1180902694999838752) was asking about needing a plugin similar to [HTMX's head support](https://v1.htmx.org/extensions/head-support/) to update title or head elements. With Datastar this is unneccessary as you can just update the title directly with a SSE fragment
```
event: datastar-fragment
data: selector title
data: fragment <title>08:30:36 from server</title>
```
In a similar fashion you can append, prepend directly to the HEAD element using [Merge Options](/examples/merge_options)
Datastar was originally a branch of HTMX that was meant to make it more modular and easier to write plugins for. What was not expected is in creating what felt like essential plugins was actually able to recreate all the needed features of a full SPA frameworks, HTMX and Alpine and do it all in smaller package. While you can still build your own plugins, the core set of plugins is enough to build a full application. Let's explore some more features
## Evaluating data-\* attributes
Before we had used something like `data-text="$foo"` attribute. What this is actually doing under the hood is turning the string into a function that is evaluated safely into `(ctx)=> ctx.store.foo.value` where `ctx` is the Datastar object that was injected into the page. In this case its connected directly to the contents of a signal but you could do any javascript that is valid. This also means you could eval to a constant string but that would looks something like `data-text="'hello world'"`, notice the quotes, otherwise it would be looking for variable `hello world` which isn't valid and would fail to evaluate. This is the one major gotcha of declarative code, you have to be careful about the context.
## More attributes
So far we've seen `data-store`, `data-model` and `data-text`. There are many more attributes that can be used to make your page more reactive. Check out the API for a full list of attributes but here are a few more that get used the most.
### `data-show`
This is a simple way to show and hide elements. It's a simple way to do `v-if` in Vue or `x-show` in Alpine.
```html
<divdata-show="$foo">Hello World</div>
```
When the `foo` signal is truthy the div will be shown, otherwise it will be hidden.
### `data-on`
This is a way to add event listeners to elements. It's a simple way to do `v-on` in Vue or `x-on` in Alpine.
When the button is clicked it will log `hello world` to the console.
## Actions
Actions are helper functions that are made available during a data-\* attribute evaluation. They are a way to do more complex operations without having to write a lot of javascript. Here are a few of the most common actions. They are prefixed with `$$` to avoid any conflicts with other attributes.
### `$$setAll`
Sometimes you want to set multiple values at once. It takes a prefix regexp and a set of signals. For example if you have a form with a bunch of inputs and you want to set them all at once you could do something like this.
```html
data-text="$$setAll('contact_',true)`
```
### `$$toggleAll`
Works the same as `$$setAll` but will flip the value of the signals.
There can be many more actions and you can even write your own. But the majority of the time you'll be using the actions that allow for backend integration, which we'll cover in the next section.
If you are familiar with libraries like [HTMX](https://htmx.org/) or [AlpineJs](https://alpinejs.dev/); Datastar brings them together. This breaks down essentially to:
1. Send the current UI from your backend via HTML fragments like HTMX.
2. Manage client side state that wouldn't make sense to be managed by your backend like AlpineJS.
I've had [thoughts](/essays/why_another_framework) on both of these in the past. TLDR; While both libraries are great, I wanted to go in a different direction.
Datastar accomplishes both tasks in a unified manner and it's [tiny](https://bundlephobia.com/package/@sudodevnull/datastar).
## Installation
To get started you must first get a copy of Datastar. There are a few ways to do this.
**Remotely**
You can include it directly into your html using a script tag:
For npm-style build systems, you can install Datastar via npm and then import this in your server file.
```bash
npm i @sudodevnull/datastar
```
## A Quick Primer
Now let's get our feet wet. We'll walk through some ways to use Datastar with a quick example. For our example we'll just spin up an [Express](https://expressjs.com/en/starter/hello-world.html) server on Node. We'll have the server prepare a template for us when we first navigate to it.
You can copy the code below to get started. Don't worry, we've already installed Datastar for you using a [CDN](#remotely).
This is the global store, if you make multiple stores they will actually merge into one store behind the scenes. If there are two fields with the same name, Datastar will resolve as last in wins.
The store is great and all but how can we use it? There are many ways. Let's check some of them out.
## Some Reactivity
If you had a keen eye, you noticed we put `input` as a field on our store. What's that about? Glad you asked! It's Datastar's way to sets up two-way data binding on an element. In our case this `input` element. Say hi to the [Model](/reference/plugins_attributes#model) attribute.
This binds to a signal so our store can stay up to date with whatever is typed into this input. You can even nest your state like this `{"nested":{"label":"foo"}}` and use `data-model="nested.label"` or access it from the backend as needed.
Good stuff so far. How can we see this? We can check the changes locally using the [data-text](/reference/plugins_attributes#text) attribute.
Create a div in your `<main>` Element:
```html
<divdata-text="$input"></div>
```
Sets the text content of an element to the value of the signal. Now check it out, client-side reactivity! We can have different types of state as well. We can even do fun stuff like `data-text="$value.toUpperCase()"`.
Speaking of which, let's do some more! Let's play hide 'n seek with the [data-show](/reference/plugins_visibility#show) attribute.
Add this to your store:
```js
{ input: "", show: false };
```
We can hide elements and show them without using JavaScript! How will we trigger this though?
## Events
We bring in the [On](/reference/plugins_attributes#on) attribute. This sets up an event listener on an element. In this example, we're using `data-on-click`. You will later see there are other `data-on` actions we can utilize. You can also do silly things like `data-on-click="console.log('hello world')"`.
So what else can we do? You can mess around and do some fun stuff with expressions. For instance, something like: `"$prompt=prompt('Enter something',$prompt);$confirm=confirm('Sure?');$confirm && $$get('/sure')"` is totally feasible.
Anyhow, we haven't really even scratched the surface. Let's keep going.
## Backend Plumbing
Now, let's send some data. To do this there's a few things we must understand but it's all fun and easy and you'll want to know it if you do not already!
Datastar uses [Server-Sent Events](https://en.wikipedia.org/wiki/Server-sent_events) or SSE. To use SSE, we have to set our backend up for it. Luckily it's extremely simple and [provides us with many advantages](/essays/event_streams_all_the_way_down).
Let's set things up. Copy the below code to your server.
Copy this to your server code:
```js
function setHeaders(res) {
res.set({
"Cache-Control": "no-cache",
"Content-Type": "text/event-stream",
Connection: "keep-alive",
});
res.flushHeaders();
}
```
`setHeaders` is simple a utility function we will use on our endpoints to set our headers to use SSE.
Copy this to your server code:
```js
function sendSSE({ res, frag, selector, merge, mergeType, end }) {
res.write("event: datastar-fragment\n");
if (selector) res.write(`data: selector ${selector}\n`);
if (merge) res.write(`data: merge ${mergeType}\n`);
res.write(`data: fragment ${frag}\n\n`);
if (end) res.end();
}
```
We will use `sendSSE` as another utility function that will help us configure our response to fit SSE and Datastar formats. Let's check that out real quick.
## Stay In Formation
SSE messages are text-based and consist of one or more "events". Each event is separated by a pair (`\n\n`) of newline characters. An individual event consists of one or more lines of text, each followed by a newline character (`\n)`), and uses a simple key-value pair [format](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#event_stream_format).
For our Datastar example:
```
event: datastar-fragment // \n
id: 129618219840307262 // \n
data: merge morph_element // \n
data: fragment <divid="id">...</div> // \n\n
```
**NOTE** in the real message the comments and newlines wouldn't be visible
Each data message in the event is separated by a (`\n`) newline and each event is separated by a pair of (`\n\n`) newlines. If you notice that is what we are doing in `sendSSE` except with a little flavor added so we can tell Datastar what we want to do using [Datastar's format](/reference/plugins_backend#datastar-sse-event).
Now let's make our route.
Copy this to your server code:
```js
app.put("/put", (req, res) => {
setHeaders(res);
const { input } = req.body;
backendData.input = input;
const output = `Your input: ${input}, is ${input.length} long.`;
let frag = `<div id="output">${output}</div>`;
sendSSE({
res,
frag,
selector: null,
merge: true,
mergeType: "morph_element",
end: true,
});
});
```
So here you see we're setting our headers with `setHeaders`. We modify state that's stored specifically on the backend. This can be anything you want, like a database. Then we construct the response string much like HTMX and include the store attribute. We send the response with the `morph_element` merge type.
...and give ourselves a place to show our new state on the client.
Voila! Now if you check out what you've done, you'll find you're able to send data to your `/put` endpoint and respond with HTML updating the output `div`. Neato!
Let's retrieve the backend data we're now storing.
We're now fetching state that's stored on the backend.
Let's try something for fun. In your `/get` route, change your call to `sendSSE` so that we do not immediately end the request connection.
Change your `sendSSE` function call in your `\get` route.
```js
sendSSE({
...
end: false,
});
```
Add this to your `sendSSE` function below the first call:
```js
frag = `<div id="output3">Check this out!</div>;`;
sendSSE({
res,
frag,
selector: "#main",
merge: true,
mergeType: "prepend_element",
end: true,
});
```
Now you'll notice you're sending two events in one call. That's because Datastar uses SSE. So using `prepend_element` we're able to prepend what we want to a target element. We do this using a `selector` and in our case this is the `<main>` element. Good stuff! You can check out all of Datastar's event types [here](http://localhost:8080/reference/plugins_backend).
There's one last thing we're going to do. Let's add a simple data feed upon loading the page.
Copy this to your server code:
```js
app.get("/feed", async (req, res) => {
setHeaders(res);
while (res.writable) {
const rand = randomBytes(8).toString("hex");
const frag = `<span id="feed">${rand}</span>`;
sendSSE({
res,
frag,
selector: null,
merge: false,
mergeType: null,
end: false,
});
await new Promise((resolve) => setTimeout(resolve, 1000));
I told you we would use another `data-on` action earlier and here it is. `data-on-load` will perform this request when the page loads. If you check things out now you should see a feed that updates using SSE upon loading. Cool!
Datastar supports all the verbs without requiring a `<form>` element: `GET, POST, PUT, PATCH, DELETE`.
So that concludes our primer! Check out the full code for our Node example [here](/examples/node).
If you're still here I imagine you want to know more. Let's define things a little better.
## A Better View
To be more precise, think of Datastar as an extension to HTML's [data attributes](https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes). Using attributes, you can introduce state to your frontend, then access it anywhere in your DOM, or a backend of your choice. You can also setup events that trigger endpoints, then respond with HTML that targets fragments of your DOM.
- Declare global state: `data-store = "{foo: ''}"`
- Link-up HTML elements to state slots: `data-model = "foo"`
- Adjust HTML elements text content: `data-text = "$foo"`
- Hookup other effects on your DOM to the state: `data-show= "$foo"`
- Setup events using `data-on-(load or click) = "$$get(/endpoint)"`
- Respond in HTML wrapped in SSE with a target element ID to update
It's that simple. To dive deeper check out some of the other links or just click below.
Web development has become a technical occultism activity, in which the focus is on JavaScript and the capabilities of making HTML content dynamic instead of making a better job of delivering HTML. This JavaScript religion has led to the rise of frontend frameworks such as React, Vue, Svelte, Solid, etc. In turn those were not enough for full application and progress led the industry to full-stack JavaScript frameworks like Next.js, Nuxt, Svelte and Solid Start. Once you need a framework for reactivity it makes sense to embrace it in the backend too for consistency.
In reality almost all frameworks come down to updating the DOM as fast and as simply as possible with some effort around improving developer experience.
## Philosophy
- **Be declarative**
- **Use signals**
- **Supply a set of plugins that handle 99% of problems**
Datastar started as just a plugin framework but found that by having no overlap in features, it was possible to replace any SPA framework and even hypermedia focused libraries like HTMX while being much smaller and _(we think)_ easier to use.
With Datastar, even if you have never coded before, with a few examples, you can easily create high interconnected web assets. It doesn't matter if you are a making a user interface for bank or a simple blog. The approach is simplicity through declarative HTML.
If Datastar doesn't match your needs, you still might be interested in using it as originally intended [and write your own library](https://github.com/delaneyj/datastar/tree/main/library/src/lib/plugins).
## Time to think declaratively
Declarative code is amazing.
```sql
SELECT *
FROM orders
WHERE state='completed'
ORDER BY time DESC
LIMIT 10
```
This is SQL. As a user you don't have to know how the query will get executed, its up to the engine used. Sure you can read the query plan or introspect indexes but you can start at a high level and drill down only when necessary.
HTML work in a similar fashion. You don't have to worry about how a `<div>Hello</div>` turned into pixels nor how the page uses resources when you tab away. This is wonderful for the majority of cases but at least in modern HTML is a bit limiting.
For example:
```html
...PSEUDO CODE..
<div>
<label>HELLO WORLD</label>
<inputvalue="hello world"/>
</div>
```
if you want to use the result of changing the input to modify the label you have to write code. What if we could extend HTML to make that process declarative? Well funny you ask...
## Custom data attributes
Luckily HTML5 has the concept of [data attributes](https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes) which allows anyone to add thier own attributes and still be valid HTML. The only real requirement is they be kebab-cased and start with `data-*`. Data star... that'd be a clever name... oh I get it!
Before we can actually use the `data-*` we need a quick aside about signals.
## Signals
Signals are a way to do fine grain reactivity in a very efficient way. They are similar to formulas in Excel. Instead of doing something like:
```js
let a = 2;
let b = 2;
let c = a * b;
console.log(c);
```
You can do:
```js
const a = signal(2);
const b = signal(2);
const c = computed(() => a() * b());
effect(() => {
console.log(c());
});
```
The difference is if you change `a` or `b`, `c` will auto schedule for updates. So you spend your time declaring relationships more than procedures. They have been popularized by [Solid,js](https://www.solidjs.com/) but are now used by many frameworks.
## The Store
Ok so back to our hypothetical framework let's have a way to declare stuff that can setup signals on the page using `data-*` attributes.
```html
<divdata-store="{value:'hello world'}"></div>
```
The contents is just a set of data that can evaluated by [JSON.parse](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse) and will convert this data into a tree of signals and merge into a store that tracks all of the reactivity on the page. In this case we want there to be a a single `value` signal with the contents of `'hello world'`.
## Models
Let's replace the hard coded value in the input with another attribute
```html
<divdata-store="{value:'hello world'}">
<label>HELLO WORLD</label>
<inputdata-model="value"/>
</div>
```
Here we've created a new attribute `data-model` with the contents of `value`. We are just saying when the signal `value` changes **or** input is edited on the page make sure you keep them in sync. We don't care how, just do it.
## Contents
Now we want to update the label relationship.
```html
<divdata-store="{value:'hello world'}">
<labeldata-text="$value"></label>
<inputdata-model="value"/>
</div>
```
Here we've added another attribute `data-text` but the content has a `$` prefix. Remember that `data-*` attributes _are just strings_, which means we can give any semantics we want as long as it's consistent. It this case we are designating the use of a signal by adding a `$` prefix. Now went the `value` signal updates, so will the `innerHTML` of the label. Pretty neat.
However it doesn't yet match the original intent, which was to make it uppercase, so let's make a quick adjustment.
```html
<divdata-store="{value:'hello world'}">
<labeldata-text="$value.toUpperCase()"></label>
<inputdata-model="value"/>
</div>
```
So with this change and in a declarative nature you'd be able extend HTML and focus on relationships.
Our HTML looks pretty neat but what would it take to make it actually work? Well just add:
somewhere on your page and it should **_just work_**. If you look at the dev tools of your browser you'll also see its tiny compared to any other full framework and we haven't scratched the surface of what's included.
So far looking a lot like [Alpine.js](https://alpinejs.dev/) which is great, but as we add more features will diverge.
The rise of the frontend framework wars makes sense. When compared with full page reloads, the ability to update only the parts of the page that need to change is a huge win. However, the way this is done is often at odds with the declarative nature of HTML. This has led to a lot of lost knowledge and a lot of confusion. When your application logic lives in both browser and backend your synchronization state is in constant flux.
One of the biggest loses of knowledge is the concept of hypermedia. Hypermedia is the idea that the web is a set resources that are interconnected. This is the basis of the web and the reason it has been so successful. However, the rise of the frontend framework wars has led to a lot of confusion about how to use hypermedia. This is where H.A.T.E.O.A.S. comes in.
## Hypermedia as the Engine of Application State (H.A.T.E.O.A.S.)
Browsers don't care about your application, they care about the rendering hypermedia. For example if you have a bank website you can go to the home page and then click on a link to go to the login page, only then are valid links to your accounts available. This has huge benefits.
1. Each interaction fuels the valid next state.
2. When implemented correctly this means the backend is where all your logic lives, leading to no client side routers, validation, etc.
3. HTML can be generated in any language.
## Hypermedia on whatever you Like (HOWL)
[Carson Gross](https://hypermedia.systems/) coined the term [HOWL stack](https://htmx.org/essays/hypermedia-on-whatever-youd-like/) which is really just getting back to the roots of the web. People make fun of HTML not being a real language but its a wonderful way of exchanging interconnect assets. The HOWL stack is about leveraging the web's capabilities and with just a thin set of shims make it as powerful as any SPA framework. This is the basis of Datastar.
HTMX is amazing for resurrecting the server rendered web. In general its far easier and faster than SPA development but there are still a few issues.
- For any larger page you have to add [hyperscript](https://hyperscript.org/) or [Alpine.js](https://alpinejs.dev/) to make the page reactive.
- There are multiple ways to target the elements on the page with `hx-target` and [out of band swaps](https://htmx.org/attributes/hx-swap-oob/). This if flexible but leads to a ton of ways to do the same thing.
- Responses are still single responses. If you want to have multiple updates as soon as possible you need to use plugins that aren't well supported (at this time).
- The list of `hx-*` attributes is growing quickly and think it points to a bigger issue with scope.
None of these are deal breakers but starting from first principles we can do better. The first thing we need to do is make the page reactive. We can do this by using [SSE](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events) or [Websockets](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API). We'll use version of SSE because
- At the end of the day its "just" a HTTP request. All your sessions, cookies, etc are still valid.
- Websockets are harder to deal with depending on sticky sessions and load balancers.
- Websockets aren't compatible with HTTP2/3. Datastar will probably move to [WebTransport](https://web.dev/webtransport/) when its more available (looking at you Apple).
- HTTP2/3 is already multiplexed so you can have multiple SSE streams open at once.
## SSE
If you aren't familiar with SSE its a way to a chunked response to the browser. The browser will automatically reconnect if the connection is lost. The server can also send a `retry` header to tell the browser how often to reconnect. The browser will also automatically reconnect if the connection is lost. The nice part of having a chunked response is you can send multiple updates or a single update and the browser will handle it. A modern backend HTTP server framework should allow for easy SSE support (it's just a few headers and a specific text format for content).
## SSE backend, fetch on the frontend
For a reason that I still haven't figured out, the SSE spec and [EventSource](https://developer.mozilla.org/en-US/docs/Web/API/EventSource) only supports GET requests. This seems like a huge oversight, but we can work around it. From the backend's perspective they can send a `Content-Type: text/event-stream` from any request. This means we can use a normal `fetch` request to get the SSE stream. The only downside is we have to manually handle the reconnects. This is handled by Datastar but it's good to be aware of.
## Let's get some data
On the [home page](/) there is a global store example. When you first load the page the contents is basically empty.
Normally you don't need to do this but its trying to show that you can load a page and then have the data update. The `$$get` action is a helper function that will make a fetch request and update the store with the results. The `datastar-indicator` class is a helper class that will show a spinner when the request is in flight.
On the backend (Go in this case) we return a set of render fragments in the form of a set of `text/event-stream` events. In this case it's just a single event.
```go
event: datastar-fragment
id: 129618219840307262
data: selector
data: merge morph_element
data: settle 0
data: fragment <div...>...</div>
```
The `selector` is the CSS selector that the fragment will be inserted into. The `merge` decides how the element is added to the page. `morph_element` uses [idiomorph](https://github.com/bigskysoftware/idiomorph) to merge into the page intelligently. The `settle` is the time in milliseconds that the fragment will be inserted. The `fragment` is the HTML that will be inserted into the page. If you are familiar with HTMX and the the structure of SSE messages this should look quite familiar. Nice thing is most of these are optional (except for `fragment`) and can be optionally added to the response. In the case of the Go implementation it includes all for the sake of completeness.
This is still a lot to explore but the main point is that we can have a reactive page without having to write any Javascript. This is the power of declarative code. I'd suggest you check out the [examples](/examples) to see how this all fits together. Then when you have a specific question check out the [reference](/reference) for more details.
# Conclusion
Please don't hesitate to reach out if you have any questions. We are always looking for feedback and ways to improve. If you are interested in contributing please check out the [Github](https://github.com/delaneyj/datastar)
Allows any valid attribute to be bound to an expression. This is useful for making elements reactive. Also can be used as a fallback for any attribute that is not supported by a plugin currently.
### Model
```html
<inputdata-model="foo"/>
```
Sets up two-way data-binding on an element.
**Note:** Always binds to a signal and therefore should exclude the `$` prefix from the signal name. Only allowed on `input`,`textarea`, `select`, `checkbox` and `radio` elements.
### Text
```html
<divdata-text="$foo"></div>
```
Sets the text content of an element to the value of the signal. This is useful for setting the text content of an element to a signal value. Can use any expression that is valid in the system. For example, `data-text="$foo + 'bar'"` would set the text content to the value of `$foo` plus the string `bar`.
Sets up an event listener on an element. The event listener will trigger the action specified in the expression. The expression can be any valid expression in the system. For example, `data-on-click="$$fn('foo','bar',1234)"` would trigger the action `fn` with the arguments `'foo','bar',1234` when the button is clicked.
If any signal in the expression changes, the event listener will be updated to reflect the new value of the signal automatically.
**Note:** the `data-on-*` matches DOM events, however there are currently a few special cases for custom events.
1. `data-on-load` which is triggered when the element is loaded into the DOM.
2. `data-on-store-change` which is triggered when the store changes.
3. `data-on-raf` which is triggered on every requestAnimationFrame event.
#### Modifiers
- `.once`\* - Only trigger the event once
- `.passive`\* - Do not call `preventDefault` on the event
Makes an HTML_VERB request to the server and merges the response with the current DOM and store. The URL can be any valid URL but the response must be a Datastar formatted SSE event.
Every request will be sent with a `{datastar: *}` object containing the current store. When using `$$get` the store will be sent as a query parameter, otherwise it will be sent as a JSON body.
## Datastar SSE Event
An example of a minimal valid response would be:
```go
event: datastar-fragment
data: fragment <divid="foo">Hello!</div>
```
Addtional `data` lines can be added to the response to override the default behavior.
| `data: selector #foo` | Select the target element using a CSS selector. Will be come the target of the `merge` process, otherwise it will use the target of the initiating element's id. | |
| `data: merge morph_element` | Merge the fragment using [Idiomorph](https://github.com/bigskysoftware/idiomorph). | \* |
| `data: merge inner_html` | Replace target's innerHTML with fragment | |
| `data: merge outer_html` | Replace target's outerHTML with fragment | |
| `data: merge prepend_element` | Prepend fragment to target's children | |
| `data: merge append_element` | Append fragment to target's children | |
| `data: merge before_element` | Insert fragment before target as sibling | |
| `data: merge after_element` | Insert fragment after target as sibling | |
| `data: merge delete_element` | Remove target from the DOM | |
| `data: merge upsert_attributes` | Merge attributes from fragment into target, useful when wanting to just update a store | |
| `data: settle 1000` | Settle the element after 1000ms, useful for transitions. Defaults to `500` if missing | \* |
| `data: fragment` | The HTML fragment to merge into the DOM. **_Should only be one per event_** | \* |
| `data: redirect /foo` | Redirect the page to `/foo`. Can be used in place of a `data: fragment`**_Should only be one per event_** | |
| `data: error oh noes` | Will throw an error with the message `oh noes` and stop the request. Can be used in place of a `data: fragment`**_Should only be one per event_** | |
## Attribute Plugins
### Header
```html
<divdata-header-X-My-Header="foo"></div>
```
Add a header to the request. This can be used to send additional information to the server.
Show a spinner when the request is in flight. The `data-fetch-indicator` attribute should be a CSS selector to the element(s). When the attribute is present, the element will be hidden when requests are not in flight and shown when they are.
The `data-is-loading-id` attribute is used to specify the name of the identifier that will be present in the store's isLoading array when an element is fetching.
These are the only plugins that are required in order to have a working system. In the default build all (including core) plugins are included as they cover the most common use cases.
## Attributes Plugins
### MergeStore
```html
<divdata-store="{foo:1234}"></div>
```
Takes the contents of the attribute and runs a BigInt aware JSON parse on it. It then merges the contents into the store. This can be used anywhere as the store is a global singleton. All keys are converted into signals, works with nested objects.
### Ref
```html
<divdata-ref="foo"></div>
```
Makes an element available in the `ctx.refs` object. This is useful for accessing elements in the DOM. Can be accessed in scripts via `~foo`.
## Preprocessor Plugins
### SignalProcessor
Takes a `$var` and converts into a `ctx.store().var.value`. Since all expressions are evaluated within an effect it setups of a reactive system.
### ActionProcessor
Takes a `$$fn('foo','bar',1234)` and converts into a `ctx.actions.fn('foo','bar',1234()`. This is used to trigger actions plugins.
### RefProcessor
Takes a `~foo` and converts into a `ctx.refs.foo`. This is used to access refs similar to how you would in a Vue or Svlete components.
Expressions are the building blocks of Datastar. In fact, Datastar started as just a way to take `data-*` attributes and turn them into expressions. For exact `data-*` attribute found
1. All `Preprocesser` plugins are run. This allows for a custom DSL. The included plugins use `$` for signals, `$$` for actions, and `~` for refs. The plugins check for regular expressions and replace them with the appropriate value. Some plugins will setup extra state on load like adding CSS classes or setting up event listeners.
2. All Datastar `AttributePlugin` plugins are run in order. Most of the time these plugins are creating `effect()` signals so that that changes to the expression with automatically update the DOM and other parts of the system.
3. Check for any elemen removals and cancel any effects that are no longer needed.
Each expression is evaluated in a new Function declaration and not in a call to `eval()`. This is done to prevent access to the global scope and to prevent access to the `Function` constructor. This is done to prevent XSS attacks. Its also why all expressions take a `ctx` which has access to the store, actions, and refs, but not the global scope. This was gleamed from how Alpine.js works but with a different reactive model.
Focused on showing and hiding elements based on signals. Most of the time you want to send updates from the server but is useful for things like modals, dropdowns, and other UI elements.
## Attributes Plugins
### Show
```html
<divdata-show="$showMe"></div>
```
Shows the element when the expression is true.
### Intersects
```html
<divdata-intersects="console.log('I am intersecting!')"></div>
```
Runs the expression when the element intersects with the viewport.
#### Modifiers
- `.once` - Only trigger the event once
- `.half` - Trigger when half of the element is visible
- `.full` - Trigger when the full element is visible
### Teleport
```html
<divdata-teleport="#foo"></div>
```
Moves the element to the target.
#### Modifiers
- `.prepend` - Prepend the element to the target
- `.append` - Append the element to the target
### Scroll Into View
```html
<divdata-scroll-into-view></div>
```
Scrolls the element into view. Useful when updating DOM from the server and you want to scroll to the new content.
#### Modifiers
- `.smooth` - Scrolling should be animate smoothly
- `.instant` - Scrolling should be instant
- `.auto` - Scrolling is determined by the computed `scroll-behavior` CSS property
- `.hstart` - Scroll to the left of the element
- `.hcenter` - Scroll to the horizontal center of the element
- `.hend` - Scroll to the right of the element
- `.hnearest` - Scroll to the nearest horizontal edge of the element
- `.vstart` - Scroll to the top of the element
- `.vcenter` - Scroll to the vertical center of the element
- `.vend` - Scroll to the bottom of the element
- `.vnearest` - Scroll to the nearest vertical edge of the element
- `.focus` - Focus the element after scrolling
### View Transition
```html
<divdata-view-transition="foo"></div>
```
Page level transitions are automatically handled by an injected meta tag. Inter-page elements are automatically transitioned if the [View Transition API](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API) is available. To set the `view-transition-name` style attribute explictily you use the `data-view-transition` attribute. The spec is still in draft and not available in all browsers but Datastar will do best effort.