micra.js 2.3.2 → 2.4.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/CHANGELOG.md +57 -0
- package/README.md +7 -4
- package/dist/index.d.ts +1 -1
- package/dist/micra.cjs.js +296 -48
- package/dist/micra.cjs.js.map +3 -3
- package/dist/micra.esm.js +296 -48
- package/dist/micra.esm.js.map +3 -3
- package/dist/micra.js +296 -48
- package/dist/micra.js.map +3 -3
- package/dist/micra.min.js +3 -2
- package/dist/types.d.ts +1 -0
- package/dist/utils/expr.d.ts +29 -22
- package/llms-full.txt +19 -19
- package/llms.txt +3 -3
- package/package.json +2 -2
- package/src/core/mount.ts +4 -0
- package/src/dom/events.ts +37 -10
- package/src/index.ts +1 -1
- package/src/types.ts +1 -0
- package/src/utils/expr.ts +278 -121
package/dist/utils/expr.d.ts
CHANGED
|
@@ -1,37 +1,44 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* src/utils/expr.ts — JS
|
|
2
|
+
* src/utils/expr.ts — CSP-safe JS-expression evaluator.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* - Shadow non-state identifiers so directive expressions cannot reach
|
|
9
|
-
* globals like `window`, `fetch`, `constructor`, etc. A small whitelist
|
|
10
|
-
* of utility globals (Math, JSON, Date, ...) remains accessible.
|
|
4
|
+
* Directive expressions (`data-text="count > 0"`, `data-class="x:a === b"`, …)
|
|
5
|
+
* are parsed into a small AST and walked by an interpreter. There is NO
|
|
6
|
+
* `new Function` / `eval` anywhere — so Micra runs under a strict
|
|
7
|
+
* Content-Security-Policy (`default-src 'self'`, no `unsafe-eval`).
|
|
11
8
|
*
|
|
12
9
|
* LLM NOTE: This module is PURE. It does not touch the DOM or mutate state.
|
|
13
10
|
*
|
|
14
11
|
* Security model:
|
|
15
|
-
*
|
|
16
|
-
* and
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
12
|
+
* The interpreter can only reach: top-level state keys, component methods,
|
|
13
|
+
* and a whitelist of utility globals (Math, JSON, Date, …). A bare
|
|
14
|
+
* identifier that is none of those resolves to `undefined` — `window`,
|
|
15
|
+
* `document`, `fetch`, `eval`, `constructor` are unreachable *by
|
|
16
|
+
* construction* (there is no scope that contains them), not by shadowing.
|
|
17
|
+
* Member access additionally refuses the prototype-escape property names
|
|
18
|
+
* (`__proto__`, `constructor`, `prototype`), closing the
|
|
19
|
+
* `item.constructor.constructor("…")()` chain that the old `with()`-based
|
|
20
|
+
* evaluator left open. Method calls still run real JS — if a component
|
|
21
|
+
* method touches `window`, that works; treat directive templates as
|
|
22
|
+
* trusted code regardless.
|
|
23
|
+
*
|
|
24
|
+
* Grammar (precedence low→high):
|
|
25
|
+
* ternary ?: | || | && | == != === !== | < <= > >= | + - |
|
|
26
|
+
* * / % | unary ! - | call() / member. | primary
|
|
27
|
+
* primary = number | string | true | false | null | undefined |
|
|
28
|
+
* identifier | ( expr )
|
|
22
29
|
*/
|
|
23
30
|
import type { StateRecord } from '../types';
|
|
24
31
|
/**
|
|
25
|
-
* Evaluate
|
|
32
|
+
* Evaluate an expression string against a state object.
|
|
26
33
|
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
34
|
+
* Cached by string. Simple dot-paths take a fast path that skips tokenizing.
|
|
35
|
+
* Parse errors warn once and thereafter resolve to `undefined`; runtime
|
|
36
|
+
* errors (e.g. calling a non-function) warn once per expression.
|
|
30
37
|
*
|
|
31
38
|
* @example
|
|
32
|
-
* evalExpr('count > 0', { count: 5 })
|
|
33
|
-
* evalExpr('user.name', { user: { name: 'Alice' } })
|
|
34
|
-
* evalExpr('price * qty', { price: 9.99, qty: 3 })
|
|
39
|
+
* evalExpr('count > 0', { count: 5 }) // → true
|
|
40
|
+
* evalExpr('user.name', { user: { name: 'Alice' } }) // → 'Alice'
|
|
41
|
+
* evalExpr('price * qty', { price: 9.99, qty: 3 }) // → 29.97
|
|
35
42
|
*/
|
|
36
43
|
export declare function evalExpr(expr: string, state: StateRecord): unknown;
|
|
37
44
|
/** @internal Consistent warning prefix. */
|
package/llms-full.txt
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
This file follows the llmstxt.org "expanded" convention: it inlines code recipes so LLMs that crawl this URL get a complete training surface in one read. The short version is `llms.txt` in the same directory.
|
|
4
4
|
|
|
5
|
-
> Lightweight reactive TypeScript framework for server-rendered apps and small SaaS frontends. ~
|
|
5
|
+
> Lightweight reactive TypeScript framework for server-rendered apps and small SaaS frontends. ~7 KB gzip. No build step required.
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
npm install micra.js@^2.
|
|
10
|
+
npm install micra.js@^2.4.0
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
```ts
|
|
@@ -17,7 +17,7 @@ import * as Micra from 'micra.js'
|
|
|
17
17
|
Or CDN (no build step):
|
|
18
18
|
|
|
19
19
|
```html
|
|
20
|
-
<script src="https://cdn.jsdelivr.net/npm/micra.js@2.
|
|
20
|
+
<script src="https://cdn.jsdelivr.net/npm/micra.js@2.4.0/dist/micra.min.js"></script>
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
This exposes a global `Micra` object.
|
|
@@ -30,7 +30,7 @@ This exposes a global `Micra` object.
|
|
|
30
30
|
## When to use Micra.js
|
|
31
31
|
|
|
32
32
|
- Server-rendered pages (Rails, Laravel, Django, Phoenix, etc.) needing a small amount of reactivity
|
|
33
|
-
- Bundle-size-sensitive (~
|
|
33
|
+
- Bundle-size-sensitive (~7 KB vs 45+ KB React)
|
|
34
34
|
- No build step desired — drop a `<script>` tag
|
|
35
35
|
- Existing HTML that just needs reactive enhancement
|
|
36
36
|
|
|
@@ -131,7 +131,7 @@ Modifiers: `.prevent`, `.stop`, `.self` (event-only — no key modifiers).
|
|
|
131
131
|
- Whitelisted globals: `Math`, `JSON`, `Date`, `String`, `Number`, `Boolean`, `Array`, `Object`, `parseInt`, `parseFloat`, `isNaN`, `isFinite`, `NaN`, `Infinity`, `undefined`
|
|
132
132
|
- Inside `data-each`: `item`, `index`, `$index`
|
|
133
133
|
|
|
134
|
-
Everything else (`window`, `document`, `fetch`, `eval`, `constructor`, `setTimeout`) resolves to `undefined` by design
|
|
134
|
+
Everything else (`window`, `document`, `fetch`, `eval`, `constructor`, `setTimeout`) resolves to `undefined` by design. Expressions are parsed + interpreted (no `new Function`/`eval`), so Micra runs under a strict CSP. Inside `@event` you may also pass arguments: `@click="select(item.id)"`, `@input="set($event.target.value)"`.
|
|
135
135
|
|
|
136
136
|
---
|
|
137
137
|
|
|
@@ -146,7 +146,7 @@ Everything else (`window`, `document`, `fetch`, `eval`, `constructor`, `setTimeo
|
|
|
146
146
|
<button @click="inc">+</button>
|
|
147
147
|
</div>
|
|
148
148
|
|
|
149
|
-
<script src="https://cdn.jsdelivr.net/npm/micra.js@2.
|
|
149
|
+
<script src="https://cdn.jsdelivr.net/npm/micra.js@2.4.0/dist/micra.min.js"></script>
|
|
150
150
|
<script>
|
|
151
151
|
Micra.define('counter', {
|
|
152
152
|
state: { count: 0 },
|
|
@@ -190,7 +190,7 @@ Everything else (`window`, `document`, `fetch`, `eval`, `constructor`, `setTimeo
|
|
|
190
190
|
</footer>
|
|
191
191
|
</div>
|
|
192
192
|
|
|
193
|
-
<script src="https://cdn.jsdelivr.net/npm/micra.js@2.
|
|
193
|
+
<script src="https://cdn.jsdelivr.net/npm/micra.js@2.4.0/dist/micra.min.js"></script>
|
|
194
194
|
<script>
|
|
195
195
|
Micra.define('todo-app', {
|
|
196
196
|
state: {
|
|
@@ -262,7 +262,7 @@ Everything else (`window`, `document`, `fetch`, `eval`, `constructor`, `setTimeo
|
|
|
262
262
|
<p data-if="filtered().length === 0">No matches.</p>
|
|
263
263
|
</div>
|
|
264
264
|
|
|
265
|
-
<script src="https://cdn.jsdelivr.net/npm/micra.js@2.
|
|
265
|
+
<script src="https://cdn.jsdelivr.net/npm/micra.js@2.4.0/dist/micra.min.js"></script>
|
|
266
266
|
<script>
|
|
267
267
|
Micra.define('users-table', {
|
|
268
268
|
state: {
|
|
@@ -303,7 +303,7 @@ Everything else (`window`, `document`, `fetch`, `eval`, `constructor`, `setTimeo
|
|
|
303
303
|
<p data-if="success">Invitation sent ✓</p>
|
|
304
304
|
</form>
|
|
305
305
|
|
|
306
|
-
<script src="https://cdn.jsdelivr.net/npm/micra.js@2.
|
|
306
|
+
<script src="https://cdn.jsdelivr.net/npm/micra.js@2.4.0/dist/micra.min.js"></script>
|
|
307
307
|
<script>
|
|
308
308
|
Micra.define('invite-form', {
|
|
309
309
|
state: { email: '', loading: false, error: '', success: false },
|
|
@@ -345,7 +345,7 @@ Everything else (`window`, `document`, `fetch`, `eval`, `constructor`, `setTimeo
|
|
|
345
345
|
</div>
|
|
346
346
|
</div>
|
|
347
347
|
|
|
348
|
-
<script src="https://cdn.jsdelivr.net/npm/micra.js@2.
|
|
348
|
+
<script src="https://cdn.jsdelivr.net/npm/micra.js@2.4.0/dist/micra.min.js"></script>
|
|
349
349
|
<script>
|
|
350
350
|
Micra.define('open-modal-btn', {
|
|
351
351
|
open() {
|
|
@@ -394,7 +394,7 @@ Everything else (`window`, `document`, `fetch`, `eval`, `constructor`, `setTimeo
|
|
|
394
394
|
<section data-if="tab === 'security'">Security content</section>
|
|
395
395
|
</div>
|
|
396
396
|
|
|
397
|
-
<script src="https://cdn.jsdelivr.net/npm/micra.js@2.
|
|
397
|
+
<script src="https://cdn.jsdelivr.net/npm/micra.js@2.4.0/dist/micra.min.js"></script>
|
|
398
398
|
<script>
|
|
399
399
|
Micra.define('tabs', {
|
|
400
400
|
state: { tab: 'overview' },
|
|
@@ -414,7 +414,7 @@ Everything else (`window`, `document`, `fetch`, `eval`, `constructor`, `setTimeo
|
|
|
414
414
|
<button @click="upgrade" data-if="plan !== 'enterprise'">Upgrade</button>
|
|
415
415
|
</div>
|
|
416
416
|
|
|
417
|
-
<script src="https://cdn.jsdelivr.net/npm/micra.js@2.
|
|
417
|
+
<script src="https://cdn.jsdelivr.net/npm/micra.js@2.4.0/dist/micra.min.js"></script>
|
|
418
418
|
<script>
|
|
419
419
|
Micra.define('user-card', {
|
|
420
420
|
state: { name: '', plan: '' },
|
|
@@ -445,7 +445,7 @@ Everything else (`window`, `document`, `fetch`, `eval`, `constructor`, `setTimeo
|
|
|
445
445
|
<p data-if="!loading && results.length === 0 && query">No results.</p>
|
|
446
446
|
</div>
|
|
447
447
|
|
|
448
|
-
<script src="https://cdn.jsdelivr.net/npm/micra.js@2.
|
|
448
|
+
<script src="https://cdn.jsdelivr.net/npm/micra.js@2.4.0/dist/micra.min.js"></script>
|
|
449
449
|
<script>
|
|
450
450
|
Micra.define('search', {
|
|
451
451
|
state: { query: '', results: [], loading: false },
|
|
@@ -479,7 +479,7 @@ Everything else (`window`, `document`, `fetch`, `eval`, `constructor`, `setTimeo
|
|
|
479
479
|
<p data-if="loading">Loading chart…</p>
|
|
480
480
|
</div>
|
|
481
481
|
|
|
482
|
-
<script src="https://cdn.jsdelivr.net/npm/micra.js@2.
|
|
482
|
+
<script src="https://cdn.jsdelivr.net/npm/micra.js@2.4.0/dist/micra.min.js"></script>
|
|
483
483
|
<script>
|
|
484
484
|
Micra.define('revenue-chart', {
|
|
485
485
|
state: { loading: true },
|
|
@@ -511,7 +511,7 @@ Everything else (`window`, `document`, `fetch`, `eval`, `constructor`, `setTimeo
|
|
|
511
511
|
<p data-if="!loading && rows.length === 0">No results.</p>
|
|
512
512
|
</div>
|
|
513
513
|
|
|
514
|
-
<script src="https://cdn.jsdelivr.net/npm/micra.js@2.
|
|
514
|
+
<script src="https://cdn.jsdelivr.net/npm/micra.js@2.4.0/dist/micra.min.js"></script>
|
|
515
515
|
<script>
|
|
516
516
|
Micra.define('search-bar', {
|
|
517
517
|
state: { query: '' },
|
|
@@ -542,7 +542,7 @@ the same page. Twelve lines of glue, written once.
|
|
|
542
542
|
<main hx-get="/page/home" hx-trigger="load" hx-swap="innerHTML"></main>
|
|
543
543
|
|
|
544
544
|
<script src="https://cdn.jsdelivr.net/npm/htmx.org@2/dist/htmx.min.js"></script>
|
|
545
|
-
<script src="https://cdn.jsdelivr.net/npm/micra.js@2.
|
|
545
|
+
<script src="https://cdn.jsdelivr.net/npm/micra.js@2.4.0/dist/micra.min.js"></script>
|
|
546
546
|
<script>
|
|
547
547
|
Micra.define('counter', {
|
|
548
548
|
state: { count: 0 },
|
|
@@ -596,7 +596,7 @@ a `turbo:load` mirror so the second navigation doesn't ghost.
|
|
|
596
596
|
```ruby
|
|
597
597
|
# config/importmap.rb
|
|
598
598
|
pin "micra",
|
|
599
|
-
to: "https://cdn.jsdelivr.net/npm/micra.js@2.
|
|
599
|
+
to: "https://cdn.jsdelivr.net/npm/micra.js@2.4.0/dist/micra.esm.js",
|
|
600
600
|
preload: true
|
|
601
601
|
```
|
|
602
602
|
|
|
@@ -717,9 +717,9 @@ import { ref, computed } from 'vue'
|
|
|
717
717
|
import Alpine from 'alpinejs'
|
|
718
718
|
|
|
719
719
|
// ❌ unpkg CDN — blocked by Claude artifacts and most AI sandbox CSPs
|
|
720
|
-
<script src="https://unpkg.com/micra.js@2.
|
|
720
|
+
<script src="https://unpkg.com/micra.js@2.4.0/dist/micra.min.js"></script>
|
|
721
721
|
// ✅ Use jsDelivr instead — it auto-mirrors npm and is CSP-allowlisted everywhere
|
|
722
|
-
<script src="https://cdn.jsdelivr.net/npm/micra.js@2.
|
|
722
|
+
<script src="https://cdn.jsdelivr.net/npm/micra.js@2.4.0/dist/micra.min.js"></script>
|
|
723
723
|
```
|
|
724
724
|
|
|
725
725
|
# Final checklist
|
package/llms.txt
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# Micra.js
|
|
2
2
|
|
|
3
|
-
> Lightweight reactive TypeScript framework for server-rendered apps and small SaaS frontends. ~
|
|
3
|
+
> Lightweight reactive TypeScript framework for server-rendered apps and small SaaS frontends. ~7 KB gzip. No build step required.
|
|
4
4
|
>
|
|
5
5
|
> **For LLM code generation:** read the expanded version at https://github.com/denisfl/micra.js/blob/master/llms-full.txt — it includes 10 full inline recipes and an anti-pattern reference. The recipes are the canonical answers to common requests like "build a todo app" or "build a search-with-debounce".
|
|
6
6
|
|
|
7
7
|
## When to use Micra.js instead of React/Vue
|
|
8
8
|
|
|
9
9
|
- You have a server-rendered page (Laravel, Rails, Django, etc.) and need a small amount of reactivity
|
|
10
|
-
- Bundle size matters (~
|
|
10
|
+
- Bundle size matters (~7 KB gzip vs ~45 KB React)
|
|
11
11
|
- You don't need a full SPA or client-side routing
|
|
12
12
|
- You want to drop a `<script>` tag and go
|
|
13
13
|
- You need reactive directives on existing HTML without rewriting templates
|
|
@@ -25,7 +25,7 @@ import * as Micra from 'micra.js'
|
|
|
25
25
|
Or via CDN (no build step):
|
|
26
26
|
|
|
27
27
|
```html
|
|
28
|
-
<script src="https://cdn.jsdelivr.net/npm/micra.js@2.
|
|
28
|
+
<script src="https://cdn.jsdelivr.net/npm/micra.js@2.4.0/dist/micra.min.js"></script>
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
This exposes a global `Micra` object.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "micra.js",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "Lightweight reactive UI framework for server-rendered pages — reactive state, directives, event bus. <
|
|
3
|
+
"version": "2.4.0",
|
|
4
|
+
"description": "Lightweight reactive UI framework for server-rendered pages — reactive state, directives, event bus. < 7 KB gzip.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/micra.cjs.js",
|
|
7
7
|
"module": "./dist/micra.esm.js",
|
package/src/core/mount.ts
CHANGED
|
@@ -153,6 +153,10 @@ export function mount<S extends StateRecord, M>(
|
|
|
153
153
|
);
|
|
154
154
|
},
|
|
155
155
|
});
|
|
156
|
+
// Exposed for events.ts so `@click="select(item.id)"` can evaluate the call
|
|
157
|
+
// against component state + methods. Row elements eval against their own
|
|
158
|
+
// `_itemState` (which prototype-chains to this); non-row elements use this.
|
|
159
|
+
instance.__micraExpr = exprState;
|
|
156
160
|
|
|
157
161
|
let warnedReentry = false;
|
|
158
162
|
instance.render = function () {
|
package/src/dom/events.ts
CHANGED
|
@@ -20,7 +20,7 @@ import type {
|
|
|
20
20
|
MicraElement,
|
|
21
21
|
StateRecord,
|
|
22
22
|
} from '../types'
|
|
23
|
-
import { warn } from '../utils/expr'
|
|
23
|
+
import { evalExpr, warn } from '../utils/expr'
|
|
24
24
|
|
|
25
25
|
/** @internal Attach a DOM listener and track it on the instance for destroy(). */
|
|
26
26
|
function track<S extends StateRecord>(
|
|
@@ -33,6 +33,38 @@ function track<S extends StateRecord>(
|
|
|
33
33
|
;(instance.__micraListeners ??= []).push({ el, type, fn })
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
/**
|
|
37
|
+
* Run an event handler. Two shapes, both used by `data-on` and `@event`:
|
|
38
|
+
* - bare method name `save` → instance.save(e)
|
|
39
|
+
* - call expression `select(item.id)` → evaluated against an event scope
|
|
40
|
+
* (row `item` if inside `data-each`, `$event`/`event`, component methods).
|
|
41
|
+
* Call expressions are the recommended form for `@event`; in `data-on` the
|
|
42
|
+
* handler separator is `,` so multi-argument calls there are not supported.
|
|
43
|
+
*/
|
|
44
|
+
function runHandler<S extends StateRecord>(
|
|
45
|
+
instance: InternalInstance<S>,
|
|
46
|
+
el: Element,
|
|
47
|
+
value: string,
|
|
48
|
+
e: Event,
|
|
49
|
+
): void {
|
|
50
|
+
if (value.includes('(')) {
|
|
51
|
+
// Build the scope: nearest ancestor-or-self row itemState (it already
|
|
52
|
+
// prototype-chains to the component's expr scope), else the expr scope.
|
|
53
|
+
let base: StateRecord | undefined
|
|
54
|
+
for (let n: Element | null = el; n && !base; n = n.parentElement) {
|
|
55
|
+
base = (n as MicraElement)._itemState
|
|
56
|
+
}
|
|
57
|
+
const scope = Object.create(base ?? instance.__micraExpr ?? null) as StateRecord
|
|
58
|
+
scope['$event'] = e
|
|
59
|
+
scope['event'] = e
|
|
60
|
+
evalExpr(value, scope) // performs the call; return value ignored
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
const fn = instance[value]
|
|
64
|
+
if (typeof fn === 'function') (fn as (e: Event) => void).call(instance, e)
|
|
65
|
+
else warn(`method "${value}" not found`)
|
|
66
|
+
}
|
|
67
|
+
|
|
36
68
|
// ── data-on ───────────────────────────────────────────────────────────────────
|
|
37
69
|
|
|
38
70
|
/**
|
|
@@ -62,15 +94,13 @@ export function bindDataOn<S extends StateRecord>(
|
|
|
62
94
|
if (!evSpec || !method) continue
|
|
63
95
|
|
|
64
96
|
const [evName, ...mods] = evSpec.split('.')
|
|
97
|
+
const handler = method.trim()
|
|
65
98
|
|
|
66
99
|
track(instance, el, evName!, (e: Event) => {
|
|
67
100
|
if (mods.includes('prevent')) e.preventDefault()
|
|
68
101
|
if (mods.includes('stop')) e.stopPropagation()
|
|
69
102
|
if (mods.includes('self') && e.target !== el) return
|
|
70
|
-
|
|
71
|
-
const fn = instance[method.trim()]
|
|
72
|
-
if (typeof fn === 'function') (fn as (e: Event) => void).call(instance, e)
|
|
73
|
-
else warn(`method "${method.trim()}" not found`)
|
|
103
|
+
runHandler(instance, el, handler, e)
|
|
74
104
|
})
|
|
75
105
|
}
|
|
76
106
|
}
|
|
@@ -101,16 +131,13 @@ export function bindAtEvents<S extends StateRecord>(
|
|
|
101
131
|
for (const attr of Array.from(el.attributes)) {
|
|
102
132
|
if (!attr.name.startsWith('@')) continue
|
|
103
133
|
const [evSpec, ...rest] = attr.name.slice(1).split('.')
|
|
104
|
-
const
|
|
134
|
+
const handler = attr.value.trim()
|
|
105
135
|
|
|
106
136
|
track(instance, el, evSpec!, (e: Event) => {
|
|
107
137
|
if (rest.includes('prevent')) e.preventDefault()
|
|
108
138
|
if (rest.includes('stop')) e.stopPropagation()
|
|
109
139
|
if (rest.includes('self') && e.target !== el) return
|
|
110
|
-
|
|
111
|
-
const fn = instance[method]
|
|
112
|
-
if (typeof fn === 'function') (fn as (e: Event) => void).call(instance, e)
|
|
113
|
-
else warn(`method "${method}" not found`)
|
|
140
|
+
runHandler(instance, el, handler, e)
|
|
114
141
|
})
|
|
115
142
|
bound = true
|
|
116
143
|
}
|
package/src/index.ts
CHANGED
package/src/types.ts
CHANGED
|
@@ -294,5 +294,6 @@ export interface InternalInstance<S extends StateRecord = StateRecord>
|
|
|
294
294
|
__micraSubs?: UnsubFn[]
|
|
295
295
|
__micraListeners?: TrackedListener[]
|
|
296
296
|
__micraDestroyed?: true
|
|
297
|
+
__micraExpr?: StateRecord // expression scope (state + bound methods) for @event call args
|
|
297
298
|
[key: string]: unknown
|
|
298
299
|
}
|