11import type { StackSnapshotStrategy } from "./StackSnapshotStrategy" ;
22
3+ const COMPOSED_METADATA_SCHEMA = "stackflow.compose-strategies" ;
4+ const COMPOSED_METADATA_VERSION = 1 ;
5+
6+ type StrategiesMetadataData <
7+ Strategies extends Record < string , StackSnapshotStrategy < any > > ,
8+ > = {
9+ [ Key in keyof Strategies ] : ReturnType < Strategies [ Key ] [ "metadata" ] [ "create" ] > ;
10+ } ;
11+
312export type StrategiesMetadata <
413 Strategies extends Record < string , StackSnapshotStrategy < any > > ,
514> = {
6- [ Key in keyof Strategies ] : Strategies [ Key ] extends StackSnapshotStrategy <
7- infer Metadata
8- >
9- ? Metadata
10- : never ;
15+ readonly schema : typeof COMPOSED_METADATA_SCHEMA ;
16+ readonly version : typeof COMPOSED_METADATA_VERSION ;
17+ readonly data : StrategiesMetadataData < Strategies > ;
1118} ;
1219
1320export function composeStrategies <
@@ -18,21 +25,72 @@ export function composeStrategies<
1825 const keys = Object . keys ( strategies ) as Array < keyof Strategies > ;
1926
2027 return {
21- createMetadata ( args ) {
22- return Object . fromEntries (
23- keys . map ( ( key ) => [ key , strategies [ key ] . createMetadata ( args ) ] ) ,
24- ) as StrategiesMetadata < Strategies >
28+ metadata : {
29+ create ( args ) {
30+ return {
31+ schema : COMPOSED_METADATA_SCHEMA ,
32+ version : COMPOSED_METADATA_VERSION ,
33+ data : Object . fromEntries (
34+ keys . map ( ( key ) => [ key , strategies [ key ] . metadata . create ( args ) ] ) ,
35+ ) ,
36+ } as StrategiesMetadata < Strategies > ;
37+ } ,
38+ parse ( data ) {
39+ if ( data === null || typeof data !== "object" ) {
40+ return { ok : false } ;
41+ }
42+
43+ const metadata = data as Record < PropertyKey , unknown > ;
44+
45+ if (
46+ ! Object . hasOwn ( metadata , "schema" ) ||
47+ ! Object . hasOwn ( metadata , "version" ) ||
48+ ! Object . hasOwn ( metadata , "data" ) ||
49+ metadata . schema !== COMPOSED_METADATA_SCHEMA ||
50+ metadata . version !== COMPOSED_METADATA_VERSION ||
51+ metadata . data === null ||
52+ typeof metadata . data !== "object"
53+ ) {
54+ return { ok : false } ;
55+ }
56+
57+ const metadataData = metadata . data as Record < PropertyKey , unknown > ;
58+
59+ if (
60+ Object . keys ( metadataData ) . length !== keys . length ||
61+ ! keys . every ( ( key ) => Object . hasOwn ( metadataData , key ) )
62+ ) {
63+ return { ok : false } ;
64+ }
65+
66+ const parsedEntries : Array < [ PropertyKey , unknown ] > = [ ] ;
67+
68+ for ( const key of keys ) {
69+ const result = strategies [ key ] . metadata . parse ( metadataData [ key ] ) ;
70+
71+ if ( ! result . ok ) {
72+ return { ok : false } ;
73+ }
74+
75+ parsedEntries . push ( [ key , result . value ] ) ;
76+ }
77+
78+ return {
79+ ok : true ,
80+ value : {
81+ schema : COMPOSED_METADATA_SCHEMA ,
82+ version : COMPOSED_METADATA_VERSION ,
83+ data : Object . fromEntries ( parsedEntries ) ,
84+ } as StrategiesMetadata < Strategies > ,
85+ } ;
86+ } ,
2587 } ,
2688 shouldReuse ( { record, initialContext } ) {
27- if ( ! keys . every ( ( key ) => Object . hasOwn ( record . metadata , key ) ) ) {
28- return false ;
29- }
30-
3189 return keys . every ( ( key ) => {
3290 return strategies [ key ] . shouldReuse ( {
3391 record : {
3492 ...record ,
35- metadata : record . metadata [ key ] ,
93+ metadata : record . metadata . data [ key ] ,
3694 } ,
3795 initialContext,
3896 } ) ;
0 commit comments