@zapier/kitcore 0.4.0 → 0.5.1
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 +65 -0
- package/README.md +60 -16
- package/dist/index.cjs +925 -227
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +728 -252
- package/dist/index.d.ts +728 -252
- package/dist/index.mjs +915 -227
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -553,6 +553,19 @@ function isNestedMethodCall() {
|
|
|
553
553
|
const store = scope.get();
|
|
554
554
|
return store !== void 0 && store.depth > 0;
|
|
555
555
|
}
|
|
556
|
+
var observerReentrancy = 0;
|
|
557
|
+
function runIsolatedObserver(fn) {
|
|
558
|
+
observerReentrancy++;
|
|
559
|
+
try {
|
|
560
|
+
fn();
|
|
561
|
+
} catch {
|
|
562
|
+
} finally {
|
|
563
|
+
observerReentrancy--;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
function isInsideObserver() {
|
|
567
|
+
return observerReentrancy > 0;
|
|
568
|
+
}
|
|
556
569
|
function runInMethodScope(fn) {
|
|
557
570
|
if (!scope.available) return fn();
|
|
558
571
|
const currentDepth = scope.get()?.depth ?? -1;
|
|
@@ -561,7 +574,36 @@ function runInMethodScope(fn) {
|
|
|
561
574
|
var runWithTelemetryContext = runInMethodScope;
|
|
562
575
|
var isTelemetryNested = isNestedMethodCall;
|
|
563
576
|
|
|
577
|
+
// src/utils/core-options.ts
|
|
578
|
+
function defaultLogDeprecation({
|
|
579
|
+
methodName,
|
|
580
|
+
deprecation
|
|
581
|
+
}) {
|
|
582
|
+
logDeprecation(`${methodName}() is deprecated. ${deprecation.message}`);
|
|
583
|
+
}
|
|
584
|
+
var CORE_OPTIONS_ID = "kitcore/coreOptions";
|
|
585
|
+
|
|
564
586
|
// src/utils/function-utils.ts
|
|
587
|
+
function resolveCoreOptions(context) {
|
|
588
|
+
const entry = context.plugins?.[CORE_OPTIONS_ID];
|
|
589
|
+
if (entry) {
|
|
590
|
+
return entry.getValue ? entry.getValue() : entry.value;
|
|
591
|
+
}
|
|
592
|
+
return context.core;
|
|
593
|
+
}
|
|
594
|
+
var INTERNAL_CALL = Symbol("kitcore.internalCall");
|
|
595
|
+
function signalDeprecation(context, methodName, getDeprecation) {
|
|
596
|
+
if (isInsideObserver()) return;
|
|
597
|
+
const deprecation = getDeprecation?.();
|
|
598
|
+
if (!deprecation?.message) return;
|
|
599
|
+
const warning = {
|
|
600
|
+
type: "deprecation",
|
|
601
|
+
methodName,
|
|
602
|
+
deprecation
|
|
603
|
+
};
|
|
604
|
+
const handler = resolveCoreOptions(context)?.logDeprecation ?? defaultLogDeprecation;
|
|
605
|
+
runIsolatedObserver(() => handler(warning));
|
|
606
|
+
}
|
|
565
607
|
function normalizeError(error, adaptError) {
|
|
566
608
|
if (error instanceof Error) return error;
|
|
567
609
|
const message = typeof error === "object" && error !== null && "message" in error && typeof error.message === "string" ? error.message : String(error);
|
|
@@ -575,17 +617,20 @@ function normalizeError(error, adaptError) {
|
|
|
575
617
|
);
|
|
576
618
|
}
|
|
577
619
|
function createFunction(coreFn, options) {
|
|
578
|
-
const { sdk, schema, name } = options;
|
|
620
|
+
const { sdk, schema, name, getDeprecation } = options;
|
|
579
621
|
const functionName = name || coreFn.name;
|
|
580
622
|
const namedFunctions = {
|
|
581
623
|
[functionName]: async function(callOptions) {
|
|
624
|
+
if (arguments[1] !== INTERNAL_CALL) {
|
|
625
|
+
signalDeprecation(sdk.context, functionName, getDeprecation);
|
|
626
|
+
}
|
|
582
627
|
return runInMethodScope(async () => {
|
|
583
628
|
const startTime = Date.now();
|
|
584
629
|
const normalizedOptions = callOptions ?? {};
|
|
585
630
|
const args = [normalizedOptions];
|
|
586
631
|
const depth = getCurrentDepth();
|
|
587
|
-
const hooks = sdk.context.hooks;
|
|
588
|
-
const adaptError = sdk.context
|
|
632
|
+
const hooks = isInsideObserver() ? void 0 : sdk.context.hooks;
|
|
633
|
+
const adaptError = resolveCoreOptions(sdk.context)?.adaptError;
|
|
589
634
|
hooks?.onMethodStart?.({
|
|
590
635
|
methodName: functionName,
|
|
591
636
|
args,
|
|
@@ -634,6 +679,62 @@ function createFunction(coreFn, options) {
|
|
|
634
679
|
};
|
|
635
680
|
return namedFunctions[functionName];
|
|
636
681
|
}
|
|
682
|
+
function createRawFunction(coreFn, options) {
|
|
683
|
+
const { sdk, name, schema, positional, getDeprecation } = options;
|
|
684
|
+
return function(rawInput) {
|
|
685
|
+
if (arguments[1] !== INTERNAL_CALL) {
|
|
686
|
+
signalDeprecation(sdk.context, name, getDeprecation);
|
|
687
|
+
}
|
|
688
|
+
return runInMethodScope(() => {
|
|
689
|
+
const startTime = Date.now();
|
|
690
|
+
const depth = getCurrentDepth();
|
|
691
|
+
const hooks = isInsideObserver() ? void 0 : sdk.context.hooks;
|
|
692
|
+
const adaptError = resolveCoreOptions(sdk.context)?.adaptError;
|
|
693
|
+
const input = schema ? rawInput ?? {} : rawInput;
|
|
694
|
+
const record = input;
|
|
695
|
+
const args = positional ? positional.filter((key2) => record?.[key2] !== void 0).map((key2) => record?.[key2]) : [input];
|
|
696
|
+
hooks?.onMethodStart?.({
|
|
697
|
+
methodName: name,
|
|
698
|
+
args,
|
|
699
|
+
isPaginated: false,
|
|
700
|
+
depth
|
|
701
|
+
});
|
|
702
|
+
const fireEnd = (error) => {
|
|
703
|
+
hooks?.onMethodEnd?.({
|
|
704
|
+
methodName: name,
|
|
705
|
+
args,
|
|
706
|
+
isPaginated: false,
|
|
707
|
+
depth,
|
|
708
|
+
durationMs: Date.now() - startTime,
|
|
709
|
+
...error ? { error } : {}
|
|
710
|
+
});
|
|
711
|
+
};
|
|
712
|
+
try {
|
|
713
|
+
const parsed = schema ? validateOptions(schema, input, { adaptError }) : input;
|
|
714
|
+
const result = coreFn(parsed);
|
|
715
|
+
if (result !== null && typeof result === "object" && typeof result.then === "function") {
|
|
716
|
+
return result.then(
|
|
717
|
+
(value) => {
|
|
718
|
+
fireEnd();
|
|
719
|
+
return value;
|
|
720
|
+
},
|
|
721
|
+
(error) => {
|
|
722
|
+
fireEnd(
|
|
723
|
+
error instanceof Error ? error : new Error(String(error))
|
|
724
|
+
);
|
|
725
|
+
throw error;
|
|
726
|
+
}
|
|
727
|
+
);
|
|
728
|
+
}
|
|
729
|
+
fireEnd();
|
|
730
|
+
return result;
|
|
731
|
+
} catch (error) {
|
|
732
|
+
fireEnd(error instanceof Error ? error : new Error(String(error)));
|
|
733
|
+
throw error;
|
|
734
|
+
}
|
|
735
|
+
});
|
|
736
|
+
};
|
|
737
|
+
}
|
|
637
738
|
function isSdkPage(value) {
|
|
638
739
|
if (typeof value !== "object" || value === null) return false;
|
|
639
740
|
const page = value;
|
|
@@ -662,7 +763,7 @@ function createPageFunction(coreFn, {
|
|
|
662
763
|
} catch (error) {
|
|
663
764
|
throw normalizeError(
|
|
664
765
|
error,
|
|
665
|
-
sdk.context
|
|
766
|
+
resolveCoreOptions(sdk.context)?.adaptError
|
|
666
767
|
);
|
|
667
768
|
}
|
|
668
769
|
}
|
|
@@ -670,82 +771,98 @@ function createPageFunction(coreFn, {
|
|
|
670
771
|
return namedFunctions[functionName];
|
|
671
772
|
}
|
|
672
773
|
function createPaginatedFunction(coreFn, options) {
|
|
673
|
-
const { sdk, schema, name, defaultPageSize, adaptPage } = options;
|
|
774
|
+
const { sdk, schema, name, defaultPageSize, adaptPage, getDeprecation } = options;
|
|
674
775
|
const pageFunction = createPageFunction(coreFn, { sdk, adaptPage });
|
|
675
776
|
const functionName = name || coreFn.name;
|
|
676
777
|
const namedFunctions = {
|
|
677
778
|
[functionName]: function(callOptions) {
|
|
779
|
+
if (arguments[1] !== INTERNAL_CALL) {
|
|
780
|
+
signalDeprecation(sdk.context, functionName, getDeprecation);
|
|
781
|
+
}
|
|
678
782
|
return runInMethodScope(() => {
|
|
679
783
|
const startTime = Date.now();
|
|
680
784
|
const normalizedOptions = callOptions ?? {};
|
|
681
785
|
const args = [normalizedOptions];
|
|
682
786
|
const depth = getCurrentDepth();
|
|
683
|
-
const hooks = sdk.context.hooks;
|
|
684
|
-
const adaptError = sdk.context
|
|
787
|
+
const hooks = isInsideObserver() ? void 0 : sdk.context.hooks;
|
|
788
|
+
const adaptError = resolveCoreOptions(sdk.context)?.adaptError;
|
|
685
789
|
hooks?.onMethodStart?.({
|
|
686
790
|
methodName: functionName,
|
|
687
791
|
args,
|
|
688
792
|
isPaginated: true,
|
|
689
793
|
depth
|
|
690
794
|
});
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
if (hooks?.onMethodEnd) {
|
|
708
|
-
firstPagePromise.then(() => {
|
|
709
|
-
hooks.onMethodEnd({
|
|
710
|
-
methodName: functionName,
|
|
711
|
-
args,
|
|
712
|
-
isPaginated: true,
|
|
713
|
-
depth,
|
|
714
|
-
durationMs: Date.now() - startTime
|
|
715
|
-
});
|
|
716
|
-
}).catch((error) => {
|
|
717
|
-
hooks.onMethodEnd({
|
|
718
|
-
methodName: functionName,
|
|
719
|
-
args,
|
|
720
|
-
isPaginated: true,
|
|
721
|
-
depth,
|
|
722
|
-
durationMs: Date.now() - startTime,
|
|
723
|
-
error: error instanceof Error ? error : new Error(String(error))
|
|
724
|
-
});
|
|
795
|
+
try {
|
|
796
|
+
const validatedOptions = {
|
|
797
|
+
...normalizedOptions,
|
|
798
|
+
...schema ? createValidator(schema, { adaptError })(normalizedOptions) : normalizedOptions
|
|
799
|
+
};
|
|
800
|
+
const pageSize = validatedOptions.pageSize ?? defaultPageSize;
|
|
801
|
+
const optimizedOptions = {
|
|
802
|
+
...validatedOptions,
|
|
803
|
+
pageSize
|
|
804
|
+
};
|
|
805
|
+
const iterator = paginate(pageFunction, optimizedOptions);
|
|
806
|
+
const firstPagePromise = iterator.next().then((result) => {
|
|
807
|
+
if (result.done) {
|
|
808
|
+
throw new Error("Paginate should always iterate at least once");
|
|
809
|
+
}
|
|
810
|
+
return result.value;
|
|
725
811
|
});
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
812
|
+
if (hooks?.onMethodEnd) {
|
|
813
|
+
firstPagePromise.then(() => {
|
|
814
|
+
hooks.onMethodEnd({
|
|
815
|
+
methodName: functionName,
|
|
816
|
+
args,
|
|
817
|
+
isPaginated: true,
|
|
818
|
+
depth,
|
|
819
|
+
durationMs: Date.now() - startTime
|
|
820
|
+
});
|
|
821
|
+
}).catch((error) => {
|
|
822
|
+
hooks.onMethodEnd({
|
|
823
|
+
methodName: functionName,
|
|
824
|
+
args,
|
|
825
|
+
isPaginated: true,
|
|
826
|
+
depth,
|
|
827
|
+
durationMs: Date.now() - startTime,
|
|
828
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
829
|
+
});
|
|
830
|
+
});
|
|
731
831
|
}
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
832
|
+
const pageStream = async function* () {
|
|
833
|
+
yield await firstPagePromise;
|
|
834
|
+
for await (const page of iterator) {
|
|
835
|
+
yield page;
|
|
836
|
+
}
|
|
837
|
+
}();
|
|
838
|
+
return Object.assign(firstPagePromise, {
|
|
839
|
+
[Symbol.asyncIterator]() {
|
|
840
|
+
return pageStream;
|
|
841
|
+
},
|
|
842
|
+
items: function() {
|
|
843
|
+
return {
|
|
844
|
+
[Symbol.asyncIterator]: async function* () {
|
|
845
|
+
for await (const page of pageStream) {
|
|
846
|
+
for (const item of page.data) {
|
|
847
|
+
yield item;
|
|
848
|
+
}
|
|
743
849
|
}
|
|
744
850
|
}
|
|
745
|
-
}
|
|
746
|
-
}
|
|
747
|
-
}
|
|
748
|
-
})
|
|
851
|
+
};
|
|
852
|
+
}
|
|
853
|
+
});
|
|
854
|
+
} catch (error) {
|
|
855
|
+
const normalizedError = normalizeError(error, adaptError);
|
|
856
|
+
hooks?.onMethodEnd?.({
|
|
857
|
+
methodName: functionName,
|
|
858
|
+
args,
|
|
859
|
+
isPaginated: true,
|
|
860
|
+
depth,
|
|
861
|
+
durationMs: Date.now() - startTime,
|
|
862
|
+
error: normalizedError
|
|
863
|
+
});
|
|
864
|
+
throw normalizedError;
|
|
865
|
+
}
|
|
749
866
|
});
|
|
750
867
|
}
|
|
751
868
|
};
|
|
@@ -754,6 +871,9 @@ function createPaginatedFunction(coreFn, options) {
|
|
|
754
871
|
|
|
755
872
|
// src/utils/plugin-utils.ts
|
|
756
873
|
function createPluginMethod(sdk, config) {
|
|
874
|
+
logDeprecation(
|
|
875
|
+
"createPluginMethod() is deprecated. Author methods with defineMethod instead."
|
|
876
|
+
);
|
|
757
877
|
const { name, inputSchema, handler, ...metaFields } = config;
|
|
758
878
|
const namedHandlers = {
|
|
759
879
|
[name]: async function(options) {
|
|
@@ -777,6 +897,9 @@ function createPluginMethod(sdk, config) {
|
|
|
777
897
|
};
|
|
778
898
|
}
|
|
779
899
|
function createPaginatedPluginMethod(sdk, config) {
|
|
900
|
+
logDeprecation(
|
|
901
|
+
'createPaginatedPluginMethod() is deprecated. Author list methods with defineMethod output "list" instead.'
|
|
902
|
+
);
|
|
780
903
|
const {
|
|
781
904
|
name,
|
|
782
905
|
inputSchema,
|
|
@@ -1008,6 +1131,9 @@ function composePlugins(...plugins) {
|
|
|
1008
1131
|
return collapseStackEntries(entries, "composePlugins");
|
|
1009
1132
|
}
|
|
1010
1133
|
function createPluginStack() {
|
|
1134
|
+
logDeprecation(
|
|
1135
|
+
"createPluginStack() is deprecated. Compose with definePlugin and build with createSdk instead."
|
|
1136
|
+
);
|
|
1011
1137
|
return buildPluginStack(null, "createPluginStack");
|
|
1012
1138
|
}
|
|
1013
1139
|
function buildPluginStack(head, callerLabel) {
|
|
@@ -1092,7 +1218,7 @@ function normalizeImports(deps) {
|
|
|
1092
1218
|
if (!deps) return { plugins: [], bindings: [] };
|
|
1093
1219
|
const seen = /* @__PURE__ */ new Map();
|
|
1094
1220
|
const bindings = [];
|
|
1095
|
-
const add = (binding, id) => {
|
|
1221
|
+
const add = (binding, id, optional) => {
|
|
1096
1222
|
const priorId = seen.get(binding);
|
|
1097
1223
|
if (priorId !== void 0 && priorId !== id) {
|
|
1098
1224
|
throw new Error(
|
|
@@ -1101,7 +1227,7 @@ function normalizeImports(deps) {
|
|
|
1101
1227
|
}
|
|
1102
1228
|
if (priorId === void 0) {
|
|
1103
1229
|
seen.set(binding, id);
|
|
1104
|
-
bindings.push({ binding, id });
|
|
1230
|
+
bindings.push(optional ? { binding, id, optional } : { binding, id });
|
|
1105
1231
|
}
|
|
1106
1232
|
};
|
|
1107
1233
|
for (const plugin of deps) {
|
|
@@ -1109,8 +1235,9 @@ function normalizeImports(deps) {
|
|
|
1109
1235
|
for (const [binding, child] of Object.entries(plugin.exports)) {
|
|
1110
1236
|
add(binding, child.id);
|
|
1111
1237
|
}
|
|
1238
|
+
} else if (plugin.pluginType === "hook") {
|
|
1112
1239
|
} else {
|
|
1113
|
-
add(plugin.name, plugin.id);
|
|
1240
|
+
add(plugin.name, plugin.id, plugin.optional);
|
|
1114
1241
|
}
|
|
1115
1242
|
}
|
|
1116
1243
|
return { plugins: deps, bindings };
|
|
@@ -1122,6 +1249,26 @@ function collectLeafMeta(config) {
|
|
|
1122
1249
|
}
|
|
1123
1250
|
return meta;
|
|
1124
1251
|
}
|
|
1252
|
+
function formatDynamicMemberName(path) {
|
|
1253
|
+
return path.map((seg) => typeof seg === "string" ? seg : `{${seg.param}}`).join(".");
|
|
1254
|
+
}
|
|
1255
|
+
function collectDynamicMembers(members) {
|
|
1256
|
+
if (!members?.length) return void 0;
|
|
1257
|
+
return members.map((member) => {
|
|
1258
|
+
const root = member.path[0];
|
|
1259
|
+
if (typeof root !== "string") {
|
|
1260
|
+
throw new Error(
|
|
1261
|
+
"defineProperty: a dynamicMember path must start with a literal segment (the owning binding), not a { param }."
|
|
1262
|
+
);
|
|
1263
|
+
}
|
|
1264
|
+
const leaf = collectLeafMeta(member) ?? {};
|
|
1265
|
+
return {
|
|
1266
|
+
name: formatDynamicMemberName(member.path),
|
|
1267
|
+
rootBinding: root,
|
|
1268
|
+
meta: member.inputSchema ? { ...leaf, inputSchema: member.inputSchema } : leaf
|
|
1269
|
+
};
|
|
1270
|
+
});
|
|
1271
|
+
}
|
|
1125
1272
|
function defineMethod(config) {
|
|
1126
1273
|
const deps = normalizeImports(config.imports);
|
|
1127
1274
|
return {
|
|
@@ -1132,15 +1279,29 @@ function defineMethod(config) {
|
|
|
1132
1279
|
imports: deps.plugins,
|
|
1133
1280
|
importBindings: deps.bindings,
|
|
1134
1281
|
inputSchema: config.inputSchema,
|
|
1282
|
+
skipInputValidation: config.skipInputValidation,
|
|
1135
1283
|
meta: collectLeafMeta(config),
|
|
1136
1284
|
resolvers: config.resolvers,
|
|
1137
1285
|
formatter: config.formatter,
|
|
1138
1286
|
output: config.output,
|
|
1139
1287
|
positional: config.positional,
|
|
1140
1288
|
setup: config.setup,
|
|
1289
|
+
dispose: config.dispose,
|
|
1141
1290
|
run: config.run
|
|
1142
1291
|
};
|
|
1143
1292
|
}
|
|
1293
|
+
function defineMethodOverride(config) {
|
|
1294
|
+
const { target, namespace, ...rest } = config;
|
|
1295
|
+
return {
|
|
1296
|
+
pluginType: "method-override",
|
|
1297
|
+
name: `override:${target}`,
|
|
1298
|
+
id: namespace ? `${namespace}/override:${target}` : `override:${target}`,
|
|
1299
|
+
target,
|
|
1300
|
+
imports: [],
|
|
1301
|
+
importBindings: [],
|
|
1302
|
+
meta: collectLeafMeta(rest)
|
|
1303
|
+
};
|
|
1304
|
+
}
|
|
1144
1305
|
function defineResolver(config) {
|
|
1145
1306
|
const deps = normalizeImports(config.imports);
|
|
1146
1307
|
const base = { imports: deps.plugins, importBindings: deps.bindings };
|
|
@@ -1232,9 +1393,11 @@ function defineProperty(config) {
|
|
|
1232
1393
|
imports: deps.plugins,
|
|
1233
1394
|
importBindings: deps.bindings,
|
|
1234
1395
|
setup: config.setup,
|
|
1396
|
+
dispose: config.dispose,
|
|
1235
1397
|
value: config.value,
|
|
1236
1398
|
get: config.get,
|
|
1237
|
-
meta: collectLeafMeta(config)
|
|
1399
|
+
meta: collectLeafMeta(config),
|
|
1400
|
+
dynamicMembers: collectDynamicMembers(config.dynamicMembers)
|
|
1238
1401
|
};
|
|
1239
1402
|
}
|
|
1240
1403
|
function declareProperty(config) {
|
|
@@ -1249,6 +1412,37 @@ function declareProperty(config) {
|
|
|
1249
1412
|
importBindings: []
|
|
1250
1413
|
};
|
|
1251
1414
|
}
|
|
1415
|
+
function declareOptionalProperty(config) {
|
|
1416
|
+
const { name, namespace } = parseId(config.id);
|
|
1417
|
+
return {
|
|
1418
|
+
pluginType: "property",
|
|
1419
|
+
name,
|
|
1420
|
+
namespace,
|
|
1421
|
+
id: makeId(name, namespace),
|
|
1422
|
+
standIn: true,
|
|
1423
|
+
optional: true,
|
|
1424
|
+
imports: [],
|
|
1425
|
+
importBindings: []
|
|
1426
|
+
// Requires nothing (phantom carrier `<never, never>`): a consumer that
|
|
1427
|
+
// imports it still passes `createSdk`'s completeness check unprovided. The
|
|
1428
|
+
// import binding is still typed `TValue | undefined` from the descriptor.
|
|
1429
|
+
};
|
|
1430
|
+
}
|
|
1431
|
+
function defineHook(config) {
|
|
1432
|
+
const deps = normalizeImports(config.imports);
|
|
1433
|
+
return {
|
|
1434
|
+
pluginType: "hook",
|
|
1435
|
+
name: config.name,
|
|
1436
|
+
namespace: config.namespace,
|
|
1437
|
+
id: makeId(config.name, config.namespace),
|
|
1438
|
+
imports: deps.plugins,
|
|
1439
|
+
importBindings: deps.bindings,
|
|
1440
|
+
setup: config.setup,
|
|
1441
|
+
dispose: config.dispose,
|
|
1442
|
+
wrap: config.wrap,
|
|
1443
|
+
observe: config.observe
|
|
1444
|
+
};
|
|
1445
|
+
}
|
|
1252
1446
|
function declarePlugin(config) {
|
|
1253
1447
|
const { name, namespace } = parseId(config.id);
|
|
1254
1448
|
return {
|
|
@@ -1263,7 +1457,12 @@ function declarePlugin(config) {
|
|
|
1263
1457
|
};
|
|
1264
1458
|
}
|
|
1265
1459
|
function definePlugin(fnOrConfig) {
|
|
1266
|
-
if (typeof fnOrConfig === "function")
|
|
1460
|
+
if (typeof fnOrConfig === "function") {
|
|
1461
|
+
logDeprecation(
|
|
1462
|
+
"definePlugin(fn) (the function form) is deprecated. Author plugins with defineMethod/defineProperty/definePlugin({ ... }) instead."
|
|
1463
|
+
);
|
|
1464
|
+
return fnOrConfig;
|
|
1465
|
+
}
|
|
1267
1466
|
const config = fnOrConfig;
|
|
1268
1467
|
const deps = normalizeImports(config.imports);
|
|
1269
1468
|
return {
|
|
@@ -1271,12 +1470,24 @@ function definePlugin(fnOrConfig) {
|
|
|
1271
1470
|
name: config.name,
|
|
1272
1471
|
namespace: config.namespace,
|
|
1273
1472
|
id: makeId(config.name, config.namespace, "aggregate"),
|
|
1274
|
-
|
|
1473
|
+
// A re-export synthetic (`selectExports` / `omitExports`) is flattened by
|
|
1474
|
+
// `normalizeExports` into bare bindings, which drops its own `imports:
|
|
1475
|
+
// [source]`. That edge is how `omitExports` keeps an omitted (unbound) leaf
|
|
1476
|
+
// materialized + addressable by id, so preserve every exported aggregate's
|
|
1477
|
+
// imports as extra reachability edges here (bindings unaffected).
|
|
1478
|
+
imports: [...deps.plugins, ...exportedAggregateImports(config.exports)],
|
|
1275
1479
|
importBindings: deps.bindings,
|
|
1276
|
-
exports: normalizeExports(config.exports)
|
|
1277
|
-
middleware: config.middleware
|
|
1480
|
+
exports: normalizeExports(config.exports)
|
|
1278
1481
|
};
|
|
1279
1482
|
}
|
|
1483
|
+
function exportedAggregateImports(exports) {
|
|
1484
|
+
if (!exports) return [];
|
|
1485
|
+
const out = [];
|
|
1486
|
+
for (const element of exports) {
|
|
1487
|
+
if (element.pluginType === "aggregate") out.push(...element.imports);
|
|
1488
|
+
}
|
|
1489
|
+
return out;
|
|
1490
|
+
}
|
|
1280
1491
|
function normalizeExports(exports) {
|
|
1281
1492
|
if (!exports) return {};
|
|
1282
1493
|
const out = {};
|
|
@@ -1336,9 +1547,33 @@ function selectExports(source, ...specs) {
|
|
|
1336
1547
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1337
1548
|
};
|
|
1338
1549
|
}
|
|
1550
|
+
function omitExports(source, omit) {
|
|
1551
|
+
const omitSet = new Set(omit);
|
|
1552
|
+
for (const name of omit) {
|
|
1553
|
+
if (!(name in source.exports)) {
|
|
1554
|
+
throw new Error(`omitExports: "${source.id}" has no export "${name}".`);
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
const kept = {};
|
|
1558
|
+
for (const [binding, child] of Object.entries(source.exports)) {
|
|
1559
|
+
if (!omitSet.has(binding)) kept[binding] = child;
|
|
1560
|
+
}
|
|
1561
|
+
return {
|
|
1562
|
+
pluginType: "aggregate",
|
|
1563
|
+
name: makeId(`omit`, source.name, "aggregate"),
|
|
1564
|
+
id: `${source.id}#omit:${selectSeq++}`,
|
|
1565
|
+
imports: [source],
|
|
1566
|
+
importBindings: [],
|
|
1567
|
+
exports: kept
|
|
1568
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1569
|
+
};
|
|
1570
|
+
}
|
|
1339
1571
|
|
|
1340
1572
|
// src/model/legacy.ts
|
|
1341
1573
|
function fromFunctionPlugin(fn, config) {
|
|
1574
|
+
logDeprecation(
|
|
1575
|
+
"fromFunctionPlugin() is deprecated. Author plugins with defineMethod/definePlugin instead."
|
|
1576
|
+
);
|
|
1342
1577
|
return {
|
|
1343
1578
|
pluginType: "legacy",
|
|
1344
1579
|
name: config.name,
|
|
@@ -1350,6 +1585,9 @@ function fromFunctionPlugin(fn, config) {
|
|
|
1350
1585
|
};
|
|
1351
1586
|
}
|
|
1352
1587
|
function defineLegacyMerge(args) {
|
|
1588
|
+
logDeprecation(
|
|
1589
|
+
"defineLegacyMerge() is deprecated. Build directly with createSdk(root, { configuration }) instead."
|
|
1590
|
+
);
|
|
1353
1591
|
return {
|
|
1354
1592
|
pluginType: "legacy-merge",
|
|
1355
1593
|
name: args.name,
|
|
@@ -1416,40 +1654,60 @@ function pluginEntryMeta(entry) {
|
|
|
1416
1654
|
if (entry.pluginType === "property" && entry.meta) return entry.meta;
|
|
1417
1655
|
return void 0;
|
|
1418
1656
|
}
|
|
1419
|
-
function
|
|
1657
|
+
function foldDynamicMembers(entry, surfaceBindings, meta) {
|
|
1658
|
+
if (entry.pluginType !== "property" || !entry.dynamicMembers) return;
|
|
1659
|
+
for (const member of entry.dynamicMembers) {
|
|
1660
|
+
if (!surfaceBindings.has(member.rootBinding)) {
|
|
1661
|
+
throw new Error(
|
|
1662
|
+
`dynamicMember "${member.name}": its root "${member.rootBinding}" is not a surfaced member. A dynamic member's path must start with a real binding.`
|
|
1663
|
+
);
|
|
1664
|
+
}
|
|
1665
|
+
meta[member.name] = member.meta;
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
function collectSurfaceProjection(context, formatterSdk) {
|
|
1420
1669
|
const meta = {};
|
|
1421
|
-
const surface = {};
|
|
1422
1670
|
const entries = {};
|
|
1423
1671
|
for (const [binding, id] of Object.entries(context.surface)) {
|
|
1424
1672
|
const entry = context.plugins[id];
|
|
1425
1673
|
if (!entry || entry.pluginType === "aggregate") continue;
|
|
1426
|
-
surface[binding] = entry.value;
|
|
1427
1674
|
entries[binding] = entry;
|
|
1428
1675
|
const m = pluginEntryMeta(entry);
|
|
1429
1676
|
if (m) meta[binding] = m;
|
|
1430
1677
|
}
|
|
1678
|
+
const surfaceBindings = new Set(Object.keys(context.surface));
|
|
1679
|
+
for (const entry of Object.values(entries)) {
|
|
1680
|
+
foldDynamicMembers(entry, surfaceBindings, meta);
|
|
1681
|
+
}
|
|
1431
1682
|
const formatters = {};
|
|
1432
1683
|
const boundResolvers = {};
|
|
1433
1684
|
const positional = {};
|
|
1434
1685
|
for (const [binding, entry] of Object.entries(entries)) {
|
|
1435
|
-
const f = normalizeFormatter(entry,
|
|
1686
|
+
const f = normalizeFormatter(entry, formatterSdk);
|
|
1436
1687
|
if (f) formatters[binding] = f;
|
|
1437
1688
|
const r = normalizeBoundResolvers(entry);
|
|
1438
1689
|
if (r) boundResolvers[binding] = r;
|
|
1439
1690
|
const p = methodPositional(entry);
|
|
1440
1691
|
if (p) positional[binding] = p;
|
|
1441
1692
|
}
|
|
1693
|
+
return { meta, formatters, boundResolvers, positional };
|
|
1694
|
+
}
|
|
1695
|
+
function buildSurfaceRegistry(context, packageFilter) {
|
|
1696
|
+
const surface = {};
|
|
1697
|
+
for (const [binding, id] of Object.entries(context.surface)) {
|
|
1698
|
+
const entry = context.plugins[id];
|
|
1699
|
+
if (!entry || entry.pluginType === "aggregate") continue;
|
|
1700
|
+
surface[binding] = entry.pluginType === "property" && entry.getValue ? entry.getValue() : entry.value;
|
|
1701
|
+
}
|
|
1442
1702
|
return buildRegistry({
|
|
1443
1703
|
sdk: surface,
|
|
1444
|
-
|
|
1445
|
-
formatters,
|
|
1446
|
-
boundResolvers,
|
|
1447
|
-
positional,
|
|
1704
|
+
...collectSurfaceProjection(context, surface),
|
|
1448
1705
|
packageFilter
|
|
1449
1706
|
});
|
|
1450
1707
|
}
|
|
1451
1708
|
|
|
1452
1709
|
// src/model/builtins.ts
|
|
1710
|
+
var coreOptionsPluginRef = declareOptionalProperty({ id: CORE_OPTIONS_ID });
|
|
1453
1711
|
var dangerousContextPlugin = {
|
|
1454
1712
|
pluginType: "property",
|
|
1455
1713
|
name: "context",
|
|
@@ -1468,12 +1726,15 @@ var getRegistryPlugin = defineMethod({
|
|
|
1468
1726
|
});
|
|
1469
1727
|
|
|
1470
1728
|
// src/model/materialize.ts
|
|
1729
|
+
var FRAMEWORK_CONFIGURATION_IDS = /* @__PURE__ */ new Set([
|
|
1730
|
+
CORE_OPTIONS_ID
|
|
1731
|
+
]);
|
|
1471
1732
|
function normalizeOutput(output) {
|
|
1472
1733
|
if (output === void 0) return { type: "raw" };
|
|
1473
1734
|
if (typeof output === "string") return { type: output };
|
|
1474
1735
|
return output;
|
|
1475
1736
|
}
|
|
1476
|
-
var CONTEXT = Symbol("kitcore.context");
|
|
1737
|
+
var CONTEXT = Symbol.for("kitcore.context");
|
|
1477
1738
|
function getContext(sdk) {
|
|
1478
1739
|
return sdk[CONTEXT];
|
|
1479
1740
|
}
|
|
@@ -1535,7 +1796,7 @@ function topoOrder(descriptors) {
|
|
|
1535
1796
|
for (const id of descriptors.keys()) visit(id);
|
|
1536
1797
|
return order;
|
|
1537
1798
|
}
|
|
1538
|
-
function collectPlugins(root, materialized = /* @__PURE__ */ new Set()) {
|
|
1799
|
+
function collectPlugins(root, materialized = /* @__PURE__ */ new Set(), configuration) {
|
|
1539
1800
|
const byId = /* @__PURE__ */ new Map();
|
|
1540
1801
|
const visit = (plugin) => {
|
|
1541
1802
|
if (materialized.has(plugin.id)) return;
|
|
@@ -1558,8 +1819,51 @@ function collectPlugins(root, materialized = /* @__PURE__ */ new Set()) {
|
|
|
1558
1819
|
for (const edge of edgesOf(plugin)) visit(edge);
|
|
1559
1820
|
};
|
|
1560
1821
|
visit(root);
|
|
1822
|
+
if (configuration) {
|
|
1823
|
+
for (const [id, value] of Object.entries(configuration)) {
|
|
1824
|
+
const existing = byId.get(id);
|
|
1825
|
+
if (!existing) {
|
|
1826
|
+
if (FRAMEWORK_CONFIGURATION_IDS.has(id)) {
|
|
1827
|
+
const { name, namespace } = parseId(id);
|
|
1828
|
+
byId.set(id, {
|
|
1829
|
+
pluginType: "property",
|
|
1830
|
+
name,
|
|
1831
|
+
namespace,
|
|
1832
|
+
id,
|
|
1833
|
+
imports: [],
|
|
1834
|
+
importBindings: [],
|
|
1835
|
+
value
|
|
1836
|
+
});
|
|
1837
|
+
continue;
|
|
1838
|
+
}
|
|
1839
|
+
throw new Error(
|
|
1840
|
+
`createSdk: configuration id "${id}" matches no plugin in the graph. An injected value must satisfy a property stand-in reachable from the root.`
|
|
1841
|
+
);
|
|
1842
|
+
}
|
|
1843
|
+
if (existing.pluginType !== "property") {
|
|
1844
|
+
throw new Error(
|
|
1845
|
+
`createSdk: configuration id "${id}" resolves to a "${existing.pluginType}" plugin; only property values can be injected.`
|
|
1846
|
+
);
|
|
1847
|
+
}
|
|
1848
|
+
if (!isStandIn(existing)) {
|
|
1849
|
+
throw new Error(
|
|
1850
|
+
`createSdk: configuration id "${id}" collides with a registered provider. A property is either injected or provided by a plugin, not both.`
|
|
1851
|
+
);
|
|
1852
|
+
}
|
|
1853
|
+
byId.set(id, {
|
|
1854
|
+
pluginType: "property",
|
|
1855
|
+
name: existing.name,
|
|
1856
|
+
namespace: existing.namespace,
|
|
1857
|
+
id,
|
|
1858
|
+
imports: [],
|
|
1859
|
+
importBindings: [],
|
|
1860
|
+
value
|
|
1861
|
+
});
|
|
1862
|
+
}
|
|
1863
|
+
}
|
|
1561
1864
|
for (const [id, plugin] of byId) {
|
|
1562
1865
|
if (isStandIn(plugin)) {
|
|
1866
|
+
if ("optional" in plugin && plugin.optional) continue;
|
|
1563
1867
|
throw new Error(
|
|
1564
1868
|
`createSdk: missing dependency "${id}". A plugin depends on it (via a stand-in) but no implementation was registered.`
|
|
1565
1869
|
);
|
|
@@ -1567,7 +1871,7 @@ function collectPlugins(root, materialized = /* @__PURE__ */ new Set()) {
|
|
|
1567
1871
|
}
|
|
1568
1872
|
return byId;
|
|
1569
1873
|
}
|
|
1570
|
-
function bindValue(target, key2, entry) {
|
|
1874
|
+
function bindValue(target, key2, entry, callType = "surface") {
|
|
1571
1875
|
if (entry.pluginType === "property" && entry.getValue) {
|
|
1572
1876
|
Object.defineProperty(target, key2, {
|
|
1573
1877
|
get: entry.getValue,
|
|
@@ -1575,8 +1879,9 @@ function bindValue(target, key2, entry) {
|
|
|
1575
1879
|
configurable: true
|
|
1576
1880
|
});
|
|
1577
1881
|
} else {
|
|
1882
|
+
const value = callType === "internal" && entry.pluginType === "method" ? entry.internalValue ?? entry.value : entry.value;
|
|
1578
1883
|
Object.defineProperty(target, key2, {
|
|
1579
|
-
value
|
|
1884
|
+
value,
|
|
1580
1885
|
writable: true,
|
|
1581
1886
|
enumerable: true,
|
|
1582
1887
|
configurable: true
|
|
@@ -1594,8 +1899,18 @@ function buildSurface(context, ...maps) {
|
|
|
1594
1899
|
}
|
|
1595
1900
|
function buildImports(plugins, importBindings) {
|
|
1596
1901
|
const imports = {};
|
|
1597
|
-
for (const { binding, id } of importBindings) {
|
|
1598
|
-
|
|
1902
|
+
for (const { binding, id, optional } of importBindings) {
|
|
1903
|
+
const entry = plugins[id];
|
|
1904
|
+
if (!entry && optional) {
|
|
1905
|
+
Object.defineProperty(imports, binding, {
|
|
1906
|
+
value: void 0,
|
|
1907
|
+
writable: true,
|
|
1908
|
+
enumerable: true,
|
|
1909
|
+
configurable: true
|
|
1910
|
+
});
|
|
1911
|
+
continue;
|
|
1912
|
+
}
|
|
1913
|
+
bindValue(imports, binding, entry, "internal");
|
|
1599
1914
|
}
|
|
1600
1915
|
return imports;
|
|
1601
1916
|
}
|
|
@@ -1619,9 +1934,31 @@ function materialize(descriptors, context) {
|
|
|
1619
1934
|
buildEagerArtifacts(descriptors, context, states);
|
|
1620
1935
|
bindAttachments(descriptors, context);
|
|
1621
1936
|
resolveAggregates(descriptors, context);
|
|
1622
|
-
assembleMiddleware(descriptors, context);
|
|
1937
|
+
assembleMiddleware(descriptors, context, states);
|
|
1938
|
+
assembleHooks(descriptors, context, states);
|
|
1939
|
+
applyMethodOverrides(descriptors, context);
|
|
1623
1940
|
return context.plugins;
|
|
1624
1941
|
}
|
|
1942
|
+
function applyMethodOverride(context, override) {
|
|
1943
|
+
const entry = context.plugins[override.target];
|
|
1944
|
+
if (!entry) {
|
|
1945
|
+
throw new Error(
|
|
1946
|
+
`defineMethodOverride: no method "${override.target}" to override. Include the target method in the SDK build.`
|
|
1947
|
+
);
|
|
1948
|
+
}
|
|
1949
|
+
if (entry.pluginType !== "method") {
|
|
1950
|
+
throw new Error(
|
|
1951
|
+
`defineMethodOverride: "${override.target}" is a ${entry.pluginType}, not a method; only methods can be overridden.`
|
|
1952
|
+
);
|
|
1953
|
+
}
|
|
1954
|
+
entry.meta = { ...entry.meta, ...override.meta };
|
|
1955
|
+
}
|
|
1956
|
+
function applyMethodOverrides(descriptors, context) {
|
|
1957
|
+
for (const descriptor of descriptors.values()) {
|
|
1958
|
+
if (descriptor.pluginType !== "method-override") continue;
|
|
1959
|
+
applyMethodOverride(context, descriptor);
|
|
1960
|
+
}
|
|
1961
|
+
}
|
|
1625
1962
|
function bindResolver(resolver, plugins) {
|
|
1626
1963
|
switch (resolver.type) {
|
|
1627
1964
|
case "static":
|
|
@@ -1667,25 +2004,24 @@ function bindResolver(resolver, plugins) {
|
|
|
1667
2004
|
bound.definitions = bindDefinitions(resolver.definitions, plugins);
|
|
1668
2005
|
return bound;
|
|
1669
2006
|
}
|
|
1670
|
-
|
|
2007
|
+
case "dynamic": {
|
|
1671
2008
|
const imports = buildImports(plugins, resolver.importBindings);
|
|
1672
|
-
const bound = {
|
|
1673
|
-
type: "dynamic",
|
|
1674
|
-
requireParameters: resolver.requireParameters,
|
|
1675
|
-
inputType: resolver.inputType,
|
|
1676
|
-
placeholder: resolver.placeholder,
|
|
1677
|
-
prompt: resolver.prompt
|
|
1678
|
-
};
|
|
1679
2009
|
const {
|
|
1680
2010
|
getContext: getContext2,
|
|
1681
2011
|
listItems,
|
|
1682
2012
|
tryResolveWithoutPrompt,
|
|
1683
2013
|
tryResolveFromSearch
|
|
1684
2014
|
} = resolver;
|
|
2015
|
+
const bound = {
|
|
2016
|
+
type: "dynamic",
|
|
2017
|
+
requireParameters: resolver.requireParameters,
|
|
2018
|
+
inputType: resolver.inputType,
|
|
2019
|
+
placeholder: resolver.placeholder,
|
|
2020
|
+
prompt: resolver.prompt,
|
|
2021
|
+
listItems: ({ input, context, search, cursor }) => listItems({ imports, input, context, search, cursor })
|
|
2022
|
+
};
|
|
1685
2023
|
if (getContext2)
|
|
1686
2024
|
bound.getContext = ({ input }) => getContext2({ imports, input });
|
|
1687
|
-
if (listItems)
|
|
1688
|
-
bound.listItems = ({ input, context, search, cursor }) => listItems({ imports, input, context, search, cursor });
|
|
1689
2025
|
if (tryResolveWithoutPrompt) {
|
|
1690
2026
|
bound.tryResolveWithoutPrompt = ({ input }) => tryResolveWithoutPrompt({ imports, input });
|
|
1691
2027
|
}
|
|
@@ -1694,6 +2030,12 @@ function bindResolver(resolver, plugins) {
|
|
|
1694
2030
|
}
|
|
1695
2031
|
return bound;
|
|
1696
2032
|
}
|
|
2033
|
+
default: {
|
|
2034
|
+
const unhandled = resolver;
|
|
2035
|
+
throw new Error(
|
|
2036
|
+
`unhandled resolver kind: ${unhandled.type}`
|
|
2037
|
+
);
|
|
2038
|
+
}
|
|
1697
2039
|
}
|
|
1698
2040
|
}
|
|
1699
2041
|
function bindFields(fields, plugins) {
|
|
@@ -1764,25 +2106,11 @@ function runLegacyPass(descriptors, context) {
|
|
|
1764
2106
|
if (!("getRegistry" in exports)) {
|
|
1765
2107
|
let getRegistry2 = function(options) {
|
|
1766
2108
|
const sdk = this ?? exports;
|
|
1767
|
-
const
|
|
1768
|
-
|
|
1769
|
-
const boundResolvers = {};
|
|
1770
|
-
for (const [binding, id2] of Object.entries(context.surface)) {
|
|
1771
|
-
const entry = context.plugins[id2];
|
|
1772
|
-
if (!entry || entry.pluginType === "aggregate") continue;
|
|
1773
|
-
const m = pluginEntryMeta(entry);
|
|
1774
|
-
if (m) meta2[binding] = m;
|
|
1775
|
-
const f = normalizeFormatter(entry, sdk);
|
|
1776
|
-
if (f) formatters[binding] = f;
|
|
1777
|
-
const r = normalizeBoundResolvers(entry);
|
|
1778
|
-
if (r) boundResolvers[binding] = r;
|
|
1779
|
-
}
|
|
1780
|
-
Object.assign(meta2, context.meta);
|
|
2109
|
+
const projection = collectSurfaceProjection(context, sdk);
|
|
2110
|
+
Object.assign(projection.meta, context.meta);
|
|
1781
2111
|
return buildRegistry({
|
|
1782
2112
|
sdk,
|
|
1783
|
-
|
|
1784
|
-
formatters,
|
|
1785
|
-
boundResolvers,
|
|
2113
|
+
...projection,
|
|
1786
2114
|
packageFilter: options?.package
|
|
1787
2115
|
});
|
|
1788
2116
|
};
|
|
@@ -1827,7 +2155,10 @@ function buildMethodEntries(descriptors, context, states) {
|
|
|
1827
2155
|
next = (i) => wrap.run({
|
|
1828
2156
|
imports: buildImports(plugins, wrap.owner.importBindings),
|
|
1829
2157
|
next: inner,
|
|
1830
|
-
input: i
|
|
2158
|
+
input: i,
|
|
2159
|
+
// Overwritten by the chain item's own closure with the owning
|
|
2160
|
+
// hook's setup state.
|
|
2161
|
+
state: void 0
|
|
1831
2162
|
});
|
|
1832
2163
|
}
|
|
1833
2164
|
return next(input);
|
|
@@ -1841,7 +2172,8 @@ function buildMethodEntries(descriptors, context, states) {
|
|
|
1841
2172
|
schema: descriptor.inputSchema,
|
|
1842
2173
|
name: descriptor.name,
|
|
1843
2174
|
defaultPageSize: out.defaultPageSize,
|
|
1844
|
-
adaptPage: out.adaptPage
|
|
2175
|
+
adaptPage: out.adaptPage,
|
|
2176
|
+
getDeprecation: () => entry.meta?.deprecation
|
|
1845
2177
|
}
|
|
1846
2178
|
);
|
|
1847
2179
|
} else if (out.type === "item") {
|
|
@@ -1850,25 +2182,43 @@ function buildMethodEntries(descriptors, context, states) {
|
|
|
1850
2182
|
});
|
|
1851
2183
|
entry.value = createFunction(
|
|
1852
2184
|
fold(itemCore),
|
|
1853
|
-
{
|
|
2185
|
+
{
|
|
2186
|
+
sdk,
|
|
2187
|
+
schema: descriptor.inputSchema,
|
|
2188
|
+
name: descriptor.name,
|
|
2189
|
+
getDeprecation: () => entry.meta?.deprecation
|
|
2190
|
+
}
|
|
1854
2191
|
);
|
|
1855
2192
|
} else {
|
|
1856
|
-
entry.value = (
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
2193
|
+
entry.value = createRawFunction(
|
|
2194
|
+
(input) => fold(callRun)(input),
|
|
2195
|
+
{
|
|
2196
|
+
sdk,
|
|
2197
|
+
name: descriptor.name,
|
|
2198
|
+
schema: descriptor.skipInputValidation ? void 0 : descriptor.inputSchema,
|
|
2199
|
+
positional: descriptor.positional,
|
|
2200
|
+
// The boundary reads the deprecation LIVE off the entry, so a
|
|
2201
|
+
// deprecation merged after build (defineMethodOverride, addPlugin)
|
|
2202
|
+
// fires too.
|
|
2203
|
+
getDeprecation: () => entry.meta?.deprecation
|
|
2204
|
+
}
|
|
2205
|
+
);
|
|
1860
2206
|
}
|
|
2207
|
+
const canonicalValue = entry.value;
|
|
1861
2208
|
if (descriptor.positional) {
|
|
1862
2209
|
const names = descriptor.positional;
|
|
1863
|
-
const
|
|
1864
|
-
entry.value = (...args) => {
|
|
2210
|
+
const pack = (args) => {
|
|
1865
2211
|
const packed = {};
|
|
1866
2212
|
names.forEach((name, i) => {
|
|
1867
2213
|
if (i < args.length) packed[name] = args[i];
|
|
1868
2214
|
});
|
|
1869
|
-
return
|
|
2215
|
+
return packed;
|
|
1870
2216
|
};
|
|
2217
|
+
entry.value = (...args) => canonicalValue(pack(args));
|
|
2218
|
+
entry.internalValue = (...args) => canonicalValue(pack(args), INTERNAL_CALL);
|
|
1871
2219
|
entry.positional = names;
|
|
2220
|
+
} else {
|
|
2221
|
+
entry.internalValue = (input) => canonicalValue(input, INTERNAL_CALL);
|
|
1872
2222
|
}
|
|
1873
2223
|
plugins[id] = entry;
|
|
1874
2224
|
}
|
|
@@ -1880,7 +2230,7 @@ function buildEagerArtifacts(descriptors, context, states) {
|
|
|
1880
2230
|
const ensureBuilt = (id) => {
|
|
1881
2231
|
if (built.has(id)) return;
|
|
1882
2232
|
const descriptor = descriptors.get(id);
|
|
1883
|
-
if (!descriptor || descriptor.pluginType === "aggregate" || descriptor.pluginType === "legacy") {
|
|
2233
|
+
if (!descriptor || descriptor.pluginType === "aggregate" || descriptor.pluginType === "legacy" || descriptor.pluginType === "method-override" || isStandIn(descriptor)) {
|
|
1884
2234
|
built.add(id);
|
|
1885
2235
|
return;
|
|
1886
2236
|
}
|
|
@@ -1889,6 +2239,30 @@ function buildEagerArtifacts(descriptors, context, states) {
|
|
|
1889
2239
|
}
|
|
1890
2240
|
building.add(id);
|
|
1891
2241
|
for (const { id: depId } of descriptor.importBindings) ensureBuilt(depId);
|
|
2242
|
+
const recordDisposer = () => {
|
|
2243
|
+
const dispose = descriptor.dispose;
|
|
2244
|
+
if (!dispose) return;
|
|
2245
|
+
context.disposers?.push({
|
|
2246
|
+
id,
|
|
2247
|
+
dispose: (input) => dispose({
|
|
2248
|
+
imports: buildImports(plugins, descriptor.importBindings),
|
|
2249
|
+
state: states.get(id),
|
|
2250
|
+
input
|
|
2251
|
+
})
|
|
2252
|
+
});
|
|
2253
|
+
};
|
|
2254
|
+
if (descriptor.pluginType === "hook") {
|
|
2255
|
+
states.set(
|
|
2256
|
+
id,
|
|
2257
|
+
descriptor.setup ? descriptor.setup({
|
|
2258
|
+
imports: buildImports(plugins, descriptor.importBindings)
|
|
2259
|
+
}) : void 0
|
|
2260
|
+
);
|
|
2261
|
+
recordDisposer();
|
|
2262
|
+
building.delete(id);
|
|
2263
|
+
built.add(id);
|
|
2264
|
+
return;
|
|
2265
|
+
}
|
|
1892
2266
|
if (descriptor.pluginType === "method") {
|
|
1893
2267
|
states.set(
|
|
1894
2268
|
id,
|
|
@@ -1908,7 +2282,8 @@ function buildEagerArtifacts(descriptors, context, states) {
|
|
|
1908
2282
|
pluginType: "property",
|
|
1909
2283
|
name: descriptor.name,
|
|
1910
2284
|
value: context,
|
|
1911
|
-
meta: descriptor.meta
|
|
2285
|
+
meta: descriptor.meta,
|
|
2286
|
+
dynamicMembers: descriptor.dynamicMembers
|
|
1912
2287
|
};
|
|
1913
2288
|
} else if (descriptor.get) {
|
|
1914
2289
|
const get = descriptor.get;
|
|
@@ -1920,22 +2295,68 @@ function buildEagerArtifacts(descriptors, context, states) {
|
|
|
1920
2295
|
imports: buildImports(plugins, importBindings),
|
|
1921
2296
|
state: states.get(id)
|
|
1922
2297
|
}),
|
|
1923
|
-
meta: descriptor.meta
|
|
2298
|
+
meta: descriptor.meta,
|
|
2299
|
+
dynamicMembers: descriptor.dynamicMembers
|
|
1924
2300
|
};
|
|
1925
2301
|
} else {
|
|
1926
2302
|
plugins[id] = {
|
|
1927
2303
|
pluginType: "property",
|
|
1928
2304
|
name: descriptor.name,
|
|
1929
2305
|
value: descriptor.value,
|
|
1930
|
-
meta: descriptor.meta
|
|
2306
|
+
meta: descriptor.meta,
|
|
2307
|
+
dynamicMembers: descriptor.dynamicMembers
|
|
1931
2308
|
};
|
|
1932
2309
|
}
|
|
1933
2310
|
}
|
|
2311
|
+
recordDisposer();
|
|
1934
2312
|
building.delete(id);
|
|
1935
2313
|
built.add(id);
|
|
1936
2314
|
};
|
|
1937
2315
|
for (const id of descriptors.keys()) ensureBuilt(id);
|
|
1938
2316
|
}
|
|
2317
|
+
function resolvePlugin(sdk, ref) {
|
|
2318
|
+
const entry = getContext(sdk).plugins[ref.id];
|
|
2319
|
+
if (!entry) {
|
|
2320
|
+
if (ref.optional) {
|
|
2321
|
+
return void 0;
|
|
2322
|
+
}
|
|
2323
|
+
throw new Error(
|
|
2324
|
+
`resolvePlugin: plugin "${ref.id}" is not materialized on the SDK.`
|
|
2325
|
+
);
|
|
2326
|
+
}
|
|
2327
|
+
if (entry.pluginType === "property" && entry.getValue) {
|
|
2328
|
+
return entry.getValue();
|
|
2329
|
+
}
|
|
2330
|
+
if (entry.pluginType === "method" && entry.internalValue) {
|
|
2331
|
+
return entry.internalValue;
|
|
2332
|
+
}
|
|
2333
|
+
return entry.value;
|
|
2334
|
+
}
|
|
2335
|
+
var CoreDisposeError = class extends Error {
|
|
2336
|
+
constructor(errors) {
|
|
2337
|
+
super(`disposeSdk: ${errors.length} dispose callback(s) failed.`);
|
|
2338
|
+
this.name = "CoreDisposeError";
|
|
2339
|
+
this.errors = errors;
|
|
2340
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
2341
|
+
}
|
|
2342
|
+
};
|
|
2343
|
+
function disposeSdk(sdk, input) {
|
|
2344
|
+
const context = getContext(sdk);
|
|
2345
|
+
if (context.disposed) return context.disposed;
|
|
2346
|
+
const disposers = context.disposers ?? [];
|
|
2347
|
+
context.disposed = (async () => {
|
|
2348
|
+
const errors = [];
|
|
2349
|
+
for (let i = disposers.length - 1; i >= 0; i--) {
|
|
2350
|
+
try {
|
|
2351
|
+
await disposers[i].dispose(input);
|
|
2352
|
+
} catch (error) {
|
|
2353
|
+
errors.push(error);
|
|
2354
|
+
}
|
|
2355
|
+
}
|
|
2356
|
+
if (errors.length > 0) throw new CoreDisposeError(errors);
|
|
2357
|
+
})();
|
|
2358
|
+
return context.disposed;
|
|
2359
|
+
}
|
|
1939
2360
|
function resolveAggregates(descriptors, context) {
|
|
1940
2361
|
const plugins = context.plugins;
|
|
1941
2362
|
for (const [id, descriptor] of descriptors) {
|
|
@@ -1947,39 +2368,74 @@ function resolveAggregates(descriptors, context) {
|
|
|
1947
2368
|
plugins[id] = { pluginType: "aggregate", name: descriptor.name, exports };
|
|
1948
2369
|
}
|
|
1949
2370
|
}
|
|
1950
|
-
function assembleMiddleware(descriptors, context) {
|
|
2371
|
+
function assembleMiddleware(descriptors, context, states) {
|
|
1951
2372
|
const plugins = context.plugins;
|
|
1952
2373
|
for (const id of topoOrder(descriptors)) {
|
|
1953
2374
|
const descriptor = descriptors.get(id);
|
|
1954
|
-
if (!descriptor || descriptor.pluginType !== "
|
|
2375
|
+
if (!descriptor || descriptor.pluginType !== "hook" || !descriptor.wrap) {
|
|
1955
2376
|
continue;
|
|
1956
2377
|
}
|
|
1957
|
-
for (const [targetBinding, fn] of Object.entries(descriptor.
|
|
2378
|
+
for (const [targetBinding, fn] of Object.entries(descriptor.wrap)) {
|
|
1958
2379
|
const edge = descriptor.importBindings.find(
|
|
1959
2380
|
(b) => b.binding === targetBinding
|
|
1960
2381
|
);
|
|
1961
2382
|
if (!edge) {
|
|
1962
2383
|
throw new Error(
|
|
1963
|
-
`createSdk:
|
|
2384
|
+
`createSdk: wrap target "${targetBinding}" in hook "${id}" is not a direct dependency. A wrap target must be a declared import of the wrapping hook.`
|
|
1964
2385
|
);
|
|
1965
2386
|
}
|
|
1966
2387
|
const target = plugins[edge.id];
|
|
1967
2388
|
if (!target || target.pluginType !== "method") {
|
|
1968
2389
|
throw new Error(
|
|
1969
|
-
`createSdk:
|
|
2390
|
+
`createSdk: wrap target "${targetBinding}" in hook "${id}" does not resolve to a method.`
|
|
1970
2391
|
);
|
|
1971
2392
|
}
|
|
1972
2393
|
if (target.output?.type === "list") {
|
|
1973
2394
|
throw new Error(
|
|
1974
|
-
`createSdk:
|
|
2395
|
+
`createSdk: wrap target "${targetBinding}" in hook "${id}" resolves to a list-output method, which does not support wrapping yet.`
|
|
1975
2396
|
);
|
|
1976
2397
|
}
|
|
1977
|
-
target.chain.push({
|
|
2398
|
+
target.chain.push({
|
|
2399
|
+
run: (bag) => fn({ ...bag, state: states.get(id) }),
|
|
2400
|
+
owner: descriptor
|
|
2401
|
+
});
|
|
2402
|
+
}
|
|
2403
|
+
}
|
|
2404
|
+
}
|
|
2405
|
+
function assembleHooks(descriptors, context, states) {
|
|
2406
|
+
const plugins = context.plugins;
|
|
2407
|
+
for (const id of topoOrder(descriptors)) {
|
|
2408
|
+
const descriptor = descriptors.get(id);
|
|
2409
|
+
if (!descriptor || descriptor.pluginType !== "hook" || !descriptor.observe) {
|
|
2410
|
+
continue;
|
|
1978
2411
|
}
|
|
2412
|
+
const { observe } = descriptor;
|
|
2413
|
+
const imports = buildImports(plugins, descriptor.importBindings);
|
|
2414
|
+
const state = states.get(id);
|
|
2415
|
+
const contributed = {};
|
|
2416
|
+
if (observe.onMethodStart) {
|
|
2417
|
+
const onStart = observe.onMethodStart;
|
|
2418
|
+
contributed.onMethodStart = (input) => {
|
|
2419
|
+
runIsolatedObserver(() => onStart({ imports, input, state }));
|
|
2420
|
+
};
|
|
2421
|
+
}
|
|
2422
|
+
if (observe.onMethodEnd) {
|
|
2423
|
+
const onEnd = observe.onMethodEnd;
|
|
2424
|
+
contributed.onMethodEnd = (input) => {
|
|
2425
|
+
runIsolatedObserver(() => onEnd({ imports, input, state }));
|
|
2426
|
+
};
|
|
2427
|
+
}
|
|
2428
|
+
context.hooks = buildHooks(context.hooks, contributed);
|
|
1979
2429
|
}
|
|
1980
2430
|
}
|
|
1981
|
-
function createSdk(root) {
|
|
1982
|
-
const context = {
|
|
2431
|
+
function createSdk(root, options) {
|
|
2432
|
+
const context = {
|
|
2433
|
+
plugins: {},
|
|
2434
|
+
meta: {},
|
|
2435
|
+
hooks: {},
|
|
2436
|
+
surface: {},
|
|
2437
|
+
disposers: []
|
|
2438
|
+
};
|
|
1983
2439
|
if (root.pluginType === "legacy-merge") {
|
|
1984
2440
|
const { legacy, plugin } = root;
|
|
1985
2441
|
const collectRoot = {
|
|
@@ -1990,7 +2446,10 @@ function createSdk(root) {
|
|
|
1990
2446
|
importBindings: [],
|
|
1991
2447
|
exports: {}
|
|
1992
2448
|
};
|
|
1993
|
-
const plugins2 = materialize(
|
|
2449
|
+
const plugins2 = materialize(
|
|
2450
|
+
collectPlugins(collectRoot, void 0, options?.configuration),
|
|
2451
|
+
context
|
|
2452
|
+
);
|
|
1994
2453
|
const legacyExports = plugins2[legacy.id].exports;
|
|
1995
2454
|
let pluginSurface;
|
|
1996
2455
|
if (plugin.pluginType === "aggregate") {
|
|
@@ -2007,7 +2466,10 @@ function createSdk(root) {
|
|
|
2007
2466
|
}
|
|
2008
2467
|
return buildSurface(context, legacyExports, pluginSurface);
|
|
2009
2468
|
}
|
|
2010
|
-
const plugins = materialize(
|
|
2469
|
+
const plugins = materialize(
|
|
2470
|
+
collectPlugins(root, void 0, options?.configuration),
|
|
2471
|
+
context
|
|
2472
|
+
);
|
|
2011
2473
|
if (root.pluginType === "method" || root.pluginType === "property") {
|
|
2012
2474
|
context.surface[root.name] = root.id;
|
|
2013
2475
|
const sdk = buildSurface(context);
|
|
@@ -2021,7 +2483,7 @@ function createSdk(root) {
|
|
|
2021
2483
|
function addModelPlugin(sdk, plugin, options = {}) {
|
|
2022
2484
|
const override = options.override === true;
|
|
2023
2485
|
const context = getContext(sdk);
|
|
2024
|
-
const surfaceKeys = plugin.pluginType === "aggregate" ? Object.keys(plugin.exports) : [plugin.name];
|
|
2486
|
+
const surfaceKeys = plugin.pluginType === "aggregate" ? Object.keys(plugin.exports) : plugin.pluginType === "hook" ? [] : [plugin.name];
|
|
2025
2487
|
checkRootKeyCollisions(sdk, surfaceKeys, override, "addPlugin");
|
|
2026
2488
|
const materialized = new Set(Object.keys(context.plugins));
|
|
2027
2489
|
if (override && materialized.has(plugin.id)) {
|
|
@@ -2030,6 +2492,7 @@ function addModelPlugin(sdk, plugin, options = {}) {
|
|
|
2030
2492
|
);
|
|
2031
2493
|
}
|
|
2032
2494
|
materialize(collectPlugins(plugin, materialized), context);
|
|
2495
|
+
if (plugin.pluginType === "hook") return;
|
|
2033
2496
|
const entry = context.plugins[plugin.id];
|
|
2034
2497
|
if (entry.pluginType === "aggregate") {
|
|
2035
2498
|
Object.defineProperties(
|
|
@@ -2063,6 +2526,13 @@ function addPlugin(sdk, plugin, options) {
|
|
|
2063
2526
|
}
|
|
2064
2527
|
return;
|
|
2065
2528
|
}
|
|
2529
|
+
if (plugin.pluginType === "method-override") {
|
|
2530
|
+
applyMethodOverride(
|
|
2531
|
+
getContext(sdk),
|
|
2532
|
+
plugin
|
|
2533
|
+
);
|
|
2534
|
+
return;
|
|
2535
|
+
}
|
|
2066
2536
|
addModelPlugin(
|
|
2067
2537
|
sdk,
|
|
2068
2538
|
plugin,
|
|
@@ -2145,20 +2615,14 @@ function objectShape(schema) {
|
|
|
2145
2615
|
}
|
|
2146
2616
|
function topoOrder2(specs) {
|
|
2147
2617
|
const byName = new Map(specs.map((s) => [s.name, s]));
|
|
2148
|
-
const ordered = [];
|
|
2149
2618
|
const placed = /* @__PURE__ */ new Set();
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
ordered.push(spec);
|
|
2158
|
-
placed.add(spec.name);
|
|
2159
|
-
progressed = true;
|
|
2160
|
-
}
|
|
2161
|
-
}
|
|
2619
|
+
const ordered = [];
|
|
2620
|
+
const isReady = (spec) => spec.requires.every((r) => !byName.has(r) || placed.has(r));
|
|
2621
|
+
for (; ; ) {
|
|
2622
|
+
const next = specs.find((s) => !placed.has(s.name) && isReady(s));
|
|
2623
|
+
if (!next) break;
|
|
2624
|
+
ordered.push(next);
|
|
2625
|
+
placed.add(next.name);
|
|
2162
2626
|
}
|
|
2163
2627
|
for (const spec of specs) if (!placed.has(spec.name)) ordered.push(spec);
|
|
2164
2628
|
return ordered;
|
|
@@ -2166,7 +2630,12 @@ function topoOrder2(specs) {
|
|
|
2166
2630
|
function planParameters(entry) {
|
|
2167
2631
|
const shape = objectShape(entry.inputSchema);
|
|
2168
2632
|
const resolvers = entry.boundResolvers ?? {};
|
|
2169
|
-
const names = shape ?
|
|
2633
|
+
const names = shape ? [
|
|
2634
|
+
...Object.keys(shape),
|
|
2635
|
+
...Object.keys(resolvers).filter(
|
|
2636
|
+
(name) => !(name in shape) && resolvers[name].type === "constant"
|
|
2637
|
+
)
|
|
2638
|
+
] : Object.keys(resolvers);
|
|
2170
2639
|
const specs = names.map((name) => {
|
|
2171
2640
|
const field = shape?.[name];
|
|
2172
2641
|
const { inner, required } = field ? unwrap(field) : { inner: void 0, required: false };
|
|
@@ -2180,7 +2649,13 @@ function planParameters(entry) {
|
|
|
2180
2649
|
requires: resolver?.requireParameters ?? []
|
|
2181
2650
|
};
|
|
2182
2651
|
});
|
|
2183
|
-
|
|
2652
|
+
const declared = shape ? specs.filter((s) => s.name in shape) : specs;
|
|
2653
|
+
const scan = [
|
|
2654
|
+
...shape ? specs.filter((s) => !(s.name in shape)) : [],
|
|
2655
|
+
...declared.filter((s) => s.required),
|
|
2656
|
+
...declared.filter((s) => !s.required)
|
|
2657
|
+
];
|
|
2658
|
+
return { parameters: topoOrder2(scan) };
|
|
2184
2659
|
}
|
|
2185
2660
|
|
|
2186
2661
|
// src/model/resolution/engine.ts
|
|
@@ -2205,10 +2680,12 @@ var key = (path) => path.join(".");
|
|
|
2205
2680
|
function isSettled(state, path) {
|
|
2206
2681
|
return state.settled.includes(key(path));
|
|
2207
2682
|
}
|
|
2208
|
-
function
|
|
2209
|
-
const k = key(path);
|
|
2683
|
+
function remember(state, k) {
|
|
2210
2684
|
if (!state.settled.includes(k)) state.settled.push(k);
|
|
2211
2685
|
}
|
|
2686
|
+
function settle(state, path) {
|
|
2687
|
+
remember(state, key(path));
|
|
2688
|
+
}
|
|
2212
2689
|
function clone(state) {
|
|
2213
2690
|
return JSON.parse(JSON.stringify(state));
|
|
2214
2691
|
}
|
|
@@ -2225,8 +2702,9 @@ function coerce(leaf, raw) {
|
|
|
2225
2702
|
return raw;
|
|
2226
2703
|
}
|
|
2227
2704
|
async function validationError(leaf, value, state) {
|
|
2705
|
+
if (leaf.resolver?.type !== "dynamic") return null;
|
|
2228
2706
|
const context = await resolveContext(leaf, state.resolved);
|
|
2229
|
-
const config = leaf.resolver
|
|
2707
|
+
const config = leaf.resolver.prompt?.({
|
|
2230
2708
|
items: state.listing?.items ?? [],
|
|
2231
2709
|
input: mergeInput(state.resolved, leaf.extraInput),
|
|
2232
2710
|
context
|
|
@@ -2236,11 +2714,6 @@ async function validationError(leaf, value, state) {
|
|
|
2236
2714
|
if (verdict === true) return null;
|
|
2237
2715
|
return typeof verdict === "string" ? verdict : `${leaf.name}: invalid value`;
|
|
2238
2716
|
}
|
|
2239
|
-
function toChoice(c) {
|
|
2240
|
-
const label = "label" in c ? c.label : c.name;
|
|
2241
|
-
const hint = Array.isArray(c.hint) ? c.hint.join(", ") : c.hint;
|
|
2242
|
-
return { label, value: String(c.value), hint };
|
|
2243
|
-
}
|
|
2244
2717
|
function isRef(r) {
|
|
2245
2718
|
return typeof r === "object" && r !== null && "ref" in r;
|
|
2246
2719
|
}
|
|
@@ -2256,6 +2729,7 @@ function toLeaf(name, field, definitions) {
|
|
|
2256
2729
|
return {
|
|
2257
2730
|
name,
|
|
2258
2731
|
required: field.required ?? false,
|
|
2732
|
+
label: field.label,
|
|
2259
2733
|
resolver,
|
|
2260
2734
|
extraInput,
|
|
2261
2735
|
// Carry the field's value type so nested answers coerce (number/boolean)
|
|
@@ -2303,6 +2777,9 @@ function arrayItem(resolver) {
|
|
|
2303
2777
|
requires: []
|
|
2304
2778
|
};
|
|
2305
2779
|
}
|
|
2780
|
+
function autoSettles(resolver) {
|
|
2781
|
+
return resolver.type === "constant" || resolver.type === "info";
|
|
2782
|
+
}
|
|
2306
2783
|
function mergeInput(input, extra) {
|
|
2307
2784
|
return extra ? { ...input, ...extra } : input;
|
|
2308
2785
|
}
|
|
@@ -2346,25 +2823,6 @@ async function arrayInfoAt(ctx, path, resolved) {
|
|
|
2346
2823
|
item: { ...arrayItem(resolver), name: String(path[path.length - 1]) }
|
|
2347
2824
|
};
|
|
2348
2825
|
}
|
|
2349
|
-
var AFFORDANCE = {
|
|
2350
|
-
choose: { action: "choose", description: "Pick one of the listed options" },
|
|
2351
|
-
custom: {
|
|
2352
|
-
action: "custom",
|
|
2353
|
-
description: "Provide a value directly",
|
|
2354
|
-
supply: "value"
|
|
2355
|
-
},
|
|
2356
|
-
search: {
|
|
2357
|
-
action: "search",
|
|
2358
|
-
description: "Filter the options by a search term",
|
|
2359
|
-
supply: "term"
|
|
2360
|
-
},
|
|
2361
|
-
more: { action: "more", description: "Load more options" },
|
|
2362
|
-
skip: { action: "skip", description: "Omit this optional parameter" },
|
|
2363
|
-
add: { action: "add", description: "Add another item" },
|
|
2364
|
-
done: { action: "done", description: "Finish the list" },
|
|
2365
|
-
retry: { action: "retry", description: "Retry loading the options" },
|
|
2366
|
-
cancel: { action: "cancel", description: "Cancel resolution" }
|
|
2367
|
-
};
|
|
2368
2826
|
async function firstPage(result) {
|
|
2369
2827
|
const page = await result;
|
|
2370
2828
|
return {
|
|
@@ -2373,18 +2831,19 @@ async function firstPage(result) {
|
|
|
2373
2831
|
};
|
|
2374
2832
|
}
|
|
2375
2833
|
async function resolveContext(leaf, input) {
|
|
2376
|
-
|
|
2834
|
+
if (leaf.resolver?.type !== "dynamic") return void 0;
|
|
2835
|
+
return leaf.resolver.getContext?.({
|
|
2377
2836
|
input: mergeInput(input, leaf.extraInput)
|
|
2378
2837
|
});
|
|
2379
2838
|
}
|
|
2380
2839
|
async function fetchListing(leaf, input, opts = {}) {
|
|
2381
2840
|
const page = await firstPage(
|
|
2382
|
-
leaf.resolver?.listItems
|
|
2841
|
+
leaf.resolver?.type === "dynamic" ? leaf.resolver.listItems({
|
|
2383
2842
|
input: mergeInput(input, leaf.extraInput),
|
|
2384
2843
|
context: opts.context,
|
|
2385
2844
|
search: opts.search,
|
|
2386
2845
|
cursor: opts.cursor
|
|
2387
|
-
})
|
|
2846
|
+
}) : void 0
|
|
2388
2847
|
);
|
|
2389
2848
|
return {
|
|
2390
2849
|
items: [...opts.priorItems ?? [], ...page.data],
|
|
@@ -2393,15 +2852,52 @@ async function fetchListing(leaf, input, opts = {}) {
|
|
|
2393
2852
|
exhausted: page.nextCursor == null
|
|
2394
2853
|
};
|
|
2395
2854
|
}
|
|
2855
|
+
|
|
2856
|
+
// src/model/resolution/questions.ts
|
|
2857
|
+
function toChoice(c) {
|
|
2858
|
+
const label = "label" in c ? c.label : c.name;
|
|
2859
|
+
const hint = Array.isArray(c.hint) ? c.hint.join(", ") : c.hint;
|
|
2860
|
+
return { label, value: String(c.value), hint };
|
|
2861
|
+
}
|
|
2862
|
+
var AFFORDANCE = {
|
|
2863
|
+
choose: { action: "choose", description: "Pick one of the listed options" },
|
|
2864
|
+
custom: {
|
|
2865
|
+
action: "custom",
|
|
2866
|
+
description: "Provide a value directly",
|
|
2867
|
+
supply: "value"
|
|
2868
|
+
},
|
|
2869
|
+
search: {
|
|
2870
|
+
action: "search",
|
|
2871
|
+
description: "Filter the options by a search term",
|
|
2872
|
+
supply: "term"
|
|
2873
|
+
},
|
|
2874
|
+
more: { action: "more", description: "Load more options" },
|
|
2875
|
+
skip: { action: "skip", description: "Omit this optional parameter" },
|
|
2876
|
+
add: { action: "add", description: "Add another item" },
|
|
2877
|
+
done: { action: "done", description: "Finish the list" },
|
|
2878
|
+
retry: { action: "retry", description: "Retry loading the options" },
|
|
2879
|
+
cancel: { action: "cancel", description: "Cancel resolution" }
|
|
2880
|
+
};
|
|
2396
2881
|
function selectActions(leaf, listing, multiple) {
|
|
2882
|
+
const searchMode = leaf.resolver?.type === "dynamic" && leaf.resolver.inputType === "search";
|
|
2883
|
+
if (searchMode && listing.search === void 0 && listing.items.length === 0) {
|
|
2884
|
+
const actions2 = multiple ? [AFFORDANCE.search] : [AFFORDANCE.search, AFFORDANCE.custom];
|
|
2885
|
+
if (!leaf.required) actions2.push(AFFORDANCE.skip);
|
|
2886
|
+
return actions2;
|
|
2887
|
+
}
|
|
2397
2888
|
const actions = multiple ? [AFFORDANCE.choose] : [AFFORDANCE.choose, AFFORDANCE.custom];
|
|
2398
|
-
if (
|
|
2889
|
+
if (searchMode) actions.push(AFFORDANCE.search);
|
|
2399
2890
|
if (listing.cursor) actions.push(AFFORDANCE.more);
|
|
2400
2891
|
if (!leaf.required) actions.push(AFFORDANCE.skip);
|
|
2401
2892
|
return actions;
|
|
2402
2893
|
}
|
|
2894
|
+
function labeledMessage(leaf) {
|
|
2895
|
+
if (!leaf.label) return void 0;
|
|
2896
|
+
return `${leaf.label} (${leaf.required ? "required" : "optional"}):`;
|
|
2897
|
+
}
|
|
2403
2898
|
function selectQuestion(leaf, input, listing, context) {
|
|
2404
|
-
const
|
|
2899
|
+
const resolver = leaf.resolver?.type === "dynamic" ? leaf.resolver : void 0;
|
|
2900
|
+
const config = resolver?.prompt?.({
|
|
2405
2901
|
items: listing.items,
|
|
2406
2902
|
input: mergeInput(input, leaf.extraInput),
|
|
2407
2903
|
context
|
|
@@ -2409,10 +2905,15 @@ function selectQuestion(leaf, input, listing, context) {
|
|
|
2409
2905
|
const multiple = config?.type === "checkbox";
|
|
2410
2906
|
return {
|
|
2411
2907
|
type: "select",
|
|
2412
|
-
|
|
2908
|
+
// A labeled field's title beats the resolver's message: per-field
|
|
2909
|
+
// resolvers are shared across fields (one choices-fetcher for every
|
|
2910
|
+
// field), so only the leaf knows which field is being asked.
|
|
2911
|
+
message: labeledMessage(leaf) ?? config?.message ?? `Select ${leaf.name}:`,
|
|
2413
2912
|
choices: (config?.choices ?? []).map(toChoice),
|
|
2414
2913
|
...multiple ? { multiple: true } : {},
|
|
2415
2914
|
...config?.notes?.length ? { notes: config.notes } : {},
|
|
2915
|
+
...listing.search !== void 0 ? { search: listing.search } : {},
|
|
2916
|
+
...resolver?.placeholder ? { placeholder: resolver.placeholder } : {},
|
|
2416
2917
|
actions: selectActions(leaf, listing, multiple)
|
|
2417
2918
|
};
|
|
2418
2919
|
}
|
|
@@ -2445,8 +2946,20 @@ function failedResult(state, name, error) {
|
|
|
2445
2946
|
async function buildQuestion(leaf, input) {
|
|
2446
2947
|
const optional = !leaf.required;
|
|
2447
2948
|
const resolver = leaf.resolver;
|
|
2448
|
-
if (resolver?.
|
|
2949
|
+
if (resolver?.type === "dynamic") {
|
|
2449
2950
|
const context = await resolveContext(leaf, input);
|
|
2951
|
+
if (resolver.inputType === "search") {
|
|
2952
|
+
const listing2 = {
|
|
2953
|
+
items: [],
|
|
2954
|
+
cursor: void 0,
|
|
2955
|
+
search: void 0,
|
|
2956
|
+
exhausted: true
|
|
2957
|
+
};
|
|
2958
|
+
return {
|
|
2959
|
+
question: selectQuestion(leaf, input, listing2, context),
|
|
2960
|
+
listing: listing2
|
|
2961
|
+
};
|
|
2962
|
+
}
|
|
2450
2963
|
const listing = await fetchListing(leaf, input, { context });
|
|
2451
2964
|
return {
|
|
2452
2965
|
question: selectQuestion(leaf, input, listing, context),
|
|
@@ -2465,37 +2978,29 @@ async function buildQuestion(leaf, input) {
|
|
|
2465
2978
|
}
|
|
2466
2979
|
};
|
|
2467
2980
|
}
|
|
2468
|
-
const
|
|
2981
|
+
const textSource = resolver?.type === "static" ? resolver : void 0;
|
|
2982
|
+
const inputType = textSource?.inputType && textSource.inputType !== "search" ? textSource.inputType : "text";
|
|
2469
2983
|
const actions = [AFFORDANCE.custom];
|
|
2470
2984
|
if (optional) actions.push(AFFORDANCE.skip);
|
|
2471
2985
|
return {
|
|
2472
2986
|
question: {
|
|
2473
2987
|
type: "input",
|
|
2474
|
-
|
|
2988
|
+
// The optional marker makes Enter-to-pass discoverable on a bare
|
|
2989
|
+
// parameter; a labeled field carries its marker via labeledMessage.
|
|
2990
|
+
message: labeledMessage(leaf) ?? `Enter ${leaf.name}${optional ? " (optional)" : ""}:`,
|
|
2475
2991
|
inputType,
|
|
2476
|
-
placeholder:
|
|
2992
|
+
placeholder: textSource?.placeholder,
|
|
2477
2993
|
actions
|
|
2478
2994
|
}
|
|
2479
2995
|
};
|
|
2480
2996
|
}
|
|
2481
|
-
function finalize(ctx, resolved) {
|
|
2482
|
-
if (!ctx.schema) return { status: "done", value: resolved };
|
|
2483
|
-
const parsed = ctx.schema.safeParse(resolved);
|
|
2484
|
-
if (parsed.success) {
|
|
2485
|
-
return { status: "done", value: parsed.data };
|
|
2486
|
-
}
|
|
2487
|
-
const issues = parsed.error.issues.map((i) => ({
|
|
2488
|
-
parameter: i.path.map(String).join(".") || void 0,
|
|
2489
|
-
message: i.message
|
|
2490
|
-
}));
|
|
2491
|
-
return { status: "invalid", issues };
|
|
2492
|
-
}
|
|
2493
2997
|
function collectionQuestion(t) {
|
|
2494
2998
|
const actions = [AFFORDANCE.add];
|
|
2495
2999
|
if (t.count >= t.min) actions.push(AFFORDANCE.done);
|
|
2496
3000
|
return {
|
|
2497
3001
|
type: "collection",
|
|
2498
|
-
message: `Add
|
|
3002
|
+
message: `Add ${t.path[t.path.length - 1]}[${t.count}]?`,
|
|
3003
|
+
container: "array",
|
|
2499
3004
|
count: t.count,
|
|
2500
3005
|
min: t.min,
|
|
2501
3006
|
// An unbounded array's max is Infinity, which JSON.stringify turns to null;
|
|
@@ -2504,6 +3009,56 @@ function collectionQuestion(t) {
|
|
|
2504
3009
|
actions
|
|
2505
3010
|
};
|
|
2506
3011
|
}
|
|
3012
|
+
function objectGateQuestion(path) {
|
|
3013
|
+
return {
|
|
3014
|
+
type: "collection",
|
|
3015
|
+
message: `Add ${path[path.length - 1]}?`,
|
|
3016
|
+
container: "object",
|
|
3017
|
+
actions: [
|
|
3018
|
+
{
|
|
3019
|
+
action: "add",
|
|
3020
|
+
description: "Provide values for these fields"
|
|
3021
|
+
},
|
|
3022
|
+
{ action: "done", description: "Skip these fields" }
|
|
3023
|
+
]
|
|
3024
|
+
};
|
|
3025
|
+
}
|
|
3026
|
+
function optionalsGateQuestion(pending) {
|
|
3027
|
+
return {
|
|
3028
|
+
type: "collection",
|
|
3029
|
+
// The prompt and its context ride separately so a host renders the info
|
|
3030
|
+
// line above the confirm without composing any text of its own.
|
|
3031
|
+
message: "Would you like to configure optional fields?",
|
|
3032
|
+
description: `There are ${pending.length} optional field(s) available.`,
|
|
3033
|
+
container: "object",
|
|
3034
|
+
// The gated fields' projection, so a smart host can show WHAT `add` would
|
|
3035
|
+
// walk (or render a form section) instead of a blind yes/no.
|
|
3036
|
+
fields: pending.map((leaf) => ({
|
|
3037
|
+
key: leaf.name,
|
|
3038
|
+
...leaf.label ? { label: leaf.label } : {},
|
|
3039
|
+
...leaf.valueType ? { valueType: leaf.valueType } : {}
|
|
3040
|
+
})),
|
|
3041
|
+
actions: [
|
|
3042
|
+
{ action: "add", description: "Configure the optional fields" },
|
|
3043
|
+
{ action: "done", description: "Skip the optional fields" }
|
|
3044
|
+
]
|
|
3045
|
+
};
|
|
3046
|
+
}
|
|
3047
|
+
|
|
3048
|
+
// src/model/resolution/walk.ts
|
|
3049
|
+
function finalize(ctx, resolved) {
|
|
3050
|
+
if (!ctx.schema) return { status: "done", value: resolved };
|
|
3051
|
+
const parsed = ctx.schema.safeParse(resolved);
|
|
3052
|
+
if (parsed.success) {
|
|
3053
|
+
return { status: "done", value: parsed.data };
|
|
3054
|
+
}
|
|
3055
|
+
const issues = parsed.error.issues.map((i) => ({
|
|
3056
|
+
parameter: i.path.map(String).join(".") || void 0,
|
|
3057
|
+
message: i.message
|
|
3058
|
+
}));
|
|
3059
|
+
return { status: "invalid", issues };
|
|
3060
|
+
}
|
|
3061
|
+
var optionalsMarker = (path) => `${key(path)}?optionals`;
|
|
2507
3062
|
async function findInArray(ctx, state, path) {
|
|
2508
3063
|
if (isSettled(state, path)) return null;
|
|
2509
3064
|
if (getAtPath(state.resolved, path) == null)
|
|
@@ -2517,8 +3072,11 @@ async function findInArray(ctx, state, path) {
|
|
|
2517
3072
|
const len = items.length;
|
|
2518
3073
|
const itemType = item.resolver?.type;
|
|
2519
3074
|
if (len > 0 && (itemType === "object" || itemType === "array")) {
|
|
2520
|
-
const
|
|
2521
|
-
if (
|
|
3075
|
+
const itemPath = [...path, len - 1];
|
|
3076
|
+
if (!isSettled(state, itemPath)) {
|
|
3077
|
+
const inner = await findNext(ctx, state, itemPath);
|
|
3078
|
+
if (inner) return inner;
|
|
3079
|
+
}
|
|
2522
3080
|
}
|
|
2523
3081
|
if (len < min) return descendItem(ctx, state, path, len, item);
|
|
2524
3082
|
if (len < max) return { kind: "array", path, count: len, min, max };
|
|
@@ -2546,14 +3104,60 @@ async function descendItem(ctx, state, arrayPath, index, item) {
|
|
|
2546
3104
|
}
|
|
2547
3105
|
async function findNext(ctx, state, path = []) {
|
|
2548
3106
|
const container = getAtPath(state.resolved, path) ?? {};
|
|
2549
|
-
|
|
3107
|
+
const children = await childrenAt(ctx, path, state.resolved);
|
|
3108
|
+
const inObject = path.length > 0;
|
|
3109
|
+
const ordered = inObject ? [
|
|
3110
|
+
...children.filter((c) => c.required),
|
|
3111
|
+
...children.filter((c) => !c.required)
|
|
3112
|
+
] : children;
|
|
3113
|
+
const asksUser = (c) => c.resolver ? !autoSettles(c.resolver) : c.required;
|
|
3114
|
+
const isPendingChild = (c) => container[c.name] === void 0 && !isSettled(state, [...path, c.name]);
|
|
3115
|
+
const hasAskableRequired = children.some((c) => c.required && asksUser(c));
|
|
3116
|
+
for (const leaf of ordered) {
|
|
2550
3117
|
const childPath = [...path, leaf.name];
|
|
2551
|
-
if (!leaf.requires.every(
|
|
3118
|
+
if (!leaf.requires.every(
|
|
3119
|
+
(r) => container[r] !== void 0 || getAtPath(state.resolved, [r]) !== void 0 || isSettled(state, [...path, r]) || isSettled(state, [r])
|
|
3120
|
+
)) {
|
|
3121
|
+
continue;
|
|
3122
|
+
}
|
|
3123
|
+
const inArrayItem = path.some((segment) => typeof segment === "number");
|
|
3124
|
+
if (inObject && !inArrayItem && state.interactive && hasAskableRequired && !leaf.required && asksUser(leaf) && isPendingChild(leaf) && !state.settled.includes(optionalsMarker(path)) && !children.some((c) => c.required && asksUser(c) && isPendingChild(c))) {
|
|
3125
|
+
const pending = ordered.filter(
|
|
3126
|
+
(c) => !c.required && asksUser(c) && isPendingChild(c)
|
|
3127
|
+
);
|
|
3128
|
+
return { kind: "optionals", path, pending };
|
|
3129
|
+
}
|
|
2552
3130
|
if (leaf.resolver?.type === "object") {
|
|
2553
|
-
if (
|
|
3131
|
+
if (isSettled(state, childPath)) continue;
|
|
3132
|
+
if (getAtPath(state.resolved, childPath) == null) {
|
|
3133
|
+
if (!leaf.required && !inArrayItem) {
|
|
3134
|
+
if (!state.interactive) {
|
|
3135
|
+
settle(state, childPath);
|
|
3136
|
+
continue;
|
|
3137
|
+
}
|
|
3138
|
+
return { kind: "object", path: childPath, leaf };
|
|
3139
|
+
}
|
|
2554
3140
|
setAtPath(state.resolved, childPath, {});
|
|
3141
|
+
}
|
|
2555
3142
|
const inner = await findNext(ctx, state, childPath);
|
|
2556
3143
|
if (inner) return inner;
|
|
3144
|
+
if (!leaf.required) {
|
|
3145
|
+
const value = getAtPath(state.resolved, childPath);
|
|
3146
|
+
if (value !== null && typeof value === "object" && Object.keys(value).length === 0) {
|
|
3147
|
+
const grandchildren = await childrenAt(
|
|
3148
|
+
ctx,
|
|
3149
|
+
childPath,
|
|
3150
|
+
state.resolved
|
|
3151
|
+
);
|
|
3152
|
+
const pending = grandchildren.some(
|
|
3153
|
+
(c) => value[c.name] === void 0 && !isSettled(state, [...childPath, c.name])
|
|
3154
|
+
);
|
|
3155
|
+
if (!pending) {
|
|
3156
|
+
delete container[leaf.name];
|
|
3157
|
+
settle(state, childPath);
|
|
3158
|
+
}
|
|
3159
|
+
}
|
|
3160
|
+
}
|
|
2557
3161
|
continue;
|
|
2558
3162
|
}
|
|
2559
3163
|
if (leaf.resolver?.type === "array") {
|
|
@@ -2569,6 +3173,7 @@ async function findNext(ctx, state, path = []) {
|
|
|
2569
3173
|
}
|
|
2570
3174
|
async function askLeaf(state, path, leaf, opts = {}) {
|
|
2571
3175
|
state.current = path;
|
|
3176
|
+
delete state.gate;
|
|
2572
3177
|
try {
|
|
2573
3178
|
const { question, listing } = await buildQuestion(leaf, state.resolved);
|
|
2574
3179
|
state.listing = listing;
|
|
@@ -2590,21 +3195,52 @@ async function advance(ctx, state) {
|
|
|
2590
3195
|
const target = await findNext(ctx, state);
|
|
2591
3196
|
if (!target) {
|
|
2592
3197
|
delete state.current;
|
|
3198
|
+
delete state.gate;
|
|
2593
3199
|
delete state.listing;
|
|
2594
3200
|
return { state, result: finalize(ctx, state.resolved) };
|
|
2595
3201
|
}
|
|
2596
3202
|
if (target.kind === "array") {
|
|
2597
3203
|
state.current = target.path;
|
|
3204
|
+
state.gate = "array";
|
|
2598
3205
|
delete state.listing;
|
|
2599
3206
|
return {
|
|
2600
3207
|
state,
|
|
2601
3208
|
result: { status: "ask", question: collectionQuestion(target) }
|
|
2602
3209
|
};
|
|
2603
3210
|
}
|
|
3211
|
+
if (target.kind === "object") {
|
|
3212
|
+
state.current = target.path;
|
|
3213
|
+
state.gate = "entry";
|
|
3214
|
+
delete state.listing;
|
|
3215
|
+
return {
|
|
3216
|
+
state,
|
|
3217
|
+
result: { status: "ask", question: objectGateQuestion(target.path) }
|
|
3218
|
+
};
|
|
3219
|
+
}
|
|
3220
|
+
if (target.kind === "optionals") {
|
|
3221
|
+
state.current = target.path;
|
|
3222
|
+
state.gate = "optionals";
|
|
3223
|
+
delete state.listing;
|
|
3224
|
+
return {
|
|
3225
|
+
state,
|
|
3226
|
+
result: {
|
|
3227
|
+
status: "ask",
|
|
3228
|
+
question: optionalsGateQuestion(target.pending)
|
|
3229
|
+
}
|
|
3230
|
+
};
|
|
3231
|
+
}
|
|
2604
3232
|
const { path, leaf } = target;
|
|
2605
|
-
const
|
|
3233
|
+
const resolver = leaf.resolver;
|
|
3234
|
+
if (resolver && autoSettles(resolver)) {
|
|
3235
|
+
if (resolver.type === "constant") {
|
|
3236
|
+
setAtPath(state.resolved, path, resolver.value);
|
|
3237
|
+
}
|
|
3238
|
+
settle(state, path);
|
|
3239
|
+
continue;
|
|
3240
|
+
}
|
|
3241
|
+
const auto = resolver?.type === "dynamic" ? await resolver.tryResolveWithoutPrompt?.({
|
|
2606
3242
|
input: mergeInput(state.resolved, leaf.extraInput)
|
|
2607
|
-
});
|
|
3243
|
+
}) : void 0;
|
|
2608
3244
|
if (auto) {
|
|
2609
3245
|
if (auto.resolvedValue !== void 0)
|
|
2610
3246
|
setAtPath(state.resolved, path, auto.resolvedValue);
|
|
@@ -2624,23 +3260,22 @@ async function advance(ctx, state) {
|
|
|
2624
3260
|
}
|
|
2625
3261
|
}
|
|
2626
3262
|
async function start(ctx, input = {}, interactive = true) {
|
|
2627
|
-
const
|
|
2628
|
-
for (const spec of ctx.parameters) {
|
|
2629
|
-
if (spec.resolver?.type === "constant")
|
|
2630
|
-
resolved[spec.name] = spec.resolver.value;
|
|
2631
|
-
}
|
|
2632
|
-
Object.assign(resolved, input);
|
|
2633
|
-
return advance(ctx, {
|
|
3263
|
+
const state = {
|
|
2634
3264
|
method: ctx.method,
|
|
2635
|
-
resolved,
|
|
3265
|
+
resolved: { ...input },
|
|
2636
3266
|
settled: [],
|
|
2637
3267
|
interactive
|
|
2638
|
-
}
|
|
3268
|
+
};
|
|
3269
|
+
for (const [name, value] of Object.entries(input)) {
|
|
3270
|
+
if (value !== void 0) settle(state, [name]);
|
|
3271
|
+
}
|
|
3272
|
+
return advance(ctx, state);
|
|
2639
3273
|
}
|
|
2640
3274
|
async function step(ctx, prior, action) {
|
|
2641
3275
|
const state = clone(prior);
|
|
2642
3276
|
if (action.type === "cancel") {
|
|
2643
3277
|
delete state.current;
|
|
3278
|
+
delete state.gate;
|
|
2644
3279
|
delete state.listing;
|
|
2645
3280
|
return { state, result: { status: "cancelled" } };
|
|
2646
3281
|
}
|
|
@@ -2651,12 +3286,27 @@ async function step(ctx, prior, action) {
|
|
|
2651
3286
|
return refine(ctx, state, leaf, path, action);
|
|
2652
3287
|
}
|
|
2653
3288
|
if (action.type === "add" || action.type === "done") {
|
|
3289
|
+
const gate = state.gate;
|
|
3290
|
+
if (!gate) {
|
|
3291
|
+
throw new Error(
|
|
3292
|
+
`action "${action.type}" is not supported here: no container decision is outstanding`
|
|
3293
|
+
);
|
|
3294
|
+
}
|
|
2654
3295
|
delete state.current;
|
|
3296
|
+
delete state.gate;
|
|
2655
3297
|
delete state.listing;
|
|
2656
3298
|
if (action.type === "done") {
|
|
2657
3299
|
settle(state, path);
|
|
2658
3300
|
return advance(ctx, state);
|
|
2659
3301
|
}
|
|
3302
|
+
if (gate === "entry") {
|
|
3303
|
+
setAtPath(state.resolved, path, {});
|
|
3304
|
+
return advance(ctx, state);
|
|
3305
|
+
}
|
|
3306
|
+
if (gate === "optionals") {
|
|
3307
|
+
remember(state, optionalsMarker(path));
|
|
3308
|
+
return advance(ctx, state);
|
|
3309
|
+
}
|
|
2660
3310
|
const items = getAtPath(state.resolved, path) ?? [];
|
|
2661
3311
|
const { item } = await arrayInfoAt(ctx, path, state.resolved);
|
|
2662
3312
|
const itemPath = [...path, items.length];
|
|
@@ -2664,12 +3314,35 @@ async function step(ctx, prior, action) {
|
|
|
2664
3314
|
return askLeaf(state, itemPath, item);
|
|
2665
3315
|
return advance(ctx, state);
|
|
2666
3316
|
}
|
|
3317
|
+
if (state.gate) {
|
|
3318
|
+
throw new Error(
|
|
3319
|
+
`action "${action.type}" is not supported here: a container decision (${state.gate}) is outstanding`
|
|
3320
|
+
);
|
|
3321
|
+
}
|
|
2667
3322
|
switch (action.type) {
|
|
2668
3323
|
case "choose":
|
|
2669
3324
|
case "custom": {
|
|
2670
3325
|
if (leaf) {
|
|
2671
3326
|
const error = await validationError(leaf, action.value, state);
|
|
2672
|
-
if (error)
|
|
3327
|
+
if (error) {
|
|
3328
|
+
if (state.listing && leaf.resolver?.type === "dynamic") {
|
|
3329
|
+
const context = await resolveContext(leaf, state.resolved);
|
|
3330
|
+
return {
|
|
3331
|
+
state,
|
|
3332
|
+
result: {
|
|
3333
|
+
status: "ask",
|
|
3334
|
+
question: selectQuestion(
|
|
3335
|
+
leaf,
|
|
3336
|
+
state.resolved,
|
|
3337
|
+
state.listing,
|
|
3338
|
+
context
|
|
3339
|
+
),
|
|
3340
|
+
error
|
|
3341
|
+
}
|
|
3342
|
+
};
|
|
3343
|
+
}
|
|
3344
|
+
return askLeaf(state, path, leaf, { error });
|
|
3345
|
+
}
|
|
2673
3346
|
}
|
|
2674
3347
|
setAtPath(
|
|
2675
3348
|
state.resolved,
|
|
@@ -2696,10 +3369,10 @@ async function refine(ctx, state, leaf, path, action) {
|
|
|
2696
3369
|
};
|
|
2697
3370
|
try {
|
|
2698
3371
|
if (action.type === "search") {
|
|
2699
|
-
const exact = await leaf.resolver
|
|
3372
|
+
const exact = leaf.resolver?.type === "dynamic" ? await leaf.resolver.tryResolveFromSearch?.({
|
|
2700
3373
|
input: mergeInput(state.resolved, leaf.extraInput),
|
|
2701
3374
|
search: action.term
|
|
2702
|
-
});
|
|
3375
|
+
}) : void 0;
|
|
2703
3376
|
if (exact) {
|
|
2704
3377
|
setAtPath(state.resolved, path, coerce(leaf, exact.resolvedValue));
|
|
2705
3378
|
delete state.current;
|
|
@@ -2750,10 +3423,11 @@ function projectMethod(entry) {
|
|
|
2750
3423
|
const inputProperties = toJsonSchema(entry.inputSchema)?.properties;
|
|
2751
3424
|
const parameters = {};
|
|
2752
3425
|
for (const spec of planParameters(entry).parameters) {
|
|
3426
|
+
const dynamic = spec.resolver?.type === "dynamic" ? spec.resolver : void 0;
|
|
2753
3427
|
parameters[spec.name] = {
|
|
2754
3428
|
required: spec.required,
|
|
2755
|
-
dynamic: Boolean(
|
|
2756
|
-
...
|
|
3429
|
+
dynamic: Boolean(dynamic),
|
|
3430
|
+
...dynamic?.inputType === "search" ? { searchable: true } : {},
|
|
2757
3431
|
...inputProperties?.[spec.name] ? { schema: inputProperties[spec.name] } : {},
|
|
2758
3432
|
...spec.staticChoices ? { choices: spec.staticChoices } : {},
|
|
2759
3433
|
...spec.requires.length ? { requireParameters: spec.requires } : {}
|
|
@@ -2820,12 +3494,13 @@ function createController(sdk) {
|
|
|
2820
3494
|
const spec = contextFor(method).parameters.find(
|
|
2821
3495
|
(p) => p.name === parameter
|
|
2822
3496
|
);
|
|
2823
|
-
|
|
2824
|
-
|
|
3497
|
+
const dynamic = spec?.resolver?.type === "dynamic" ? spec.resolver : void 0;
|
|
3498
|
+
if (!dynamic) return { data: [] };
|
|
3499
|
+
const context = await dynamic.getContext?.({ input });
|
|
2825
3500
|
const page = await firstPage(
|
|
2826
|
-
|
|
3501
|
+
dynamic.listItems({ input, context, search, cursor })
|
|
2827
3502
|
);
|
|
2828
|
-
const config =
|
|
3503
|
+
const config = dynamic.prompt?.({ items: page.data, input, context });
|
|
2829
3504
|
const data = (config?.choices ?? []).map(toChoice);
|
|
2830
3505
|
return { data, nextCursor: page.nextCursor };
|
|
2831
3506
|
};
|
|
@@ -2834,6 +3509,9 @@ function createController(sdk) {
|
|
|
2834
3509
|
|
|
2835
3510
|
// src/utils/core-plugin.ts
|
|
2836
3511
|
function createCorePlugin(options) {
|
|
3512
|
+
logDeprecation(
|
|
3513
|
+
"createCorePlugin() is deprecated. Inject the options under CORE_OPTIONS_ID via createSdk's configuration instead."
|
|
3514
|
+
);
|
|
2837
3515
|
return () => ({
|
|
2838
3516
|
context: {
|
|
2839
3517
|
core: options
|
|
@@ -2896,14 +3574,17 @@ function openEnum(values, description) {
|
|
|
2896
3574
|
export {
|
|
2897
3575
|
CONTEXT,
|
|
2898
3576
|
CORE_ERROR_SYMBOL,
|
|
3577
|
+
CORE_OPTIONS_ID,
|
|
2899
3578
|
CORE_SIGNAL_SYMBOL,
|
|
2900
3579
|
CoreCancelledSignal,
|
|
3580
|
+
CoreDisposeError,
|
|
2901
3581
|
CoreError,
|
|
2902
3582
|
CoreErrorCode,
|
|
2903
3583
|
CoreSignal,
|
|
2904
3584
|
addPlugin,
|
|
2905
3585
|
composePlugins,
|
|
2906
3586
|
concatPaginated,
|
|
3587
|
+
coreOptionsPluginRef,
|
|
2907
3588
|
createAsyncContext,
|
|
2908
3589
|
createController,
|
|
2909
3590
|
createCoreError,
|
|
@@ -2919,15 +3600,20 @@ export {
|
|
|
2919
3600
|
createValidator,
|
|
2920
3601
|
dangerousContextPlugin,
|
|
2921
3602
|
declareMethod,
|
|
3603
|
+
declareOptionalProperty,
|
|
2922
3604
|
declarePlugin,
|
|
2923
3605
|
declareProperty,
|
|
2924
3606
|
decodeIncomingCursor,
|
|
3607
|
+
defaultLogDeprecation,
|
|
2925
3608
|
defineFormatter,
|
|
3609
|
+
defineHook,
|
|
2926
3610
|
defineLegacyMerge,
|
|
2927
3611
|
defineMethod,
|
|
3612
|
+
defineMethodOverride,
|
|
2928
3613
|
definePlugin,
|
|
2929
3614
|
defineProperty,
|
|
2930
3615
|
defineResolver,
|
|
3616
|
+
disposeSdk,
|
|
2931
3617
|
fromFunctionPlugin,
|
|
2932
3618
|
getContext,
|
|
2933
3619
|
getCoreErrorCause,
|
|
@@ -2943,10 +3629,12 @@ export {
|
|
|
2943
3629
|
isNestedMethodCall,
|
|
2944
3630
|
isPositional,
|
|
2945
3631
|
isTelemetryNested,
|
|
3632
|
+
omitExports,
|
|
2946
3633
|
openEnum,
|
|
2947
3634
|
paginate,
|
|
2948
3635
|
paginateBuffered,
|
|
2949
3636
|
paginateMaxItems,
|
|
3637
|
+
resolvePlugin,
|
|
2950
3638
|
runInMethodScope,
|
|
2951
3639
|
runWithTelemetryContext,
|
|
2952
3640
|
selectExports,
|