26#include <netinet/in.h>
27#include <sys/socket.h>
33 char *body_text = NULL;
34 if (out_size != NULL) {
37 if (request == NULL) {
40 if (request->
body != NULL) {
41 body_text = strdup(request->
body);
45 if (body_text == NULL) {
46 body_text = strdup(
"");
48 if (out_size != NULL && body_text != NULL) {
49 *out_size = strlen(body_text);
55 if (field_name == NULL || field_name[0] ==
'\0') {
62 if (body_bytes != NULL) {
79 const char *status_text,
80 const char *error_kind,
84 int written = snprintf(
87 "{\"error\":\"%s\",\"message\":\"%s\"}",
88 error_kind != NULL ? error_kind :
"error",
89 message != NULL ? message :
"An error occurred"
91 if (written < 0 || (
size_t)written >=
sizeof(body)) {
98 "Content-Type: application/json\r\n"
99 "Cache-Control: no-store\r\n"
100 "Content-Length: %zu\r\n"
101 "Connection: close\r\n"
105 status_text != NULL ? status_text :
"Error",
112 time_t now = time(NULL);
118 if (gmtime_s(&tm_value, &now) != 0) {
122 if (gmtime_r(&now, &tm_value) == NULL) {
126 return strftime(out, 20,
"%Y-%m-%d %H:%M:%S", &tm_value) == 19;
137 char *manifest_path = NULL;
141 char *relative = NULL;
142 char *resolved = NULL;
143 if (app == NULL || fixture_key == NULL || fixture_key[0] ==
'\0') {
147 if (manifest_path == NULL) {
157 if (fixtures != NULL) {
160 if (relative != NULL && relative[0] !=
'\0') {
161 if (relative[0] ==
'/') {
187 if (actions == NULL || action_id == NULL || !
yyjson_is_arr(actions)) {
204 if (workflow_out != NULL) {
205 *workflow_out = NULL;
207 if (workflows == NULL || action_id == NULL || !
yyjson_is_arr(workflows)) {
221 if (workflow_out != NULL) {
222 *workflow_out = workflow;
278 if (context == NULL) {
285 memset(context, 0,
sizeof(*context));
295 memset(url, 0,
sizeof(*url));
299 if (result == NULL) {
304 memset(result, 0,
sizeof(*result));
308 time_t now = time(NULL);
313 now += offset_seconds;
315 if (gmtime_s(&tm_value, &now) != 0) {
319 if (gmtime_r(&now, &tm_value) == NULL) {
323 return strftime(out, 20,
"%Y-%m-%d %H:%M:%S", &tm_value) == 19;
332 copy = strdup(value);
343 if (context == NULL || record_id <= 0) {
355 if (resource_id != NULL && resource_id[0] !=
'\0') {
365 if (context == NULL || record_id <= 0) {
380 const char *overflow_policy
382 if (context == NULL) {
386 if (matched_count > applied_count) {
389 if (overflow_policy != NULL && overflow_policy[0] !=
'\0'
401 for (index = 0; index < map->
count; index++) {
412 if (map == NULL || field_id == NULL) {
415 for (index = 0; index < map->
count; index++) {
425 if (presentation == NULL || field_name == NULL || field_name[0] ==
'\0') {
433 if (presentation == NULL || field_name == NULL || field_name[0] ==
'\0') {
443 char **error_message_out
451 if (error_message_out != NULL) {
452 *error_message_out = NULL;
457 memset(map, 0,
sizeof(*map));
466 if (map->
items == NULL) {
476 if (field_id == NULL || field_id[0] ==
'\0') {
483 if (required && (value == NULL || value[0] ==
'\0')) {
484 if (error_message_out != NULL) {
485 size_t needed = strlen(field_id) + 64;
486 *error_message_out = (
char *)malloc(needed);
487 if (*error_message_out != NULL) {
488 snprintf(*error_message_out, needed,
"Missing required field: %s.", field_id);
497 if (type != NULL &&
streq(type,
"integer") && value != NULL && value[0] !=
'\0') {
499 (void)strtol(value, &end, 10);
500 if (end == NULL || *end !=
'\0') {
501 if (error_message_out != NULL) {
502 size_t needed = strlen(field_id) + 64;
503 *error_message_out = (
char *)malloc(needed);
504 if (*error_message_out != NULL) {
505 snprintf(*error_message_out, needed,
"Invalid integer field: %s.", field_id);
516 map->
items[index].
value = value != NULL ? value : strdup(
"");
528 const char *list_item_text,
534 if (value_contract == NULL) {
541 snprintf(buffer,
sizeof(buffer),
"%lld", (
long long)
yyjson_get_sint(value_contract));
542 return strdup(buffer);
551 if (source != NULL &&
streq(source,
"input")) {
555 }
else if (source != NULL &&
streq(source,
"literal")) {
562 snprintf(buffer,
sizeof(buffer),
"%lld", (
long long)
yyjson_get_sint(literal));
563 result = strdup(buffer);
567 }
else if (source != NULL &&
streq(source,
"list_item")) {
568 result = strdup(list_item_text != NULL ? list_item_text :
"");
569 }
else if (source != NULL &&
streq(source,
"context_anchor_created_id")) {
570 snprintf(buffer,
sizeof(buffer),
"%ld", context != NULL ? context->
anchor_record_id : 0L);
571 result = strdup(buffer);
572 }
else if (source != NULL &&
streq(source,
"context_last_created_id")) {
574 result = strdup(buffer);
575 }
else if (source != NULL &&
streq(source,
"context_last_touched_id")) {
577 result = strdup(buffer);
578 }
else if (source != NULL &&
streq(source,
"source_record_field")) {
586 snprintf(buffer,
sizeof(buffer),
"%lld", (
long long)
yyjson_get_sint(current));
587 result = strdup(buffer);
604 const char *list_item_text,
609 if (doc == NULL || value_contract == NULL || !
yyjson_is_obj(value_contract)) {
613 if (source != NULL &&
streq(source,
"input")) {
619 }
else if (source != NULL &&
streq(source,
"next_id")) {
621 }
else if (source != NULL &&
streq(source,
"literal")) {
632 }
else if (source != NULL &&
streq(source,
"toggle_boolean")) {
637 }
else if (source != NULL &&
streq(source,
"record_field")) {
650 }
else if (source != NULL &&
streq(source,
"context_anchor_created_id")) {
652 }
else if (source != NULL &&
streq(source,
"context_last_created_id")) {
654 }
else if (source != NULL &&
streq(source,
"context_last_touched_id")) {
656 }
else if (source != NULL &&
streq(source,
"list_item")) {
658 }
else if (source != NULL &&
streq(source,
"source_record_field")) {
679 if (items_out == NULL || count_out == NULL) {
687 if (item[0] ==
'\0') {
691 grown = (
char **)realloc(*items_out,
sizeof(
char *) * (*count_out + 1));
697 (*items_out)[*count_out] = item;
698 *count_out = *count_out + 1;
704 for (index = 0; index < count; index++) {
711 const char *cursor = NULL;
712 const char *next = NULL;
713 size_t delimiter_len = 0;
714 if (items_out == NULL || count_out == NULL) {
719 if (raw_value == NULL) {
722 delimiter = (delimiter != NULL && delimiter[0] !=
'\0') ? delimiter :
"\n";
723 delimiter_len = strlen(delimiter);
724 if (delimiter_len == 0) {
730 next = strstr(cursor, delimiter);
737 cursor = next + delimiter_len;
742 if (doc == NULL || obj == NULL || field_name == NULL || value == NULL || !
yyjson_mut_is_obj(obj)) {
751 char *resource_id = NULL;
752 if (collection_name == NULL || collection_name[0] ==
'\0') {
755 if (field_name == NULL || field_name[0] ==
'\0' || expected_text == NULL || expected_text[0] ==
'\0') {
756 needed = strlen(collection_name) + 8;
757 resource_id = (
char *)malloc(needed);
758 if (resource_id != NULL) {
759 snprintf(resource_id, needed,
"%s:scope", collection_name);
763 needed = strlen(collection_name) + strlen(field_name) + strlen(expected_text) + 16;
764 resource_id = (
char *)malloc(needed);
765 if (resource_id != NULL) {
766 snprintf(resource_id, needed,
"%s:%s:%s", collection_name, field_name, expected_text);
774 if (record == NULL || field_name == NULL || field_name[0] ==
'\0' || expected_text == NULL) {
785 snprintf(buffer,
sizeof(buffer),
"%lld", (
long long)
yyjson_get_sint(field));
786 return streq(buffer, expected_text);
804 char *from_input = NULL;
810 if (from_input != NULL && from_input[0] !=
'\0') {
822 const char *scope_field,
823 const char *expected_text
828 long matched_count = 0;
830 || scope_field == NULL || scope_field[0] ==
'\0'
831 || expected_text == NULL) {
840 return matched_count;
845 const char *scope_field,
846 const char *expected_text
858 long *created_record_id_out,
859 char **resource_id_out
868 if (created_record_id_out != NULL) {
869 *created_record_id_out = 0;
871 if (resource_id_out != NULL) {
872 *resource_id_out = NULL;
874 if (collection_name == NULL || record_contract == NULL || !
yyjson_is_obj(record_contract)) {
875 free(collection_name);
879 if (collection == NULL) {
882 free(collection_name);
887 free(collection_name);
891 if (record == NULL) {
892 free(collection_name);
899 if (field_name == NULL || field_contract == NULL) {
900 free(collection_name);
906 free(collection_name);
913 free(collection_name);
916 if (created_record_id_out != NULL) {
920 if (resource_id_out != NULL && created_record_id_out != NULL && *created_record_id_out > 0) {
921 size_t needed = strlen(collection_name) + 32;
922 *resource_id_out = (
char *)malloc(needed);
923 if (*resource_id_out != NULL) {
924 snprintf(*resource_id_out, needed,
"%s:%ld", collection_name, *created_record_id_out);
927 free(collection_name);
954 long *created_count_out,
955 char **resource_id_out
964 size_t item_count = 0;
967 if (created_count_out != NULL) {
968 *created_count_out = 0;
970 if (resource_id_out != NULL) {
971 *resource_id_out = NULL;
973 if (collection_name == NULL || list_source == NULL || record_contract == NULL || !
yyjson_is_obj(record_contract)) {
974 free(collection_name);
981 free(collection_name);
989 if (collection == NULL) {
992 free(collection_name);
1001 free(collection_name);
1008 for (index = 0; index < item_count; index++) {
1014 char *item_resource_id = NULL;
1015 if (record == NULL) {
1016 free(collection_name);
1028 if (record_field_name == NULL || field_contract == NULL) {
1029 free(collection_name);
1038 free(collection_name);
1048 free(collection_name);
1059 if (record_id > 0) {
1060 size_t needed = strlen(collection_name) + 32;
1061 item_resource_id = (
char *)malloc(needed);
1062 if (item_resource_id == NULL) {
1063 free(collection_name);
1070 snprintf(item_resource_id, needed,
"%s:%ld", collection_name, record_id);
1072 free(item_resource_id);
1073 free(collection_name);
1080 if (resource_id_out != NULL) {
1081 free(*resource_id_out);
1082 *resource_id_out = item_resource_id;
1083 item_resource_id = NULL;
1085 free(item_resource_id);
1088 if (created_count_out != NULL) {
1089 *created_count_out = (long)item_count;
1091 free(collection_name);
1128 long *created_count_out,
1129 char **resource_id_out
1144 long matched_count = 0;
1145 long created_count = 0;
1146 if (created_count_out != NULL) {
1147 *created_count_out = 0;
1149 if (resource_id_out != NULL) {
1150 *resource_id_out = NULL;
1152 if (source_collection_name == NULL || source_collection_name[0] ==
'\0') {
1153 free(source_collection_name);
1154 source_collection_name = collection_name != NULL ? strdup(collection_name) : NULL;
1156 if (overflow_policy == NULL || overflow_policy[0] ==
'\0') {
1157 free(overflow_policy);
1158 overflow_policy = strdup(
"truncate");
1160 if (collection_name == NULL || source_collection_name == NULL || scope_field == NULL || expected_text == NULL
1161 || record_contract == NULL || !
yyjson_is_obj(record_contract) || overflow_policy == NULL) {
1162 free(collection_name);
1163 free(source_collection_name);
1165 free(expected_text);
1166 free(overflow_policy);
1170 if (target_collection == NULL) {
1173 free(collection_name);
1174 free(source_collection_name);
1176 free(expected_text);
1177 free(overflow_policy);
1181 source_collection = source_root != NULL ?
native_json_obj_get(source_root, source_collection_name) : NULL;
1183 free(collection_name);
1184 free(source_collection_name);
1186 free(expected_text);
1187 free(overflow_policy);
1191 if (matched_count <= 0) {
1192 free(collection_name);
1193 free(source_collection_name);
1195 free(expected_text);
1196 free(overflow_policy);
1199 if (
streq(overflow_policy,
"fail") && matched_count > max_records) {
1200 free(collection_name);
1201 free(source_collection_name);
1203 free(expected_text);
1204 free(overflow_policy);
1214 char *record_resource_id = NULL;
1215 if (created_count >= max_records) {
1223 if (record == NULL) {
1224 free(collection_name);
1225 free(source_collection_name);
1227 free(expected_text);
1228 free(overflow_policy);
1236 if (record_field_name == NULL || field_contract == NULL) {
1237 free(collection_name);
1238 free(source_collection_name);
1240 free(expected_text);
1241 free(overflow_policy);
1246 free(collection_name);
1247 free(source_collection_name);
1249 free(expected_text);
1250 free(overflow_policy);
1256 free(collection_name);
1257 free(source_collection_name);
1259 free(expected_text);
1260 free(overflow_policy);
1265 if (record_id > 0) {
1266 size_t needed = strlen(collection_name) + 32;
1267 record_resource_id = (
char *)malloc(needed);
1268 if (record_resource_id == NULL) {
1269 free(collection_name);
1270 free(source_collection_name);
1272 free(expected_text);
1273 free(overflow_policy);
1276 snprintf(record_resource_id, needed,
"%s:%ld", collection_name, record_id);
1278 free(record_resource_id);
1279 free(collection_name);
1280 free(source_collection_name);
1282 free(expected_text);
1283 free(overflow_policy);
1286 if (resource_id_out != NULL) {
1287 free(*resource_id_out);
1288 *resource_id_out = record_resource_id;
1289 record_resource_id = NULL;
1291 free(record_resource_id);
1295 if (created_count == 0) {
1296 free(collection_name);
1297 free(source_collection_name);
1299 free(expected_text);
1300 free(overflow_policy);
1304 free(collection_name);
1305 free(source_collection_name);
1307 free(expected_text);
1308 free(overflow_policy);
1311 if (resource_id_out != NULL && *resource_id_out == NULL) {
1314 if (created_count_out != NULL) {
1315 *created_count_out = created_count;
1317 free(collection_name);
1318 free(source_collection_name);
1320 free(expected_text);
1321 free(overflow_policy);
1331 char **resource_id_out,
1332 char **from_state_out,
1341 long match_id = input_value != NULL ? strtol(input_value, NULL, 10) : 0;
1347 if (resource_id_out != NULL) {
1348 *resource_id_out = NULL;
1350 if (from_state_out != NULL) {
1351 *from_state_out = NULL;
1353 if (to_state_out != NULL) {
1354 *to_state_out = NULL;
1356 if (collection_name == NULL || match_field == NULL || from_input == NULL || updates == NULL || !
yyjson_is_obj(updates) || match_id <= 0) {
1357 free(collection_name);
1365 if (record == NULL) {
1366 free(collection_name);
1372 if (from_state_out != NULL) {
1382 if (field_name == NULL || field_contract == NULL) {
1383 free(collection_name);
1392 free(collection_name);
1399 free(*to_state_out);
1405 if (resource_id_out != NULL) {
1406 size_t needed = strlen(collection_name) + 32;
1407 *resource_id_out = (
char *)malloc(needed);
1408 if (*resource_id_out != NULL) {
1409 snprintf(*resource_id_out, needed,
"%s:%ld", collection_name, match_id);
1413 free(collection_name);
1419 free(collection_name);
1433 char **resource_id_out
1448 long updated_count = 0;
1449 long matched_count = 0;
1450 if (resource_id_out != NULL) {
1451 *resource_id_out = NULL;
1453 if (overflow_policy == NULL || overflow_policy[0] ==
'\0') {
1454 free(overflow_policy);
1455 overflow_policy = strdup(
"truncate");
1457 if (collection_name == NULL || scope_field == NULL || target_field == NULL || value_contract == NULL || expected_text == NULL || overflow_policy == NULL) {
1458 free(collection_name);
1460 free(expected_text);
1462 free(overflow_policy);
1466 source_collection = source_root != NULL ?
native_json_obj_get(source_root, collection_name) : NULL;
1468 || source_collection == NULL || !
yyjson_is_arr(source_collection)) {
1469 free(collection_name);
1471 free(expected_text);
1473 free(overflow_policy);
1477 if (matched_count <= 0) {
1478 free(collection_name);
1480 free(expected_text);
1482 free(overflow_policy);
1485 if (
streq(overflow_policy,
"fail") && matched_count > max_records) {
1486 free(collection_name);
1488 free(expected_text);
1490 free(overflow_policy);
1497 char *record_resource_id = NULL;
1499 if (updated_count >= max_records) {
1509 if (record == NULL) {
1510 free(collection_name);
1512 free(expected_text);
1514 free(overflow_policy);
1519 free(collection_name);
1521 free(expected_text);
1523 free(overflow_policy);
1526 if (record_id > 0) {
1527 size_t needed = strlen(collection_name) + 32;
1528 record_resource_id = (
char *)malloc(needed);
1529 if (record_resource_id == NULL) {
1530 free(collection_name);
1532 free(expected_text);
1534 free(overflow_policy);
1537 snprintf(record_resource_id, needed,
"%s:%ld", collection_name, record_id);
1539 free(record_resource_id);
1540 free(collection_name);
1542 free(expected_text);
1544 free(overflow_policy);
1547 free(record_resource_id);
1551 if (updated_count == 0) {
1552 free(collection_name);
1554 free(expected_text);
1556 free(overflow_policy);
1561 free(collection_name);
1563 free(expected_text);
1565 free(overflow_policy);
1568 if (resource_id_out != NULL) {
1571 free(collection_name);
1573 free(expected_text);
1575 free(overflow_policy);
1585 char **resource_id_out,
1586 char **from_state_out,
1600 long updated_count = 0;
1601 long matched_count = 0;
1602 if (resource_id_out != NULL) {
1603 *resource_id_out = NULL;
1605 if (from_state_out != NULL) {
1606 *from_state_out = NULL;
1608 if (to_state_out != NULL) {
1609 *to_state_out = NULL;
1611 if (overflow_policy == NULL || overflow_policy[0] ==
'\0') {
1612 free(overflow_policy);
1613 overflow_policy = strdup(
"truncate");
1615 if (collection_name == NULL || scope_field == NULL || expected_text == NULL || updates == NULL || !
yyjson_is_obj(updates) || overflow_policy == NULL) {
1616 free(collection_name);
1618 free(expected_text);
1619 free(overflow_policy);
1624 free(collection_name);
1626 free(expected_text);
1627 free(overflow_policy);
1631 if (matched_count <= 0) {
1632 free(collection_name);
1634 free(expected_text);
1635 free(overflow_policy);
1638 if (
streq(overflow_policy,
"fail") && matched_count > max_records) {
1639 free(collection_name);
1641 free(expected_text);
1642 free(overflow_policy);
1651 char *record_resource_id = NULL;
1652 if (updated_count >= max_records) {
1658 if (from_state_out != NULL && *from_state_out == NULL) {
1669 if (field_name == NULL || field_contract == NULL) {
1670 free(collection_name);
1672 free(expected_text);
1673 free(overflow_policy);
1678 free(collection_name);
1680 free(expected_text);
1681 free(overflow_policy);
1685 free(*to_state_out);
1692 if (record_id > 0) {
1693 size_t needed = strlen(collection_name) + 32;
1694 record_resource_id = (
char *)malloc(needed);
1695 if (record_resource_id == NULL) {
1696 free(collection_name);
1698 free(expected_text);
1699 free(overflow_policy);
1702 snprintf(record_resource_id, needed,
"%s:%ld", collection_name, record_id);
1704 free(record_resource_id);
1705 free(collection_name);
1707 free(expected_text);
1708 free(overflow_policy);
1711 free(record_resource_id);
1715 if (updated_count == 0) {
1716 free(collection_name);
1718 free(expected_text);
1719 free(overflow_policy);
1724 free(collection_name);
1726 free(expected_text);
1727 free(overflow_policy);
1730 if (resource_id_out != NULL) {
1733 free(collection_name);
1735 free(expected_text);
1736 free(overflow_policy);
1759 if (field_name == NULL || field_contract == NULL) {
1805 if (array == NULL || obj == NULL) {
1815 if (adapters == NULL || adapter_id == NULL || adapter_id[0] ==
'\0' || !
yyjson_is_arr(adapters)) {
1829 char *target_url = NULL;
1830 char *target_url_env = NULL;
1835 if (target_url != NULL && target_url[0] !=
'\0') {
1840 if (target_url_env != NULL && target_url_env[0] !=
'\0') {
1841 const char *env_value = getenv(target_url_env);
1842 free(target_url_env);
1843 if (env_value != NULL && env_value[0] !=
'\0') {
1844 return strdup(env_value);
1848 free(target_url_env);
1853 const char *remainder = NULL;
1854 const char *slash = NULL;
1855 const char *colon = NULL;
1856 size_t host_len = 0;
1857 if (parsed == NULL) {
1860 memset(parsed, 0,
sizeof(*parsed));
1861 if (url == NULL || strncmp(url,
"http://", 7) != 0) {
1864 parsed->
scheme = strdup(
"http");
1865 remainder = url + 7;
1866 slash = strchr(remainder,
'/');
1867 if (slash == NULL) {
1868 slash = remainder + strlen(remainder);
1869 parsed->
path = strdup(
"/");
1871 parsed->
path = strdup(slash[0] !=
'\0' ? slash :
"/");
1875 const char *cursor = remainder;
1876 while (cursor < slash) {
1877 if (*cursor ==
':') {
1883 if (colon != NULL) {
1884 host_len = (size_t)(colon - remainder);
1885 parsed->
port = atoi(colon + 1);
1887 host_len = (size_t)(slash - remainder);
1891 if (parsed->
scheme == NULL || parsed->
host == NULL || parsed->
path == NULL || parsed->
host[0] ==
'\0' || parsed->
port <= 0) {
1901 if (buffer == NULL || length == NULL || capacity == NULL || chunk == NULL) {
1904 needed = *length + chunk_len + 1;
1905 if (needed > *capacity) {
1906 size_t next_capacity = *capacity == 0 ? 1024 : *capacity;
1907 while (next_capacity < needed) {
1910 grown = (
char *)realloc(*buffer, next_capacity);
1911 if (grown == NULL) {
1915 *capacity = next_capacity;
1917 memcpy(*buffer + *length, chunk, chunk_len);
1918 *length += chunk_len;
1919 (*buffer)[*length] =
'\0';
1924 const char *target_url,
1925 const char *content_type,
1926 const char *payload_json,
1930 struct addrinfo hints;
1931 struct addrinfo *records = NULL;
1932 struct addrinfo *current = NULL;
1934 char *request = NULL;
1935 size_t request_length = 0;
1938 char response_chunk[4096];
1939 char *response_text = NULL;
1940 size_t response_length = 0;
1941 size_t response_capacity = 0;
1942 if (result == NULL) {
1945 memset(result, 0,
sizeof(*result));
1950 snprintf(port_text,
sizeof(port_text),
"%d", parsed.
port);
1951 memset(&hints, 0,
sizeof(hints));
1952 hints.ai_family = AF_UNSPEC;
1953 hints.ai_socktype = SOCK_STREAM;
1954 if (getaddrinfo(parsed.
host, port_text, &hints, &records) != 0) {
1959 for (current = records; current != NULL; current = current->ai_next) {
1960 fd = socket(current->ai_family, current->ai_socktype, current->ai_protocol);
1966 struct timeval timeout;
1967 timeout.tv_sec = 10;
1968 timeout.tv_usec = 0;
1969 setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout,
sizeof(timeout));
1970 setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeout,
sizeof(timeout));
1973 if (connect(fd, current->ai_addr, current->ai_addrlen) == 0) {
1984 freeaddrinfo(records);
1985 if (!connected || fd < 0) {
1991 size_t needed = strlen(parsed.
path) + strlen(parsed.
host) + strlen(content_type != NULL ? content_type :
"application/json")
1992 + strlen(payload_json != NULL ? payload_json :
"{}") + 256;
1993 request = (
char *)malloc(needed);
1994 if (request == NULL) {
2004 request_length = (size_t)snprintf(
2007 "POST %s HTTP/1.1\r\nHost: %s\r\nContent-Type: %s\r\nContent-Length: %zu\r\nConnection: close\r\n\r\n%s",
2010 content_type != NULL ? content_type :
"application/json",
2011 strlen(payload_json != NULL ? payload_json :
"{}"),
2012 payload_json != NULL ? payload_json :
"{}"
2015 if (request == NULL) {
2027 while (sent < request_length) {
2028 ssize_t wrote = send(fd, request + sent, request_length - sent, 0);
2037 free(response_text);
2041 sent += (size_t)wrote;
2046 ssize_t read_count = recv(fd, response_chunk,
sizeof(response_chunk), 0);
2047 if (read_count == 0) {
2050 if (read_count < 0) {
2051 free(response_text);
2061 if (!
append_bytes_logic_local(&response_text, &response_length, &response_capacity, response_chunk, (
size_t)read_count)) {
2062 free(response_text);
2079 if (response_text != NULL) {
2080 char *body_start = strstr(response_text,
"\r\n\r\n");
2081 char *status_line_end = strstr(response_text,
"\r\n");
2082 if (status_line_end != NULL) {
2083 char *first_space = strchr(response_text,
' ');
2084 if (first_space != NULL) {
2085 result->
http_status = strtol(first_space + 1, NULL, 10);
2088 if (body_start != NULL) {
2097 snprintf(buffer,
sizeof(buffer),
"http_%ld", result->
http_status);
2100 free(response_text);
2101 return result->
ok ? 1 : 0;
2105 const char *action_id,
2106 const char *adapter_id,
2107 const char *resource_id,
2108 const char *replay_trace_id,
2109 const char *created_at
2113 char *result = NULL;
2114 if (doc == NULL || root == NULL) {
2135 long job_execution_id,
2136 const char *execution_status,
2137 const char *payload_json,
2138 const char *created_at,
2139 const char *error_message
2146 int ok = attempts != NULL && attempt != NULL
2150 &&
yyjson_mut_obj_add_strcpy(mut_doc, attempt,
"execution_status", execution_status != NULL ? execution_status :
"succeeded")
2164 const char *created_at,
2165 const char *payload_json
2170 char next_attempt_at[20];
2171 long delivery_id = 0;
2172 long attempt_count = 0;
2173 long max_attempts = 0;
2174 long backoff_seconds = 0;
2175 char *target_url = NULL;
2176 char *delivery_status = NULL;
2177 char *error_message = NULL;
2179 memset(&result, 0,
sizeof(result));
2180 if (delivery == NULL || attempts == NULL || attempt == NULL) {
2189 delivery_status = strdup(
"succeeded");
2190 }
else if (attempt_count < max_attempts) {
2191 delivery_status = strdup(
"retryable");
2193 delivery_status = strdup(
"failed");
2197 snprintf(next_attempt_at,
sizeof(next_attempt_at),
"%s", created_at != NULL ? created_at :
"");
2199 if (delivery_status == NULL || error_message == NULL
2217 free(delivery_status);
2218 free(error_message);
2223 free(delivery_status);
2224 free(error_message);
2232 const char *created_at,
2233 const char *payload_json
2239 if (deliveries == NULL) {
2246 if (status != NULL && (
streq(status,
"queued") ||
streq(status,
"retryable"))) {
2247 if (next_attempt_at == NULL || next_attempt_at[0] ==
'\0' || created_at == NULL || strcmp(next_attempt_at, created_at) <= 0) {
2252 free(next_attempt_at);
2271 const char *resource_id,
2272 const char *from_state,
2273 const char *to_state,
2274 const char *created_at,
2275 const char *policy_gate,
2276 const char *success_message,
2277 const char *operator_summary,
2278 const char *transition_label,
2279 const char *from_state_label,
2280 const char *to_state_label,
2281 long workflow_execution_id,
2282 long created_record_id,
2283 char **report_json_out,
2284 char **replay_trace_id_out
2292 char replay_trace_buffer[256];
2293 if (report_json_out != NULL) {
2294 *report_json_out = NULL;
2296 if (replay_trace_id_out != NULL) {
2297 *replay_trace_id_out = NULL;
2301 if (action_reports == NULL || report == NULL || action_id == NULL) {
2304 free(transition_id);
2308 snprintf(replay_trace_buffer,
sizeof(replay_trace_buffer),
"trace:%s:%s:%ld", action_id, resource_id != NULL ? resource_id :
"resource", workflow_execution_id);
2309 if (replay_trace_id_out != NULL) {
2310 *replay_trace_id_out = strdup(replay_trace_buffer);
2336 || !
yyjson_mut_obj_add_strcpy(mut_doc, report,
"from_state_label", from_state_label != NULL ? from_state_label : (from_state != NULL ? from_state :
"none"))
2337 || !
yyjson_mut_obj_add_strcpy(mut_doc, report,
"to_state_label", to_state_label != NULL ? to_state_label : (to_state != NULL ? to_state :
"applied"))
2344 free(transition_id);
2374 || !
yyjson_mut_obj_add_strcpy(mut_doc, summary,
"from_state_label", from_state_label != NULL ? from_state_label : (from_state != NULL ? from_state :
"none"))
2375 || !
yyjson_mut_obj_add_strcpy(mut_doc, summary,
"to_state_label", to_state_label != NULL ? to_state_label : (to_state != NULL ? to_state :
"applied"))
2382 free(transition_id);
2386 if (report_json_out != NULL) {
2387 size_t needed = strlen(action_id) + strlen(policy_gate != NULL ? policy_gate :
"public")
2388 + strlen(resource_id != NULL ? resource_id :
"") + strlen(from_state != NULL ? from_state :
"none")
2389 + strlen(to_state != NULL ? to_state :
"applied") + strlen(replay_trace_buffer)
2390 + strlen(workflow_id != NULL ? workflow_id :
"") + strlen(transition_id != NULL ? transition_id :
"")
2391 + strlen(event_name != NULL ? event_name :
"") + strlen(created_at != NULL ? created_at :
"")
2392 + strlen(success_message != NULL ? success_message :
"") + strlen(operator_summary != NULL ? operator_summary :
"")
2393 + strlen(transition_label != NULL ? transition_label :
"")
2394 + strlen(from_state_label != NULL ? from_state_label : (from_state != NULL ? from_state :
"none"))
2395 + strlen(to_state_label != NULL ? to_state_label : (to_state != NULL ? to_state :
"applied")) + 896;
2396 *report_json_out = (
char *)malloc(needed);
2397 if (*report_json_out == NULL) {
2400 free(transition_id);
2407 "{\"action_id\":\"%s\",\"policy_gate\":\"%s\",\"resource_id\":\"%s\",\"from_state\":\"%s\",\"to_state\":\"%s\",\"workflow_execution_id\":%ld,\"created_record_id\":%ld,\"created_at\":\"%s\",\"replay_trace_id\":\"%s\",\"workflow_id\":\"%s\",\"transition_id\":\"%s\",\"event_name\":\"%s\",\"anchor_record_id\":%ld,\"last_created_record_id\":%ld,\"last_touched_record_id\":%ld,\"created_record_count\":%ld,\"updated_record_count\":%ld,\"matched_record_count\":%ld,\"skipped_record_count\":%ld,\"overflow_policy\":\"%s\",\"operator_message\":\"%s\",\"operator_summary\":\"%s\",\"transition_label\":\"%s\",\"from_state_label\":\"%s\",\"to_state_label\":\"%s\",\"queued_job_count\":%ld,\"queued_webhook_count\":%ld,\"queued_notification_count\":%ld}",
2409 policy_gate != NULL ? policy_gate :
"public",
2410 resource_id != NULL ? resource_id :
"",
2411 from_state != NULL ? from_state :
"none",
2412 to_state != NULL ? to_state :
"applied",
2413 workflow_execution_id,
2415 created_at != NULL ? created_at :
"",
2416 replay_trace_buffer,
2417 workflow_id != NULL ? workflow_id :
"",
2418 transition_id != NULL ? transition_id :
"",
2419 event_name != NULL ? event_name :
"",
2428 success_message != NULL ? success_message :
"",
2429 operator_summary != NULL ? operator_summary :
"",
2430 transition_label != NULL ? transition_label :
"",
2431 from_state_label != NULL ? from_state_label : (from_state != NULL ? from_state :
"none"),
2432 to_state_label != NULL ? to_state_label : (to_state != NULL ? to_state :
"applied"),
2433 effect_counts != NULL ? effect_counts->
job_count : 0,
2441 free(transition_id);
2455 const char *policy_gate,
2456 const char *resource_id,
2457 const char *from_state,
2458 const char *to_state,
2459 const char *created_at,
2460 const char *replay_trace_id,
2461 long *workflow_execution_id_out,
2493 (void)job_execution_attempts;
2494 (void)integration_delivery_attempts;
2495 if (workflow_execution_id_out != NULL) {
2496 *workflow_execution_id_out = 0;
2498 if (effect_counts_out != NULL) {
2499 memset(effect_counts_out, 0,
sizeof(*effect_counts_out));
2501 if (workflow_executions == NULL || workflow_events == NULL || job_executions == NULL || job_execution_attempts == NULL || webhook_receipts == NULL || notification_deliveries == NULL || integration_deliveries == NULL || integration_delivery_attempts == NULL || execution == NULL || event == NULL) {
2503 free(transition_id);
2509 || !
yyjson_mut_obj_add_strcpy(mut_doc, execution,
"workflow_id", workflow_id != NULL ? workflow_id :
"framework_action")
2510 || !
yyjson_mut_obj_add_strcpy(mut_doc, execution,
"transition_id", transition_id != NULL ? transition_id :
"action_applied")
2527 free(transition_id);
2537 free(transition_id);
2545 || !
yyjson_mut_obj_add_strcpy(mut_doc, payload,
"transition_id", transition_id != NULL ? transition_id :
"action_applied")
2553 free(transition_id);
2558 if (event_name != NULL && event_name[0] !=
'\0') {
2561 if (emitted == NULL || payload == NULL
2573 free(transition_id);
2577 workflow_event_id++;
2585 if (job == NULL || job_event == NULL || job_payload == NULL
2620 free(transition_id);
2624 if (effect_counts_out != NULL) {
2628 workflow_event_id++;
2640 if (receipt == NULL || integration_delivery == NULL || payload_json == NULL
2667 free(transition_id);
2674 if (effect_counts_out != NULL) {
2682 char *adapter_id = NULL;
2683 char *channel = NULL;
2684 char *recipient = NULL;
2685 char *template_id = NULL;
2697 if (adapter_id == NULL) {
2698 adapter_id = strdup(
"notification");
2700 if (channel == NULL) {
2701 channel = strdup(
"generic");
2703 if (recipient == NULL) {
2704 recipient = strdup(
"");
2706 if (template_id == NULL) {
2707 template_id = strdup(
"");
2709 if (delivery == NULL || notification_event == NULL || notification_payload == NULL
2710 || adapter_id == NULL || channel == NULL || recipient == NULL || template_id == NULL
2733 || !
yyjson_mut_obj_add_strcpy(mut_doc, notification_payload,
"replay_trace_id", replay_trace_id != NULL ? replay_trace_id :
"")
2741 free(transition_id);
2745 if (effect_counts_out != NULL) {
2753 workflow_event_id++;
2756 if (workflow_execution_id_out != NULL) {
2757 *workflow_execution_id_out = workflow_execution_id;
2760 free(transition_id);
2770 const char *resource_id,
2771 const char *created_at,
2772 const char *replay_trace_id,
2773 const char *report_json,
2774 const char *from_state,
2775 const char *to_state
2781 if (audit_log == NULL || audit == NULL || payload == NULL) {
2794 if (report_json != NULL && report_json[0] !=
'\0') {
2816 if (app == NULL || mut_doc == NULL) {
2825 return code == 0 ? 0 : 69;
2843 return "No Content";
2847 return "Bad Request";
2851 return "Unprocessable Entity";
2869 if (location == NULL || location[0] ==
'\0') {
2870 location = strdup(
"/");
2874 "HTTP/1.1 %d See Other\r\n"
2875 "Location: %s%s\r\n"
2876 "Cache-Control: no-store\r\n"
2877 "Content-Length: 0\r\n"
2878 "Connection: close\r\n"
2882 location != NULL ? location :
"/"
2906 const char *action_id,
2907 const char *resource_id,
2908 long workflow_execution_id,
2909 const char *report_json,
2922 size_t body_length = 0;
2923 if (connection == NULL || report_json == NULL || mut_doc == NULL) {
2927 response_root = response_doc != NULL ?
yyjson_mut_obj(response_doc) : NULL;
2930 if (response_doc != NULL && response_root != NULL) {
2933 if (response_doc == NULL || response_root == NULL || report_root == NULL
2944 if (include_state) {
2946 if (state_text == NULL) {
2964 body_length = body != NULL ? strlen(body) : 0;
2974 "HTTP/1.1 %d %s\r\n"
2975 "Content-Type: application/json\r\n"
2976 "Cache-Control: no-store\r\n"
2977 "Content-Length: %zu\r\n"
2978 "Connection: close\r\n"
3010 const char *action_id,
3011 const char *resource_id,
3012 long workflow_execution_id,
3013 const char *report_json,
3018 if (response_kind != NULL &&
streq(response_kind,
"json")) {
3023 free(response_kind);
3029 const char *action_id,
3030 char **submit_label_out,
3037 if (submit_label_out != NULL) {
3040 if (summary_out != NULL) {
3044 return action_contract != NULL ? 1 : 0;
3051 const char *action_id
3067 char *input_error = NULL;
3068 char *policy_gate = NULL;
3069 char *success_message = NULL;
3070 char *failure_message = NULL;
3071 char *operator_summary = NULL;
3072 char *transition_label = NULL;
3073 char *from_state_label = NULL;
3074 char *to_state_label = NULL;
3075 char *created_at = NULL;
3076 char *resource_id = NULL;
3077 char *from_state = NULL;
3078 char *to_state = NULL;
3079 char *report_json = NULL;
3080 char *replay_trace_id = NULL;
3085 long created_record_id = 0;
3086 long workflow_execution_id = 0;
3088 char created_at_buffer[20];
3091 if (action_doc == NULL) {
3097 if (action_contract == NULL) {
3115 if (input_error != NULL) {
3120 "Unprocessable Entity",
3121 "invalid_action_input",
3122 failure_message != NULL && failure_message[0] !=
'\0' ? failure_message : input_error
3130 write_json_error_logic_local(connection, 500,
"Internal Server Error",
"state_unavailable",
"Declarative action state is unavailable.");
3137 write_json_error_logic_local(connection, 500,
"Internal Server Error",
"action_runtime_error",
"Declarative action runtime initialization failed.");
3140 created_at = strdup(created_at_buffer);
3143 if (mutation_primitives == NULL || !
yyjson_is_arr(mutation_primitives)) {
3145 write_json_error_logic_local(connection, 500,
"Internal Server Error",
"missing_mutation_primitives",
"Declarative action is missing mutation primitives.");
3151 char *primitive_resource_id = NULL;
3152 int primitive_code = 0;
3153 long created_count = 0;
3154 if (kind != NULL &&
streq(kind,
"append_record")) {
3156 if (primitive_code == 1) {
3158 free(primitive_resource_id);
3161 write_json_error_logic_local(connection, 500,
"Internal Server Error",
"action_runtime_error",
"Failed to record created action context.");
3166 free(primitive_resource_id);
3172 if (from_state == NULL) {
3173 from_state = strdup(
"none");
3175 if (to_state == NULL) {
3176 to_state = strdup(
"created");
3179 }
else if (kind != NULL &&
streq(kind,
"append_records_from_input_list")) {
3181 if (primitive_code == 1 && created_count > 0) {
3182 next_id += created_count;
3184 free(primitive_resource_id);
3190 if (from_state == NULL) {
3191 from_state = strdup(
"none");
3193 if (to_state == NULL) {
3194 to_state = strdup(
"created");
3197 }
else if (kind != NULL &&
streq(kind,
"append_records_from_related_collection")) {
3199 if (primitive_code == 1 && created_count > 0) {
3200 next_id += created_count;
3202 free(primitive_resource_id);
3208 if (from_state == NULL) {
3209 from_state = strdup(
"none");
3211 if (to_state == NULL) {
3212 to_state = strdup(
"created");
3215 }
else if (kind != NULL &&
streq(kind,
"update_record")) {
3217 }
else if (kind != NULL &&
streq(kind,
"scoped_reassignment")) {
3219 }
else if (kind != NULL &&
streq(kind,
"bulk_update")) {
3221 }
else if (kind != NULL &&
streq(kind,
"set_root_fields")) {
3224 if (primitive_resource_id != NULL) {
3225 if (resource_id == NULL) {
3226 resource_id = primitive_resource_id;
3227 primitive_resource_id = NULL;
3229 free(primitive_resource_id);
3232 if (primitive_code == -2) {
3237 "Unprocessable Entity",
3238 "action_scope_overflow",
3239 failure_message != NULL && failure_message[0] !=
'\0'
3241 :
"Matching scope exceeded the configured action bound."
3245 if (primitive_code == -1) {
3251 "action_target_not_found",
3252 failure_message != NULL && failure_message[0] !=
'\0'
3254 :
"Action target record not found."
3258 if (primitive_code == 0) {
3260 write_json_error_logic_local(connection, 500,
"Internal Server Error",
"action_runtime_error",
"Declarative action primitive failed.");
3266 && from_state != NULL && to_state != NULL
3267 &&
streq(from_state,
"none") &&
streq(to_state,
"created")) {
3270 if (transition_from_state != NULL && transition_from_state[0] !=
'\0') {
3272 from_state = transition_from_state;
3273 transition_from_state = NULL;
3275 if (transition_to_state != NULL && transition_to_state[0] !=
'\0') {
3277 to_state = transition_to_state;
3278 transition_to_state = NULL;
3280 free(transition_from_state);
3281 free(transition_to_state);
3283 if (from_state == NULL && transition != NULL) {
3286 if (to_state == NULL && transition != NULL) {
3289 if (from_state == NULL) {
3290 from_state = strdup(
"none");
3292 if (to_state == NULL) {
3293 to_state = strdup(
"applied");
3295 if (created_record_id <= 0) {
3304 if (resource_id == NULL && created_record_id > 0) {
3306 resource_id = (
char *)malloc(needed);
3307 if (resource_id != NULL) {
3308 snprintf(resource_id, needed,
"record:%ld", created_record_id);
3311 if (resource_id == NULL) {
3312 resource_id = strdup(
"resource:unknown");
3314 if (!
append_workflow_and_effects_logic_local(app, mut_doc, mut_root, action_contract, workflow, transition, &inputs, &context, policy_gate, resource_id, from_state, to_state, created_at, NULL, &workflow_execution_id, &effect_counts)) {
3316 write_json_error_logic_local(connection, 500,
"Internal Server Error",
"workflow_recording_failed",
"Failed to record workflow execution.");
3337 workflow_execution_id,
3340 &replay_trace_id)) {
3345 if (!
append_audit_event_logic_local(mut_doc, mut_root, transition, resource_id, created_at, replay_trace_id, report_json, from_state, to_state)) {
3352 write_json_error_logic_local(connection, 500,
"Internal Server Error",
"state_persist_failed",
"Failed to persist declarative action state.");
3361 workflow_execution_id,
3374 free(success_message);
3375 free(failure_message);
3376 free(operator_summary);
3377 free(transition_label);
3378 free(from_state_label);
3379 free(to_state_label);
3385 free(replay_trace_id);
static yyjson_val * find_integration_adapter_logic_local(yyjson_val *root, const char *adapter_id)
static yyjson_val * find_action_contract_logic_local(yyjson_val *root, const char *action_id)
static int parse_http_url_logic_local(const char *url, ParsedHttpUrlLogicLocal *parsed)
static yyjson_mut_val * find_mut_record_by_id_logic_local(yyjson_mut_val *collection, long record_id)
static yyjson_doc * load_fixture_doc_logic_local(const AppRuntime *app, const char *fixture_key)
static long matching_record_count_logic_local(yyjson_mut_val *collection, const char *scope_field, const char *expected_text)
static int set_root_fields_primitive_logic_local(yyjson_mut_doc *mut_doc, yyjson_mut_val *mut_root, yyjson_val *primitive, const InputFieldMapLogicLocal *inputs, const ActionExecutionContextLogicLocal *context, long next_id)
static int append_records_from_related_collection_primitive_logic_local(yyjson_mut_doc *mut_doc, yyjson_mut_val *mut_root, yyjson_val *source_root, yyjson_val *primitive, const InputFieldMapLogicLocal *inputs, ActionExecutionContextLogicLocal *context, long next_id, long *created_count_out, char **resource_id_out)
static char * action_presentation_string_dup_logic_local(yyjson_val *action_contract, const char *field_name)
static int bulk_update_primitive_logic_local(yyjson_mut_doc *mut_doc, yyjson_mut_val *mut_root, yyjson_val *primitive, const InputFieldMapLogicLocal *inputs, ActionExecutionContextLogicLocal *context, char **resource_id_out, char **from_state_out, char **to_state_out)
static char * scope_match_expected_text_dup_logic_local(yyjson_val *scope_match, const InputFieldMapLogicLocal *inputs, const ActionExecutionContextLogicLocal *context)
static int process_webhook_delivery_logic_local(yyjson_mut_doc *mut_doc, yyjson_mut_val *mut_root, yyjson_mut_val *delivery, const char *created_at, const char *payload_json)
static int process_due_integration_deliveries_logic_local(yyjson_mut_doc *mut_doc, yyjson_mut_val *mut_root, const char *created_at, const char *payload_json)
static int note_updated_record_logic_local(ActionExecutionContextLogicLocal *context, long record_id, const char *resource_id)
static char * string_value_from_contract_logic_local(yyjson_val *value_contract, const InputFieldMapLogicLocal *inputs, const ActionExecutionContextLogicLocal *context, const char *list_item_text, yyjson_val *source_record_context)
static int load_action_inputs_logic_local(const HttpRequest *request, yyjson_val *action_contract, InputFieldMapLogicLocal *map, char **error_message_out)
static int append_action_report_logic_local(yyjson_mut_doc *mut_doc, yyjson_mut_val *mut_root, yyjson_val *action_contract, yyjson_val *workflow, yyjson_val *transition, const ActionExecutionContextLogicLocal *context, const ActionEffectCountsLogicLocal *effect_counts, const char *resource_id, const char *from_state, const char *to_state, const char *created_at, const char *policy_gate, const char *success_message, const char *operator_summary, const char *transition_label, const char *from_state_label, const char *to_state_label, long workflow_execution_id, long created_record_id, char **report_json_out, char **replay_trace_id_out)
static yyjson_mut_val * find_mut_record_by_long_field_logic_local(yyjson_mut_val *array, const char *field_name, long match_value)
static int send_action_response_logic_local(NativeConnection *connection, const AppRuntime *app, yyjson_val *response_contract, const char *action_id, const char *resource_id, long workflow_execution_id, const char *report_json, yyjson_mut_doc *mut_doc)
Sends the configured declarative response contract for an action.
static int append_mut_object_to_array_logic_local(yyjson_mut_doc *doc, yyjson_mut_val *root, const char *section_name, yyjson_mut_val *obj)
static int note_bounded_scope_logic_local(ActionExecutionContextLogicLocal *context, long matched_count, long applied_count, const char *overflow_policy)
static long next_section_id_logic_local(yyjson_mut_val *root, const char *section_name)
static int send_redirect_logic_local(NativeConnection *connection, const AppRuntime *app, yyjson_val *response_contract)
Sends a declarative redirect response contract.
static char * integration_adapter_target_url_dup_logic_local(yyjson_val *adapter)
static long matching_record_count_value_logic_local(yyjson_val *collection, const char *scope_field, const char *expected_text)
static int update_record_primitive_logic_local(yyjson_mut_doc *mut_doc, yyjson_mut_val *mut_root, yyjson_val *primitive, const InputFieldMapLogicLocal *inputs, ActionExecutionContextLogicLocal *context, char **resource_id_out, char **from_state_out, char **to_state_out)
int native_dynamic_execute_declarative_action(NativeConnection *connection, const AppRuntime *app, const HttpRequest *request, const char *action_id)
Executes one declarative dynamic-app action contract against JSON state.
static void free_outbound_http_result_logic_local(OutboundHttpResultLogicLocal *result)
static yyjson_mut_val * ensure_mut_array_field_logic_local(yyjson_mut_doc *doc, yyjson_mut_val *root, const char *field_name)
int native_dynamic_action_presentation_dup(const AppRuntime *app, const char *action_id, char **submit_label_out, char **summary_out)
Loads declarative presentation overrides for one action contract.
static char * scoped_resource_id_dup_logic_local(const char *collection_name, const char *field_name, const char *expected_text)
static int append_record_primitive_logic_local(yyjson_mut_doc *mut_doc, yyjson_mut_val *mut_root, yyjson_val *primitive, const InputFieldMapLogicLocal *inputs, const ActionExecutionContextLogicLocal *context, long next_id, long *created_record_id_out, char **resource_id_out)
static int append_records_from_input_list_primitive_logic_local(yyjson_mut_doc *mut_doc, yyjson_mut_val *mut_root, yyjson_val *primitive, const InputFieldMapLogicLocal *inputs, ActionExecutionContextLogicLocal *context, long next_id, long *created_count_out, char **resource_id_out)
static char * input_field_value_dup_logic_local(const InputFieldMapLogicLocal *map, const char *field_id)
static int append_audit_event_logic_local(yyjson_mut_doc *mut_doc, yyjson_mut_val *mut_root, yyjson_val *transition, const char *resource_id, const char *created_at, const char *replay_trace_id, const char *report_json, const char *from_state, const char *to_state)
static void free_input_field_map_logic_local(InputFieldMapLogicLocal *map)
static char * app_manifest_path_dup_logic_local(const AppRuntime *app)
static char * request_form_value_dup_logic_local(const HttpRequest *request, const char *field_name)
static int persist_mut_doc_logic_local(const AppRuntime *app, yyjson_mut_doc *mut_doc)
static void free_action_execution_context_logic_local(ActionExecutionContextLogicLocal *context)
static int append_list_item_logic_local(char ***items_out, size_t *count_out, const char *start, size_t length)
static int append_bytes_logic_local(char **buffer, size_t *length, size_t *capacity, const char *chunk, size_t chunk_len)
static char * fixture_path_dup_logic_local(const AppRuntime *app, const char *fixture_key)
static int outbound_http_post_logic_local(const char *target_url, const char *content_type, const char *payload_json, OutboundHttpResultLogicLocal *result)
static int set_mut_object_field_logic_local(yyjson_mut_doc *doc, yyjson_mut_val *obj, const char *field_name, yyjson_mut_val *value)
static int record_field_matches_text_logic_local(yyjson_mut_val *record, const char *field_name, const char *expected_text)
static void free_parsed_http_url_logic_local(ParsedHttpUrlLogicLocal *url)
static int append_job_attempt_logic_local(yyjson_mut_doc *mut_doc, yyjson_mut_val *mut_root, long job_execution_id, const char *execution_status, const char *payload_json, const char *created_at, const char *error_message)
static int now_iso_utc_logic_local(char out[20])
static int split_input_list_logic_local(const char *raw_value, const char *delimiter, char ***items_out, size_t *count_out)
static const char * http_reason_phrase_logic_local(int status)
Resolves a minimal HTTP reason phrase for framework-owned responses.
static int send_json_action_result_logic_local(NativeConnection *connection, yyjson_val *response_contract, const char *action_id, const char *resource_id, long workflow_execution_id, const char *report_json, yyjson_mut_doc *mut_doc)
Sends a declarative JSON action-result response contract.
static void free_split_items_logic_local(char **items, size_t count)
static int replace_owned_string_logic_local(char **slot, const char *value)
static char * workflow_presentation_string_dup_logic_local(yyjson_val *transition, const char *field_name)
static int now_plus_seconds_iso_utc_logic_local(long offset_seconds, char out[20])
static int note_created_record_logic_local(ActionExecutionContextLogicLocal *context, long record_id, const char *resource_id)
static int append_workflow_and_effects_logic_local(const AppRuntime *app, yyjson_mut_doc *mut_doc, yyjson_mut_val *mut_root, yyjson_val *action_contract, yyjson_val *workflow, yyjson_val *transition, const InputFieldMapLogicLocal *inputs, const ActionExecutionContextLogicLocal *context, const char *policy_gate, const char *resource_id, const char *from_state, const char *to_state, const char *created_at, const char *replay_trace_id, long *workflow_execution_id_out, ActionEffectCountsLogicLocal *effect_counts_out)
static yyjson_val * find_workflow_transition_logic_local(yyjson_val *root, const char *action_id, yyjson_val **workflow_out)
static char * request_body_bytes_dup_logic_local(const HttpRequest *request, size_t *out_size)
static char * effect_payload_json_dup_logic_local(const char *action_id, const char *adapter_id, const char *resource_id, const char *replay_trace_id, const char *created_at)
static yyjson_mut_val * field_value_from_contract_logic_local(yyjson_mut_doc *doc, yyjson_val *value_contract, const InputFieldMapLogicLocal *inputs, long next_id, yyjson_val *record_context, const ActionExecutionContextLogicLocal *context, const char *list_item_text, yyjson_val *source_record_context)
static void write_json_error_logic_local(NativeConnection *connection, int status, const char *status_text, const char *error_kind, const char *message)
static int scoped_reassignment_primitive_logic_local(yyjson_mut_doc *mut_doc, yyjson_mut_val *mut_root, yyjson_val *source_root, yyjson_val *primitive, const InputFieldMapLogicLocal *inputs, ActionExecutionContextLogicLocal *context, char **resource_id_out)
static int record_field_matches_text_value_logic_local(yyjson_val *record, const char *field_name, const char *expected_text)
Shared declarative action, workflow, and effect helpers for dynamic-app business-logic scaffolding.
char * native_dynamic_multipart_extract_text(const unsigned char *body, size_t body_len, const char *content_type, const char *field_name)
Extracts a text value from a multipart form field.
Pharos-owned multipart form-data parsing for in-process dynamic handling.
char * native_dynamic_form_value(const char *body, const char *key)
Extracts a value from a URL-encoded form body.
Request field extraction helpers for in-process dynamic handling.
int write_text_file(const char *path, const char *content)
Writes a text buffer to a file path.
Declares native filesystem helper routines.
void native_send_text_response(NativeConnection *connection, int status, const char *status_text, const char *body)
Sends a plain-text HTTP response with explicit content length.
int native_connection_printf(NativeConnection *connection, const char *format,...)
Writes formatted bytes to a native connection.
Declares native HTTP response writing helpers.
char * path_join(const char *left, const char *right)
Joins two path segments using the native path separator.
void trim_in_place(char *text)
Trims leading and trailing ASCII whitespace from a string in place.
char * dup_range(const char *start, size_t len)
Duplicates a byte range into a newly allocated NUL-terminated string.
int streq(const char *a, const char *b)
Tests whether two strings are exactly equal.
char * read_file_text(const char *path)
Reads an entire file into a newly allocated text buffer.
Declares shared low-level support helpers used across the Pharos native runtime.
yyjson_doc * native_json_doc_load_file(const char *path)
Parses a JSON file into an owned yyjson document.
long native_json_obj_get_long_default(yyjson_val *obj, const char *key, long default_value, int *found_out)
Reads a numeric object member or returns a fallback value.
void native_json_doc_free(yyjson_doc *doc)
Releases a document returned by the native yyjson helpers.
yyjson_doc * native_json_doc_load_text(const char *text)
Parses JSON text into an owned yyjson document.
char * native_json_obj_get_string_dup(yyjson_val *obj, const char *key)
Duplicates a string-valued key from a JSON object.
yyjson_val * native_json_obj_get(yyjson_val *obj, const char *key)
Looks up a key from a JSON object value.
long matched_record_count
char * last_touched_resource_id
char * last_created_resource_id
char * anchor_resource_id
long last_created_record_id
long created_record_count
long last_touched_record_id
long skipped_record_count
long updated_record_count
Loaded runtime metadata for one Pharos app artifact.
Parsed HTTP request fields extracted from a native connection.
Accepted client connection and its resolved transport metadata.
void yyjson_mut_doc_free(yyjson_mut_doc *doc)
yyjson_mut_doc * yyjson_mut_doc_new(const yyjson_alc *alc)
yyjson_mut_doc * yyjson_doc_mut_copy(const yyjson_doc *doc, const yyjson_alc *alc)
yyjson_mut_val * yyjson_val_mut_copy(yyjson_mut_doc *m_doc, const yyjson_val *i_vals)
yyjson_api_inline yyjson_mut_val * yyjson_mut_obj_remove_key(yyjson_mut_val *obj, const char *key)
yyjson_api_inline bool yyjson_is_str(const yyjson_val *val)
yyjson_api_inline bool yyjson_is_obj(const yyjson_val *val)
#define yyjson_mut_arr_foreach(arr, idx, max, val)
yyjson_api_inline yyjson_mut_val * yyjson_mut_obj_get(const yyjson_mut_val *obj, const char *key)
yyjson_api_inline yyjson_val * yyjson_obj_iter_get_val(yyjson_val *key)
yyjson_api_inline int64_t yyjson_get_sint(const yyjson_val *val)
yyjson_api_inline bool yyjson_mut_obj_add_int(yyjson_mut_doc *doc, yyjson_mut_val *obj, const char *key, int64_t val)
yyjson_api_inline yyjson_mut_val * yyjson_mut_bool(yyjson_mut_doc *doc, bool val)
yyjson_api_inline yyjson_val * yyjson_obj_iter_next(yyjson_obj_iter *iter)
#define yyjson_arr_foreach(arr, idx, max, val)
yyjson_api_inline yyjson_val * yyjson_doc_get_root(const yyjson_doc *doc)
yyjson_api_inline bool yyjson_is_arr(const yyjson_val *val)
yyjson_api_inline yyjson_mut_val * yyjson_mut_obj(yyjson_mut_doc *doc)
yyjson_api_inline yyjson_mut_val * yyjson_mut_null(yyjson_mut_doc *doc)
yyjson_api_inline bool yyjson_obj_iter_init(const yyjson_val *obj, yyjson_obj_iter *iter)
yyjson_api_inline char * yyjson_mut_write(const yyjson_mut_doc *doc, yyjson_write_flag flg, size_t *len)
yyjson_api_inline void yyjson_mut_doc_set_root(yyjson_mut_doc *doc, yyjson_mut_val *root)
yyjson_api_inline yyjson_mut_val * yyjson_mut_int(yyjson_mut_doc *doc, int64_t num)
yyjson_api_inline yyjson_mut_val * yyjson_mut_strcpy(yyjson_mut_doc *doc, const char *str)
yyjson_api_inline bool yyjson_equals_str(const yyjson_val *val, const char *str)
yyjson_api_inline bool yyjson_mut_is_obj(const yyjson_mut_val *val)
yyjson_api_inline yyjson_mut_val * yyjson_mut_doc_get_root(yyjson_mut_doc *doc)
yyjson_api_inline yyjson_val * yyjson_arr_iter_next(yyjson_arr_iter *iter)
yyjson_api_inline bool yyjson_is_num(const yyjson_val *val)
yyjson_api_inline bool yyjson_is_null(const yyjson_val *val)
yyjson_api_inline bool yyjson_mut_obj_add_bool(yyjson_mut_doc *doc, yyjson_mut_val *obj, const char *key, bool val)
yyjson_api_inline bool yyjson_mut_obj_add_strcpy(yyjson_mut_doc *doc, yyjson_mut_val *obj, const char *key, const char *val)
yyjson_api_inline size_t yyjson_arr_size(const yyjson_val *arr)
yyjson_api_inline bool yyjson_mut_obj_put(yyjson_mut_val *obj, yyjson_mut_val *key, yyjson_mut_val *val)
yyjson_api_inline bool yyjson_mut_is_str(const yyjson_mut_val *val)
yyjson_api_inline yyjson_mut_val * yyjson_mut_sint(yyjson_mut_doc *doc, int64_t num)
static const yyjson_write_flag YYJSON_WRITE_NOFLAG
yyjson_api_inline const char * yyjson_get_str(const yyjson_val *val)
yyjson_api_inline bool yyjson_mut_is_arr(const yyjson_mut_val *val)
yyjson_api_inline yyjson_mut_val * yyjson_mut_arr(yyjson_mut_doc *doc)
yyjson_api_inline bool yyjson_mut_arr_append(yyjson_mut_val *arr, yyjson_mut_val *val)
yyjson_api_inline bool yyjson_arr_iter_init(const yyjson_val *arr, yyjson_arr_iter *iter)
yyjson_api_inline bool yyjson_get_bool(const yyjson_val *val)
yyjson_api_inline bool yyjson_is_bool(const yyjson_val *val)