@coherent.js/core 1.1.2 → 2.0.0-rc.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 +60 -72
- package/dist/index.js +849 -506
- package/dist/index.js.map +3 -3
- package/package.json +1 -1
- package/types/index.d.ts +123 -21
package/package.json
CHANGED
package/types/index.d.ts
CHANGED
|
@@ -20,15 +20,20 @@ export type Primitive = string | number | boolean | null | undefined;
|
|
|
20
20
|
/** Allow objects and functions in attributes */
|
|
21
21
|
export type AttributeValue = Primitive | object;
|
|
22
22
|
|
|
23
|
+
/** Value accepted by `className` / `class` */
|
|
24
|
+
export type ClassValue = string | ReadonlyArray<ClassValue | false | null | undefined> | Record<string, unknown>;
|
|
25
|
+
|
|
23
26
|
/** HTML attributes object */
|
|
24
27
|
export interface HTMLAttributes {
|
|
25
28
|
[key: string]: AttributeValue;
|
|
26
|
-
className
|
|
27
|
-
|
|
29
|
+
/** A string, an array (falsy entries dropped) or an object of `{ className: condition }` */
|
|
30
|
+
className?: ClassValue;
|
|
31
|
+
class?: ClassValue;
|
|
28
32
|
id?: string;
|
|
29
33
|
style?: string | Record<string, string | number>;
|
|
30
|
-
|
|
31
|
-
|
|
34
|
+
/** Inline code, or a function attached by @coherent.js/client's hydrate() (not rendered on the server) */
|
|
35
|
+
onClick?: string | ((event: any) => void);
|
|
36
|
+
onSubmit?: string | ((event: any) => void);
|
|
32
37
|
href?: string;
|
|
33
38
|
src?: string;
|
|
34
39
|
alt?: string;
|
|
@@ -385,13 +390,16 @@ export interface WithStateHOC {
|
|
|
385
390
|
|
|
386
391
|
/** Memoization options */
|
|
387
392
|
export interface MemoOptions {
|
|
393
|
+
/**
|
|
394
|
+
* 'lru' (default) and 'ttl' keep at most `maxSize` entries; 'simple' is an
|
|
395
|
+
* unbounded Map; 'weak' keys on the identity of the first argument.
|
|
396
|
+
*/
|
|
388
397
|
strategy?: 'lru' | 'ttl' | 'weak' | 'simple';
|
|
389
398
|
maxSize?: number;
|
|
399
|
+
/** Entry lifetime in milliseconds (default 5000 with the 'ttl' strategy). */
|
|
390
400
|
ttl?: number;
|
|
391
401
|
keyFn?: (...args: any[]) => string;
|
|
392
402
|
keySerializer?: (value: any) => string;
|
|
393
|
-
compareFn?: (a: any, b: any) => boolean;
|
|
394
|
-
shallow?: boolean;
|
|
395
403
|
onHit?: (key: string, value: any, args: any[]) => void;
|
|
396
404
|
onMiss?: (key: string, args: any[]) => void;
|
|
397
405
|
onEvict?: (key: string, value: any) => void;
|
|
@@ -516,12 +524,26 @@ export interface PerformanceMetrics {
|
|
|
516
524
|
|
|
517
525
|
/** Render options for `render(component, options)` */
|
|
518
526
|
export interface RenderOptions {
|
|
527
|
+
/**
|
|
528
|
+
* Cache the HTML of whole renders, keyed on the full component tree.
|
|
529
|
+
* Off by default; trees containing functions are never cached.
|
|
530
|
+
*/
|
|
519
531
|
enableCache?: boolean;
|
|
532
|
+
/** Cache instance (from createCacheManager) to use instead of the shared one. */
|
|
533
|
+
cache?: CacheManager;
|
|
520
534
|
enableMonitoring?: boolean;
|
|
521
535
|
minify?: boolean;
|
|
522
536
|
maxDepth?: number;
|
|
537
|
+
/** @deprecated Ignored. Pass `cache: createCacheManager({ maxCacheSize })` instead. */
|
|
523
538
|
cacheSize?: number;
|
|
539
|
+
/** Time-to-live in milliseconds for cache entries this render adds. */
|
|
524
540
|
cacheTTL?: number;
|
|
541
|
+
/**
|
|
542
|
+
* Called when a function component throws. Its return value is rendered in
|
|
543
|
+
* place of the component (`null` omits it). Without it, the error
|
|
544
|
+
* propagates out of render().
|
|
545
|
+
*/
|
|
546
|
+
onError?: (error: unknown, info: { path: string }) => CoherentNode;
|
|
525
547
|
scoped?: boolean;
|
|
526
548
|
encapsulate?: boolean;
|
|
527
549
|
}
|
|
@@ -529,6 +551,49 @@ export interface RenderOptions {
|
|
|
529
551
|
/** Render a Coherent node to an HTML string */
|
|
530
552
|
export function render(component: CoherentNode, options?: RenderOptions): string;
|
|
531
553
|
|
|
554
|
+
export interface StreamOptions extends Omit<RenderOptions, 'enableCache' | 'cache' | 'cacheSize' | 'cacheTTL' | 'minify'> {
|
|
555
|
+
/** Approximate size of each yielded chunk in characters (default 8192). */
|
|
556
|
+
chunkSize?: number;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
/**
|
|
560
|
+
* Stream a Coherent node as HTML chunks with the same output as render().
|
|
561
|
+
* The event loop gets a turn after every chunk. Errors reject the iteration.
|
|
562
|
+
*
|
|
563
|
+
* @example
|
|
564
|
+
* Readable.from(renderToStream(Page())).pipe(res);
|
|
565
|
+
*/
|
|
566
|
+
export function renderToStream(component: CoherentNode, options?: StreamOptions): AsyncGenerator<string, void, undefined>;
|
|
567
|
+
|
|
568
|
+
export interface StreamingResponse {
|
|
569
|
+
headersSent?: boolean;
|
|
570
|
+
getHeader?(name: string): unknown;
|
|
571
|
+
setHeader(name: string, value: string): void;
|
|
572
|
+
write(chunk: string): boolean;
|
|
573
|
+
/** Set once the connection is gone (Node's `ServerResponse#destroyed`). */
|
|
574
|
+
destroyed?: boolean;
|
|
575
|
+
on(event: 'drain' | 'close' | 'error', listener: (...args: unknown[]) => void): unknown;
|
|
576
|
+
off(event: 'drain' | 'close' | 'error', listener: (...args: unknown[]) => void): unknown;
|
|
577
|
+
end(): void;
|
|
578
|
+
destroy(error?: Error): void;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
export const streamingUtils: {
|
|
582
|
+
/** Concatenate every chunk. */
|
|
583
|
+
collectChunks(chunks: AsyncIterable<string>): Promise<string>;
|
|
584
|
+
/**
|
|
585
|
+
* Write chunks to a Node response with backpressure; aborts it on error.
|
|
586
|
+
* Resolves to the byte count. If the client disconnects, rendering stops and
|
|
587
|
+
* it resolves with the bytes written so far.
|
|
588
|
+
*/
|
|
589
|
+
streamToResponse(chunks: AsyncIterable<string>, response: StreamingResponse): Promise<number>;
|
|
590
|
+
/** Re-yield chunks, reporting progress after each one. */
|
|
591
|
+
streamWithProgress(
|
|
592
|
+
chunks: AsyncIterable<string>,
|
|
593
|
+
onProgress?: (progress: { chunkCount: number; totalBytes: number; chunk: string }) => void
|
|
594
|
+
): AsyncGenerator<string, void, undefined>;
|
|
595
|
+
};
|
|
596
|
+
|
|
532
597
|
export interface RenderUtilityOptions {
|
|
533
598
|
enablePerformanceMonitoring?: boolean;
|
|
534
599
|
template?: string;
|
|
@@ -572,7 +637,7 @@ export const withState: WithStateHOC;
|
|
|
572
637
|
/** Memoization function */
|
|
573
638
|
export function memo<T extends (...args: any[]) => any>(
|
|
574
639
|
fn: T,
|
|
575
|
-
options?: MemoOptions
|
|
640
|
+
options?: MemoOptions | ((props: Parameters<T>[0]) => string)
|
|
576
641
|
): MemoizedFunction<T>;
|
|
577
642
|
|
|
578
643
|
/** Validate component structure */
|
|
@@ -715,6 +780,10 @@ export interface PerformanceMonitor {
|
|
|
715
780
|
startRender(componentName?: string): string;
|
|
716
781
|
endRender(renderId: string): number;
|
|
717
782
|
recordMetric(name: string, value: number, tags?: Record<string, any>): void;
|
|
783
|
+
/** Called by the renderer when `enableMonitoring` is on. */
|
|
784
|
+
recordRender(operation: string, duration: number, fromCache?: boolean, metadata?: Record<string, any>): void;
|
|
785
|
+
/** Called by the renderer when `enableMonitoring` is on. */
|
|
786
|
+
recordError(operation: string, error: unknown, metadata?: Record<string, any>): void;
|
|
718
787
|
addMetric(name: string, value: number, tags?: Record<string, any>): void;
|
|
719
788
|
measure<T>(name: string, fn: () => T): T;
|
|
720
789
|
measureAsync<T>(name: string, fn: () => Promise<T>): Promise<T>;
|
|
@@ -734,20 +803,43 @@ export const performanceMonitor: PerformanceMonitor;
|
|
|
734
803
|
|
|
735
804
|
/** Cache manager options */
|
|
736
805
|
export interface CacheManagerOptions {
|
|
806
|
+
/** Maximum entries per cache type (default 1000). */
|
|
807
|
+
maxCacheSize?: number;
|
|
808
|
+
/** Alias for `maxCacheSize`. */
|
|
737
809
|
maxSize?: number;
|
|
738
|
-
|
|
739
|
-
|
|
810
|
+
/** Memory budget across all cache types, keys included (default 100). */
|
|
811
|
+
maxMemoryMB?: number;
|
|
812
|
+
/** Default time-to-live in milliseconds (default 5 minutes). */
|
|
813
|
+
ttlMs?: number;
|
|
814
|
+
enableStatistics?: boolean;
|
|
740
815
|
}
|
|
741
816
|
|
|
742
|
-
|
|
817
|
+
export type CacheType = 'static' | 'component' | 'template' | 'data';
|
|
818
|
+
|
|
819
|
+
export interface CacheStats {
|
|
820
|
+
hits: number;
|
|
821
|
+
misses: number;
|
|
822
|
+
/** Approximate memory use in bytes. */
|
|
823
|
+
size: number;
|
|
824
|
+
entries: number;
|
|
825
|
+
hitRate: Record<CacheType, number>;
|
|
826
|
+
accessCount: Record<CacheType, number>;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
/** Cache manager: one least-recently-used map per cache type. */
|
|
743
830
|
export interface CacheManager {
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
clear(): void;
|
|
749
|
-
|
|
750
|
-
|
|
831
|
+
/** Returns the cached value, or null when missing or expired. */
|
|
832
|
+
get(key: string, type?: CacheType): any;
|
|
833
|
+
set(key: string, value: any, type?: CacheType, metadata?: { ttlMs?: number; [key: string]: any }): void;
|
|
834
|
+
remove(key: string, type?: CacheType): boolean;
|
|
835
|
+
clear(type?: CacheType): void;
|
|
836
|
+
getStats(): CacheStats;
|
|
837
|
+
cleanup(): { freed: number };
|
|
838
|
+
destroy(): void;
|
|
839
|
+
generateCacheKey(component: any, props?: Record<string, any>, context?: Record<string, any>): string;
|
|
840
|
+
hashObject(obj: any): string;
|
|
841
|
+
readonly memoryUsage: number;
|
|
842
|
+
readonly maxMemory: number;
|
|
751
843
|
}
|
|
752
844
|
|
|
753
845
|
/** Shared cache manager instance */
|
|
@@ -891,15 +983,24 @@ export function formatAttributes(props: Record<string, any>): string;
|
|
|
891
983
|
/** Mark content as trusted so it is emitted without escaping */
|
|
892
984
|
export function dangerouslySetInnerContent(content: string): TrustedContent;
|
|
893
985
|
|
|
894
|
-
/** Content marked trusted by dangerouslySetInnerContent() */
|
|
986
|
+
/** Content marked trusted by dangerouslySetInnerContent() (frozen, symbol-branded) */
|
|
895
987
|
export interface TrustedContent {
|
|
896
|
-
__html: string;
|
|
897
|
-
__trusted: true;
|
|
988
|
+
readonly __html: string;
|
|
989
|
+
readonly __trusted: true;
|
|
898
990
|
}
|
|
899
991
|
|
|
900
|
-
/**
|
|
992
|
+
/**
|
|
993
|
+
* Detect content marked by dangerouslySetInnerContent(). Markers carry a
|
|
994
|
+
* symbol brand, so objects parsed from JSON are never trusted.
|
|
995
|
+
*/
|
|
901
996
|
export function isTrustedContent(value: unknown): value is TrustedContent;
|
|
902
997
|
|
|
998
|
+
/**
|
|
999
|
+
* Whether a string can be emitted as an attribute name. Rendering an element
|
|
1000
|
+
* with an invalid name (whitespace, quotes, `<`, `>`, `/`, `=`, controls) throws.
|
|
1001
|
+
*/
|
|
1002
|
+
export function isValidAttributeName(name: string): boolean;
|
|
1003
|
+
|
|
903
1004
|
// ============================================================================
|
|
904
1005
|
// Utility Types and Constants
|
|
905
1006
|
// ============================================================================
|
|
@@ -913,6 +1014,7 @@ export const compose: ComposeUtils;
|
|
|
913
1014
|
/** Default export with all core functionality */
|
|
914
1015
|
declare const coherent: {
|
|
915
1016
|
render: typeof render;
|
|
1017
|
+
renderToStream: typeof renderToStream;
|
|
916
1018
|
withState: typeof withState;
|
|
917
1019
|
memo: typeof memo;
|
|
918
1020
|
validateComponent: typeof validateComponent;
|