diff --git a/src/workerd/jsg/jsg.h b/src/workerd/jsg/jsg.h index cd21e220202..41160ee1574 100644 --- a/src/workerd/jsg/jsg.h +++ b/src/workerd/jsg/jsg.h @@ -582,6 +582,13 @@ using HasGetTemplateOverload = static constexpr char _JSG_STRUCT_TS_OVERRIDE_DO_NOT_USE_DIRECTLY[] = \ JSG_STRING_LITERAL(__VA_ARGS__) +// Like JSG_STRUCT_TS_OVERRIDE, however it enables dynamic selection of TS_OVERRIDE. +// Should be placed adjacent to the JSG_STRUCT delcaration, inside the same struct definition. +#define JSG_STRUCT_TS_OVERRIDE_DYNAMIC(...) \ + static void jsgConfiguration(__VA_ARGS__); \ + template \ + static void registerTypeScriptDynamicOverride(Registry& registry, ##__VA_ARGS__) + // Like JSG_TS_DEFINE but for use with JSG_STRUCT. Should be placed adjacent to the JSG_STRUCT // declaration, inside the same `struct` definition. See the `## TypeScript`section of the JSG README.md // for more details. @@ -644,6 +651,20 @@ struct HasStructTypeScriptDefine \ - static void registerMembers(Registry& registry) { \ + template \ + using JsgFieldWrappers = \ + ::workerd::jsg::TypeTuple; \ + template \ + static void registerMembersInternal(Registry& registry, Config arg) { \ JSG_FOR_EACH(JSG_STRUCT_REGISTER_MEMBER, , __VA_ARGS__); \ if constexpr (::workerd::jsg::HasStructTypeScriptRoot::value) { \ registry.registerTypeScriptRoot(); \ } \ - if constexpr (::workerd::jsg::HasStructTypeScriptOverride::value) { \ + if constexpr (requires(jsg::GetConfiguration arg) { \ + registerTypeScriptDynamicOverride(registry, arg); \ + }) { \ + registerTypeScriptDynamicOverride(registry, arg); \ + } else if constexpr (::workerd::jsg::HasStructTypeScriptOverride::value) { \ registry.template registerTypeScriptOverride< \ Self::_JSG_STRUCT_TS_OVERRIDE_DO_NOT_USE_DIRECTLY>(); \ } \ @@ -669,9 +697,18 @@ struct HasStructTypeScriptDefine(); \ } \ } \ - template \ - using JsgFieldWrappers = \ - ::workerd::jsg::TypeTuple + template \ + static void registerMembers(Registry& registry) \ + requires(!jsg::HasConfiguration) \ + { \ + registerMembersInternal(registry, nullptr); \ + } \ + template \ + static void registerMembers(Registry& registry, jsg::GetConfiguration arg) \ + requires jsg::HasConfiguration \ + { \ + registerMembersInternal>(registry, arg); \ + } namespace { template diff --git a/src/workerd/jsg/struct.h b/src/workerd/jsg/struct.h index c719cf8d8ee..bbc22a0b68e 100644 --- a/src/workerd/jsg/struct.h +++ b/src/workerd/jsg/struct.h @@ -137,8 +137,17 @@ class StructWrapper, kj::_::Indexes(fields).unwrap(static_cast(*this), isolate, context, in)...}; + + // Note that if a `validate` function is provided, then it will be called after the struct is + // unwrapped from v8. This would be an appropriate time to throw an error. + // Signature: void validate(jsg::Lock& js); + if constexpr (requires(jsg::Lock& js) { t.validate(js); }) { + jsg::Lock& js = jsg::Lock::from(isolate); + t.validate(js); + } - return T{kj::get(fields).unwrap(static_cast(*this), isolate, context, in)...}; + return t; } void newContext() = delete; diff --git a/src/workerd/jsg/util.h b/src/workerd/jsg/util.h index 9c1f5596d2d..b40fdf9bcc1 100644 --- a/src/workerd/jsg/util.h +++ b/src/workerd/jsg/util.h @@ -421,6 +421,9 @@ auto getParameterType(void (*)(Arg)) -> Arg; template using GetConfiguration = decltype(getParameterType(&T::jsgConfiguration)); +template +concept HasConfiguration = requires(GetConfiguration arg) { T::jsgConfiguration(arg); }; + inline bool isFinite(double value) { return !(kj::isNaN(value) || value == kj::inf() || value == -kj::inf()); }