|
@@ -1549,6 +1549,104 @@ void ScriptEngine::setupBuiltinAPIs() {
|
|
|
return result;
|
|
return result;
|
|
|
}, "pick", 2));
|
|
}, "pick", 2));
|
|
|
|
|
|
|
|
|
|
+ // utils.getFieldValue(data, path) - Read a nested value by dotted path.
|
|
|
|
|
+ // Supports a.b.c, arrays via key[0] or a bare numeric segment, and .length
|
|
|
|
|
+ // on an array. Returns undefined for anything missing.
|
|
|
|
|
+ JS_SetPropertyStr(ctx, utils, "getFieldValue", JS_NewCFunction(ctx, [](JSContext* ctx, JSValue this_val, int argc, JSValue* argv) -> JSValue {
|
|
|
|
|
+ if (argc < 1) {
|
|
|
|
|
+ return JS_ThrowTypeError(ctx, "utils.getFieldValue requires a value and a path");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (argc < 2 || JS_IsUndefined(argv[1]) || JS_IsNull(argv[1])) {
|
|
|
|
|
+ return JS_DupValue(ctx, argv[0]);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const char* path_c = JS_ToCString(ctx, argv[1]);
|
|
|
|
|
+ if (!path_c) {
|
|
|
|
|
+ return JS_EXCEPTION;
|
|
|
|
|
+ }
|
|
|
|
|
+ std::string path(path_c);
|
|
|
|
|
+ JS_FreeCString(ctx, path_c);
|
|
|
|
|
+
|
|
|
|
|
+ if (path.empty()) {
|
|
|
|
|
+ return JS_DupValue(ctx, argv[0]);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ std::vector<std::string> keys;
|
|
|
|
|
+ size_t start = 0;
|
|
|
|
|
+ while (true) {
|
|
|
|
|
+ size_t dot = path.find('.', start);
|
|
|
|
|
+ if (dot == std::string::npos) {
|
|
|
|
|
+ keys.push_back(path.substr(start));
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ keys.push_back(path.substr(start, dot - start));
|
|
|
|
|
+ start = dot + 1;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ JSValue current = JS_DupValue(ctx, argv[0]);
|
|
|
|
|
+
|
|
|
|
|
+ for (const auto& raw_key : keys) {
|
|
|
|
|
+ if (JS_IsUndefined(current) || JS_IsNull(current)) {
|
|
|
|
|
+ JS_FreeValue(ctx, current);
|
|
|
|
|
+ return JS_UNDEFINED;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ bool is_array = JS_IsArray(ctx, current);
|
|
|
|
|
+
|
|
|
|
|
+ if (raw_key == "length" && is_array) {
|
|
|
|
|
+ JSValue length = JS_GetPropertyStr(ctx, current, "length");
|
|
|
|
|
+ JS_FreeValue(ctx, current);
|
|
|
|
|
+ current = length;
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // key[3] - step into the property, then index it
|
|
|
|
|
+ std::string key = raw_key;
|
|
|
|
|
+ bool has_index = false;
|
|
|
|
|
+ int64_t index = 0;
|
|
|
|
|
+ size_t bracket = raw_key.find('[');
|
|
|
|
|
+ if (bracket != std::string::npos && raw_key.back() == ']') {
|
|
|
|
|
+ std::string index_text = raw_key.substr(bracket + 1, raw_key.size() - bracket - 2);
|
|
|
|
|
+ if (!index_text.empty() &&
|
|
|
|
|
+ index_text.find_first_not_of("0123456789") == std::string::npos) {
|
|
|
|
|
+ key = raw_key.substr(0, bracket);
|
|
|
|
|
+ index = std::stoll(index_text);
|
|
|
|
|
+ has_index = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (!key.empty()) {
|
|
|
|
|
+ bool numeric = key.find_first_not_of("0123456789") == std::string::npos;
|
|
|
|
|
+ if (numeric && is_array) {
|
|
|
|
|
+ JSValue element = JS_GetPropertyUint32(ctx, current, (uint32_t)std::stoll(key));
|
|
|
|
|
+ JS_FreeValue(ctx, current);
|
|
|
|
|
+ current = element;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ if (!JS_IsObject(current)) {
|
|
|
|
|
+ JS_FreeValue(ctx, current);
|
|
|
|
|
+ return JS_UNDEFINED;
|
|
|
|
|
+ }
|
|
|
|
|
+ JSValue next = JS_GetPropertyStr(ctx, current, key.c_str());
|
|
|
|
|
+ JS_FreeValue(ctx, current);
|
|
|
|
|
+ current = next;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (has_index) {
|
|
|
|
|
+ if (!JS_IsArray(ctx, current)) {
|
|
|
|
|
+ JS_FreeValue(ctx, current);
|
|
|
|
|
+ return JS_UNDEFINED;
|
|
|
|
|
+ }
|
|
|
|
|
+ JSValue element = JS_GetPropertyUint32(ctx, current, (uint32_t)index);
|
|
|
|
|
+ JS_FreeValue(ctx, current);
|
|
|
|
|
+ current = element;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return current;
|
|
|
|
|
+ }, "getFieldValue", 2));
|
|
|
|
|
+
|
|
|
// utils.omit(obj, keys) - Omit specific keys from an object
|
|
// utils.omit(obj, keys) - Omit specific keys from an object
|
|
|
JS_SetPropertyStr(ctx, utils, "omit", JS_NewCFunction(ctx, [](JSContext* ctx, JSValue this_val, int argc, JSValue* argv) -> JSValue {
|
|
JS_SetPropertyStr(ctx, utils, "omit", JS_NewCFunction(ctx, [](JSContext* ctx, JSValue this_val, int argc, JSValue* argv) -> JSValue {
|
|
|
if (argc < 2 || !JS_IsObject(argv[0]) || !JS_IsArray(ctx, argv[1])) {
|
|
if (argc < 2 || !JS_IsObject(argv[0]) || !JS_IsArray(ctx, argv[1])) {
|