@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.cjs
CHANGED
|
@@ -22,14 +22,17 @@ var index_exports = {};
|
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
CONTEXT: () => CONTEXT,
|
|
24
24
|
CORE_ERROR_SYMBOL: () => CORE_ERROR_SYMBOL,
|
|
25
|
+
CORE_OPTIONS_ID: () => CORE_OPTIONS_ID,
|
|
25
26
|
CORE_SIGNAL_SYMBOL: () => CORE_SIGNAL_SYMBOL,
|
|
26
27
|
CoreCancelledSignal: () => CoreCancelledSignal,
|
|
28
|
+
CoreDisposeError: () => CoreDisposeError,
|
|
27
29
|
CoreError: () => CoreError,
|
|
28
30
|
CoreErrorCode: () => CoreErrorCode,
|
|
29
31
|
CoreSignal: () => CoreSignal,
|
|
30
32
|
addPlugin: () => addPlugin,
|
|
31
33
|
composePlugins: () => composePlugins,
|
|
32
34
|
concatPaginated: () => concatPaginated,
|
|
35
|
+
coreOptionsPluginRef: () => coreOptionsPluginRef,
|
|
33
36
|
createAsyncContext: () => createAsyncContext,
|
|
34
37
|
createController: () => createController,
|
|
35
38
|
createCoreError: () => createCoreError,
|
|
@@ -45,15 +48,20 @@ __export(index_exports, {
|
|
|
45
48
|
createValidator: () => createValidator,
|
|
46
49
|
dangerousContextPlugin: () => dangerousContextPlugin,
|
|
47
50
|
declareMethod: () => declareMethod,
|
|
51
|
+
declareOptionalProperty: () => declareOptionalProperty,
|
|
48
52
|
declarePlugin: () => declarePlugin,
|
|
49
53
|
declareProperty: () => declareProperty,
|
|
50
54
|
decodeIncomingCursor: () => decodeIncomingCursor,
|
|
55
|
+
defaultLogDeprecation: () => defaultLogDeprecation,
|
|
51
56
|
defineFormatter: () => defineFormatter,
|
|
57
|
+
defineHook: () => defineHook,
|
|
52
58
|
defineLegacyMerge: () => defineLegacyMerge,
|
|
53
59
|
defineMethod: () => defineMethod,
|
|
60
|
+
defineMethodOverride: () => defineMethodOverride,
|
|
54
61
|
definePlugin: () => definePlugin,
|
|
55
62
|
defineProperty: () => defineProperty,
|
|
56
63
|
defineResolver: () => defineResolver,
|
|
64
|
+
disposeSdk: () => disposeSdk,
|
|
57
65
|
fromFunctionPlugin: () => fromFunctionPlugin,
|
|
58
66
|
getContext: () => getContext,
|
|
59
67
|
getCoreErrorCause: () => getCoreErrorCause,
|
|
@@ -69,10 +77,12 @@ __export(index_exports, {
|
|
|
69
77
|
isNestedMethodCall: () => isNestedMethodCall,
|
|
70
78
|
isPositional: () => isPositional,
|
|
71
79
|
isTelemetryNested: () => isTelemetryNested,
|
|
80
|
+
omitExports: () => omitExports,
|
|
72
81
|
openEnum: () => openEnum,
|
|
73
82
|
paginate: () => paginate,
|
|
74
83
|
paginateBuffered: () => paginateBuffered,
|
|
75
84
|
paginateMaxItems: () => paginateMaxItems,
|
|
85
|
+
resolvePlugin: () => resolvePlugin,
|
|
76
86
|
runInMethodScope: () => runInMethodScope,
|
|
77
87
|
runWithTelemetryContext: () => runWithTelemetryContext,
|
|
78
88
|
selectExports: () => selectExports,
|
|
@@ -640,6 +650,19 @@ function isNestedMethodCall() {
|
|
|
640
650
|
const store = scope.get();
|
|
641
651
|
return store !== void 0 && store.depth > 0;
|
|
642
652
|
}
|
|
653
|
+
var observerReentrancy = 0;
|
|
654
|
+
function runIsolatedObserver(fn) {
|
|
655
|
+
observerReentrancy++;
|
|
656
|
+
try {
|
|
657
|
+
fn();
|
|
658
|
+
} catch {
|
|
659
|
+
} finally {
|
|
660
|
+
observerReentrancy--;
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
function isInsideObserver() {
|
|
664
|
+
return observerReentrancy > 0;
|
|
665
|
+
}
|
|
643
666
|
function runInMethodScope(fn) {
|
|
644
667
|
if (!scope.available) return fn();
|
|
645
668
|
const currentDepth = scope.get()?.depth ?? -1;
|
|
@@ -648,7 +671,36 @@ function runInMethodScope(fn) {
|
|
|
648
671
|
var runWithTelemetryContext = runInMethodScope;
|
|
649
672
|
var isTelemetryNested = isNestedMethodCall;
|
|
650
673
|
|
|
674
|
+
// src/utils/core-options.ts
|
|
675
|
+
function defaultLogDeprecation({
|
|
676
|
+
methodName,
|
|
677
|
+
deprecation
|
|
678
|
+
}) {
|
|
679
|
+
logDeprecation(`${methodName}() is deprecated. ${deprecation.message}`);
|
|
680
|
+
}
|
|
681
|
+
var CORE_OPTIONS_ID = "kitcore/coreOptions";
|
|
682
|
+
|
|
651
683
|
// src/utils/function-utils.ts
|
|
684
|
+
function resolveCoreOptions(context) {
|
|
685
|
+
const entry = context.plugins?.[CORE_OPTIONS_ID];
|
|
686
|
+
if (entry) {
|
|
687
|
+
return entry.getValue ? entry.getValue() : entry.value;
|
|
688
|
+
}
|
|
689
|
+
return context.core;
|
|
690
|
+
}
|
|
691
|
+
var INTERNAL_CALL = Symbol("kitcore.internalCall");
|
|
692
|
+
function signalDeprecation(context, methodName, getDeprecation) {
|
|
693
|
+
if (isInsideObserver()) return;
|
|
694
|
+
const deprecation = getDeprecation?.();
|
|
695
|
+
if (!deprecation?.message) return;
|
|
696
|
+
const warning = {
|
|
697
|
+
type: "deprecation",
|
|
698
|
+
methodName,
|
|
699
|
+
deprecation
|
|
700
|
+
};
|
|
701
|
+
const handler = resolveCoreOptions(context)?.logDeprecation ?? defaultLogDeprecation;
|
|
702
|
+
runIsolatedObserver(() => handler(warning));
|
|
703
|
+
}
|
|
652
704
|
function normalizeError(error, adaptError) {
|
|
653
705
|
if (error instanceof Error) return error;
|
|
654
706
|
const message = typeof error === "object" && error !== null && "message" in error && typeof error.message === "string" ? error.message : String(error);
|
|
@@ -662,17 +714,20 @@ function normalizeError(error, adaptError) {
|
|
|
662
714
|
);
|
|
663
715
|
}
|
|
664
716
|
function createFunction(coreFn, options) {
|
|
665
|
-
const { sdk, schema, name } = options;
|
|
717
|
+
const { sdk, schema, name, getDeprecation } = options;
|
|
666
718
|
const functionName = name || coreFn.name;
|
|
667
719
|
const namedFunctions = {
|
|
668
720
|
[functionName]: async function(callOptions) {
|
|
721
|
+
if (arguments[1] !== INTERNAL_CALL) {
|
|
722
|
+
signalDeprecation(sdk.context, functionName, getDeprecation);
|
|
723
|
+
}
|
|
669
724
|
return runInMethodScope(async () => {
|
|
670
725
|
const startTime = Date.now();
|
|
671
726
|
const normalizedOptions = callOptions ?? {};
|
|
672
727
|
const args = [normalizedOptions];
|
|
673
728
|
const depth = getCurrentDepth();
|
|
674
|
-
const hooks = sdk.context.hooks;
|
|
675
|
-
const adaptError = sdk.context
|
|
729
|
+
const hooks = isInsideObserver() ? void 0 : sdk.context.hooks;
|
|
730
|
+
const adaptError = resolveCoreOptions(sdk.context)?.adaptError;
|
|
676
731
|
hooks?.onMethodStart?.({
|
|
677
732
|
methodName: functionName,
|
|
678
733
|
args,
|
|
@@ -721,6 +776,62 @@ function createFunction(coreFn, options) {
|
|
|
721
776
|
};
|
|
722
777
|
return namedFunctions[functionName];
|
|
723
778
|
}
|
|
779
|
+
function createRawFunction(coreFn, options) {
|
|
780
|
+
const { sdk, name, schema, positional, getDeprecation } = options;
|
|
781
|
+
return function(rawInput) {
|
|
782
|
+
if (arguments[1] !== INTERNAL_CALL) {
|
|
783
|
+
signalDeprecation(sdk.context, name, getDeprecation);
|
|
784
|
+
}
|
|
785
|
+
return runInMethodScope(() => {
|
|
786
|
+
const startTime = Date.now();
|
|
787
|
+
const depth = getCurrentDepth();
|
|
788
|
+
const hooks = isInsideObserver() ? void 0 : sdk.context.hooks;
|
|
789
|
+
const adaptError = resolveCoreOptions(sdk.context)?.adaptError;
|
|
790
|
+
const input = schema ? rawInput ?? {} : rawInput;
|
|
791
|
+
const record = input;
|
|
792
|
+
const args = positional ? positional.filter((key2) => record?.[key2] !== void 0).map((key2) => record?.[key2]) : [input];
|
|
793
|
+
hooks?.onMethodStart?.({
|
|
794
|
+
methodName: name,
|
|
795
|
+
args,
|
|
796
|
+
isPaginated: false,
|
|
797
|
+
depth
|
|
798
|
+
});
|
|
799
|
+
const fireEnd = (error) => {
|
|
800
|
+
hooks?.onMethodEnd?.({
|
|
801
|
+
methodName: name,
|
|
802
|
+
args,
|
|
803
|
+
isPaginated: false,
|
|
804
|
+
depth,
|
|
805
|
+
durationMs: Date.now() - startTime,
|
|
806
|
+
...error ? { error } : {}
|
|
807
|
+
});
|
|
808
|
+
};
|
|
809
|
+
try {
|
|
810
|
+
const parsed = schema ? validateOptions(schema, input, { adaptError }) : input;
|
|
811
|
+
const result = coreFn(parsed);
|
|
812
|
+
if (result !== null && typeof result === "object" && typeof result.then === "function") {
|
|
813
|
+
return result.then(
|
|
814
|
+
(value) => {
|
|
815
|
+
fireEnd();
|
|
816
|
+
return value;
|
|
817
|
+
},
|
|
818
|
+
(error) => {
|
|
819
|
+
fireEnd(
|
|
820
|
+
error instanceof Error ? error : new Error(String(error))
|
|
821
|
+
);
|
|
822
|
+
throw error;
|
|
823
|
+
}
|
|
824
|
+
);
|
|
825
|
+
}
|
|
826
|
+
fireEnd();
|
|
827
|
+
return result;
|
|
828
|
+
} catch (error) {
|
|
829
|
+
fireEnd(error instanceof Error ? error : new Error(String(error)));
|
|
830
|
+
throw error;
|
|
831
|
+
}
|
|
832
|
+
});
|
|
833
|
+
};
|
|
834
|
+
}
|
|
724
835
|
function isSdkPage(value) {
|
|
725
836
|
if (typeof value !== "object" || value === null) return false;
|
|
726
837
|
const page = value;
|
|
@@ -749,7 +860,7 @@ function createPageFunction(coreFn, {
|
|
|
749
860
|
} catch (error) {
|
|
750
861
|
throw normalizeError(
|
|
751
862
|
error,
|
|
752
|
-
sdk.context
|
|
863
|
+
resolveCoreOptions(sdk.context)?.adaptError
|
|
753
864
|
);
|
|
754
865
|
}
|
|
755
866
|
}
|
|
@@ -757,82 +868,98 @@ function createPageFunction(coreFn, {
|
|
|
757
868
|
return namedFunctions[functionName];
|
|
758
869
|
}
|
|
759
870
|
function createPaginatedFunction(coreFn, options) {
|
|
760
|
-
const { sdk, schema, name, defaultPageSize, adaptPage } = options;
|
|
871
|
+
const { sdk, schema, name, defaultPageSize, adaptPage, getDeprecation } = options;
|
|
761
872
|
const pageFunction = createPageFunction(coreFn, { sdk, adaptPage });
|
|
762
873
|
const functionName = name || coreFn.name;
|
|
763
874
|
const namedFunctions = {
|
|
764
875
|
[functionName]: function(callOptions) {
|
|
876
|
+
if (arguments[1] !== INTERNAL_CALL) {
|
|
877
|
+
signalDeprecation(sdk.context, functionName, getDeprecation);
|
|
878
|
+
}
|
|
765
879
|
return runInMethodScope(() => {
|
|
766
880
|
const startTime = Date.now();
|
|
767
881
|
const normalizedOptions = callOptions ?? {};
|
|
768
882
|
const args = [normalizedOptions];
|
|
769
883
|
const depth = getCurrentDepth();
|
|
770
|
-
const hooks = sdk.context.hooks;
|
|
771
|
-
const adaptError = sdk.context
|
|
884
|
+
const hooks = isInsideObserver() ? void 0 : sdk.context.hooks;
|
|
885
|
+
const adaptError = resolveCoreOptions(sdk.context)?.adaptError;
|
|
772
886
|
hooks?.onMethodStart?.({
|
|
773
887
|
methodName: functionName,
|
|
774
888
|
args,
|
|
775
889
|
isPaginated: true,
|
|
776
890
|
depth
|
|
777
891
|
});
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
if (hooks?.onMethodEnd) {
|
|
795
|
-
firstPagePromise.then(() => {
|
|
796
|
-
hooks.onMethodEnd({
|
|
797
|
-
methodName: functionName,
|
|
798
|
-
args,
|
|
799
|
-
isPaginated: true,
|
|
800
|
-
depth,
|
|
801
|
-
durationMs: Date.now() - startTime
|
|
802
|
-
});
|
|
803
|
-
}).catch((error) => {
|
|
804
|
-
hooks.onMethodEnd({
|
|
805
|
-
methodName: functionName,
|
|
806
|
-
args,
|
|
807
|
-
isPaginated: true,
|
|
808
|
-
depth,
|
|
809
|
-
durationMs: Date.now() - startTime,
|
|
810
|
-
error: error instanceof Error ? error : new Error(String(error))
|
|
811
|
-
});
|
|
892
|
+
try {
|
|
893
|
+
const validatedOptions = {
|
|
894
|
+
...normalizedOptions,
|
|
895
|
+
...schema ? createValidator(schema, { adaptError })(normalizedOptions) : normalizedOptions
|
|
896
|
+
};
|
|
897
|
+
const pageSize = validatedOptions.pageSize ?? defaultPageSize;
|
|
898
|
+
const optimizedOptions = {
|
|
899
|
+
...validatedOptions,
|
|
900
|
+
pageSize
|
|
901
|
+
};
|
|
902
|
+
const iterator = paginate(pageFunction, optimizedOptions);
|
|
903
|
+
const firstPagePromise = iterator.next().then((result) => {
|
|
904
|
+
if (result.done) {
|
|
905
|
+
throw new Error("Paginate should always iterate at least once");
|
|
906
|
+
}
|
|
907
|
+
return result.value;
|
|
812
908
|
});
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
909
|
+
if (hooks?.onMethodEnd) {
|
|
910
|
+
firstPagePromise.then(() => {
|
|
911
|
+
hooks.onMethodEnd({
|
|
912
|
+
methodName: functionName,
|
|
913
|
+
args,
|
|
914
|
+
isPaginated: true,
|
|
915
|
+
depth,
|
|
916
|
+
durationMs: Date.now() - startTime
|
|
917
|
+
});
|
|
918
|
+
}).catch((error) => {
|
|
919
|
+
hooks.onMethodEnd({
|
|
920
|
+
methodName: functionName,
|
|
921
|
+
args,
|
|
922
|
+
isPaginated: true,
|
|
923
|
+
depth,
|
|
924
|
+
durationMs: Date.now() - startTime,
|
|
925
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
926
|
+
});
|
|
927
|
+
});
|
|
818
928
|
}
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
929
|
+
const pageStream = async function* () {
|
|
930
|
+
yield await firstPagePromise;
|
|
931
|
+
for await (const page of iterator) {
|
|
932
|
+
yield page;
|
|
933
|
+
}
|
|
934
|
+
}();
|
|
935
|
+
return Object.assign(firstPagePromise, {
|
|
936
|
+
[Symbol.asyncIterator]() {
|
|
937
|
+
return pageStream;
|
|
938
|
+
},
|
|
939
|
+
items: function() {
|
|
940
|
+
return {
|
|
941
|
+
[Symbol.asyncIterator]: async function* () {
|
|
942
|
+
for await (const page of pageStream) {
|
|
943
|
+
for (const item of page.data) {
|
|
944
|
+
yield item;
|
|
945
|
+
}
|
|
830
946
|
}
|
|
831
947
|
}
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
}
|
|
835
|
-
})
|
|
948
|
+
};
|
|
949
|
+
}
|
|
950
|
+
});
|
|
951
|
+
} catch (error) {
|
|
952
|
+
const normalizedError = normalizeError(error, adaptError);
|
|
953
|
+
hooks?.onMethodEnd?.({
|
|
954
|
+
methodName: functionName,
|
|
955
|
+
args,
|
|
956
|
+
isPaginated: true,
|
|
957
|
+
depth,
|
|
958
|
+
durationMs: Date.now() - startTime,
|
|
959
|
+
error: normalizedError
|
|
960
|
+
});
|
|
961
|
+
throw normalizedError;
|
|
962
|
+
}
|
|
836
963
|
});
|
|
837
964
|
}
|
|
838
965
|
};
|
|
@@ -841,6 +968,9 @@ function createPaginatedFunction(coreFn, options) {
|
|
|
841
968
|
|
|
842
969
|
// src/utils/plugin-utils.ts
|
|
843
970
|
function createPluginMethod(sdk, config) {
|
|
971
|
+
logDeprecation(
|
|
972
|
+
"createPluginMethod() is deprecated. Author methods with defineMethod instead."
|
|
973
|
+
);
|
|
844
974
|
const { name, inputSchema, handler, ...metaFields } = config;
|
|
845
975
|
const namedHandlers = {
|
|
846
976
|
[name]: async function(options) {
|
|
@@ -864,6 +994,9 @@ function createPluginMethod(sdk, config) {
|
|
|
864
994
|
};
|
|
865
995
|
}
|
|
866
996
|
function createPaginatedPluginMethod(sdk, config) {
|
|
997
|
+
logDeprecation(
|
|
998
|
+
'createPaginatedPluginMethod() is deprecated. Author list methods with defineMethod output "list" instead.'
|
|
999
|
+
);
|
|
867
1000
|
const {
|
|
868
1001
|
name,
|
|
869
1002
|
inputSchema,
|
|
@@ -1095,6 +1228,9 @@ function composePlugins(...plugins) {
|
|
|
1095
1228
|
return collapseStackEntries(entries, "composePlugins");
|
|
1096
1229
|
}
|
|
1097
1230
|
function createPluginStack() {
|
|
1231
|
+
logDeprecation(
|
|
1232
|
+
"createPluginStack() is deprecated. Compose with definePlugin and build with createSdk instead."
|
|
1233
|
+
);
|
|
1098
1234
|
return buildPluginStack(null, "createPluginStack");
|
|
1099
1235
|
}
|
|
1100
1236
|
function buildPluginStack(head, callerLabel) {
|
|
@@ -1179,7 +1315,7 @@ function normalizeImports(deps) {
|
|
|
1179
1315
|
if (!deps) return { plugins: [], bindings: [] };
|
|
1180
1316
|
const seen = /* @__PURE__ */ new Map();
|
|
1181
1317
|
const bindings = [];
|
|
1182
|
-
const add = (binding, id) => {
|
|
1318
|
+
const add = (binding, id, optional) => {
|
|
1183
1319
|
const priorId = seen.get(binding);
|
|
1184
1320
|
if (priorId !== void 0 && priorId !== id) {
|
|
1185
1321
|
throw new Error(
|
|
@@ -1188,7 +1324,7 @@ function normalizeImports(deps) {
|
|
|
1188
1324
|
}
|
|
1189
1325
|
if (priorId === void 0) {
|
|
1190
1326
|
seen.set(binding, id);
|
|
1191
|
-
bindings.push({ binding, id });
|
|
1327
|
+
bindings.push(optional ? { binding, id, optional } : { binding, id });
|
|
1192
1328
|
}
|
|
1193
1329
|
};
|
|
1194
1330
|
for (const plugin of deps) {
|
|
@@ -1196,8 +1332,9 @@ function normalizeImports(deps) {
|
|
|
1196
1332
|
for (const [binding, child] of Object.entries(plugin.exports)) {
|
|
1197
1333
|
add(binding, child.id);
|
|
1198
1334
|
}
|
|
1335
|
+
} else if (plugin.pluginType === "hook") {
|
|
1199
1336
|
} else {
|
|
1200
|
-
add(plugin.name, plugin.id);
|
|
1337
|
+
add(plugin.name, plugin.id, plugin.optional);
|
|
1201
1338
|
}
|
|
1202
1339
|
}
|
|
1203
1340
|
return { plugins: deps, bindings };
|
|
@@ -1209,6 +1346,26 @@ function collectLeafMeta(config) {
|
|
|
1209
1346
|
}
|
|
1210
1347
|
return meta;
|
|
1211
1348
|
}
|
|
1349
|
+
function formatDynamicMemberName(path) {
|
|
1350
|
+
return path.map((seg) => typeof seg === "string" ? seg : `{${seg.param}}`).join(".");
|
|
1351
|
+
}
|
|
1352
|
+
function collectDynamicMembers(members) {
|
|
1353
|
+
if (!members?.length) return void 0;
|
|
1354
|
+
return members.map((member) => {
|
|
1355
|
+
const root = member.path[0];
|
|
1356
|
+
if (typeof root !== "string") {
|
|
1357
|
+
throw new Error(
|
|
1358
|
+
"defineProperty: a dynamicMember path must start with a literal segment (the owning binding), not a { param }."
|
|
1359
|
+
);
|
|
1360
|
+
}
|
|
1361
|
+
const leaf = collectLeafMeta(member) ?? {};
|
|
1362
|
+
return {
|
|
1363
|
+
name: formatDynamicMemberName(member.path),
|
|
1364
|
+
rootBinding: root,
|
|
1365
|
+
meta: member.inputSchema ? { ...leaf, inputSchema: member.inputSchema } : leaf
|
|
1366
|
+
};
|
|
1367
|
+
});
|
|
1368
|
+
}
|
|
1212
1369
|
function defineMethod(config) {
|
|
1213
1370
|
const deps = normalizeImports(config.imports);
|
|
1214
1371
|
return {
|
|
@@ -1219,15 +1376,29 @@ function defineMethod(config) {
|
|
|
1219
1376
|
imports: deps.plugins,
|
|
1220
1377
|
importBindings: deps.bindings,
|
|
1221
1378
|
inputSchema: config.inputSchema,
|
|
1379
|
+
skipInputValidation: config.skipInputValidation,
|
|
1222
1380
|
meta: collectLeafMeta(config),
|
|
1223
1381
|
resolvers: config.resolvers,
|
|
1224
1382
|
formatter: config.formatter,
|
|
1225
1383
|
output: config.output,
|
|
1226
1384
|
positional: config.positional,
|
|
1227
1385
|
setup: config.setup,
|
|
1386
|
+
dispose: config.dispose,
|
|
1228
1387
|
run: config.run
|
|
1229
1388
|
};
|
|
1230
1389
|
}
|
|
1390
|
+
function defineMethodOverride(config) {
|
|
1391
|
+
const { target, namespace, ...rest } = config;
|
|
1392
|
+
return {
|
|
1393
|
+
pluginType: "method-override",
|
|
1394
|
+
name: `override:${target}`,
|
|
1395
|
+
id: namespace ? `${namespace}/override:${target}` : `override:${target}`,
|
|
1396
|
+
target,
|
|
1397
|
+
imports: [],
|
|
1398
|
+
importBindings: [],
|
|
1399
|
+
meta: collectLeafMeta(rest)
|
|
1400
|
+
};
|
|
1401
|
+
}
|
|
1231
1402
|
function defineResolver(config) {
|
|
1232
1403
|
const deps = normalizeImports(config.imports);
|
|
1233
1404
|
const base = { imports: deps.plugins, importBindings: deps.bindings };
|
|
@@ -1319,9 +1490,11 @@ function defineProperty(config) {
|
|
|
1319
1490
|
imports: deps.plugins,
|
|
1320
1491
|
importBindings: deps.bindings,
|
|
1321
1492
|
setup: config.setup,
|
|
1493
|
+
dispose: config.dispose,
|
|
1322
1494
|
value: config.value,
|
|
1323
1495
|
get: config.get,
|
|
1324
|
-
meta: collectLeafMeta(config)
|
|
1496
|
+
meta: collectLeafMeta(config),
|
|
1497
|
+
dynamicMembers: collectDynamicMembers(config.dynamicMembers)
|
|
1325
1498
|
};
|
|
1326
1499
|
}
|
|
1327
1500
|
function declareProperty(config) {
|
|
@@ -1336,6 +1509,37 @@ function declareProperty(config) {
|
|
|
1336
1509
|
importBindings: []
|
|
1337
1510
|
};
|
|
1338
1511
|
}
|
|
1512
|
+
function declareOptionalProperty(config) {
|
|
1513
|
+
const { name, namespace } = parseId(config.id);
|
|
1514
|
+
return {
|
|
1515
|
+
pluginType: "property",
|
|
1516
|
+
name,
|
|
1517
|
+
namespace,
|
|
1518
|
+
id: makeId(name, namespace),
|
|
1519
|
+
standIn: true,
|
|
1520
|
+
optional: true,
|
|
1521
|
+
imports: [],
|
|
1522
|
+
importBindings: []
|
|
1523
|
+
// Requires nothing (phantom carrier `<never, never>`): a consumer that
|
|
1524
|
+
// imports it still passes `createSdk`'s completeness check unprovided. The
|
|
1525
|
+
// import binding is still typed `TValue | undefined` from the descriptor.
|
|
1526
|
+
};
|
|
1527
|
+
}
|
|
1528
|
+
function defineHook(config) {
|
|
1529
|
+
const deps = normalizeImports(config.imports);
|
|
1530
|
+
return {
|
|
1531
|
+
pluginType: "hook",
|
|
1532
|
+
name: config.name,
|
|
1533
|
+
namespace: config.namespace,
|
|
1534
|
+
id: makeId(config.name, config.namespace),
|
|
1535
|
+
imports: deps.plugins,
|
|
1536
|
+
importBindings: deps.bindings,
|
|
1537
|
+
setup: config.setup,
|
|
1538
|
+
dispose: config.dispose,
|
|
1539
|
+
wrap: config.wrap,
|
|
1540
|
+
observe: config.observe
|
|
1541
|
+
};
|
|
1542
|
+
}
|
|
1339
1543
|
function declarePlugin(config) {
|
|
1340
1544
|
const { name, namespace } = parseId(config.id);
|
|
1341
1545
|
return {
|
|
@@ -1350,7 +1554,12 @@ function declarePlugin(config) {
|
|
|
1350
1554
|
};
|
|
1351
1555
|
}
|
|
1352
1556
|
function definePlugin(fnOrConfig) {
|
|
1353
|
-
if (typeof fnOrConfig === "function")
|
|
1557
|
+
if (typeof fnOrConfig === "function") {
|
|
1558
|
+
logDeprecation(
|
|
1559
|
+
"definePlugin(fn) (the function form) is deprecated. Author plugins with defineMethod/defineProperty/definePlugin({ ... }) instead."
|
|
1560
|
+
);
|
|
1561
|
+
return fnOrConfig;
|
|
1562
|
+
}
|
|
1354
1563
|
const config = fnOrConfig;
|
|
1355
1564
|
const deps = normalizeImports(config.imports);
|
|
1356
1565
|
return {
|
|
@@ -1358,12 +1567,24 @@ function definePlugin(fnOrConfig) {
|
|
|
1358
1567
|
name: config.name,
|
|
1359
1568
|
namespace: config.namespace,
|
|
1360
1569
|
id: makeId(config.name, config.namespace, "aggregate"),
|
|
1361
|
-
|
|
1570
|
+
// A re-export synthetic (`selectExports` / `omitExports`) is flattened by
|
|
1571
|
+
// `normalizeExports` into bare bindings, which drops its own `imports:
|
|
1572
|
+
// [source]`. That edge is how `omitExports` keeps an omitted (unbound) leaf
|
|
1573
|
+
// materialized + addressable by id, so preserve every exported aggregate's
|
|
1574
|
+
// imports as extra reachability edges here (bindings unaffected).
|
|
1575
|
+
imports: [...deps.plugins, ...exportedAggregateImports(config.exports)],
|
|
1362
1576
|
importBindings: deps.bindings,
|
|
1363
|
-
exports: normalizeExports(config.exports)
|
|
1364
|
-
middleware: config.middleware
|
|
1577
|
+
exports: normalizeExports(config.exports)
|
|
1365
1578
|
};
|
|
1366
1579
|
}
|
|
1580
|
+
function exportedAggregateImports(exports2) {
|
|
1581
|
+
if (!exports2) return [];
|
|
1582
|
+
const out = [];
|
|
1583
|
+
for (const element of exports2) {
|
|
1584
|
+
if (element.pluginType === "aggregate") out.push(...element.imports);
|
|
1585
|
+
}
|
|
1586
|
+
return out;
|
|
1587
|
+
}
|
|
1367
1588
|
function normalizeExports(exports2) {
|
|
1368
1589
|
if (!exports2) return {};
|
|
1369
1590
|
const out = {};
|
|
@@ -1423,9 +1644,33 @@ function selectExports(source, ...specs) {
|
|
|
1423
1644
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1424
1645
|
};
|
|
1425
1646
|
}
|
|
1647
|
+
function omitExports(source, omit) {
|
|
1648
|
+
const omitSet = new Set(omit);
|
|
1649
|
+
for (const name of omit) {
|
|
1650
|
+
if (!(name in source.exports)) {
|
|
1651
|
+
throw new Error(`omitExports: "${source.id}" has no export "${name}".`);
|
|
1652
|
+
}
|
|
1653
|
+
}
|
|
1654
|
+
const kept = {};
|
|
1655
|
+
for (const [binding, child] of Object.entries(source.exports)) {
|
|
1656
|
+
if (!omitSet.has(binding)) kept[binding] = child;
|
|
1657
|
+
}
|
|
1658
|
+
return {
|
|
1659
|
+
pluginType: "aggregate",
|
|
1660
|
+
name: makeId(`omit`, source.name, "aggregate"),
|
|
1661
|
+
id: `${source.id}#omit:${selectSeq++}`,
|
|
1662
|
+
imports: [source],
|
|
1663
|
+
importBindings: [],
|
|
1664
|
+
exports: kept
|
|
1665
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1666
|
+
};
|
|
1667
|
+
}
|
|
1426
1668
|
|
|
1427
1669
|
// src/model/legacy.ts
|
|
1428
1670
|
function fromFunctionPlugin(fn, config) {
|
|
1671
|
+
logDeprecation(
|
|
1672
|
+
"fromFunctionPlugin() is deprecated. Author plugins with defineMethod/definePlugin instead."
|
|
1673
|
+
);
|
|
1429
1674
|
return {
|
|
1430
1675
|
pluginType: "legacy",
|
|
1431
1676
|
name: config.name,
|
|
@@ -1437,6 +1682,9 @@ function fromFunctionPlugin(fn, config) {
|
|
|
1437
1682
|
};
|
|
1438
1683
|
}
|
|
1439
1684
|
function defineLegacyMerge(args) {
|
|
1685
|
+
logDeprecation(
|
|
1686
|
+
"defineLegacyMerge() is deprecated. Build directly with createSdk(root, { configuration }) instead."
|
|
1687
|
+
);
|
|
1440
1688
|
return {
|
|
1441
1689
|
pluginType: "legacy-merge",
|
|
1442
1690
|
name: args.name,
|
|
@@ -1503,40 +1751,60 @@ function pluginEntryMeta(entry) {
|
|
|
1503
1751
|
if (entry.pluginType === "property" && entry.meta) return entry.meta;
|
|
1504
1752
|
return void 0;
|
|
1505
1753
|
}
|
|
1506
|
-
function
|
|
1754
|
+
function foldDynamicMembers(entry, surfaceBindings, meta) {
|
|
1755
|
+
if (entry.pluginType !== "property" || !entry.dynamicMembers) return;
|
|
1756
|
+
for (const member of entry.dynamicMembers) {
|
|
1757
|
+
if (!surfaceBindings.has(member.rootBinding)) {
|
|
1758
|
+
throw new Error(
|
|
1759
|
+
`dynamicMember "${member.name}": its root "${member.rootBinding}" is not a surfaced member. A dynamic member's path must start with a real binding.`
|
|
1760
|
+
);
|
|
1761
|
+
}
|
|
1762
|
+
meta[member.name] = member.meta;
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
function collectSurfaceProjection(context, formatterSdk) {
|
|
1507
1766
|
const meta = {};
|
|
1508
|
-
const surface = {};
|
|
1509
1767
|
const entries = {};
|
|
1510
1768
|
for (const [binding, id] of Object.entries(context.surface)) {
|
|
1511
1769
|
const entry = context.plugins[id];
|
|
1512
1770
|
if (!entry || entry.pluginType === "aggregate") continue;
|
|
1513
|
-
surface[binding] = entry.value;
|
|
1514
1771
|
entries[binding] = entry;
|
|
1515
1772
|
const m = pluginEntryMeta(entry);
|
|
1516
1773
|
if (m) meta[binding] = m;
|
|
1517
1774
|
}
|
|
1775
|
+
const surfaceBindings = new Set(Object.keys(context.surface));
|
|
1776
|
+
for (const entry of Object.values(entries)) {
|
|
1777
|
+
foldDynamicMembers(entry, surfaceBindings, meta);
|
|
1778
|
+
}
|
|
1518
1779
|
const formatters = {};
|
|
1519
1780
|
const boundResolvers = {};
|
|
1520
1781
|
const positional = {};
|
|
1521
1782
|
for (const [binding, entry] of Object.entries(entries)) {
|
|
1522
|
-
const f = normalizeFormatter(entry,
|
|
1783
|
+
const f = normalizeFormatter(entry, formatterSdk);
|
|
1523
1784
|
if (f) formatters[binding] = f;
|
|
1524
1785
|
const r = normalizeBoundResolvers(entry);
|
|
1525
1786
|
if (r) boundResolvers[binding] = r;
|
|
1526
1787
|
const p = methodPositional(entry);
|
|
1527
1788
|
if (p) positional[binding] = p;
|
|
1528
1789
|
}
|
|
1790
|
+
return { meta, formatters, boundResolvers, positional };
|
|
1791
|
+
}
|
|
1792
|
+
function buildSurfaceRegistry(context, packageFilter) {
|
|
1793
|
+
const surface = {};
|
|
1794
|
+
for (const [binding, id] of Object.entries(context.surface)) {
|
|
1795
|
+
const entry = context.plugins[id];
|
|
1796
|
+
if (!entry || entry.pluginType === "aggregate") continue;
|
|
1797
|
+
surface[binding] = entry.pluginType === "property" && entry.getValue ? entry.getValue() : entry.value;
|
|
1798
|
+
}
|
|
1529
1799
|
return buildRegistry({
|
|
1530
1800
|
sdk: surface,
|
|
1531
|
-
|
|
1532
|
-
formatters,
|
|
1533
|
-
boundResolvers,
|
|
1534
|
-
positional,
|
|
1801
|
+
...collectSurfaceProjection(context, surface),
|
|
1535
1802
|
packageFilter
|
|
1536
1803
|
});
|
|
1537
1804
|
}
|
|
1538
1805
|
|
|
1539
1806
|
// src/model/builtins.ts
|
|
1807
|
+
var coreOptionsPluginRef = declareOptionalProperty({ id: CORE_OPTIONS_ID });
|
|
1540
1808
|
var dangerousContextPlugin = {
|
|
1541
1809
|
pluginType: "property",
|
|
1542
1810
|
name: "context",
|
|
@@ -1555,12 +1823,15 @@ var getRegistryPlugin = defineMethod({
|
|
|
1555
1823
|
});
|
|
1556
1824
|
|
|
1557
1825
|
// src/model/materialize.ts
|
|
1826
|
+
var FRAMEWORK_CONFIGURATION_IDS = /* @__PURE__ */ new Set([
|
|
1827
|
+
CORE_OPTIONS_ID
|
|
1828
|
+
]);
|
|
1558
1829
|
function normalizeOutput(output) {
|
|
1559
1830
|
if (output === void 0) return { type: "raw" };
|
|
1560
1831
|
if (typeof output === "string") return { type: output };
|
|
1561
1832
|
return output;
|
|
1562
1833
|
}
|
|
1563
|
-
var CONTEXT = Symbol("kitcore.context");
|
|
1834
|
+
var CONTEXT = Symbol.for("kitcore.context");
|
|
1564
1835
|
function getContext(sdk) {
|
|
1565
1836
|
return sdk[CONTEXT];
|
|
1566
1837
|
}
|
|
@@ -1622,7 +1893,7 @@ function topoOrder(descriptors) {
|
|
|
1622
1893
|
for (const id of descriptors.keys()) visit(id);
|
|
1623
1894
|
return order;
|
|
1624
1895
|
}
|
|
1625
|
-
function collectPlugins(root, materialized = /* @__PURE__ */ new Set()) {
|
|
1896
|
+
function collectPlugins(root, materialized = /* @__PURE__ */ new Set(), configuration) {
|
|
1626
1897
|
const byId = /* @__PURE__ */ new Map();
|
|
1627
1898
|
const visit = (plugin) => {
|
|
1628
1899
|
if (materialized.has(plugin.id)) return;
|
|
@@ -1645,8 +1916,51 @@ function collectPlugins(root, materialized = /* @__PURE__ */ new Set()) {
|
|
|
1645
1916
|
for (const edge of edgesOf(plugin)) visit(edge);
|
|
1646
1917
|
};
|
|
1647
1918
|
visit(root);
|
|
1919
|
+
if (configuration) {
|
|
1920
|
+
for (const [id, value] of Object.entries(configuration)) {
|
|
1921
|
+
const existing = byId.get(id);
|
|
1922
|
+
if (!existing) {
|
|
1923
|
+
if (FRAMEWORK_CONFIGURATION_IDS.has(id)) {
|
|
1924
|
+
const { name, namespace } = parseId(id);
|
|
1925
|
+
byId.set(id, {
|
|
1926
|
+
pluginType: "property",
|
|
1927
|
+
name,
|
|
1928
|
+
namespace,
|
|
1929
|
+
id,
|
|
1930
|
+
imports: [],
|
|
1931
|
+
importBindings: [],
|
|
1932
|
+
value
|
|
1933
|
+
});
|
|
1934
|
+
continue;
|
|
1935
|
+
}
|
|
1936
|
+
throw new Error(
|
|
1937
|
+
`createSdk: configuration id "${id}" matches no plugin in the graph. An injected value must satisfy a property stand-in reachable from the root.`
|
|
1938
|
+
);
|
|
1939
|
+
}
|
|
1940
|
+
if (existing.pluginType !== "property") {
|
|
1941
|
+
throw new Error(
|
|
1942
|
+
`createSdk: configuration id "${id}" resolves to a "${existing.pluginType}" plugin; only property values can be injected.`
|
|
1943
|
+
);
|
|
1944
|
+
}
|
|
1945
|
+
if (!isStandIn(existing)) {
|
|
1946
|
+
throw new Error(
|
|
1947
|
+
`createSdk: configuration id "${id}" collides with a registered provider. A property is either injected or provided by a plugin, not both.`
|
|
1948
|
+
);
|
|
1949
|
+
}
|
|
1950
|
+
byId.set(id, {
|
|
1951
|
+
pluginType: "property",
|
|
1952
|
+
name: existing.name,
|
|
1953
|
+
namespace: existing.namespace,
|
|
1954
|
+
id,
|
|
1955
|
+
imports: [],
|
|
1956
|
+
importBindings: [],
|
|
1957
|
+
value
|
|
1958
|
+
});
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1648
1961
|
for (const [id, plugin] of byId) {
|
|
1649
1962
|
if (isStandIn(plugin)) {
|
|
1963
|
+
if ("optional" in plugin && plugin.optional) continue;
|
|
1650
1964
|
throw new Error(
|
|
1651
1965
|
`createSdk: missing dependency "${id}". A plugin depends on it (via a stand-in) but no implementation was registered.`
|
|
1652
1966
|
);
|
|
@@ -1654,7 +1968,7 @@ function collectPlugins(root, materialized = /* @__PURE__ */ new Set()) {
|
|
|
1654
1968
|
}
|
|
1655
1969
|
return byId;
|
|
1656
1970
|
}
|
|
1657
|
-
function bindValue(target, key2, entry) {
|
|
1971
|
+
function bindValue(target, key2, entry, callType = "surface") {
|
|
1658
1972
|
if (entry.pluginType === "property" && entry.getValue) {
|
|
1659
1973
|
Object.defineProperty(target, key2, {
|
|
1660
1974
|
get: entry.getValue,
|
|
@@ -1662,8 +1976,9 @@ function bindValue(target, key2, entry) {
|
|
|
1662
1976
|
configurable: true
|
|
1663
1977
|
});
|
|
1664
1978
|
} else {
|
|
1979
|
+
const value = callType === "internal" && entry.pluginType === "method" ? entry.internalValue ?? entry.value : entry.value;
|
|
1665
1980
|
Object.defineProperty(target, key2, {
|
|
1666
|
-
value
|
|
1981
|
+
value,
|
|
1667
1982
|
writable: true,
|
|
1668
1983
|
enumerable: true,
|
|
1669
1984
|
configurable: true
|
|
@@ -1681,8 +1996,18 @@ function buildSurface(context, ...maps) {
|
|
|
1681
1996
|
}
|
|
1682
1997
|
function buildImports(plugins, importBindings) {
|
|
1683
1998
|
const imports = {};
|
|
1684
|
-
for (const { binding, id } of importBindings) {
|
|
1685
|
-
|
|
1999
|
+
for (const { binding, id, optional } of importBindings) {
|
|
2000
|
+
const entry = plugins[id];
|
|
2001
|
+
if (!entry && optional) {
|
|
2002
|
+
Object.defineProperty(imports, binding, {
|
|
2003
|
+
value: void 0,
|
|
2004
|
+
writable: true,
|
|
2005
|
+
enumerable: true,
|
|
2006
|
+
configurable: true
|
|
2007
|
+
});
|
|
2008
|
+
continue;
|
|
2009
|
+
}
|
|
2010
|
+
bindValue(imports, binding, entry, "internal");
|
|
1686
2011
|
}
|
|
1687
2012
|
return imports;
|
|
1688
2013
|
}
|
|
@@ -1706,9 +2031,31 @@ function materialize(descriptors, context) {
|
|
|
1706
2031
|
buildEagerArtifacts(descriptors, context, states);
|
|
1707
2032
|
bindAttachments(descriptors, context);
|
|
1708
2033
|
resolveAggregates(descriptors, context);
|
|
1709
|
-
assembleMiddleware(descriptors, context);
|
|
2034
|
+
assembleMiddleware(descriptors, context, states);
|
|
2035
|
+
assembleHooks(descriptors, context, states);
|
|
2036
|
+
applyMethodOverrides(descriptors, context);
|
|
1710
2037
|
return context.plugins;
|
|
1711
2038
|
}
|
|
2039
|
+
function applyMethodOverride(context, override) {
|
|
2040
|
+
const entry = context.plugins[override.target];
|
|
2041
|
+
if (!entry) {
|
|
2042
|
+
throw new Error(
|
|
2043
|
+
`defineMethodOverride: no method "${override.target}" to override. Include the target method in the SDK build.`
|
|
2044
|
+
);
|
|
2045
|
+
}
|
|
2046
|
+
if (entry.pluginType !== "method") {
|
|
2047
|
+
throw new Error(
|
|
2048
|
+
`defineMethodOverride: "${override.target}" is a ${entry.pluginType}, not a method; only methods can be overridden.`
|
|
2049
|
+
);
|
|
2050
|
+
}
|
|
2051
|
+
entry.meta = { ...entry.meta, ...override.meta };
|
|
2052
|
+
}
|
|
2053
|
+
function applyMethodOverrides(descriptors, context) {
|
|
2054
|
+
for (const descriptor of descriptors.values()) {
|
|
2055
|
+
if (descriptor.pluginType !== "method-override") continue;
|
|
2056
|
+
applyMethodOverride(context, descriptor);
|
|
2057
|
+
}
|
|
2058
|
+
}
|
|
1712
2059
|
function bindResolver(resolver, plugins) {
|
|
1713
2060
|
switch (resolver.type) {
|
|
1714
2061
|
case "static":
|
|
@@ -1754,25 +2101,24 @@ function bindResolver(resolver, plugins) {
|
|
|
1754
2101
|
bound.definitions = bindDefinitions(resolver.definitions, plugins);
|
|
1755
2102
|
return bound;
|
|
1756
2103
|
}
|
|
1757
|
-
|
|
2104
|
+
case "dynamic": {
|
|
1758
2105
|
const imports = buildImports(plugins, resolver.importBindings);
|
|
1759
|
-
const bound = {
|
|
1760
|
-
type: "dynamic",
|
|
1761
|
-
requireParameters: resolver.requireParameters,
|
|
1762
|
-
inputType: resolver.inputType,
|
|
1763
|
-
placeholder: resolver.placeholder,
|
|
1764
|
-
prompt: resolver.prompt
|
|
1765
|
-
};
|
|
1766
2106
|
const {
|
|
1767
2107
|
getContext: getContext2,
|
|
1768
2108
|
listItems,
|
|
1769
2109
|
tryResolveWithoutPrompt,
|
|
1770
2110
|
tryResolveFromSearch
|
|
1771
2111
|
} = resolver;
|
|
2112
|
+
const bound = {
|
|
2113
|
+
type: "dynamic",
|
|
2114
|
+
requireParameters: resolver.requireParameters,
|
|
2115
|
+
inputType: resolver.inputType,
|
|
2116
|
+
placeholder: resolver.placeholder,
|
|
2117
|
+
prompt: resolver.prompt,
|
|
2118
|
+
listItems: ({ input, context, search, cursor }) => listItems({ imports, input, context, search, cursor })
|
|
2119
|
+
};
|
|
1772
2120
|
if (getContext2)
|
|
1773
2121
|
bound.getContext = ({ input }) => getContext2({ imports, input });
|
|
1774
|
-
if (listItems)
|
|
1775
|
-
bound.listItems = ({ input, context, search, cursor }) => listItems({ imports, input, context, search, cursor });
|
|
1776
2122
|
if (tryResolveWithoutPrompt) {
|
|
1777
2123
|
bound.tryResolveWithoutPrompt = ({ input }) => tryResolveWithoutPrompt({ imports, input });
|
|
1778
2124
|
}
|
|
@@ -1781,6 +2127,12 @@ function bindResolver(resolver, plugins) {
|
|
|
1781
2127
|
}
|
|
1782
2128
|
return bound;
|
|
1783
2129
|
}
|
|
2130
|
+
default: {
|
|
2131
|
+
const unhandled = resolver;
|
|
2132
|
+
throw new Error(
|
|
2133
|
+
`unhandled resolver kind: ${unhandled.type}`
|
|
2134
|
+
);
|
|
2135
|
+
}
|
|
1784
2136
|
}
|
|
1785
2137
|
}
|
|
1786
2138
|
function bindFields(fields, plugins) {
|
|
@@ -1851,25 +2203,11 @@ function runLegacyPass(descriptors, context) {
|
|
|
1851
2203
|
if (!("getRegistry" in exports2)) {
|
|
1852
2204
|
let getRegistry2 = function(options) {
|
|
1853
2205
|
const sdk = this ?? exports2;
|
|
1854
|
-
const
|
|
1855
|
-
|
|
1856
|
-
const boundResolvers = {};
|
|
1857
|
-
for (const [binding, id2] of Object.entries(context.surface)) {
|
|
1858
|
-
const entry = context.plugins[id2];
|
|
1859
|
-
if (!entry || entry.pluginType === "aggregate") continue;
|
|
1860
|
-
const m = pluginEntryMeta(entry);
|
|
1861
|
-
if (m) meta2[binding] = m;
|
|
1862
|
-
const f = normalizeFormatter(entry, sdk);
|
|
1863
|
-
if (f) formatters[binding] = f;
|
|
1864
|
-
const r = normalizeBoundResolvers(entry);
|
|
1865
|
-
if (r) boundResolvers[binding] = r;
|
|
1866
|
-
}
|
|
1867
|
-
Object.assign(meta2, context.meta);
|
|
2206
|
+
const projection = collectSurfaceProjection(context, sdk);
|
|
2207
|
+
Object.assign(projection.meta, context.meta);
|
|
1868
2208
|
return buildRegistry({
|
|
1869
2209
|
sdk,
|
|
1870
|
-
|
|
1871
|
-
formatters,
|
|
1872
|
-
boundResolvers,
|
|
2210
|
+
...projection,
|
|
1873
2211
|
packageFilter: options?.package
|
|
1874
2212
|
});
|
|
1875
2213
|
};
|
|
@@ -1914,7 +2252,10 @@ function buildMethodEntries(descriptors, context, states) {
|
|
|
1914
2252
|
next = (i) => wrap.run({
|
|
1915
2253
|
imports: buildImports(plugins, wrap.owner.importBindings),
|
|
1916
2254
|
next: inner,
|
|
1917
|
-
input: i
|
|
2255
|
+
input: i,
|
|
2256
|
+
// Overwritten by the chain item's own closure with the owning
|
|
2257
|
+
// hook's setup state.
|
|
2258
|
+
state: void 0
|
|
1918
2259
|
});
|
|
1919
2260
|
}
|
|
1920
2261
|
return next(input);
|
|
@@ -1928,7 +2269,8 @@ function buildMethodEntries(descriptors, context, states) {
|
|
|
1928
2269
|
schema: descriptor.inputSchema,
|
|
1929
2270
|
name: descriptor.name,
|
|
1930
2271
|
defaultPageSize: out.defaultPageSize,
|
|
1931
|
-
adaptPage: out.adaptPage
|
|
2272
|
+
adaptPage: out.adaptPage,
|
|
2273
|
+
getDeprecation: () => entry.meta?.deprecation
|
|
1932
2274
|
}
|
|
1933
2275
|
);
|
|
1934
2276
|
} else if (out.type === "item") {
|
|
@@ -1937,25 +2279,43 @@ function buildMethodEntries(descriptors, context, states) {
|
|
|
1937
2279
|
});
|
|
1938
2280
|
entry.value = createFunction(
|
|
1939
2281
|
fold(itemCore),
|
|
1940
|
-
{
|
|
2282
|
+
{
|
|
2283
|
+
sdk,
|
|
2284
|
+
schema: descriptor.inputSchema,
|
|
2285
|
+
name: descriptor.name,
|
|
2286
|
+
getDeprecation: () => entry.meta?.deprecation
|
|
2287
|
+
}
|
|
1941
2288
|
);
|
|
1942
2289
|
} else {
|
|
1943
|
-
entry.value = (
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
2290
|
+
entry.value = createRawFunction(
|
|
2291
|
+
(input) => fold(callRun)(input),
|
|
2292
|
+
{
|
|
2293
|
+
sdk,
|
|
2294
|
+
name: descriptor.name,
|
|
2295
|
+
schema: descriptor.skipInputValidation ? void 0 : descriptor.inputSchema,
|
|
2296
|
+
positional: descriptor.positional,
|
|
2297
|
+
// The boundary reads the deprecation LIVE off the entry, so a
|
|
2298
|
+
// deprecation merged after build (defineMethodOverride, addPlugin)
|
|
2299
|
+
// fires too.
|
|
2300
|
+
getDeprecation: () => entry.meta?.deprecation
|
|
2301
|
+
}
|
|
2302
|
+
);
|
|
1947
2303
|
}
|
|
2304
|
+
const canonicalValue = entry.value;
|
|
1948
2305
|
if (descriptor.positional) {
|
|
1949
2306
|
const names = descriptor.positional;
|
|
1950
|
-
const
|
|
1951
|
-
entry.value = (...args) => {
|
|
2307
|
+
const pack = (args) => {
|
|
1952
2308
|
const packed = {};
|
|
1953
2309
|
names.forEach((name, i) => {
|
|
1954
2310
|
if (i < args.length) packed[name] = args[i];
|
|
1955
2311
|
});
|
|
1956
|
-
return
|
|
2312
|
+
return packed;
|
|
1957
2313
|
};
|
|
2314
|
+
entry.value = (...args) => canonicalValue(pack(args));
|
|
2315
|
+
entry.internalValue = (...args) => canonicalValue(pack(args), INTERNAL_CALL);
|
|
1958
2316
|
entry.positional = names;
|
|
2317
|
+
} else {
|
|
2318
|
+
entry.internalValue = (input) => canonicalValue(input, INTERNAL_CALL);
|
|
1959
2319
|
}
|
|
1960
2320
|
plugins[id] = entry;
|
|
1961
2321
|
}
|
|
@@ -1967,7 +2327,7 @@ function buildEagerArtifacts(descriptors, context, states) {
|
|
|
1967
2327
|
const ensureBuilt = (id) => {
|
|
1968
2328
|
if (built.has(id)) return;
|
|
1969
2329
|
const descriptor = descriptors.get(id);
|
|
1970
|
-
if (!descriptor || descriptor.pluginType === "aggregate" || descriptor.pluginType === "legacy") {
|
|
2330
|
+
if (!descriptor || descriptor.pluginType === "aggregate" || descriptor.pluginType === "legacy" || descriptor.pluginType === "method-override" || isStandIn(descriptor)) {
|
|
1971
2331
|
built.add(id);
|
|
1972
2332
|
return;
|
|
1973
2333
|
}
|
|
@@ -1976,6 +2336,30 @@ function buildEagerArtifacts(descriptors, context, states) {
|
|
|
1976
2336
|
}
|
|
1977
2337
|
building.add(id);
|
|
1978
2338
|
for (const { id: depId } of descriptor.importBindings) ensureBuilt(depId);
|
|
2339
|
+
const recordDisposer = () => {
|
|
2340
|
+
const dispose = descriptor.dispose;
|
|
2341
|
+
if (!dispose) return;
|
|
2342
|
+
context.disposers?.push({
|
|
2343
|
+
id,
|
|
2344
|
+
dispose: (input) => dispose({
|
|
2345
|
+
imports: buildImports(plugins, descriptor.importBindings),
|
|
2346
|
+
state: states.get(id),
|
|
2347
|
+
input
|
|
2348
|
+
})
|
|
2349
|
+
});
|
|
2350
|
+
};
|
|
2351
|
+
if (descriptor.pluginType === "hook") {
|
|
2352
|
+
states.set(
|
|
2353
|
+
id,
|
|
2354
|
+
descriptor.setup ? descriptor.setup({
|
|
2355
|
+
imports: buildImports(plugins, descriptor.importBindings)
|
|
2356
|
+
}) : void 0
|
|
2357
|
+
);
|
|
2358
|
+
recordDisposer();
|
|
2359
|
+
building.delete(id);
|
|
2360
|
+
built.add(id);
|
|
2361
|
+
return;
|
|
2362
|
+
}
|
|
1979
2363
|
if (descriptor.pluginType === "method") {
|
|
1980
2364
|
states.set(
|
|
1981
2365
|
id,
|
|
@@ -1995,7 +2379,8 @@ function buildEagerArtifacts(descriptors, context, states) {
|
|
|
1995
2379
|
pluginType: "property",
|
|
1996
2380
|
name: descriptor.name,
|
|
1997
2381
|
value: context,
|
|
1998
|
-
meta: descriptor.meta
|
|
2382
|
+
meta: descriptor.meta,
|
|
2383
|
+
dynamicMembers: descriptor.dynamicMembers
|
|
1999
2384
|
};
|
|
2000
2385
|
} else if (descriptor.get) {
|
|
2001
2386
|
const get = descriptor.get;
|
|
@@ -2007,22 +2392,68 @@ function buildEagerArtifacts(descriptors, context, states) {
|
|
|
2007
2392
|
imports: buildImports(plugins, importBindings),
|
|
2008
2393
|
state: states.get(id)
|
|
2009
2394
|
}),
|
|
2010
|
-
meta: descriptor.meta
|
|
2395
|
+
meta: descriptor.meta,
|
|
2396
|
+
dynamicMembers: descriptor.dynamicMembers
|
|
2011
2397
|
};
|
|
2012
2398
|
} else {
|
|
2013
2399
|
plugins[id] = {
|
|
2014
2400
|
pluginType: "property",
|
|
2015
2401
|
name: descriptor.name,
|
|
2016
2402
|
value: descriptor.value,
|
|
2017
|
-
meta: descriptor.meta
|
|
2403
|
+
meta: descriptor.meta,
|
|
2404
|
+
dynamicMembers: descriptor.dynamicMembers
|
|
2018
2405
|
};
|
|
2019
2406
|
}
|
|
2020
2407
|
}
|
|
2408
|
+
recordDisposer();
|
|
2021
2409
|
building.delete(id);
|
|
2022
2410
|
built.add(id);
|
|
2023
2411
|
};
|
|
2024
2412
|
for (const id of descriptors.keys()) ensureBuilt(id);
|
|
2025
2413
|
}
|
|
2414
|
+
function resolvePlugin(sdk, ref) {
|
|
2415
|
+
const entry = getContext(sdk).plugins[ref.id];
|
|
2416
|
+
if (!entry) {
|
|
2417
|
+
if (ref.optional) {
|
|
2418
|
+
return void 0;
|
|
2419
|
+
}
|
|
2420
|
+
throw new Error(
|
|
2421
|
+
`resolvePlugin: plugin "${ref.id}" is not materialized on the SDK.`
|
|
2422
|
+
);
|
|
2423
|
+
}
|
|
2424
|
+
if (entry.pluginType === "property" && entry.getValue) {
|
|
2425
|
+
return entry.getValue();
|
|
2426
|
+
}
|
|
2427
|
+
if (entry.pluginType === "method" && entry.internalValue) {
|
|
2428
|
+
return entry.internalValue;
|
|
2429
|
+
}
|
|
2430
|
+
return entry.value;
|
|
2431
|
+
}
|
|
2432
|
+
var CoreDisposeError = class extends Error {
|
|
2433
|
+
constructor(errors) {
|
|
2434
|
+
super(`disposeSdk: ${errors.length} dispose callback(s) failed.`);
|
|
2435
|
+
this.name = "CoreDisposeError";
|
|
2436
|
+
this.errors = errors;
|
|
2437
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
2438
|
+
}
|
|
2439
|
+
};
|
|
2440
|
+
function disposeSdk(sdk, input) {
|
|
2441
|
+
const context = getContext(sdk);
|
|
2442
|
+
if (context.disposed) return context.disposed;
|
|
2443
|
+
const disposers = context.disposers ?? [];
|
|
2444
|
+
context.disposed = (async () => {
|
|
2445
|
+
const errors = [];
|
|
2446
|
+
for (let i = disposers.length - 1; i >= 0; i--) {
|
|
2447
|
+
try {
|
|
2448
|
+
await disposers[i].dispose(input);
|
|
2449
|
+
} catch (error) {
|
|
2450
|
+
errors.push(error);
|
|
2451
|
+
}
|
|
2452
|
+
}
|
|
2453
|
+
if (errors.length > 0) throw new CoreDisposeError(errors);
|
|
2454
|
+
})();
|
|
2455
|
+
return context.disposed;
|
|
2456
|
+
}
|
|
2026
2457
|
function resolveAggregates(descriptors, context) {
|
|
2027
2458
|
const plugins = context.plugins;
|
|
2028
2459
|
for (const [id, descriptor] of descriptors) {
|
|
@@ -2034,39 +2465,74 @@ function resolveAggregates(descriptors, context) {
|
|
|
2034
2465
|
plugins[id] = { pluginType: "aggregate", name: descriptor.name, exports: exports2 };
|
|
2035
2466
|
}
|
|
2036
2467
|
}
|
|
2037
|
-
function assembleMiddleware(descriptors, context) {
|
|
2468
|
+
function assembleMiddleware(descriptors, context, states) {
|
|
2038
2469
|
const plugins = context.plugins;
|
|
2039
2470
|
for (const id of topoOrder(descriptors)) {
|
|
2040
2471
|
const descriptor = descriptors.get(id);
|
|
2041
|
-
if (!descriptor || descriptor.pluginType !== "
|
|
2472
|
+
if (!descriptor || descriptor.pluginType !== "hook" || !descriptor.wrap) {
|
|
2042
2473
|
continue;
|
|
2043
2474
|
}
|
|
2044
|
-
for (const [targetBinding, fn] of Object.entries(descriptor.
|
|
2475
|
+
for (const [targetBinding, fn] of Object.entries(descriptor.wrap)) {
|
|
2045
2476
|
const edge = descriptor.importBindings.find(
|
|
2046
2477
|
(b) => b.binding === targetBinding
|
|
2047
2478
|
);
|
|
2048
2479
|
if (!edge) {
|
|
2049
2480
|
throw new Error(
|
|
2050
|
-
`createSdk:
|
|
2481
|
+
`createSdk: wrap target "${targetBinding}" in hook "${id}" is not a direct dependency. A wrap target must be a declared import of the wrapping hook.`
|
|
2051
2482
|
);
|
|
2052
2483
|
}
|
|
2053
2484
|
const target = plugins[edge.id];
|
|
2054
2485
|
if (!target || target.pluginType !== "method") {
|
|
2055
2486
|
throw new Error(
|
|
2056
|
-
`createSdk:
|
|
2487
|
+
`createSdk: wrap target "${targetBinding}" in hook "${id}" does not resolve to a method.`
|
|
2057
2488
|
);
|
|
2058
2489
|
}
|
|
2059
2490
|
if (target.output?.type === "list") {
|
|
2060
2491
|
throw new Error(
|
|
2061
|
-
`createSdk:
|
|
2492
|
+
`createSdk: wrap target "${targetBinding}" in hook "${id}" resolves to a list-output method, which does not support wrapping yet.`
|
|
2062
2493
|
);
|
|
2063
2494
|
}
|
|
2064
|
-
target.chain.push({
|
|
2495
|
+
target.chain.push({
|
|
2496
|
+
run: (bag) => fn({ ...bag, state: states.get(id) }),
|
|
2497
|
+
owner: descriptor
|
|
2498
|
+
});
|
|
2499
|
+
}
|
|
2500
|
+
}
|
|
2501
|
+
}
|
|
2502
|
+
function assembleHooks(descriptors, context, states) {
|
|
2503
|
+
const plugins = context.plugins;
|
|
2504
|
+
for (const id of topoOrder(descriptors)) {
|
|
2505
|
+
const descriptor = descriptors.get(id);
|
|
2506
|
+
if (!descriptor || descriptor.pluginType !== "hook" || !descriptor.observe) {
|
|
2507
|
+
continue;
|
|
2065
2508
|
}
|
|
2509
|
+
const { observe } = descriptor;
|
|
2510
|
+
const imports = buildImports(plugins, descriptor.importBindings);
|
|
2511
|
+
const state = states.get(id);
|
|
2512
|
+
const contributed = {};
|
|
2513
|
+
if (observe.onMethodStart) {
|
|
2514
|
+
const onStart = observe.onMethodStart;
|
|
2515
|
+
contributed.onMethodStart = (input) => {
|
|
2516
|
+
runIsolatedObserver(() => onStart({ imports, input, state }));
|
|
2517
|
+
};
|
|
2518
|
+
}
|
|
2519
|
+
if (observe.onMethodEnd) {
|
|
2520
|
+
const onEnd = observe.onMethodEnd;
|
|
2521
|
+
contributed.onMethodEnd = (input) => {
|
|
2522
|
+
runIsolatedObserver(() => onEnd({ imports, input, state }));
|
|
2523
|
+
};
|
|
2524
|
+
}
|
|
2525
|
+
context.hooks = buildHooks(context.hooks, contributed);
|
|
2066
2526
|
}
|
|
2067
2527
|
}
|
|
2068
|
-
function createSdk(root) {
|
|
2069
|
-
const context = {
|
|
2528
|
+
function createSdk(root, options) {
|
|
2529
|
+
const context = {
|
|
2530
|
+
plugins: {},
|
|
2531
|
+
meta: {},
|
|
2532
|
+
hooks: {},
|
|
2533
|
+
surface: {},
|
|
2534
|
+
disposers: []
|
|
2535
|
+
};
|
|
2070
2536
|
if (root.pluginType === "legacy-merge") {
|
|
2071
2537
|
const { legacy, plugin } = root;
|
|
2072
2538
|
const collectRoot = {
|
|
@@ -2077,7 +2543,10 @@ function createSdk(root) {
|
|
|
2077
2543
|
importBindings: [],
|
|
2078
2544
|
exports: {}
|
|
2079
2545
|
};
|
|
2080
|
-
const plugins2 = materialize(
|
|
2546
|
+
const plugins2 = materialize(
|
|
2547
|
+
collectPlugins(collectRoot, void 0, options?.configuration),
|
|
2548
|
+
context
|
|
2549
|
+
);
|
|
2081
2550
|
const legacyExports = plugins2[legacy.id].exports;
|
|
2082
2551
|
let pluginSurface;
|
|
2083
2552
|
if (plugin.pluginType === "aggregate") {
|
|
@@ -2094,7 +2563,10 @@ function createSdk(root) {
|
|
|
2094
2563
|
}
|
|
2095
2564
|
return buildSurface(context, legacyExports, pluginSurface);
|
|
2096
2565
|
}
|
|
2097
|
-
const plugins = materialize(
|
|
2566
|
+
const plugins = materialize(
|
|
2567
|
+
collectPlugins(root, void 0, options?.configuration),
|
|
2568
|
+
context
|
|
2569
|
+
);
|
|
2098
2570
|
if (root.pluginType === "method" || root.pluginType === "property") {
|
|
2099
2571
|
context.surface[root.name] = root.id;
|
|
2100
2572
|
const sdk = buildSurface(context);
|
|
@@ -2108,7 +2580,7 @@ function createSdk(root) {
|
|
|
2108
2580
|
function addModelPlugin(sdk, plugin, options = {}) {
|
|
2109
2581
|
const override = options.override === true;
|
|
2110
2582
|
const context = getContext(sdk);
|
|
2111
|
-
const surfaceKeys = plugin.pluginType === "aggregate" ? Object.keys(plugin.exports) : [plugin.name];
|
|
2583
|
+
const surfaceKeys = plugin.pluginType === "aggregate" ? Object.keys(plugin.exports) : plugin.pluginType === "hook" ? [] : [plugin.name];
|
|
2112
2584
|
checkRootKeyCollisions(sdk, surfaceKeys, override, "addPlugin");
|
|
2113
2585
|
const materialized = new Set(Object.keys(context.plugins));
|
|
2114
2586
|
if (override && materialized.has(plugin.id)) {
|
|
@@ -2117,6 +2589,7 @@ function addModelPlugin(sdk, plugin, options = {}) {
|
|
|
2117
2589
|
);
|
|
2118
2590
|
}
|
|
2119
2591
|
materialize(collectPlugins(plugin, materialized), context);
|
|
2592
|
+
if (plugin.pluginType === "hook") return;
|
|
2120
2593
|
const entry = context.plugins[plugin.id];
|
|
2121
2594
|
if (entry.pluginType === "aggregate") {
|
|
2122
2595
|
Object.defineProperties(
|
|
@@ -2150,6 +2623,13 @@ function addPlugin(sdk, plugin, options) {
|
|
|
2150
2623
|
}
|
|
2151
2624
|
return;
|
|
2152
2625
|
}
|
|
2626
|
+
if (plugin.pluginType === "method-override") {
|
|
2627
|
+
applyMethodOverride(
|
|
2628
|
+
getContext(sdk),
|
|
2629
|
+
plugin
|
|
2630
|
+
);
|
|
2631
|
+
return;
|
|
2632
|
+
}
|
|
2153
2633
|
addModelPlugin(
|
|
2154
2634
|
sdk,
|
|
2155
2635
|
plugin,
|
|
@@ -2232,20 +2712,14 @@ function objectShape(schema) {
|
|
|
2232
2712
|
}
|
|
2233
2713
|
function topoOrder2(specs) {
|
|
2234
2714
|
const byName = new Map(specs.map((s) => [s.name, s]));
|
|
2235
|
-
const ordered = [];
|
|
2236
2715
|
const placed = /* @__PURE__ */ new Set();
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
ordered.push(spec);
|
|
2245
|
-
placed.add(spec.name);
|
|
2246
|
-
progressed = true;
|
|
2247
|
-
}
|
|
2248
|
-
}
|
|
2716
|
+
const ordered = [];
|
|
2717
|
+
const isReady = (spec) => spec.requires.every((r) => !byName.has(r) || placed.has(r));
|
|
2718
|
+
for (; ; ) {
|
|
2719
|
+
const next = specs.find((s) => !placed.has(s.name) && isReady(s));
|
|
2720
|
+
if (!next) break;
|
|
2721
|
+
ordered.push(next);
|
|
2722
|
+
placed.add(next.name);
|
|
2249
2723
|
}
|
|
2250
2724
|
for (const spec of specs) if (!placed.has(spec.name)) ordered.push(spec);
|
|
2251
2725
|
return ordered;
|
|
@@ -2253,7 +2727,12 @@ function topoOrder2(specs) {
|
|
|
2253
2727
|
function planParameters(entry) {
|
|
2254
2728
|
const shape = objectShape(entry.inputSchema);
|
|
2255
2729
|
const resolvers = entry.boundResolvers ?? {};
|
|
2256
|
-
const names = shape ?
|
|
2730
|
+
const names = shape ? [
|
|
2731
|
+
...Object.keys(shape),
|
|
2732
|
+
...Object.keys(resolvers).filter(
|
|
2733
|
+
(name) => !(name in shape) && resolvers[name].type === "constant"
|
|
2734
|
+
)
|
|
2735
|
+
] : Object.keys(resolvers);
|
|
2257
2736
|
const specs = names.map((name) => {
|
|
2258
2737
|
const field = shape?.[name];
|
|
2259
2738
|
const { inner, required } = field ? unwrap(field) : { inner: void 0, required: false };
|
|
@@ -2267,7 +2746,13 @@ function planParameters(entry) {
|
|
|
2267
2746
|
requires: resolver?.requireParameters ?? []
|
|
2268
2747
|
};
|
|
2269
2748
|
});
|
|
2270
|
-
|
|
2749
|
+
const declared = shape ? specs.filter((s) => s.name in shape) : specs;
|
|
2750
|
+
const scan = [
|
|
2751
|
+
...shape ? specs.filter((s) => !(s.name in shape)) : [],
|
|
2752
|
+
...declared.filter((s) => s.required),
|
|
2753
|
+
...declared.filter((s) => !s.required)
|
|
2754
|
+
];
|
|
2755
|
+
return { parameters: topoOrder2(scan) };
|
|
2271
2756
|
}
|
|
2272
2757
|
|
|
2273
2758
|
// src/model/resolution/engine.ts
|
|
@@ -2292,10 +2777,12 @@ var key = (path) => path.join(".");
|
|
|
2292
2777
|
function isSettled(state, path) {
|
|
2293
2778
|
return state.settled.includes(key(path));
|
|
2294
2779
|
}
|
|
2295
|
-
function
|
|
2296
|
-
const k = key(path);
|
|
2780
|
+
function remember(state, k) {
|
|
2297
2781
|
if (!state.settled.includes(k)) state.settled.push(k);
|
|
2298
2782
|
}
|
|
2783
|
+
function settle(state, path) {
|
|
2784
|
+
remember(state, key(path));
|
|
2785
|
+
}
|
|
2299
2786
|
function clone(state) {
|
|
2300
2787
|
return JSON.parse(JSON.stringify(state));
|
|
2301
2788
|
}
|
|
@@ -2312,8 +2799,9 @@ function coerce(leaf, raw) {
|
|
|
2312
2799
|
return raw;
|
|
2313
2800
|
}
|
|
2314
2801
|
async function validationError(leaf, value, state) {
|
|
2802
|
+
if (leaf.resolver?.type !== "dynamic") return null;
|
|
2315
2803
|
const context = await resolveContext(leaf, state.resolved);
|
|
2316
|
-
const config = leaf.resolver
|
|
2804
|
+
const config = leaf.resolver.prompt?.({
|
|
2317
2805
|
items: state.listing?.items ?? [],
|
|
2318
2806
|
input: mergeInput(state.resolved, leaf.extraInput),
|
|
2319
2807
|
context
|
|
@@ -2323,11 +2811,6 @@ async function validationError(leaf, value, state) {
|
|
|
2323
2811
|
if (verdict === true) return null;
|
|
2324
2812
|
return typeof verdict === "string" ? verdict : `${leaf.name}: invalid value`;
|
|
2325
2813
|
}
|
|
2326
|
-
function toChoice(c) {
|
|
2327
|
-
const label = "label" in c ? c.label : c.name;
|
|
2328
|
-
const hint = Array.isArray(c.hint) ? c.hint.join(", ") : c.hint;
|
|
2329
|
-
return { label, value: String(c.value), hint };
|
|
2330
|
-
}
|
|
2331
2814
|
function isRef(r) {
|
|
2332
2815
|
return typeof r === "object" && r !== null && "ref" in r;
|
|
2333
2816
|
}
|
|
@@ -2343,6 +2826,7 @@ function toLeaf(name, field, definitions) {
|
|
|
2343
2826
|
return {
|
|
2344
2827
|
name,
|
|
2345
2828
|
required: field.required ?? false,
|
|
2829
|
+
label: field.label,
|
|
2346
2830
|
resolver,
|
|
2347
2831
|
extraInput,
|
|
2348
2832
|
// Carry the field's value type so nested answers coerce (number/boolean)
|
|
@@ -2390,6 +2874,9 @@ function arrayItem(resolver) {
|
|
|
2390
2874
|
requires: []
|
|
2391
2875
|
};
|
|
2392
2876
|
}
|
|
2877
|
+
function autoSettles(resolver) {
|
|
2878
|
+
return resolver.type === "constant" || resolver.type === "info";
|
|
2879
|
+
}
|
|
2393
2880
|
function mergeInput(input, extra) {
|
|
2394
2881
|
return extra ? { ...input, ...extra } : input;
|
|
2395
2882
|
}
|
|
@@ -2433,25 +2920,6 @@ async function arrayInfoAt(ctx, path, resolved) {
|
|
|
2433
2920
|
item: { ...arrayItem(resolver), name: String(path[path.length - 1]) }
|
|
2434
2921
|
};
|
|
2435
2922
|
}
|
|
2436
|
-
var AFFORDANCE = {
|
|
2437
|
-
choose: { action: "choose", description: "Pick one of the listed options" },
|
|
2438
|
-
custom: {
|
|
2439
|
-
action: "custom",
|
|
2440
|
-
description: "Provide a value directly",
|
|
2441
|
-
supply: "value"
|
|
2442
|
-
},
|
|
2443
|
-
search: {
|
|
2444
|
-
action: "search",
|
|
2445
|
-
description: "Filter the options by a search term",
|
|
2446
|
-
supply: "term"
|
|
2447
|
-
},
|
|
2448
|
-
more: { action: "more", description: "Load more options" },
|
|
2449
|
-
skip: { action: "skip", description: "Omit this optional parameter" },
|
|
2450
|
-
add: { action: "add", description: "Add another item" },
|
|
2451
|
-
done: { action: "done", description: "Finish the list" },
|
|
2452
|
-
retry: { action: "retry", description: "Retry loading the options" },
|
|
2453
|
-
cancel: { action: "cancel", description: "Cancel resolution" }
|
|
2454
|
-
};
|
|
2455
2923
|
async function firstPage(result) {
|
|
2456
2924
|
const page = await result;
|
|
2457
2925
|
return {
|
|
@@ -2460,18 +2928,19 @@ async function firstPage(result) {
|
|
|
2460
2928
|
};
|
|
2461
2929
|
}
|
|
2462
2930
|
async function resolveContext(leaf, input) {
|
|
2463
|
-
|
|
2931
|
+
if (leaf.resolver?.type !== "dynamic") return void 0;
|
|
2932
|
+
return leaf.resolver.getContext?.({
|
|
2464
2933
|
input: mergeInput(input, leaf.extraInput)
|
|
2465
2934
|
});
|
|
2466
2935
|
}
|
|
2467
2936
|
async function fetchListing(leaf, input, opts = {}) {
|
|
2468
2937
|
const page = await firstPage(
|
|
2469
|
-
leaf.resolver?.listItems
|
|
2938
|
+
leaf.resolver?.type === "dynamic" ? leaf.resolver.listItems({
|
|
2470
2939
|
input: mergeInput(input, leaf.extraInput),
|
|
2471
2940
|
context: opts.context,
|
|
2472
2941
|
search: opts.search,
|
|
2473
2942
|
cursor: opts.cursor
|
|
2474
|
-
})
|
|
2943
|
+
}) : void 0
|
|
2475
2944
|
);
|
|
2476
2945
|
return {
|
|
2477
2946
|
items: [...opts.priorItems ?? [], ...page.data],
|
|
@@ -2480,15 +2949,52 @@ async function fetchListing(leaf, input, opts = {}) {
|
|
|
2480
2949
|
exhausted: page.nextCursor == null
|
|
2481
2950
|
};
|
|
2482
2951
|
}
|
|
2952
|
+
|
|
2953
|
+
// src/model/resolution/questions.ts
|
|
2954
|
+
function toChoice(c) {
|
|
2955
|
+
const label = "label" in c ? c.label : c.name;
|
|
2956
|
+
const hint = Array.isArray(c.hint) ? c.hint.join(", ") : c.hint;
|
|
2957
|
+
return { label, value: String(c.value), hint };
|
|
2958
|
+
}
|
|
2959
|
+
var AFFORDANCE = {
|
|
2960
|
+
choose: { action: "choose", description: "Pick one of the listed options" },
|
|
2961
|
+
custom: {
|
|
2962
|
+
action: "custom",
|
|
2963
|
+
description: "Provide a value directly",
|
|
2964
|
+
supply: "value"
|
|
2965
|
+
},
|
|
2966
|
+
search: {
|
|
2967
|
+
action: "search",
|
|
2968
|
+
description: "Filter the options by a search term",
|
|
2969
|
+
supply: "term"
|
|
2970
|
+
},
|
|
2971
|
+
more: { action: "more", description: "Load more options" },
|
|
2972
|
+
skip: { action: "skip", description: "Omit this optional parameter" },
|
|
2973
|
+
add: { action: "add", description: "Add another item" },
|
|
2974
|
+
done: { action: "done", description: "Finish the list" },
|
|
2975
|
+
retry: { action: "retry", description: "Retry loading the options" },
|
|
2976
|
+
cancel: { action: "cancel", description: "Cancel resolution" }
|
|
2977
|
+
};
|
|
2483
2978
|
function selectActions(leaf, listing, multiple) {
|
|
2979
|
+
const searchMode = leaf.resolver?.type === "dynamic" && leaf.resolver.inputType === "search";
|
|
2980
|
+
if (searchMode && listing.search === void 0 && listing.items.length === 0) {
|
|
2981
|
+
const actions2 = multiple ? [AFFORDANCE.search] : [AFFORDANCE.search, AFFORDANCE.custom];
|
|
2982
|
+
if (!leaf.required) actions2.push(AFFORDANCE.skip);
|
|
2983
|
+
return actions2;
|
|
2984
|
+
}
|
|
2484
2985
|
const actions = multiple ? [AFFORDANCE.choose] : [AFFORDANCE.choose, AFFORDANCE.custom];
|
|
2485
|
-
if (
|
|
2986
|
+
if (searchMode) actions.push(AFFORDANCE.search);
|
|
2486
2987
|
if (listing.cursor) actions.push(AFFORDANCE.more);
|
|
2487
2988
|
if (!leaf.required) actions.push(AFFORDANCE.skip);
|
|
2488
2989
|
return actions;
|
|
2489
2990
|
}
|
|
2991
|
+
function labeledMessage(leaf) {
|
|
2992
|
+
if (!leaf.label) return void 0;
|
|
2993
|
+
return `${leaf.label} (${leaf.required ? "required" : "optional"}):`;
|
|
2994
|
+
}
|
|
2490
2995
|
function selectQuestion(leaf, input, listing, context) {
|
|
2491
|
-
const
|
|
2996
|
+
const resolver = leaf.resolver?.type === "dynamic" ? leaf.resolver : void 0;
|
|
2997
|
+
const config = resolver?.prompt?.({
|
|
2492
2998
|
items: listing.items,
|
|
2493
2999
|
input: mergeInput(input, leaf.extraInput),
|
|
2494
3000
|
context
|
|
@@ -2496,10 +3002,15 @@ function selectQuestion(leaf, input, listing, context) {
|
|
|
2496
3002
|
const multiple = config?.type === "checkbox";
|
|
2497
3003
|
return {
|
|
2498
3004
|
type: "select",
|
|
2499
|
-
|
|
3005
|
+
// A labeled field's title beats the resolver's message: per-field
|
|
3006
|
+
// resolvers are shared across fields (one choices-fetcher for every
|
|
3007
|
+
// field), so only the leaf knows which field is being asked.
|
|
3008
|
+
message: labeledMessage(leaf) ?? config?.message ?? `Select ${leaf.name}:`,
|
|
2500
3009
|
choices: (config?.choices ?? []).map(toChoice),
|
|
2501
3010
|
...multiple ? { multiple: true } : {},
|
|
2502
3011
|
...config?.notes?.length ? { notes: config.notes } : {},
|
|
3012
|
+
...listing.search !== void 0 ? { search: listing.search } : {},
|
|
3013
|
+
...resolver?.placeholder ? { placeholder: resolver.placeholder } : {},
|
|
2503
3014
|
actions: selectActions(leaf, listing, multiple)
|
|
2504
3015
|
};
|
|
2505
3016
|
}
|
|
@@ -2532,8 +3043,20 @@ function failedResult(state, name, error) {
|
|
|
2532
3043
|
async function buildQuestion(leaf, input) {
|
|
2533
3044
|
const optional = !leaf.required;
|
|
2534
3045
|
const resolver = leaf.resolver;
|
|
2535
|
-
if (resolver?.
|
|
3046
|
+
if (resolver?.type === "dynamic") {
|
|
2536
3047
|
const context = await resolveContext(leaf, input);
|
|
3048
|
+
if (resolver.inputType === "search") {
|
|
3049
|
+
const listing2 = {
|
|
3050
|
+
items: [],
|
|
3051
|
+
cursor: void 0,
|
|
3052
|
+
search: void 0,
|
|
3053
|
+
exhausted: true
|
|
3054
|
+
};
|
|
3055
|
+
return {
|
|
3056
|
+
question: selectQuestion(leaf, input, listing2, context),
|
|
3057
|
+
listing: listing2
|
|
3058
|
+
};
|
|
3059
|
+
}
|
|
2537
3060
|
const listing = await fetchListing(leaf, input, { context });
|
|
2538
3061
|
return {
|
|
2539
3062
|
question: selectQuestion(leaf, input, listing, context),
|
|
@@ -2552,37 +3075,29 @@ async function buildQuestion(leaf, input) {
|
|
|
2552
3075
|
}
|
|
2553
3076
|
};
|
|
2554
3077
|
}
|
|
2555
|
-
const
|
|
3078
|
+
const textSource = resolver?.type === "static" ? resolver : void 0;
|
|
3079
|
+
const inputType = textSource?.inputType && textSource.inputType !== "search" ? textSource.inputType : "text";
|
|
2556
3080
|
const actions = [AFFORDANCE.custom];
|
|
2557
3081
|
if (optional) actions.push(AFFORDANCE.skip);
|
|
2558
3082
|
return {
|
|
2559
3083
|
question: {
|
|
2560
3084
|
type: "input",
|
|
2561
|
-
|
|
3085
|
+
// The optional marker makes Enter-to-pass discoverable on a bare
|
|
3086
|
+
// parameter; a labeled field carries its marker via labeledMessage.
|
|
3087
|
+
message: labeledMessage(leaf) ?? `Enter ${leaf.name}${optional ? " (optional)" : ""}:`,
|
|
2562
3088
|
inputType,
|
|
2563
|
-
placeholder:
|
|
3089
|
+
placeholder: textSource?.placeholder,
|
|
2564
3090
|
actions
|
|
2565
3091
|
}
|
|
2566
3092
|
};
|
|
2567
3093
|
}
|
|
2568
|
-
function finalize(ctx, resolved) {
|
|
2569
|
-
if (!ctx.schema) return { status: "done", value: resolved };
|
|
2570
|
-
const parsed = ctx.schema.safeParse(resolved);
|
|
2571
|
-
if (parsed.success) {
|
|
2572
|
-
return { status: "done", value: parsed.data };
|
|
2573
|
-
}
|
|
2574
|
-
const issues = parsed.error.issues.map((i) => ({
|
|
2575
|
-
parameter: i.path.map(String).join(".") || void 0,
|
|
2576
|
-
message: i.message
|
|
2577
|
-
}));
|
|
2578
|
-
return { status: "invalid", issues };
|
|
2579
|
-
}
|
|
2580
3094
|
function collectionQuestion(t) {
|
|
2581
3095
|
const actions = [AFFORDANCE.add];
|
|
2582
3096
|
if (t.count >= t.min) actions.push(AFFORDANCE.done);
|
|
2583
3097
|
return {
|
|
2584
3098
|
type: "collection",
|
|
2585
|
-
message: `Add
|
|
3099
|
+
message: `Add ${t.path[t.path.length - 1]}[${t.count}]?`,
|
|
3100
|
+
container: "array",
|
|
2586
3101
|
count: t.count,
|
|
2587
3102
|
min: t.min,
|
|
2588
3103
|
// An unbounded array's max is Infinity, which JSON.stringify turns to null;
|
|
@@ -2591,6 +3106,56 @@ function collectionQuestion(t) {
|
|
|
2591
3106
|
actions
|
|
2592
3107
|
};
|
|
2593
3108
|
}
|
|
3109
|
+
function objectGateQuestion(path) {
|
|
3110
|
+
return {
|
|
3111
|
+
type: "collection",
|
|
3112
|
+
message: `Add ${path[path.length - 1]}?`,
|
|
3113
|
+
container: "object",
|
|
3114
|
+
actions: [
|
|
3115
|
+
{
|
|
3116
|
+
action: "add",
|
|
3117
|
+
description: "Provide values for these fields"
|
|
3118
|
+
},
|
|
3119
|
+
{ action: "done", description: "Skip these fields" }
|
|
3120
|
+
]
|
|
3121
|
+
};
|
|
3122
|
+
}
|
|
3123
|
+
function optionalsGateQuestion(pending) {
|
|
3124
|
+
return {
|
|
3125
|
+
type: "collection",
|
|
3126
|
+
// The prompt and its context ride separately so a host renders the info
|
|
3127
|
+
// line above the confirm without composing any text of its own.
|
|
3128
|
+
message: "Would you like to configure optional fields?",
|
|
3129
|
+
description: `There are ${pending.length} optional field(s) available.`,
|
|
3130
|
+
container: "object",
|
|
3131
|
+
// The gated fields' projection, so a smart host can show WHAT `add` would
|
|
3132
|
+
// walk (or render a form section) instead of a blind yes/no.
|
|
3133
|
+
fields: pending.map((leaf) => ({
|
|
3134
|
+
key: leaf.name,
|
|
3135
|
+
...leaf.label ? { label: leaf.label } : {},
|
|
3136
|
+
...leaf.valueType ? { valueType: leaf.valueType } : {}
|
|
3137
|
+
})),
|
|
3138
|
+
actions: [
|
|
3139
|
+
{ action: "add", description: "Configure the optional fields" },
|
|
3140
|
+
{ action: "done", description: "Skip the optional fields" }
|
|
3141
|
+
]
|
|
3142
|
+
};
|
|
3143
|
+
}
|
|
3144
|
+
|
|
3145
|
+
// src/model/resolution/walk.ts
|
|
3146
|
+
function finalize(ctx, resolved) {
|
|
3147
|
+
if (!ctx.schema) return { status: "done", value: resolved };
|
|
3148
|
+
const parsed = ctx.schema.safeParse(resolved);
|
|
3149
|
+
if (parsed.success) {
|
|
3150
|
+
return { status: "done", value: parsed.data };
|
|
3151
|
+
}
|
|
3152
|
+
const issues = parsed.error.issues.map((i) => ({
|
|
3153
|
+
parameter: i.path.map(String).join(".") || void 0,
|
|
3154
|
+
message: i.message
|
|
3155
|
+
}));
|
|
3156
|
+
return { status: "invalid", issues };
|
|
3157
|
+
}
|
|
3158
|
+
var optionalsMarker = (path) => `${key(path)}?optionals`;
|
|
2594
3159
|
async function findInArray(ctx, state, path) {
|
|
2595
3160
|
if (isSettled(state, path)) return null;
|
|
2596
3161
|
if (getAtPath(state.resolved, path) == null)
|
|
@@ -2604,8 +3169,11 @@ async function findInArray(ctx, state, path) {
|
|
|
2604
3169
|
const len = items.length;
|
|
2605
3170
|
const itemType = item.resolver?.type;
|
|
2606
3171
|
if (len > 0 && (itemType === "object" || itemType === "array")) {
|
|
2607
|
-
const
|
|
2608
|
-
if (
|
|
3172
|
+
const itemPath = [...path, len - 1];
|
|
3173
|
+
if (!isSettled(state, itemPath)) {
|
|
3174
|
+
const inner = await findNext(ctx, state, itemPath);
|
|
3175
|
+
if (inner) return inner;
|
|
3176
|
+
}
|
|
2609
3177
|
}
|
|
2610
3178
|
if (len < min) return descendItem(ctx, state, path, len, item);
|
|
2611
3179
|
if (len < max) return { kind: "array", path, count: len, min, max };
|
|
@@ -2633,14 +3201,60 @@ async function descendItem(ctx, state, arrayPath, index, item) {
|
|
|
2633
3201
|
}
|
|
2634
3202
|
async function findNext(ctx, state, path = []) {
|
|
2635
3203
|
const container = getAtPath(state.resolved, path) ?? {};
|
|
2636
|
-
|
|
3204
|
+
const children = await childrenAt(ctx, path, state.resolved);
|
|
3205
|
+
const inObject = path.length > 0;
|
|
3206
|
+
const ordered = inObject ? [
|
|
3207
|
+
...children.filter((c) => c.required),
|
|
3208
|
+
...children.filter((c) => !c.required)
|
|
3209
|
+
] : children;
|
|
3210
|
+
const asksUser = (c) => c.resolver ? !autoSettles(c.resolver) : c.required;
|
|
3211
|
+
const isPendingChild = (c) => container[c.name] === void 0 && !isSettled(state, [...path, c.name]);
|
|
3212
|
+
const hasAskableRequired = children.some((c) => c.required && asksUser(c));
|
|
3213
|
+
for (const leaf of ordered) {
|
|
2637
3214
|
const childPath = [...path, leaf.name];
|
|
2638
|
-
if (!leaf.requires.every(
|
|
3215
|
+
if (!leaf.requires.every(
|
|
3216
|
+
(r) => container[r] !== void 0 || getAtPath(state.resolved, [r]) !== void 0 || isSettled(state, [...path, r]) || isSettled(state, [r])
|
|
3217
|
+
)) {
|
|
3218
|
+
continue;
|
|
3219
|
+
}
|
|
3220
|
+
const inArrayItem = path.some((segment) => typeof segment === "number");
|
|
3221
|
+
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))) {
|
|
3222
|
+
const pending = ordered.filter(
|
|
3223
|
+
(c) => !c.required && asksUser(c) && isPendingChild(c)
|
|
3224
|
+
);
|
|
3225
|
+
return { kind: "optionals", path, pending };
|
|
3226
|
+
}
|
|
2639
3227
|
if (leaf.resolver?.type === "object") {
|
|
2640
|
-
if (
|
|
3228
|
+
if (isSettled(state, childPath)) continue;
|
|
3229
|
+
if (getAtPath(state.resolved, childPath) == null) {
|
|
3230
|
+
if (!leaf.required && !inArrayItem) {
|
|
3231
|
+
if (!state.interactive) {
|
|
3232
|
+
settle(state, childPath);
|
|
3233
|
+
continue;
|
|
3234
|
+
}
|
|
3235
|
+
return { kind: "object", path: childPath, leaf };
|
|
3236
|
+
}
|
|
2641
3237
|
setAtPath(state.resolved, childPath, {});
|
|
3238
|
+
}
|
|
2642
3239
|
const inner = await findNext(ctx, state, childPath);
|
|
2643
3240
|
if (inner) return inner;
|
|
3241
|
+
if (!leaf.required) {
|
|
3242
|
+
const value = getAtPath(state.resolved, childPath);
|
|
3243
|
+
if (value !== null && typeof value === "object" && Object.keys(value).length === 0) {
|
|
3244
|
+
const grandchildren = await childrenAt(
|
|
3245
|
+
ctx,
|
|
3246
|
+
childPath,
|
|
3247
|
+
state.resolved
|
|
3248
|
+
);
|
|
3249
|
+
const pending = grandchildren.some(
|
|
3250
|
+
(c) => value[c.name] === void 0 && !isSettled(state, [...childPath, c.name])
|
|
3251
|
+
);
|
|
3252
|
+
if (!pending) {
|
|
3253
|
+
delete container[leaf.name];
|
|
3254
|
+
settle(state, childPath);
|
|
3255
|
+
}
|
|
3256
|
+
}
|
|
3257
|
+
}
|
|
2644
3258
|
continue;
|
|
2645
3259
|
}
|
|
2646
3260
|
if (leaf.resolver?.type === "array") {
|
|
@@ -2656,6 +3270,7 @@ async function findNext(ctx, state, path = []) {
|
|
|
2656
3270
|
}
|
|
2657
3271
|
async function askLeaf(state, path, leaf, opts = {}) {
|
|
2658
3272
|
state.current = path;
|
|
3273
|
+
delete state.gate;
|
|
2659
3274
|
try {
|
|
2660
3275
|
const { question, listing } = await buildQuestion(leaf, state.resolved);
|
|
2661
3276
|
state.listing = listing;
|
|
@@ -2677,21 +3292,52 @@ async function advance(ctx, state) {
|
|
|
2677
3292
|
const target = await findNext(ctx, state);
|
|
2678
3293
|
if (!target) {
|
|
2679
3294
|
delete state.current;
|
|
3295
|
+
delete state.gate;
|
|
2680
3296
|
delete state.listing;
|
|
2681
3297
|
return { state, result: finalize(ctx, state.resolved) };
|
|
2682
3298
|
}
|
|
2683
3299
|
if (target.kind === "array") {
|
|
2684
3300
|
state.current = target.path;
|
|
3301
|
+
state.gate = "array";
|
|
2685
3302
|
delete state.listing;
|
|
2686
3303
|
return {
|
|
2687
3304
|
state,
|
|
2688
3305
|
result: { status: "ask", question: collectionQuestion(target) }
|
|
2689
3306
|
};
|
|
2690
3307
|
}
|
|
3308
|
+
if (target.kind === "object") {
|
|
3309
|
+
state.current = target.path;
|
|
3310
|
+
state.gate = "entry";
|
|
3311
|
+
delete state.listing;
|
|
3312
|
+
return {
|
|
3313
|
+
state,
|
|
3314
|
+
result: { status: "ask", question: objectGateQuestion(target.path) }
|
|
3315
|
+
};
|
|
3316
|
+
}
|
|
3317
|
+
if (target.kind === "optionals") {
|
|
3318
|
+
state.current = target.path;
|
|
3319
|
+
state.gate = "optionals";
|
|
3320
|
+
delete state.listing;
|
|
3321
|
+
return {
|
|
3322
|
+
state,
|
|
3323
|
+
result: {
|
|
3324
|
+
status: "ask",
|
|
3325
|
+
question: optionalsGateQuestion(target.pending)
|
|
3326
|
+
}
|
|
3327
|
+
};
|
|
3328
|
+
}
|
|
2691
3329
|
const { path, leaf } = target;
|
|
2692
|
-
const
|
|
3330
|
+
const resolver = leaf.resolver;
|
|
3331
|
+
if (resolver && autoSettles(resolver)) {
|
|
3332
|
+
if (resolver.type === "constant") {
|
|
3333
|
+
setAtPath(state.resolved, path, resolver.value);
|
|
3334
|
+
}
|
|
3335
|
+
settle(state, path);
|
|
3336
|
+
continue;
|
|
3337
|
+
}
|
|
3338
|
+
const auto = resolver?.type === "dynamic" ? await resolver.tryResolveWithoutPrompt?.({
|
|
2693
3339
|
input: mergeInput(state.resolved, leaf.extraInput)
|
|
2694
|
-
});
|
|
3340
|
+
}) : void 0;
|
|
2695
3341
|
if (auto) {
|
|
2696
3342
|
if (auto.resolvedValue !== void 0)
|
|
2697
3343
|
setAtPath(state.resolved, path, auto.resolvedValue);
|
|
@@ -2711,23 +3357,22 @@ async function advance(ctx, state) {
|
|
|
2711
3357
|
}
|
|
2712
3358
|
}
|
|
2713
3359
|
async function start(ctx, input = {}, interactive = true) {
|
|
2714
|
-
const
|
|
2715
|
-
for (const spec of ctx.parameters) {
|
|
2716
|
-
if (spec.resolver?.type === "constant")
|
|
2717
|
-
resolved[spec.name] = spec.resolver.value;
|
|
2718
|
-
}
|
|
2719
|
-
Object.assign(resolved, input);
|
|
2720
|
-
return advance(ctx, {
|
|
3360
|
+
const state = {
|
|
2721
3361
|
method: ctx.method,
|
|
2722
|
-
resolved,
|
|
3362
|
+
resolved: { ...input },
|
|
2723
3363
|
settled: [],
|
|
2724
3364
|
interactive
|
|
2725
|
-
}
|
|
3365
|
+
};
|
|
3366
|
+
for (const [name, value] of Object.entries(input)) {
|
|
3367
|
+
if (value !== void 0) settle(state, [name]);
|
|
3368
|
+
}
|
|
3369
|
+
return advance(ctx, state);
|
|
2726
3370
|
}
|
|
2727
3371
|
async function step(ctx, prior, action) {
|
|
2728
3372
|
const state = clone(prior);
|
|
2729
3373
|
if (action.type === "cancel") {
|
|
2730
3374
|
delete state.current;
|
|
3375
|
+
delete state.gate;
|
|
2731
3376
|
delete state.listing;
|
|
2732
3377
|
return { state, result: { status: "cancelled" } };
|
|
2733
3378
|
}
|
|
@@ -2738,12 +3383,27 @@ async function step(ctx, prior, action) {
|
|
|
2738
3383
|
return refine(ctx, state, leaf, path, action);
|
|
2739
3384
|
}
|
|
2740
3385
|
if (action.type === "add" || action.type === "done") {
|
|
3386
|
+
const gate = state.gate;
|
|
3387
|
+
if (!gate) {
|
|
3388
|
+
throw new Error(
|
|
3389
|
+
`action "${action.type}" is not supported here: no container decision is outstanding`
|
|
3390
|
+
);
|
|
3391
|
+
}
|
|
2741
3392
|
delete state.current;
|
|
3393
|
+
delete state.gate;
|
|
2742
3394
|
delete state.listing;
|
|
2743
3395
|
if (action.type === "done") {
|
|
2744
3396
|
settle(state, path);
|
|
2745
3397
|
return advance(ctx, state);
|
|
2746
3398
|
}
|
|
3399
|
+
if (gate === "entry") {
|
|
3400
|
+
setAtPath(state.resolved, path, {});
|
|
3401
|
+
return advance(ctx, state);
|
|
3402
|
+
}
|
|
3403
|
+
if (gate === "optionals") {
|
|
3404
|
+
remember(state, optionalsMarker(path));
|
|
3405
|
+
return advance(ctx, state);
|
|
3406
|
+
}
|
|
2747
3407
|
const items = getAtPath(state.resolved, path) ?? [];
|
|
2748
3408
|
const { item } = await arrayInfoAt(ctx, path, state.resolved);
|
|
2749
3409
|
const itemPath = [...path, items.length];
|
|
@@ -2751,12 +3411,35 @@ async function step(ctx, prior, action) {
|
|
|
2751
3411
|
return askLeaf(state, itemPath, item);
|
|
2752
3412
|
return advance(ctx, state);
|
|
2753
3413
|
}
|
|
3414
|
+
if (state.gate) {
|
|
3415
|
+
throw new Error(
|
|
3416
|
+
`action "${action.type}" is not supported here: a container decision (${state.gate}) is outstanding`
|
|
3417
|
+
);
|
|
3418
|
+
}
|
|
2754
3419
|
switch (action.type) {
|
|
2755
3420
|
case "choose":
|
|
2756
3421
|
case "custom": {
|
|
2757
3422
|
if (leaf) {
|
|
2758
3423
|
const error = await validationError(leaf, action.value, state);
|
|
2759
|
-
if (error)
|
|
3424
|
+
if (error) {
|
|
3425
|
+
if (state.listing && leaf.resolver?.type === "dynamic") {
|
|
3426
|
+
const context = await resolveContext(leaf, state.resolved);
|
|
3427
|
+
return {
|
|
3428
|
+
state,
|
|
3429
|
+
result: {
|
|
3430
|
+
status: "ask",
|
|
3431
|
+
question: selectQuestion(
|
|
3432
|
+
leaf,
|
|
3433
|
+
state.resolved,
|
|
3434
|
+
state.listing,
|
|
3435
|
+
context
|
|
3436
|
+
),
|
|
3437
|
+
error
|
|
3438
|
+
}
|
|
3439
|
+
};
|
|
3440
|
+
}
|
|
3441
|
+
return askLeaf(state, path, leaf, { error });
|
|
3442
|
+
}
|
|
2760
3443
|
}
|
|
2761
3444
|
setAtPath(
|
|
2762
3445
|
state.resolved,
|
|
@@ -2783,10 +3466,10 @@ async function refine(ctx, state, leaf, path, action) {
|
|
|
2783
3466
|
};
|
|
2784
3467
|
try {
|
|
2785
3468
|
if (action.type === "search") {
|
|
2786
|
-
const exact = await leaf.resolver
|
|
3469
|
+
const exact = leaf.resolver?.type === "dynamic" ? await leaf.resolver.tryResolveFromSearch?.({
|
|
2787
3470
|
input: mergeInput(state.resolved, leaf.extraInput),
|
|
2788
3471
|
search: action.term
|
|
2789
|
-
});
|
|
3472
|
+
}) : void 0;
|
|
2790
3473
|
if (exact) {
|
|
2791
3474
|
setAtPath(state.resolved, path, coerce(leaf, exact.resolvedValue));
|
|
2792
3475
|
delete state.current;
|
|
@@ -2837,10 +3520,11 @@ function projectMethod(entry) {
|
|
|
2837
3520
|
const inputProperties = toJsonSchema(entry.inputSchema)?.properties;
|
|
2838
3521
|
const parameters = {};
|
|
2839
3522
|
for (const spec of planParameters(entry).parameters) {
|
|
3523
|
+
const dynamic = spec.resolver?.type === "dynamic" ? spec.resolver : void 0;
|
|
2840
3524
|
parameters[spec.name] = {
|
|
2841
3525
|
required: spec.required,
|
|
2842
|
-
dynamic: Boolean(
|
|
2843
|
-
...
|
|
3526
|
+
dynamic: Boolean(dynamic),
|
|
3527
|
+
...dynamic?.inputType === "search" ? { searchable: true } : {},
|
|
2844
3528
|
...inputProperties?.[spec.name] ? { schema: inputProperties[spec.name] } : {},
|
|
2845
3529
|
...spec.staticChoices ? { choices: spec.staticChoices } : {},
|
|
2846
3530
|
...spec.requires.length ? { requireParameters: spec.requires } : {}
|
|
@@ -2907,12 +3591,13 @@ function createController(sdk) {
|
|
|
2907
3591
|
const spec = contextFor(method).parameters.find(
|
|
2908
3592
|
(p) => p.name === parameter
|
|
2909
3593
|
);
|
|
2910
|
-
|
|
2911
|
-
|
|
3594
|
+
const dynamic = spec?.resolver?.type === "dynamic" ? spec.resolver : void 0;
|
|
3595
|
+
if (!dynamic) return { data: [] };
|
|
3596
|
+
const context = await dynamic.getContext?.({ input });
|
|
2912
3597
|
const page = await firstPage(
|
|
2913
|
-
|
|
3598
|
+
dynamic.listItems({ input, context, search, cursor })
|
|
2914
3599
|
);
|
|
2915
|
-
const config =
|
|
3600
|
+
const config = dynamic.prompt?.({ items: page.data, input, context });
|
|
2916
3601
|
const data = (config?.choices ?? []).map(toChoice);
|
|
2917
3602
|
return { data, nextCursor: page.nextCursor };
|
|
2918
3603
|
};
|
|
@@ -2921,6 +3606,9 @@ function createController(sdk) {
|
|
|
2921
3606
|
|
|
2922
3607
|
// src/utils/core-plugin.ts
|
|
2923
3608
|
function createCorePlugin(options) {
|
|
3609
|
+
logDeprecation(
|
|
3610
|
+
"createCorePlugin() is deprecated. Inject the options under CORE_OPTIONS_ID via createSdk's configuration instead."
|
|
3611
|
+
);
|
|
2924
3612
|
return () => ({
|
|
2925
3613
|
context: {
|
|
2926
3614
|
core: options
|
|
@@ -2984,14 +3672,17 @@ function openEnum(values, description) {
|
|
|
2984
3672
|
0 && (module.exports = {
|
|
2985
3673
|
CONTEXT,
|
|
2986
3674
|
CORE_ERROR_SYMBOL,
|
|
3675
|
+
CORE_OPTIONS_ID,
|
|
2987
3676
|
CORE_SIGNAL_SYMBOL,
|
|
2988
3677
|
CoreCancelledSignal,
|
|
3678
|
+
CoreDisposeError,
|
|
2989
3679
|
CoreError,
|
|
2990
3680
|
CoreErrorCode,
|
|
2991
3681
|
CoreSignal,
|
|
2992
3682
|
addPlugin,
|
|
2993
3683
|
composePlugins,
|
|
2994
3684
|
concatPaginated,
|
|
3685
|
+
coreOptionsPluginRef,
|
|
2995
3686
|
createAsyncContext,
|
|
2996
3687
|
createController,
|
|
2997
3688
|
createCoreError,
|
|
@@ -3007,15 +3698,20 @@ function openEnum(values, description) {
|
|
|
3007
3698
|
createValidator,
|
|
3008
3699
|
dangerousContextPlugin,
|
|
3009
3700
|
declareMethod,
|
|
3701
|
+
declareOptionalProperty,
|
|
3010
3702
|
declarePlugin,
|
|
3011
3703
|
declareProperty,
|
|
3012
3704
|
decodeIncomingCursor,
|
|
3705
|
+
defaultLogDeprecation,
|
|
3013
3706
|
defineFormatter,
|
|
3707
|
+
defineHook,
|
|
3014
3708
|
defineLegacyMerge,
|
|
3015
3709
|
defineMethod,
|
|
3710
|
+
defineMethodOverride,
|
|
3016
3711
|
definePlugin,
|
|
3017
3712
|
defineProperty,
|
|
3018
3713
|
defineResolver,
|
|
3714
|
+
disposeSdk,
|
|
3019
3715
|
fromFunctionPlugin,
|
|
3020
3716
|
getContext,
|
|
3021
3717
|
getCoreErrorCause,
|
|
@@ -3031,10 +3727,12 @@ function openEnum(values, description) {
|
|
|
3031
3727
|
isNestedMethodCall,
|
|
3032
3728
|
isPositional,
|
|
3033
3729
|
isTelemetryNested,
|
|
3730
|
+
omitExports,
|
|
3034
3731
|
openEnum,
|
|
3035
3732
|
paginate,
|
|
3036
3733
|
paginateBuffered,
|
|
3037
3734
|
paginateMaxItems,
|
|
3735
|
+
resolvePlugin,
|
|
3038
3736
|
runInMethodScope,
|
|
3039
3737
|
runWithTelemetryContext,
|
|
3040
3738
|
selectExports,
|