@zapier/kitcore 0.6.0 → 0.7.0
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/CHANGELOG.md +14 -0
- package/dist/index.cjs +199 -112
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +134 -34
- package/dist/index.d.ts +134 -34
- package/dist/index.mjs +199 -112
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -202,7 +202,8 @@ function getCoreErrorCause(value) {
|
|
|
202
202
|
var CURSOR_VERSION = 1;
|
|
203
203
|
var CURSOR_SOURCE = {
|
|
204
204
|
API: "api",
|
|
205
|
-
SDK: "sdk"
|
|
205
|
+
SDK: "sdk",
|
|
206
|
+
CONCAT: "concat"
|
|
206
207
|
};
|
|
207
208
|
function encodeBase64(str) {
|
|
208
209
|
return btoa(
|
|
@@ -422,10 +423,32 @@ async function* paginateBuffered(pageFunction, pageOptions) {
|
|
|
422
423
|
}
|
|
423
424
|
}
|
|
424
425
|
var paginate = paginateBuffered;
|
|
426
|
+
function encodeConcatCursor(index, cursor) {
|
|
427
|
+
const envelope = {
|
|
428
|
+
v: CURSOR_VERSION,
|
|
429
|
+
source: CURSOR_SOURCE.CONCAT,
|
|
430
|
+
index,
|
|
431
|
+
cursor
|
|
432
|
+
};
|
|
433
|
+
return encodeBase64(JSON.stringify(envelope));
|
|
434
|
+
}
|
|
435
|
+
function decodeConcatCursor(incoming) {
|
|
436
|
+
if (!incoming) {
|
|
437
|
+
return { index: 0, cursor: void 0 };
|
|
438
|
+
}
|
|
439
|
+
try {
|
|
440
|
+
const envelope = JSON.parse(decodeBase64(incoming));
|
|
441
|
+
if (envelope.v === CURSOR_VERSION && envelope.source === CURSOR_SOURCE.CONCAT && typeof envelope.index === "number") {
|
|
442
|
+
return { index: envelope.index, cursor: envelope.cursor };
|
|
443
|
+
}
|
|
444
|
+
} catch {
|
|
445
|
+
}
|
|
446
|
+
return { index: 0, cursor: incoming };
|
|
447
|
+
}
|
|
425
448
|
function concatPaginated({
|
|
426
449
|
sources,
|
|
427
|
-
|
|
428
|
-
|
|
450
|
+
pageSize = 100,
|
|
451
|
+
cursor
|
|
429
452
|
}) {
|
|
430
453
|
if (sources.length === 0) {
|
|
431
454
|
const empty = { data: [] };
|
|
@@ -435,40 +458,24 @@ function concatPaginated({
|
|
|
435
458
|
}
|
|
436
459
|
});
|
|
437
460
|
}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
if (!
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
}
|
|
447
|
-
const next = await currentIterator.next();
|
|
448
|
-
if (next.done) {
|
|
449
|
-
sourceIndex++;
|
|
450
|
-
currentIterator = null;
|
|
461
|
+
const pageFunction = async (options) => {
|
|
462
|
+
let { index, cursor: sourceCursor } = decodeConcatCursor(options.cursor);
|
|
463
|
+
while (index < sources.length) {
|
|
464
|
+
const page = await sources[index]({ cursor: sourceCursor });
|
|
465
|
+
const hasMoreInSource = page.nextCursor != null;
|
|
466
|
+
if (page.data.length === 0 && !hasMoreInSource) {
|
|
467
|
+
index++;
|
|
468
|
+
sourceCursor = void 0;
|
|
451
469
|
continue;
|
|
452
470
|
}
|
|
453
|
-
let items = next.value.data;
|
|
454
|
-
if (dedupe) {
|
|
455
|
-
if (sourceIndex > 0) {
|
|
456
|
-
items = items.filter((item) => !seen.has(dedupe(item)));
|
|
457
|
-
}
|
|
458
|
-
for (const item of items) {
|
|
459
|
-
seen.add(dedupe(item));
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
const hasMoreInSource = next.value.nextCursor != null;
|
|
463
|
-
const hasMoreSources = sourceIndex < sources.length - 1;
|
|
464
471
|
return {
|
|
465
|
-
data:
|
|
466
|
-
nextCursor: hasMoreInSource
|
|
472
|
+
data: page.data,
|
|
473
|
+
nextCursor: hasMoreInSource ? encodeConcatCursor(index, page.nextCursor) : index < sources.length - 1 ? encodeConcatCursor(index + 1, void 0) : void 0
|
|
467
474
|
};
|
|
468
475
|
}
|
|
469
476
|
return { data: [] };
|
|
470
477
|
};
|
|
471
|
-
const iterator = paginateBuffered(pageFunction, { pageSize });
|
|
478
|
+
const iterator = paginateBuffered(pageFunction, { pageSize, cursor });
|
|
472
479
|
const firstPagePromise = iterator.next().then((result) => {
|
|
473
480
|
if (result.done) {
|
|
474
481
|
return { data: [] };
|
|
@@ -1351,6 +1358,7 @@ function defineResolver(config) {
|
|
|
1351
1358
|
getContext: config.getContext,
|
|
1352
1359
|
listItems: config.listItems,
|
|
1353
1360
|
prompt: config.prompt,
|
|
1361
|
+
validate: config.validate,
|
|
1354
1362
|
tryResolveWithoutPrompt: config.tryResolveWithoutPrompt,
|
|
1355
1363
|
tryResolveFromSearch: config.tryResolveFromSearch
|
|
1356
1364
|
};
|
|
@@ -2009,6 +2017,7 @@ function bindResolver(resolver, plugins) {
|
|
|
2009
2017
|
const {
|
|
2010
2018
|
getContext: getContext2,
|
|
2011
2019
|
listItems,
|
|
2020
|
+
validate,
|
|
2012
2021
|
tryResolveWithoutPrompt,
|
|
2013
2022
|
tryResolveFromSearch
|
|
2014
2023
|
} = resolver;
|
|
@@ -2022,6 +2031,9 @@ function bindResolver(resolver, plugins) {
|
|
|
2022
2031
|
};
|
|
2023
2032
|
if (getContext2)
|
|
2024
2033
|
bound.getContext = ({ input }) => getContext2({ imports, input });
|
|
2034
|
+
if (validate) {
|
|
2035
|
+
bound.validate = ({ value, input, context }) => validate({ imports, value, input, context });
|
|
2036
|
+
}
|
|
2025
2037
|
if (tryResolveWithoutPrompt) {
|
|
2026
2038
|
bound.tryResolveWithoutPrompt = ({ input }) => tryResolveWithoutPrompt({ imports, input });
|
|
2027
2039
|
}
|
|
@@ -2701,14 +2713,14 @@ function coerce(leaf, raw) {
|
|
|
2701
2713
|
}
|
|
2702
2714
|
async function validationError(leaf, value, state) {
|
|
2703
2715
|
if (leaf.resolver?.type !== "dynamic") return null;
|
|
2716
|
+
const validate = leaf.resolver.validate;
|
|
2717
|
+
if (!validate) return null;
|
|
2704
2718
|
const context = await resolveContext(leaf, state.resolved);
|
|
2705
|
-
const
|
|
2706
|
-
|
|
2719
|
+
const verdict = await validate({
|
|
2720
|
+
value,
|
|
2707
2721
|
input: mergeInput(state.resolved, leaf.extraInput),
|
|
2708
2722
|
context
|
|
2709
2723
|
});
|
|
2710
|
-
if (!config?.validate) return null;
|
|
2711
|
-
const verdict = config.validate(value);
|
|
2712
2724
|
if (verdict === true) return null;
|
|
2713
2725
|
return typeof verdict === "string" ? verdict : `${leaf.name}: invalid value`;
|
|
2714
2726
|
}
|
|
@@ -2834,20 +2846,36 @@ async function resolveContext(leaf, input) {
|
|
|
2834
2846
|
input: mergeInput(input, leaf.extraInput)
|
|
2835
2847
|
});
|
|
2836
2848
|
}
|
|
2837
|
-
|
|
2849
|
+
function firstPagePosition(opts = {}) {
|
|
2850
|
+
return {
|
|
2851
|
+
...opts.search !== void 0 ? { search: opts.search } : {},
|
|
2852
|
+
pageCursor: null,
|
|
2853
|
+
previousCursors: [],
|
|
2854
|
+
generation: opts.generation ?? 0
|
|
2855
|
+
};
|
|
2856
|
+
}
|
|
2857
|
+
async function fetchListing(leaf, input, position, context) {
|
|
2858
|
+
if (leaf.resolver?.type === "dynamic" && leaf.resolver.inputType === "search" && position.search === void 0) {
|
|
2859
|
+
return { position, items: [] };
|
|
2860
|
+
}
|
|
2838
2861
|
const page = await firstPage(
|
|
2839
2862
|
leaf.resolver?.type === "dynamic" ? leaf.resolver.listItems({
|
|
2840
2863
|
input: mergeInput(input, leaf.extraInput),
|
|
2841
|
-
context
|
|
2842
|
-
search:
|
|
2843
|
-
cursor:
|
|
2864
|
+
context,
|
|
2865
|
+
search: position.search,
|
|
2866
|
+
cursor: position.pageCursor ?? void 0
|
|
2844
2867
|
}) : void 0
|
|
2845
2868
|
);
|
|
2846
2869
|
return {
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2870
|
+
position,
|
|
2871
|
+
items: page.data,
|
|
2872
|
+
...page.nextCursor != null ? { nextCursor: page.nextCursor } : {}
|
|
2873
|
+
};
|
|
2874
|
+
}
|
|
2875
|
+
function toPagination(page) {
|
|
2876
|
+
return {
|
|
2877
|
+
position: page.position,
|
|
2878
|
+
...page.nextCursor !== void 0 ? { nextCursor: page.nextCursor } : {}
|
|
2851
2879
|
};
|
|
2852
2880
|
}
|
|
2853
2881
|
|
|
@@ -2869,23 +2897,32 @@ var AFFORDANCE = {
|
|
|
2869
2897
|
description: "Filter the options by a search term",
|
|
2870
2898
|
supply: "term"
|
|
2871
2899
|
},
|
|
2872
|
-
|
|
2900
|
+
nextPage: {
|
|
2901
|
+
action: "next_page",
|
|
2902
|
+
description: "Fetch the next page of options"
|
|
2903
|
+
},
|
|
2904
|
+
previousPage: {
|
|
2905
|
+
action: "previous_page",
|
|
2906
|
+
description: "Return to the previous page of options"
|
|
2907
|
+
},
|
|
2873
2908
|
skip: { action: "skip", description: "Omit this optional parameter" },
|
|
2874
2909
|
add: { action: "add", description: "Add another item" },
|
|
2875
2910
|
done: { action: "done", description: "Finish the list" },
|
|
2876
2911
|
retry: { action: "retry", description: "Retry loading the options" },
|
|
2877
2912
|
cancel: { action: "cancel", description: "Cancel resolution" }
|
|
2878
2913
|
};
|
|
2879
|
-
function selectActions(leaf,
|
|
2914
|
+
function selectActions(leaf, page, multiple) {
|
|
2880
2915
|
const searchMode = leaf.resolver?.type === "dynamic" && leaf.resolver.inputType === "search";
|
|
2881
|
-
if (searchMode &&
|
|
2916
|
+
if (searchMode && page.position.search === void 0 && page.items.length === 0) {
|
|
2882
2917
|
const actions2 = multiple ? [AFFORDANCE.search] : [AFFORDANCE.search, AFFORDANCE.custom];
|
|
2883
2918
|
if (!leaf.required) actions2.push(AFFORDANCE.skip);
|
|
2884
2919
|
return actions2;
|
|
2885
2920
|
}
|
|
2886
2921
|
const actions = multiple ? [AFFORDANCE.choose] : [AFFORDANCE.choose, AFFORDANCE.custom];
|
|
2887
2922
|
if (searchMode) actions.push(AFFORDANCE.search);
|
|
2888
|
-
if (
|
|
2923
|
+
if (page.nextCursor) actions.push(AFFORDANCE.nextPage);
|
|
2924
|
+
if (page.position.previousCursors.length > 0)
|
|
2925
|
+
actions.push(AFFORDANCE.previousPage);
|
|
2889
2926
|
if (!leaf.required) actions.push(AFFORDANCE.skip);
|
|
2890
2927
|
return actions;
|
|
2891
2928
|
}
|
|
@@ -2893,16 +2930,17 @@ function labeledMessage(leaf) {
|
|
|
2893
2930
|
if (!leaf.label) return void 0;
|
|
2894
2931
|
return `${leaf.label} (${leaf.required ? "required" : "optional"}):`;
|
|
2895
2932
|
}
|
|
2896
|
-
function selectQuestion(leaf, input,
|
|
2933
|
+
function selectQuestion(leaf, path, input, page, context) {
|
|
2897
2934
|
const resolver = leaf.resolver?.type === "dynamic" ? leaf.resolver : void 0;
|
|
2898
2935
|
const config = resolver?.prompt?.({
|
|
2899
|
-
items:
|
|
2936
|
+
items: page.items,
|
|
2900
2937
|
input: mergeInput(input, leaf.extraInput),
|
|
2901
2938
|
context
|
|
2902
2939
|
});
|
|
2903
2940
|
const multiple = config?.type === "checkbox";
|
|
2904
2941
|
return {
|
|
2905
2942
|
type: "select",
|
|
2943
|
+
path,
|
|
2906
2944
|
// A labeled field's title beats the resolver's message: per-field
|
|
2907
2945
|
// resolvers are shared across fields (one choices-fetcher for every
|
|
2908
2946
|
// field), so only the leaf knows which field is being asked.
|
|
@@ -2910,9 +2948,13 @@ function selectQuestion(leaf, input, listing, context) {
|
|
|
2910
2948
|
choices: (config?.choices ?? []).map(toChoice),
|
|
2911
2949
|
...multiple ? { multiple: true } : {},
|
|
2912
2950
|
...config?.notes?.length ? { notes: config.notes } : {},
|
|
2913
|
-
...
|
|
2951
|
+
...page.position.search !== void 0 ? { search: page.position.search } : {},
|
|
2914
2952
|
...resolver?.placeholder ? { placeholder: resolver.placeholder } : {},
|
|
2915
|
-
|
|
2953
|
+
page: {
|
|
2954
|
+
generation: page.position.generation,
|
|
2955
|
+
index: page.position.previousCursors.length
|
|
2956
|
+
},
|
|
2957
|
+
actions: selectActions(leaf, page, multiple)
|
|
2916
2958
|
};
|
|
2917
2959
|
}
|
|
2918
2960
|
function toControllerError(error) {
|
|
@@ -2934,6 +2976,7 @@ function failedResult(state, name, error) {
|
|
|
2934
2976
|
error: toControllerError(error),
|
|
2935
2977
|
question: {
|
|
2936
2978
|
type: "select",
|
|
2979
|
+
path: state.current ?? [],
|
|
2937
2980
|
message: `Could not load options for ${name}.`,
|
|
2938
2981
|
choices: [],
|
|
2939
2982
|
actions: [AFFORDANCE.retry, AFFORDANCE.cancel]
|
|
@@ -2941,27 +2984,15 @@ function failedResult(state, name, error) {
|
|
|
2941
2984
|
}
|
|
2942
2985
|
};
|
|
2943
2986
|
}
|
|
2944
|
-
async function buildQuestion(leaf, input) {
|
|
2987
|
+
async function buildQuestion(leaf, path, input) {
|
|
2945
2988
|
const optional = !leaf.required;
|
|
2946
2989
|
const resolver = leaf.resolver;
|
|
2947
2990
|
if (resolver?.type === "dynamic") {
|
|
2948
2991
|
const context = await resolveContext(leaf, input);
|
|
2949
|
-
|
|
2950
|
-
const listing2 = {
|
|
2951
|
-
items: [],
|
|
2952
|
-
cursor: void 0,
|
|
2953
|
-
search: void 0,
|
|
2954
|
-
exhausted: true
|
|
2955
|
-
};
|
|
2956
|
-
return {
|
|
2957
|
-
question: selectQuestion(leaf, input, listing2, context),
|
|
2958
|
-
listing: listing2
|
|
2959
|
-
};
|
|
2960
|
-
}
|
|
2961
|
-
const listing = await fetchListing(leaf, input, { context });
|
|
2992
|
+
const page = await fetchListing(leaf, input, firstPagePosition(), context);
|
|
2962
2993
|
return {
|
|
2963
|
-
question: selectQuestion(leaf, input,
|
|
2964
|
-
|
|
2994
|
+
question: selectQuestion(leaf, path, input, page, context),
|
|
2995
|
+
pagination: toPagination(page)
|
|
2965
2996
|
};
|
|
2966
2997
|
}
|
|
2967
2998
|
if (leaf.staticChoices) {
|
|
@@ -2970,6 +3001,7 @@ async function buildQuestion(leaf, input) {
|
|
|
2970
3001
|
return {
|
|
2971
3002
|
question: {
|
|
2972
3003
|
type: "select",
|
|
3004
|
+
path,
|
|
2973
3005
|
message: `Select ${leaf.name}:`,
|
|
2974
3006
|
choices: leaf.staticChoices,
|
|
2975
3007
|
actions: actions2
|
|
@@ -2983,6 +3015,7 @@ async function buildQuestion(leaf, input) {
|
|
|
2983
3015
|
return {
|
|
2984
3016
|
question: {
|
|
2985
3017
|
type: "input",
|
|
3018
|
+
path,
|
|
2986
3019
|
// The optional marker makes Enter-to-pass discoverable on a bare
|
|
2987
3020
|
// parameter; a labeled field carries its marker via labeledMessage.
|
|
2988
3021
|
message: labeledMessage(leaf) ?? `Enter ${leaf.name}${optional ? " (optional)" : ""}:`,
|
|
@@ -2997,6 +3030,7 @@ function collectionQuestion(t) {
|
|
|
2997
3030
|
if (t.count >= t.min) actions.push(AFFORDANCE.done);
|
|
2998
3031
|
return {
|
|
2999
3032
|
type: "collection",
|
|
3033
|
+
path: t.path,
|
|
3000
3034
|
message: `Add ${t.path[t.path.length - 1]}[${t.count}]?`,
|
|
3001
3035
|
container: "array",
|
|
3002
3036
|
count: t.count,
|
|
@@ -3010,6 +3044,7 @@ function collectionQuestion(t) {
|
|
|
3010
3044
|
function objectGateQuestion(path) {
|
|
3011
3045
|
return {
|
|
3012
3046
|
type: "collection",
|
|
3047
|
+
path,
|
|
3013
3048
|
message: `Add ${path[path.length - 1]}?`,
|
|
3014
3049
|
container: "object",
|
|
3015
3050
|
actions: [
|
|
@@ -3021,9 +3056,10 @@ function objectGateQuestion(path) {
|
|
|
3021
3056
|
]
|
|
3022
3057
|
};
|
|
3023
3058
|
}
|
|
3024
|
-
function optionalsGateQuestion(pending) {
|
|
3059
|
+
function optionalsGateQuestion(path, pending) {
|
|
3025
3060
|
return {
|
|
3026
3061
|
type: "collection",
|
|
3062
|
+
path,
|
|
3027
3063
|
// The prompt and its context ride separately so a host renders the info
|
|
3028
3064
|
// line above the confirm without composing any text of its own.
|
|
3029
3065
|
message: "Would you like to configure optional fields?",
|
|
@@ -3173,8 +3209,12 @@ async function askLeaf(state, path, leaf, opts = {}) {
|
|
|
3173
3209
|
state.current = path;
|
|
3174
3210
|
delete state.gate;
|
|
3175
3211
|
try {
|
|
3176
|
-
const { question,
|
|
3177
|
-
|
|
3212
|
+
const { question, pagination } = await buildQuestion(
|
|
3213
|
+
leaf,
|
|
3214
|
+
path,
|
|
3215
|
+
state.resolved
|
|
3216
|
+
);
|
|
3217
|
+
state.pagination = pagination;
|
|
3178
3218
|
return {
|
|
3179
3219
|
state,
|
|
3180
3220
|
result: {
|
|
@@ -3184,7 +3224,7 @@ async function askLeaf(state, path, leaf, opts = {}) {
|
|
|
3184
3224
|
}
|
|
3185
3225
|
};
|
|
3186
3226
|
} catch (error) {
|
|
3187
|
-
state.
|
|
3227
|
+
state.pagination = failedPagination(void 0, firstPagePosition());
|
|
3188
3228
|
return failedResult(state, leaf.name, error);
|
|
3189
3229
|
}
|
|
3190
3230
|
}
|
|
@@ -3194,13 +3234,13 @@ async function advance(ctx, state) {
|
|
|
3194
3234
|
if (!target) {
|
|
3195
3235
|
delete state.current;
|
|
3196
3236
|
delete state.gate;
|
|
3197
|
-
delete state.
|
|
3237
|
+
delete state.pagination;
|
|
3198
3238
|
return { state, result: finalize(ctx, state.resolved) };
|
|
3199
3239
|
}
|
|
3200
3240
|
if (target.kind === "array") {
|
|
3201
3241
|
state.current = target.path;
|
|
3202
3242
|
state.gate = "array";
|
|
3203
|
-
delete state.
|
|
3243
|
+
delete state.pagination;
|
|
3204
3244
|
return {
|
|
3205
3245
|
state,
|
|
3206
3246
|
result: { status: "ask", question: collectionQuestion(target) }
|
|
@@ -3209,7 +3249,7 @@ async function advance(ctx, state) {
|
|
|
3209
3249
|
if (target.kind === "object") {
|
|
3210
3250
|
state.current = target.path;
|
|
3211
3251
|
state.gate = "entry";
|
|
3212
|
-
delete state.
|
|
3252
|
+
delete state.pagination;
|
|
3213
3253
|
return {
|
|
3214
3254
|
state,
|
|
3215
3255
|
result: { status: "ask", question: objectGateQuestion(target.path) }
|
|
@@ -3218,12 +3258,12 @@ async function advance(ctx, state) {
|
|
|
3218
3258
|
if (target.kind === "optionals") {
|
|
3219
3259
|
state.current = target.path;
|
|
3220
3260
|
state.gate = "optionals";
|
|
3221
|
-
delete state.
|
|
3261
|
+
delete state.pagination;
|
|
3222
3262
|
return {
|
|
3223
3263
|
state,
|
|
3224
3264
|
result: {
|
|
3225
3265
|
status: "ask",
|
|
3226
|
-
question: optionalsGateQuestion(target.pending)
|
|
3266
|
+
question: optionalsGateQuestion(target.path, target.pending)
|
|
3227
3267
|
}
|
|
3228
3268
|
};
|
|
3229
3269
|
}
|
|
@@ -3274,13 +3314,13 @@ async function step(ctx, prior, action) {
|
|
|
3274
3314
|
if (action.type === "cancel") {
|
|
3275
3315
|
delete state.current;
|
|
3276
3316
|
delete state.gate;
|
|
3277
|
-
delete state.
|
|
3317
|
+
delete state.pagination;
|
|
3278
3318
|
return { state, result: { status: "cancelled" } };
|
|
3279
3319
|
}
|
|
3280
3320
|
const path = state.current;
|
|
3281
3321
|
if (!path) throw new Error("step called with no outstanding question");
|
|
3282
3322
|
const leaf = await leafAt(ctx, path, state.resolved);
|
|
3283
|
-
if (leaf && (action.type === "search" || action.type === "
|
|
3323
|
+
if (leaf && (action.type === "search" || action.type === "next_page" || action.type === "previous_page" || action.type === "retry")) {
|
|
3284
3324
|
return refine(ctx, state, leaf, path, action);
|
|
3285
3325
|
}
|
|
3286
3326
|
if (action.type === "add" || action.type === "done") {
|
|
@@ -3292,7 +3332,7 @@ async function step(ctx, prior, action) {
|
|
|
3292
3332
|
}
|
|
3293
3333
|
delete state.current;
|
|
3294
3334
|
delete state.gate;
|
|
3295
|
-
delete state.
|
|
3335
|
+
delete state.pagination;
|
|
3296
3336
|
if (action.type === "done") {
|
|
3297
3337
|
settle(state, path);
|
|
3298
3338
|
return advance(ctx, state);
|
|
@@ -3321,23 +3361,44 @@ async function step(ctx, prior, action) {
|
|
|
3321
3361
|
case "choose":
|
|
3322
3362
|
case "custom": {
|
|
3323
3363
|
if (leaf) {
|
|
3324
|
-
|
|
3364
|
+
let error;
|
|
3365
|
+
try {
|
|
3366
|
+
error = await validationError(leaf, action.value, state);
|
|
3367
|
+
} catch (thrown) {
|
|
3368
|
+
return failedResult(state, leaf.name, thrown);
|
|
3369
|
+
}
|
|
3325
3370
|
if (error) {
|
|
3326
|
-
if (state.
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3371
|
+
if (state.pagination && leaf.resolver?.type === "dynamic") {
|
|
3372
|
+
try {
|
|
3373
|
+
const context = await resolveContext(leaf, state.resolved);
|
|
3374
|
+
const page = await fetchListing(
|
|
3375
|
+
leaf,
|
|
3376
|
+
state.resolved,
|
|
3377
|
+
state.pagination.position,
|
|
3378
|
+
context
|
|
3379
|
+
);
|
|
3380
|
+
state.pagination = toPagination(page);
|
|
3381
|
+
return {
|
|
3382
|
+
state,
|
|
3383
|
+
result: {
|
|
3384
|
+
status: "ask",
|
|
3385
|
+
question: selectQuestion(
|
|
3386
|
+
leaf,
|
|
3387
|
+
path,
|
|
3388
|
+
state.resolved,
|
|
3389
|
+
page,
|
|
3390
|
+
context
|
|
3391
|
+
),
|
|
3392
|
+
error
|
|
3393
|
+
}
|
|
3394
|
+
};
|
|
3395
|
+
} catch (fetchError) {
|
|
3396
|
+
state.pagination = failedPagination(
|
|
3397
|
+
state.pagination,
|
|
3398
|
+
state.pagination.position
|
|
3399
|
+
);
|
|
3400
|
+
return failedResult(state, leaf.name, fetchError);
|
|
3401
|
+
}
|
|
3341
3402
|
}
|
|
3342
3403
|
return askLeaf(state, path, leaf, { error });
|
|
3343
3404
|
}
|
|
@@ -3356,15 +3417,11 @@ async function step(ctx, prior, action) {
|
|
|
3356
3417
|
throw new Error(`action "${action.type}" is not supported here`);
|
|
3357
3418
|
}
|
|
3358
3419
|
delete state.current;
|
|
3359
|
-
delete state.
|
|
3420
|
+
delete state.pagination;
|
|
3360
3421
|
return advance(ctx, state);
|
|
3361
3422
|
}
|
|
3362
3423
|
async function refine(ctx, state, leaf, path, action) {
|
|
3363
|
-
const
|
|
3364
|
-
search: state.listing?.search,
|
|
3365
|
-
cursor: state.listing?.cursor,
|
|
3366
|
-
priorItems: state.listing?.items ?? []
|
|
3367
|
-
};
|
|
3424
|
+
const position = positionAfter(state.pagination, action);
|
|
3368
3425
|
try {
|
|
3369
3426
|
if (action.type === "search") {
|
|
3370
3427
|
const exact = leaf.resolver?.type === "dynamic" ? await leaf.resolver.tryResolveFromSearch?.({
|
|
@@ -3374,32 +3431,62 @@ async function refine(ctx, state, leaf, path, action) {
|
|
|
3374
3431
|
if (exact) {
|
|
3375
3432
|
setAtPath(state.resolved, path, coerce(leaf, exact.resolvedValue));
|
|
3376
3433
|
delete state.current;
|
|
3377
|
-
delete state.
|
|
3434
|
+
delete state.pagination;
|
|
3378
3435
|
return advance(ctx, state);
|
|
3379
3436
|
}
|
|
3380
3437
|
}
|
|
3381
3438
|
const context = await resolveContext(leaf, state.resolved);
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
context
|
|
3385
|
-
});
|
|
3439
|
+
const page = await fetchListing(leaf, state.resolved, position, context);
|
|
3440
|
+
state.pagination = toPagination(page);
|
|
3386
3441
|
return {
|
|
3387
3442
|
state,
|
|
3388
3443
|
result: {
|
|
3389
3444
|
status: "ask",
|
|
3390
|
-
question: selectQuestion(leaf, state.resolved,
|
|
3445
|
+
question: selectQuestion(leaf, path, state.resolved, page, context)
|
|
3391
3446
|
}
|
|
3392
3447
|
};
|
|
3393
3448
|
} catch (error) {
|
|
3394
|
-
state.
|
|
3395
|
-
items: attempt.priorItems,
|
|
3396
|
-
search: attempt.search,
|
|
3397
|
-
cursor: attempt.cursor,
|
|
3398
|
-
exhausted: false
|
|
3399
|
-
};
|
|
3449
|
+
state.pagination = failedPagination(state.pagination, position);
|
|
3400
3450
|
return failedResult(state, leaf.name, error);
|
|
3401
3451
|
}
|
|
3402
3452
|
}
|
|
3453
|
+
function failedPagination(pagination, retryPosition) {
|
|
3454
|
+
return {
|
|
3455
|
+
position: pagination?.position ?? firstPagePosition(),
|
|
3456
|
+
...pagination?.nextCursor !== void 0 ? { nextCursor: pagination.nextCursor } : {},
|
|
3457
|
+
retryPosition
|
|
3458
|
+
};
|
|
3459
|
+
}
|
|
3460
|
+
function positionAfter(pagination, action) {
|
|
3461
|
+
const current = pagination?.position ?? firstPagePosition();
|
|
3462
|
+
switch (action.type) {
|
|
3463
|
+
case "search":
|
|
3464
|
+
return firstPagePosition({
|
|
3465
|
+
search: action.term,
|
|
3466
|
+
generation: current.generation + 1
|
|
3467
|
+
});
|
|
3468
|
+
case "next_page": {
|
|
3469
|
+
if (pagination?.nextCursor == null) return current;
|
|
3470
|
+
return {
|
|
3471
|
+
...current.search !== void 0 ? { search: current.search } : {},
|
|
3472
|
+
pageCursor: pagination.nextCursor,
|
|
3473
|
+
previousCursors: [...current.previousCursors, current.pageCursor],
|
|
3474
|
+
generation: current.generation
|
|
3475
|
+
};
|
|
3476
|
+
}
|
|
3477
|
+
case "previous_page": {
|
|
3478
|
+
if (current.previousCursors.length === 0) return current;
|
|
3479
|
+
return {
|
|
3480
|
+
...current.search !== void 0 ? { search: current.search } : {},
|
|
3481
|
+
pageCursor: current.previousCursors[current.previousCursors.length - 1],
|
|
3482
|
+
previousCursors: current.previousCursors.slice(0, -1),
|
|
3483
|
+
generation: current.generation
|
|
3484
|
+
};
|
|
3485
|
+
}
|
|
3486
|
+
case "retry":
|
|
3487
|
+
return pagination?.retryPosition ?? current;
|
|
3488
|
+
}
|
|
3489
|
+
}
|
|
3403
3490
|
|
|
3404
3491
|
// src/model/resolution/controller.ts
|
|
3405
3492
|
function toJsonSchema(schema) {
|