hyper-element 1.1.0 → 2.0.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/README.md +560 -21
- package/build/hyperElement.min.js +14 -1
- package/build/hyperElement.min.js.map +3 -3
- package/index.d.ts +344 -3
- package/package.json +13 -8
package/index.d.ts
CHANGED
|
@@ -133,21 +133,40 @@ export type SetupFunction = (
|
|
|
133
133
|
*/
|
|
134
134
|
export type MethodFunction = (ctx: ElementContext, ...args: any[]) => any;
|
|
135
135
|
|
|
136
|
+
/**
|
|
137
|
+
* SSR hydration lifecycle hook for functional components.
|
|
138
|
+
* Called before buffered events are replayed.
|
|
139
|
+
*/
|
|
140
|
+
export type OnBeforeHydrateFunction = (
|
|
141
|
+
ctx: ElementContext,
|
|
142
|
+
events: BufferedEvent[]
|
|
143
|
+
) => BufferedEvent[];
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* SSR hydration lifecycle hook for functional components.
|
|
147
|
+
* Called after event replay completes.
|
|
148
|
+
*/
|
|
149
|
+
export type OnAfterHydrateFunction = (ctx: ElementContext) => void;
|
|
150
|
+
|
|
136
151
|
/**
|
|
137
152
|
* Functional component definition object.
|
|
153
|
+
* Note: observedAttributes is not needed - all attributes are automatically reactive via MutationObserver.
|
|
138
154
|
*/
|
|
139
155
|
export interface FunctionalDefinition {
|
|
140
|
-
/** Attributes to observe for changes */
|
|
141
|
-
observedAttributes?: string[];
|
|
142
156
|
/** Setup lifecycle function */
|
|
143
157
|
setup?: SetupFunction;
|
|
144
158
|
/** Render function (required) */
|
|
145
159
|
render: RenderFunction;
|
|
160
|
+
/** SSR hydration hook - filter events before replay */
|
|
161
|
+
onBeforeHydrate?: OnBeforeHydrateFunction;
|
|
162
|
+
/** SSR hydration hook - called after replay completes */
|
|
163
|
+
onAfterHydrate?: OnAfterHydrateFunction;
|
|
146
164
|
/** Additional methods */
|
|
147
165
|
[key: string]:
|
|
148
|
-
| string[]
|
|
149
166
|
| SetupFunction
|
|
150
167
|
| RenderFunction
|
|
168
|
+
| OnBeforeHydrateFunction
|
|
169
|
+
| OnAfterHydrateFunction
|
|
151
170
|
| MethodFunction
|
|
152
171
|
| undefined;
|
|
153
172
|
}
|
|
@@ -189,6 +208,9 @@ export interface HyperElementFactory {
|
|
|
189
208
|
|
|
190
209
|
/** Prototype for class inheritance */
|
|
191
210
|
prototype: hyperElement;
|
|
211
|
+
|
|
212
|
+
/** Configure SSR hydration settings */
|
|
213
|
+
configureSSR(options: SSRConfig): void;
|
|
192
214
|
}
|
|
193
215
|
|
|
194
216
|
/**
|
|
@@ -273,6 +295,35 @@ export class hyperElement extends HTMLElement {
|
|
|
273
295
|
* ```
|
|
274
296
|
*/
|
|
275
297
|
render(Html: HtmlFunction, ...data: any[]): void;
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* SSR hydration lifecycle hook. Called before buffered events are replayed.
|
|
301
|
+
* Override to filter or modify events before replay.
|
|
302
|
+
*
|
|
303
|
+
* @param events - Buffered events captured during SSR
|
|
304
|
+
* @returns Filtered events to replay
|
|
305
|
+
*
|
|
306
|
+
* @example
|
|
307
|
+
* ```javascript
|
|
308
|
+
* onBeforeHydrate(events) {
|
|
309
|
+
* return events.filter(e => e.type !== 'focus');
|
|
310
|
+
* }
|
|
311
|
+
* ```
|
|
312
|
+
*/
|
|
313
|
+
onBeforeHydrate?(events: BufferedEvent[]): BufferedEvent[];
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* SSR hydration lifecycle hook. Called after event replay completes.
|
|
317
|
+
* Override to perform post-hydration setup.
|
|
318
|
+
*
|
|
319
|
+
* @example
|
|
320
|
+
* ```javascript
|
|
321
|
+
* onAfterHydrate() {
|
|
322
|
+
* console.log('Component hydrated');
|
|
323
|
+
* }
|
|
324
|
+
* ```
|
|
325
|
+
*/
|
|
326
|
+
onAfterHydrate?(): void;
|
|
276
327
|
}
|
|
277
328
|
|
|
278
329
|
/**
|
|
@@ -294,3 +345,293 @@ declare global {
|
|
|
294
345
|
|
|
295
346
|
export { hyperElement };
|
|
296
347
|
export default hyperElement;
|
|
348
|
+
|
|
349
|
+
// ============================================================================
|
|
350
|
+
// Signals API - Fine-grained reactivity primitives
|
|
351
|
+
// ============================================================================
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* A reactive signal that holds a value and notifies subscribers when it changes.
|
|
355
|
+
*/
|
|
356
|
+
export interface Signal<T> {
|
|
357
|
+
/** Get or set the current value. Reading tracks dependencies. */
|
|
358
|
+
value: T;
|
|
359
|
+
/** Read the current value without tracking dependencies. */
|
|
360
|
+
peek(): T;
|
|
361
|
+
/** Subscribe to value changes. Returns an unsubscribe function. */
|
|
362
|
+
subscribe(fn: () => void): () => void;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* A computed signal that derives its value from other signals.
|
|
367
|
+
*/
|
|
368
|
+
export interface Computed<T> {
|
|
369
|
+
/** Get the computed value. Automatically tracks dependencies and recomputes when they change. */
|
|
370
|
+
readonly value: T;
|
|
371
|
+
/** Read the computed value without tracking dependencies. */
|
|
372
|
+
peek(): T;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Creates a reactive signal.
|
|
377
|
+
* @param initialValue - The initial value of the signal
|
|
378
|
+
* @returns A signal object with value getter/setter, peek(), and subscribe()
|
|
379
|
+
*
|
|
380
|
+
* @example
|
|
381
|
+
* ```javascript
|
|
382
|
+
* const count = signal(0);
|
|
383
|
+
* count.value; // 0
|
|
384
|
+
* count.value = 1; // Updates and notifies subscribers
|
|
385
|
+
* count.peek(); // Read without tracking
|
|
386
|
+
* ```
|
|
387
|
+
*/
|
|
388
|
+
export function signal<T>(initialValue: T): Signal<T>;
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Creates a computed signal that derives from other signals.
|
|
392
|
+
* The computation is lazy and cached until dependencies change.
|
|
393
|
+
* @param fn - Computation function that reads other signals
|
|
394
|
+
* @returns A computed signal object with value getter and peek()
|
|
395
|
+
*
|
|
396
|
+
* @example
|
|
397
|
+
* ```javascript
|
|
398
|
+
* const count = signal(0);
|
|
399
|
+
* const doubled = computed(() => count.value * 2);
|
|
400
|
+
* doubled.value; // 0
|
|
401
|
+
* count.value = 5;
|
|
402
|
+
* doubled.value; // 10
|
|
403
|
+
* ```
|
|
404
|
+
*/
|
|
405
|
+
export function computed<T>(fn: () => T): Computed<T>;
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Creates an effect that runs when its dependencies change.
|
|
409
|
+
* The effect runs immediately and re-runs whenever any signal it reads changes.
|
|
410
|
+
* @param fn - Effect function. Can return a cleanup function.
|
|
411
|
+
* @returns Cleanup function to stop the effect
|
|
412
|
+
*
|
|
413
|
+
* @example
|
|
414
|
+
* ```javascript
|
|
415
|
+
* const count = signal(0);
|
|
416
|
+
* const cleanup = effect(() => {
|
|
417
|
+
* console.log('Count:', count.value);
|
|
418
|
+
* return () => console.log('Cleanup');
|
|
419
|
+
* });
|
|
420
|
+
* // Logs: "Count: 0"
|
|
421
|
+
* count.value = 1;
|
|
422
|
+
* // Logs: "Cleanup", then "Count: 1"
|
|
423
|
+
* cleanup(); // Stop the effect
|
|
424
|
+
* ```
|
|
425
|
+
*/
|
|
426
|
+
export function effect(fn: () => void | (() => void)): () => void;
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Batches multiple signal updates into a single notification.
|
|
430
|
+
* Effects are deferred until the batch completes.
|
|
431
|
+
* @param fn - Function containing signal updates
|
|
432
|
+
*
|
|
433
|
+
* @example
|
|
434
|
+
* ```javascript
|
|
435
|
+
* const a = signal(0);
|
|
436
|
+
* const b = signal(0);
|
|
437
|
+
* batch(() => {
|
|
438
|
+
* a.value = 1;
|
|
439
|
+
* b.value = 2;
|
|
440
|
+
* }); // Effects run once after both updates
|
|
441
|
+
* ```
|
|
442
|
+
*/
|
|
443
|
+
export function batch(fn: () => void): void;
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Runs a function without tracking signal dependencies.
|
|
447
|
+
* Useful for reading signals without creating subscriptions.
|
|
448
|
+
* @param fn - Function to run untracked
|
|
449
|
+
* @returns The return value of fn
|
|
450
|
+
*
|
|
451
|
+
* @example
|
|
452
|
+
* ```javascript
|
|
453
|
+
* const count = signal(0);
|
|
454
|
+
* effect(() => {
|
|
455
|
+
* const val = untracked(() => count.value);
|
|
456
|
+
* // This effect won't re-run when count changes
|
|
457
|
+
* });
|
|
458
|
+
* ```
|
|
459
|
+
*/
|
|
460
|
+
export function untracked<T>(fn: () => T): T;
|
|
461
|
+
|
|
462
|
+
// ============================================================================
|
|
463
|
+
// SSR Hydration API
|
|
464
|
+
// ============================================================================
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* Buffered event captured during SSR hydration.
|
|
468
|
+
*/
|
|
469
|
+
export interface BufferedEvent {
|
|
470
|
+
/** Event type (e.g., 'click', 'input') */
|
|
471
|
+
type: string;
|
|
472
|
+
/** Path from custom element to target (e.g., "DIV:0/BUTTON:1") */
|
|
473
|
+
targetPath: string;
|
|
474
|
+
/** Timestamp when event was captured */
|
|
475
|
+
timestamp: number;
|
|
476
|
+
/** Event-specific details */
|
|
477
|
+
detail: Record<string, any>;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* SSR configuration options.
|
|
482
|
+
*/
|
|
483
|
+
export interface SSRConfig {
|
|
484
|
+
/** Event types to capture during SSR hydration (default: all interactive events) */
|
|
485
|
+
events?: string[];
|
|
486
|
+
/** Show visual indicator in development mode (default: false) */
|
|
487
|
+
devMode?: boolean;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* Configure SSR hydration settings.
|
|
492
|
+
* Call this before any custom elements are defined to customize behavior.
|
|
493
|
+
*
|
|
494
|
+
* @param options - Configuration options
|
|
495
|
+
*
|
|
496
|
+
* @example
|
|
497
|
+
* ```javascript
|
|
498
|
+
* // Enable dev mode indicator
|
|
499
|
+
* hyperElement.configureSSR({ devMode: true });
|
|
500
|
+
*
|
|
501
|
+
* // Customize captured events
|
|
502
|
+
* hyperElement.configureSSR({
|
|
503
|
+
* events: ['click', 'input', 'submit'],
|
|
504
|
+
* devMode: true
|
|
505
|
+
* });
|
|
506
|
+
* ```
|
|
507
|
+
*/
|
|
508
|
+
export function configureSSR(options: SSRConfig): void;
|
|
509
|
+
|
|
510
|
+
// ============================================================================
|
|
511
|
+
// SSR String Rendering API (Node.js/Server-side)
|
|
512
|
+
// ============================================================================
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* Options for renderElement function.
|
|
516
|
+
*/
|
|
517
|
+
export interface RenderElementOptions<T = any> {
|
|
518
|
+
/** Attributes to pass to the component */
|
|
519
|
+
attrs?: Record<string, unknown>;
|
|
520
|
+
/** Store data for render context */
|
|
521
|
+
store?: T;
|
|
522
|
+
/** Wrap content in Declarative Shadow DOM template (default: false) */
|
|
523
|
+
shadowDOM?: boolean;
|
|
524
|
+
/** Fragment functions for the component */
|
|
525
|
+
fragments?: Record<string, (data: any) => any>;
|
|
526
|
+
/** Render function (Html, ctx) => void */
|
|
527
|
+
render: (Html: HtmlFunction, ctx: ElementContext) => void;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* Renders a component definition to an HTML string.
|
|
532
|
+
* Use this in Node.js/Deno/Bun for server-side rendering.
|
|
533
|
+
*
|
|
534
|
+
* @param tagName - Custom element tag name (e.g., 'my-component')
|
|
535
|
+
* @param options - Render options
|
|
536
|
+
* @returns Promise resolving to HTML string
|
|
537
|
+
*
|
|
538
|
+
* @example
|
|
539
|
+
* ```javascript
|
|
540
|
+
* const { renderElement } = require('hyper-element/ssr/server');
|
|
541
|
+
*
|
|
542
|
+
* const html = await renderElement('my-greeting', {
|
|
543
|
+
* attrs: { name: 'World' },
|
|
544
|
+
* render: (Html, ctx) => Html`<div>Hello ${ctx.attrs.name}!</div>`
|
|
545
|
+
* });
|
|
546
|
+
* // Returns: '<my-greeting name="World"><div>Hello World!</div></my-greeting>'
|
|
547
|
+
*
|
|
548
|
+
* // With Declarative Shadow DOM
|
|
549
|
+
* const shadowHtml = await renderElement('my-greeting', {
|
|
550
|
+
* attrs: { name: 'World' },
|
|
551
|
+
* shadowDOM: true,
|
|
552
|
+
* render: (Html, ctx) => Html`<div>Hello ${ctx.attrs.name}!</div>`
|
|
553
|
+
* });
|
|
554
|
+
* // Returns: '<my-greeting name="World"><template shadowrootmode="open"><div>Hello World!</div></template></my-greeting>'
|
|
555
|
+
* ```
|
|
556
|
+
*/
|
|
557
|
+
export function renderElement<T = any>(
|
|
558
|
+
tagName: string,
|
|
559
|
+
options: RenderElementOptions<T>
|
|
560
|
+
): Promise<string>;
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* Renders multiple elements in parallel.
|
|
564
|
+
* Useful for batch SSR rendering.
|
|
565
|
+
*
|
|
566
|
+
* @param elements - Array of elements to render
|
|
567
|
+
* @returns Promise resolving to array of HTML strings
|
|
568
|
+
*/
|
|
569
|
+
export function renderElements(
|
|
570
|
+
elements: Array<{ tagName: string; options: RenderElementOptions }>
|
|
571
|
+
): Promise<string[]>;
|
|
572
|
+
|
|
573
|
+
/**
|
|
574
|
+
* Creates a reusable component renderer.
|
|
575
|
+
* Useful for rendering the same component multiple times with different data.
|
|
576
|
+
*
|
|
577
|
+
* @param tagName - Custom element tag name
|
|
578
|
+
* @param render - Render function
|
|
579
|
+
* @param baseOptions - Base options merged with each render
|
|
580
|
+
* @returns Renderer function
|
|
581
|
+
*/
|
|
582
|
+
export function createRenderer<T = any>(
|
|
583
|
+
tagName: string,
|
|
584
|
+
render: (Html: HtmlFunction, ctx: ElementContext) => void,
|
|
585
|
+
baseOptions?: Partial<RenderElementOptions<T>>
|
|
586
|
+
): (attrs?: Record<string, unknown>, store?: T) => Promise<string>;
|
|
587
|
+
|
|
588
|
+
/**
|
|
589
|
+
* Renders a tagged template literal to an HTML string.
|
|
590
|
+
* Lower-level API for direct template rendering.
|
|
591
|
+
*
|
|
592
|
+
* @param template - Template strings array
|
|
593
|
+
* @param values - Interpolated values
|
|
594
|
+
* @param xml - SVG/XML mode (default: false)
|
|
595
|
+
* @returns HTML string
|
|
596
|
+
*/
|
|
597
|
+
export function renderToString(
|
|
598
|
+
template: TemplateStringsArray,
|
|
599
|
+
values: unknown[],
|
|
600
|
+
xml?: boolean
|
|
601
|
+
): string;
|
|
602
|
+
|
|
603
|
+
/**
|
|
604
|
+
* Creates an SSR Html tagged template function.
|
|
605
|
+
* Use this for custom SSR rendering scenarios.
|
|
606
|
+
*
|
|
607
|
+
* @param context - Render context with attrs, store, fragments
|
|
608
|
+
* @returns Html function with wire, raw, lite methods
|
|
609
|
+
*/
|
|
610
|
+
export function createSSRHtml(context?: {
|
|
611
|
+
attrs?: Record<string, unknown>;
|
|
612
|
+
store?: any;
|
|
613
|
+
fragments?: Record<string, (data: any) => any>;
|
|
614
|
+
}): HtmlFunction;
|
|
615
|
+
|
|
616
|
+
/**
|
|
617
|
+
* SSR HTML tagged template function.
|
|
618
|
+
* Renders directly to string without any component wrapper.
|
|
619
|
+
*/
|
|
620
|
+
export const ssrHtml: (
|
|
621
|
+
strings: TemplateStringsArray,
|
|
622
|
+
...values: unknown[]
|
|
623
|
+
) => string;
|
|
624
|
+
|
|
625
|
+
/**
|
|
626
|
+
* Escapes HTML special characters to prevent XSS.
|
|
627
|
+
* @param str - String to escape
|
|
628
|
+
* @returns Escaped string
|
|
629
|
+
*/
|
|
630
|
+
export function escapeHtml(str: string): string;
|
|
631
|
+
|
|
632
|
+
/**
|
|
633
|
+
* Marks a string as safe HTML that should not be escaped.
|
|
634
|
+
* @param html - HTML string to mark as safe
|
|
635
|
+
* @returns Safe HTML object
|
|
636
|
+
*/
|
|
637
|
+
export function safeHtml(html: string): { value: string };
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "
|
|
2
|
+
"version": "2.0.0",
|
|
3
3
|
"name": "hyper-element",
|
|
4
4
|
"author": "Brian Shannon (github.com/codemeasandwich)",
|
|
5
5
|
"description": "Fast, lightweight web components",
|
|
@@ -12,11 +12,14 @@
|
|
|
12
12
|
],
|
|
13
13
|
"scripts": {
|
|
14
14
|
"build": "node scripts/build.js",
|
|
15
|
-
"test": "npm run
|
|
16
|
-
"test:
|
|
17
|
-
"test:
|
|
18
|
-
"test:
|
|
19
|
-
"test:
|
|
15
|
+
"test": "npm run test:src && npm run test:bundle",
|
|
16
|
+
"test:src": "rm -rf coverage && playwright test --project=source-coverage && npm run test:ssr-coverage && npm run test:coverage-report",
|
|
17
|
+
"test:bundle": "npm run build && playwright test --project=bundle-verify; rm -rf build",
|
|
18
|
+
"test:ssr": "node test/ssr.test.mjs",
|
|
19
|
+
"test:ssr-coverage": "node test/ssr-with-coverage.mjs",
|
|
20
|
+
"test:coverage-report": "node test/coverage-report.mjs",
|
|
21
|
+
"test:ui": "playwright test --project=source-coverage --ui",
|
|
22
|
+
"test:headed": "playwright test --project=source-coverage --headed",
|
|
20
23
|
"kitchensink": "npm run build && cp -r build kitchensink/ && (sleep 1 && open http://localhost:3000) & npx serve ./kitchensink",
|
|
21
24
|
"release": "bash scripts/publish.sh",
|
|
22
25
|
"hooks:install": "cp .hooks/pre-commit .git/hooks/ && cp .hooks/commit-msg .git/hooks/ && cp -r .hooks/pre-commit.d .git/hooks/ && chmod +x .git/hooks/pre-commit .git/hooks/commit-msg .git/hooks/pre-commit.d/*.sh",
|
|
@@ -50,14 +53,16 @@
|
|
|
50
53
|
"eslint-config-prettier": "^9.1.2",
|
|
51
54
|
"eslint-plugin-prettier": "^5.5.5",
|
|
52
55
|
"husky": "^9.1.7",
|
|
56
|
+
"istanbul-lib-coverage": "^3.2.2",
|
|
57
|
+
"istanbul-lib-report": "^3.0.1",
|
|
58
|
+
"istanbul-reports": "^3.2.0",
|
|
53
59
|
"lint-staged": "^15.5.2",
|
|
54
|
-
"lodash": "^4.17.21",
|
|
55
60
|
"minimist": "^1.2.8",
|
|
56
61
|
"mkdirp": "^1.0.4",
|
|
57
62
|
"prettier": "^3.8.0",
|
|
58
63
|
"serve": "^14.2.5",
|
|
59
64
|
"set-value": "^2.0.1",
|
|
60
|
-
"
|
|
65
|
+
"source-map": "^0.7.6",
|
|
61
66
|
"union-value": "^2.0.1",
|
|
62
67
|
"v8-to-istanbul": "^9.3.0"
|
|
63
68
|
}
|