sigmx 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +173 -0
- package/dist/auto.d.ts +1 -0
- package/dist/auto.js +7 -0
- package/dist/auto.js.map +7 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +26 -0
- package/dist/cli.js.map +7 -0
- package/dist/core/csp.d.ts +7 -0
- package/dist/core/csp.js +20 -0
- package/dist/core/csp.js.map +7 -0
- package/dist/core/engine.d.ts +10 -0
- package/dist/core/engine.js +200 -0
- package/dist/core/engine.js.map +7 -0
- package/dist/core/expr.d.ts +12 -0
- package/dist/core/expr.js +94 -0
- package/dist/core/expr.js.map +7 -0
- package/dist/core/index.d.ts +16 -0
- package/dist/core/index.js +46 -0
- package/dist/core/index.js.map +7 -0
- package/dist/core/signals.d.ts +42 -0
- package/dist/core/signals.js +195 -0
- package/dist/core/signals.js.map +7 -0
- package/dist/core/store.d.ts +32 -0
- package/dist/core/store.js +185 -0
- package/dist/core/store.js.map +7 -0
- package/dist/core/types.d.ts +101 -0
- package/dist/core/types.js +1 -0
- package/dist/core/types.js.map +7 -0
- package/dist/kernel/compile.d.ts +16 -0
- package/dist/kernel/compile.js +96 -0
- package/dist/kernel/compile.js.map +7 -0
- package/dist/kernel/contracts.d.ts +121 -0
- package/dist/kernel/contracts.js +1 -0
- package/dist/kernel/contracts.js.map +7 -0
- package/dist/kernel/index.d.ts +20 -0
- package/dist/kernel/index.js +54 -0
- package/dist/kernel/index.js.map +7 -0
- package/dist/kernel/precompile.d.ts +25 -0
- package/dist/kernel/precompile.js +111 -0
- package/dist/kernel/precompile.js.map +7 -0
- package/dist/kernel/precompiled.d.ts +11 -0
- package/dist/kernel/precompiled.js +13 -0
- package/dist/kernel/precompiled.js.map +7 -0
- package/dist/kernel/reactive.d.ts +42 -0
- package/dist/kernel/reactive.js +201 -0
- package/dist/kernel/reactive.js.map +7 -0
- package/dist/kernel/runtime.d.ts +11 -0
- package/dist/kernel/runtime.js +210 -0
- package/dist/kernel/runtime.js.map +7 -0
- package/dist/kernel/scan.d.ts +42 -0
- package/dist/kernel/scan.js +68 -0
- package/dist/kernel/scan.js.map +7 -0
- package/dist/kernel/state.d.ts +32 -0
- package/dist/kernel/state.js +186 -0
- package/dist/kernel/state.js.map +7 -0
- package/dist/kernel/strict-csp.d.ts +7 -0
- package/dist/kernel/strict-csp.js +20 -0
- package/dist/kernel/strict-csp.js.map +7 -0
- package/dist/lib/casing.d.ts +7 -0
- package/dist/lib/casing.js +16 -0
- package/dist/lib/casing.js.map +7 -0
- package/dist/lib/objects.d.ts +9 -0
- package/dist/lib/objects.js +26 -0
- package/dist/lib/objects.js.map +7 -0
- package/dist/lib/schedule.d.ts +14 -0
- package/dist/lib/schedule.js +51 -0
- package/dist/lib/schedule.js.map +7 -0
- package/dist/lib/sse.d.ts +10 -0
- package/dist/lib/sse.js +55 -0
- package/dist/lib/sse.js.map +7 -0
- package/dist/plugins/actions/clipboard.d.ts +2 -0
- package/dist/plugins/actions/clipboard.js +11 -0
- package/dist/plugins/actions/clipboard.js.map +7 -0
- package/dist/plugins/actions/fit.d.ts +2 -0
- package/dist/plugins/actions/fit.js +14 -0
- package/dist/plugins/actions/fit.js.map +7 -0
- package/dist/plugins/actions/intl.d.ts +2 -0
- package/dist/plugins/actions/intl.js +21 -0
- package/dist/plugins/actions/intl.js.map +7 -0
- package/dist/plugins/actions/peek.d.ts +2 -0
- package/dist/plugins/actions/peek.js +6 -0
- package/dist/plugins/actions/peek.js.map +7 -0
- package/dist/plugins/actions/setAll.d.ts +2 -0
- package/dist/plugins/actions/setAll.js +11 -0
- package/dist/plugins/actions/setAll.js.map +7 -0
- package/dist/plugins/actions/toggleAll.d.ts +2 -0
- package/dist/plugins/actions/toggleAll.js +11 -0
- package/dist/plugins/actions/toggleAll.js.map +7 -0
- package/dist/plugins/attributes/attr.d.ts +2 -0
- package/dist/plugins/attributes/attr.js +20 -0
- package/dist/plugins/attributes/attr.js.map +7 -0
- package/dist/plugins/attributes/bind.d.ts +6 -0
- package/dist/plugins/attributes/bind.js +81 -0
- package/dist/plugins/attributes/bind.js.map +7 -0
- package/dist/plugins/attributes/class.d.ts +2 -0
- package/dist/plugins/attributes/class.js +22 -0
- package/dist/plugins/attributes/class.js.map +7 -0
- package/dist/plugins/attributes/computed.d.ts +2 -0
- package/dist/plugins/attributes/computed.js +20 -0
- package/dist/plugins/attributes/computed.js.map +7 -0
- package/dist/plugins/attributes/customValidity.d.ts +2 -0
- package/dist/plugins/attributes/customValidity.js +14 -0
- package/dist/plugins/attributes/customValidity.js.map +7 -0
- package/dist/plugins/attributes/effect.d.ts +2 -0
- package/dist/plugins/attributes/effect.js +12 -0
- package/dist/plugins/attributes/effect.js.map +7 -0
- package/dist/plugins/attributes/indicator.d.ts +2 -0
- package/dist/plugins/attributes/indicator.js +20 -0
- package/dist/plugins/attributes/indicator.js.map +7 -0
- package/dist/plugins/attributes/init.d.ts +2 -0
- package/dist/plugins/attributes/init.js +14 -0
- package/dist/plugins/attributes/init.js.map +7 -0
- package/dist/plugins/attributes/jsonSignals.d.ts +2 -0
- package/dist/plugins/attributes/jsonSignals.js +15 -0
- package/dist/plugins/attributes/jsonSignals.js.map +7 -0
- package/dist/plugins/attributes/matchMedia.d.ts +2 -0
- package/dist/plugins/attributes/matchMedia.js +19 -0
- package/dist/plugins/attributes/matchMedia.js.map +7 -0
- package/dist/plugins/attributes/on.d.ts +5 -0
- package/dist/plugins/attributes/on.js +28 -0
- package/dist/plugins/attributes/on.js.map +7 -0
- package/dist/plugins/attributes/onIntersect.d.ts +2 -0
- package/dist/plugins/attributes/onIntersect.js +28 -0
- package/dist/plugins/attributes/onIntersect.js.map +7 -0
- package/dist/plugins/attributes/onInterval.d.ts +2 -0
- package/dist/plugins/attributes/onInterval.js +18 -0
- package/dist/plugins/attributes/onInterval.js.map +7 -0
- package/dist/plugins/attributes/onRaf.d.ts +1 -0
- package/dist/plugins/attributes/onRaf.js +19 -0
- package/dist/plugins/attributes/onRaf.js.map +7 -0
- package/dist/plugins/attributes/onResize.d.ts +1 -0
- package/dist/plugins/attributes/onResize.js +17 -0
- package/dist/plugins/attributes/onResize.js.map +7 -0
- package/dist/plugins/attributes/onSignalPatch.d.ts +5 -0
- package/dist/plugins/attributes/onSignalPatch.js +35 -0
- package/dist/plugins/attributes/onSignalPatch.js.map +7 -0
- package/dist/plugins/attributes/persist.d.ts +6 -0
- package/dist/plugins/attributes/persist.js +16 -0
- package/dist/plugins/attributes/persist.js.map +7 -0
- package/dist/plugins/attributes/queryString.d.ts +5 -0
- package/dist/plugins/attributes/queryString.js +54 -0
- package/dist/plugins/attributes/queryString.js.map +7 -0
- package/dist/plugins/attributes/ref.d.ts +2 -0
- package/dist/plugins/attributes/ref.js +13 -0
- package/dist/plugins/attributes/ref.js.map +7 -0
- package/dist/plugins/attributes/replaceUrl.d.ts +1 -0
- package/dist/plugins/attributes/replaceUrl.js +11 -0
- package/dist/plugins/attributes/replaceUrl.js.map +7 -0
- package/dist/plugins/attributes/scrollIntoView.d.ts +2 -0
- package/dist/plugins/attributes/scrollIntoView.js +19 -0
- package/dist/plugins/attributes/scrollIntoView.js.map +7 -0
- package/dist/plugins/attributes/show.d.ts +2 -0
- package/dist/plugins/attributes/show.js +19 -0
- package/dist/plugins/attributes/show.js.map +7 -0
- package/dist/plugins/attributes/signals.d.ts +2 -0
- package/dist/plugins/attributes/signals.js +13 -0
- package/dist/plugins/attributes/signals.js.map +7 -0
- package/dist/plugins/attributes/style.d.ts +2 -0
- package/dist/plugins/attributes/style.js +25 -0
- package/dist/plugins/attributes/style.js.map +7 -0
- package/dist/plugins/attributes/text.d.ts +1 -0
- package/dist/plugins/attributes/text.js +13 -0
- package/dist/plugins/attributes/text.js.map +7 -0
- package/dist/plugins/attributes/viewTransition.d.ts +1 -0
- package/dist/plugins/attributes/viewTransition.js +11 -0
- package/dist/plugins/attributes/viewTransition.js.map +7 -0
- package/dist/plugins/directives/attribute-sync.d.ts +2 -0
- package/dist/plugins/directives/attribute-sync.js +20 -0
- package/dist/plugins/directives/attribute-sync.js.map +7 -0
- package/dist/plugins/directives/boost.d.ts +6 -0
- package/dist/plugins/directives/boost.js +35 -0
- package/dist/plugins/directives/boost.js.map +7 -0
- package/dist/plugins/directives/busy-flag.d.ts +2 -0
- package/dist/plugins/directives/busy-flag.js +21 -0
- package/dist/plugins/directives/busy-flag.js.map +7 -0
- package/dist/plugins/directives/class-toggle.d.ts +2 -0
- package/dist/plugins/directives/class-toggle.js +22 -0
- package/dist/plugins/directives/class-toggle.js.map +7 -0
- package/dist/plugins/directives/cloak.d.ts +5 -0
- package/dist/plugins/directives/cloak.js +11 -0
- package/dist/plugins/directives/cloak.js.map +7 -0
- package/dist/plugins/directives/collapse.d.ts +5 -0
- package/dist/plugins/directives/collapse.js +42 -0
- package/dist/plugins/directives/collapse.js.map +7 -0
- package/dist/plugins/directives/declare.d.ts +2 -0
- package/dist/plugins/directives/declare.js +13 -0
- package/dist/plugins/directives/declare.js.map +7 -0
- package/dist/plugins/directives/derive.d.ts +2 -0
- package/dist/plugins/directives/derive.js +20 -0
- package/dist/plugins/directives/derive.js.map +7 -0
- package/dist/plugins/directives/element-ref.d.ts +2 -0
- package/dist/plugins/directives/element-ref.js +13 -0
- package/dist/plugins/directives/element-ref.js.map +7 -0
- package/dist/plugins/directives/event-listener.d.ts +5 -0
- package/dist/plugins/directives/event-listener.js +28 -0
- package/dist/plugins/directives/event-listener.js.map +7 -0
- package/dist/plugins/directives/frame-loop.d.ts +1 -0
- package/dist/plugins/directives/frame-loop.js +19 -0
- package/dist/plugins/directives/frame-loop.js.map +7 -0
- package/dist/plugins/directives/html.d.ts +2 -0
- package/dist/plugins/directives/html.js +13 -0
- package/dist/plugins/directives/html.js.map +7 -0
- package/dist/plugins/directives/inline-style.d.ts +2 -0
- package/dist/plugins/directives/inline-style.js +25 -0
- package/dist/plugins/directives/inline-style.js.map +7 -0
- package/dist/plugins/directives/mask.d.ts +7 -0
- package/dist/plugins/directives/mask.js +47 -0
- package/dist/plugins/directives/mask.js.map +7 -0
- package/dist/plugins/directives/media-query.d.ts +2 -0
- package/dist/plugins/directives/media-query.js +19 -0
- package/dist/plugins/directives/media-query.js.map +7 -0
- package/dist/plugins/directives/on-mount.d.ts +2 -0
- package/dist/plugins/directives/on-mount.js +14 -0
- package/dist/plugins/directives/on-mount.js.map +7 -0
- package/dist/plugins/directives/patch-listener.d.ts +5 -0
- package/dist/plugins/directives/patch-listener.js +35 -0
- package/dist/plugins/directives/patch-listener.js.map +7 -0
- package/dist/plugins/directives/remove-me.d.ts +2 -0
- package/dist/plugins/directives/remove-me.js +13 -0
- package/dist/plugins/directives/remove-me.js.map +7 -0
- package/dist/plugins/directives/scroll-to.d.ts +2 -0
- package/dist/plugins/directives/scroll-to.js +19 -0
- package/dist/plugins/directives/scroll-to.js.map +7 -0
- package/dist/plugins/directives/size-watch.d.ts +1 -0
- package/dist/plugins/directives/size-watch.js +17 -0
- package/dist/plugins/directives/size-watch.js.map +7 -0
- package/dist/plugins/directives/state-dump.d.ts +2 -0
- package/dist/plugins/directives/state-dump.js +15 -0
- package/dist/plugins/directives/state-dump.js.map +7 -0
- package/dist/plugins/directives/storage-sync.d.ts +6 -0
- package/dist/plugins/directives/storage-sync.js +16 -0
- package/dist/plugins/directives/storage-sync.js.map +7 -0
- package/dist/plugins/directives/teleport.d.ts +5 -0
- package/dist/plugins/directives/teleport.js +19 -0
- package/dist/plugins/directives/teleport.js.map +7 -0
- package/dist/plugins/directives/text-content.d.ts +1 -0
- package/dist/plugins/directives/text-content.js +13 -0
- package/dist/plugins/directives/text-content.js.map +7 -0
- package/dist/plugins/directives/timer.d.ts +2 -0
- package/dist/plugins/directives/timer.js +18 -0
- package/dist/plugins/directives/timer.js.map +7 -0
- package/dist/plugins/directives/transition-name.d.ts +1 -0
- package/dist/plugins/directives/transition-name.js +11 -0
- package/dist/plugins/directives/transition-name.js.map +7 -0
- package/dist/plugins/directives/transition.d.ts +5 -0
- package/dist/plugins/directives/transition.js +40 -0
- package/dist/plugins/directives/transition.js.map +7 -0
- package/dist/plugins/directives/trap.d.ts +5 -0
- package/dist/plugins/directives/trap.js +48 -0
- package/dist/plugins/directives/trap.js.map +7 -0
- package/dist/plugins/directives/tween.d.ts +6 -0
- package/dist/plugins/directives/tween.js +58 -0
- package/dist/plugins/directives/tween.js.map +7 -0
- package/dist/plugins/directives/two-way-bind.d.ts +6 -0
- package/dist/plugins/directives/two-way-bind.js +82 -0
- package/dist/plugins/directives/two-way-bind.js.map +7 -0
- package/dist/plugins/directives/url-params.d.ts +13 -0
- package/dist/plugins/directives/url-params.js +76 -0
- package/dist/plugins/directives/url-params.js.map +7 -0
- package/dist/plugins/directives/url-replace.d.ts +1 -0
- package/dist/plugins/directives/url-replace.js +11 -0
- package/dist/plugins/directives/url-replace.js.map +7 -0
- package/dist/plugins/directives/validity.d.ts +2 -0
- package/dist/plugins/directives/validity.js +14 -0
- package/dist/plugins/directives/validity.js.map +7 -0
- package/dist/plugins/directives/viewport.d.ts +2 -0
- package/dist/plugins/directives/viewport.js +28 -0
- package/dist/plugins/directives/viewport.js.map +7 -0
- package/dist/plugins/directives/visibility.d.ts +2 -0
- package/dist/plugins/directives/visibility.js +19 -0
- package/dist/plugins/directives/visibility.js.map +7 -0
- package/dist/plugins/directives/watch.d.ts +2 -0
- package/dist/plugins/directives/watch.js +12 -0
- package/dist/plugins/directives/watch.js.map +7 -0
- package/dist/plugins/functions/bulk-set.d.ts +2 -0
- package/dist/plugins/functions/bulk-set.js +11 -0
- package/dist/plugins/functions/bulk-set.js.map +7 -0
- package/dist/plugins/functions/bulk-toggle.d.ts +2 -0
- package/dist/plugins/functions/bulk-toggle.js +11 -0
- package/dist/plugins/functions/bulk-toggle.js.map +7 -0
- package/dist/plugins/functions/confirm.d.ts +2 -0
- package/dist/plugins/functions/confirm.js +6 -0
- package/dist/plugins/functions/confirm.js.map +7 -0
- package/dist/plugins/functions/copy-text.d.ts +2 -0
- package/dist/plugins/functions/copy-text.js +11 -0
- package/dist/plugins/functions/copy-text.js.map +7 -0
- package/dist/plugins/functions/dispatch.d.ts +2 -0
- package/dist/plugins/functions/dispatch.js +9 -0
- package/dist/plugins/functions/dispatch.js.map +7 -0
- package/dist/plugins/functions/locale-format.d.ts +2 -0
- package/dist/plugins/functions/locale-format.js +21 -0
- package/dist/plugins/functions/locale-format.js.map +7 -0
- package/dist/plugins/functions/range-map.d.ts +2 -0
- package/dist/plugins/functions/range-map.js +14 -0
- package/dist/plugins/functions/range-map.js.map +7 -0
- package/dist/plugins/functions/request.d.ts +30 -0
- package/dist/plugins/functions/request.js +152 -0
- package/dist/plugins/functions/request.js.map +7 -0
- package/dist/plugins/functions/untracked-read.d.ts +2 -0
- package/dist/plugins/functions/untracked-read.js +6 -0
- package/dist/plugins/functions/untracked-read.js.map +7 -0
- package/dist/plugins/functions/websocket.d.ts +11 -0
- package/dist/plugins/functions/websocket.js +35 -0
- package/dist/plugins/functions/websocket.js.map +7 -0
- package/dist/plugins/index.d.ts +52 -0
- package/dist/plugins/index.js +108 -0
- package/dist/plugins/index.js.map +7 -0
- package/dist/plugins/server-events/apply-elements.d.ts +2 -0
- package/dist/plugins/server-events/apply-elements.js +62 -0
- package/dist/plugins/server-events/apply-elements.js.map +7 -0
- package/dist/plugins/server-events/apply-state.d.ts +2 -0
- package/dist/plugins/server-events/apply-state.js +18 -0
- package/dist/plugins/server-events/apply-state.js.map +7 -0
- package/dist/plugins/server-events/morph.d.ts +12 -0
- package/dist/plugins/server-events/morph.js +124 -0
- package/dist/plugins/server-events/morph.js.map +7 -0
- package/dist/presets/all.d.ts +3 -0
- package/dist/presets/all.js +112 -0
- package/dist/presets/all.js.map +7 -0
- package/dist/presets/essentials.d.ts +3 -0
- package/dist/presets/essentials.js +18 -0
- package/dist/presets/essentials.js.map +7 -0
- package/dist/presets/minimal.d.ts +3 -0
- package/dist/presets/minimal.js +9 -0
- package/dist/presets/minimal.js.map +7 -0
- package/dist/sigmx.auto.js +5 -0
- package/dist/sigmx.standalone.js +1 -0
- package/dist/standalone.d.ts +1 -0
- package/dist/standalone.js +8 -0
- package/dist/standalone.js.map +7 -0
- package/dist/utils/object.d.ts +9 -0
- package/dist/utils/object.js +26 -0
- package/dist/utils/object.js.map +7 -0
- package/dist/utils/text.d.ts +7 -0
- package/dist/utils/text.js +15 -0
- package/dist/utils/text.js.map +7 -0
- package/dist/utils/timing.d.ts +14 -0
- package/dist/utils/timing.js +58 -0
- package/dist/utils/timing.js.map +7 -0
- package/dist/vite.d.ts +42 -0
- package/dist/vite.js +79 -0
- package/dist/vite.js.map +7 -0
- package/package.json +111 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Callum Bonnyman
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# sigmx
|
|
2
|
+
|
|
3
|
+
**htmx on steroids, in a runtime smaller than htmx.**
|
|
4
|
+
|
|
5
|
+
> **Status: an experiment.** sigmx is a personal exploration of what a hypermedia library looks like when it starts from a tiny, plugin-based core. It is inspired by [htmx](https://htmx.org) (the server sends HTML, the browser swaps it in), [Alpine.js](https://alpinejs.dev) (declarative `data-*` attributes with a little reactivity) and [Datastar](https://data-star.dev) (which fused the two and whose attribute syntax sigmx keeps for compatibility). The code is original, the APIs may still change, and it has not been used in production. Try it, break it, and open an issue with what you find.
|
|
6
|
+
|
|
7
|
+
Your server renders HTML, as it always has. The browser asks for more of it with `@get` and `@post`, and sigmx merges the response into the page with a morph that keeps focus, typed input and scroll position. When you want live updates, the same request can return a stream of patches instead. Signals, two-way binding and reactive attributes cover the client-side state. You write `data-*` attributes and register only the plugins you use.
|
|
8
|
+
|
|
9
|
+
- **Tiny.** 4.0 KB core, 8.9 KB with the essentials, 12.0 KB with all 52 plugins (brotli). Zero runtime dependencies.
|
|
10
|
+
- **Server-driven.** Return plain HTML from any endpoint and it is morphed into the page by id, or targeted with a header. Return JSON to merge signals, or an event stream to push many patches over one request, with `Last-Event-ID` reconnects.
|
|
11
|
+
- **Reactive where it matters.** Signals declared in markup; `bind`, `show`, `class`, computed values and effects keep the page in step. No component model, no virtual DOM.
|
|
12
|
+
- **Pay for what you use.** Every directive and function is an opt-in plugin, and auto mode scans your source to bundle exactly the set it finds.
|
|
13
|
+
- **Batteries from the ecosystem.** The Alpine plugins (`collapse`, `mask`, `trap`, `teleport`, …) and htmx extensions (`boost`, `ws`, `remove-me`, …) people bolt on are built in as plugins.
|
|
14
|
+
- **Datastar-compatible markup.** Existing Datastar templates run unchanged.
|
|
15
|
+
|
|
16
|
+
```html
|
|
17
|
+
<div data-signals="{ count: 0, name: '' }">
|
|
18
|
+
<input data-bind:name placeholder="Your name">
|
|
19
|
+
<p data-show="$name">Hello, <span data-text="$name"></span>!</p>
|
|
20
|
+
<button data-on:click="$count++">Clicked <b data-text="$count"></b> times</button>
|
|
21
|
+
<button data-on:click="@post('/save')" data-indicator:saving data-attr:disabled="$saving">Save</button>
|
|
22
|
+
</div>
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Install
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install sigmx
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import { createSigmx } from 'sigmx'
|
|
33
|
+
import { essentials } from 'sigmx/presets/essentials'
|
|
34
|
+
|
|
35
|
+
createSigmx({ plugins: essentials })
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Or pick plugins by hand (`sigmx/plugins` is tree-shakeable), use `sigmx/presets/minimal` or `sigmx/presets/all`, let [auto mode](#auto-mode) choose, or drop in the script-tag build:
|
|
39
|
+
|
|
40
|
+
```html
|
|
41
|
+
<script type="module" src="/sigmx.standalone.js"></script>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Astro users: `npm install sigmx sigmx-astro` and add `sigmx()` to the integrations; see [sdks/astro](sdks/astro).
|
|
45
|
+
|
|
46
|
+
## How it works
|
|
47
|
+
|
|
48
|
+
Attributes are `data-<plugin>[:key][__modifier.arg]="expression"`. Expressions are plain JavaScript in which `$name` is a signal:
|
|
49
|
+
|
|
50
|
+
```html
|
|
51
|
+
<button data-on:click__debounce.300ms="@get('/search')">Search</button>
|
|
52
|
+
<li data-class:active="$page === 3"></li>
|
|
53
|
+
<input data-bind:user.email>
|
|
54
|
+
<p data-text="`${$items.length} items, ${@intl('number', $total, { style: 'currency', currency: 'EUR' })}`"></p>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
State is one JSON-like store addressed by dotted paths and patched with merge-patch semantics: objects merge, `null` removes, arrays notify on `push`. Requests send the store (minus `_`-prefixed paths) and apply whatever comes back, decided by the response's content type:
|
|
58
|
+
|
|
59
|
+
| the server returns | sigmx does |
|
|
60
|
+
|---|---|
|
|
61
|
+
| `text/html` | morphs it into the page: top-level elements by `id`, or a target from the `sigmx-selector` and `sigmx-mode` headers |
|
|
62
|
+
| `application/json` | merges it into the signals |
|
|
63
|
+
| `text/event-stream` | applies each event as it arrives: `patch-elements`, `patch-signals`, or your own handlers |
|
|
64
|
+
|
|
65
|
+
The everyday case is the first row: render a partial with whatever templating you already use and return it. The morph keeps every element whose id appears on both sides, so a re-rendered form keeps what the user typed. Streams are there for progress, chat and dashboards, where one request should keep delivering:
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
event: sigmx-patch-signals
|
|
69
|
+
data: signals {"progress": 40}
|
|
70
|
+
|
|
71
|
+
event: sigmx-patch-elements
|
|
72
|
+
data: selector #log
|
|
73
|
+
data: mode append
|
|
74
|
+
data: elements <li>Validating…</li>
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Plugins
|
|
78
|
+
|
|
79
|
+
| kind | plugins |
|
|
80
|
+
|---|---|
|
|
81
|
+
| state | `signals` `computed` `effect` `ref` `persist` `query-string` |
|
|
82
|
+
| rendering | `text` `html` `show` `class` `style` `attr` `animate` `transition` `collapse` `view-transition` `cloak` |
|
|
83
|
+
| forms | `bind` `custom-validity` `mask` |
|
|
84
|
+
| events | `on` `init` `on-interval` `on-intersect` `on-resize` `on-raf` `on-signal-patch` `match-media` |
|
|
85
|
+
| layout | `teleport` `trap` `scroll-into-view` `remove-me` |
|
|
86
|
+
| server | `@get` `@post` `@put` `@patch` `@delete` `@ws` `boost` `indicator` `patch-elements` `patch-signals` |
|
|
87
|
+
| utilities | `@peek` `@setAll` `@toggleAll` `@fit` `@clipboard` `@intl` `@dispatch` `@confirm` `json-signals` `replace-url` |
|
|
88
|
+
|
|
89
|
+
Every plugin has a page in the docs with a live example and its measured size.
|
|
90
|
+
|
|
91
|
+
## Sizes
|
|
92
|
+
|
|
93
|
+
Brotli, measured from the real source by `npm run size` and `npm run compare` (nothing is quoted from memory):
|
|
94
|
+
|
|
95
|
+
| | brotli |
|
|
96
|
+
|---|---:|
|
|
97
|
+
| sigmx core | 4.0 KB |
|
|
98
|
+
| sigmx essentials (state, rendering, forms, requests, morph) | 8.9 KB |
|
|
99
|
+
| sigmx everything, 52 plugins | 12.0 KB |
|
|
100
|
+
| htmx 2.0.10 | 14.6 KB |
|
|
101
|
+
| Datastar 1.0.3, free bundle (23 plugins) | 11.8 KB |
|
|
102
|
+
| Alpine.js 3.17.1 | 17.6 KB |
|
|
103
|
+
|
|
104
|
+
Precompiling expressions at build time removes the runtime compiler and any use of `new Function`, taking the core to 3.6 KB and making strict CSP trivial.
|
|
105
|
+
|
|
106
|
+
## Auto mode
|
|
107
|
+
|
|
108
|
+
Like a CSS framework scanning for class names, sigmx can scan your source for the directives and functions you use and bundle only those, with an allowlist and support for your own plugins:
|
|
109
|
+
|
|
110
|
+
```js
|
|
111
|
+
// vite.config.js
|
|
112
|
+
import { sigmxAuto } from 'sigmx/vite'
|
|
113
|
+
export default { plugins: [sigmxAuto({ always: ['signals'], custom: { upper: 'src/plugins/upper.ts' } })] }
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
import { createSigmx } from 'sigmx'
|
|
118
|
+
import { plugins } from 'virtual:sigmx-plugins'
|
|
119
|
+
createSigmx({ plugins })
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Without Vite: `npx sigmx scan src --out src/sigmx-plugins.js`. In Astro: `sigmx({ plugins: 'auto' })`.
|
|
123
|
+
|
|
124
|
+
## Writing a plugin
|
|
125
|
+
|
|
126
|
+
```ts
|
|
127
|
+
import { attribute } from 'sigmx'
|
|
128
|
+
|
|
129
|
+
export const upper = attribute({
|
|
130
|
+
name: 'upper',
|
|
131
|
+
value: 'required',
|
|
132
|
+
mount({ el, evaluate, effect }) {
|
|
133
|
+
effect(() => { el.textContent = String(evaluate()).toUpperCase() })
|
|
134
|
+
},
|
|
135
|
+
})
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Plugins are plain objects; nothing registers on import. The mount context gives you `evaluate`, `effect`, `listen`, `cleanup`, the store and the runtime, and everything registered through it is torn down when the attribute or element goes away. Functions (`@name()`) and server-event handlers work the same way with `action()` and `handler()`.
|
|
139
|
+
|
|
140
|
+
## Documentation
|
|
141
|
+
|
|
142
|
+
The full documentation, guides and live examples live in [`website/`](website), built with Astro and Starlight:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
cd website && npm install && npm run dev
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Repository
|
|
149
|
+
|
|
150
|
+
```
|
|
151
|
+
src/kernel/ reactive graph, store, expression compiler, runtime (DOM-free core)
|
|
152
|
+
src/plugins/ directives, functions, server-event handlers, the morph
|
|
153
|
+
src/presets/ minimal, essentials, all
|
|
154
|
+
sdks/astro/ sigmx-astro: integration, server helpers, auto mode, precompilation
|
|
155
|
+
website/ documentation site
|
|
156
|
+
scripts/ build, size and comparison measurements, dev server
|
|
157
|
+
tests/ node:test suite for the core; browser suite under tests/browser
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
npm install
|
|
162
|
+
npm run check # type-check
|
|
163
|
+
npm test # build, then node:test over the DOM-free core
|
|
164
|
+
npm run dev # dev server with SSE and WebSocket endpoints; open /tests/browser/index.html
|
|
165
|
+
npm run size # bundle sizes for representative plugin sets
|
|
166
|
+
npm run compare # sizes of htmx, Datastar and Alpine at pinned versions
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Contributions are welcome. Open an issue first for anything larger than a fix, keep plugins as small as their reference-page size table suggests, and run both test suites before a pull request.
|
|
170
|
+
|
|
171
|
+
## Licence
|
|
172
|
+
|
|
173
|
+
[MIT](LICENSE). sigmx is an independent implementation: its attribute syntax is compatible with Datastar's public API so that markup can be migrated, and it contains no Datastar code.
|
package/dist/auto.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const sigmx: import("./core/types.js").Sigmx;
|
package/dist/auto.js
ADDED
package/dist/auto.js.map
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/auto.ts"],
|
|
4
|
+
"sourcesContent": ["// Script-tag entry: registers every plugin and starts on DOM ready.\nimport { createSigmx } from './core/index.js'\nimport { all } from './presets/all.js'\n\nexport const sigmx = createSigmx({ plugins: all })\n"],
|
|
5
|
+
"mappings": "AACA,SAAS,mBAAmB;AAC5B,SAAS,WAAW;AAEb,MAAM,QAAQ,YAAY,EAAE,SAAS,IAAI,CAAC;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { writeFileSync } from "node:fs";
|
|
3
|
+
import { generatePluginsModule } from "./kernel/scan.js";
|
|
4
|
+
import { scanProject } from "./vite.js";
|
|
5
|
+
const args = process.argv.slice(2);
|
|
6
|
+
const cmd = args.shift();
|
|
7
|
+
const opt = (name) => {
|
|
8
|
+
const i = args.indexOf(`--${name}`);
|
|
9
|
+
return i < 0 ? void 0 : args.splice(i, 2)[1];
|
|
10
|
+
};
|
|
11
|
+
if (cmd !== "scan") {
|
|
12
|
+
console.log("usage: sigmx scan [dirs...] [--out src/sigmx-plugins.js] [--always signals,httpGet] [--custom upper=src/plugins/upper.ts] [--prefix data-,hx-]");
|
|
13
|
+
process.exit(cmd ? 1 : 0);
|
|
14
|
+
}
|
|
15
|
+
const out = opt("out");
|
|
16
|
+
const always = opt("always")?.split(",").filter(Boolean);
|
|
17
|
+
const prefixes = opt("prefix")?.split(",").filter(Boolean);
|
|
18
|
+
const custom = Object.fromEntries((opt("custom")?.split(",") ?? []).map((pair) => pair.split("=")));
|
|
19
|
+
const sel = scanProject({ include: args.length ? args : void 0, always, prefixes, custom });
|
|
20
|
+
const code = generatePluginsModule(sel);
|
|
21
|
+
if (out) {
|
|
22
|
+
writeFileSync(out, code);
|
|
23
|
+
console.log(`wrote ${out}: ${sel.plugins.length} plugins, ${sel.unused.length} unused`);
|
|
24
|
+
} else process.stdout.write(code);
|
|
25
|
+
for (const [exp, why] of Object.entries(sel.reasons)) console.error(` ${exp.padEnd(16)} ${why}`);
|
|
26
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/cli.ts"],
|
|
4
|
+
"sourcesContent": ["#!/usr/bin/env node\n// `sigmx scan [dirs...] [--out file] [--always a,b] [--custom name=path,...] [--prefix data-,hx-]`\nimport { writeFileSync } from 'node:fs'\nimport { generatePluginsModule } from './kernel/scan.js'\nimport { scanProject } from './vite.js'\n\nconst args = process.argv.slice(2)\nconst cmd = args.shift()\nconst opt = (name: string) => {\n const i = args.indexOf(`--${name}`)\n return i < 0 ? undefined : args.splice(i, 2)[1]\n}\nif (cmd !== 'scan') {\n console.log('usage: sigmx scan [dirs...] [--out src/sigmx-plugins.js] [--always signals,httpGet] [--custom upper=src/plugins/upper.ts] [--prefix data-,hx-]')\n process.exit(cmd ? 1 : 0)\n}\nconst out = opt('out')\nconst always = opt('always')?.split(',').filter(Boolean)\nconst prefixes = opt('prefix')?.split(',').filter(Boolean)\nconst custom = Object.fromEntries((opt('custom')?.split(',') ?? []).map((pair) => pair.split('=') as [string, string]))\nconst sel = scanProject({ include: args.length ? args : undefined, always, prefixes, custom })\nconst code = generatePluginsModule(sel)\nif (out) {\n writeFileSync(out, code)\n console.log(`wrote ${out}: ${sel.plugins.length} plugins, ${sel.unused.length} unused`)\n} else process.stdout.write(code)\nfor (const [exp, why] of Object.entries(sel.reasons)) console.error(` ${exp.padEnd(16)} ${why}`)\n"],
|
|
5
|
+
"mappings": ";AAEA,SAAS,qBAAqB;AAC9B,SAAS,6BAA6B;AACtC,SAAS,mBAAmB;AAE5B,MAAM,OAAO,QAAQ,KAAK,MAAM,CAAC;AACjC,MAAM,MAAM,KAAK,MAAM;AACvB,MAAM,MAAM,CAAC,SAAiB;AAC5B,QAAM,IAAI,KAAK,QAAQ,KAAK,IAAI,EAAE;AAClC,SAAO,IAAI,IAAI,SAAY,KAAK,OAAO,GAAG,CAAC,EAAE,CAAC;AAChD;AACA,IAAI,QAAQ,QAAQ;AAClB,UAAQ,IAAI,gJAAgJ;AAC5J,UAAQ,KAAK,MAAM,IAAI,CAAC;AAC1B;AACA,MAAM,MAAM,IAAI,KAAK;AACrB,MAAM,SAAS,IAAI,QAAQ,GAAG,MAAM,GAAG,EAAE,OAAO,OAAO;AACvD,MAAM,WAAW,IAAI,QAAQ,GAAG,MAAM,GAAG,EAAE,OAAO,OAAO;AACzD,MAAM,SAAS,OAAO,aAAa,IAAI,QAAQ,GAAG,MAAM,GAAG,KAAK,CAAC,GAAG,IAAI,CAAC,SAAS,KAAK,MAAM,GAAG,CAAqB,CAAC;AACtH,MAAM,MAAM,YAAY,EAAE,SAAS,KAAK,SAAS,OAAO,QAAW,QAAQ,UAAU,OAAO,CAAC;AAC7F,MAAM,OAAO,sBAAsB,GAAG;AACtC,IAAI,KAAK;AACP,gBAAc,KAAK,IAAI;AACvB,UAAQ,IAAI,SAAS,GAAG,KAAK,IAAI,QAAQ,MAAM,aAAa,IAAI,OAAO,MAAM,SAAS;AACxF,MAAO,SAAQ,OAAO,MAAM,IAAI;AAChC,WAAW,CAAC,KAAK,GAAG,KAAK,OAAO,QAAQ,IAAI,OAAO,EAAG,SAAQ,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC,IAAI,GAAG,EAAE;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Compiler } from './expr.js';
|
|
2
|
+
/**
|
|
3
|
+
* Compiler for pages with a strict Content-Security-Policy: compiles through a nonce-bearing
|
|
4
|
+
* inline `<script>` (and a Trusted Types policy when present) instead of `new Function`.
|
|
5
|
+
* Pass as `createSigmx({ compile: cspCompiler(nonce) })`.
|
|
6
|
+
*/
|
|
7
|
+
export declare const cspCompiler: (nonce: string) => Compiler;
|
package/dist/core/csp.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const cspCompiler = (nonce) => {
|
|
2
|
+
if (!nonce) throw new Error("cspCompiler needs the page nonce");
|
|
3
|
+
const tt = window.trustedTypes;
|
|
4
|
+
const policy = tt?.createPolicy("sigmx", { createScript: (s) => s });
|
|
5
|
+
return (params, body) => {
|
|
6
|
+
const script = document.createElement("script");
|
|
7
|
+
script.nonce = nonce;
|
|
8
|
+
const text = `document.currentScript.f=function(${params.join(",")}){${body}}`;
|
|
9
|
+
script.text = policy ? policy.createScript(text) : text;
|
|
10
|
+
document.head.append(script);
|
|
11
|
+
script.remove();
|
|
12
|
+
const fn = script.f;
|
|
13
|
+
if (!fn) throw new Error("the Content-Security-Policy blocked expression compilation");
|
|
14
|
+
return fn;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
export {
|
|
18
|
+
cspCompiler
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=csp.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/core/csp.ts"],
|
|
4
|
+
"sourcesContent": ["import type { Compiler } from './expr.js'\n\n/**\n * Compiler for pages with a strict Content-Security-Policy: compiles through a nonce-bearing\n * inline `<script>` (and a Trusted Types policy when present) instead of `new Function`.\n * Pass as `createSigmx({ compile: cspCompiler(nonce) })`.\n */\nexport const cspCompiler = (nonce: string): Compiler => {\n if (!nonce) throw new Error('cspCompiler needs the page nonce')\n const tt = (window as any).trustedTypes\n const policy = tt?.createPolicy('sigmx', { createScript: (s: string) => s })\n return (params, body) => {\n const script = document.createElement('script')\n script.nonce = nonce\n const text = `document.currentScript.f=function(${params.join(',')}){${body}}`\n script.text = policy ? policy.createScript(text) : text\n document.head.append(script)\n script.remove()\n const fn = (script as any).f\n if (!fn) throw new Error('the Content-Security-Policy blocked expression compilation')\n return fn\n }\n}\n"],
|
|
5
|
+
"mappings": "AAOO,MAAM,cAAc,CAAC,UAA4B;AACtD,MAAI,CAAC,MAAO,OAAM,IAAI,MAAM,kCAAkC;AAC9D,QAAM,KAAM,OAAe;AAC3B,QAAM,SAAS,IAAI,aAAa,SAAS,EAAE,cAAc,CAAC,MAAc,EAAE,CAAC;AAC3E,SAAO,CAAC,QAAQ,SAAS;AACvB,UAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,WAAO,QAAQ;AACf,UAAM,OAAO,qCAAqC,OAAO,KAAK,GAAG,CAAC,KAAK,IAAI;AAC3E,WAAO,OAAO,SAAS,OAAO,aAAa,IAAI,IAAI;AACnD,aAAS,KAAK,OAAO,MAAM;AAC3B,WAAO,OAAO;AACd,UAAM,KAAM,OAAe;AAC3B,QAAI,CAAC,GAAI,OAAM,IAAI,MAAM,4DAA4D;AACrF,WAAO;AAAA,EACT;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Mods, Sigmx, SigmxOptions } from './types.js';
|
|
2
|
+
type Parsed = {
|
|
3
|
+
plugin: string;
|
|
4
|
+
key: string | undefined;
|
|
5
|
+
mods: Mods;
|
|
6
|
+
};
|
|
7
|
+
/** 'on:click__debounce.300ms__prevent' → { plugin: 'on', key: 'click', mods: {debounce: ['300ms'], prevent: []} } */
|
|
8
|
+
export declare const parseAttr: (raw: string) => Parsed;
|
|
9
|
+
export declare const createSigmx: (options?: SigmxOptions) => Sigmx;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { compile, functionCompiler } from "./expr.js";
|
|
2
|
+
import { effect } from "./signals.js";
|
|
3
|
+
import { createStore } from "./store.js";
|
|
4
|
+
import { recase } from "../utils/text.js";
|
|
5
|
+
const isEl = (n) => n instanceof HTMLElement || n instanceof SVGElement || n instanceof MathMLElement;
|
|
6
|
+
const parsed = /* @__PURE__ */ new Map();
|
|
7
|
+
const parseAttr = (raw) => {
|
|
8
|
+
let p = parsed.get(raw);
|
|
9
|
+
if (!p) {
|
|
10
|
+
const [head, ...modParts] = raw.split("__");
|
|
11
|
+
const i = head.indexOf(":");
|
|
12
|
+
const mods = /* @__PURE__ */ new Map();
|
|
13
|
+
for (const m of modParts) {
|
|
14
|
+
const [name, ...args] = m.split(".");
|
|
15
|
+
mods.set(name, args);
|
|
16
|
+
}
|
|
17
|
+
p = { plugin: i < 0 ? head : head.slice(0, i), key: i < 0 ? void 0 : head.slice(i + 1), mods };
|
|
18
|
+
parsed.set(raw, p);
|
|
19
|
+
}
|
|
20
|
+
return p;
|
|
21
|
+
};
|
|
22
|
+
const fail = (message, info) => Object.assign(new Error(message), { info });
|
|
23
|
+
const createSigmx = (options = {}) => {
|
|
24
|
+
const prefixes = [].concat(options.prefix ?? "data-");
|
|
25
|
+
const store = options.store ?? createStore();
|
|
26
|
+
const compiler = options.compile ?? functionCompiler;
|
|
27
|
+
const onError = options.onError ?? ((e, info) => console.error(e, info));
|
|
28
|
+
const attributes = /* @__PURE__ */ Object.create(null);
|
|
29
|
+
const actions = /* @__PURE__ */ Object.create(null);
|
|
30
|
+
const handlers = /* @__PURE__ */ Object.create(null);
|
|
31
|
+
const runtime = {
|
|
32
|
+
prefixes,
|
|
33
|
+
attr: (name) => prefixes[0] + name,
|
|
34
|
+
store,
|
|
35
|
+
compiler,
|
|
36
|
+
emit: (type, detail) => document.dispatchEvent(new CustomEvent(`sigmx-${type}`, { detail })),
|
|
37
|
+
call: (name, ctx, args) => {
|
|
38
|
+
const a = actions[name];
|
|
39
|
+
if (!a) throw ctx.error(`unknown action @${name}`);
|
|
40
|
+
return a.call(ctx, ...args);
|
|
41
|
+
},
|
|
42
|
+
attributes,
|
|
43
|
+
actions,
|
|
44
|
+
handlers
|
|
45
|
+
};
|
|
46
|
+
const mounted = /* @__PURE__ */ new Map();
|
|
47
|
+
const roots = /* @__PURE__ */ new Set();
|
|
48
|
+
const ignoreSelf = prefixes.map((p) => `[${p}ignore__self]`).join();
|
|
49
|
+
const ignoreAny = prefixes.map((p) => `[${p}ignore]`).join();
|
|
50
|
+
const ignored = (el) => el.matches(ignoreSelf) || !!el.closest(ignoreAny);
|
|
51
|
+
const strip = (attr) => {
|
|
52
|
+
for (const p of prefixes) if (attr.startsWith(p)) return attr.slice(p.length);
|
|
53
|
+
};
|
|
54
|
+
const unmount = (els) => {
|
|
55
|
+
for (const el of els) {
|
|
56
|
+
const m = mounted.get(el);
|
|
57
|
+
if (m && mounted.delete(el)) for (const d of m.values()) d();
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
const unmountAttr = (el, attr) => {
|
|
61
|
+
const m = mounted.get(el);
|
|
62
|
+
m?.get(attr)?.();
|
|
63
|
+
m?.delete(attr);
|
|
64
|
+
};
|
|
65
|
+
const actionsFor = (el, evt, error) => {
|
|
66
|
+
const ctx = { el, evt, store, runtime, error };
|
|
67
|
+
return new Proxy(/* @__PURE__ */ Object.create(null), { get: (_, name) => (...args) => runtime.call(name, ctx, args) });
|
|
68
|
+
};
|
|
69
|
+
const mount = (el, attr, raw, value, only) => {
|
|
70
|
+
const { plugin: name, key, mods } = parseAttr(raw);
|
|
71
|
+
const plugin = attributes[name];
|
|
72
|
+
if (!plugin || only && !only.has(name)) return;
|
|
73
|
+
unmountAttr(el, attr);
|
|
74
|
+
const disposers = [];
|
|
75
|
+
const info = { plugin: name, el, attr };
|
|
76
|
+
const error = (message, extra) => fail(`${prefixes[0]}${name}: ${message}`, { ...info, ...extra });
|
|
77
|
+
const report = (e) => onError(e, info);
|
|
78
|
+
let fn;
|
|
79
|
+
const ctx = {
|
|
80
|
+
el,
|
|
81
|
+
plugin: name,
|
|
82
|
+
attr,
|
|
83
|
+
key,
|
|
84
|
+
value,
|
|
85
|
+
mods,
|
|
86
|
+
cased: (style = "camel") => recase(key ?? "", mods.get("case")?.[0] || style),
|
|
87
|
+
evaluate: (evt, ...args) => {
|
|
88
|
+
fn ??= compile(compiler, value, ["el", "evt", ...plugin.args ?? []], plugin.returns ?? true);
|
|
89
|
+
return fn(store.scope, actionsFor(el, evt, error), el, evt, ...args);
|
|
90
|
+
},
|
|
91
|
+
effect: (f) => disposers.push(effect(f, report)),
|
|
92
|
+
listen: (target, type, f, opts) => {
|
|
93
|
+
const h = (e) => {
|
|
94
|
+
try {
|
|
95
|
+
f(e);
|
|
96
|
+
} catch (err) {
|
|
97
|
+
report(err);
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
target.addEventListener(type, h, opts);
|
|
101
|
+
disposers.push(() => target.removeEventListener(type, h, opts));
|
|
102
|
+
},
|
|
103
|
+
cleanup: (f) => disposers.push(f),
|
|
104
|
+
error,
|
|
105
|
+
store,
|
|
106
|
+
runtime
|
|
107
|
+
};
|
|
108
|
+
let m = mounted.get(el);
|
|
109
|
+
if (!m) mounted.set(el, m = /* @__PURE__ */ new Map());
|
|
110
|
+
m.set(attr, () => {
|
|
111
|
+
for (const d of disposers.splice(0)) d();
|
|
112
|
+
});
|
|
113
|
+
try {
|
|
114
|
+
if (plugin.key === "required" && !key) throw error("needs a key, e.g. :name");
|
|
115
|
+
if (plugin.key === "forbidden" && key) throw error("does not take a key");
|
|
116
|
+
if (plugin.value === "required" && !value) throw error("needs a value");
|
|
117
|
+
if (plugin.value === "forbidden" && value) throw error("does not take a value");
|
|
118
|
+
const r = plugin.mount(ctx);
|
|
119
|
+
if (typeof r === "function") disposers.push(r);
|
|
120
|
+
} catch (e) {
|
|
121
|
+
report(e);
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
const mountEls = (els, only) => {
|
|
125
|
+
for (const el of els) {
|
|
126
|
+
if (ignored(el)) continue;
|
|
127
|
+
for (const { name, value } of [...el.attributes]) {
|
|
128
|
+
const raw = strip(name);
|
|
129
|
+
if (raw) mount(el, name, raw, value, only);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
const observer = new MutationObserver((records) => {
|
|
134
|
+
for (const { type, target, attributeName, addedNodes, removedNodes } of records) {
|
|
135
|
+
if (type === "childList") {
|
|
136
|
+
for (const n of removedNodes) if (isEl(n)) unmount([n, ...n.querySelectorAll("*")]);
|
|
137
|
+
for (const n of addedNodes) if (isEl(n)) mountEls([n, ...n.querySelectorAll("*")]);
|
|
138
|
+
} else if (attributeName && isEl(target) && !ignored(target)) {
|
|
139
|
+
const raw = strip(attributeName);
|
|
140
|
+
if (!raw) continue;
|
|
141
|
+
const value = target.getAttribute(attributeName);
|
|
142
|
+
if (value === null) unmountAttr(target, attributeName);
|
|
143
|
+
else mount(target, attributeName, raw, value);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
let ready = false;
|
|
148
|
+
const apply = (root = document.documentElement, observe = true, only) => {
|
|
149
|
+
mountEls([...isEl(root) ? [root] : [], ...root.querySelectorAll("*")], only);
|
|
150
|
+
if (observe && !roots.has(root)) {
|
|
151
|
+
observer.observe(root, { subtree: true, childList: true, attributes: true });
|
|
152
|
+
roots.add(root);
|
|
153
|
+
}
|
|
154
|
+
if (!ready && roots.has(document.documentElement)) {
|
|
155
|
+
ready = true;
|
|
156
|
+
runtime.emit("ready");
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
const use = (...plugins) => {
|
|
160
|
+
const added = /* @__PURE__ */ new Set();
|
|
161
|
+
for (const p of plugins) {
|
|
162
|
+
if (p.type === "attribute") {
|
|
163
|
+
attributes[p.name] = p;
|
|
164
|
+
added.add(p.name);
|
|
165
|
+
} else if (p.type === "action") actions[p.name] = p;
|
|
166
|
+
else handlers[p.name] = p;
|
|
167
|
+
}
|
|
168
|
+
if (added.size) for (const r of roots) apply(r, false, added);
|
|
169
|
+
};
|
|
170
|
+
const stopPatches = store.onPatch((patch) => runtime.emit("signal-patch", patch));
|
|
171
|
+
use(...options.plugins ?? []);
|
|
172
|
+
const sigmx = {
|
|
173
|
+
runtime,
|
|
174
|
+
store,
|
|
175
|
+
get $() {
|
|
176
|
+
return store.$;
|
|
177
|
+
},
|
|
178
|
+
apply: (root, observe) => apply(root, observe),
|
|
179
|
+
use,
|
|
180
|
+
destroy: () => {
|
|
181
|
+
observer.disconnect();
|
|
182
|
+
stopPatches();
|
|
183
|
+
unmount([...mounted.keys()]);
|
|
184
|
+
roots.clear();
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
if (options.autoStart ?? true) {
|
|
188
|
+
if (document.readyState === "loading") {
|
|
189
|
+
document.addEventListener("DOMContentLoaded", () => sigmx.apply(), { once: true });
|
|
190
|
+
} else {
|
|
191
|
+
sigmx.apply();
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return sigmx;
|
|
195
|
+
};
|
|
196
|
+
export {
|
|
197
|
+
createSigmx,
|
|
198
|
+
parseAttr
|
|
199
|
+
};
|
|
200
|
+
//# sourceMappingURL=engine.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/core/engine.ts"],
|
|
4
|
+
"sourcesContent": ["import { compile, functionCompiler } from './expr.js'\nimport { effect } from './signals.js'\nimport { createStore } from './store.js'\nimport type { ActionCtx, AttributePlugin, Ctx, El, Mods, Plugin, Runtime, Sigmx, SigmxOptions } from './types.js'\nimport { recase, type CaseStyle } from '../utils/text.js'\n\nconst isEl = (n: Node): n is El => n instanceof HTMLElement || n instanceof SVGElement || n instanceof MathMLElement\n\ntype Parsed = { plugin: string; key: string | undefined; mods: Mods }\nconst parsed = new Map<string, Parsed>()\n\n/** 'on:click__debounce.300ms__prevent' \u2192 { plugin: 'on', key: 'click', mods: {debounce: ['300ms'], prevent: []} } */\nexport const parseAttr = (raw: string): Parsed => {\n let p = parsed.get(raw)\n if (!p) {\n const [head, ...modParts] = raw.split('__')\n const i = head.indexOf(':')\n const mods: Mods = new Map()\n for (const m of modParts) {\n const [name, ...args] = m.split('.')\n mods.set(name, args)\n }\n p = { plugin: i < 0 ? head : head.slice(0, i), key: i < 0 ? undefined : head.slice(i + 1), mods }\n parsed.set(raw, p)\n }\n return p\n}\n\nconst fail = (message: string, info: Record<string, unknown>): Error =>\n Object.assign(new Error(message), { info })\n\nexport const createSigmx = (options: SigmxOptions = {}): Sigmx => {\n const prefixes = ([] as string[]).concat(options.prefix ?? 'data-')\n const store = options.store ?? createStore()\n const compiler = options.compile ?? functionCompiler\n const onError = options.onError ?? ((e, info) => console.error(e, info))\n const attributes: Record<string, AttributePlugin> = Object.create(null)\n const actions: Runtime['actions'] = Object.create(null)\n const handlers: Runtime['handlers'] = Object.create(null)\n\n const runtime: Runtime = {\n prefixes,\n attr: (name) => prefixes[0] + name,\n store,\n compiler,\n emit: (type, detail) => document.dispatchEvent(new CustomEvent(`sigmx-${type}`, { detail })),\n call: (name, ctx, args) => {\n const a = actions[name]\n if (!a) throw ctx.error(`unknown action @${name}`)\n return a.call(ctx, ...args)\n },\n attributes,\n actions,\n handlers,\n }\n\n // element \u2192 attribute name \u2192 dispose\n const mounted = new Map<El, Map<string, () => void>>()\n const roots = new Set<El | ShadowRoot>()\n const ignoreSelf = prefixes.map((p) => `[${p}ignore__self]`).join()\n const ignoreAny = prefixes.map((p) => `[${p}ignore]`).join()\n const ignored = (el: El) => el.matches(ignoreSelf) || !!el.closest(ignoreAny)\n\n const strip = (attr: string): string | undefined => {\n for (const p of prefixes) if (attr.startsWith(p)) return attr.slice(p.length)\n }\n\n const unmount = (els: Iterable<El>): void => {\n for (const el of els) {\n const m = mounted.get(el)\n if (m && mounted.delete(el)) for (const d of m.values()) d()\n }\n }\n\n const unmountAttr = (el: El, attr: string): void => {\n const m = mounted.get(el)\n m?.get(attr)?.()\n m?.delete(attr)\n }\n\n const actionsFor = (el: El, evt: Event | undefined, error: Ctx['error']) => {\n const ctx: ActionCtx = { el, evt, store, runtime, error }\n return new Proxy(Object.create(null), { get: (_, name: string) => (...args: any[]) => runtime.call(name, ctx, args) })\n }\n\n const mount = (el: El, attr: string, raw: string, value: string, only?: Set<string>): void => {\n const { plugin: name, key, mods } = parseAttr(raw)\n const plugin = attributes[name]\n if (!plugin || (only && !only.has(name))) return\n unmountAttr(el, attr)\n\n const disposers: (() => void)[] = []\n const info = { plugin: name, el, attr }\n const error: Ctx['error'] = (message, extra) => fail(`${prefixes[0]}${name}: ${message}`, { ...info, ...extra })\n const report = (e: unknown) => onError(e, info)\n let fn: ((...args: any[]) => any) | undefined\n const ctx: Ctx = {\n el,\n plugin: name,\n attr,\n key,\n value,\n mods,\n cased: (style = 'camel') => recase(key ?? '', (mods.get('case')?.[0] as CaseStyle) || style),\n evaluate: (evt, ...args) => {\n fn ??= compile(compiler, value, ['el', 'evt', ...(plugin.args ?? [])], plugin.returns ?? true)\n return fn(store.scope, actionsFor(el, evt, error), el, evt, ...args)\n },\n effect: (f) => disposers.push(effect(f, report)),\n listen: (target, type, f, opts) => {\n const h = (e: Event) => {\n try {\n f(e)\n } catch (err) {\n report(err)\n }\n }\n target.addEventListener(type, h, opts)\n disposers.push(() => target.removeEventListener(type, h, opts))\n },\n cleanup: (f) => disposers.push(f),\n error,\n store,\n runtime,\n }\n\n let m = mounted.get(el)\n if (!m) mounted.set(el, (m = new Map()))\n m.set(attr, () => {\n for (const d of disposers.splice(0)) d()\n })\n\n try {\n if (plugin.key === 'required' && !key) throw error('needs a key, e.g. :name')\n if (plugin.key === 'forbidden' && key) throw error('does not take a key')\n if (plugin.value === 'required' && !value) throw error('needs a value')\n if (plugin.value === 'forbidden' && value) throw error('does not take a value')\n const r = plugin.mount(ctx)\n if (typeof r === 'function') disposers.push(r)\n } catch (e) {\n report(e)\n }\n }\n\n const mountEls = (els: Iterable<El>, only?: Set<string>): void => {\n for (const el of els) {\n if (ignored(el)) continue\n for (const { name, value } of [...el.attributes]) {\n const raw = strip(name)\n if (raw) mount(el, name, raw, value, only)\n }\n }\n }\n\n const observer = new MutationObserver((records) => {\n for (const { type, target, attributeName, addedNodes, removedNodes } of records) {\n if (type === 'childList') {\n for (const n of removedNodes) if (isEl(n)) unmount([n, ...n.querySelectorAll<El>('*')])\n for (const n of addedNodes) if (isEl(n)) mountEls([n, ...n.querySelectorAll<El>('*')])\n } else if (attributeName && isEl(target) && !ignored(target)) {\n const raw = strip(attributeName)\n if (!raw) continue\n const value = target.getAttribute(attributeName)\n if (value === null) unmountAttr(target, attributeName)\n else mount(target, attributeName, raw, value)\n }\n }\n })\n\n let ready = false\n const apply = (root: El | ShadowRoot = document.documentElement, observe = true, only?: Set<string>) => {\n mountEls([...(isEl(root) ? [root] : []), ...root.querySelectorAll<El>('*')], only)\n if (observe && !roots.has(root)) {\n observer.observe(root, { subtree: true, childList: true, attributes: true })\n roots.add(root)\n }\n if (!ready && roots.has(document.documentElement)) {\n ready = true\n runtime.emit('ready')\n }\n }\n\n const use = (...plugins: Plugin[]): void => {\n const added = new Set<string>()\n for (const p of plugins) {\n if (p.type === 'attribute') {\n attributes[p.name] = p\n added.add(p.name)\n } else if (p.type === 'action') actions[p.name] = p\n else handlers[p.name] = p\n }\n if (added.size) for (const r of roots) apply(r, false, added)\n }\n\n const stopPatches = store.onPatch((patch) => runtime.emit('signal-patch', patch))\n use(...(options.plugins ?? []))\n\n const sigmx: Sigmx = {\n runtime,\n store,\n get $() {\n return store.$\n },\n apply: (root, observe) => apply(root, observe),\n use,\n destroy: () => {\n observer.disconnect()\n stopPatches()\n unmount([...mounted.keys()])\n roots.clear()\n },\n }\n\n if (options.autoStart ?? true) {\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', () => sigmx.apply(), { once: true })\n } else {\n sigmx.apply()\n }\n }\n return sigmx\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type Compiler = (params: string[], body: string) => (...args: any[]) => any;
|
|
2
|
+
/** Default compiler: `new Function`. Not usable under a strict CSP; see `cspCompiler`. */
|
|
3
|
+
export declare const functionCompiler: Compiler;
|
|
4
|
+
/** Replace `@name(` with `__a.name(` outside strings and comments. */
|
|
5
|
+
export declare const rewriteActions: (src: string) => string;
|
|
6
|
+
/** Split on top-level semicolons. */
|
|
7
|
+
export declare const splitStatements: (src: string) => string[];
|
|
8
|
+
/**
|
|
9
|
+
* Compile an expression to `(scope, actions, ...params) => any`. With `returns`, the value of the
|
|
10
|
+
* last statement is returned, so `$a = 1; $a * 2` works. Compiled functions are cached by source.
|
|
11
|
+
*/
|
|
12
|
+
export declare const compile: (compiler: Compiler, src: string, params: string[], returns: boolean) => ((...args: any[]) => any);
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
const functionCompiler = (params, body) => Function(...params, body);
|
|
2
|
+
const cache = /* @__PURE__ */ new Map();
|
|
3
|
+
const isIdent = (c) => /[A-Za-z0-9_$]/.test(c);
|
|
4
|
+
const scan = (src, onCode) => {
|
|
5
|
+
let i = 0;
|
|
6
|
+
const n = src.length;
|
|
7
|
+
while (i < n) {
|
|
8
|
+
const c = src[i];
|
|
9
|
+
const next = src[i + 1];
|
|
10
|
+
if (c === '"' || c === "'" || c === "`") {
|
|
11
|
+
let j = i + 1;
|
|
12
|
+
while (j < n && src[j] !== c) j += src[j] === "\\" ? 2 : 1;
|
|
13
|
+
onCode(src.slice(i, j + 1), i);
|
|
14
|
+
i = j + 1;
|
|
15
|
+
} else if (c === "/" && next === "/") {
|
|
16
|
+
const j = src.indexOf("\n", i);
|
|
17
|
+
i = j < 0 ? n : j;
|
|
18
|
+
} else if (c === "/" && next === "*") {
|
|
19
|
+
const j = src.indexOf("*/", i + 2);
|
|
20
|
+
i = j < 0 ? n : j + 2;
|
|
21
|
+
} else {
|
|
22
|
+
const skip = onCode(c, i);
|
|
23
|
+
i += skip || 1;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
const rewriteActions = (src) => {
|
|
28
|
+
let out = "";
|
|
29
|
+
scan(src, (c, i) => {
|
|
30
|
+
if (c === "@") {
|
|
31
|
+
let j = i + 1;
|
|
32
|
+
while (j < src.length && isIdent(src[j])) j++;
|
|
33
|
+
let k = j;
|
|
34
|
+
while (k < src.length && /\s/.test(src[k])) k++;
|
|
35
|
+
if (j > i + 1 && src[k] === "(") {
|
|
36
|
+
out += `__a.${src.slice(i + 1, j)}(`;
|
|
37
|
+
return k + 1 - i;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
out += c;
|
|
41
|
+
});
|
|
42
|
+
return out;
|
|
43
|
+
};
|
|
44
|
+
const splitStatements = (src) => {
|
|
45
|
+
const parts = [];
|
|
46
|
+
let depth = 0;
|
|
47
|
+
let start = 0;
|
|
48
|
+
scan(src, (c, i) => {
|
|
49
|
+
if (c.length > 1) return;
|
|
50
|
+
if ("([{".includes(c)) depth++;
|
|
51
|
+
else if (")]}".includes(c)) depth--;
|
|
52
|
+
else if (c === ";" && !depth) {
|
|
53
|
+
parts.push(src.slice(start, i));
|
|
54
|
+
start = i + 1;
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
parts.push(src.slice(start));
|
|
58
|
+
return parts.filter((p) => p.trim());
|
|
59
|
+
};
|
|
60
|
+
const compile = (compiler, src, params, returns) => {
|
|
61
|
+
const key = `${+returns}|${params.join()}|${src}`;
|
|
62
|
+
const hit = cache.get(key);
|
|
63
|
+
if (hit) return hit;
|
|
64
|
+
const code = rewriteActions(src.trim());
|
|
65
|
+
const make = (body) => compiler(["$", "__a", ...params], `with($){${body}
|
|
66
|
+
}`);
|
|
67
|
+
let fn;
|
|
68
|
+
if (!returns) {
|
|
69
|
+
fn = make(code);
|
|
70
|
+
} else {
|
|
71
|
+
try {
|
|
72
|
+
fn = make(`return(${code}
|
|
73
|
+
)`);
|
|
74
|
+
} catch {
|
|
75
|
+
const parts = splitStatements(code);
|
|
76
|
+
const last = parts.pop() ?? "";
|
|
77
|
+
try {
|
|
78
|
+
fn = make(`${parts.join(";")};return(${last}
|
|
79
|
+
)`);
|
|
80
|
+
} catch {
|
|
81
|
+
fn = make(code);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
cache.set(key, fn);
|
|
86
|
+
return fn;
|
|
87
|
+
};
|
|
88
|
+
export {
|
|
89
|
+
compile,
|
|
90
|
+
functionCompiler,
|
|
91
|
+
rewriteActions,
|
|
92
|
+
splitStatements
|
|
93
|
+
};
|
|
94
|
+
//# sourceMappingURL=expr.js.map
|