diff --git a/packages/envio/src/HandlerRegister.res b/packages/envio/src/HandlerRegister.res index da73759ce9..5998d7455d 100644 --- a/packages/envio/src/HandlerRegister.res +++ b/packages/envio/src/HandlerRegister.res @@ -1,7 +1,7 @@ type eventRegistration = { handler: option, contractRegister: option, - eventOptions: option>, + eventOptions: option>, } let empty = { @@ -175,6 +175,7 @@ let getContractRegister = (~contractName, ~eventName) => let getEventFilters = (~contractName, ~eventName) => get(~contractName, ~eventName).eventOptions ->Belt.Option.flatMap(value => value.eventFilters) + ->(Utils.magic: option => option) let isWildcard = (~contractName, ~eventName) => get(~contractName, ~eventName).eventOptions @@ -187,26 +188,49 @@ let hasRegistration = (~contractName, ~eventName) => { } type eventNamespace = {contractName: string, eventName: string} -exception DuplicateEventRegistration(eventNamespace) + +let raiseDuplicateRegistration = (~contractName, ~eventName, ~msg, ~logger) => { + let fullMsg = msg ++ " for " ++ contractName ++ "." ++ eventName + Logging.createChildFrom(~logger, ~params={contractName, eventName})->Logging.childError(fullMsg) + Js.Exn.raiseError(fullMsg) +} + +let eventFiltersMatch = (a: option, b: option) => { + switch (a, b) { + | (None, None) => true + | (Some(Static(a)), Some(Static(b))) => a == b + | (Some(Dynamic(a)), Some(Dynamic(b))) => a === b + | _ => false + } +} + +let eventOptionsMatch = ( + existing: option>, + incoming: option>, +) => { + switch (existing, incoming) { + | (None, None) => true + | (Some(a), Some(b)) => + a.wildcard === b.wildcard && eventFiltersMatch(a.eventFilters, b.eventFilters) + | _ => false + } +} let setEventOptions = (~contractName, ~eventName, ~eventOptions, ~logger=Logging.getLogger()) => { switch eventOptions { | Some(value) => let value = - value->(Utils.magic: Internal.eventOptions<'eventFilters> => Internal.eventOptions) + value->(Utils.magic: Internal.eventOptions<'eventFilters> => Internal.eventOptions) let t = get(~contractName, ~eventName) switch t.eventOptions { | None => set(~contractName, ~eventName, {...t, eventOptions: Some(value)}) | Some(existingValue) => - if ( - existingValue.wildcard !== value.wildcard || - // TODO: Can improve the check by using deepEqual - existingValue.eventFilters !== value.eventFilters - ) { - let eventNamespace = {contractName, eventName} - DuplicateEventRegistration(eventNamespace)->ErrorHandling.mkLogAndRaise( - ~logger=Logging.createChildFrom(~logger, ~params=eventNamespace), - ~msg="Duplicate eventOptions in handlers not allowed", + if !eventOptionsMatch(Some(existingValue), Some(value)) { + raiseDuplicateRegistration( + ~contractName, + ~eventName, + ~msg="Cannot register handler with different options. Make sure all handlers for the same event use identical options (wildcard, eventFilters)", + ~logger, ) } } @@ -217,46 +241,79 @@ let setEventOptions = (~contractName, ~eventName, ~eventOptions, ~logger=Logging let setHandler = (~contractName, ~eventName, handler, ~eventOptions, ~logger=Logging.getLogger()) => { withRegistration(_registration => { let t = get(~contractName, ~eventName) + let newHandler = handler->(Utils.magic: Internal.genericHandler<'args> => Internal.handler) switch t.handler { | None => + setEventOptions(~contractName, ~eventName, ~eventOptions, ~logger) + let t = get(~contractName, ~eventName) set(~contractName, ~eventName, { ...t, - handler: handler - ->(Utils.magic: Internal.genericHandler<'args> => Internal.handler) - ->Some, + handler: Some(newHandler), }) - | Some(_) => - let eventNamespace = {contractName, eventName} - DuplicateEventRegistration(eventNamespace)->ErrorHandling.mkLogAndRaise( - ~logger=Logging.createChildFrom(~logger, ~params=eventNamespace), - ~msg="Duplicate registration of event handlers not allowed", - ) + | Some(prevHandler) => + let incomingEventOptions = + eventOptions->Belt.Option.map(v => + v->(Utils.magic: Internal.eventOptions<'eventFilters> => Internal.eventOptions) + ) + if eventOptionsMatch(t.eventOptions, incomingEventOptions) { + let composedHandler: Internal.handler = async args => { + await prevHandler(args) + await newHandler(args) + } + set(~contractName, ~eventName, { + ...t, + handler: Some(composedHandler), + }) + } else { + raiseDuplicateRegistration( + ~contractName, + ~eventName, + ~msg="Cannot register a second handler with different options. Make sure all handlers for the same event use identical options (wildcard, eventFilters)", + ~logger, + ) + } } - - setEventOptions(~contractName, ~eventName, ~eventOptions, ~logger) }) } let setContractRegister = (~contractName, ~eventName, contractRegister, ~eventOptions, ~logger=Logging.getLogger()) => { withRegistration(_registration => { let t = get(~contractName, ~eventName) + let newContractRegister = contractRegister->( + Utils.magic: Internal.genericContractRegister< + Internal.genericContractRegisterArgs<'event, 'context>, + > => Internal.contractRegister + ) switch t.contractRegister { | None => + setEventOptions(~contractName, ~eventName, ~eventOptions, ~logger) + let t = get(~contractName, ~eventName) set(~contractName, ~eventName, { ...t, - contractRegister: contractRegister->( - Utils.magic: Internal.genericContractRegister< - Internal.genericContractRegisterArgs<'event, 'context>, - > => Internal.contractRegister - )->Some, + contractRegister: Some(newContractRegister), }) - | Some(_) => - let eventNamespace = {contractName, eventName} - DuplicateEventRegistration(eventNamespace)->ErrorHandling.mkLogAndRaise( - ~logger=Logging.createChildFrom(~logger, ~params=eventNamespace), - ~msg="Duplicate contractRegister handlers not allowed", - ) + | Some(prevContractRegister) => + let incomingEventOptions = + eventOptions->Belt.Option.map(v => + v->(Utils.magic: Internal.eventOptions<'eventFilters> => Internal.eventOptions) + ) + if eventOptionsMatch(t.eventOptions, incomingEventOptions) { + let composedContractRegister: Internal.contractRegister = async args => { + await prevContractRegister(args) + await newContractRegister(args) + } + set(~contractName, ~eventName, { + ...t, + contractRegister: Some(composedContractRegister), + }) + } else { + raiseDuplicateRegistration( + ~contractName, + ~eventName, + ~msg="Cannot register a second contractRegister with different options. Make sure all handlers for the same event use identical options (wildcard, eventFilters)", + ~logger, + ) + } } - setEventOptions(~contractName, ~eventName, ~eventOptions, ~logger) }) } diff --git a/scenarios/test_codegen/pnpm-lock.yaml b/scenarios/test_codegen/pnpm-lock.yaml index ba9b0f7788..475f3c5797 100644 --- a/scenarios/test_codegen/pnpm-lock.yaml +++ b/scenarios/test_codegen/pnpm-lock.yaml @@ -65,6 +65,39 @@ importers: specifier: 9.3.4 version: 9.3.4(rescript@11.1.3) + generated: + dependencies: + bignumber.js: + specifier: 9.1.2 + version: 9.1.2 + date-fns: + specifier: 3.3.1 + version: 3.3.1 + envio: + specifier: file:/home/user/hyperindex/target/debug/envio/../../../packages/envio + version: file:../../packages/envio(react-dom@19.2.3(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + js-sdsl: + specifier: 4.4.2 + version: 4.4.2 + pino: + specifier: 8.16.1 + version: 8.16.1 + prom-client: + specifier: 15.0.0 + version: 15.0.0 + react: + specifier: 19.2.0 + version: 19.2.0 + rescript: + specifier: 11.1.3 + version: 11.1.3 + rescript-schema: + specifier: 9.3.4 + version: 9.3.4(rescript@11.1.3) + viem: + specifier: 2.21.0 + version: 2.21.0(typescript@5.9.3) + packages: '@adraffy/ens-normalize@1.10.0': @@ -265,18 +298,21 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [glibc] '@envio-dev/hyperfuel-client-linux-x64-gnu@1.2.2': resolution: {integrity: sha512-XDkvkBG/frS+xiZkJdY4KqOaoAwyxPdi2MysDQgF8NmZdssi32SWch0r4LTqKWLLlCBg9/R55POeXL5UAjg2wQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [glibc] '@envio-dev/hyperfuel-client-linux-x64-musl@1.2.2': resolution: {integrity: sha512-DKnKJJSwsYtA7YT0EFGhFB5Eqoo42X0l0vZBv4lDuxngEXiiNjeLemXoKQVDzhcbILD7eyXNa5jWUc+2hpmkEg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [musl] '@envio-dev/hyperfuel-client-win32-x64-msvc@1.2.2': resolution: {integrity: sha512-SwIgTAVM9QhCFPyHwL+e1yQ6o3paV6q25klESkXw+r/KW9QPhOOyA6Yr8nfnur3uqMTLJHAKHTLUnkyi/Nh7Aw==} @@ -305,18 +341,21 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [glibc] '@envio-dev/hypersync-client-linux-x64-gnu@0.7.0': resolution: {integrity: sha512-Rpz3VSLqq2O9Qp3GiDXHmF3MPm7TcsTfRzSz2RkRVEfdchaKA7Ob86jQ2ueRyG1UEEu47V9JzL6mYmVs4gNa7w==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [glibc] '@envio-dev/hypersync-client-linux-x64-musl@0.7.0': resolution: {integrity: sha512-SZ8RHNaCEcPvubFGdA7gkbxJeJe4whhw0oaAvSyGEbUjL5byFI9nqMVzW8o8mPACqqjlOHIq77GYxbbUI+53vQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [musl] '@envio-dev/hypersync-client-win32-x64-msvc@0.7.0': resolution: {integrity: sha512-R7M1IBcWfqx1TyBcK5Mc/VJmayGeK9woWPzSPzjTgYsHFl65gE1e0hXb8B/gi+km8GQDZYNcaHyBh6xSVHkrzg==} @@ -619,66 +658,79 @@ packages: resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==} cpu: [arm] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.59.0': resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==} cpu: [arm] os: [linux] + libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.59.0': resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==} cpu: [arm64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.59.0': resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==} cpu: [arm64] os: [linux] + libc: [musl] '@rollup/rollup-linux-loong64-gnu@4.59.0': resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==} cpu: [loong64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-loong64-musl@4.59.0': resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==} cpu: [loong64] os: [linux] + libc: [musl] '@rollup/rollup-linux-ppc64-gnu@4.59.0': resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==} cpu: [ppc64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-ppc64-musl@4.59.0': resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==} cpu: [ppc64] os: [linux] + libc: [musl] '@rollup/rollup-linux-riscv64-gnu@4.59.0': resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==} cpu: [riscv64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.59.0': resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==} cpu: [riscv64] os: [linux] + libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.59.0': resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==} cpu: [s390x] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.59.0': resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==} cpu: [x64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-musl@4.59.0': resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==} cpu: [x64] os: [linux] + libc: [musl] '@rollup/rollup-openbsd-x64@4.59.0': resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==} @@ -842,6 +894,10 @@ packages: zod: optional: true + abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + accepts@1.3.8: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} @@ -951,6 +1007,9 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + baseline-browser-mapping@2.8.29: resolution: {integrity: sha512-sXdt2elaVnhpDNRDz+1BDx1JQoJRuNk7oVlAlbGiFkLikHCAQiccexF/9e91zVi6RCgqspl04aP+6Cnl9zRLrA==} hasBin: true @@ -986,6 +1045,9 @@ packages: buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + bytes@3.1.2: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} @@ -1307,6 +1369,14 @@ packages: resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} engines: {node: '>= 0.6'} + event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + eventsource-parser@3.0.6: resolution: {integrity: sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==} engines: {node: '>=18.0.0'} @@ -1348,6 +1418,10 @@ packages: resolution: {integrity: sha512-ar+hQ4+OIurUGjSJD1anvYSDcUflywhKjfxnsW4TBTD7+u0tJufv6DKRWoQk3vI6YBOWMoz0TQtfbe7dxbQmvA==} engines: {node: '>= 10.0.0'} + fast-redact@3.5.0: + resolution: {integrity: sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==} + engines: {node: '>=6'} + fast-safe-stringify@2.1.1: resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} @@ -1486,6 +1560,9 @@ packages: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + import-local@3.2.0: resolution: {integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==} engines: {node: '>=8'} @@ -1764,6 +1841,9 @@ packages: resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} engines: {node: '>=10'} + js-sdsl@4.4.2: + resolution: {integrity: sha512-dwXFwByc/ajSV6m5bcKAPwe4yDDF6D614pxmIi5odytzxRlwqF6nwoiCek80Ixc7Cvma5awClxrzFtxCQvcM8w==} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -2009,6 +2089,9 @@ packages: resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} + pino-abstract-transport@1.1.0: + resolution: {integrity: sha512-lsleG3/2a/JIWUtf9Q5gUNErBqwIu1tUKTT3dUzaf5DySw9ra1wcqKjJjLX1VTY64Wk1eEOYsVGSaGfCK85ekA==} + pino-abstract-transport@2.0.0: resolution: {integrity: sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==} @@ -2019,6 +2102,9 @@ packages: resolution: {integrity: sha512-ttXRkkOz6WWC95KeY9+xxWL6AtImwbyMHrL1mSwqwW9u+vLp/WIElvHvCSDg0xO/Dzrggz1zv3rN5ovTRVowKg==} hasBin: true + pino-std-serializers@6.2.2: + resolution: {integrity: sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==} + pino-std-serializers@7.0.0: resolution: {integrity: sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==} @@ -2026,6 +2112,10 @@ packages: resolution: {integrity: sha512-0zZC2ygfdqvqK8zJIr1e+wT1T/L+LF6qvqvbzEQ6tiMAoTqEVK9a1K3YRu8HEUvGEvNqZyPJTtb2sNIoTkB83w==} hasBin: true + pino@8.16.1: + resolution: {integrity: sha512-3bKsVhBmgPjGV9pyn4fO/8RtoVDR8ssW1ev819FsRXlRNgW8gR/9Kx+gCK4UPWd4JjrRDLWpzd/pb1AyWm3MGA==} + hasBin: true + pirates@4.0.7: resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} engines: {node: '>= 6'} @@ -2046,9 +2136,16 @@ packages: resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + process-warning@2.3.2: + resolution: {integrity: sha512-n9wh8tvBe5sFmsqlg+XQhaQLumwpqoAUruLwjCopgTmUBjJ/fjtBsJzKleCaIGBOMXYEhp1YfKl4d7rJ5ZKJGA==} + process-warning@5.0.0: resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==} + process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} + prom-client@15.0.0: resolution: {integrity: sha512-UocpgIrKyA2TKLVZDSfm8rGkL13C19YrQBAiG3xo3aDFWcHedxRxI3z+cIcucoxpSO0h5lff5iv/SXoxyeopeA==} engines: {node: ^16 || ^18 || >=20} @@ -2113,6 +2210,10 @@ packages: resolution: {integrity: sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==} engines: {node: '>=0.10.0'} + readable-stream@4.7.0: + resolution: {integrity: sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + real-require@0.2.0: resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} engines: {node: '>= 12.13.0'} @@ -2265,6 +2366,9 @@ packages: resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==} engines: {node: '>=18'} + sonic-boom@3.8.1: + resolution: {integrity: sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg==} + sonic-boom@4.2.0: resolution: {integrity: sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==} @@ -2324,6 +2428,9 @@ packages: resolution: {integrity: sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg==} engines: {node: '>=20'} + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -2378,6 +2485,9 @@ packages: resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} engines: {node: '>=8'} + thread-stream@2.7.0: + resolution: {integrity: sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw==} + thread-stream@3.1.0: resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==} @@ -3471,6 +3581,10 @@ snapshots: optionalDependencies: typescript: 5.9.3 + abort-controller@3.0.0: + dependencies: + event-target-shim: 5.0.1 + accepts@1.3.8: dependencies: mime-types: 2.1.35 @@ -3597,6 +3711,8 @@ snapshots: balanced-match@1.0.2: {} + base64-js@1.5.1: {} + baseline-browser-mapping@2.8.29: {} bignumber.js@9.1.2: {} @@ -3645,6 +3761,11 @@ snapshots: buffer-from@1.1.2: {} + buffer@6.0.3: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + bytes@3.1.2: {} call-bind-apply-helpers@1.0.2: @@ -3946,6 +4067,10 @@ snapshots: etag@1.8.1: {} + event-target-shim@5.0.1: {} + + events@3.3.0: {} + eventsource-parser@3.0.6: {} eventsource@4.1.0: @@ -4024,6 +4149,8 @@ snapshots: rfdc: 1.4.1 string-similarity: 4.0.4 + fast-redact@3.5.0: {} + fast-safe-stringify@2.1.1: {} fb-watchman@2.0.2: @@ -4168,6 +4295,8 @@ snapshots: dependencies: safer-buffer: 2.1.2 + ieee754@1.2.1: {} + import-local@3.2.0: dependencies: pkg-dir: 4.2.0 @@ -4668,6 +4797,8 @@ snapshots: joycon@3.1.1: {} + js-sdsl@4.4.2: {} + js-tokens@4.0.0: {} js-yaml@3.14.2: @@ -4879,6 +5010,11 @@ snapshots: picomatch@4.0.3: {} + pino-abstract-transport@1.1.0: + dependencies: + readable-stream: 4.7.0 + split2: 4.2.0 + pino-abstract-transport@2.0.0: dependencies: split2: 4.2.0 @@ -4903,6 +5039,8 @@ snapshots: sonic-boom: 4.2.0 strip-json-comments: 5.0.3 + pino-std-serializers@6.2.2: {} + pino-std-serializers@7.0.0: {} pino@10.1.0: @@ -4919,6 +5057,20 @@ snapshots: sonic-boom: 4.2.0 thread-stream: 3.1.0 + pino@8.16.1: + dependencies: + atomic-sleep: 1.0.0 + fast-redact: 3.5.0 + on-exit-leak-free: 2.1.2 + pino-abstract-transport: 1.1.0 + pino-std-serializers: 6.2.2 + process-warning: 2.3.2 + quick-format-unescaped: 4.0.4 + real-require: 0.2.0 + safe-stable-stringify: 2.5.0 + sonic-boom: 3.8.1 + thread-stream: 2.7.0 + pirates@4.0.7: {} pkg-dir@4.2.0: @@ -4939,8 +5091,12 @@ snapshots: ansi-styles: 5.2.0 react-is: 17.0.2 + process-warning@2.3.2: {} + process-warning@5.0.0: {} + process@0.11.10: {} + prom-client@15.0.0: dependencies: '@opentelemetry/api': 1.9.0 @@ -5006,6 +5162,14 @@ snapshots: react@19.2.0: {} + readable-stream@4.7.0: + dependencies: + abort-controller: 3.0.0 + buffer: 6.0.3 + events: 3.3.0 + process: 0.11.10 + string_decoder: 1.3.0 + real-require@0.2.0: {} require-directory@2.1.1: {} @@ -5184,6 +5348,10 @@ snapshots: ansi-styles: 6.2.3 is-fullwidth-code-point: 5.1.0 + sonic-boom@3.8.1: + dependencies: + atomic-sleep: 1.0.0 + sonic-boom@4.2.0: dependencies: atomic-sleep: 1.0.0 @@ -5237,6 +5405,10 @@ snapshots: get-east-asian-width: 1.4.0 strip-ansi: 7.1.2 + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 @@ -5285,6 +5457,10 @@ snapshots: glob: 7.2.3 minimatch: 3.1.2 + thread-stream@2.7.0: + dependencies: + real-require: 0.2.0 + thread-stream@3.1.0: dependencies: real-require: 0.2.0 diff --git a/scenarios/test_codegen/src/handlers/EventHandlers.ts b/scenarios/test_codegen/src/handlers/EventHandlers.ts index 742a6ddb51..c83360b388 100644 --- a/scenarios/test_codegen/src/handlers/EventHandlers.ts +++ b/scenarios/test_codegen/src/handlers/EventHandlers.ts @@ -757,3 +757,24 @@ EventFiltersTest.FilterTestEvent.handler( }, }, ); + +// Duplicate handler registration tests + +// Same options (no options) → should compose without error +export let composedHandlerCalled = false; +Gravatar.CustomSelection.handler(async () => { + composedHandlerCalled = true; +}); + +export let composedContractRegisterCalled = false; +Gravatar.FactoryEvent.contractRegister(() => { + composedContractRegisterCalled = true; +}); + +// Different options → should throw +export let mismatchedHandlerOptionsError: Error | undefined; +try { + Gravatar.CustomSelection.handler(async () => {}, { wildcard: true }); +} catch (e) { + mismatchedHandlerOptionsError = e as Error; +} diff --git a/scenarios/test_codegen/test/CustomSelection_test.ts b/scenarios/test_codegen/test/CustomSelection.test.ts similarity index 100% rename from scenarios/test_codegen/test/CustomSelection_test.ts rename to scenarios/test_codegen/test/CustomSelection.test.ts diff --git a/scenarios/test_codegen/test/EventHandler_test.ts b/scenarios/test_codegen/test/EventHandler.test.ts similarity index 96% rename from scenarios/test_codegen/test/EventHandler_test.ts rename to scenarios/test_codegen/test/EventHandler.test.ts index 045a333248..5b8b5a3504 100644 --- a/scenarios/test_codegen/test/EventHandler_test.ts +++ b/scenarios/test_codegen/test/EventHandler.test.ts @@ -624,6 +624,31 @@ describe("Use Envio test framework to test event handlers", () => { }); }); + it("composes duplicate handlers with same options and rejects mismatched options", async () => { + const mockDbInitial = MockDb.createMockDb(); + + const event = Gravatar.FactoryEvent.createMockEvent({ + contract: "0x1234567890123456789012345678901234567890", + testCase: "syncRegistration", + }); + + // Trigger module load via autoLoadFromSrcHandlers + await mockDbInitial.processEvents([event]); + + // Dynamic-import EventHandlers.js to access exported error values + const handlers = await import("../src/handlers/EventHandlers"); + + // Same options → composed without error + // contractRegister ran during factory event processing, proving compose works + assert.strictEqual(handlers.composedContractRegisterCalled, true); + + // Different options → throws a user-friendly error + assert.strictEqual( + handlers.mismatchedHandlerOptionsError?.message, + "Cannot register a second handler with different options. Make sure all handlers for the same event use identical options (wildcard, eventFilters) for Gravatar.CustomSelection" + ); + }); + it("Currently filters are ignored by the test framework", async () => { const mockDbInitial = MockDb.createMockDb(); diff --git a/scenarios/test_codegen/test/Logging_test.ts b/scenarios/test_codegen/test/Logging.test.ts similarity index 100% rename from scenarios/test_codegen/test/Logging_test.ts rename to scenarios/test_codegen/test/Logging.test.ts diff --git a/scenarios/test_codegen/test/S_test.ts b/scenarios/test_codegen/test/S.test.ts similarity index 100% rename from scenarios/test_codegen/test/S_test.ts rename to scenarios/test_codegen/test/S.test.ts diff --git a/scenarios/test_codegen/test/fixtures/LogTesting.res b/scenarios/test_codegen/test/fixtures/LogTesting.res index 2937f1bf58..8ad3615aef 100644 --- a/scenarios/test_codegen/test/fixtures/LogTesting.res +++ b/scenarios/test_codegen/test/fixtures/LogTesting.res @@ -2,7 +2,7 @@ switch NodeJs.Process.process.env->Js.Dict.get("ENVIO_TEST_LOGGING_FORMAT") { | None => Js.Exn.raiseError( - "LogTesting.res should only be run via Logging_test.ts. " ++ "Set ENVIO_TEST_LOGGING_FORMAT=1 to run directly.", + "LogTesting.res should only be run via Logging.test.ts. " ++ "Set ENVIO_TEST_LOGGING_FORMAT=1 to run directly.", ) | Some(_) => () }