@zapier/kitcore 0.7.0 → 0.9.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 +25 -0
- package/dist/index.cjs +626 -316
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +229 -114
- package/dist/index.d.ts +229 -114
- package/dist/index.mjs +621 -313
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
// src/registry.ts
|
|
2
|
-
import { z } from "zod";
|
|
3
|
-
|
|
4
1
|
// src/utils/string-utils.ts
|
|
5
2
|
function toTitleCase(input) {
|
|
6
3
|
return input.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_\-]+/g, " ").replace(/\s+/g, " ").trim().split(" ").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
|
|
@@ -24,6 +21,65 @@ function pluralizeLastWord(title) {
|
|
|
24
21
|
return [...words.slice(0, -1), pluralize(words[words.length - 1])].join(" ");
|
|
25
22
|
}
|
|
26
23
|
|
|
24
|
+
// src/utils/schema-utils.ts
|
|
25
|
+
import { z } from "zod";
|
|
26
|
+
function canonicalInputSchema(schema) {
|
|
27
|
+
if (schema instanceof z.ZodUnion) {
|
|
28
|
+
return schema.options[0];
|
|
29
|
+
}
|
|
30
|
+
return schema;
|
|
31
|
+
}
|
|
32
|
+
function getOutputSchema(inputSchema) {
|
|
33
|
+
return inputSchema._zod.def.outputSchema;
|
|
34
|
+
}
|
|
35
|
+
function withOutputSchema(inputSchema, outputSchema) {
|
|
36
|
+
Object.assign(inputSchema._zod.def, {
|
|
37
|
+
outputSchema
|
|
38
|
+
});
|
|
39
|
+
return inputSchema;
|
|
40
|
+
}
|
|
41
|
+
function withResolver(schema, config) {
|
|
42
|
+
schema._zod.def.resolverMeta = config;
|
|
43
|
+
return schema;
|
|
44
|
+
}
|
|
45
|
+
function getSchemaDescription(schema) {
|
|
46
|
+
return schema.description;
|
|
47
|
+
}
|
|
48
|
+
function getFieldDescriptions(schema) {
|
|
49
|
+
const descriptions = {};
|
|
50
|
+
const shape = schema.shape;
|
|
51
|
+
for (const [key, fieldSchema] of Object.entries(shape)) {
|
|
52
|
+
if (fieldSchema instanceof z.ZodType && fieldSchema.description) {
|
|
53
|
+
descriptions[key] = fieldSchema.description;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return descriptions;
|
|
57
|
+
}
|
|
58
|
+
function withPositional(schema) {
|
|
59
|
+
Object.assign(schema._zod.def, {
|
|
60
|
+
positionalMeta: { positional: true }
|
|
61
|
+
});
|
|
62
|
+
return schema;
|
|
63
|
+
}
|
|
64
|
+
function schemaHasPositionalMeta(schema) {
|
|
65
|
+
return "positionalMeta" in schema._zod.def;
|
|
66
|
+
}
|
|
67
|
+
function isPositional(schema) {
|
|
68
|
+
if (schemaHasPositionalMeta(schema) && schema._zod.def.positionalMeta?.positional) {
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
if (schema instanceof z.ZodOptional) {
|
|
72
|
+
return isPositional(schema._zod.def.innerType);
|
|
73
|
+
}
|
|
74
|
+
if (schema instanceof z.ZodDefault) {
|
|
75
|
+
return isPositional(schema._zod.def.innerType);
|
|
76
|
+
}
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
function openEnum(values, description) {
|
|
80
|
+
return z.union([z.enum(values), z.string()]).describe(description);
|
|
81
|
+
}
|
|
82
|
+
|
|
27
83
|
// src/registry.ts
|
|
28
84
|
function resolveCategoryDefinition(ref) {
|
|
29
85
|
const def = typeof ref === "string" ? { key: ref } : ref;
|
|
@@ -34,30 +90,25 @@ function resolveCategoryDefinition(ref) {
|
|
|
34
90
|
titlePlural: def.titlePlural ?? pluralizeLastWord(title)
|
|
35
91
|
};
|
|
36
92
|
}
|
|
37
|
-
function canonicalInputSchema(schema) {
|
|
38
|
-
if (schema instanceof z.ZodUnion) {
|
|
39
|
-
return schema.options[0];
|
|
40
|
-
}
|
|
41
|
-
return schema;
|
|
42
|
-
}
|
|
43
93
|
function buildRegistry({
|
|
44
94
|
sdk,
|
|
45
95
|
meta,
|
|
46
96
|
formatters,
|
|
47
|
-
|
|
97
|
+
resolvers,
|
|
48
98
|
positional,
|
|
99
|
+
skipInputValidation,
|
|
49
100
|
packageFilter
|
|
50
101
|
}) {
|
|
51
102
|
const definitionsByKey = /* @__PURE__ */ new Map();
|
|
52
103
|
const objectDeclaredKeys = /* @__PURE__ */ new Set();
|
|
53
104
|
for (const m of Object.values(meta)) {
|
|
54
105
|
for (const ref of m.categories ?? []) {
|
|
55
|
-
const
|
|
106
|
+
const key = typeof ref === "string" ? ref : ref.key;
|
|
56
107
|
if (typeof ref === "object") {
|
|
57
|
-
objectDeclaredKeys.add(
|
|
58
|
-
definitionsByKey.set(
|
|
59
|
-
} else if (!objectDeclaredKeys.has(
|
|
60
|
-
definitionsByKey.set(
|
|
108
|
+
objectDeclaredKeys.add(key);
|
|
109
|
+
definitionsByKey.set(key, resolveCategoryDefinition(ref));
|
|
110
|
+
} else if (!objectDeclaredKeys.has(key)) {
|
|
111
|
+
definitionsByKey.set(key, resolveCategoryDefinition(ref));
|
|
61
112
|
}
|
|
62
113
|
}
|
|
63
114
|
}
|
|
@@ -65,30 +116,29 @@ function buildRegistry({
|
|
|
65
116
|
definitionsByKey.set("other", resolveCategoryDefinition("other"));
|
|
66
117
|
}
|
|
67
118
|
const knownCategories = Array.from(definitionsByKey.keys());
|
|
68
|
-
const functions = Object.keys(meta).filter((
|
|
69
|
-
const property = sdk[
|
|
119
|
+
const functions = Object.keys(meta).filter((key) => {
|
|
120
|
+
const property = sdk[key];
|
|
70
121
|
if (typeof property === "function") return true;
|
|
71
|
-
const [rootKey] =
|
|
122
|
+
const [rootKey] = key.split(".");
|
|
72
123
|
const rootProperty = sdk[rootKey];
|
|
73
124
|
return typeof rootProperty === "object" && rootProperty !== null;
|
|
74
|
-
}).map((
|
|
75
|
-
const m = meta[
|
|
125
|
+
}).map((key) => {
|
|
126
|
+
const m = meta[key];
|
|
76
127
|
return {
|
|
77
|
-
name:
|
|
128
|
+
name: key,
|
|
78
129
|
description: m.description,
|
|
79
130
|
type: m.type,
|
|
80
131
|
itemType: m.itemType,
|
|
81
132
|
returnType: m.returnType,
|
|
82
133
|
inputSchema: canonicalInputSchema(m.inputSchema),
|
|
83
|
-
inputParameters: m.inputParameters,
|
|
84
134
|
outputSchema: m.outputSchema,
|
|
85
|
-
positional: positional?.[
|
|
135
|
+
positional: positional?.[key],
|
|
136
|
+
skipInputValidation: skipInputValidation?.[key],
|
|
86
137
|
categories: (m.categories ?? []).map(
|
|
87
138
|
(c) => typeof c === "string" ? c : c.key
|
|
88
139
|
),
|
|
89
|
-
resolvers:
|
|
90
|
-
|
|
91
|
-
formatter: formatters?.[key2],
|
|
140
|
+
resolvers: resolvers?.[key],
|
|
141
|
+
formatter: formatters?.[key],
|
|
92
142
|
experimental: m.experimental,
|
|
93
143
|
packages: m.packages,
|
|
94
144
|
confirm: m.confirm ?? (m.type === "delete" ? "delete" : void 0),
|
|
@@ -445,53 +495,49 @@ function decodeConcatCursor(incoming) {
|
|
|
445
495
|
}
|
|
446
496
|
return { index: 0, cursor: incoming };
|
|
447
497
|
}
|
|
448
|
-
function
|
|
498
|
+
async function concatLists({
|
|
449
499
|
sources,
|
|
450
500
|
pageSize = 100,
|
|
451
501
|
cursor
|
|
452
502
|
}) {
|
|
453
503
|
if (sources.length === 0) {
|
|
454
|
-
|
|
455
|
-
return Object.assign(Promise.resolve(empty), {
|
|
456
|
-
[Symbol.asyncIterator]: async function* () {
|
|
457
|
-
yield empty;
|
|
458
|
-
}
|
|
459
|
-
});
|
|
504
|
+
return { data: [] };
|
|
460
505
|
}
|
|
461
506
|
const pageFunction = async (options) => {
|
|
462
|
-
let { index, cursor:
|
|
507
|
+
let { index, cursor: listCursor } = decodeConcatCursor(options.cursor);
|
|
463
508
|
while (index < sources.length) {
|
|
464
|
-
const page = await sources[index]({ cursor:
|
|
465
|
-
const
|
|
466
|
-
if (page.data.length === 0 && !
|
|
509
|
+
const page = await sources[index]({ cursor: listCursor });
|
|
510
|
+
const hasMoreInList = page.nextCursor != null;
|
|
511
|
+
if (page.data.length === 0 && !hasMoreInList) {
|
|
467
512
|
index++;
|
|
468
|
-
|
|
513
|
+
listCursor = void 0;
|
|
469
514
|
continue;
|
|
470
515
|
}
|
|
471
516
|
return {
|
|
472
517
|
data: page.data,
|
|
473
|
-
nextCursor:
|
|
518
|
+
nextCursor: hasMoreInList ? encodeConcatCursor(index, page.nextCursor) : index < sources.length - 1 ? encodeConcatCursor(index + 1, void 0) : void 0
|
|
474
519
|
};
|
|
475
520
|
}
|
|
476
521
|
return { data: [] };
|
|
477
522
|
};
|
|
478
|
-
const
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
}
|
|
492
|
-
});
|
|
523
|
+
const result = await paginateBuffered(pageFunction, {
|
|
524
|
+
pageSize,
|
|
525
|
+
cursor
|
|
526
|
+
}).next();
|
|
527
|
+
return result.done ? { data: [] } : result.value;
|
|
528
|
+
}
|
|
529
|
+
function concatPaginated({
|
|
530
|
+
sources,
|
|
531
|
+
pageSize,
|
|
532
|
+
cursor
|
|
533
|
+
}) {
|
|
534
|
+
logDeprecation("concatPaginated() is deprecated. Use concatLists() instead.");
|
|
535
|
+
return concatLists({ sources, pageSize, cursor });
|
|
493
536
|
}
|
|
494
537
|
function toIterable(source) {
|
|
538
|
+
logDeprecation(
|
|
539
|
+
"toIterable() is deprecated. Call .pages() on the paginated result instead."
|
|
540
|
+
);
|
|
495
541
|
return { [Symbol.asyncIterator]: () => source[Symbol.asyncIterator]() };
|
|
496
542
|
}
|
|
497
543
|
|
|
@@ -581,6 +627,52 @@ function runInMethodScope(fn) {
|
|
|
581
627
|
var runWithTelemetryContext = runInMethodScope;
|
|
582
628
|
var isTelemetryNested = isNestedMethodCall;
|
|
583
629
|
|
|
630
|
+
// src/utils/call-context.ts
|
|
631
|
+
var CALL_CONTEXT_BRAND = Symbol("kitcore.callContext");
|
|
632
|
+
function isCallContext(value) {
|
|
633
|
+
return typeof value === "object" && value !== null && value[CALL_CONTEXT_BRAND] === true;
|
|
634
|
+
}
|
|
635
|
+
function generateCallId() {
|
|
636
|
+
try {
|
|
637
|
+
const webCrypto = globalThis.crypto;
|
|
638
|
+
if (webCrypto?.randomUUID) {
|
|
639
|
+
return webCrypto.randomUUID();
|
|
640
|
+
}
|
|
641
|
+
if (webCrypto?.getRandomValues) {
|
|
642
|
+
const bytes = webCrypto.getRandomValues(new Uint8Array(16));
|
|
643
|
+
const hex = Array.from(bytes, (byte, i) => {
|
|
644
|
+
const value = i === 6 ? byte & 15 | 64 : i === 8 ? byte & 63 | 128 : byte;
|
|
645
|
+
return value.toString(16).padStart(2, "0");
|
|
646
|
+
});
|
|
647
|
+
return [
|
|
648
|
+
hex.slice(0, 4).join(""),
|
|
649
|
+
hex.slice(4, 6).join(""),
|
|
650
|
+
hex.slice(6, 8).join(""),
|
|
651
|
+
hex.slice(8, 10).join(""),
|
|
652
|
+
hex.slice(10, 16).join("")
|
|
653
|
+
].join("-");
|
|
654
|
+
}
|
|
655
|
+
} catch {
|
|
656
|
+
}
|
|
657
|
+
return null;
|
|
658
|
+
}
|
|
659
|
+
function rootCallContext() {
|
|
660
|
+
return {
|
|
661
|
+
callId: generateCallId(),
|
|
662
|
+
depth: 0,
|
|
663
|
+
annotations: {},
|
|
664
|
+
[CALL_CONTEXT_BRAND]: true
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
function childCallContext(parent) {
|
|
668
|
+
return {
|
|
669
|
+
callId: parent.callId,
|
|
670
|
+
depth: parent.depth + 1,
|
|
671
|
+
annotations: {},
|
|
672
|
+
[CALL_CONTEXT_BRAND]: true
|
|
673
|
+
};
|
|
674
|
+
}
|
|
675
|
+
|
|
584
676
|
// src/utils/core-options.ts
|
|
585
677
|
function defaultLogDeprecation({
|
|
586
678
|
methodName,
|
|
@@ -599,6 +691,9 @@ function resolveCoreOptions(context) {
|
|
|
599
691
|
return context.core;
|
|
600
692
|
}
|
|
601
693
|
var INTERNAL_CALL = Symbol("kitcore.internalCall");
|
|
694
|
+
function resolveCallContext(secondArg) {
|
|
695
|
+
return isCallContext(secondArg) ? secondArg : rootCallContext();
|
|
696
|
+
}
|
|
602
697
|
function signalDeprecation(context, methodName, getDeprecation) {
|
|
603
698
|
if (isInsideObserver()) return;
|
|
604
699
|
const deprecation = getDeprecation?.();
|
|
@@ -628,14 +723,16 @@ function createFunction(coreFn, options) {
|
|
|
628
723
|
const functionName = name || coreFn.name;
|
|
629
724
|
const namedFunctions = {
|
|
630
725
|
[functionName]: async function(callOptions) {
|
|
631
|
-
|
|
726
|
+
const internal = arguments[1];
|
|
727
|
+
const context = resolveCallContext(internal);
|
|
728
|
+
if (!isCallContext(internal) && internal !== INTERNAL_CALL) {
|
|
632
729
|
signalDeprecation(sdk.context, functionName, getDeprecation);
|
|
633
730
|
}
|
|
634
731
|
return runInMethodScope(async () => {
|
|
635
732
|
const startTime = Date.now();
|
|
636
733
|
const normalizedOptions = callOptions ?? {};
|
|
637
734
|
const args = [normalizedOptions];
|
|
638
|
-
const depth = getCurrentDepth();
|
|
735
|
+
const depth = Math.max(context.depth, getCurrentDepth());
|
|
639
736
|
const hooks = isInsideObserver() ? void 0 : sdk.context.hooks;
|
|
640
737
|
const adaptError = resolveCoreOptions(sdk.context)?.adaptError;
|
|
641
738
|
hooks?.onMethodStart?.({
|
|
@@ -654,12 +751,15 @@ function createFunction(coreFn, options) {
|
|
|
654
751
|
adaptError
|
|
655
752
|
}
|
|
656
753
|
);
|
|
657
|
-
result = await coreFn(
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
754
|
+
result = await coreFn(
|
|
755
|
+
{
|
|
756
|
+
...normalizedOptions,
|
|
757
|
+
...validatedOptions
|
|
758
|
+
},
|
|
759
|
+
context
|
|
760
|
+
);
|
|
661
761
|
} else {
|
|
662
|
-
result = await coreFn(normalizedOptions);
|
|
762
|
+
result = await coreFn(normalizedOptions, context);
|
|
663
763
|
}
|
|
664
764
|
hooks?.onMethodEnd?.({
|
|
665
765
|
methodName: functionName,
|
|
@@ -689,17 +789,19 @@ function createFunction(coreFn, options) {
|
|
|
689
789
|
function createRawFunction(coreFn, options) {
|
|
690
790
|
const { sdk, name, schema, positional, getDeprecation } = options;
|
|
691
791
|
return function(rawInput) {
|
|
692
|
-
|
|
792
|
+
const internal = arguments[1];
|
|
793
|
+
const context = resolveCallContext(internal);
|
|
794
|
+
if (!isCallContext(internal) && internal !== INTERNAL_CALL) {
|
|
693
795
|
signalDeprecation(sdk.context, name, getDeprecation);
|
|
694
796
|
}
|
|
695
797
|
return runInMethodScope(() => {
|
|
696
798
|
const startTime = Date.now();
|
|
697
|
-
const depth = getCurrentDepth();
|
|
799
|
+
const depth = Math.max(context.depth, getCurrentDepth());
|
|
698
800
|
const hooks = isInsideObserver() ? void 0 : sdk.context.hooks;
|
|
699
801
|
const adaptError = resolveCoreOptions(sdk.context)?.adaptError;
|
|
700
802
|
const input = schema ? rawInput ?? {} : rawInput;
|
|
701
803
|
const record = input;
|
|
702
|
-
const args = positional ? positional.filter((
|
|
804
|
+
const args = positional ? positional.filter((key) => record?.[key] !== void 0).map((key) => record?.[key]) : [input];
|
|
703
805
|
hooks?.onMethodStart?.({
|
|
704
806
|
methodName: name,
|
|
705
807
|
args,
|
|
@@ -718,7 +820,7 @@ function createRawFunction(coreFn, options) {
|
|
|
718
820
|
};
|
|
719
821
|
try {
|
|
720
822
|
const parsed = schema ? validateOptions(schema, input, { adaptError }) : input;
|
|
721
|
-
const result = coreFn(parsed);
|
|
823
|
+
const result = coreFn(parsed, context);
|
|
722
824
|
if (result !== null && typeof result === "object" && typeof result.then === "function") {
|
|
723
825
|
return result.then(
|
|
724
826
|
(value) => {
|
|
@@ -757,9 +859,9 @@ function createPageFunction(coreFn, {
|
|
|
757
859
|
}) {
|
|
758
860
|
const functionName = coreFn.name + "Page";
|
|
759
861
|
const namedFunctions = {
|
|
760
|
-
[functionName]: async function(options) {
|
|
862
|
+
[functionName]: async function(options, callContext) {
|
|
761
863
|
try {
|
|
762
|
-
const response = await coreFn(options);
|
|
864
|
+
const response = await coreFn(options, callContext);
|
|
763
865
|
const page = adaptPage ? adaptPage(response) : response;
|
|
764
866
|
if (!isSdkPage(page)) {
|
|
765
867
|
throw new Error(
|
|
@@ -783,14 +885,16 @@ function createPaginatedFunction(coreFn, options) {
|
|
|
783
885
|
const functionName = name || coreFn.name;
|
|
784
886
|
const namedFunctions = {
|
|
785
887
|
[functionName]: function(callOptions) {
|
|
786
|
-
|
|
888
|
+
const internal = arguments[1];
|
|
889
|
+
const context = resolveCallContext(internal);
|
|
890
|
+
if (!isCallContext(internal) && internal !== INTERNAL_CALL) {
|
|
787
891
|
signalDeprecation(sdk.context, functionName, getDeprecation);
|
|
788
892
|
}
|
|
789
893
|
return runInMethodScope(() => {
|
|
790
894
|
const startTime = Date.now();
|
|
791
895
|
const normalizedOptions = callOptions ?? {};
|
|
792
896
|
const args = [normalizedOptions];
|
|
793
|
-
const depth = getCurrentDepth();
|
|
897
|
+
const depth = Math.max(context.depth, getCurrentDepth());
|
|
794
898
|
const hooks = isInsideObserver() ? void 0 : sdk.context.hooks;
|
|
795
899
|
const adaptError = resolveCoreOptions(sdk.context)?.adaptError;
|
|
796
900
|
hooks?.onMethodStart?.({
|
|
@@ -809,7 +913,11 @@ function createPaginatedFunction(coreFn, options) {
|
|
|
809
913
|
...validatedOptions,
|
|
810
914
|
pageSize
|
|
811
915
|
};
|
|
812
|
-
const iterator = paginate(
|
|
916
|
+
const iterator = paginate(
|
|
917
|
+
(pageOptions) => pageFunction(pageOptions, context),
|
|
918
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
919
|
+
optimizedOptions
|
|
920
|
+
);
|
|
813
921
|
const firstPagePromise = iterator.next().then((result) => {
|
|
814
922
|
if (result.done) {
|
|
815
923
|
throw new Error("Paginate should always iterate at least once");
|
|
@@ -846,6 +954,13 @@ function createPaginatedFunction(coreFn, options) {
|
|
|
846
954
|
[Symbol.asyncIterator]() {
|
|
847
955
|
return pageStream;
|
|
848
956
|
},
|
|
957
|
+
pages: function() {
|
|
958
|
+
return {
|
|
959
|
+
[Symbol.asyncIterator]() {
|
|
960
|
+
return pageStream;
|
|
961
|
+
}
|
|
962
|
+
};
|
|
963
|
+
},
|
|
849
964
|
items: function() {
|
|
850
965
|
return {
|
|
851
966
|
[Symbol.asyncIterator]: async function* () {
|
|
@@ -953,11 +1068,11 @@ var RESERVED_ROOT_KEYS = /* @__PURE__ */ new Set([
|
|
|
953
1068
|
"context",
|
|
954
1069
|
"getRegistry"
|
|
955
1070
|
]);
|
|
956
|
-
function hasOwn(obj,
|
|
957
|
-
return Object.prototype.hasOwnProperty.call(obj,
|
|
1071
|
+
function hasOwn(obj, key) {
|
|
1072
|
+
return Object.prototype.hasOwnProperty.call(obj, key);
|
|
958
1073
|
}
|
|
959
|
-
function setOwn(target,
|
|
960
|
-
Object.defineProperty(target,
|
|
1074
|
+
function setOwn(target, key, value) {
|
|
1075
|
+
Object.defineProperty(target, key, {
|
|
961
1076
|
value,
|
|
962
1077
|
enumerable: true,
|
|
963
1078
|
configurable: true,
|
|
@@ -969,31 +1084,31 @@ function checkCollisions(target, source, kind, callerLabel, override) {
|
|
|
969
1084
|
checkRootKeyCollisions(target, Object.keys(source), override, callerLabel);
|
|
970
1085
|
return;
|
|
971
1086
|
}
|
|
972
|
-
for (const
|
|
973
|
-
if (!override && hasOwn(target,
|
|
1087
|
+
for (const key of Object.keys(source)) {
|
|
1088
|
+
if (!override && hasOwn(target, key)) {
|
|
974
1089
|
throw new Error(
|
|
975
|
-
`${callerLabel}: duplicate ${kind} "${
|
|
1090
|
+
`${callerLabel}: duplicate ${kind} "${key}". If the override is intentional, pass { override: true } in the options.`
|
|
976
1091
|
);
|
|
977
1092
|
}
|
|
978
1093
|
}
|
|
979
1094
|
}
|
|
980
1095
|
function checkRootKeyCollisions(target, keys, override, callerLabel) {
|
|
981
|
-
for (const
|
|
982
|
-
if (RESERVED_ROOT_KEYS.has(
|
|
1096
|
+
for (const key of keys) {
|
|
1097
|
+
if (RESERVED_ROOT_KEYS.has(key)) {
|
|
983
1098
|
throw new Error(
|
|
984
|
-
`${callerLabel}: plugin attempted to register reserved root key "${
|
|
1099
|
+
`${callerLabel}: plugin attempted to register reserved root key "${key}". The SDK uses this key for its own accessor; rename the plugin's method.`
|
|
985
1100
|
);
|
|
986
1101
|
}
|
|
987
|
-
if (!override && hasOwn(target,
|
|
1102
|
+
if (!override && hasOwn(target, key)) {
|
|
988
1103
|
throw new Error(
|
|
989
|
-
`${callerLabel}: duplicate root key "${
|
|
1104
|
+
`${callerLabel}: duplicate root key "${key}". If the override is intentional, pass { override: true } in the options.`
|
|
990
1105
|
);
|
|
991
1106
|
}
|
|
992
1107
|
}
|
|
993
1108
|
}
|
|
994
1109
|
function applyOwnProperties(target, source) {
|
|
995
|
-
for (const
|
|
996
|
-
setOwn(target,
|
|
1110
|
+
for (const key of Object.keys(source)) {
|
|
1111
|
+
setOwn(target, key, source[key]);
|
|
997
1112
|
}
|
|
998
1113
|
}
|
|
999
1114
|
function createPluginAccumulator(initialProperties = {}, initialContext = {}) {
|
|
@@ -1211,7 +1326,6 @@ var LEAF_META_KEYS = [
|
|
|
1211
1326
|
"itemType",
|
|
1212
1327
|
"returnType",
|
|
1213
1328
|
"outputSchema",
|
|
1214
|
-
"inputParameters",
|
|
1215
1329
|
"packages",
|
|
1216
1330
|
"experimental",
|
|
1217
1331
|
"confirm",
|
|
@@ -1251,8 +1365,8 @@ function normalizeImports(deps) {
|
|
|
1251
1365
|
}
|
|
1252
1366
|
function collectLeafMeta(config) {
|
|
1253
1367
|
let meta;
|
|
1254
|
-
for (const
|
|
1255
|
-
if (config[
|
|
1368
|
+
for (const key of LEAF_META_KEYS) {
|
|
1369
|
+
if (config[key] !== void 0) (meta ?? (meta = {}))[key] = config[key];
|
|
1256
1370
|
}
|
|
1257
1371
|
return meta;
|
|
1258
1372
|
}
|
|
@@ -1335,7 +1449,8 @@ function defineResolver(config) {
|
|
|
1335
1449
|
type: "object",
|
|
1336
1450
|
properties: config.properties,
|
|
1337
1451
|
definitions: config.definitions,
|
|
1338
|
-
getProperties: config.getProperties
|
|
1452
|
+
getProperties: config.getProperties,
|
|
1453
|
+
additionalKeys: config.additionalKeys
|
|
1339
1454
|
};
|
|
1340
1455
|
case "array":
|
|
1341
1456
|
return {
|
|
@@ -1647,7 +1762,7 @@ function normalizeFormatter(entry, sdk) {
|
|
|
1647
1762
|
const legacy = entry.meta?.formatter;
|
|
1648
1763
|
return legacy ? adaptLegacyFormatter(legacy, sdk) : void 0;
|
|
1649
1764
|
}
|
|
1650
|
-
function
|
|
1765
|
+
function normalizeResolvers(entry) {
|
|
1651
1766
|
if (entry.pluginType !== "method") return void 0;
|
|
1652
1767
|
return entry.resolvers;
|
|
1653
1768
|
}
|
|
@@ -1688,17 +1803,20 @@ function collectSurfaceProjection(context, formatterSdk) {
|
|
|
1688
1803
|
foldDynamicMembers(entry, surfaceBindings, meta);
|
|
1689
1804
|
}
|
|
1690
1805
|
const formatters = {};
|
|
1691
|
-
const
|
|
1806
|
+
const resolvers = {};
|
|
1692
1807
|
const positional = {};
|
|
1808
|
+
const skipInputValidation = {};
|
|
1693
1809
|
for (const [binding, entry] of Object.entries(entries)) {
|
|
1694
1810
|
const f = normalizeFormatter(entry, formatterSdk);
|
|
1695
1811
|
if (f) formatters[binding] = f;
|
|
1696
|
-
const r =
|
|
1697
|
-
if (r)
|
|
1812
|
+
const r = normalizeResolvers(entry);
|
|
1813
|
+
if (r) resolvers[binding] = r;
|
|
1698
1814
|
const p = methodPositional(entry);
|
|
1699
1815
|
if (p) positional[binding] = p;
|
|
1816
|
+
if (entry.pluginType === "method" && entry.skipInputValidation)
|
|
1817
|
+
skipInputValidation[binding] = true;
|
|
1700
1818
|
}
|
|
1701
|
-
return { meta, formatters,
|
|
1819
|
+
return { meta, formatters, resolvers, positional, skipInputValidation };
|
|
1702
1820
|
}
|
|
1703
1821
|
function buildSurfaceRegistry(context, packageFilter) {
|
|
1704
1822
|
const surface = {};
|
|
@@ -1755,6 +1873,11 @@ function nestedResolvers(resolver) {
|
|
|
1755
1873
|
for (const field of Object.values(resolver.properties ?? {})) {
|
|
1756
1874
|
if (!isResolverRef(field.resolver)) out.push(field.resolver);
|
|
1757
1875
|
}
|
|
1876
|
+
const ak = resolver.additionalKeys;
|
|
1877
|
+
if (ak) {
|
|
1878
|
+
if (!isResolverRef(ak.values)) out.push(ak.values);
|
|
1879
|
+
if (ak.keys && !isResolverRef(ak.keys)) out.push(ak.keys);
|
|
1880
|
+
}
|
|
1758
1881
|
out.push(...Object.values(resolver.definitions ?? {}));
|
|
1759
1882
|
} else if (resolver.type === "array") {
|
|
1760
1883
|
if (!isResolverRef(resolver.items)) out.push(resolver.items);
|
|
@@ -1879,16 +2002,16 @@ function collectPlugins(root, materialized = /* @__PURE__ */ new Set(), configur
|
|
|
1879
2002
|
}
|
|
1880
2003
|
return byId;
|
|
1881
2004
|
}
|
|
1882
|
-
function bindValue(target,
|
|
2005
|
+
function bindValue(target, key, entry, callType = "surface", ctx) {
|
|
1883
2006
|
if (entry.pluginType === "property" && entry.getValue) {
|
|
1884
|
-
Object.defineProperty(target,
|
|
2007
|
+
Object.defineProperty(target, key, {
|
|
1885
2008
|
get: entry.getValue,
|
|
1886
2009
|
enumerable: true,
|
|
1887
2010
|
configurable: true
|
|
1888
2011
|
});
|
|
1889
2012
|
} else {
|
|
1890
|
-
const value = callType === "internal" && entry.pluginType === "method" ? entry.internalValue ?? entry.value : entry.value;
|
|
1891
|
-
Object.defineProperty(target,
|
|
2013
|
+
const value = callType === "internal" && entry.pluginType === "method" ? entry.bindInternal?.(ctx) ?? entry.internalValue ?? entry.value : entry.value;
|
|
2014
|
+
Object.defineProperty(target, key, {
|
|
1892
2015
|
value,
|
|
1893
2016
|
writable: true,
|
|
1894
2017
|
enumerable: true,
|
|
@@ -1905,7 +2028,7 @@ function buildSurface(context, ...maps) {
|
|
|
1905
2028
|
sdk[CONTEXT] = context;
|
|
1906
2029
|
return sdk;
|
|
1907
2030
|
}
|
|
1908
|
-
function buildImports(plugins, importBindings) {
|
|
2031
|
+
function buildImports(plugins, importBindings, ctx) {
|
|
1909
2032
|
const imports = {};
|
|
1910
2033
|
for (const { binding, id, optional } of importBindings) {
|
|
1911
2034
|
const entry = plugins[id];
|
|
@@ -1918,7 +2041,7 @@ function buildImports(plugins, importBindings) {
|
|
|
1918
2041
|
});
|
|
1919
2042
|
continue;
|
|
1920
2043
|
}
|
|
1921
|
-
bindValue(imports, binding, entry, "internal");
|
|
2044
|
+
bindValue(imports, binding, entry, "internal", ctx);
|
|
1922
2045
|
}
|
|
1923
2046
|
return imports;
|
|
1924
2047
|
}
|
|
@@ -1997,6 +2120,19 @@ function bindResolver(resolver, plugins) {
|
|
|
1997
2120
|
const { getProperties } = resolver;
|
|
1998
2121
|
if (getProperties)
|
|
1999
2122
|
bound.getProperties = ({ input }) => getProperties({ imports, input });
|
|
2123
|
+
if (resolver.additionalKeys) {
|
|
2124
|
+
const ak = resolver.additionalKeys;
|
|
2125
|
+
const boundAk = {
|
|
2126
|
+
values: isResolverRef(ak.values) ? ak.values : bindResolver(ak.values, plugins),
|
|
2127
|
+
minEntries: ak.minEntries,
|
|
2128
|
+
maxEntries: ak.maxEntries,
|
|
2129
|
+
keyValueType: ak.keyValueType,
|
|
2130
|
+
valueValueType: ak.valueValueType
|
|
2131
|
+
};
|
|
2132
|
+
if (ak.keys)
|
|
2133
|
+
boundAk.keys = isResolverRef(ak.keys) ? ak.keys : bindResolver(ak.keys, plugins);
|
|
2134
|
+
bound.additionalKeys = boundAk;
|
|
2135
|
+
}
|
|
2000
2136
|
return bound;
|
|
2001
2137
|
}
|
|
2002
2138
|
case "array": {
|
|
@@ -2052,8 +2188,8 @@ function bindResolver(resolver, plugins) {
|
|
|
2052
2188
|
}
|
|
2053
2189
|
function bindFields(fields, plugins) {
|
|
2054
2190
|
const out = {};
|
|
2055
|
-
for (const [
|
|
2056
|
-
out[
|
|
2191
|
+
for (const [key, field] of Object.entries(fields)) {
|
|
2192
|
+
out[key] = {
|
|
2057
2193
|
...field,
|
|
2058
2194
|
resolver: isResolverRef(field.resolver) ? field.resolver : bindResolver(field.resolver, plugins)
|
|
2059
2195
|
};
|
|
@@ -2062,8 +2198,8 @@ function bindFields(fields, plugins) {
|
|
|
2062
2198
|
}
|
|
2063
2199
|
function bindDefinitions(definitions, plugins) {
|
|
2064
2200
|
const out = {};
|
|
2065
|
-
for (const [
|
|
2066
|
-
out[
|
|
2201
|
+
for (const [key, def] of Object.entries(definitions)) {
|
|
2202
|
+
out[key] = bindResolver(def, plugins);
|
|
2067
2203
|
}
|
|
2068
2204
|
return out;
|
|
2069
2205
|
}
|
|
@@ -2148,6 +2284,7 @@ function buildMethodEntries(descriptors, context, states) {
|
|
|
2148
2284
|
name: descriptor.name,
|
|
2149
2285
|
chain: [],
|
|
2150
2286
|
inputSchema: descriptor.inputSchema,
|
|
2287
|
+
skipInputValidation: descriptor.skipInputValidation,
|
|
2151
2288
|
// Derive the presentation type from the output mode when the author did
|
|
2152
2289
|
// not set one; an explicit meta.type (e.g. "create") still wins.
|
|
2153
2290
|
meta: out.type === "raw" || descriptor.meta?.type ? descriptor.meta : { ...descriptor.meta, type: out.type },
|
|
@@ -2155,17 +2292,17 @@ function buildMethodEntries(descriptors, context, states) {
|
|
|
2155
2292
|
// Replaced below; never called.
|
|
2156
2293
|
value: () => void 0
|
|
2157
2294
|
};
|
|
2158
|
-
const callRun = (input) => descriptor.run({
|
|
2159
|
-
imports: buildImports(plugins, descriptor.importBindings),
|
|
2295
|
+
const callRun = (input, ctx) => descriptor.run({
|
|
2296
|
+
imports: buildImports(plugins, descriptor.importBindings, ctx),
|
|
2160
2297
|
state: states.get(id),
|
|
2161
2298
|
input
|
|
2162
2299
|
});
|
|
2163
|
-
const fold = (coreFn) => (input) => {
|
|
2164
|
-
let next = coreFn;
|
|
2300
|
+
const fold = (coreFn) => (input, ctx) => {
|
|
2301
|
+
let next = (i) => coreFn(i, ctx);
|
|
2165
2302
|
for (const wrap of entry.chain) {
|
|
2166
2303
|
const inner = next;
|
|
2167
2304
|
next = (i) => wrap.run({
|
|
2168
|
-
imports: buildImports(plugins, wrap.owner.importBindings),
|
|
2305
|
+
imports: buildImports(plugins, wrap.owner.importBindings, ctx),
|
|
2169
2306
|
next: inner,
|
|
2170
2307
|
input: i,
|
|
2171
2308
|
// Overwritten by the chain item's own closure with the owning
|
|
@@ -2189,7 +2326,7 @@ function buildMethodEntries(descriptors, context, states) {
|
|
|
2189
2326
|
}
|
|
2190
2327
|
);
|
|
2191
2328
|
} else if (out.type === "item") {
|
|
2192
|
-
const itemCore = async (input) => callRun(input);
|
|
2329
|
+
const itemCore = async (input, ctx) => callRun(input, ctx);
|
|
2193
2330
|
entry.value = createFunction(
|
|
2194
2331
|
fold(itemCore),
|
|
2195
2332
|
{
|
|
@@ -2201,7 +2338,7 @@ function buildMethodEntries(descriptors, context, states) {
|
|
|
2201
2338
|
);
|
|
2202
2339
|
} else {
|
|
2203
2340
|
entry.value = createRawFunction(
|
|
2204
|
-
(input) => fold(callRun)(input),
|
|
2341
|
+
(input, ctx) => fold(callRun)(input, ctx),
|
|
2205
2342
|
{
|
|
2206
2343
|
sdk,
|
|
2207
2344
|
name: descriptor.name,
|
|
@@ -2224,11 +2361,15 @@ function buildMethodEntries(descriptors, context, states) {
|
|
|
2224
2361
|
});
|
|
2225
2362
|
return packed;
|
|
2226
2363
|
};
|
|
2364
|
+
const internalValue = (...args) => canonicalValue(pack(args), INTERNAL_CALL);
|
|
2227
2365
|
entry.value = (...args) => canonicalValue(pack(args));
|
|
2228
|
-
entry.internalValue =
|
|
2366
|
+
entry.internalValue = internalValue;
|
|
2367
|
+
entry.bindInternal = (ctx) => ctx ? (...args) => canonicalValue(pack(args), childCallContext(ctx)) : internalValue;
|
|
2229
2368
|
entry.positional = names;
|
|
2230
2369
|
} else {
|
|
2231
|
-
|
|
2370
|
+
const internalValue = (input) => canonicalValue(input, INTERNAL_CALL);
|
|
2371
|
+
entry.internalValue = internalValue;
|
|
2372
|
+
entry.bindInternal = (ctx) => ctx ? (input) => canonicalValue(input, childCallContext(ctx)) : internalValue;
|
|
2232
2373
|
}
|
|
2233
2374
|
plugins[id] = entry;
|
|
2234
2375
|
}
|
|
@@ -2468,7 +2609,7 @@ function createSdk(root, options) {
|
|
|
2468
2609
|
pluginSurface = {};
|
|
2469
2610
|
bindValue(pluginSurface, plugin.name, plugins2[plugin.id]);
|
|
2470
2611
|
}
|
|
2471
|
-
for (const
|
|
2612
|
+
for (const key of Object.keys(legacyExports)) context.surface[key] = key;
|
|
2472
2613
|
if (plugin.pluginType === "aggregate") {
|
|
2473
2614
|
recordExportSurface(context, plugin.exports);
|
|
2474
2615
|
} else {
|
|
@@ -2607,6 +2748,7 @@ function valueTypeOf(inner) {
|
|
|
2607
2748
|
if (inner instanceof z3.ZodEnum) return "string";
|
|
2608
2749
|
if (inner instanceof z3.ZodArray) return "array";
|
|
2609
2750
|
if (inner instanceof z3.ZodObject) return "object";
|
|
2751
|
+
if (inner instanceof z3.ZodRecord) return "object";
|
|
2610
2752
|
return void 0;
|
|
2611
2753
|
}
|
|
2612
2754
|
function staticChoicesOf(inner) {
|
|
@@ -2617,7 +2759,8 @@ function staticChoicesOf(inner) {
|
|
|
2617
2759
|
return void 0;
|
|
2618
2760
|
}
|
|
2619
2761
|
function objectShape(schema) {
|
|
2620
|
-
const
|
|
2762
|
+
const canonical = canonicalInputSchema(schema);
|
|
2763
|
+
const { inner } = canonical ? unwrap(canonical) : { inner: void 0 };
|
|
2621
2764
|
if (inner instanceof z3.ZodObject) {
|
|
2622
2765
|
return inner.shape;
|
|
2623
2766
|
}
|
|
@@ -2639,7 +2782,7 @@ function topoOrder2(specs) {
|
|
|
2639
2782
|
}
|
|
2640
2783
|
function planParameters(entry) {
|
|
2641
2784
|
const shape = objectShape(entry.inputSchema);
|
|
2642
|
-
const resolvers = entry.
|
|
2785
|
+
const resolvers = entry.resolvers ?? {};
|
|
2643
2786
|
const names = shape ? [
|
|
2644
2787
|
...Object.keys(shape),
|
|
2645
2788
|
...Object.keys(resolvers).filter(
|
|
@@ -2677,24 +2820,48 @@ function getAtPath(root, path) {
|
|
|
2677
2820
|
}
|
|
2678
2821
|
return node;
|
|
2679
2822
|
}
|
|
2823
|
+
function defineOwn(node, key, value) {
|
|
2824
|
+
Object.defineProperty(node, key, {
|
|
2825
|
+
value,
|
|
2826
|
+
writable: true,
|
|
2827
|
+
enumerable: true,
|
|
2828
|
+
configurable: true
|
|
2829
|
+
});
|
|
2830
|
+
}
|
|
2680
2831
|
function setAtPath(root, path, value) {
|
|
2681
2832
|
let node = root;
|
|
2682
2833
|
for (let i = 0; i < path.length - 1; i++) {
|
|
2683
2834
|
const seg = path[i];
|
|
2684
|
-
|
|
2685
|
-
|
|
2835
|
+
const existing = Object.prototype.hasOwnProperty.call(node, seg) ? node[seg] : void 0;
|
|
2836
|
+
if (existing != null && typeof existing === "object") {
|
|
2837
|
+
node = existing;
|
|
2838
|
+
} else {
|
|
2839
|
+
const child = {};
|
|
2840
|
+
defineOwn(node, seg, child);
|
|
2841
|
+
node = child;
|
|
2842
|
+
}
|
|
2686
2843
|
}
|
|
2687
|
-
node
|
|
2844
|
+
defineOwn(node, path[path.length - 1], value);
|
|
2688
2845
|
}
|
|
2689
|
-
var
|
|
2846
|
+
var SAFE_SEGMENT = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
2847
|
+
var pathToKey = (path) => {
|
|
2848
|
+
let out = "";
|
|
2849
|
+
for (const segment of path) {
|
|
2850
|
+
if (typeof segment === "number") out += `[${segment}]`;
|
|
2851
|
+
else if (SAFE_SEGMENT.test(segment))
|
|
2852
|
+
out += out === "" ? segment : `.${segment}`;
|
|
2853
|
+
else out += `[${JSON.stringify(segment)}]`;
|
|
2854
|
+
}
|
|
2855
|
+
return out;
|
|
2856
|
+
};
|
|
2690
2857
|
function isSettled(state, path) {
|
|
2691
|
-
return state.settled.includes(
|
|
2858
|
+
return state.settled.includes(pathToKey(path));
|
|
2692
2859
|
}
|
|
2693
2860
|
function remember(state, k) {
|
|
2694
2861
|
if (!state.settled.includes(k)) state.settled.push(k);
|
|
2695
2862
|
}
|
|
2696
2863
|
function settle(state, path) {
|
|
2697
|
-
remember(state,
|
|
2864
|
+
remember(state, pathToKey(path));
|
|
2698
2865
|
}
|
|
2699
2866
|
function clone(state) {
|
|
2700
2867
|
return JSON.parse(JSON.stringify(state));
|
|
@@ -2709,6 +2876,28 @@ function coerce(leaf, raw) {
|
|
|
2709
2876
|
if (raw === "true") return true;
|
|
2710
2877
|
if (raw === "false") return false;
|
|
2711
2878
|
}
|
|
2879
|
+
if (leaf.valueType === "object") {
|
|
2880
|
+
const trimmed = raw.trim();
|
|
2881
|
+
if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
|
|
2882
|
+
try {
|
|
2883
|
+
return JSON.parse(trimmed);
|
|
2884
|
+
} catch {
|
|
2885
|
+
return raw;
|
|
2886
|
+
}
|
|
2887
|
+
}
|
|
2888
|
+
return raw;
|
|
2889
|
+
}
|
|
2890
|
+
if (leaf.valueType === "array") {
|
|
2891
|
+
const trimmed = raw.trim();
|
|
2892
|
+
if (trimmed.startsWith("[")) {
|
|
2893
|
+
try {
|
|
2894
|
+
return JSON.parse(trimmed);
|
|
2895
|
+
} catch {
|
|
2896
|
+
return raw;
|
|
2897
|
+
}
|
|
2898
|
+
}
|
|
2899
|
+
return raw;
|
|
2900
|
+
}
|
|
2712
2901
|
return raw;
|
|
2713
2902
|
}
|
|
2714
2903
|
async function validationError(leaf, value, state) {
|
|
@@ -2766,27 +2955,6 @@ async function objectChildren(resolver, input) {
|
|
|
2766
2955
|
return toLeaf(name, field, resolver.definitions);
|
|
2767
2956
|
});
|
|
2768
2957
|
}
|
|
2769
|
-
function arrayItem(resolver) {
|
|
2770
|
-
const items = resolver.items;
|
|
2771
|
-
const valueType = resolver.itemValueType;
|
|
2772
|
-
if (isRef(items)) {
|
|
2773
|
-
return {
|
|
2774
|
-
name: "",
|
|
2775
|
-
required: true,
|
|
2776
|
-
resolver: resolver.definitions?.[items.ref],
|
|
2777
|
-
extraInput: items.input,
|
|
2778
|
-
valueType,
|
|
2779
|
-
requires: []
|
|
2780
|
-
};
|
|
2781
|
-
}
|
|
2782
|
-
return {
|
|
2783
|
-
name: "",
|
|
2784
|
-
required: true,
|
|
2785
|
-
resolver: items,
|
|
2786
|
-
valueType,
|
|
2787
|
-
requires: []
|
|
2788
|
-
};
|
|
2789
|
-
}
|
|
2790
2958
|
function autoSettles(resolver) {
|
|
2791
2959
|
return resolver.type === "constant" || resolver.type === "info";
|
|
2792
2960
|
}
|
|
@@ -2806,10 +2974,28 @@ async function leafAt(ctx, path, resolved) {
|
|
|
2806
2974
|
const seg = path[i];
|
|
2807
2975
|
if (typeof seg === "number") {
|
|
2808
2976
|
if (leaf?.resolver?.type !== "array") return void 0;
|
|
2809
|
-
leaf =
|
|
2977
|
+
leaf = boundLeaf(
|
|
2978
|
+
"",
|
|
2979
|
+
leaf.resolver.items,
|
|
2980
|
+
leaf.resolver.definitions,
|
|
2981
|
+
leaf.resolver.itemValueType
|
|
2982
|
+
);
|
|
2810
2983
|
} else {
|
|
2811
|
-
|
|
2812
|
-
|
|
2984
|
+
const parent = leaf;
|
|
2985
|
+
const found = children.find((c) => c.name === seg);
|
|
2986
|
+
if (found) {
|
|
2987
|
+
leaf = found;
|
|
2988
|
+
} else if (parent?.resolver?.type === "object" && parent.resolver.additionalKeys) {
|
|
2989
|
+
const ak = parent.resolver.additionalKeys;
|
|
2990
|
+
leaf = boundLeaf(
|
|
2991
|
+
String(seg),
|
|
2992
|
+
ak.values,
|
|
2993
|
+
parent.resolver.definitions,
|
|
2994
|
+
ak.valueValueType
|
|
2995
|
+
);
|
|
2996
|
+
} else {
|
|
2997
|
+
return void 0;
|
|
2998
|
+
}
|
|
2813
2999
|
}
|
|
2814
3000
|
if (i < path.length - 1 && typeof path[i + 1] === "string") {
|
|
2815
3001
|
if (leaf?.resolver?.type !== "object") return void 0;
|
|
@@ -2821,16 +3007,81 @@ async function leafAt(ctx, path, resolved) {
|
|
|
2821
3007
|
}
|
|
2822
3008
|
return leaf;
|
|
2823
3009
|
}
|
|
3010
|
+
function boundLeaf(name, resolverOrRef, definitions, valueType) {
|
|
3011
|
+
if (isRef(resolverOrRef)) {
|
|
3012
|
+
return {
|
|
3013
|
+
name,
|
|
3014
|
+
required: true,
|
|
3015
|
+
resolver: definitions?.[resolverOrRef.ref],
|
|
3016
|
+
extraInput: resolverOrRef.input,
|
|
3017
|
+
valueType,
|
|
3018
|
+
requires: []
|
|
3019
|
+
};
|
|
3020
|
+
}
|
|
3021
|
+
return {
|
|
3022
|
+
name,
|
|
3023
|
+
required: true,
|
|
3024
|
+
resolver: resolverOrRef,
|
|
3025
|
+
valueType,
|
|
3026
|
+
requires: []
|
|
3027
|
+
};
|
|
3028
|
+
}
|
|
3029
|
+
async function recordInfoAt(ctx, path, resolved) {
|
|
3030
|
+
const leaf = await leafAt(ctx, path, resolved);
|
|
3031
|
+
const resolver = leaf?.resolver;
|
|
3032
|
+
if (resolver?.type !== "object" || !resolver.additionalKeys) {
|
|
3033
|
+
throw new Error(
|
|
3034
|
+
`expected an object resolver with additionalKeys at "${pathToKey(path)}"`
|
|
3035
|
+
);
|
|
3036
|
+
}
|
|
3037
|
+
if (resolver.getProperties) {
|
|
3038
|
+
throw new Error(
|
|
3039
|
+
`object resolver at "${pathToKey(path)}" cannot combine getProperties with additionalKeys`
|
|
3040
|
+
);
|
|
3041
|
+
}
|
|
3042
|
+
const ak = resolver.additionalKeys;
|
|
3043
|
+
const defs = resolver.definitions;
|
|
3044
|
+
const keyLeaf = ak.keys ? boundLeaf("key", ak.keys, defs, ak.keyValueType ?? "string") : {
|
|
3045
|
+
name: "key",
|
|
3046
|
+
required: true,
|
|
3047
|
+
resolver: { type: "static", inputType: "text" },
|
|
3048
|
+
valueType: "string",
|
|
3049
|
+
requires: []
|
|
3050
|
+
};
|
|
3051
|
+
const valueLeaf = boundLeaf("value", ak.values, defs, ak.valueValueType);
|
|
3052
|
+
if (keyLeaf.resolver && keyLeaf.resolver.type !== "static") {
|
|
3053
|
+
throw new Error(
|
|
3054
|
+
`record key resolver at "${pathToKey(path)}" must be a static free-text prompt, not "${keyLeaf.resolver.type}"`
|
|
3055
|
+
);
|
|
3056
|
+
}
|
|
3057
|
+
if (valueLeaf.resolver?.type === "object" || valueLeaf.resolver?.type === "array") {
|
|
3058
|
+
throw new Error(
|
|
3059
|
+
`record value resolver at "${pathToKey(path)}" must be a single value, not "${valueLeaf.resolver.type}"`
|
|
3060
|
+
);
|
|
3061
|
+
}
|
|
3062
|
+
return {
|
|
3063
|
+
min: ak.minEntries ?? 0,
|
|
3064
|
+
max: ak.maxEntries ?? Infinity,
|
|
3065
|
+
keyLeaf,
|
|
3066
|
+
valueLeaf,
|
|
3067
|
+
fixedKeys: Object.keys(resolver.properties ?? {})
|
|
3068
|
+
};
|
|
3069
|
+
}
|
|
2824
3070
|
async function arrayInfoAt(ctx, path, resolved) {
|
|
2825
3071
|
const leaf = await leafAt(ctx, path, resolved);
|
|
2826
3072
|
const resolver = leaf?.resolver;
|
|
2827
3073
|
if (resolver?.type !== "array") {
|
|
2828
|
-
throw new Error(`expected an array resolver at "${
|
|
3074
|
+
throw new Error(`expected an array resolver at "${pathToKey(path)}"`);
|
|
2829
3075
|
}
|
|
2830
3076
|
return {
|
|
2831
3077
|
min: resolver.minItems ?? 0,
|
|
2832
3078
|
max: resolver.maxItems ?? Infinity,
|
|
2833
|
-
item:
|
|
3079
|
+
item: boundLeaf(
|
|
3080
|
+
String(path[path.length - 1]),
|
|
3081
|
+
resolver.items,
|
|
3082
|
+
resolver.definitions,
|
|
3083
|
+
resolver.itemValueType
|
|
3084
|
+
)
|
|
2834
3085
|
};
|
|
2835
3086
|
}
|
|
2836
3087
|
async function firstPage(result) {
|
|
@@ -2911,6 +3162,9 @@ var AFFORDANCE = {
|
|
|
2911
3162
|
retry: { action: "retry", description: "Retry loading the options" },
|
|
2912
3163
|
cancel: { action: "cancel", description: "Cancel resolution" }
|
|
2913
3164
|
};
|
|
3165
|
+
function affordance(base, description) {
|
|
3166
|
+
return { ...base, description };
|
|
3167
|
+
}
|
|
2914
3168
|
function selectActions(leaf, page, multiple) {
|
|
2915
3169
|
const searchMode = leaf.resolver?.type === "dynamic" && leaf.resolver.inputType === "search";
|
|
2916
3170
|
if (searchMode && page.position.search === void 0 && page.items.length === 0) {
|
|
@@ -3025,7 +3279,7 @@ async function buildQuestion(leaf, path, input) {
|
|
|
3025
3279
|
}
|
|
3026
3280
|
};
|
|
3027
3281
|
}
|
|
3028
|
-
function
|
|
3282
|
+
function arrayItemsQuestion(t) {
|
|
3029
3283
|
const actions = [AFFORDANCE.add];
|
|
3030
3284
|
if (t.count >= t.min) actions.push(AFFORDANCE.done);
|
|
3031
3285
|
return {
|
|
@@ -3041,22 +3295,37 @@ function collectionQuestion(t) {
|
|
|
3041
3295
|
actions
|
|
3042
3296
|
};
|
|
3043
3297
|
}
|
|
3044
|
-
function
|
|
3298
|
+
function recordEntriesQuestion(t) {
|
|
3299
|
+
const actions = [
|
|
3300
|
+
affordance(AFFORDANCE.add, "Add another entry")
|
|
3301
|
+
];
|
|
3302
|
+
if (t.count >= t.min) {
|
|
3303
|
+
actions.push(affordance(AFFORDANCE.done, "Finish the entries"));
|
|
3304
|
+
}
|
|
3305
|
+
return {
|
|
3306
|
+
type: "collection",
|
|
3307
|
+
path: t.path,
|
|
3308
|
+
message: `Add another ${t.path[t.path.length - 1]} entry? (${t.count} so far)`,
|
|
3309
|
+
container: "record",
|
|
3310
|
+
count: t.count,
|
|
3311
|
+
min: t.min,
|
|
3312
|
+
...Number.isFinite(t.max) ? { max: t.max } : {},
|
|
3313
|
+
actions
|
|
3314
|
+
};
|
|
3315
|
+
}
|
|
3316
|
+
function objectOptionalQuestion(path) {
|
|
3045
3317
|
return {
|
|
3046
3318
|
type: "collection",
|
|
3047
3319
|
path,
|
|
3048
3320
|
message: `Add ${path[path.length - 1]}?`,
|
|
3049
3321
|
container: "object",
|
|
3050
3322
|
actions: [
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
description: "Provide values for these fields"
|
|
3054
|
-
},
|
|
3055
|
-
{ action: "done", description: "Skip these fields" }
|
|
3323
|
+
affordance(AFFORDANCE.add, "Provide values for these fields"),
|
|
3324
|
+
affordance(AFFORDANCE.done, "Skip these fields")
|
|
3056
3325
|
]
|
|
3057
3326
|
};
|
|
3058
3327
|
}
|
|
3059
|
-
function
|
|
3328
|
+
function objectOptionalPropertiesQuestion(path, pending) {
|
|
3060
3329
|
return {
|
|
3061
3330
|
type: "collection",
|
|
3062
3331
|
path,
|
|
@@ -3073,8 +3342,8 @@ function optionalsGateQuestion(path, pending) {
|
|
|
3073
3342
|
...leaf.valueType ? { valueType: leaf.valueType } : {}
|
|
3074
3343
|
})),
|
|
3075
3344
|
actions: [
|
|
3076
|
-
|
|
3077
|
-
|
|
3345
|
+
affordance(AFFORDANCE.add, "Configure the optional fields"),
|
|
3346
|
+
affordance(AFFORDANCE.done, "Skip the optional fields")
|
|
3078
3347
|
]
|
|
3079
3348
|
};
|
|
3080
3349
|
}
|
|
@@ -3092,7 +3361,8 @@ function finalize(ctx, resolved) {
|
|
|
3092
3361
|
}));
|
|
3093
3362
|
return { status: "invalid", issues };
|
|
3094
3363
|
}
|
|
3095
|
-
var optionalsMarker = (path) => `${
|
|
3364
|
+
var optionalsMarker = (path) => `${pathToKey(path)}?optionals`;
|
|
3365
|
+
var UNSAFE_RECORD_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
3096
3366
|
async function findInArray(ctx, state, path) {
|
|
3097
3367
|
if (isSettled(state, path)) return null;
|
|
3098
3368
|
if (getAtPath(state.resolved, path) == null)
|
|
@@ -3113,7 +3383,28 @@ async function findInArray(ctx, state, path) {
|
|
|
3113
3383
|
}
|
|
3114
3384
|
}
|
|
3115
3385
|
if (len < min) return descendItem(ctx, state, path, len, item);
|
|
3116
|
-
if (len < max) return {
|
|
3386
|
+
if (len < max) return { type: "array_items", path, count: len, min, max };
|
|
3387
|
+
settle(state, path);
|
|
3388
|
+
return null;
|
|
3389
|
+
}
|
|
3390
|
+
async function findInRecord(ctx, state, path) {
|
|
3391
|
+
if (isSettled(state, path)) return null;
|
|
3392
|
+
if (getAtPath(state.resolved, path) == null)
|
|
3393
|
+
setAtPath(state.resolved, path, {});
|
|
3394
|
+
if (!state.interactive) {
|
|
3395
|
+
settle(state, path);
|
|
3396
|
+
return null;
|
|
3397
|
+
}
|
|
3398
|
+
const { min, max, keyLeaf, fixedKeys } = await recordInfoAt(
|
|
3399
|
+
ctx,
|
|
3400
|
+
path,
|
|
3401
|
+
state.resolved
|
|
3402
|
+
);
|
|
3403
|
+
const container = getAtPath(state.resolved, path);
|
|
3404
|
+
const fixed = new Set(fixedKeys);
|
|
3405
|
+
const count = Object.keys(container).filter((k) => !fixed.has(k)).length;
|
|
3406
|
+
if (count < min) return { type: "record_key", path, leaf: keyLeaf };
|
|
3407
|
+
if (count < max) return { type: "record_entries", path, count, min, max };
|
|
3117
3408
|
settle(state, path);
|
|
3118
3409
|
return null;
|
|
3119
3410
|
}
|
|
@@ -3131,10 +3422,10 @@ function seedItemSlot(state, itemPath, item) {
|
|
|
3131
3422
|
}
|
|
3132
3423
|
async function descendItem(ctx, state, arrayPath, index, item) {
|
|
3133
3424
|
const itemPath = [...arrayPath, index];
|
|
3134
|
-
const
|
|
3135
|
-
if (
|
|
3136
|
-
if (
|
|
3137
|
-
return {
|
|
3425
|
+
const slotType = seedItemSlot(state, itemPath, item);
|
|
3426
|
+
if (slotType === "object") return findNext(ctx, state, itemPath);
|
|
3427
|
+
if (slotType === "array") return findInArray(ctx, state, itemPath);
|
|
3428
|
+
return { type: "leaf", path: itemPath, leaf: item };
|
|
3138
3429
|
}
|
|
3139
3430
|
async function findNext(ctx, state, path = []) {
|
|
3140
3431
|
const container = getAtPath(state.resolved, path) ?? {};
|
|
@@ -3159,7 +3450,7 @@ async function findNext(ctx, state, path = []) {
|
|
|
3159
3450
|
const pending = ordered.filter(
|
|
3160
3451
|
(c) => !c.required && asksUser(c) && isPendingChild(c)
|
|
3161
3452
|
);
|
|
3162
|
-
return {
|
|
3453
|
+
return { type: "object_optional_properties", path, pending };
|
|
3163
3454
|
}
|
|
3164
3455
|
if (leaf.resolver?.type === "object") {
|
|
3165
3456
|
if (isSettled(state, childPath)) continue;
|
|
@@ -3169,7 +3460,7 @@ async function findNext(ctx, state, path = []) {
|
|
|
3169
3460
|
settle(state, childPath);
|
|
3170
3461
|
continue;
|
|
3171
3462
|
}
|
|
3172
|
-
return {
|
|
3463
|
+
return { type: "object_optional", path: childPath, leaf };
|
|
3173
3464
|
}
|
|
3174
3465
|
setAtPath(state.resolved, childPath, {});
|
|
3175
3466
|
}
|
|
@@ -3201,13 +3492,21 @@ async function findNext(ctx, state, path = []) {
|
|
|
3201
3492
|
}
|
|
3202
3493
|
if (container[leaf.name] !== void 0 || isSettled(state, childPath))
|
|
3203
3494
|
continue;
|
|
3204
|
-
return {
|
|
3495
|
+
return { type: "leaf", path: childPath, leaf };
|
|
3496
|
+
}
|
|
3497
|
+
if (inObject && !isSettled(state, path)) {
|
|
3498
|
+
const self = await leafAt(ctx, path, state.resolved);
|
|
3499
|
+
if (self?.resolver?.type === "object" && self.resolver.additionalKeys) {
|
|
3500
|
+
const rec = await findInRecord(ctx, state, path);
|
|
3501
|
+
if (rec) return rec;
|
|
3502
|
+
}
|
|
3205
3503
|
}
|
|
3206
3504
|
return null;
|
|
3207
3505
|
}
|
|
3208
3506
|
async function askLeaf(state, path, leaf, opts = {}) {
|
|
3209
3507
|
state.current = path;
|
|
3210
|
-
|
|
3508
|
+
if (opts.gate) state.gate = opts.gate;
|
|
3509
|
+
else delete state.gate;
|
|
3211
3510
|
try {
|
|
3212
3511
|
const { question, pagination } = await buildQuestion(
|
|
3213
3512
|
leaf,
|
|
@@ -3228,6 +3527,28 @@ async function askLeaf(state, path, leaf, opts = {}) {
|
|
|
3228
3527
|
return failedResult(state, leaf.name, error);
|
|
3229
3528
|
}
|
|
3230
3529
|
}
|
|
3530
|
+
async function askRecordKey(state, path, keyLeaf, opts = {}) {
|
|
3531
|
+
return askLeaf(state, path, keyLeaf, { gate: "record_key", ...opts });
|
|
3532
|
+
}
|
|
3533
|
+
async function autoResolveLeaf(state, path, leaf) {
|
|
3534
|
+
const resolver = leaf.resolver;
|
|
3535
|
+
if (resolver && autoSettles(resolver)) {
|
|
3536
|
+
if (resolver.type === "constant")
|
|
3537
|
+
setAtPath(state.resolved, path, resolver.value);
|
|
3538
|
+
settle(state, path);
|
|
3539
|
+
return true;
|
|
3540
|
+
}
|
|
3541
|
+
const auto = resolver?.type === "dynamic" ? await resolver.tryResolveWithoutPrompt?.({
|
|
3542
|
+
input: mergeInput(state.resolved, leaf.extraInput)
|
|
3543
|
+
}) : void 0;
|
|
3544
|
+
if (auto) {
|
|
3545
|
+
if (auto.resolvedValue !== void 0)
|
|
3546
|
+
setAtPath(state.resolved, path, auto.resolvedValue);
|
|
3547
|
+
settle(state, path);
|
|
3548
|
+
return true;
|
|
3549
|
+
}
|
|
3550
|
+
return false;
|
|
3551
|
+
}
|
|
3231
3552
|
async function advance(ctx, state) {
|
|
3232
3553
|
for (; ; ) {
|
|
3233
3554
|
const target = await findNext(ctx, state);
|
|
@@ -3237,54 +3558,56 @@ async function advance(ctx, state) {
|
|
|
3237
3558
|
delete state.pagination;
|
|
3238
3559
|
return { state, result: finalize(ctx, state.resolved) };
|
|
3239
3560
|
}
|
|
3240
|
-
if (target.
|
|
3561
|
+
if (target.type === "array_items") {
|
|
3241
3562
|
state.current = target.path;
|
|
3242
|
-
state.gate = "
|
|
3563
|
+
state.gate = "array_items";
|
|
3243
3564
|
delete state.pagination;
|
|
3244
3565
|
return {
|
|
3245
3566
|
state,
|
|
3246
|
-
result: { status: "ask", question:
|
|
3567
|
+
result: { status: "ask", question: arrayItemsQuestion(target) }
|
|
3247
3568
|
};
|
|
3248
3569
|
}
|
|
3249
|
-
if (target.
|
|
3570
|
+
if (target.type === "object_optional") {
|
|
3250
3571
|
state.current = target.path;
|
|
3251
|
-
state.gate = "
|
|
3572
|
+
state.gate = "object_optional";
|
|
3252
3573
|
delete state.pagination;
|
|
3253
3574
|
return {
|
|
3254
3575
|
state,
|
|
3255
|
-
result: {
|
|
3576
|
+
result: {
|
|
3577
|
+
status: "ask",
|
|
3578
|
+
question: objectOptionalQuestion(target.path)
|
|
3579
|
+
}
|
|
3256
3580
|
};
|
|
3257
3581
|
}
|
|
3258
|
-
if (target.
|
|
3582
|
+
if (target.type === "object_optional_properties") {
|
|
3259
3583
|
state.current = target.path;
|
|
3260
|
-
state.gate = "
|
|
3584
|
+
state.gate = "object_optional_properties";
|
|
3261
3585
|
delete state.pagination;
|
|
3262
3586
|
return {
|
|
3263
3587
|
state,
|
|
3264
3588
|
result: {
|
|
3265
3589
|
status: "ask",
|
|
3266
|
-
question:
|
|
3590
|
+
question: objectOptionalPropertiesQuestion(
|
|
3591
|
+
target.path,
|
|
3592
|
+
target.pending
|
|
3593
|
+
)
|
|
3267
3594
|
}
|
|
3268
3595
|
};
|
|
3269
3596
|
}
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3597
|
+
if (target.type === "record_entries") {
|
|
3598
|
+
state.current = target.path;
|
|
3599
|
+
state.gate = "record_entries";
|
|
3600
|
+
delete state.pagination;
|
|
3601
|
+
return {
|
|
3602
|
+
state,
|
|
3603
|
+
result: { status: "ask", question: recordEntriesQuestion(target) }
|
|
3604
|
+
};
|
|
3278
3605
|
}
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
}) : void 0;
|
|
3282
|
-
if (auto) {
|
|
3283
|
-
if (auto.resolvedValue !== void 0)
|
|
3284
|
-
setAtPath(state.resolved, path, auto.resolvedValue);
|
|
3285
|
-
settle(state, path);
|
|
3286
|
-
continue;
|
|
3606
|
+
if (target.type === "record_key") {
|
|
3607
|
+
return askRecordKey(state, target.path, target.leaf);
|
|
3287
3608
|
}
|
|
3609
|
+
const { path, leaf } = target;
|
|
3610
|
+
if (await autoResolveLeaf(state, path, leaf)) continue;
|
|
3288
3611
|
if (!state.interactive) {
|
|
3289
3612
|
if (!leaf.required) {
|
|
3290
3613
|
settle(state, path);
|
|
@@ -3317,10 +3640,18 @@ async function step(ctx, prior, action) {
|
|
|
3317
3640
|
delete state.pagination;
|
|
3318
3641
|
return { state, result: { status: "cancelled" } };
|
|
3319
3642
|
}
|
|
3643
|
+
if (state.gate === "record_key") {
|
|
3644
|
+
return stepRecordKey(ctx, state, action);
|
|
3645
|
+
}
|
|
3320
3646
|
const path = state.current;
|
|
3321
3647
|
if (!path) throw new Error("step called with no outstanding question");
|
|
3322
3648
|
const leaf = await leafAt(ctx, path, state.resolved);
|
|
3323
|
-
if (leaf
|
|
3649
|
+
if (!leaf) {
|
|
3650
|
+
throw new Error(
|
|
3651
|
+
`no resolver for the outstanding question at "${pathToKey(path)}"`
|
|
3652
|
+
);
|
|
3653
|
+
}
|
|
3654
|
+
if (action.type === "search" || action.type === "next_page" || action.type === "previous_page" || action.type === "retry") {
|
|
3324
3655
|
return refine(ctx, state, leaf, path, action);
|
|
3325
3656
|
}
|
|
3326
3657
|
if (action.type === "add" || action.type === "done") {
|
|
@@ -3337,19 +3668,26 @@ async function step(ctx, prior, action) {
|
|
|
3337
3668
|
settle(state, path);
|
|
3338
3669
|
return advance(ctx, state);
|
|
3339
3670
|
}
|
|
3340
|
-
if (gate === "
|
|
3671
|
+
if (gate === "object_optional") {
|
|
3341
3672
|
setAtPath(state.resolved, path, {});
|
|
3342
3673
|
return advance(ctx, state);
|
|
3343
3674
|
}
|
|
3344
|
-
if (gate === "
|
|
3675
|
+
if (gate === "object_optional_properties") {
|
|
3345
3676
|
remember(state, optionalsMarker(path));
|
|
3346
3677
|
return advance(ctx, state);
|
|
3347
3678
|
}
|
|
3679
|
+
if (gate === "record_entries") {
|
|
3680
|
+
const { keyLeaf } = await recordInfoAt(ctx, path, state.resolved);
|
|
3681
|
+
return askRecordKey(state, path, keyLeaf);
|
|
3682
|
+
}
|
|
3348
3683
|
const items = getAtPath(state.resolved, path) ?? [];
|
|
3349
3684
|
const { item } = await arrayInfoAt(ctx, path, state.resolved);
|
|
3350
3685
|
const itemPath = [...path, items.length];
|
|
3351
|
-
if (seedItemSlot(state, itemPath, item) === "leaf")
|
|
3686
|
+
if (seedItemSlot(state, itemPath, item) === "leaf") {
|
|
3687
|
+
if (await autoResolveLeaf(state, itemPath, item))
|
|
3688
|
+
return advance(ctx, state);
|
|
3352
3689
|
return askLeaf(state, itemPath, item);
|
|
3690
|
+
}
|
|
3353
3691
|
return advance(ctx, state);
|
|
3354
3692
|
}
|
|
3355
3693
|
if (state.gate) {
|
|
@@ -3360,81 +3698,37 @@ async function step(ctx, prior, action) {
|
|
|
3360
3698
|
switch (action.type) {
|
|
3361
3699
|
case "choose":
|
|
3362
3700
|
case "custom": {
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
if (
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
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
|
-
}
|
|
3402
|
-
}
|
|
3403
|
-
return askLeaf(state, path, leaf, { error });
|
|
3701
|
+
let error;
|
|
3702
|
+
try {
|
|
3703
|
+
error = await validationError(leaf, action.value, state);
|
|
3704
|
+
} catch (thrown) {
|
|
3705
|
+
return failedResult(state, leaf.name, thrown);
|
|
3706
|
+
}
|
|
3707
|
+
if (error) {
|
|
3708
|
+
if (state.pagination && leaf.resolver?.type === "dynamic") {
|
|
3709
|
+
return renderPageAt(state, leaf, path, state.pagination.position, {
|
|
3710
|
+
error
|
|
3711
|
+
});
|
|
3404
3712
|
}
|
|
3713
|
+
return askLeaf(state, path, leaf, { error });
|
|
3405
3714
|
}
|
|
3406
|
-
setAtPath(
|
|
3407
|
-
state.resolved,
|
|
3408
|
-
path,
|
|
3409
|
-
leaf ? coerce(leaf, action.value) : action.value
|
|
3410
|
-
);
|
|
3715
|
+
setAtPath(state.resolved, path, coerce(leaf, action.value));
|
|
3411
3716
|
break;
|
|
3412
3717
|
}
|
|
3413
3718
|
case "skip":
|
|
3414
3719
|
settle(state, path);
|
|
3415
3720
|
break;
|
|
3416
3721
|
default:
|
|
3417
|
-
throw new Error(
|
|
3722
|
+
throw new Error(
|
|
3723
|
+
`action "${action.type}" is not supported here`
|
|
3724
|
+
);
|
|
3418
3725
|
}
|
|
3419
3726
|
delete state.current;
|
|
3420
3727
|
delete state.pagination;
|
|
3421
3728
|
return advance(ctx, state);
|
|
3422
3729
|
}
|
|
3423
|
-
async function
|
|
3424
|
-
const position = positionAfter(state.pagination, action);
|
|
3730
|
+
async function renderPageAt(state, leaf, path, position, opts = {}) {
|
|
3425
3731
|
try {
|
|
3426
|
-
if (action.type === "search") {
|
|
3427
|
-
const exact = leaf.resolver?.type === "dynamic" ? await leaf.resolver.tryResolveFromSearch?.({
|
|
3428
|
-
input: mergeInput(state.resolved, leaf.extraInput),
|
|
3429
|
-
search: action.term
|
|
3430
|
-
}) : void 0;
|
|
3431
|
-
if (exact) {
|
|
3432
|
-
setAtPath(state.resolved, path, coerce(leaf, exact.resolvedValue));
|
|
3433
|
-
delete state.current;
|
|
3434
|
-
delete state.pagination;
|
|
3435
|
-
return advance(ctx, state);
|
|
3436
|
-
}
|
|
3437
|
-
}
|
|
3438
3732
|
const context = await resolveContext(leaf, state.resolved);
|
|
3439
3733
|
const page = await fetchListing(leaf, state.resolved, position, context);
|
|
3440
3734
|
state.pagination = toPagination(page);
|
|
@@ -3442,7 +3736,8 @@ async function refine(ctx, state, leaf, path, action) {
|
|
|
3442
3736
|
state,
|
|
3443
3737
|
result: {
|
|
3444
3738
|
status: "ask",
|
|
3445
|
-
question: selectQuestion(leaf, path, state.resolved, page, context)
|
|
3739
|
+
question: selectQuestion(leaf, path, state.resolved, page, context),
|
|
3740
|
+
...opts.error ? { error: opts.error } : {}
|
|
3446
3741
|
}
|
|
3447
3742
|
};
|
|
3448
3743
|
} catch (error) {
|
|
@@ -3450,6 +3745,65 @@ async function refine(ctx, state, leaf, path, action) {
|
|
|
3450
3745
|
return failedResult(state, leaf.name, error);
|
|
3451
3746
|
}
|
|
3452
3747
|
}
|
|
3748
|
+
async function refine(ctx, state, leaf, path, action) {
|
|
3749
|
+
const position = positionAfter(state.pagination, action);
|
|
3750
|
+
if (action.type === "search") {
|
|
3751
|
+
try {
|
|
3752
|
+
const exact = leaf.resolver?.type === "dynamic" ? await leaf.resolver.tryResolveFromSearch?.({
|
|
3753
|
+
input: mergeInput(state.resolved, leaf.extraInput),
|
|
3754
|
+
search: action.term
|
|
3755
|
+
}) : void 0;
|
|
3756
|
+
if (exact) {
|
|
3757
|
+
setAtPath(state.resolved, path, coerce(leaf, exact.resolvedValue));
|
|
3758
|
+
delete state.current;
|
|
3759
|
+
delete state.pagination;
|
|
3760
|
+
return advance(ctx, state);
|
|
3761
|
+
}
|
|
3762
|
+
} catch (error) {
|
|
3763
|
+
state.pagination = failedPagination(state.pagination, position);
|
|
3764
|
+
return failedResult(state, leaf.name, error);
|
|
3765
|
+
}
|
|
3766
|
+
}
|
|
3767
|
+
return renderPageAt(state, leaf, path, position);
|
|
3768
|
+
}
|
|
3769
|
+
async function stepRecordKey(ctx, state, action) {
|
|
3770
|
+
const path = state.current;
|
|
3771
|
+
if (!path)
|
|
3772
|
+
throw new Error("record key step called with no outstanding question");
|
|
3773
|
+
const { keyLeaf, valueLeaf } = await recordInfoAt(ctx, path, state.resolved);
|
|
3774
|
+
if (action.type === "skip") {
|
|
3775
|
+
delete state.gate;
|
|
3776
|
+
delete state.current;
|
|
3777
|
+
delete state.pagination;
|
|
3778
|
+
return advance(ctx, state);
|
|
3779
|
+
}
|
|
3780
|
+
if (action.type !== "custom" && action.type !== "choose") {
|
|
3781
|
+
throw new Error(
|
|
3782
|
+
`action "${action.type}" is not supported while entering a record key`
|
|
3783
|
+
);
|
|
3784
|
+
}
|
|
3785
|
+
const raw = Array.isArray(action.value) ? action.value[0] : action.value;
|
|
3786
|
+
const entryKey = String(coerce(keyLeaf, raw));
|
|
3787
|
+
if (entryKey.trim() === "") {
|
|
3788
|
+
return askRecordKey(state, path, keyLeaf, { error: "A key is required." });
|
|
3789
|
+
}
|
|
3790
|
+
if (UNSAFE_RECORD_KEYS.has(entryKey)) {
|
|
3791
|
+
return askRecordKey(state, path, keyLeaf, {
|
|
3792
|
+
error: `"${entryKey}" is not an allowed key.`
|
|
3793
|
+
});
|
|
3794
|
+
}
|
|
3795
|
+
const container = getAtPath(state.resolved, path);
|
|
3796
|
+
if (Object.prototype.hasOwnProperty.call(container, entryKey)) {
|
|
3797
|
+
return askRecordKey(state, path, keyLeaf, {
|
|
3798
|
+
error: `"${entryKey}" is already set.`
|
|
3799
|
+
});
|
|
3800
|
+
}
|
|
3801
|
+
const valuePath = [...path, entryKey];
|
|
3802
|
+
if (await autoResolveLeaf(state, valuePath, valueLeaf)) {
|
|
3803
|
+
return advance(ctx, state);
|
|
3804
|
+
}
|
|
3805
|
+
return askLeaf(state, valuePath, valueLeaf);
|
|
3806
|
+
}
|
|
3453
3807
|
function failedPagination(pagination, retryPosition) {
|
|
3454
3808
|
return {
|
|
3455
3809
|
position: pagination?.position ?? firstPagePosition(),
|
|
@@ -3505,7 +3859,7 @@ function projectSummary(entry) {
|
|
|
3505
3859
|
};
|
|
3506
3860
|
}
|
|
3507
3861
|
function projectMethod(entry) {
|
|
3508
|
-
const inputProperties = toJsonSchema(entry.inputSchema)?.properties;
|
|
3862
|
+
const inputProperties = toJsonSchema(canonicalInputSchema(entry.inputSchema))?.properties;
|
|
3509
3863
|
const parameters = {};
|
|
3510
3864
|
for (const spec of planParameters(entry).parameters) {
|
|
3511
3865
|
const dynamic = spec.resolver?.type === "dynamic" ? spec.resolver : void 0;
|
|
@@ -3538,7 +3892,12 @@ function createController(sdk) {
|
|
|
3538
3892
|
const entry = entryFor(method);
|
|
3539
3893
|
return {
|
|
3540
3894
|
method,
|
|
3541
|
-
|
|
3895
|
+
// A method that owns its input validation (`skipInputValidation`, e.g.
|
|
3896
|
+
// fetch) must not be re-validated by the controller's final `safeParse`;
|
|
3897
|
+
// drop the schema so `finalize` returns the resolved input untouched.
|
|
3898
|
+
// Planning still reads `entry.inputSchema` directly, so parameters are
|
|
3899
|
+
// unaffected.
|
|
3900
|
+
schema: entry.skipInputValidation ? void 0 : entry.inputSchema,
|
|
3542
3901
|
parameters: planParameters(entry).parameters
|
|
3543
3902
|
};
|
|
3544
3903
|
}
|
|
@@ -3603,59 +3962,6 @@ function createCorePlugin(options) {
|
|
|
3603
3962
|
}
|
|
3604
3963
|
});
|
|
3605
3964
|
}
|
|
3606
|
-
|
|
3607
|
-
// src/utils/schema-utils.ts
|
|
3608
|
-
import { z as z5 } from "zod";
|
|
3609
|
-
function getOutputSchema(inputSchema) {
|
|
3610
|
-
return inputSchema._zod.def.outputSchema;
|
|
3611
|
-
}
|
|
3612
|
-
function withOutputSchema(inputSchema, outputSchema) {
|
|
3613
|
-
Object.assign(inputSchema._zod.def, {
|
|
3614
|
-
outputSchema
|
|
3615
|
-
});
|
|
3616
|
-
return inputSchema;
|
|
3617
|
-
}
|
|
3618
|
-
function withResolver(schema, config) {
|
|
3619
|
-
schema._zod.def.resolverMeta = config;
|
|
3620
|
-
return schema;
|
|
3621
|
-
}
|
|
3622
|
-
function getSchemaDescription(schema) {
|
|
3623
|
-
return schema.description;
|
|
3624
|
-
}
|
|
3625
|
-
function getFieldDescriptions(schema) {
|
|
3626
|
-
const descriptions = {};
|
|
3627
|
-
const shape = schema.shape;
|
|
3628
|
-
for (const [key2, fieldSchema] of Object.entries(shape)) {
|
|
3629
|
-
if (fieldSchema instanceof z5.ZodType && fieldSchema.description) {
|
|
3630
|
-
descriptions[key2] = fieldSchema.description;
|
|
3631
|
-
}
|
|
3632
|
-
}
|
|
3633
|
-
return descriptions;
|
|
3634
|
-
}
|
|
3635
|
-
function withPositional(schema) {
|
|
3636
|
-
Object.assign(schema._zod.def, {
|
|
3637
|
-
positionalMeta: { positional: true }
|
|
3638
|
-
});
|
|
3639
|
-
return schema;
|
|
3640
|
-
}
|
|
3641
|
-
function schemaHasPositionalMeta(schema) {
|
|
3642
|
-
return "positionalMeta" in schema._zod.def;
|
|
3643
|
-
}
|
|
3644
|
-
function isPositional(schema) {
|
|
3645
|
-
if (schemaHasPositionalMeta(schema) && schema._zod.def.positionalMeta?.positional) {
|
|
3646
|
-
return true;
|
|
3647
|
-
}
|
|
3648
|
-
if (schema instanceof z5.ZodOptional) {
|
|
3649
|
-
return isPositional(schema._zod.def.innerType);
|
|
3650
|
-
}
|
|
3651
|
-
if (schema instanceof z5.ZodDefault) {
|
|
3652
|
-
return isPositional(schema._zod.def.innerType);
|
|
3653
|
-
}
|
|
3654
|
-
return false;
|
|
3655
|
-
}
|
|
3656
|
-
function openEnum(values, description) {
|
|
3657
|
-
return z5.union([z5.enum(values), z5.string()]).describe(description);
|
|
3658
|
-
}
|
|
3659
3965
|
export {
|
|
3660
3966
|
CONTEXT,
|
|
3661
3967
|
CORE_ERROR_SYMBOL,
|
|
@@ -3667,7 +3973,9 @@ export {
|
|
|
3667
3973
|
CoreErrorCode,
|
|
3668
3974
|
CoreSignal,
|
|
3669
3975
|
addPlugin,
|
|
3976
|
+
canonicalInputSchema,
|
|
3670
3977
|
composePlugins,
|
|
3978
|
+
concatLists,
|
|
3671
3979
|
concatPaginated,
|
|
3672
3980
|
coreOptionsPluginRef,
|
|
3673
3981
|
createAsyncContext,
|