@zapier/kitcore 0.6.0 → 0.8.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 +24 -0
- package/dist/index.cjs +225 -133
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +183 -54
- package/dist/index.d.ts +183 -54
- package/dist/index.mjs +224 -133
- 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,69 +423,71 @@ async function* paginateBuffered(pageFunction, pageOptions) {
|
|
|
422
423
|
}
|
|
423
424
|
}
|
|
424
425
|
var paginate = paginateBuffered;
|
|
425
|
-
function
|
|
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
|
+
}
|
|
448
|
+
async function concatLists({
|
|
426
449
|
sources,
|
|
427
|
-
|
|
428
|
-
|
|
450
|
+
pageSize = 100,
|
|
451
|
+
cursor
|
|
429
452
|
}) {
|
|
430
453
|
if (sources.length === 0) {
|
|
431
|
-
|
|
432
|
-
return Object.assign(Promise.resolve(empty), {
|
|
433
|
-
[Symbol.asyncIterator]: async function* () {
|
|
434
|
-
yield empty;
|
|
435
|
-
}
|
|
436
|
-
});
|
|
454
|
+
return { data: [] };
|
|
437
455
|
}
|
|
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;
|
|
456
|
+
const pageFunction = async (options) => {
|
|
457
|
+
let { index, cursor: listCursor } = decodeConcatCursor(options.cursor);
|
|
458
|
+
while (index < sources.length) {
|
|
459
|
+
const page = await sources[index]({ cursor: listCursor });
|
|
460
|
+
const hasMoreInList = page.nextCursor != null;
|
|
461
|
+
if (page.data.length === 0 && !hasMoreInList) {
|
|
462
|
+
index++;
|
|
463
|
+
listCursor = void 0;
|
|
451
464
|
continue;
|
|
452
465
|
}
|
|
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
466
|
return {
|
|
465
|
-
data:
|
|
466
|
-
nextCursor:
|
|
467
|
+
data: page.data,
|
|
468
|
+
nextCursor: hasMoreInList ? encodeConcatCursor(index, page.nextCursor) : index < sources.length - 1 ? encodeConcatCursor(index + 1, void 0) : void 0
|
|
467
469
|
};
|
|
468
470
|
}
|
|
469
471
|
return { data: [] };
|
|
470
472
|
};
|
|
471
|
-
const
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
}
|
|
485
|
-
});
|
|
473
|
+
const result = await paginateBuffered(pageFunction, {
|
|
474
|
+
pageSize,
|
|
475
|
+
cursor
|
|
476
|
+
}).next();
|
|
477
|
+
return result.done ? { data: [] } : result.value;
|
|
478
|
+
}
|
|
479
|
+
function concatPaginated({
|
|
480
|
+
sources,
|
|
481
|
+
pageSize,
|
|
482
|
+
cursor
|
|
483
|
+
}) {
|
|
484
|
+
logDeprecation("concatPaginated() is deprecated. Use concatLists() instead.");
|
|
485
|
+
return concatLists({ sources, pageSize, cursor });
|
|
486
486
|
}
|
|
487
487
|
function toIterable(source) {
|
|
488
|
+
logDeprecation(
|
|
489
|
+
"toIterable() is deprecated. Call .pages() on the paginated result instead."
|
|
490
|
+
);
|
|
488
491
|
return { [Symbol.asyncIterator]: () => source[Symbol.asyncIterator]() };
|
|
489
492
|
}
|
|
490
493
|
|
|
@@ -839,6 +842,13 @@ function createPaginatedFunction(coreFn, options) {
|
|
|
839
842
|
[Symbol.asyncIterator]() {
|
|
840
843
|
return pageStream;
|
|
841
844
|
},
|
|
845
|
+
pages: function() {
|
|
846
|
+
return {
|
|
847
|
+
[Symbol.asyncIterator]() {
|
|
848
|
+
return pageStream;
|
|
849
|
+
}
|
|
850
|
+
};
|
|
851
|
+
},
|
|
842
852
|
items: function() {
|
|
843
853
|
return {
|
|
844
854
|
[Symbol.asyncIterator]: async function* () {
|
|
@@ -1351,6 +1361,7 @@ function defineResolver(config) {
|
|
|
1351
1361
|
getContext: config.getContext,
|
|
1352
1362
|
listItems: config.listItems,
|
|
1353
1363
|
prompt: config.prompt,
|
|
1364
|
+
validate: config.validate,
|
|
1354
1365
|
tryResolveWithoutPrompt: config.tryResolveWithoutPrompt,
|
|
1355
1366
|
tryResolveFromSearch: config.tryResolveFromSearch
|
|
1356
1367
|
};
|
|
@@ -2009,6 +2020,7 @@ function bindResolver(resolver, plugins) {
|
|
|
2009
2020
|
const {
|
|
2010
2021
|
getContext: getContext2,
|
|
2011
2022
|
listItems,
|
|
2023
|
+
validate,
|
|
2012
2024
|
tryResolveWithoutPrompt,
|
|
2013
2025
|
tryResolveFromSearch
|
|
2014
2026
|
} = resolver;
|
|
@@ -2022,6 +2034,9 @@ function bindResolver(resolver, plugins) {
|
|
|
2022
2034
|
};
|
|
2023
2035
|
if (getContext2)
|
|
2024
2036
|
bound.getContext = ({ input }) => getContext2({ imports, input });
|
|
2037
|
+
if (validate) {
|
|
2038
|
+
bound.validate = ({ value, input, context }) => validate({ imports, value, input, context });
|
|
2039
|
+
}
|
|
2025
2040
|
if (tryResolveWithoutPrompt) {
|
|
2026
2041
|
bound.tryResolveWithoutPrompt = ({ input }) => tryResolveWithoutPrompt({ imports, input });
|
|
2027
2042
|
}
|
|
@@ -2701,14 +2716,14 @@ function coerce(leaf, raw) {
|
|
|
2701
2716
|
}
|
|
2702
2717
|
async function validationError(leaf, value, state) {
|
|
2703
2718
|
if (leaf.resolver?.type !== "dynamic") return null;
|
|
2719
|
+
const validate = leaf.resolver.validate;
|
|
2720
|
+
if (!validate) return null;
|
|
2704
2721
|
const context = await resolveContext(leaf, state.resolved);
|
|
2705
|
-
const
|
|
2706
|
-
|
|
2722
|
+
const verdict = await validate({
|
|
2723
|
+
value,
|
|
2707
2724
|
input: mergeInput(state.resolved, leaf.extraInput),
|
|
2708
2725
|
context
|
|
2709
2726
|
});
|
|
2710
|
-
if (!config?.validate) return null;
|
|
2711
|
-
const verdict = config.validate(value);
|
|
2712
2727
|
if (verdict === true) return null;
|
|
2713
2728
|
return typeof verdict === "string" ? verdict : `${leaf.name}: invalid value`;
|
|
2714
2729
|
}
|
|
@@ -2834,20 +2849,36 @@ async function resolveContext(leaf, input) {
|
|
|
2834
2849
|
input: mergeInput(input, leaf.extraInput)
|
|
2835
2850
|
});
|
|
2836
2851
|
}
|
|
2837
|
-
|
|
2852
|
+
function firstPagePosition(opts = {}) {
|
|
2853
|
+
return {
|
|
2854
|
+
...opts.search !== void 0 ? { search: opts.search } : {},
|
|
2855
|
+
pageCursor: null,
|
|
2856
|
+
previousCursors: [],
|
|
2857
|
+
generation: opts.generation ?? 0
|
|
2858
|
+
};
|
|
2859
|
+
}
|
|
2860
|
+
async function fetchListing(leaf, input, position, context) {
|
|
2861
|
+
if (leaf.resolver?.type === "dynamic" && leaf.resolver.inputType === "search" && position.search === void 0) {
|
|
2862
|
+
return { position, items: [] };
|
|
2863
|
+
}
|
|
2838
2864
|
const page = await firstPage(
|
|
2839
2865
|
leaf.resolver?.type === "dynamic" ? leaf.resolver.listItems({
|
|
2840
2866
|
input: mergeInput(input, leaf.extraInput),
|
|
2841
|
-
context
|
|
2842
|
-
search:
|
|
2843
|
-
cursor:
|
|
2867
|
+
context,
|
|
2868
|
+
search: position.search,
|
|
2869
|
+
cursor: position.pageCursor ?? void 0
|
|
2844
2870
|
}) : void 0
|
|
2845
2871
|
);
|
|
2846
2872
|
return {
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2873
|
+
position,
|
|
2874
|
+
items: page.data,
|
|
2875
|
+
...page.nextCursor != null ? { nextCursor: page.nextCursor } : {}
|
|
2876
|
+
};
|
|
2877
|
+
}
|
|
2878
|
+
function toPagination(page) {
|
|
2879
|
+
return {
|
|
2880
|
+
position: page.position,
|
|
2881
|
+
...page.nextCursor !== void 0 ? { nextCursor: page.nextCursor } : {}
|
|
2851
2882
|
};
|
|
2852
2883
|
}
|
|
2853
2884
|
|
|
@@ -2869,23 +2900,32 @@ var AFFORDANCE = {
|
|
|
2869
2900
|
description: "Filter the options by a search term",
|
|
2870
2901
|
supply: "term"
|
|
2871
2902
|
},
|
|
2872
|
-
|
|
2903
|
+
nextPage: {
|
|
2904
|
+
action: "next_page",
|
|
2905
|
+
description: "Fetch the next page of options"
|
|
2906
|
+
},
|
|
2907
|
+
previousPage: {
|
|
2908
|
+
action: "previous_page",
|
|
2909
|
+
description: "Return to the previous page of options"
|
|
2910
|
+
},
|
|
2873
2911
|
skip: { action: "skip", description: "Omit this optional parameter" },
|
|
2874
2912
|
add: { action: "add", description: "Add another item" },
|
|
2875
2913
|
done: { action: "done", description: "Finish the list" },
|
|
2876
2914
|
retry: { action: "retry", description: "Retry loading the options" },
|
|
2877
2915
|
cancel: { action: "cancel", description: "Cancel resolution" }
|
|
2878
2916
|
};
|
|
2879
|
-
function selectActions(leaf,
|
|
2917
|
+
function selectActions(leaf, page, multiple) {
|
|
2880
2918
|
const searchMode = leaf.resolver?.type === "dynamic" && leaf.resolver.inputType === "search";
|
|
2881
|
-
if (searchMode &&
|
|
2919
|
+
if (searchMode && page.position.search === void 0 && page.items.length === 0) {
|
|
2882
2920
|
const actions2 = multiple ? [AFFORDANCE.search] : [AFFORDANCE.search, AFFORDANCE.custom];
|
|
2883
2921
|
if (!leaf.required) actions2.push(AFFORDANCE.skip);
|
|
2884
2922
|
return actions2;
|
|
2885
2923
|
}
|
|
2886
2924
|
const actions = multiple ? [AFFORDANCE.choose] : [AFFORDANCE.choose, AFFORDANCE.custom];
|
|
2887
2925
|
if (searchMode) actions.push(AFFORDANCE.search);
|
|
2888
|
-
if (
|
|
2926
|
+
if (page.nextCursor) actions.push(AFFORDANCE.nextPage);
|
|
2927
|
+
if (page.position.previousCursors.length > 0)
|
|
2928
|
+
actions.push(AFFORDANCE.previousPage);
|
|
2889
2929
|
if (!leaf.required) actions.push(AFFORDANCE.skip);
|
|
2890
2930
|
return actions;
|
|
2891
2931
|
}
|
|
@@ -2893,16 +2933,17 @@ function labeledMessage(leaf) {
|
|
|
2893
2933
|
if (!leaf.label) return void 0;
|
|
2894
2934
|
return `${leaf.label} (${leaf.required ? "required" : "optional"}):`;
|
|
2895
2935
|
}
|
|
2896
|
-
function selectQuestion(leaf, input,
|
|
2936
|
+
function selectQuestion(leaf, path, input, page, context) {
|
|
2897
2937
|
const resolver = leaf.resolver?.type === "dynamic" ? leaf.resolver : void 0;
|
|
2898
2938
|
const config = resolver?.prompt?.({
|
|
2899
|
-
items:
|
|
2939
|
+
items: page.items,
|
|
2900
2940
|
input: mergeInput(input, leaf.extraInput),
|
|
2901
2941
|
context
|
|
2902
2942
|
});
|
|
2903
2943
|
const multiple = config?.type === "checkbox";
|
|
2904
2944
|
return {
|
|
2905
2945
|
type: "select",
|
|
2946
|
+
path,
|
|
2906
2947
|
// A labeled field's title beats the resolver's message: per-field
|
|
2907
2948
|
// resolvers are shared across fields (one choices-fetcher for every
|
|
2908
2949
|
// field), so only the leaf knows which field is being asked.
|
|
@@ -2910,9 +2951,13 @@ function selectQuestion(leaf, input, listing, context) {
|
|
|
2910
2951
|
choices: (config?.choices ?? []).map(toChoice),
|
|
2911
2952
|
...multiple ? { multiple: true } : {},
|
|
2912
2953
|
...config?.notes?.length ? { notes: config.notes } : {},
|
|
2913
|
-
...
|
|
2954
|
+
...page.position.search !== void 0 ? { search: page.position.search } : {},
|
|
2914
2955
|
...resolver?.placeholder ? { placeholder: resolver.placeholder } : {},
|
|
2915
|
-
|
|
2956
|
+
page: {
|
|
2957
|
+
generation: page.position.generation,
|
|
2958
|
+
index: page.position.previousCursors.length
|
|
2959
|
+
},
|
|
2960
|
+
actions: selectActions(leaf, page, multiple)
|
|
2916
2961
|
};
|
|
2917
2962
|
}
|
|
2918
2963
|
function toControllerError(error) {
|
|
@@ -2934,6 +2979,7 @@ function failedResult(state, name, error) {
|
|
|
2934
2979
|
error: toControllerError(error),
|
|
2935
2980
|
question: {
|
|
2936
2981
|
type: "select",
|
|
2982
|
+
path: state.current ?? [],
|
|
2937
2983
|
message: `Could not load options for ${name}.`,
|
|
2938
2984
|
choices: [],
|
|
2939
2985
|
actions: [AFFORDANCE.retry, AFFORDANCE.cancel]
|
|
@@ -2941,27 +2987,15 @@ function failedResult(state, name, error) {
|
|
|
2941
2987
|
}
|
|
2942
2988
|
};
|
|
2943
2989
|
}
|
|
2944
|
-
async function buildQuestion(leaf, input) {
|
|
2990
|
+
async function buildQuestion(leaf, path, input) {
|
|
2945
2991
|
const optional = !leaf.required;
|
|
2946
2992
|
const resolver = leaf.resolver;
|
|
2947
2993
|
if (resolver?.type === "dynamic") {
|
|
2948
2994
|
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 });
|
|
2995
|
+
const page = await fetchListing(leaf, input, firstPagePosition(), context);
|
|
2962
2996
|
return {
|
|
2963
|
-
question: selectQuestion(leaf, input,
|
|
2964
|
-
|
|
2997
|
+
question: selectQuestion(leaf, path, input, page, context),
|
|
2998
|
+
pagination: toPagination(page)
|
|
2965
2999
|
};
|
|
2966
3000
|
}
|
|
2967
3001
|
if (leaf.staticChoices) {
|
|
@@ -2970,6 +3004,7 @@ async function buildQuestion(leaf, input) {
|
|
|
2970
3004
|
return {
|
|
2971
3005
|
question: {
|
|
2972
3006
|
type: "select",
|
|
3007
|
+
path,
|
|
2973
3008
|
message: `Select ${leaf.name}:`,
|
|
2974
3009
|
choices: leaf.staticChoices,
|
|
2975
3010
|
actions: actions2
|
|
@@ -2983,6 +3018,7 @@ async function buildQuestion(leaf, input) {
|
|
|
2983
3018
|
return {
|
|
2984
3019
|
question: {
|
|
2985
3020
|
type: "input",
|
|
3021
|
+
path,
|
|
2986
3022
|
// The optional marker makes Enter-to-pass discoverable on a bare
|
|
2987
3023
|
// parameter; a labeled field carries its marker via labeledMessage.
|
|
2988
3024
|
message: labeledMessage(leaf) ?? `Enter ${leaf.name}${optional ? " (optional)" : ""}:`,
|
|
@@ -2997,6 +3033,7 @@ function collectionQuestion(t) {
|
|
|
2997
3033
|
if (t.count >= t.min) actions.push(AFFORDANCE.done);
|
|
2998
3034
|
return {
|
|
2999
3035
|
type: "collection",
|
|
3036
|
+
path: t.path,
|
|
3000
3037
|
message: `Add ${t.path[t.path.length - 1]}[${t.count}]?`,
|
|
3001
3038
|
container: "array",
|
|
3002
3039
|
count: t.count,
|
|
@@ -3010,6 +3047,7 @@ function collectionQuestion(t) {
|
|
|
3010
3047
|
function objectGateQuestion(path) {
|
|
3011
3048
|
return {
|
|
3012
3049
|
type: "collection",
|
|
3050
|
+
path,
|
|
3013
3051
|
message: `Add ${path[path.length - 1]}?`,
|
|
3014
3052
|
container: "object",
|
|
3015
3053
|
actions: [
|
|
@@ -3021,9 +3059,10 @@ function objectGateQuestion(path) {
|
|
|
3021
3059
|
]
|
|
3022
3060
|
};
|
|
3023
3061
|
}
|
|
3024
|
-
function optionalsGateQuestion(pending) {
|
|
3062
|
+
function optionalsGateQuestion(path, pending) {
|
|
3025
3063
|
return {
|
|
3026
3064
|
type: "collection",
|
|
3065
|
+
path,
|
|
3027
3066
|
// The prompt and its context ride separately so a host renders the info
|
|
3028
3067
|
// line above the confirm without composing any text of its own.
|
|
3029
3068
|
message: "Would you like to configure optional fields?",
|
|
@@ -3173,8 +3212,12 @@ async function askLeaf(state, path, leaf, opts = {}) {
|
|
|
3173
3212
|
state.current = path;
|
|
3174
3213
|
delete state.gate;
|
|
3175
3214
|
try {
|
|
3176
|
-
const { question,
|
|
3177
|
-
|
|
3215
|
+
const { question, pagination } = await buildQuestion(
|
|
3216
|
+
leaf,
|
|
3217
|
+
path,
|
|
3218
|
+
state.resolved
|
|
3219
|
+
);
|
|
3220
|
+
state.pagination = pagination;
|
|
3178
3221
|
return {
|
|
3179
3222
|
state,
|
|
3180
3223
|
result: {
|
|
@@ -3184,7 +3227,7 @@ async function askLeaf(state, path, leaf, opts = {}) {
|
|
|
3184
3227
|
}
|
|
3185
3228
|
};
|
|
3186
3229
|
} catch (error) {
|
|
3187
|
-
state.
|
|
3230
|
+
state.pagination = failedPagination(void 0, firstPagePosition());
|
|
3188
3231
|
return failedResult(state, leaf.name, error);
|
|
3189
3232
|
}
|
|
3190
3233
|
}
|
|
@@ -3194,13 +3237,13 @@ async function advance(ctx, state) {
|
|
|
3194
3237
|
if (!target) {
|
|
3195
3238
|
delete state.current;
|
|
3196
3239
|
delete state.gate;
|
|
3197
|
-
delete state.
|
|
3240
|
+
delete state.pagination;
|
|
3198
3241
|
return { state, result: finalize(ctx, state.resolved) };
|
|
3199
3242
|
}
|
|
3200
3243
|
if (target.kind === "array") {
|
|
3201
3244
|
state.current = target.path;
|
|
3202
3245
|
state.gate = "array";
|
|
3203
|
-
delete state.
|
|
3246
|
+
delete state.pagination;
|
|
3204
3247
|
return {
|
|
3205
3248
|
state,
|
|
3206
3249
|
result: { status: "ask", question: collectionQuestion(target) }
|
|
@@ -3209,7 +3252,7 @@ async function advance(ctx, state) {
|
|
|
3209
3252
|
if (target.kind === "object") {
|
|
3210
3253
|
state.current = target.path;
|
|
3211
3254
|
state.gate = "entry";
|
|
3212
|
-
delete state.
|
|
3255
|
+
delete state.pagination;
|
|
3213
3256
|
return {
|
|
3214
3257
|
state,
|
|
3215
3258
|
result: { status: "ask", question: objectGateQuestion(target.path) }
|
|
@@ -3218,12 +3261,12 @@ async function advance(ctx, state) {
|
|
|
3218
3261
|
if (target.kind === "optionals") {
|
|
3219
3262
|
state.current = target.path;
|
|
3220
3263
|
state.gate = "optionals";
|
|
3221
|
-
delete state.
|
|
3264
|
+
delete state.pagination;
|
|
3222
3265
|
return {
|
|
3223
3266
|
state,
|
|
3224
3267
|
result: {
|
|
3225
3268
|
status: "ask",
|
|
3226
|
-
question: optionalsGateQuestion(target.pending)
|
|
3269
|
+
question: optionalsGateQuestion(target.path, target.pending)
|
|
3227
3270
|
}
|
|
3228
3271
|
};
|
|
3229
3272
|
}
|
|
@@ -3274,13 +3317,13 @@ async function step(ctx, prior, action) {
|
|
|
3274
3317
|
if (action.type === "cancel") {
|
|
3275
3318
|
delete state.current;
|
|
3276
3319
|
delete state.gate;
|
|
3277
|
-
delete state.
|
|
3320
|
+
delete state.pagination;
|
|
3278
3321
|
return { state, result: { status: "cancelled" } };
|
|
3279
3322
|
}
|
|
3280
3323
|
const path = state.current;
|
|
3281
3324
|
if (!path) throw new Error("step called with no outstanding question");
|
|
3282
3325
|
const leaf = await leafAt(ctx, path, state.resolved);
|
|
3283
|
-
if (leaf && (action.type === "search" || action.type === "
|
|
3326
|
+
if (leaf && (action.type === "search" || action.type === "next_page" || action.type === "previous_page" || action.type === "retry")) {
|
|
3284
3327
|
return refine(ctx, state, leaf, path, action);
|
|
3285
3328
|
}
|
|
3286
3329
|
if (action.type === "add" || action.type === "done") {
|
|
@@ -3292,7 +3335,7 @@ async function step(ctx, prior, action) {
|
|
|
3292
3335
|
}
|
|
3293
3336
|
delete state.current;
|
|
3294
3337
|
delete state.gate;
|
|
3295
|
-
delete state.
|
|
3338
|
+
delete state.pagination;
|
|
3296
3339
|
if (action.type === "done") {
|
|
3297
3340
|
settle(state, path);
|
|
3298
3341
|
return advance(ctx, state);
|
|
@@ -3321,23 +3364,44 @@ async function step(ctx, prior, action) {
|
|
|
3321
3364
|
case "choose":
|
|
3322
3365
|
case "custom": {
|
|
3323
3366
|
if (leaf) {
|
|
3324
|
-
|
|
3367
|
+
let error;
|
|
3368
|
+
try {
|
|
3369
|
+
error = await validationError(leaf, action.value, state);
|
|
3370
|
+
} catch (thrown) {
|
|
3371
|
+
return failedResult(state, leaf.name, thrown);
|
|
3372
|
+
}
|
|
3325
3373
|
if (error) {
|
|
3326
|
-
if (state.
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3374
|
+
if (state.pagination && leaf.resolver?.type === "dynamic") {
|
|
3375
|
+
try {
|
|
3376
|
+
const context = await resolveContext(leaf, state.resolved);
|
|
3377
|
+
const page = await fetchListing(
|
|
3378
|
+
leaf,
|
|
3379
|
+
state.resolved,
|
|
3380
|
+
state.pagination.position,
|
|
3381
|
+
context
|
|
3382
|
+
);
|
|
3383
|
+
state.pagination = toPagination(page);
|
|
3384
|
+
return {
|
|
3385
|
+
state,
|
|
3386
|
+
result: {
|
|
3387
|
+
status: "ask",
|
|
3388
|
+
question: selectQuestion(
|
|
3389
|
+
leaf,
|
|
3390
|
+
path,
|
|
3391
|
+
state.resolved,
|
|
3392
|
+
page,
|
|
3393
|
+
context
|
|
3394
|
+
),
|
|
3395
|
+
error
|
|
3396
|
+
}
|
|
3397
|
+
};
|
|
3398
|
+
} catch (fetchError) {
|
|
3399
|
+
state.pagination = failedPagination(
|
|
3400
|
+
state.pagination,
|
|
3401
|
+
state.pagination.position
|
|
3402
|
+
);
|
|
3403
|
+
return failedResult(state, leaf.name, fetchError);
|
|
3404
|
+
}
|
|
3341
3405
|
}
|
|
3342
3406
|
return askLeaf(state, path, leaf, { error });
|
|
3343
3407
|
}
|
|
@@ -3356,15 +3420,11 @@ async function step(ctx, prior, action) {
|
|
|
3356
3420
|
throw new Error(`action "${action.type}" is not supported here`);
|
|
3357
3421
|
}
|
|
3358
3422
|
delete state.current;
|
|
3359
|
-
delete state.
|
|
3423
|
+
delete state.pagination;
|
|
3360
3424
|
return advance(ctx, state);
|
|
3361
3425
|
}
|
|
3362
3426
|
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
|
-
};
|
|
3427
|
+
const position = positionAfter(state.pagination, action);
|
|
3368
3428
|
try {
|
|
3369
3429
|
if (action.type === "search") {
|
|
3370
3430
|
const exact = leaf.resolver?.type === "dynamic" ? await leaf.resolver.tryResolveFromSearch?.({
|
|
@@ -3374,32 +3434,62 @@ async function refine(ctx, state, leaf, path, action) {
|
|
|
3374
3434
|
if (exact) {
|
|
3375
3435
|
setAtPath(state.resolved, path, coerce(leaf, exact.resolvedValue));
|
|
3376
3436
|
delete state.current;
|
|
3377
|
-
delete state.
|
|
3437
|
+
delete state.pagination;
|
|
3378
3438
|
return advance(ctx, state);
|
|
3379
3439
|
}
|
|
3380
3440
|
}
|
|
3381
3441
|
const context = await resolveContext(leaf, state.resolved);
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
context
|
|
3385
|
-
});
|
|
3442
|
+
const page = await fetchListing(leaf, state.resolved, position, context);
|
|
3443
|
+
state.pagination = toPagination(page);
|
|
3386
3444
|
return {
|
|
3387
3445
|
state,
|
|
3388
3446
|
result: {
|
|
3389
3447
|
status: "ask",
|
|
3390
|
-
question: selectQuestion(leaf, state.resolved,
|
|
3448
|
+
question: selectQuestion(leaf, path, state.resolved, page, context)
|
|
3391
3449
|
}
|
|
3392
3450
|
};
|
|
3393
3451
|
} catch (error) {
|
|
3394
|
-
state.
|
|
3395
|
-
items: attempt.priorItems,
|
|
3396
|
-
search: attempt.search,
|
|
3397
|
-
cursor: attempt.cursor,
|
|
3398
|
-
exhausted: false
|
|
3399
|
-
};
|
|
3452
|
+
state.pagination = failedPagination(state.pagination, position);
|
|
3400
3453
|
return failedResult(state, leaf.name, error);
|
|
3401
3454
|
}
|
|
3402
3455
|
}
|
|
3456
|
+
function failedPagination(pagination, retryPosition) {
|
|
3457
|
+
return {
|
|
3458
|
+
position: pagination?.position ?? firstPagePosition(),
|
|
3459
|
+
...pagination?.nextCursor !== void 0 ? { nextCursor: pagination.nextCursor } : {},
|
|
3460
|
+
retryPosition
|
|
3461
|
+
};
|
|
3462
|
+
}
|
|
3463
|
+
function positionAfter(pagination, action) {
|
|
3464
|
+
const current = pagination?.position ?? firstPagePosition();
|
|
3465
|
+
switch (action.type) {
|
|
3466
|
+
case "search":
|
|
3467
|
+
return firstPagePosition({
|
|
3468
|
+
search: action.term,
|
|
3469
|
+
generation: current.generation + 1
|
|
3470
|
+
});
|
|
3471
|
+
case "next_page": {
|
|
3472
|
+
if (pagination?.nextCursor == null) return current;
|
|
3473
|
+
return {
|
|
3474
|
+
...current.search !== void 0 ? { search: current.search } : {},
|
|
3475
|
+
pageCursor: pagination.nextCursor,
|
|
3476
|
+
previousCursors: [...current.previousCursors, current.pageCursor],
|
|
3477
|
+
generation: current.generation
|
|
3478
|
+
};
|
|
3479
|
+
}
|
|
3480
|
+
case "previous_page": {
|
|
3481
|
+
if (current.previousCursors.length === 0) return current;
|
|
3482
|
+
return {
|
|
3483
|
+
...current.search !== void 0 ? { search: current.search } : {},
|
|
3484
|
+
pageCursor: current.previousCursors[current.previousCursors.length - 1],
|
|
3485
|
+
previousCursors: current.previousCursors.slice(0, -1),
|
|
3486
|
+
generation: current.generation
|
|
3487
|
+
};
|
|
3488
|
+
}
|
|
3489
|
+
case "retry":
|
|
3490
|
+
return pagination?.retryPosition ?? current;
|
|
3491
|
+
}
|
|
3492
|
+
}
|
|
3403
3493
|
|
|
3404
3494
|
// src/model/resolution/controller.ts
|
|
3405
3495
|
function toJsonSchema(schema) {
|
|
@@ -3581,6 +3671,7 @@ export {
|
|
|
3581
3671
|
CoreSignal,
|
|
3582
3672
|
addPlugin,
|
|
3583
3673
|
composePlugins,
|
|
3674
|
+
concatLists,
|
|
3584
3675
|
concatPaginated,
|
|
3585
3676
|
coreOptionsPluginRef,
|
|
3586
3677
|
createAsyncContext,
|