microtags 0.0.0 → 0.0.3

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.
Files changed (45) hide show
  1. package/README.md +343 -46
  2. package/dist/attributes.d.ts +10 -0
  3. package/dist/attributes.d.ts.map +1 -0
  4. package/dist/builders.d.ts +43 -4
  5. package/dist/builders.d.ts.map +1 -1
  6. package/dist/chunk-7QVYU63E.js +7 -0
  7. package/dist/chunk-7QVYU63E.js.map +7 -0
  8. package/dist/chunk-CCYWRJW6.js +98 -0
  9. package/dist/chunk-CCYWRJW6.js.map +7 -0
  10. package/dist/chunk-E7ZGOHL3.js +51 -0
  11. package/dist/chunk-E7ZGOHL3.js.map +7 -0
  12. package/dist/context.cjs +10 -4
  13. package/dist/context.cjs.map +3 -3
  14. package/dist/context.d.ts.map +1 -1
  15. package/dist/context.js +7 -63
  16. package/dist/context.js.map +3 -3
  17. package/dist/context.min.js.map +2 -2
  18. package/dist/factory.d.ts.map +1 -1
  19. package/dist/index.cjs +416 -5
  20. package/dist/index.cjs.map +3 -3
  21. package/dist/index.js +345 -2
  22. package/dist/index.js.map +3 -3
  23. package/dist/index.min.js +1 -1
  24. package/dist/index.min.js.map +4 -4
  25. package/dist/meta.json +277 -27
  26. package/dist/reactivity.d.ts.map +1 -1
  27. package/dist/render.cjs +6 -4
  28. package/dist/render.cjs.map +2 -2
  29. package/dist/render.d.ts.map +1 -1
  30. package/dist/render.js +9 -6
  31. package/dist/render.js.map +2 -2
  32. package/dist/render.min.js.map +1 -1
  33. package/dist/setup-context.d.ts.map +1 -1
  34. package/dist/types.d.ts +45 -10
  35. package/dist/types.d.ts.map +1 -1
  36. package/dist/util.cjs +68 -0
  37. package/dist/util.cjs.map +7 -0
  38. package/dist/{utils.d.ts → util.d.ts} +1 -1
  39. package/dist/util.d.ts.map +1 -0
  40. package/dist/util.js +16 -0
  41. package/dist/util.js.map +7 -0
  42. package/dist/util.min.js +2 -0
  43. package/dist/util.min.js.map +7 -0
  44. package/package.json +38 -19
  45. package/dist/utils.d.ts.map +0 -1
package/README.md CHANGED
@@ -1,70 +1,164 @@
1
1
  # microtags
2
2
 
3
- ![tests](https://github.com/nichoth/microtags/actions/workflows/nodejs.yml/badge.svg)
3
+ [![tests](https://img.shields.io/github/actions/workflow/status/nichoth/microtags/nodejs.yml?style=flat-square)](https://github.com/nichoth/microtags/actions/workflows/nodejs.yml)
4
4
  [![types](https://img.shields.io/npm/types/microtags?style=flat-square)](README.md)
5
5
  [![module](https://img.shields.io/badge/module-ESM%2FCJS-blue?style=flat-square)](README.md)
6
6
  [![semantic versioning](https://img.shields.io/badge/semver-2.0.0-blue?logo=semver&style=flat-square)](https://semver.org/)
7
7
  [![Common Changelog](https://nichoth.github.io/badge/common-changelog.svg)](./CHANGELOG.md)
8
+ [![install size](https://flat.badgen.net/packagephobia/install/microtags?cache-control=no-cache)](https://packagephobia.com/result?p=microtags)
9
+ [![gzip size](https://img.shields.io/bundlephobia/minzip/microtags?style=flat-square)](https://bundlephobia.com/package/microtags)
8
10
  [![license](https://img.shields.io/badge/license-Big_Time-blue?style=flat-square)](LICENSE)
9
11
 
10
12
  Web component helper with reactive props, automatic cleanup,
11
13
  and no Shadow DOM. Reactive via
12
14
  [alien-signals](https://github.com/stackblitz/alien-signals).
13
15
 
14
- Inspired by [nanotags](https://nanotags.psdcoder.dev/), with one change:
15
- reactive props are **alien-signals** instead of nanostores atoms.
16
- You call `count()` to read and `count(42)` to write — no `$` prefix,
17
- no `.get()` / `.set()`.
16
+ **Size**: 3.41 kB with all dependencies, minified and brotlied
17
+
18
+ Inspired by [nanotags](https://nanotags.psdcoder.dev/). Reactive props use
19
+ **alien-signals** instead of nanostores atoms.
18
20
 
19
21
  <details><summary><h2>Contents</h2></summary>
20
22
 
23
+ <!-- toc -->
24
+
21
25
  - [Install](#install)
22
- - [Quick start](#quick-start)
26
+ * [Import map](#import-map)
27
+ * [Pre-bundled](#pre-bundled)
28
+ - [Example](#example)
29
+ * [Client-side validation](#client-side-validation)
30
+ - [Serverside Example](#serverside-example)
31
+ * [`.TAG`](#tag)
32
+ * [`.refs`](#refs)
23
33
  - [API](#api)
24
- - [define](#define)
25
- - [withProps](#withprops)
26
- - [withRefs](#withrefs)
27
- - [withContexts](#withcontexts)
28
- - [setup and ctx](#setup-and-ctx)
34
+ * [Props](#props)
35
+ + [`withProps`](#withprops)
36
+ * [Refs](#refs)
37
+ + [`withRefs`](#withrefs)
38
+ + [Element types](#element-types)
39
+ * [define](#define)
40
+ * [withContexts](#withcontexts)
41
+ * [setup and ctx](#setup-and-ctx)
42
+ + [peek and untracked](#peek-and-untracked)
29
43
  - [Subpaths](#subpaths)
30
- - [microtags/context](#microtagscontext)
31
- - [microtags/render](#microtagsrender)
44
+ * [microtags/context](#microtagscontext)
45
+ * [microtags/render](#microtagsrender)
32
46
  - [Divergence from nanotags](#divergence-from-nanotags)
33
47
 
48
+ <!-- tocstop -->
49
+
34
50
  </details>
35
51
 
52
+ ----------------------------------------------------------------------
36
53
  ## Install
54
+ ----------------------------------------------------------------------
37
55
 
38
56
  ```sh
39
57
  npm i -S microtags
40
58
  ```
41
59
 
42
- ## Quick start
60
+ ### Import map
61
+
62
+ No build step needed. Resolve `microtags` with an
63
+ [import map](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/script/type/importmap)
64
+ and `esm.sh`.
65
+
66
+
67
+ ```html
68
+ <script type="importmap">
69
+ {
70
+ "imports": {
71
+ "microtags": "https://esm.sh/microtags"
72
+ }
73
+ }
74
+ </script>
75
+
76
+ <script type="module">
77
+ import { define } from 'microtags'
78
+ </script>
79
+ ```
80
+
81
+ Subpaths follow the same URL pattern: `microtags/context`, `microtags/render`.
82
+
83
+ ### Pre-bundled
84
+
85
+ For raw-file CDNs (jsDelivr, unpkg) or local files, where dependencies are
86
+ not resolved for you, use the pre-bundled `microtags/min` build. It is a
87
+ single self-contained file with `alien-signals` inlined, so no other
88
+ mappings are needed:
89
+
90
+ ```html
91
+ <script type="importmap">
92
+ {
93
+ "imports": {
94
+ "microtags": "https://cdn.jsdelivr.net/npm/microtags/dist/index.min.js"
95
+ }
96
+ }
97
+ </script>
98
+ ```
99
+
100
+
101
+ ----------------------------------------------------------------------
102
+ ## Example
103
+ ----------------------------------------------------------------------
104
+
105
+ This example depends on having the full HTML already in the DOM. If you want
106
+ to do client-side-rendered components, that is possible. You would update the
107
+ DOM inside `.setup`.
108
+
109
+ Since the full HTML exists already, defining your components in the
110
+ client-side JS is about "hydrating" the page (adding behavior).
111
+
112
+ ---
113
+
114
+ `withProps`, `withRefs`, and `withContexts` are optional and can appear in
115
+ any order. `.setup` ends the chain: it calls `customElements.define` under the
116
+ hood and returns a typed constructor.
43
117
 
44
118
  ```ts
45
119
  import { define } from 'microtags'
46
120
  import { signal } from 'alien-signals'
47
121
 
48
- define('my-counter')
122
+ /**
123
+ * - attribute-backed prop (`start`)
124
+ * - data-ref elements (the <count-button> and <copy-btn> children)
125
+ * - ctx.effect, ctx.on
126
+ */
127
+ export const myCounter = define('my-counter')
49
128
  .withProps(p => ({
50
- start: p.number(), // hydrated from the `start` attribute
129
+ start: p.number(),
51
130
  }))
52
- .withRefs(r => ({
53
- display: r.one, // [data-ref="display"] inside the element
54
- inc: r.one,
55
- dec: r.one,
131
+ .withRefs(r => ({ // the keys in this object are the ref names
132
+ display: r.one<HTMLDivElement>(), // typed as HTMLDivElement
133
+ inc: r.one(), // defaults to HTMLElement
134
+ dec: r.one(),
135
+ copy: r.one(),
56
136
  }))
57
137
  .setup(ctx => {
58
- // Create a local signal seeded from the hydrated prop.
138
+ // all event <-> state logic here
59
139
  const count = signal(ctx.props.start())
140
+ const display = ctx.refs.display
141
+
142
+ ctx.effect(() => {
143
+ display.textContent = `Count: ${count()}`
144
+ display.style.color = count() < 0 ? 'red' : 'inherit'
145
+ })
146
+
147
+ // Reflect the live count onto the copy button's `value` attribute.
148
+ // Because `value` is attribute-backed, this flows into <copy-btn>'s
149
+ // own signal, so it always copies the current count.
150
+ ctx.effect(() => {
151
+ const copy = ctx.refs.copy
152
+ copy.setAttribute('value', String(count()))
153
+ })
60
154
 
61
- // Two-way bind the display's textContent to a derived value.
62
- ctx.bind(ctx.refs.display as HTMLElement, 'textContent',
63
- () => `Count: ${count()}`)
155
+ ctx.on(ctx.refs.inc, 'click', () => {
156
+ count(count() + 1)
157
+ })
64
158
 
65
- // Wire up click handlers — auto-removed on disconnect.
66
- ctx.on(ctx.refs.inc as HTMLElement, 'click', () => count(count() + 1))
67
- ctx.on(ctx.refs.dec as HTMLElement, 'click', () => count(count() - 1))
159
+ ctx.on(ctx.refs.dec, 'click', () => {
160
+ count(count() - 1)
161
+ })
68
162
  })
69
163
  ```
70
164
 
@@ -76,29 +170,164 @@ define('my-counter')
76
170
  </my-counter>
77
171
  ```
78
172
 
79
- ## API
173
+ ### Client-side validation
80
174
 
81
- ### define
175
+ See [example/subscribe-form.ts](./example/subscribe-form.ts).
176
+
177
+ There are two separate things people call "validation", and `microtags`
178
+ handles them in different places:
179
+
180
+ 1. **Prop validation** checks the attributes passed *into* a component.
181
+ Pass a [Standard Schema](https://standardschema.dev) (Zod, Valibot,
182
+ ArkType) to [`p.schema`](#withprops); it coerces the attribute value
183
+ and falls back to `undefined` when the value doesn't match.
184
+ 2. **Form input validation** checks live user input in a form, for example a
185
+ value that changes on every keystroke and needs to report *why* it is invalid.
186
+ There is no dedicated API for this. Validate the schema directly
187
+ inside `.setup()` and hold the result in a signal:
188
+
189
+ The example below is the second kind -- form input (client-side) validation.
82
190
 
83
191
  ```ts
192
+ import { signal, startBatch, endBatch } from 'alien-signals'
193
+ import { z } from 'zod'
84
194
  import { define } from 'microtags'
85
195
 
86
- define(tagName: string): ComponentBuilder
196
+ const Email = z.email('Please enter a valid email address.')
197
+
198
+ define('subscribe-form')
199
+ .withRefs(r => ({
200
+ form: r.one('form'),
201
+ input: r.one('input'),
202
+ submit: r.one('button'),
203
+ error: r.one('.error'),
204
+ status: r.one('.status'),
205
+ }))
206
+ .setup(ctx => {
207
+ const value = signal('')
208
+ const touched = signal(false)
209
+
210
+ // capture the live value (two-way; clears the field on reset)
211
+ ctx.bind(value, ctx.refs.input)
212
+ // only reveal errors after the first blur
213
+ ctx.on(ctx.refs.input, 'blur', () => touched(true))
214
+
215
+ ctx.effect(() => {
216
+ const result = Email.safeParse(value())
217
+ const message = result.success ?
218
+ '' :
219
+ result.error.issues[0].message
220
+ const showError = touched() && !result.success
221
+
222
+ ctx.refs.submit.disabled = !result.success
223
+ ctx.refs.input.setAttribute('aria-invalid', String(showError))
224
+ ctx.refs.error.textContent = showError ? message : ''
225
+ })
226
+
227
+ ctx.on(ctx.refs.form, 'submit', ev => {
228
+ ev.preventDefault()
229
+ if (!Email.safeParse(value()).success) return
230
+ ctx.refs.status.textContent = 'Thanks for subscribing!'
231
+ startBatch()
232
+ value('')
233
+ touched(false)
234
+ endBatch()
235
+ })
236
+ })
87
237
  ```
88
238
 
89
- Returns a fluent builder. Call `.setup()` to register the custom element.
90
- All builder methods are chainable and fully type-inferred.
239
+ The error stays hidden until the field is blurred once, then updates live;
240
+ the submit button is gated on validity. See `example/subscribe-form.ts`
241
+ for the full working component.
242
+
243
+
244
+ ----------------------------------------------------------------------
245
+ ## Serverside Example
246
+ ----------------------------------------------------------------------
91
247
 
92
- ### withProps
248
+ ### `.TAG`
249
+
250
+ Each tag name that you pass to `define` is exposed as a property `.TAG`
251
+ on the return value. It is possible to import your web components in Node,
252
+ even though Node does not have browser APIs.
253
+
254
+ ### `.refs`
255
+
256
+ All refs are defined per-component, and the ref names are exposed at `.refs`,
257
+ so you can import your web components on the server and read the ref names
258
+ as strings.
259
+
260
+ ```ts
261
+ import { MyCounter, CountBtn, CopyBtn } from './index.js'
262
+
263
+ // this runs in Node
264
+
265
+ export function render ():string {
266
+ return `<div>
267
+ <${MyCounter.TAG} start="${START}">
268
+ <div data-ref="${MyCounter.refs.display}"
269
+ role="status"
270
+ class="count"
271
+ >${START}</div>
272
+
273
+ <div class="controls">
274
+ <${CountBtn.TAG}
275
+ class="dec"
276
+ data-ref="${MyCounter.refs.dec}"
277
+ aria-label="Decrement"
278
+ >
279
+ <button>-</button>
280
+ </${CountBtn.TAG}>
281
+
282
+ <${CopyBtn.TAG}
283
+ class="copy"
284
+ data-ref="${MyCounter.refs.copy}"
285
+ ></${CopyBtn.TAG}>
286
+
287
+ <${CountBtn.TAG}
288
+ class="inc"
289
+ data-ref="${MyCounter.refs.inc}"
290
+ aria-label="Increment"
291
+ >
292
+ <button>+</button>
293
+ </${CountBtn.TAG}>
294
+ </div>
295
+ </${MyCounter.TAG}>
296
+ </div>`
297
+ }
298
+ ```
299
+
300
+ ----------------------------------------------------------------------
301
+ ## API
302
+ ----------------------------------------------------------------------
303
+
304
+ ### Props
305
+
306
+ Declare reactive attributes via `withProps`. Each prop becomes:
307
+
308
+ * An observed HTML attribute (auto-synced via `attributeChangedCallback`)
309
+ * A signal at `ctx.props.propName`
310
+ * A typed getter/setter on the element instance
311
+ * Four built-in validators coerce raw attribute strings to typed values:
312
+
313
+ | Validator | Coercion | `null` attr |
314
+ |-----------|----------|-------------|
315
+ | `p.string()` | `String(val)` | `""` |
316
+ | `p.number()` | `Number(val)` | `0` |
317
+ | `p.boolean()` | `"true"` / `""` &rarr; `true`, `"false"` &rarr; `false` | `false` |
318
+ | `p.oneOf(opts)` | Picklist enum, throws on invalid | throws |
319
+
320
+
321
+ #### `withProps`
93
322
 
94
323
  ```ts
95
324
  .withProps(p => ({
96
- count: p.number(), // attribute -> number (NaN when absent)
97
- label: p.string(), // attribute -> string ('' when absent)
98
- active: p.boolean(), // attribute -> boolean (true if present)
99
- data: p.json<MyType>(), // attribute -> JSON.parse or undefined
100
- schema: p.schema(zodSchema), // Standard Schema (Zod, Valibot, ArkType …)
101
- value: p.prop(0), // property-only, not observed as an attribute
325
+ count: p.number(), // attribute -> number (NaN when absent)
326
+ label: p.string(), // attribute -> string ('' when absent)
327
+ active: p.boolean(), // attribute -> boolean (true if present)
328
+ data: p.json<MyType>(), // attribute -> JSON.parse or undefined
329
+ schema: p.schema(zodSchema), // Standard Schema (Zod, Valibot, ArkType)
330
+ value: p.prop(0), // property-only, not an observed attribute
102
331
  }))
103
332
  ```
104
333
 
@@ -109,22 +338,86 @@ ctx.props.count() // read
109
338
  ctx.props.count(42) // write
110
339
  ```
111
340
 
112
- ### withRefs
341
+
342
+ ### Refs
343
+
344
+ The `data-ref` attribute is a fallback. It's used when you don't pass a
345
+ selector to `r.one()`. If you pass explicit CSS selectors to `withRefs`,
346
+ e.g. `{ copy: r.one('button') }`, then the HTML does not require a `data-ref`
347
+ attribute.
348
+
349
+ #### `withRefs`
350
+
351
+ Call `r.one()` / `r.all()` with no argument to match `[data-ref="<key>"]`,
352
+ where `key` is the key name you pass in, or pass a CSS selector to query by
353
+ it instead. Queries are resolved relative to the host element, during
354
+ [the `connectedCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks).
355
+
113
356
 
114
357
  ```ts
115
358
  .withRefs(r => ({
116
- button: r.one, // resolves [data-ref="button"] — throws if missing
117
- items: r.all, // resolves all [data-ref="items"] — empty array if none
359
+ button: r.one(), // resolves [data-ref="button"] — throws if missing
360
+ items: r.all(), // resolves all [data-ref="items"] — empty array if none
118
361
  }))
119
362
  ```
120
363
 
121
- Refs are resolved from within the element during `connectedCallback`.
364
+ If there are no `data-ref` attribtues in the HTML, use CSS query selectors.
122
365
 
123
366
  ```ts
124
- ctx.refs.button // Element
125
- ctx.refs.items // Element[]
367
+ .withRefs(r => ({
368
+ code: r.one('code'), // host.querySelector('code')
369
+ items: r.all('li'), // host.querySelectorAll('li')
370
+ }))
126
371
  ```
127
372
 
373
+ See [#refs in serverside-rendering](#refs). Each component created with
374
+ `micortags` exposes a `.refs` property that is an object map
375
+ of ref names, eg `MyComponent.refs.copyButton`.
376
+
377
+ #### Element types
378
+
379
+ An un-annotated `r.one()` is typed as `HTMLElement`, so `.style`,
380
+ `.textContent`, and `.dataset` work without a cast. There are two ways to
381
+ get a more specific element type:
382
+
383
+ ```ts
384
+ .withRefs(r => ({
385
+ // Generic argument: sets the type, keeps the [data-ref] selector.
386
+ display: r.one<HTMLDivElement>(),
387
+
388
+ // Tag string: infers the type AND queries by that tag, exactly like
389
+ // document.querySelector('button'). It does NOT keep [data-ref].
390
+ save: r.one('button'), // SingleRefDef<HTMLButtonElement>
391
+ items: r.all('li'), // MultipleRefDef<HTMLLIElement>
392
+ button: r.one(), // HTMLElement (un-annotated default)
393
+ }))
394
+ ```
395
+
396
+ These flow straight through to `ctx.refs`:
397
+
398
+ ```ts
399
+ ctx.refs.display // HTMLDivElement
400
+ ctx.refs.save // HTMLButtonElement
401
+ ctx.refs.items // HTMLLIElement[]
402
+ ctx.refs.button // HTMLElement (the un-annotated default)
403
+ ```
404
+
405
+ Non-HTML refs (SVG / MathML) are out of scope: the type parameter is
406
+ constrained to `HTMLElement`, so cast at the use site if you need one
407
+ (e.g. `ctx.refs.icon as unknown as SVGSVGElement`).
408
+
409
+
410
+ ### define
411
+
412
+ ```ts
413
+ import { define } from 'microtags'
414
+
415
+ define(tagName: string): ComponentBuilder
416
+ ```
417
+
418
+ Returns a fluent builder. Call `.setup()` to register the custom element.
419
+ All builder methods are chainable and fully type-inferred.
420
+
128
421
  ### withContexts
129
422
 
130
423
  ```ts
@@ -161,7 +454,7 @@ define('my-child')
161
454
  })
162
455
 
163
456
  // Bind a DOM property to a signal expression; auto-cleaned.
164
- ctx.bind(el, 'textContent', () => String(ctx.props.count()))
457
+ ctx.bind(() => String(ctx.props.count()), el, { prop: 'textContent' })
165
458
 
166
459
  // Add an event listener; auto-removed on disconnect.
167
460
  ctx.on(el, 'click', handler)
@@ -244,3 +537,7 @@ renderList(container, template, {
244
537
 
245
538
  Props declared with `withProps` are native alien-signals callable signals.
246
539
  There is no `$` prefix convention — all prop names are plain identifiers.
540
+
541
+ ```
542
+ /ed3d-plan-and-execute:execute-implementation-plan /Users/nick/code/_microtags/docs/implementation-plans/2026-06-28-zod-validation-example/ /Users/nick/code/_microtags/
543
+ ```
@@ -0,0 +1,10 @@
1
+ export type Attrs = Record<string, undefined | null | string | number | boolean | (string | number)[]>;
2
+ /**
3
+ * Transform an object into an HTML attributes string. The object should be
4
+ * like `{ attributeName: value }`.
5
+ *
6
+ * @param attrs An object for the attributes.
7
+ * @returns {string} A string suitable for use as HTML attributes.
8
+ */
9
+ export declare function toAttributes(attrs: Attrs): string;
10
+ //# sourceMappingURL=attributes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attributes.d.ts","sourceRoot":"","sources":["../src/attributes.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,SAAS,GAAC,IAAI,GAAC,MAAM,GAAC,MAAM,GAAC,OAAO,GAAC,CAAC,MAAM,GAAC,MAAM,CAAC,EAAE,CAAC,CAAA;AAE1F;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAE,KAAK,EAAC,KAAK,GAAE,MAAM,CAgBhD"}
@@ -12,11 +12,20 @@ export type PropDSL = {
12
12
  export declare const propDSL: PropDSL;
13
13
  /**
14
14
  * DSL object passed to the withRefs callback.
15
- * Assign `r.one` for a single element or `r.all` for multiple.
15
+ * Call `r.one()` for a single element or `r.all()` for multiple. Pass a
16
+ * CSS selector to query by it, or omit it to match `[data-ref="<key>"]`.
17
+ *
18
+ * Element typing:
19
+ * - `r.one()` defaults to `HTMLElement`.
20
+ * - `r.one<T>()` sets the type and keeps the `[data-ref]` selector.
21
+ * - `r.one('button')` infers the type from the tag AND queries by that
22
+ * tag, exactly like `document.querySelector('button')`.
16
23
  */
17
24
  export type RefsDSL = {
18
- readonly one: SingleRefDef;
19
- readonly all: MultipleRefDef;
25
+ one<K extends keyof HTMLElementTagNameMap>(selector: K): SingleRefDef<HTMLElementTagNameMap[K]>;
26
+ one<T extends HTMLElement = HTMLElement>(selector?: string): SingleRefDef<T>;
27
+ all<K extends keyof HTMLElementTagNameMap>(selector: K): MultipleRefDef<HTMLElementTagNameMap[K]>;
28
+ all<T extends HTMLElement = HTMLElement>(selector?: string): MultipleRefDef<T>;
20
29
  };
21
30
  export declare const refsDSL: RefsDSL;
22
31
  /**
@@ -25,6 +34,30 @@ export declare const refsDSL: RefsDSL;
25
34
  * context tokens directly in the returned record.
26
35
  */
27
36
  export type ContextDSL = Record<never, never>;
37
+ /**
38
+ * Maps each declared ref key to its own name as a string literal. Exposed
39
+ * as the static `refs` on a registered element so server-rendered templates
40
+ * can fill in `data-ref` values without hardcoding the names, e.g.
41
+ * `data-ref="${myCounter.refs.dec}"`.
42
+ */
43
+ export type RefNames<RefDefs extends Record<string, RefDef>> = {
44
+ readonly [K in keyof RefDefs]: K & string;
45
+ };
46
+ /**
47
+ * A registered custom element class that also exposes, as static members,
48
+ * the tag name it was registered under (`TAG`) and the names of its
49
+ * declared refs (`refs`).
50
+ */
51
+ export type MicrotagElementClass<RefDefs extends Record<string, RefDef> = Record<never, never>> = typeof HTMLElement & {
52
+ /** The custom element tag name, e.g. 'my-counter'. */
53
+ readonly TAG: string;
54
+ /**
55
+ * The declared ref names, each mapping to its own `data-ref` string.
56
+ * Lets server-rendered markup reference refs by key instead of a magic
57
+ * string: `myCounter.refs.dec`.
58
+ */
59
+ readonly refs: RefNames<RefDefs>;
60
+ };
28
61
  /**
29
62
  * Fluent builder that accumulates props/refs/contexts config
30
63
  * before registering a custom element via `.setup()`.
@@ -36,10 +69,16 @@ export declare class ComponentBuilder<Props extends Record<string, PropDef> = Re
36
69
  private readonly _refDefs;
37
70
  private readonly _ctxDefs;
38
71
  constructor(tagName: string, coercers?: Record<string, Coercer<unknown>>, observed?: string[], refDefs?: Record<string, RefDef>, ctxDefs?: Record<string, ContextToken<unknown>>);
72
+ /**
73
+ * The custom element tag name, e.g. 'my-counter'. Available at every
74
+ * point in the fluent chain since each builder method returns a new
75
+ * builder carrying the same tag name.
76
+ */
77
+ get TAG(): string;
39
78
  withProps<P extends Record<string, PropDef>>(fn: (p: PropDSL) => P): ComponentBuilder<P, RefDefs, CtxDefs>;
40
79
  withRefs<R extends Record<string, RefDef>>(fn: (r: RefsDSL) => R): ComponentBuilder<Props, R, CtxDefs>;
41
80
  withContexts<C extends Record<string, ContextToken<unknown>>>(fn: (c: ContextDSL) => C): ComponentBuilder<Props, RefDefs, C>;
42
- setup(fn: (ctx: SetupContext<Props, RefsMap<RefDefs>, ContextsMap<CtxDefs>>) => void): typeof HTMLElement;
81
+ setup(fn: (ctx: SetupContext<Props, RefsMap<RefDefs>, ContextsMap<CtxDefs>>) => void): MicrotagElementClass<RefDefs>;
43
82
  }
44
83
  /** Create a new component builder for the given tag name. */
45
84
  export declare function define(tagName: string): ComponentBuilder;
@@ -1 +1 @@
1
- {"version":3,"file":"builders.d.ts","sourceRoot":"","sources":["../src/builders.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAC7D,OAAO,KAAK,EACR,OAAO,EACP,gBAAgB,EAChB,YAAY,EACZ,OAAO,EACP,MAAM,EACN,YAAY,EACZ,cAAc,EACd,OAAO,EACP,YAAY,EACZ,WAAW,EACd,MAAM,YAAY,CAAA;AAiDnB,uDAAuD;AACvD,MAAM,MAAM,OAAO,GAAG;IAClB,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC,CAAA;IACzB,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC,CAAA;IACzB,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,CAAA;IAC3B,IAAI,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAA;IACjC,MAAM,CAAC,CAAC,EAAE,SAAS,EAAE,gBAAgB,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,gBAAgB,CAAC,OAAO,EAAE,CAAC,CAAC,CAAA;IAChF,IAAI,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAAA;CAC3C,CAAA;AAED,eAAO,MAAM,OAAO,EAAE,OASrB,CAAA;AAED;;;GAGG;AACH,MAAM,MAAM,OAAO,GAAG;IAClB,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAA;IAC1B,QAAQ,CAAC,GAAG,EAAE,cAAc,CAAA;CAC/B,CAAA;AAED,eAAO,MAAM,OAAO,EAAE,OAGrB,CAAA;AAED;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;AAG7C;;;GAGG;AACH,qBAAa,gBAAgB,CACzB,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EAC5D,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EAC7D,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC,GACjD,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC;IAQpB,QAAQ,CAAC,OAAO,EAAE,MAAM;IAN5B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAkC;IAC5D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAU;IACpC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAwB;IACjD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAuC;gBAGnD,OAAO,EAAE,MAAM,EACxB,QAAQ,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,CAAM,EAC/C,QAAQ,GAAE,MAAM,EAAO,EACvB,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,EACpC,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,OAAO,CAAC,CAAM;IAQvD,SAAS,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACvC,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,CAAC,GACtB,gBAAgB,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC;IAiBxC,QAAQ,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACrC,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,CAAC,GACtB,gBAAgB,CAAC,KAAK,EAAE,CAAC,EAAE,OAAO,CAAC;IAWtC,YAAY,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC,EACxD,EAAE,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,CAAC,GACzB,gBAAgB,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;IAWtC,KAAK,CACD,EAAE,EAAE,CAAC,GAAG,EAAE,YAAY,CAClB,KAAK,EACL,OAAO,CAAC,OAAO,CAAC,EAChB,WAAW,CAAC,OAAO,CAAC,CACvB,KAAK,IAAI,GACX,OAAO,WAAW;CAWxB;AAED,6DAA6D;AAC7D,wBAAgB,MAAM,CAAE,OAAO,EAAE,MAAM,GAAG,gBAAgB,CAEzD"}
1
+ {"version":3,"file":"builders.d.ts","sourceRoot":"","sources":["../src/builders.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
+
4
+ export {
5
+ __name
6
+ };
7
+ //# sourceMappingURL=chunk-7QVYU63E.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": [],
4
+ "sourcesContent": [],
5
+ "mappings": "",
6
+ "names": []
7
+ }