qingflow-mcp 0.3.15 → 0.3.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/server.js +598 -106
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -63,7 +63,9 @@ const ADAPTIVE_TARGET_PAGE_MS = toPositiveInt(process.env.QINGFLOW_ADAPTIVE_TARG
|
|
|
63
63
|
const MAX_LIST_ITEMS_BYTES = toPositiveInt(process.env.QINGFLOW_LIST_MAX_ITEMS_BYTES) ?? 400000;
|
|
64
64
|
const REQUEST_TIMEOUT_MS = toPositiveInt(process.env.QINGFLOW_REQUEST_TIMEOUT_MS) ?? 18000;
|
|
65
65
|
const EXECUTION_BUDGET_MS = toPositiveInt(process.env.QINGFLOW_EXECUTION_BUDGET_MS) ?? 20000;
|
|
66
|
-
const
|
|
66
|
+
const WAIT_RESULT_DEFAULT_TIMEOUT_MS = toPositiveInt(process.env.QINGFLOW_WAIT_RESULT_TIMEOUT_MS) ?? 5000;
|
|
67
|
+
const WAIT_RESULT_POLL_INTERVAL_MS = toPositiveInt(process.env.QINGFLOW_WAIT_RESULT_POLL_INTERVAL_MS) ?? 500;
|
|
68
|
+
const SERVER_VERSION = "0.3.16";
|
|
67
69
|
const accessToken = process.env.QINGFLOW_ACCESS_TOKEN;
|
|
68
70
|
const baseUrl = process.env.QINGFLOW_BASE_URL;
|
|
69
71
|
if (!accessToken) {
|
|
@@ -174,6 +176,7 @@ const queryContractFields = {
|
|
|
174
176
|
output_profile: outputProfileSchema.optional(),
|
|
175
177
|
completeness: completenessSchema.optional(),
|
|
176
178
|
evidence: z.record(z.unknown()).optional(),
|
|
179
|
+
resolved_mappings: z.record(z.unknown()).optional(),
|
|
177
180
|
error_code: z.null().optional(),
|
|
178
181
|
fix_hint: z.null().optional(),
|
|
179
182
|
next_page_token: z.string().nullable().optional()
|
|
@@ -484,6 +487,8 @@ const createInputPublicSchema = z
|
|
|
484
487
|
app_key: publicStringSchema,
|
|
485
488
|
user_id: publicStringSchema.optional(),
|
|
486
489
|
force_refresh_form: z.boolean().optional(),
|
|
490
|
+
wait_result: z.boolean().optional(),
|
|
491
|
+
wait_timeout_ms: z.number().int().positive().max(20000).optional(),
|
|
487
492
|
apply_user: publicApplyUserSchema.optional(),
|
|
488
493
|
answers: z.array(publicAnswerInputSchema).optional(),
|
|
489
494
|
fields: z.record(z.unknown()).optional()
|
|
@@ -493,6 +498,8 @@ const createInputSchema = z
|
|
|
493
498
|
app_key: z.string().min(1),
|
|
494
499
|
user_id: z.string().min(1).optional(),
|
|
495
500
|
force_refresh_form: z.boolean().optional(),
|
|
501
|
+
wait_result: z.boolean().optional(),
|
|
502
|
+
wait_timeout_ms: z.number().int().positive().max(20000).optional(),
|
|
496
503
|
apply_user: z
|
|
497
504
|
.object({
|
|
498
505
|
email: z.string().optional(),
|
|
@@ -512,6 +519,9 @@ const createSuccessOutputSchema = z.object({
|
|
|
512
519
|
data: z.object({
|
|
513
520
|
request_id: z.string().nullable(),
|
|
514
521
|
apply_id: z.union([z.string(), z.number(), z.null()]),
|
|
522
|
+
resolved: z.boolean().optional(),
|
|
523
|
+
timed_out: z.boolean().optional(),
|
|
524
|
+
operation_result: z.unknown().optional(),
|
|
515
525
|
async_hint: z.string()
|
|
516
526
|
}),
|
|
517
527
|
meta: apiMetaSchema
|
|
@@ -523,6 +533,8 @@ const updateInputPublicSchema = z
|
|
|
523
533
|
app_key: publicStringSchema.optional(),
|
|
524
534
|
user_id: publicStringSchema.optional(),
|
|
525
535
|
force_refresh_form: z.boolean().optional(),
|
|
536
|
+
wait_result: z.boolean().optional(),
|
|
537
|
+
wait_timeout_ms: z.number().int().positive().max(20000).optional(),
|
|
526
538
|
answers: z.array(publicAnswerInputSchema).optional(),
|
|
527
539
|
fields: z.record(z.unknown()).optional()
|
|
528
540
|
});
|
|
@@ -532,6 +544,8 @@ const updateInputSchema = z
|
|
|
532
544
|
app_key: z.string().min(1).optional(),
|
|
533
545
|
user_id: z.string().min(1).optional(),
|
|
534
546
|
force_refresh_form: z.boolean().optional(),
|
|
547
|
+
wait_result: z.boolean().optional(),
|
|
548
|
+
wait_timeout_ms: z.number().int().positive().max(20000).optional(),
|
|
535
549
|
answers: z.array(answerInputSchema).optional(),
|
|
536
550
|
fields: z.record(fieldValueSchema).optional()
|
|
537
551
|
})
|
|
@@ -542,6 +556,10 @@ const updateSuccessOutputSchema = z.object({
|
|
|
542
556
|
ok: z.literal(true),
|
|
543
557
|
data: z.object({
|
|
544
558
|
request_id: z.string().nullable(),
|
|
559
|
+
apply_id: z.union([z.string(), z.number(), z.null()]).optional(),
|
|
560
|
+
resolved: z.boolean().optional(),
|
|
561
|
+
timed_out: z.boolean().optional(),
|
|
562
|
+
operation_result: z.unknown().optional(),
|
|
545
563
|
async_hint: z.string()
|
|
546
564
|
}),
|
|
547
565
|
meta: apiMetaSchema
|
|
@@ -592,7 +610,7 @@ const queryInputPublicSchema = z
|
|
|
592
610
|
max_rows: z.number().int().positive().max(200).optional(),
|
|
593
611
|
max_items: z.number().int().positive().max(200).optional(),
|
|
594
612
|
max_columns: z.number().int().positive().max(MAX_COLUMN_LIMIT).optional(),
|
|
595
|
-
select_columns: z.array(publicFieldSelectorSchema).min(1).max(MAX_COLUMN_LIMIT),
|
|
613
|
+
select_columns: z.array(publicFieldSelectorSchema).min(1).max(MAX_COLUMN_LIMIT).optional(),
|
|
596
614
|
include_answers: z.boolean().optional(),
|
|
597
615
|
amount_column: publicFieldSelectorSchema.optional(),
|
|
598
616
|
time_range: publicTimeRangeSchema.optional(),
|
|
@@ -694,6 +712,7 @@ const querySummaryOutputSchema = z.object({
|
|
|
694
712
|
rows: z.array(z.record(z.unknown())),
|
|
695
713
|
completeness: completenessSchema.optional(),
|
|
696
714
|
evidence: evidenceSchema.optional(),
|
|
715
|
+
resolved_mappings: z.record(z.unknown()).optional(),
|
|
697
716
|
meta: z.object({
|
|
698
717
|
field_mapping: z.array(z.object({
|
|
699
718
|
role: z.enum(["row", "amount", "time"]),
|
|
@@ -773,7 +792,7 @@ const aggregateInputPublicSchema = z
|
|
|
773
792
|
sort: z.array(publicSortItemSchema).optional(),
|
|
774
793
|
filters: z.array(publicFilterItemSchema).optional(),
|
|
775
794
|
time_range: publicTimeRangeSchema.optional(),
|
|
776
|
-
group_by: z.array(publicFieldSelectorSchema).
|
|
795
|
+
group_by: z.array(publicFieldSelectorSchema).max(20).optional(),
|
|
777
796
|
amount_column: publicFieldSelectorSchema.optional(),
|
|
778
797
|
amount_columns: z.array(publicFieldSelectorSchema).min(1).max(5).optional(),
|
|
779
798
|
metrics: z.array(z.enum(["count", "sum", "avg", "min", "max"])).min(1).max(5).optional(),
|
|
@@ -838,7 +857,7 @@ const aggregateInputSchema = z
|
|
|
838
857
|
timezone: z.string().optional()
|
|
839
858
|
})
|
|
840
859
|
.optional(),
|
|
841
|
-
group_by: z.array(z.union([z.string().min(1), z.number().int()])).
|
|
860
|
+
group_by: z.array(z.union([z.string().min(1), z.number().int()])).max(20).optional(),
|
|
842
861
|
amount_column: z.union([z.string().min(1), z.number().int()]).optional(),
|
|
843
862
|
amount_columns: z
|
|
844
863
|
.array(z.union([z.string().min(1), z.number().int()]))
|
|
@@ -1308,7 +1327,7 @@ server.registerTool("qf_field_resolve", {
|
|
|
1308
1327
|
});
|
|
1309
1328
|
server.registerTool("qf_query_plan", {
|
|
1310
1329
|
title: "Qingflow Query Plan",
|
|
1311
|
-
description: "
|
|
1330
|
+
description: "Debug/explain tool: preflight query arguments, validate required fields, resolve field mappings and estimate scan pages before actual execution. Use for complex queries or when troubleshooting. For normal queries, use qf_query directly.",
|
|
1312
1331
|
inputSchema: queryPlanInputPublicSchema,
|
|
1313
1332
|
outputSchema: queryPlanOutputSchema,
|
|
1314
1333
|
annotations: {
|
|
@@ -1422,7 +1441,7 @@ server.registerTool("qf_export_json", {
|
|
|
1422
1441
|
});
|
|
1423
1442
|
server.registerTool("qf_query", {
|
|
1424
1443
|
title: "Qingflow Unified Query",
|
|
1425
|
-
description: "Unified read entry for list/record/summary. Use query_mode=auto to route
|
|
1444
|
+
description: "Unified read entry for list/record/summary modes. Use query_mode=auto to route: apply_id→record; amount_column/time_range/stat_policy→summary; otherwise→list. In summary mode, select_columns is optional (auto-derived from amount_column/time_range). Field titles are accepted everywhere—que_id resolution is automatic.",
|
|
1426
1445
|
inputSchema: queryInputPublicSchema,
|
|
1427
1446
|
outputSchema: queryOutputSchema,
|
|
1428
1447
|
annotations: {
|
|
@@ -1474,6 +1493,7 @@ server.registerTool("qf_query", {
|
|
|
1474
1493
|
summary: executed.data
|
|
1475
1494
|
},
|
|
1476
1495
|
output_profile: executed.outputProfile,
|
|
1496
|
+
resolved_mappings: executed.resolvedMappings,
|
|
1477
1497
|
...(isVerboseProfile(executed.outputProfile)
|
|
1478
1498
|
? {
|
|
1479
1499
|
completeness,
|
|
@@ -1502,6 +1522,7 @@ server.registerTool("qf_query", {
|
|
|
1502
1522
|
list: executed.payload.data
|
|
1503
1523
|
},
|
|
1504
1524
|
output_profile: executed.outputProfile,
|
|
1525
|
+
resolved_mappings: executed.resolvedMappings,
|
|
1505
1526
|
...(isVerboseProfile(executed.outputProfile)
|
|
1506
1527
|
? {
|
|
1507
1528
|
completeness,
|
|
@@ -1524,7 +1545,7 @@ server.registerTool("qf_query", {
|
|
|
1524
1545
|
});
|
|
1525
1546
|
server.registerTool("qf_record_create", {
|
|
1526
1547
|
title: "Qingflow Record Create",
|
|
1527
|
-
description: "Create one record. Supports explicit answers
|
|
1548
|
+
description: "Create one record. Supports fields{} mapping by title or queId, and explicit answers[]. Set wait_result=true to poll until the record is resolved (returns apply_id directly instead of requiring a follow-up qf_operation_get call).",
|
|
1528
1549
|
inputSchema: createInputPublicSchema,
|
|
1529
1550
|
outputSchema: createOutputSchema,
|
|
1530
1551
|
annotations: {
|
|
@@ -1540,7 +1561,8 @@ server.registerTool("qf_record_create", {
|
|
|
1540
1561
|
const normalizedAnswers = resolveAnswers({
|
|
1541
1562
|
explicitAnswers: parsedArgs.answers,
|
|
1542
1563
|
fields: parsedArgs.fields,
|
|
1543
|
-
form: form?.result
|
|
1564
|
+
form: form?.result,
|
|
1565
|
+
tool: "qf_record_create"
|
|
1544
1566
|
});
|
|
1545
1567
|
const payload = {
|
|
1546
1568
|
answers: normalizedAnswers
|
|
@@ -1552,12 +1574,34 @@ server.registerTool("qf_record_create", {
|
|
|
1552
1574
|
userId: parsedArgs.user_id
|
|
1553
1575
|
});
|
|
1554
1576
|
const result = asObject(response.result);
|
|
1577
|
+
const requestId = asNullableString(result?.requestId);
|
|
1578
|
+
const immediateApplyId = result?.applyId ?? null;
|
|
1579
|
+
const shouldWaitForResult = (parsedArgs.wait_result ?? false) && requestId !== null && immediateApplyId === null;
|
|
1580
|
+
let finalApplyId = immediateApplyId;
|
|
1581
|
+
let isResolved = immediateApplyId !== null;
|
|
1582
|
+
let isTimedOut = false;
|
|
1583
|
+
let operationResult = null;
|
|
1584
|
+
if (shouldWaitForResult) {
|
|
1585
|
+
const waited = await waitForOperationResolution({
|
|
1586
|
+
requestId: requestId,
|
|
1587
|
+
timeoutMs: parsedArgs.wait_timeout_ms ?? WAIT_RESULT_DEFAULT_TIMEOUT_MS
|
|
1588
|
+
});
|
|
1589
|
+
isResolved = waited.resolved;
|
|
1590
|
+
isTimedOut = waited.timedOut;
|
|
1591
|
+
operationResult = waited.operationResult;
|
|
1592
|
+
finalApplyId = waited.applyId;
|
|
1593
|
+
}
|
|
1555
1594
|
return okResult({
|
|
1556
1595
|
ok: true,
|
|
1557
1596
|
data: {
|
|
1558
|
-
request_id:
|
|
1559
|
-
apply_id:
|
|
1560
|
-
|
|
1597
|
+
request_id: requestId,
|
|
1598
|
+
apply_id: finalApplyId,
|
|
1599
|
+
resolved: isResolved,
|
|
1600
|
+
...(isTimedOut ? { timed_out: true } : {}),
|
|
1601
|
+
...(operationResult !== null ? { operation_result: operationResult } : {}),
|
|
1602
|
+
async_hint: isResolved
|
|
1603
|
+
? "Record created and resolved."
|
|
1604
|
+
: "Use qf_operation_get with request_id to fetch the result."
|
|
1561
1605
|
},
|
|
1562
1606
|
meta: buildMeta(response)
|
|
1563
1607
|
}, `Create request sent for app ${parsedArgs.app_key}`);
|
|
@@ -1568,7 +1612,7 @@ server.registerTool("qf_record_create", {
|
|
|
1568
1612
|
});
|
|
1569
1613
|
server.registerTool("qf_record_update", {
|
|
1570
1614
|
title: "Qingflow Record Update",
|
|
1571
|
-
description: "Patch one record by applyId with explicit answers or ergonomic fields mapping.",
|
|
1615
|
+
description: "Patch one record by applyId with explicit answers or ergonomic fields mapping (title or queId). Set wait_result=true to poll until the update is confirmed instead of requiring a follow-up qf_operation_get call.",
|
|
1572
1616
|
inputSchema: updateInputPublicSchema,
|
|
1573
1617
|
outputSchema: updateOutputSchema,
|
|
1574
1618
|
annotations: {
|
|
@@ -1588,15 +1632,38 @@ server.registerTool("qf_record_update", {
|
|
|
1588
1632
|
const normalizedAnswers = resolveAnswers({
|
|
1589
1633
|
explicitAnswers: parsedArgs.answers,
|
|
1590
1634
|
fields: parsedArgs.fields,
|
|
1591
|
-
form: form?.result
|
|
1635
|
+
form: form?.result,
|
|
1636
|
+
tool: "qf_record_update"
|
|
1592
1637
|
});
|
|
1593
1638
|
const response = await client.updateRecord(String(parsedArgs.apply_id), { answers: normalizedAnswers }, { userId: parsedArgs.user_id });
|
|
1594
1639
|
const result = asObject(response.result);
|
|
1640
|
+
const updateRequestId = asNullableString(result?.requestId);
|
|
1641
|
+
const shouldWaitForUpdate = (parsedArgs.wait_result ?? false) && updateRequestId !== null;
|
|
1642
|
+
let updateIsResolved = false;
|
|
1643
|
+
let updateIsTimedOut = false;
|
|
1644
|
+
let updateOperationResult = null;
|
|
1645
|
+
let updateApplyId = null;
|
|
1646
|
+
if (shouldWaitForUpdate) {
|
|
1647
|
+
const waited = await waitForOperationResolution({
|
|
1648
|
+
requestId: updateRequestId,
|
|
1649
|
+
timeoutMs: parsedArgs.wait_timeout_ms ?? WAIT_RESULT_DEFAULT_TIMEOUT_MS
|
|
1650
|
+
});
|
|
1651
|
+
updateIsResolved = waited.resolved;
|
|
1652
|
+
updateIsTimedOut = waited.timedOut;
|
|
1653
|
+
updateOperationResult = waited.operationResult;
|
|
1654
|
+
updateApplyId = waited.applyId;
|
|
1655
|
+
}
|
|
1595
1656
|
return okResult({
|
|
1596
1657
|
ok: true,
|
|
1597
1658
|
data: {
|
|
1598
|
-
request_id:
|
|
1599
|
-
|
|
1659
|
+
request_id: updateRequestId,
|
|
1660
|
+
...(updateApplyId !== null ? { apply_id: updateApplyId } : {}),
|
|
1661
|
+
resolved: updateIsResolved,
|
|
1662
|
+
...(updateIsTimedOut ? { timed_out: true } : {}),
|
|
1663
|
+
...(updateOperationResult !== null ? { operation_result: updateOperationResult } : {}),
|
|
1664
|
+
async_hint: updateIsResolved
|
|
1665
|
+
? "Record updated and resolved."
|
|
1666
|
+
: "Use qf_operation_get with request_id to fetch the update result."
|
|
1600
1667
|
},
|
|
1601
1668
|
meta: buildMeta(response)
|
|
1602
1669
|
}, `Update request sent for apply ${String(parsedArgs.apply_id)}`);
|
|
@@ -1632,7 +1699,7 @@ server.registerTool("qf_operation_get", {
|
|
|
1632
1699
|
});
|
|
1633
1700
|
server.registerTool("qf_records_aggregate", {
|
|
1634
1701
|
title: "Qingflow Records Aggregate",
|
|
1635
|
-
description: "Aggregate records
|
|
1702
|
+
description: "Aggregate records with optional group_by columns and amount metrics. Omit group_by for total-only summary (count/sum/avg across all records). Field titles are resolved automatically. Designed for deterministic, auditable statistics.",
|
|
1636
1703
|
inputSchema: aggregateInputPublicSchema,
|
|
1637
1704
|
outputSchema: aggregateOutputSchema,
|
|
1638
1705
|
annotations: {
|
|
@@ -2068,7 +2135,8 @@ function buildToolSpecCatalog() {
|
|
|
2068
2135
|
required: ["tool"],
|
|
2069
2136
|
limits: {
|
|
2070
2137
|
tool: "qf_records_list|qf_record_get|qf_query|qf_records_aggregate|qf_records_batch_get|qf_export_csv|qf_export_json",
|
|
2071
|
-
input_contract: "strict JSON only; arguments must be a native JSON object"
|
|
2138
|
+
input_contract: "strict JSON only; arguments must be a native JSON object",
|
|
2139
|
+
usage_hint: "Debug/explain only. For normal queries use qf_query directly."
|
|
2072
2140
|
},
|
|
2073
2141
|
aliases: {},
|
|
2074
2142
|
minimal_example: {
|
|
@@ -2185,9 +2253,9 @@ function buildToolSpecCatalog() {
|
|
|
2185
2253
|
{
|
|
2186
2254
|
tool: "qf_query",
|
|
2187
2255
|
required: [
|
|
2188
|
-
"record mode: apply_id
|
|
2256
|
+
"record mode: apply_id (select_columns recommended)",
|
|
2189
2257
|
"list mode: app_key + select_columns",
|
|
2190
|
-
"summary mode: app_key
|
|
2258
|
+
"summary mode: app_key only (select_columns auto-derived from amount_column/time_range)"
|
|
2191
2259
|
],
|
|
2192
2260
|
limits: {
|
|
2193
2261
|
query_mode: "auto|list|record|summary",
|
|
@@ -2219,7 +2287,7 @@ function buildToolSpecCatalog() {
|
|
|
2219
2287
|
},
|
|
2220
2288
|
{
|
|
2221
2289
|
tool: "qf_records_aggregate",
|
|
2222
|
-
required: ["app_key"
|
|
2290
|
+
required: ["app_key"],
|
|
2223
2291
|
limits: {
|
|
2224
2292
|
page_size_max: 200,
|
|
2225
2293
|
requested_pages_max: 500,
|
|
@@ -2255,6 +2323,8 @@ function buildToolSpecCatalog() {
|
|
|
2255
2323
|
required: ["app_key", "answers or fields"],
|
|
2256
2324
|
limits: {
|
|
2257
2325
|
write_mode: "Provide either answers[] or fields{}",
|
|
2326
|
+
wait_result: "optional boolean; when true, polls qf_operation_get internally and returns resolved apply_id directly",
|
|
2327
|
+
wait_timeout_ms: "optional int (max 20000); default 5000ms",
|
|
2258
2328
|
input_contract: "strict JSON only; answers must be array and fields must be object"
|
|
2259
2329
|
},
|
|
2260
2330
|
aliases: {},
|
|
@@ -2271,6 +2341,8 @@ function buildToolSpecCatalog() {
|
|
|
2271
2341
|
required: ["apply_id", "answers or fields"],
|
|
2272
2342
|
limits: {
|
|
2273
2343
|
write_mode: "Provide either answers[] or fields{}",
|
|
2344
|
+
wait_result: "optional boolean; when true, polls qf_operation_get internally and returns resolved result directly",
|
|
2345
|
+
wait_timeout_ms: "optional int (max 20000); default 5000ms",
|
|
2274
2346
|
input_contract: "strict JSON only; answers must be array and fields must be object"
|
|
2275
2347
|
},
|
|
2276
2348
|
aliases: {},
|
|
@@ -3590,6 +3662,149 @@ function scoreFieldMatches(requested, fields, fuzzy, topK) {
|
|
|
3590
3662
|
}
|
|
3591
3663
|
return scored.sort((a, b) => b.score - a.score).slice(0, topK);
|
|
3592
3664
|
}
|
|
3665
|
+
function buildFieldCandidateList(fields) {
|
|
3666
|
+
return fields
|
|
3667
|
+
.filter((field) => field.queId !== undefined && field.queId !== null)
|
|
3668
|
+
.map((field) => ({
|
|
3669
|
+
que_id: normalizeQueId(field.queId),
|
|
3670
|
+
que_title: asNullableString(field.queTitle),
|
|
3671
|
+
que_type: field.queType
|
|
3672
|
+
}));
|
|
3673
|
+
}
|
|
3674
|
+
function buildFieldSuggestions(requested, index, topK = 3) {
|
|
3675
|
+
return scoreFieldMatches(requested, Array.from(index.byId.values()), true, topK);
|
|
3676
|
+
}
|
|
3677
|
+
function buildResolvedMappingEntry(params) {
|
|
3678
|
+
const field = params.field ?? null;
|
|
3679
|
+
return {
|
|
3680
|
+
requested: params.requested,
|
|
3681
|
+
resolved: params.resolved,
|
|
3682
|
+
que_id: field?.queId !== undefined && field?.queId !== null ? normalizeQueId(field.queId) : null,
|
|
3683
|
+
que_title: asNullableString(field?.queTitle),
|
|
3684
|
+
que_type: field?.queType ?? null,
|
|
3685
|
+
...(params.reason ? { reason: params.reason } : {}),
|
|
3686
|
+
...(params.auto_selected ? { auto_selected: true } : {})
|
|
3687
|
+
};
|
|
3688
|
+
}
|
|
3689
|
+
function resolveFieldSelectorStrict(params) {
|
|
3690
|
+
const requested = String(params.fieldKey ?? "").trim();
|
|
3691
|
+
if (!requested) {
|
|
3692
|
+
throw new InputValidationError({
|
|
3693
|
+
message: `${params.location} contains an empty field selector`,
|
|
3694
|
+
errorCode: "EMPTY_FIELD_SELECTOR",
|
|
3695
|
+
fixHint: "Pass a non-empty field title or que_id.",
|
|
3696
|
+
details: {
|
|
3697
|
+
tool: params.tool,
|
|
3698
|
+
location: params.location
|
|
3699
|
+
}
|
|
3700
|
+
});
|
|
3701
|
+
}
|
|
3702
|
+
let resolved = null;
|
|
3703
|
+
if (isNumericKey(requested)) {
|
|
3704
|
+
resolved = params.index.byId.get(String(Number(requested))) ?? null;
|
|
3705
|
+
if (!resolved) {
|
|
3706
|
+
throw new InputValidationError({
|
|
3707
|
+
message: `${params.location} references unknown que_id "${requested}"`,
|
|
3708
|
+
errorCode: "FIELD_NOT_FOUND",
|
|
3709
|
+
fixHint: "Use qf_form_get or qf_field_resolve to confirm the exact field que_id before retrying.",
|
|
3710
|
+
details: {
|
|
3711
|
+
tool: params.tool,
|
|
3712
|
+
location: params.location,
|
|
3713
|
+
requested,
|
|
3714
|
+
suggestions: buildFieldSuggestions(requested, params.index)
|
|
3715
|
+
}
|
|
3716
|
+
});
|
|
3717
|
+
}
|
|
3718
|
+
}
|
|
3719
|
+
else {
|
|
3720
|
+
const matches = params.index.byTitle.get(requested.toLowerCase()) ?? [];
|
|
3721
|
+
if (matches.length === 1) {
|
|
3722
|
+
resolved = matches[0];
|
|
3723
|
+
}
|
|
3724
|
+
else if (matches.length > 1) {
|
|
3725
|
+
throw new InputValidationError({
|
|
3726
|
+
message: `${params.location} field "${requested}" is ambiguous`,
|
|
3727
|
+
errorCode: "AMBIGUOUS_FIELD",
|
|
3728
|
+
fixHint: "Use numeric que_id, or call qf_field_resolve first to disambiguate the field title.",
|
|
3729
|
+
details: {
|
|
3730
|
+
tool: params.tool,
|
|
3731
|
+
location: params.location,
|
|
3732
|
+
requested,
|
|
3733
|
+
candidates: buildFieldCandidateList(matches)
|
|
3734
|
+
}
|
|
3735
|
+
});
|
|
3736
|
+
}
|
|
3737
|
+
else {
|
|
3738
|
+
throw new InputValidationError({
|
|
3739
|
+
message: `${params.location} cannot resolve field "${requested}"`,
|
|
3740
|
+
errorCode: "FIELD_NOT_FOUND",
|
|
3741
|
+
fixHint: "Use qf_form_get or qf_field_resolve to confirm the exact field title before retrying.",
|
|
3742
|
+
details: {
|
|
3743
|
+
tool: params.tool,
|
|
3744
|
+
location: params.location,
|
|
3745
|
+
requested,
|
|
3746
|
+
suggestions: buildFieldSuggestions(requested, params.index)
|
|
3747
|
+
}
|
|
3748
|
+
});
|
|
3749
|
+
}
|
|
3750
|
+
}
|
|
3751
|
+
if (params.expectDateType && !isDateLikeQueType(resolved.queType)) {
|
|
3752
|
+
throw new InputValidationError({
|
|
3753
|
+
message: `Field "${requested}" is not a date field and cannot be used in ${params.location}`,
|
|
3754
|
+
errorCode: "TIME_RANGE_FIELD_TYPE_MISMATCH",
|
|
3755
|
+
fixHint: "Use qf_form_get to pick a date field (queType=4), then retry time_range with that field.",
|
|
3756
|
+
details: {
|
|
3757
|
+
tool: params.tool,
|
|
3758
|
+
location: params.location,
|
|
3759
|
+
requested,
|
|
3760
|
+
resolved_field: buildResolvedMappingEntry({
|
|
3761
|
+
requested,
|
|
3762
|
+
field: resolved,
|
|
3763
|
+
resolved: true
|
|
3764
|
+
})
|
|
3765
|
+
}
|
|
3766
|
+
});
|
|
3767
|
+
}
|
|
3768
|
+
return resolved;
|
|
3769
|
+
}
|
|
3770
|
+
function resolveOutputColumn(column, index, label, tool) {
|
|
3771
|
+
const requested = String(column).trim();
|
|
3772
|
+
if (!requested) {
|
|
3773
|
+
throw new InputValidationError({
|
|
3774
|
+
message: `${label} contains an empty column selector`,
|
|
3775
|
+
errorCode: "EMPTY_FIELD_SELECTOR",
|
|
3776
|
+
fixHint: "Pass a non-empty field title or que_id.",
|
|
3777
|
+
details: {
|
|
3778
|
+
tool,
|
|
3779
|
+
location: label
|
|
3780
|
+
}
|
|
3781
|
+
});
|
|
3782
|
+
}
|
|
3783
|
+
if (isNumericKey(requested)) {
|
|
3784
|
+
const hit = index.byId.get(String(Number(requested)));
|
|
3785
|
+
return {
|
|
3786
|
+
requested,
|
|
3787
|
+
que_id: hit?.queId !== undefined && hit?.queId !== null ? normalizeQueId(hit.queId) : Number(requested),
|
|
3788
|
+
que_title: asNullableString(hit?.queTitle),
|
|
3789
|
+
que_type: hit?.queType ?? null
|
|
3790
|
+
};
|
|
3791
|
+
}
|
|
3792
|
+
const hit = resolveFieldSelectorStrict({
|
|
3793
|
+
fieldKey: requested,
|
|
3794
|
+
index,
|
|
3795
|
+
tool,
|
|
3796
|
+
location: label
|
|
3797
|
+
});
|
|
3798
|
+
return {
|
|
3799
|
+
requested,
|
|
3800
|
+
que_id: normalizeQueId(hit.queId),
|
|
3801
|
+
que_title: asNullableString(hit.queTitle),
|
|
3802
|
+
que_type: hit.queType
|
|
3803
|
+
};
|
|
3804
|
+
}
|
|
3805
|
+
function resolveOutputColumns(columns, index, label, tool) {
|
|
3806
|
+
return normalizeColumnSelectors(columns).map((requested) => resolveOutputColumn(requested, index, label, tool));
|
|
3807
|
+
}
|
|
3593
3808
|
function normalizedTextSimilarity(left, right) {
|
|
3594
3809
|
if (!left || !right) {
|
|
3595
3810
|
return 0;
|
|
@@ -4139,6 +4354,18 @@ async function executeRecordsExport(format, args) {
|
|
|
4139
4354
|
});
|
|
4140
4355
|
}
|
|
4141
4356
|
const outputProfile = resolveOutputProfile(args.output_profile);
|
|
4357
|
+
const form = await getFormCached(args.app_key, args.user_id, false);
|
|
4358
|
+
const index = buildFieldIndex(form.result);
|
|
4359
|
+
const selectResolution = resolveSelectColumnsWithIndex(args.select_columns, index, `qf_export_${format}`);
|
|
4360
|
+
const filterResolution = resolveFiltersWithIndex(args.filters, index, `qf_export_${format}`);
|
|
4361
|
+
const timeRangeResolution = resolveTimeRangeWithIndex(args.time_range, index, `qf_export_${format}`);
|
|
4362
|
+
const sortResolution = resolveSortWithIndex(args.sort, index, `qf_export_${format}`);
|
|
4363
|
+
const resolvedMappings = {
|
|
4364
|
+
select_columns: selectResolution.mappings,
|
|
4365
|
+
...(filterResolution.mappings.length > 0 ? { filters: filterResolution.mappings } : {}),
|
|
4366
|
+
...(sortResolution.mappings.length > 0 ? { sort: sortResolution.mappings } : {}),
|
|
4367
|
+
...(timeRangeResolution.mapping ? { time_range: timeRangeResolution.mapping } : {})
|
|
4368
|
+
};
|
|
4142
4369
|
const queryId = randomUUID();
|
|
4143
4370
|
const pageNum = resolveStartPage(args.page_num, args.page_token, args.app_key);
|
|
4144
4371
|
const requestedPages = args.requested_pages ?? EXPORT_DEFAULT_PAGES;
|
|
@@ -4146,14 +4373,12 @@ async function executeRecordsExport(format, args) {
|
|
|
4146
4373
|
const maxRows = Math.min(args.max_rows ?? EXPORT_MAX_ROWS, EXPORT_MAX_ROWS);
|
|
4147
4374
|
const startedAt = Date.now();
|
|
4148
4375
|
const adaptivePaging = createAdaptivePagingState(args.page_size ?? DEFAULT_PAGE_SIZE);
|
|
4149
|
-
const effectiveFilters = appendTimeRangeFilter(
|
|
4150
|
-
assertTimeRangeFilterApplied(`qf_export_${format}`,
|
|
4376
|
+
const effectiveFilters = appendTimeRangeFilter(filterResolution.filters, timeRangeResolution.time_range);
|
|
4377
|
+
assertTimeRangeFilterApplied(`qf_export_${format}`, timeRangeResolution.time_range, effectiveFilters);
|
|
4151
4378
|
if (hasDateLikeRangeFilters(effectiveFilters)) {
|
|
4152
|
-
const form = await getFormCached(args.app_key, args.user_id, false);
|
|
4153
|
-
const index = buildFieldIndex(form.result);
|
|
4154
4379
|
validateDateRangeFilters(effectiveFilters, index, `qf_export_${format}`);
|
|
4155
4380
|
}
|
|
4156
|
-
const normalizedSort =
|
|
4381
|
+
const normalizedSort = sortResolution.sort;
|
|
4157
4382
|
let currentPage = pageNum;
|
|
4158
4383
|
let fetchedPages = 0;
|
|
4159
4384
|
let hasMore = false;
|
|
@@ -4220,27 +4445,29 @@ async function executeRecordsExport(format, args) {
|
|
|
4220
4445
|
throw new Error(`Failed to export ${format}: empty response`);
|
|
4221
4446
|
}
|
|
4222
4447
|
const normalizedItems = rawItems.map((raw) => normalizeRecordItem(raw, true));
|
|
4448
|
+
const requestedSelectColumns = selectResolution.columns.map((item) => item.requested);
|
|
4223
4449
|
const projection = projectRecordItemsColumns({
|
|
4224
4450
|
items: normalizedItems,
|
|
4225
4451
|
includeAnswers: true,
|
|
4226
4452
|
maxColumns: args.max_columns,
|
|
4227
|
-
selectColumns:
|
|
4453
|
+
selectColumns: requestedSelectColumns
|
|
4228
4454
|
});
|
|
4229
4455
|
if (normalizedItems.length > 0 && projection.matchedAnswersCount === 0) {
|
|
4230
4456
|
throw new InputValidationError({
|
|
4231
|
-
message: `No answers matched select_columns (${
|
|
4457
|
+
message: `No answers matched select_columns (${requestedSelectColumns
|
|
4232
4458
|
.map((item) => String(item))
|
|
4233
4459
|
.join(", ")}).`,
|
|
4234
4460
|
errorCode: "COLUMN_SELECTOR_NOT_FOUND",
|
|
4235
|
-
fixHint: "Use qf_form_get to confirm
|
|
4461
|
+
fixHint: "Use qf_form_get or qf_field_resolve to confirm the exact field title/que_id before retrying.",
|
|
4236
4462
|
details: {
|
|
4237
|
-
select_columns:
|
|
4463
|
+
select_columns: requestedSelectColumns,
|
|
4464
|
+
resolved_mappings: selectResolution.mappings
|
|
4238
4465
|
}
|
|
4239
4466
|
});
|
|
4240
4467
|
}
|
|
4241
4468
|
const selectedColumnsForRows = args.max_columns !== undefined
|
|
4242
|
-
?
|
|
4243
|
-
:
|
|
4469
|
+
? requestedSelectColumns.slice(0, args.max_columns)
|
|
4470
|
+
: requestedSelectColumns;
|
|
4244
4471
|
const rows = buildFlatRowsFromItems({
|
|
4245
4472
|
items: normalizedItems,
|
|
4246
4473
|
selectedColumns: selectedColumnsForRows
|
|
@@ -4320,6 +4547,7 @@ async function executeRecordsExport(format, args) {
|
|
|
4320
4547
|
? {
|
|
4321
4548
|
completeness,
|
|
4322
4549
|
evidence,
|
|
4550
|
+
resolved_mappings: resolvedMappings,
|
|
4323
4551
|
execution: {
|
|
4324
4552
|
scanned_pages: fetchedPages,
|
|
4325
4553
|
requested_pages: requestedPages,
|
|
@@ -4347,7 +4575,8 @@ async function executeRecordsExport(format, args) {
|
|
|
4347
4575
|
};
|
|
4348
4576
|
return {
|
|
4349
4577
|
payload,
|
|
4350
|
-
message: `Exported ${rows.length} rows to ${filePath}
|
|
4578
|
+
message: `Exported ${rows.length} rows to ${filePath}`,
|
|
4579
|
+
resolvedMappings
|
|
4351
4580
|
};
|
|
4352
4581
|
}
|
|
4353
4582
|
async function executeRecordsList(args) {
|
|
@@ -4366,20 +4595,30 @@ async function executeRecordsList(args) {
|
|
|
4366
4595
|
});
|
|
4367
4596
|
}
|
|
4368
4597
|
const outputProfile = resolveOutputProfile(args.output_profile);
|
|
4598
|
+
const form = await getFormCached(args.app_key, args.user_id, false);
|
|
4599
|
+
const index = buildFieldIndex(form.result);
|
|
4600
|
+
const selectResolution = resolveSelectColumnsWithIndex(args.select_columns, index, "qf_records_list");
|
|
4601
|
+
const filterResolution = resolveFiltersWithIndex(args.filters, index, "qf_records_list");
|
|
4602
|
+
const timeRangeResolution = resolveTimeRangeWithIndex(args.time_range, index, "qf_records_list");
|
|
4603
|
+
const sortResolution = resolveSortWithIndex(args.sort, index, "qf_records_list");
|
|
4604
|
+
const resolvedMappings = {
|
|
4605
|
+
select_columns: selectResolution.mappings,
|
|
4606
|
+
...(filterResolution.mappings.length > 0 ? { filters: filterResolution.mappings } : {}),
|
|
4607
|
+
...(sortResolution.mappings.length > 0 ? { sort: sortResolution.mappings } : {}),
|
|
4608
|
+
...(timeRangeResolution.mapping ? { time_range: timeRangeResolution.mapping } : {})
|
|
4609
|
+
};
|
|
4369
4610
|
const queryId = randomUUID();
|
|
4370
4611
|
const pageNum = resolveStartPage(args.page_num, args.page_token, args.app_key);
|
|
4371
4612
|
const pageSize = args.page_size ?? DEFAULT_PAGE_SIZE;
|
|
4372
4613
|
const adaptivePaging = createAdaptivePagingState(pageSize);
|
|
4373
4614
|
const requestedPages = args.requested_pages ?? 1;
|
|
4374
4615
|
const scanMaxPages = args.scan_max_pages ?? requestedPages;
|
|
4375
|
-
const effectiveFilters = appendTimeRangeFilter(
|
|
4376
|
-
assertTimeRangeFilterApplied("qf_records_list",
|
|
4616
|
+
const effectiveFilters = appendTimeRangeFilter(filterResolution.filters, timeRangeResolution.time_range);
|
|
4617
|
+
assertTimeRangeFilterApplied("qf_records_list", timeRangeResolution.time_range, effectiveFilters);
|
|
4377
4618
|
if (hasDateLikeRangeFilters(effectiveFilters)) {
|
|
4378
|
-
const form = await getFormCached(args.app_key, args.user_id, false);
|
|
4379
|
-
const index = buildFieldIndex(form.result);
|
|
4380
4619
|
validateDateRangeFilters(effectiveFilters, index, "qf_records_list");
|
|
4381
4620
|
}
|
|
4382
|
-
const normalizedSort =
|
|
4621
|
+
const normalizedSort = sortResolution.sort;
|
|
4383
4622
|
const includeAnswers = true;
|
|
4384
4623
|
const startedAt = Date.now();
|
|
4385
4624
|
let currentPage = pageNum;
|
|
@@ -4451,27 +4690,29 @@ async function executeRecordsList(args) {
|
|
|
4451
4690
|
.slice(0, listLimit.limit)
|
|
4452
4691
|
.map((raw) => normalizeRecordItem(raw, includeAnswers));
|
|
4453
4692
|
const sourceItemsForRows = items.slice();
|
|
4693
|
+
const requestedSelectColumns = selectResolution.columns.map((item) => item.requested);
|
|
4454
4694
|
const columnProjection = projectRecordItemsColumns({
|
|
4455
4695
|
items,
|
|
4456
4696
|
includeAnswers,
|
|
4457
4697
|
maxColumns: args.max_columns,
|
|
4458
|
-
selectColumns:
|
|
4698
|
+
selectColumns: requestedSelectColumns
|
|
4459
4699
|
});
|
|
4460
4700
|
if (items.length > 0 && columnProjection.matchedAnswersCount === 0) {
|
|
4461
4701
|
throw new InputValidationError({
|
|
4462
|
-
message: `No answers matched select_columns (${
|
|
4702
|
+
message: `No answers matched select_columns (${requestedSelectColumns
|
|
4463
4703
|
.map((item) => String(item))
|
|
4464
4704
|
.join(", ")}).`,
|
|
4465
4705
|
errorCode: "COLUMN_SELECTOR_NOT_FOUND",
|
|
4466
|
-
fixHint: "Use qf_form_get to confirm
|
|
4706
|
+
fixHint: "Use qf_form_get or qf_field_resolve to confirm the exact field title/que_id before retrying.",
|
|
4467
4707
|
details: {
|
|
4468
|
-
select_columns:
|
|
4708
|
+
select_columns: requestedSelectColumns,
|
|
4709
|
+
resolved_mappings: selectResolution.mappings
|
|
4469
4710
|
}
|
|
4470
4711
|
});
|
|
4471
4712
|
}
|
|
4472
4713
|
const selectedColumnsForRows = args.max_columns !== undefined
|
|
4473
|
-
?
|
|
4474
|
-
:
|
|
4714
|
+
? requestedSelectColumns.slice(0, args.max_columns)
|
|
4715
|
+
: requestedSelectColumns;
|
|
4475
4716
|
const rows = buildFlatRowsFromItems({
|
|
4476
4717
|
items: sourceItemsForRows,
|
|
4477
4718
|
selectedColumns: selectedColumnsForRows
|
|
@@ -4509,14 +4750,14 @@ async function executeRecordsList(args) {
|
|
|
4509
4750
|
const listState = {
|
|
4510
4751
|
query_id: queryId,
|
|
4511
4752
|
app_key: args.app_key,
|
|
4512
|
-
selected_columns:
|
|
4753
|
+
selected_columns: requestedSelectColumns,
|
|
4513
4754
|
filters: echoFilters(effectiveFilters),
|
|
4514
|
-
time_range:
|
|
4755
|
+
time_range: timeRangeResolution.mapping
|
|
4515
4756
|
? {
|
|
4516
|
-
column: String(args.time_range.
|
|
4517
|
-
from:
|
|
4518
|
-
to:
|
|
4519
|
-
timezone:
|
|
4757
|
+
column: String(args.time_range?.column ?? timeRangeResolution.mapping.requested ?? ""),
|
|
4758
|
+
from: timeRangeResolution.time_range?.from ?? null,
|
|
4759
|
+
to: timeRangeResolution.time_range?.to ?? null,
|
|
4760
|
+
timezone: timeRangeResolution.time_range?.timezone ?? null
|
|
4520
4761
|
}
|
|
4521
4762
|
: null
|
|
4522
4763
|
};
|
|
@@ -4553,6 +4794,7 @@ async function executeRecordsList(args) {
|
|
|
4553
4794
|
: {})
|
|
4554
4795
|
},
|
|
4555
4796
|
output_profile: outputProfile,
|
|
4797
|
+
resolved_mappings: resolvedMappings,
|
|
4556
4798
|
...(isVerboseProfile(outputProfile)
|
|
4557
4799
|
? {
|
|
4558
4800
|
completeness,
|
|
@@ -4577,7 +4819,8 @@ async function executeRecordsList(args) {
|
|
|
4577
4819
|
}),
|
|
4578
4820
|
completeness,
|
|
4579
4821
|
evidence,
|
|
4580
|
-
outputProfile
|
|
4822
|
+
outputProfile,
|
|
4823
|
+
resolvedMappings
|
|
4581
4824
|
};
|
|
4582
4825
|
}
|
|
4583
4826
|
async function executeRecordGet(args) {
|
|
@@ -4772,13 +5015,6 @@ async function executeRecordsSummary(args) {
|
|
|
4772
5015
|
fixHint: "Provide app_key, for example: {\"query_mode\":\"summary\",\"app_key\":\"21b3d559\",...}"
|
|
4773
5016
|
});
|
|
4774
5017
|
}
|
|
4775
|
-
if (!args.select_columns?.length) {
|
|
4776
|
-
throw missingRequiredFieldError({
|
|
4777
|
-
field: "select_columns",
|
|
4778
|
-
tool: "qf_query(summary)",
|
|
4779
|
-
fixHint: "Provide select_columns as an array (<=2), for example: {\"select_columns\":[\"客户全称\"]}"
|
|
4780
|
-
});
|
|
4781
|
-
}
|
|
4782
5018
|
const outputProfile = resolveOutputProfile(args.output_profile);
|
|
4783
5019
|
const strictFull = args.strict_full ?? true;
|
|
4784
5020
|
const includeNegative = args.stat_policy?.include_negative ?? true;
|
|
@@ -4793,7 +5029,10 @@ async function executeRecordsSummary(args) {
|
|
|
4793
5029
|
const timezone = args.time_range?.timezone ?? "Asia/Shanghai";
|
|
4794
5030
|
const form = await getFormCached(args.app_key, args.user_id, false);
|
|
4795
5031
|
const index = buildFieldIndex(form.result);
|
|
4796
|
-
const
|
|
5032
|
+
const selectResolution = args.select_columns && args.select_columns.length > 0
|
|
5033
|
+
? resolveSelectColumnsWithIndex(args.select_columns, index, "qf_query(summary)")
|
|
5034
|
+
: buildDefaultSummarySelectColumns(args, index);
|
|
5035
|
+
const selectedColumns = selectResolution.columns;
|
|
4797
5036
|
const effectiveColumns = args.max_columns !== undefined ? selectedColumns.slice(0, args.max_columns) : selectedColumns;
|
|
4798
5037
|
if (effectiveColumns.length === 0) {
|
|
4799
5038
|
throw new Error("No output columns remain after max_columns cap");
|
|
@@ -4801,14 +5040,28 @@ async function executeRecordsSummary(args) {
|
|
|
4801
5040
|
const amountColumn = args.amount_column !== undefined
|
|
4802
5041
|
? resolveSummaryColumn(args.amount_column, index, "amount_column")
|
|
4803
5042
|
: null;
|
|
4804
|
-
const
|
|
4805
|
-
const
|
|
4806
|
-
|
|
4807
|
-
|
|
5043
|
+
const timeRangeResolution = resolveTimeRangeWithIndex(args.time_range, index, "qf_query(summary)");
|
|
5044
|
+
const timeColumn = timeRangeResolution.time_range
|
|
5045
|
+
? resolveSummaryColumn(timeRangeResolution.time_range.column, index, "time_range.column")
|
|
5046
|
+
: null;
|
|
5047
|
+
const filterResolution = resolveFiltersWithIndex(args.filters, index, "qf_query(summary)");
|
|
5048
|
+
const sortResolution = resolveSortWithIndex(args.sort, index, "qf_query(summary)");
|
|
5049
|
+
const resolvedMappings = {
|
|
5050
|
+
select_columns: selectResolution.mappings,
|
|
5051
|
+
...(amountColumn
|
|
5052
|
+
? { amount_column: buildResolvedMappingFromSummaryColumn(amountColumn) }
|
|
5053
|
+
: {}),
|
|
5054
|
+
...(filterResolution.mappings.length > 0 ? { filters: filterResolution.mappings } : {}),
|
|
5055
|
+
...(sortResolution.mappings.length > 0 ? { sort: sortResolution.mappings } : {}),
|
|
5056
|
+
...(timeRangeResolution.mapping ? { time_range: timeRangeResolution.mapping } : {})
|
|
5057
|
+
};
|
|
5058
|
+
const normalizedSort = sortResolution.sort;
|
|
5059
|
+
const summaryFilters = [...(filterResolution.filters ?? [])];
|
|
5060
|
+
if (timeColumn && (timeRangeResolution.time_range?.from || timeRangeResolution.time_range?.to)) {
|
|
4808
5061
|
summaryFilters.push({
|
|
4809
5062
|
que_id: timeColumn.que_id,
|
|
4810
|
-
...(
|
|
4811
|
-
...(
|
|
5063
|
+
...(timeRangeResolution.time_range.from ? { min_value: timeRangeResolution.time_range.from } : {}),
|
|
5064
|
+
...(timeRangeResolution.time_range.to ? { max_value: timeRangeResolution.time_range.to } : {})
|
|
4812
5065
|
});
|
|
4813
5066
|
}
|
|
4814
5067
|
validateDateRangeFilters(summaryFilters, index, "qf_query(summary)");
|
|
@@ -4824,7 +5077,7 @@ async function executeRecordsSummary(args) {
|
|
|
4824
5077
|
select_columns: effectiveColumns,
|
|
4825
5078
|
amount_column: amountColumn,
|
|
4826
5079
|
time_column: timeColumn,
|
|
4827
|
-
time_range:
|
|
5080
|
+
time_range: timeRangeResolution.time_range,
|
|
4828
5081
|
stat_policy: {
|
|
4829
5082
|
include_negative: includeNegative,
|
|
4830
5083
|
include_null: includeNull
|
|
@@ -4841,8 +5094,8 @@ async function executeRecordsSummary(args) {
|
|
|
4841
5094
|
time_range: timeColumn
|
|
4842
5095
|
? {
|
|
4843
5096
|
column: timeColumn.requested,
|
|
4844
|
-
from:
|
|
4845
|
-
to:
|
|
5097
|
+
from: timeRangeResolution.time_range?.from ?? null,
|
|
5098
|
+
to: timeRangeResolution.time_range?.to ?? null,
|
|
4846
5099
|
timezone
|
|
4847
5100
|
}
|
|
4848
5101
|
: null
|
|
@@ -5061,6 +5314,7 @@ async function executeRecordsSummary(args) {
|
|
|
5061
5314
|
},
|
|
5062
5315
|
rows,
|
|
5063
5316
|
completeness,
|
|
5317
|
+
resolved_mappings: resolvedMappings,
|
|
5064
5318
|
...(isVerboseProfile(outputProfile)
|
|
5065
5319
|
? {
|
|
5066
5320
|
evidence,
|
|
@@ -5071,8 +5325,8 @@ async function executeRecordsSummary(args) {
|
|
|
5071
5325
|
time_range: timeColumn
|
|
5072
5326
|
? {
|
|
5073
5327
|
column: timeColumn.requested,
|
|
5074
|
-
from:
|
|
5075
|
-
to:
|
|
5328
|
+
from: timeRangeResolution.time_range?.from ?? null,
|
|
5329
|
+
to: timeRangeResolution.time_range?.to ?? null,
|
|
5076
5330
|
timezone
|
|
5077
5331
|
}
|
|
5078
5332
|
: null
|
|
@@ -5099,7 +5353,8 @@ async function executeRecordsSummary(args) {
|
|
|
5099
5353
|
: `Summarized ${scannedRecords}/${knownResultAmount} records (partial)`,
|
|
5100
5354
|
completeness,
|
|
5101
5355
|
evidence,
|
|
5102
|
-
outputProfile
|
|
5356
|
+
outputProfile,
|
|
5357
|
+
resolvedMappings
|
|
5103
5358
|
};
|
|
5104
5359
|
}
|
|
5105
5360
|
async function executeRecordsAggregate(args) {
|
|
@@ -5118,7 +5373,12 @@ async function executeRecordsAggregate(args) {
|
|
|
5118
5373
|
const timeBucket = args.time_bucket ?? null;
|
|
5119
5374
|
const form = await getFormCached(args.app_key, args.user_id, false);
|
|
5120
5375
|
const index = buildFieldIndex(form.result);
|
|
5121
|
-
const
|
|
5376
|
+
const filterResolution = resolveFiltersWithIndex(args.filters, index, "qf_records_aggregate");
|
|
5377
|
+
const sortResolution = resolveSortWithIndex(args.sort, index, "qf_records_aggregate");
|
|
5378
|
+
const timeRangeResolution = resolveTimeRangeWithIndex(args.time_range, index, "qf_records_aggregate");
|
|
5379
|
+
const groupColumns = args.group_by && args.group_by.length > 0
|
|
5380
|
+
? resolveSummaryColumns(args.group_by, index, "group_by")
|
|
5381
|
+
: [];
|
|
5122
5382
|
const amountSelectors = args.amount_columns && args.amount_columns.length > 0
|
|
5123
5383
|
? args.amount_columns
|
|
5124
5384
|
: args.amount_column !== undefined
|
|
@@ -5129,14 +5389,25 @@ async function executeRecordsAggregate(args) {
|
|
|
5129
5389
|
: [];
|
|
5130
5390
|
const primaryAmountColumn = amountColumns[0] ?? null;
|
|
5131
5391
|
const metrics = resolveAggregateMetrics(args.metrics, amountColumns.length > 0);
|
|
5132
|
-
const timeColumn =
|
|
5133
|
-
|
|
5134
|
-
|
|
5135
|
-
|
|
5392
|
+
const timeColumn = timeRangeResolution.time_range
|
|
5393
|
+
? resolveSummaryColumn(timeRangeResolution.time_range.column, index, "time_range.column")
|
|
5394
|
+
: null;
|
|
5395
|
+
const resolvedMappings = {
|
|
5396
|
+
group_by: groupColumns.map((item) => buildResolvedMappingFromSummaryColumn(item)),
|
|
5397
|
+
...(amountColumns.length > 0
|
|
5398
|
+
? { amount_columns: amountColumns.map((item) => buildResolvedMappingFromSummaryColumn(item)) }
|
|
5399
|
+
: {}),
|
|
5400
|
+
...(filterResolution.mappings.length > 0 ? { filters: filterResolution.mappings } : {}),
|
|
5401
|
+
...(sortResolution.mappings.length > 0 ? { sort: sortResolution.mappings } : {}),
|
|
5402
|
+
...(timeRangeResolution.mapping ? { time_range: timeRangeResolution.mapping } : {})
|
|
5403
|
+
};
|
|
5404
|
+
const normalizedSort = sortResolution.sort;
|
|
5405
|
+
const aggregateFilters = [...(filterResolution.filters ?? [])];
|
|
5406
|
+
if (timeColumn && (timeRangeResolution.time_range?.from || timeRangeResolution.time_range?.to)) {
|
|
5136
5407
|
aggregateFilters.push({
|
|
5137
5408
|
que_id: timeColumn.que_id,
|
|
5138
|
-
...(
|
|
5139
|
-
...(
|
|
5409
|
+
...(timeRangeResolution.time_range.from ? { min_value: timeRangeResolution.time_range.from } : {}),
|
|
5410
|
+
...(timeRangeResolution.time_range.to ? { max_value: timeRangeResolution.time_range.to } : {})
|
|
5140
5411
|
});
|
|
5141
5412
|
}
|
|
5142
5413
|
validateDateRangeFilters(aggregateFilters, index, "qf_records_aggregate");
|
|
@@ -5153,7 +5424,7 @@ async function executeRecordsAggregate(args) {
|
|
|
5153
5424
|
amount_columns: amountColumns,
|
|
5154
5425
|
metrics,
|
|
5155
5426
|
time_column: timeColumn,
|
|
5156
|
-
time_range:
|
|
5427
|
+
time_range: timeRangeResolution.time_range,
|
|
5157
5428
|
time_bucket: timeBucket,
|
|
5158
5429
|
stat_policy: {
|
|
5159
5430
|
include_negative: includeNegative,
|
|
@@ -5174,8 +5445,8 @@ async function executeRecordsAggregate(args) {
|
|
|
5174
5445
|
time_range: timeColumn
|
|
5175
5446
|
? {
|
|
5176
5447
|
column: timeColumn.requested,
|
|
5177
|
-
from:
|
|
5178
|
-
to:
|
|
5448
|
+
from: timeRangeResolution.time_range?.from ?? null,
|
|
5449
|
+
to: timeRangeResolution.time_range?.to ?? null,
|
|
5179
5450
|
timezone
|
|
5180
5451
|
}
|
|
5181
5452
|
: null
|
|
@@ -5441,6 +5712,7 @@ async function executeRecordsAggregate(args) {
|
|
|
5441
5712
|
: {})
|
|
5442
5713
|
},
|
|
5443
5714
|
output_profile: outputProfile,
|
|
5715
|
+
resolved_mappings: resolvedMappings,
|
|
5444
5716
|
...(isVerboseProfile(outputProfile)
|
|
5445
5717
|
? {
|
|
5446
5718
|
completeness,
|
|
@@ -5466,25 +5738,12 @@ function resolveSummaryColumns(columns, index, label) {
|
|
|
5466
5738
|
}
|
|
5467
5739
|
function resolveSummaryColumn(column, index, label) {
|
|
5468
5740
|
const requested = String(column).trim();
|
|
5469
|
-
|
|
5470
|
-
|
|
5471
|
-
|
|
5472
|
-
|
|
5473
|
-
|
|
5474
|
-
|
|
5475
|
-
throw new Error(`${label} references unknown que_id "${requested}"`);
|
|
5476
|
-
}
|
|
5477
|
-
return {
|
|
5478
|
-
requested,
|
|
5479
|
-
que_id: normalizeQueId(hit.queId),
|
|
5480
|
-
que_title: asNullableString(hit.queTitle),
|
|
5481
|
-
que_type: hit.queType
|
|
5482
|
-
};
|
|
5483
|
-
}
|
|
5484
|
-
const hit = resolveFieldByKey(requested, index);
|
|
5485
|
-
if (!hit || hit.queId === undefined || hit.queId === null) {
|
|
5486
|
-
throw new Error(`${label} cannot resolve field "${requested}"`);
|
|
5487
|
-
}
|
|
5741
|
+
const hit = resolveFieldSelectorStrict({
|
|
5742
|
+
fieldKey: requested,
|
|
5743
|
+
index,
|
|
5744
|
+
tool: label.startsWith("group_by") ? "qf_records_aggregate" : "qf_query",
|
|
5745
|
+
location: label
|
|
5746
|
+
});
|
|
5488
5747
|
return {
|
|
5489
5748
|
requested,
|
|
5490
5749
|
que_id: normalizeQueId(hit.queId),
|
|
@@ -5762,7 +6021,7 @@ function normalizeRecordItem(raw, includeAnswers) {
|
|
|
5762
6021
|
return normalized;
|
|
5763
6022
|
}
|
|
5764
6023
|
function resolveAnswers(params) {
|
|
5765
|
-
const normalizedFromFields = resolveFieldAnswers(params.fields, params.form);
|
|
6024
|
+
const normalizedFromFields = resolveFieldAnswers(params.fields, params.form, params.tool);
|
|
5766
6025
|
const normalizedExplicit = normalizeExplicitAnswers(params.explicitAnswers);
|
|
5767
6026
|
const merged = new Map();
|
|
5768
6027
|
for (const answer of normalizedFromFields) {
|
|
@@ -5812,7 +6071,7 @@ function normalizeExplicitAnswers(answers) {
|
|
|
5812
6071
|
}
|
|
5813
6072
|
return output;
|
|
5814
6073
|
}
|
|
5815
|
-
function resolveFieldAnswers(fields, form) {
|
|
6074
|
+
function resolveFieldAnswers(fields, form, tool = "qf_record_create") {
|
|
5816
6075
|
const entries = Object.entries(fields ?? {});
|
|
5817
6076
|
if (entries.length === 0) {
|
|
5818
6077
|
return [];
|
|
@@ -5820,9 +6079,30 @@ function resolveFieldAnswers(fields, form) {
|
|
|
5820
6079
|
const index = buildFieldIndex(form);
|
|
5821
6080
|
const answers = [];
|
|
5822
6081
|
for (const [fieldKey, fieldValue] of entries) {
|
|
5823
|
-
|
|
6082
|
+
let field;
|
|
6083
|
+
if (isNumericKey(fieldKey)) {
|
|
6084
|
+
field = resolveFieldByKey(fieldKey, index);
|
|
6085
|
+
}
|
|
6086
|
+
else {
|
|
6087
|
+
field = resolveFieldSelectorStrict({
|
|
6088
|
+
fieldKey,
|
|
6089
|
+
index,
|
|
6090
|
+
tool,
|
|
6091
|
+
location: `fields.${fieldKey}`
|
|
6092
|
+
});
|
|
6093
|
+
}
|
|
5824
6094
|
if (!field) {
|
|
5825
|
-
throw new
|
|
6095
|
+
throw new InputValidationError({
|
|
6096
|
+
message: `Cannot resolve field key "${fieldKey}" from form metadata`,
|
|
6097
|
+
errorCode: "FIELD_NOT_FOUND",
|
|
6098
|
+
fixHint: "Use qf_form_get or qf_field_resolve to confirm the exact field title before retrying.",
|
|
6099
|
+
details: {
|
|
6100
|
+
tool,
|
|
6101
|
+
location: `fields.${fieldKey}`,
|
|
6102
|
+
requested: fieldKey,
|
|
6103
|
+
suggestions: buildFieldSuggestions(fieldKey, index)
|
|
6104
|
+
}
|
|
6105
|
+
});
|
|
5826
6106
|
}
|
|
5827
6107
|
answers.push(makeAnswerFromField(field, fieldValue));
|
|
5828
6108
|
}
|
|
@@ -5971,15 +6251,227 @@ async function normalizeListSort(sort, appKey, userId) {
|
|
|
5971
6251
|
const index = buildFieldIndex(form.result);
|
|
5972
6252
|
return sort.map((item) => {
|
|
5973
6253
|
const rawKey = String(item.que_id).trim();
|
|
5974
|
-
const resolved =
|
|
5975
|
-
|
|
5976
|
-
|
|
6254
|
+
const resolved = resolveFieldSelectorStrict({
|
|
6255
|
+
fieldKey: rawKey,
|
|
6256
|
+
index,
|
|
6257
|
+
tool: "qf_records_list",
|
|
6258
|
+
location: "sort[].que_id"
|
|
6259
|
+
});
|
|
6260
|
+
return {
|
|
6261
|
+
que_id: normalizeQueId(resolved.queId),
|
|
6262
|
+
...(item.ascend !== undefined ? { ascend: item.ascend } : {})
|
|
6263
|
+
};
|
|
6264
|
+
});
|
|
6265
|
+
}
|
|
6266
|
+
function buildResolvedMappingFromSummaryColumn(column, extra = {}) {
|
|
6267
|
+
return {
|
|
6268
|
+
requested: column.requested,
|
|
6269
|
+
resolved: true,
|
|
6270
|
+
que_id: column.que_id,
|
|
6271
|
+
que_title: column.que_title,
|
|
6272
|
+
que_type: column.que_type ?? null,
|
|
6273
|
+
...extra
|
|
6274
|
+
};
|
|
6275
|
+
}
|
|
6276
|
+
function resolveFiltersWithIndex(filters, index, tool) {
|
|
6277
|
+
if (!filters?.length) {
|
|
6278
|
+
return {
|
|
6279
|
+
filters,
|
|
6280
|
+
mappings: []
|
|
6281
|
+
};
|
|
6282
|
+
}
|
|
6283
|
+
const mappings = [];
|
|
6284
|
+
const resolvedFilters = filters.map((filter, indexInArray) => {
|
|
6285
|
+
if (filter.que_id === undefined || filter.que_id === null) {
|
|
6286
|
+
return filter;
|
|
5977
6287
|
}
|
|
6288
|
+
const resolved = resolveFieldSelectorStrict({
|
|
6289
|
+
fieldKey: filter.que_id,
|
|
6290
|
+
index,
|
|
6291
|
+
tool,
|
|
6292
|
+
location: `filters[${indexInArray}].que_id`
|
|
6293
|
+
});
|
|
6294
|
+
mappings.push(buildResolvedMappingEntry({
|
|
6295
|
+
requested: String(filter.que_id),
|
|
6296
|
+
field: resolved,
|
|
6297
|
+
resolved: true
|
|
6298
|
+
}));
|
|
6299
|
+
return {
|
|
6300
|
+
...filter,
|
|
6301
|
+
que_id: normalizeQueId(resolved.queId)
|
|
6302
|
+
};
|
|
6303
|
+
});
|
|
6304
|
+
return {
|
|
6305
|
+
filters: resolvedFilters,
|
|
6306
|
+
mappings
|
|
6307
|
+
};
|
|
6308
|
+
}
|
|
6309
|
+
function resolveSortWithIndex(sort, index, tool) {
|
|
6310
|
+
if (!sort?.length) {
|
|
6311
|
+
return {
|
|
6312
|
+
sort,
|
|
6313
|
+
mappings: []
|
|
6314
|
+
};
|
|
6315
|
+
}
|
|
6316
|
+
const mappings = [];
|
|
6317
|
+
const resolvedSort = sort.map((item, indexInArray) => {
|
|
6318
|
+
const resolved = resolveFieldSelectorStrict({
|
|
6319
|
+
fieldKey: item.que_id,
|
|
6320
|
+
index,
|
|
6321
|
+
tool,
|
|
6322
|
+
location: `sort[${indexInArray}].que_id`
|
|
6323
|
+
});
|
|
6324
|
+
mappings.push(buildResolvedMappingEntry({
|
|
6325
|
+
requested: String(item.que_id),
|
|
6326
|
+
field: resolved,
|
|
6327
|
+
resolved: true
|
|
6328
|
+
}));
|
|
5978
6329
|
return {
|
|
5979
6330
|
que_id: normalizeQueId(resolved.queId),
|
|
5980
6331
|
...(item.ascend !== undefined ? { ascend: item.ascend } : {})
|
|
5981
6332
|
};
|
|
5982
6333
|
});
|
|
6334
|
+
return {
|
|
6335
|
+
sort: resolvedSort,
|
|
6336
|
+
mappings
|
|
6337
|
+
};
|
|
6338
|
+
}
|
|
6339
|
+
function resolveTimeRangeWithIndex(timeRange, index, tool) {
|
|
6340
|
+
if (!timeRange) {
|
|
6341
|
+
return {
|
|
6342
|
+
time_range: undefined,
|
|
6343
|
+
mapping: null
|
|
6344
|
+
};
|
|
6345
|
+
}
|
|
6346
|
+
const resolved = resolveFieldSelectorStrict({
|
|
6347
|
+
fieldKey: timeRange.column,
|
|
6348
|
+
index,
|
|
6349
|
+
tool,
|
|
6350
|
+
location: "time_range.column",
|
|
6351
|
+
expectDateType: true
|
|
6352
|
+
});
|
|
6353
|
+
return {
|
|
6354
|
+
time_range: {
|
|
6355
|
+
...timeRange,
|
|
6356
|
+
column: normalizeQueId(resolved.queId)
|
|
6357
|
+
},
|
|
6358
|
+
mapping: buildResolvedMappingEntry({
|
|
6359
|
+
requested: String(timeRange.column),
|
|
6360
|
+
field: resolved,
|
|
6361
|
+
resolved: true
|
|
6362
|
+
})
|
|
6363
|
+
};
|
|
6364
|
+
}
|
|
6365
|
+
function resolveSelectColumnsWithIndex(selectColumns, index, tool) {
|
|
6366
|
+
const columns = resolveOutputColumns(selectColumns, index, "select_columns", tool);
|
|
6367
|
+
return {
|
|
6368
|
+
columns,
|
|
6369
|
+
mappings: columns.map((column) => buildResolvedMappingFromSummaryColumn(column))
|
|
6370
|
+
};
|
|
6371
|
+
}
|
|
6372
|
+
function buildDefaultSummarySelectColumns(args, index) {
|
|
6373
|
+
const candidates = [];
|
|
6374
|
+
if (args.time_range?.column !== undefined) {
|
|
6375
|
+
candidates.push({
|
|
6376
|
+
selector: args.time_range.column,
|
|
6377
|
+
reason: "auto-selected from time_range.column"
|
|
6378
|
+
});
|
|
6379
|
+
}
|
|
6380
|
+
if (args.amount_column !== undefined) {
|
|
6381
|
+
candidates.push({
|
|
6382
|
+
selector: args.amount_column,
|
|
6383
|
+
reason: "auto-selected from amount_column"
|
|
6384
|
+
});
|
|
6385
|
+
}
|
|
6386
|
+
const idZero = index.byId.get("0");
|
|
6387
|
+
if (idZero?.queId !== undefined && idZero.queId !== null) {
|
|
6388
|
+
candidates.push({
|
|
6389
|
+
selector: normalizeQueId(idZero.queId),
|
|
6390
|
+
reason: "auto-selected default row preview column"
|
|
6391
|
+
});
|
|
6392
|
+
}
|
|
6393
|
+
else {
|
|
6394
|
+
const firstBusinessField = Array.from(index.byId.values()).find((field) => {
|
|
6395
|
+
const normalized = field.queId !== undefined && field.queId !== null ? normalizeQueId(field.queId) : null;
|
|
6396
|
+
return typeof normalized === "number" ? normalized > 0 : Boolean(normalized);
|
|
6397
|
+
});
|
|
6398
|
+
if (firstBusinessField?.queId !== undefined && firstBusinessField.queId !== null) {
|
|
6399
|
+
candidates.push({
|
|
6400
|
+
selector: normalizeQueId(firstBusinessField.queId),
|
|
6401
|
+
reason: "auto-selected first business field for row preview"
|
|
6402
|
+
});
|
|
6403
|
+
}
|
|
6404
|
+
}
|
|
6405
|
+
const deduped = new Set();
|
|
6406
|
+
const selectedCandidates = [];
|
|
6407
|
+
for (const candidate of candidates) {
|
|
6408
|
+
const key = normalizeColumnSelector(candidate.selector);
|
|
6409
|
+
if (deduped.has(key)) {
|
|
6410
|
+
continue;
|
|
6411
|
+
}
|
|
6412
|
+
deduped.add(key);
|
|
6413
|
+
selectedCandidates.push(candidate);
|
|
6414
|
+
if (selectedCandidates.length >= MAX_COLUMN_LIMIT) {
|
|
6415
|
+
break;
|
|
6416
|
+
}
|
|
6417
|
+
}
|
|
6418
|
+
const columns = selectedCandidates.map((candidate) => resolveOutputColumn(candidate.selector, index, "select_columns", "qf_query(summary)"));
|
|
6419
|
+
return {
|
|
6420
|
+
columns,
|
|
6421
|
+
mappings: columns.map((column, indexInArray) => buildResolvedMappingFromSummaryColumn(column, {
|
|
6422
|
+
auto_selected: true,
|
|
6423
|
+
reason: selectedCandidates[indexInArray]?.reason ?? "auto-selected"
|
|
6424
|
+
}))
|
|
6425
|
+
};
|
|
6426
|
+
}
|
|
6427
|
+
function delay(ms) {
|
|
6428
|
+
return new Promise((resolve) => {
|
|
6429
|
+
setTimeout(resolve, ms);
|
|
6430
|
+
});
|
|
6431
|
+
}
|
|
6432
|
+
function extractOperationStatus(operationResult) {
|
|
6433
|
+
const obj = asObject(operationResult);
|
|
6434
|
+
const rawStatus = asNullableString(obj?.status ?? obj?.operationStatus ?? obj?.operation_status ?? obj?.resultStatus ?? null);
|
|
6435
|
+
return rawStatus ? rawStatus.trim().toUpperCase() : null;
|
|
6436
|
+
}
|
|
6437
|
+
function isPendingOperationStatus(status) {
|
|
6438
|
+
if (!status) {
|
|
6439
|
+
return false;
|
|
6440
|
+
}
|
|
6441
|
+
return ["PENDING", "PROCESSING", "RUNNING", "IN_PROGRESS", "QUEUED"].includes(status);
|
|
6442
|
+
}
|
|
6443
|
+
function extractOperationApplyId(operationResult) {
|
|
6444
|
+
const obj = asObject(operationResult);
|
|
6445
|
+
return obj?.applyId ?? obj?.apply_id ?? null;
|
|
6446
|
+
}
|
|
6447
|
+
async function waitForOperationResolution(params) {
|
|
6448
|
+
const deadline = Date.now() + Math.max(1, params.timeoutMs);
|
|
6449
|
+
let lastResult = null;
|
|
6450
|
+
while (Date.now() <= deadline) {
|
|
6451
|
+
const response = await client.getOperation(params.requestId);
|
|
6452
|
+
lastResult = response.result;
|
|
6453
|
+
const status = extractOperationStatus(lastResult);
|
|
6454
|
+
const applyId = extractOperationApplyId(lastResult);
|
|
6455
|
+
if ((status && !isPendingOperationStatus(status)) || applyId !== null) {
|
|
6456
|
+
return {
|
|
6457
|
+
resolved: true,
|
|
6458
|
+
timedOut: false,
|
|
6459
|
+
operationResult: lastResult,
|
|
6460
|
+
applyId
|
|
6461
|
+
};
|
|
6462
|
+
}
|
|
6463
|
+
const remaining = deadline - Date.now();
|
|
6464
|
+
if (remaining <= 0) {
|
|
6465
|
+
break;
|
|
6466
|
+
}
|
|
6467
|
+
await delay(Math.min(WAIT_RESULT_POLL_INTERVAL_MS, remaining));
|
|
6468
|
+
}
|
|
6469
|
+
return {
|
|
6470
|
+
resolved: false,
|
|
6471
|
+
timedOut: true,
|
|
6472
|
+
operationResult: lastResult,
|
|
6473
|
+
applyId: extractOperationApplyId(lastResult)
|
|
6474
|
+
};
|
|
5983
6475
|
}
|
|
5984
6476
|
function resolveListItemLimit(params) {
|
|
5985
6477
|
if (params.total <= 0) {
|