// Copyright (c) 2023 Cloudflare, Inc. // Licensed under the Apache 2.0 license found in the LICENSE file or at: // https://opensource.org/licenses/Apache-2.0 #pragma once #include #include #include namespace workerd::api::node { // Originally implemented by Node.js contributors. // Available at https://github.com/nodejs/node/blob/b7b96282b212a2274b9db605ac29d388246754de/src/node_buffer.h#L75 // This is verbose to be explicit with inline commenting static constexpr bool IsWithinBounds(size_t off, size_t len, size_t max) noexcept { // Asking to seek too far into the buffer // check to avoid wrapping in subsequent subtraction if (off > max) return false; // Asking for more than is left over in the buffer if (max - off < len) return false; // Otherwise we're in bounds return true; } class MIMEType; class MIMEParams final: public jsg::Object { private: template struct IteratorState final { kj::Array values; uint index = 0; }; public: MIMEParams(kj::Maybe mimeType = kj::none); static jsg::Ref constructor(jsg::Lock& js); void delete_(kj::String name); kj::Maybe get(kj::String name); bool has(kj::String name); void set(kj::String name, kj::String value); kj::String toString(); JSG_ITERATOR(EntryIterator, entries, kj::Array, IteratorState>, iteratorNext>); JSG_ITERATOR(KeyIterator, keys, kj::String, IteratorState, iteratorNext); JSG_ITERATOR( ValueIterator, values, kj::String, IteratorState, iteratorNext); JSG_RESOURCE_TYPE(MIMEParams) { JSG_METHOD_NAMED(delete, delete_); JSG_METHOD(get); JSG_METHOD(has); JSG_METHOD(set); JSG_METHOD(entries); JSG_METHOD(keys); JSG_METHOD(values); JSG_METHOD(toString); JSG_METHOD_NAMED(toJSON, toString); JSG_ITERABLE(entries); } private: template static kj::Maybe iteratorNext(jsg::Lock& js, IteratorState& state) { if (state.index >= state.values.size()) { return kj::none; } auto& item = state.values[state.index++]; if constexpr (kj::isSameType>()) { return KJ_MAP(i, item) { return kj::str(i); }; } else { static_assert(kj::isSameType()); return kj::str(item); } KJ_UNREACHABLE; } kj::Maybe mimeType; friend class MIMEType; }; class MIMEType final: public jsg::Object { public: explicit MIMEType(jsg::Lock& js, MimeType inner); ~MIMEType() noexcept(false); static jsg::Ref constructor(jsg::Lock& js, kj::String input); kj::StringPtr getType(); void setType(kj::String type); kj::StringPtr getSubtype(); void setSubtype(kj::String subtype); kj::String getEssence(); jsg::Ref getParams(); kj::String toString(); JSG_RESOURCE_TYPE(MIMEType) { JSG_PROTOTYPE_PROPERTY(type, getType, setType); JSG_PROTOTYPE_PROPERTY(subtype, getSubtype, setSubtype); JSG_READONLY_PROTOTYPE_PROPERTY(essence, getEssence); JSG_READONLY_PROTOTYPE_PROPERTY(params, getParams); JSG_METHOD(toString); JSG_METHOD_NAMED(toJSON, toString); } private: workerd::MimeType inner; jsg::Ref params; }; #define JS_UTIL_IS_TYPES(V) \ V(ArrayBufferView) \ V(ArgumentsObject) \ V(ArrayBuffer) \ V(AsyncFunction) \ V(BigInt64Array) \ V(BigIntObject) \ V(BigUint64Array) \ V(BooleanObject) \ V(DataView) \ V(Date) \ V(External) \ V(Float16Array) \ V(Float32Array) \ V(Float64Array) \ V(GeneratorFunction) \ V(GeneratorObject) \ V(Int8Array) \ V(Int16Array) \ V(Int32Array) \ V(Map) \ V(MapIterator) \ V(ModuleNamespaceObject) \ V(NativeError) \ V(NumberObject) \ V(Promise) \ V(Proxy) \ V(RegExp) \ V(Set) \ V(SetIterator) \ V(SharedArrayBuffer) \ V(StringObject) \ V(SymbolObject) \ V(TypedArray) \ V(Uint8Array) \ V(Uint8ClampedArray) \ V(Uint16Array) \ V(Uint32Array) \ V(WeakMap) \ V(WeakSet) class UtilModule final: public jsg::Object { public: UtilModule() = default; UtilModule(jsg::Lock&, const jsg::Url&) {} jsg::Name getResourceTypeInspect(jsg::Lock& js); // `getOwnNonIndexProperties()` `filter`s static constexpr int ALL_PROPERTIES = jsg::PropertyFilter::ALL_PROPERTIES; static constexpr int ONLY_ENUMERABLE = jsg::PropertyFilter::ONLY_ENUMERABLE; jsg::JsArray getOwnNonIndexProperties(jsg::Lock& js, jsg::JsObject value, int filter); // `PromiseDetails` `state`s static constexpr int kPending = jsg::PromiseState::PENDING; static constexpr int kFulfilled = jsg::PromiseState::FULFILLED; static constexpr int kRejected = jsg::PromiseState::REJECTED; struct PromiseDetails { int state; // TODO: can we make this a `jsg::PromiseState` jsg::Optional> result; JSG_STRUCT(state, result); }; jsg::Optional getPromiseDetails(jsg::Lock& js, jsg::JsValue value); struct ProxyDetails { jsg::JsRef target; jsg::JsRef handler; JSG_STRUCT(target, handler); }; jsg::Optional getProxyDetails(jsg::Lock& js, jsg::JsValue value); struct PreviewedEntries { jsg::JsRef entries; bool isKeyValue; JSG_STRUCT(entries, isKeyValue); }; jsg::Optional previewEntries(jsg::Lock& js, jsg::JsValue value); jsg::JsString getConstructorName(jsg::Lock& js, jsg::JsObject value); struct CallSiteEntry { kj::String functionName; kj::String scriptName; int lineNumber; // Node.js originally introduced the API with the name `getCallSite()` as an experimental // API but then renamed it to `getCallSites()` soon after. We had already implemented the // API with the original name in a release. To avoid the possibility of breaking, we export // the function using both names. int columnNumber; int column; JSG_STRUCT(functionName, scriptName, lineNumber, columnNumber, column); }; kj::Array getCallSites(jsg::Lock& js, jsg::Optional frames); #define V(Type) bool is##Type(jsg::JsValue value); JS_UTIL_IS_TYPES(V) #undef V bool isAnyArrayBuffer(jsg::JsValue value); bool isBoxedPrimitive(jsg::JsValue value); JSG_RESOURCE_TYPE(UtilModule) { JSG_NESTED_TYPE(MIMEType); JSG_NESTED_TYPE(MIMEParams); JSG_READONLY_INSTANCE_PROPERTY(kResourceTypeInspect, getResourceTypeInspect); JSG_STATIC_CONSTANT(ALL_PROPERTIES); JSG_STATIC_CONSTANT(ONLY_ENUMERABLE); JSG_METHOD(getOwnNonIndexProperties); JSG_STATIC_CONSTANT(kPending); JSG_STATIC_CONSTANT(kFulfilled); JSG_STATIC_CONSTANT(kRejected); JSG_METHOD(getPromiseDetails); JSG_METHOD(getProxyDetails); JSG_METHOD(previewEntries); JSG_METHOD(getConstructorName); JSG_METHOD(getCallSites); JSG_NESTED_TYPE(Performance); JSG_NESTED_TYPE(PerformanceEntry); JSG_NESTED_TYPE(PerformanceMeasure); JSG_NESTED_TYPE(PerformanceMark); JSG_NESTED_TYPE(PerformanceObserver); JSG_NESTED_TYPE(PerformanceObserverEntryList); JSG_NESTED_TYPE(PerformanceResourceTiming); #define V(Type) JSG_METHOD(is##Type); JS_UTIL_IS_TYPES(V) #undef V JSG_METHOD(isAnyArrayBuffer); JSG_METHOD(isBoxedPrimitive); } }; #define EW_NODE_UTIL_ISOLATE_TYPES \ api::node::UtilModule, api::node::UtilModule::PromiseDetails, \ api::node::UtilModule::ProxyDetails, api::node::UtilModule::PreviewedEntries, \ api::node::MIMEType, api::node::MIMEParams, api::node::MIMEParams::EntryIterator, \ api::node::MIMEParams::ValueIterator, api::node::MIMEParams::KeyIterator, \ api::node::MIMEParams::EntryIterator::Next, api::node::MIMEParams::ValueIterator::Next, \ api::node::MIMEParams::KeyIterator::Next, api::node::UtilModule::CallSiteEntry } // namespace workerd::api::node