@zapier/kitcore 0.10.1 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -197,12 +197,19 @@ function composeVoid(existing, added) {
197
197
  isolated.add(composed);
198
198
  return composed;
199
199
  }
200
+ function composeAnnotators(existing, added) {
201
+ if (!existing) return added;
202
+ if (!added) return existing;
203
+ return (ctx) => ({ ...existing(ctx), ...added(ctx) });
204
+ }
200
205
  function buildHooks(existing, added) {
201
206
  const result = {};
202
207
  const start2 = composeVoid(existing.onMethodStart, added.onMethodStart);
203
208
  if (start2) result.onMethodStart = start2;
204
209
  const end = composeVoid(existing.onMethodEnd, added.onMethodEnd);
205
210
  if (end) result.onMethodEnd = end;
211
+ const annotator = composeAnnotators(existing.annotator, added.annotator);
212
+ if (annotator) result.annotator = annotator;
206
213
  return result;
207
214
  }
208
215
 
@@ -677,11 +684,14 @@ function generateCallId() {
677
684
  }
678
685
  return null;
679
686
  }
680
- function rootCallContext() {
687
+ function rootCallContext({
688
+ callOrigin = "surface"
689
+ } = {}) {
681
690
  return {
682
691
  callId: generateCallId(),
683
692
  depth: 0,
684
693
  annotations: {},
694
+ callOrigin,
685
695
  [CALL_CONTEXT_BRAND]: true
686
696
  };
687
697
  }
@@ -690,6 +700,7 @@ function childCallContext(parent) {
690
700
  callId: parent.callId,
691
701
  depth: parent.depth + 1,
692
702
  annotations: {},
703
+ callOrigin: parent.callOrigin,
693
704
  [CALL_CONTEXT_BRAND]: true
694
705
  };
695
706
  }
@@ -715,6 +726,28 @@ var INTERNAL_CALL = Symbol("kitcore.internalCall");
715
726
  function resolveCallContext(secondArg) {
716
727
  return isCallContext(secondArg) ? secondArg : rootCallContext();
717
728
  }
729
+ var hookAnnotatorReentrancy = 0;
730
+ function applyAnnotations({
731
+ context,
732
+ methodName,
733
+ input,
734
+ hookAnnotator,
735
+ methodAnnotator
736
+ }) {
737
+ if (hookAnnotator && !isInsideObserver() && context.depth === 0 && context.callOrigin !== "internal" && hookAnnotatorReentrancy === 0) {
738
+ hookAnnotatorReentrancy++;
739
+ try {
740
+ Object.assign(context.annotations, hookAnnotator({ methodName, input }));
741
+ } catch {
742
+ } finally {
743
+ hookAnnotatorReentrancy--;
744
+ }
745
+ }
746
+ try {
747
+ Object.assign(context.annotations, methodAnnotator?.(input));
748
+ } catch {
749
+ }
750
+ }
718
751
  function signalDeprecation(context, methodName, getDeprecation) {
719
752
  if (isInsideObserver()) return;
720
753
  const deprecation = getDeprecation?.();
@@ -740,7 +773,7 @@ function normalizeError(error, adaptError) {
740
773
  );
741
774
  }
742
775
  function createFunction(coreFn, options) {
743
- const { sdk, schema, name, getDeprecation } = options;
776
+ const { sdk, schema, name, annotator, getDeprecation } = options;
744
777
  const functionName = name || coreFn.name;
745
778
  const namedFunctions = {
746
779
  [functionName]: async function(callOptions) {
@@ -754,14 +787,26 @@ function createFunction(coreFn, options) {
754
787
  const normalizedOptions = callOptions ?? {};
755
788
  const args = [normalizedOptions];
756
789
  const depth = Math.max(context.depth, getCurrentDepth());
757
- const hooks = isInsideObserver() ? void 0 : sdk.context.hooks;
790
+ const insideObserver = isInsideObserver();
791
+ const hooks = insideObserver ? void 0 : sdk.context.hooks;
758
792
  const adaptError = resolveCoreOptions(sdk.context)?.adaptError;
759
- hooks?.onMethodStart?.({
793
+ applyAnnotations({
794
+ context,
795
+ methodName: functionName,
796
+ input: normalizedOptions,
797
+ hookAnnotator: hooks?.annotator,
798
+ methodAnnotator: annotator
799
+ });
800
+ const hookBase = {
760
801
  methodName: functionName,
761
802
  args,
762
803
  isPaginated: false,
763
- depth
764
- });
804
+ depth,
805
+ callId: context.callId,
806
+ callOrigin: context.callOrigin,
807
+ annotations: context.annotations
808
+ };
809
+ hooks?.onMethodStart?.({ ...hookBase });
765
810
  try {
766
811
  let result;
767
812
  if (schema) {
@@ -783,20 +828,14 @@ function createFunction(coreFn, options) {
783
828
  result = await coreFn(normalizedOptions, context);
784
829
  }
785
830
  hooks?.onMethodEnd?.({
786
- methodName: functionName,
787
- args,
788
- isPaginated: false,
789
- depth,
831
+ ...hookBase,
790
832
  durationMs: Date.now() - startTime
791
833
  });
792
834
  return result;
793
835
  } catch (error) {
794
836
  const normalizedError = normalizeError(error, adaptError);
795
837
  hooks?.onMethodEnd?.({
796
- methodName: functionName,
797
- args,
798
- isPaginated: false,
799
- depth,
838
+ ...hookBase,
800
839
  durationMs: Date.now() - startTime,
801
840
  error: normalizedError
802
841
  });
@@ -808,7 +847,7 @@ function createFunction(coreFn, options) {
808
847
  return namedFunctions[functionName];
809
848
  }
810
849
  function createRawFunction(coreFn, options) {
811
- const { sdk, name, schema, positional, getDeprecation } = options;
850
+ const { sdk, name, schema, positional, annotator, getDeprecation } = options;
812
851
  return function(rawInput) {
813
852
  const internal = arguments[1];
814
853
  const context = resolveCallContext(internal);
@@ -818,23 +857,32 @@ function createRawFunction(coreFn, options) {
818
857
  return runInMethodScope(() => {
819
858
  const startTime = Date.now();
820
859
  const depth = Math.max(context.depth, getCurrentDepth());
821
- const hooks = isInsideObserver() ? void 0 : sdk.context.hooks;
860
+ const insideObserver = isInsideObserver();
861
+ const hooks = insideObserver ? void 0 : sdk.context.hooks;
822
862
  const adaptError = resolveCoreOptions(sdk.context)?.adaptError;
823
863
  const input = schema ? rawInput ?? {} : rawInput;
864
+ applyAnnotations({
865
+ context,
866
+ methodName: name,
867
+ input,
868
+ hookAnnotator: hooks?.annotator,
869
+ methodAnnotator: annotator
870
+ });
824
871
  const record = input;
825
872
  const args = positional ? positional.filter((key) => record?.[key] !== void 0).map((key) => record?.[key]) : [input];
826
- hooks?.onMethodStart?.({
873
+ const hookBase = {
827
874
  methodName: name,
828
875
  args,
829
876
  isPaginated: false,
830
- depth
831
- });
877
+ depth,
878
+ callId: context.callId,
879
+ callOrigin: context.callOrigin,
880
+ annotations: context.annotations
881
+ };
882
+ hooks?.onMethodStart?.({ ...hookBase });
832
883
  const fireEnd = (error) => {
833
884
  hooks?.onMethodEnd?.({
834
- methodName: name,
835
- args,
836
- isPaginated: false,
837
- depth,
885
+ ...hookBase,
838
886
  durationMs: Date.now() - startTime,
839
887
  ...error ? { error } : {}
840
888
  });
@@ -876,7 +924,8 @@ function isSdkPage(value) {
876
924
  }
877
925
  function createPageFunction(coreFn, {
878
926
  sdk,
879
- adaptPage
927
+ adaptPage,
928
+ finalizePage
880
929
  }) {
881
930
  const functionName = coreFn.name + "Page";
882
931
  const namedFunctions = {
@@ -889,7 +938,7 @@ function createPageFunction(coreFn, {
889
938
  `${functionName}: paginated result must be exactly { data: TItem[], nextCursor? } (produced by the handler or its \`adaptPage\`); got keys [${page && typeof page === "object" ? Object.keys(page).join(", ") : typeof page}]. If the handler returns a raw shape, set \`adaptPage\` to translate it; if \`adaptPage\` already runs, it must return only \`data\`/\`nextCursor\`.`
890
939
  );
891
940
  }
892
- return page;
941
+ return finalizePage ? finalizePage(page) : page;
893
942
  } catch (error) {
894
943
  throw normalizeError(
895
944
  error,
@@ -901,8 +950,21 @@ function createPageFunction(coreFn, {
901
950
  return namedFunctions[functionName];
902
951
  }
903
952
  function createPaginatedFunction(coreFn, options) {
904
- const { sdk, schema, name, defaultPageSize, adaptPage, getDeprecation } = options;
905
- const pageFunction = createPageFunction(coreFn, { sdk, adaptPage });
953
+ const {
954
+ sdk,
955
+ schema,
956
+ name,
957
+ defaultPageSize,
958
+ adaptPage,
959
+ annotator,
960
+ finalizePage,
961
+ getDeprecation
962
+ } = options;
963
+ const pageFunction = createPageFunction(coreFn, {
964
+ sdk,
965
+ adaptPage,
966
+ finalizePage
967
+ });
906
968
  const functionName = name || coreFn.name;
907
969
  const namedFunctions = {
908
970
  [functionName]: function(callOptions) {
@@ -916,14 +978,26 @@ function createPaginatedFunction(coreFn, options) {
916
978
  const normalizedOptions = callOptions ?? {};
917
979
  const args = [normalizedOptions];
918
980
  const depth = Math.max(context.depth, getCurrentDepth());
919
- const hooks = isInsideObserver() ? void 0 : sdk.context.hooks;
981
+ const insideObserver = isInsideObserver();
982
+ const hooks = insideObserver ? void 0 : sdk.context.hooks;
920
983
  const adaptError = resolveCoreOptions(sdk.context)?.adaptError;
921
- hooks?.onMethodStart?.({
984
+ applyAnnotations({
985
+ context,
986
+ methodName: functionName,
987
+ input: normalizedOptions,
988
+ hookAnnotator: hooks?.annotator,
989
+ methodAnnotator: annotator
990
+ });
991
+ const hookBase = {
922
992
  methodName: functionName,
923
993
  args,
924
994
  isPaginated: true,
925
- depth
926
- });
995
+ depth,
996
+ callId: context.callId,
997
+ callOrigin: context.callOrigin,
998
+ annotations: context.annotations
999
+ };
1000
+ hooks?.onMethodStart?.({ ...hookBase });
927
1001
  try {
928
1002
  const validatedOptions = {
929
1003
  ...normalizedOptions,
@@ -949,19 +1023,13 @@ function createPaginatedFunction(coreFn, options) {
949
1023
  firstPagePromise.then(
950
1024
  () => {
951
1025
  hooks.onMethodEnd({
952
- methodName: functionName,
953
- args,
954
- isPaginated: true,
955
- depth,
1026
+ ...hookBase,
956
1027
  durationMs: Date.now() - startTime
957
1028
  });
958
1029
  },
959
1030
  (error) => {
960
1031
  hooks.onMethodEnd({
961
- methodName: functionName,
962
- args,
963
- isPaginated: true,
964
- depth,
1032
+ ...hookBase,
965
1033
  durationMs: Date.now() - startTime,
966
1034
  error: error instanceof Error ? error : new Error(String(error))
967
1035
  });
@@ -1000,10 +1068,7 @@ function createPaginatedFunction(coreFn, options) {
1000
1068
  } catch (error) {
1001
1069
  const normalizedError = normalizeError(error, adaptError);
1002
1070
  hooks?.onMethodEnd?.({
1003
- methodName: functionName,
1004
- args,
1005
- isPaginated: true,
1006
- depth,
1071
+ ...hookBase,
1007
1072
  durationMs: Date.now() - startTime,
1008
1073
  error: normalizedError
1009
1074
  });
@@ -1425,9 +1490,11 @@ function defineMethod(config) {
1425
1490
  importBindings: deps.bindings,
1426
1491
  inputSchema: config.inputSchema,
1427
1492
  skipInputValidation: config.skipInputValidation,
1493
+ skipOutputValidation: config.skipOutputValidation,
1428
1494
  meta: collectLeafMeta(config),
1429
1495
  resolvers: config.resolvers,
1430
1496
  formatter: config.formatter,
1497
+ annotator: config.annotator,
1431
1498
  output: config.output,
1432
1499
  positional: config.positional,
1433
1500
  setup: config.setup,
@@ -1530,6 +1597,29 @@ function declareMethod(config) {
1530
1597
  }
1531
1598
  };
1532
1599
  }
1600
+ function declareOptionalMethod(config) {
1601
+ const { name, namespace } = parseId(config.id);
1602
+ const id = makeId(name, namespace);
1603
+ return {
1604
+ pluginType: "method",
1605
+ name,
1606
+ namespace,
1607
+ id,
1608
+ standIn: true,
1609
+ optional: true,
1610
+ imports: [],
1611
+ importBindings: [],
1612
+ run: () => {
1613
+ throw new Error(
1614
+ `Plugin "${id}" is an optional stand-in (declareOptionalMethod) with no implementation. Its binding is \`undefined\` unless a real plugin is registered under this id.`
1615
+ );
1616
+ }
1617
+ // Requires nothing (phantom carrier `<never, never>`): a consumer that
1618
+ // imports it still passes `createSdk`'s completeness check unprovided. The
1619
+ // `optional: true` literal drives `PluginSurface` to type the binding
1620
+ // `| undefined`.
1621
+ };
1622
+ }
1533
1623
  function defineProperty(config) {
1534
1624
  const deps = normalizeImports(config.imports);
1535
1625
  return {
@@ -1575,6 +1665,11 @@ function declareOptionalProperty(config) {
1575
1665
  // import binding is still typed `TValue | undefined` from the descriptor.
1576
1666
  };
1577
1667
  }
1668
+ function declareDefault({
1669
+ plugin
1670
+ }) {
1671
+ return { ...plugin, defaultSource: plugin };
1672
+ }
1578
1673
  function defineHook(config) {
1579
1674
  const deps = normalizeImports(config.imports);
1580
1675
  return {
@@ -1587,7 +1682,8 @@ function defineHook(config) {
1587
1682
  setup: config.setup,
1588
1683
  dispose: config.dispose,
1589
1684
  wrap: config.wrap,
1590
- observe: config.observe
1685
+ observe: config.observe,
1686
+ annotator: config.annotator
1591
1687
  };
1592
1688
  }
1593
1689
  function declarePlugin(config) {
@@ -1875,6 +1971,97 @@ var getRegistryPlugin = defineMethod({
1875
1971
  run: ({ imports, input }) => buildSurfaceRegistry(imports.context, input?.package)
1876
1972
  });
1877
1973
 
1974
+ // src/utils/output-policy.ts
1975
+ function isRecord(value) {
1976
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1977
+ }
1978
+ function diffDroppedPaths(raw, parsed, prefix = "") {
1979
+ const paths = [];
1980
+ walkDroppedPaths(raw, parsed, prefix, paths);
1981
+ return paths;
1982
+ }
1983
+ function walkDroppedPaths(raw, parsed, prefix, out) {
1984
+ if (Array.isArray(raw) && Array.isArray(parsed)) {
1985
+ const seen = /* @__PURE__ */ new Set();
1986
+ const length = Math.min(raw.length, parsed.length);
1987
+ for (let index = 0; index < length; index++) {
1988
+ const elementPaths = [];
1989
+ walkDroppedPaths(raw[index], parsed[index], `${prefix}[]`, elementPaths);
1990
+ for (const path of elementPaths) {
1991
+ if (seen.has(path)) continue;
1992
+ seen.add(path);
1993
+ out.push(path);
1994
+ }
1995
+ }
1996
+ return;
1997
+ }
1998
+ if (isRecord(raw) && isRecord(parsed)) {
1999
+ for (const key of Object.keys(raw)) {
2000
+ const path = prefix ? `${prefix}.${key}` : key;
2001
+ if (!(key in parsed)) {
2002
+ out.push(path);
2003
+ continue;
2004
+ }
2005
+ walkDroppedPaths(raw[key], parsed[key], path, out);
2006
+ }
2007
+ return;
2008
+ }
2009
+ }
2010
+ function parseOutput(schema, value, policy, locator) {
2011
+ const result = schema.safeParse(value);
2012
+ if (result.success) return result.data;
2013
+ const issues = result.error.issues.map((issue) => {
2014
+ const path = issue.path.length > 0 ? issue.path.join(".") : "data";
2015
+ return `${path}: ${issue.message}`;
2016
+ });
2017
+ const subject = policy.methodName ? ` for "${policy.methodName}"` : "";
2018
+ const at = locator ? ` at ${locator}` : "";
2019
+ throw createCoreError(
2020
+ {
2021
+ code: CoreErrorCode.Validation,
2022
+ message: `Output validation failed${subject}${at}:
2023
+ ${issues.join("\n ")}
2024
+
2025
+ The response does not match the method's \`outputSchema\`. Correct the schema, or set \`skipOutputValidation: true\` on the method to pass the response through unvalidated.`,
2026
+ details: { zodErrors: result.error.issues, output: value }
2027
+ },
2028
+ policy.adaptError
2029
+ );
2030
+ }
2031
+ function applyItemOutputPolicy(result, policy) {
2032
+ const schema = policy.outputSchema;
2033
+ if (!schema || policy.skipOutputValidation) return result;
2034
+ if (!isRecord(result) || !("data" in result)) return result;
2035
+ const data = parseOutput(schema, result.data, policy);
2036
+ const next = { ...result, data };
2037
+ if (policy.includeOutputValidationDroppedPaths) {
2038
+ const droppedPaths = diffDroppedPaths(result.data, data);
2039
+ if (droppedPaths.length > 0) {
2040
+ next.meta = withOutputValidation(result.meta, droppedPaths);
2041
+ }
2042
+ }
2043
+ return next;
2044
+ }
2045
+ function withOutputValidation(existing, droppedPaths) {
2046
+ const base = isRecord(existing) ? existing : {};
2047
+ return { ...base, outputValidation: { droppedPaths } };
2048
+ }
2049
+ function applyListOutputPolicy(page, policy) {
2050
+ const schema = policy.outputSchema;
2051
+ if (!schema || policy.skipOutputValidation) return page;
2052
+ const data = page.data.map(
2053
+ (item, index) => parseOutput(schema, item, policy, `data[${index}]`)
2054
+ );
2055
+ const next = { ...page, data };
2056
+ if (policy.includeOutputValidationDroppedPaths) {
2057
+ const droppedPaths = diffDroppedPaths(page.data, data);
2058
+ if (droppedPaths.length > 0) {
2059
+ next.meta = { ...page.meta, outputValidation: { droppedPaths } };
2060
+ }
2061
+ }
2062
+ return next;
2063
+ }
2064
+
1878
2065
  // src/model/materialize.ts
1879
2066
  var FRAMEWORK_CONFIGURATION_IDS = /* @__PURE__ */ new Set([
1880
2067
  CORE_OPTIONS_ID
@@ -1938,6 +2125,9 @@ function edgesOf(plugin) {
1938
2125
  function isStandIn(plugin) {
1939
2126
  return (plugin.pluginType === "method" || plugin.pluginType === "property" || plugin.pluginType === "aggregate") && plugin.standIn === true;
1940
2127
  }
2128
+ function isDefault(plugin) {
2129
+ return (plugin.pluginType === "method" || plugin.pluginType === "property") && plugin.defaultSource !== void 0;
2130
+ }
1941
2131
  function topoOrder(descriptors) {
1942
2132
  const order = [];
1943
2133
  const visited = /* @__PURE__ */ new Set();
@@ -1952,28 +2142,89 @@ function topoOrder(descriptors) {
1952
2142
  return order;
1953
2143
  }
1954
2144
  function collectPlugins(root, materialized = /* @__PURE__ */ new Set(), configuration) {
2145
+ const rank = (plugin) => isStandIn(plugin) ? 0 : isDefault(plugin) ? 1 : 2;
2146
+ const allNodes = [];
2147
+ const seen = /* @__PURE__ */ new Set();
2148
+ const collect = (plugin) => {
2149
+ if (materialized.has(plugin.id) || seen.has(plugin)) return;
2150
+ seen.add(plugin);
2151
+ allNodes.push(plugin);
2152
+ for (const edge of edgesOf(plugin)) collect(edge);
2153
+ };
2154
+ collect(root);
2155
+ const childrenOf = /* @__PURE__ */ new Map();
2156
+ const candidatesById = /* @__PURE__ */ new Map();
2157
+ for (const node of allNodes) {
2158
+ childrenOf.set(
2159
+ node,
2160
+ edgesOf(node).filter((edge) => seen.has(edge))
2161
+ );
2162
+ const candidates = candidatesById.get(node.id);
2163
+ if (candidates) candidates.push(node);
2164
+ else candidatesById.set(node.id, [node]);
2165
+ }
2166
+ const live = new Set(allNodes);
2167
+ for (; ; ) {
2168
+ const reachable = /* @__PURE__ */ new Set();
2169
+ if (live.has(root)) reachable.add(root);
2170
+ const queue = reachable.has(root) ? [root] : [];
2171
+ while (queue.length) {
2172
+ const node = queue.pop();
2173
+ for (const child of childrenOf.get(node) ?? []) {
2174
+ if (reachable.has(child)) continue;
2175
+ reachable.add(child);
2176
+ if (live.has(child)) queue.push(child);
2177
+ }
2178
+ }
2179
+ let changed = false;
2180
+ for (const node of live) {
2181
+ if (!reachable.has(node)) {
2182
+ live.delete(node);
2183
+ changed = true;
2184
+ }
2185
+ }
2186
+ for (const candidates of candidatesById.values()) {
2187
+ let maxRank = -1;
2188
+ for (const candidate of candidates) {
2189
+ if (live.has(candidate)) maxRank = Math.max(maxRank, rank(candidate));
2190
+ }
2191
+ if (maxRank < 0) continue;
2192
+ for (const candidate of candidates) {
2193
+ if (live.has(candidate) && rank(candidate) < maxRank) {
2194
+ live.delete(candidate);
2195
+ changed = true;
2196
+ }
2197
+ }
2198
+ }
2199
+ if (!changed) break;
2200
+ }
1955
2201
  const byId = /* @__PURE__ */ new Map();
1956
- const visit = (plugin) => {
1957
- if (materialized.has(plugin.id)) return;
1958
- const existing = byId.get(plugin.id);
1959
- if (existing === plugin) return;
1960
- if (existing) {
1961
- const bothReal = !isStandIn(existing) && !isStandIn(plugin);
1962
- if (bothReal) {
2202
+ const conflictedDefaults = /* @__PURE__ */ new Set();
2203
+ const isOptional = (plugin) => "optional" in plugin && plugin.optional === true;
2204
+ for (const [id, candidates] of candidatesById) {
2205
+ const liveCandidates = candidates.filter(
2206
+ (candidate) => live.has(candidate)
2207
+ );
2208
+ const winner = liveCandidates.find((candidate) => !isOptional(candidate)) ?? liveCandidates[0];
2209
+ if (!winner) continue;
2210
+ if (liveCandidates.length > 1) {
2211
+ const winnerRank = rank(winner);
2212
+ if (winnerRank === 2) {
1963
2213
  throw new Error(
1964
- `createSdk: duplicate plugin id "${plugin.id}". Two different plugins registered under the same id.`
2214
+ `createSdk: duplicate plugin id "${id}". Two different plugins registered under the same id.`
1965
2215
  );
1966
2216
  }
1967
- if (isStandIn(existing) && !isStandIn(plugin)) {
1968
- byId.set(plugin.id, plugin);
1969
- for (const edge of edgesOf(plugin)) visit(edge);
2217
+ if (winnerRank === 1) {
2218
+ const sources = new Set(
2219
+ liveCandidates.map(
2220
+ (candidate) => candidate.defaultSource
2221
+ )
2222
+ );
2223
+ if (sources.size > 1) conflictedDefaults.add(id);
1970
2224
  }
1971
- return;
1972
2225
  }
1973
- byId.set(plugin.id, plugin);
1974
- for (const edge of edgesOf(plugin)) visit(edge);
1975
- };
1976
- visit(root);
2226
+ byId.set(id, winner);
2227
+ }
1977
2228
  if (configuration) {
1978
2229
  for (const [id, value] of Object.entries(configuration)) {
1979
2230
  const existing = byId.get(id);
@@ -2024,9 +2275,24 @@ function collectPlugins(root, materialized = /* @__PURE__ */ new Set(), configur
2024
2275
  );
2025
2276
  }
2026
2277
  }
2278
+ for (const id of conflictedDefaults) {
2279
+ const winner = byId.get(id);
2280
+ if (winner && isDefault(winner)) {
2281
+ throw new Error(
2282
+ `createSdk: conflicting defaults for "${id}". Two different plugins were declared as defaults for the same id and nothing else provides it. Register an explicit (non-default) plugin for this id to choose the winner, or give the implementations distinct ids if they are meant to coexist.`
2283
+ );
2284
+ }
2285
+ }
2027
2286
  return byId;
2028
2287
  }
2029
- function bindValue(target, key, entry, callType = "surface", ctx) {
2288
+ function bindValue({
2289
+ target,
2290
+ key,
2291
+ entry,
2292
+ bindMode = "surface",
2293
+ ctx,
2294
+ frameworkOrigin = false
2295
+ }) {
2030
2296
  if (entry.pluginType === "property" && entry.getValue) {
2031
2297
  Object.defineProperty(target, key, {
2032
2298
  get: entry.getValue,
@@ -2034,7 +2300,7 @@ function bindValue(target, key, entry, callType = "surface", ctx) {
2034
2300
  configurable: true
2035
2301
  });
2036
2302
  } else {
2037
- const value = callType === "internal" && entry.pluginType === "method" ? entry.bindInternal?.(ctx) ?? entry.internalValue ?? entry.value : entry.value;
2303
+ const value = bindMode === "internal" && entry.pluginType === "method" ? entry.bindInternal?.({ ctx, frameworkOrigin }) ?? entry.internalValue ?? entry.value : entry.value;
2038
2304
  Object.defineProperty(target, key, {
2039
2305
  value,
2040
2306
  writable: true,
@@ -2052,7 +2318,12 @@ function buildSurface(context, ...maps) {
2052
2318
  sdk[CONTEXT] = context;
2053
2319
  return sdk;
2054
2320
  }
2055
- function buildImports(plugins, importBindings, ctx) {
2321
+ function buildImports({
2322
+ plugins,
2323
+ importBindings,
2324
+ ctx,
2325
+ frameworkOrigin = false
2326
+ }) {
2056
2327
  const imports = {};
2057
2328
  for (const { binding, id, optional } of importBindings) {
2058
2329
  const entry = plugins[id];
@@ -2065,10 +2336,31 @@ function buildImports(plugins, importBindings, ctx) {
2065
2336
  });
2066
2337
  continue;
2067
2338
  }
2068
- bindValue(imports, binding, entry, "internal", ctx);
2339
+ bindValue({
2340
+ target: imports,
2341
+ key: binding,
2342
+ entry,
2343
+ bindMode: "internal",
2344
+ ctx,
2345
+ frameworkOrigin
2346
+ });
2069
2347
  }
2070
2348
  return imports;
2071
2349
  }
2350
+ function bindInternalTwin({
2351
+ ctx,
2352
+ frameworkOrigin,
2353
+ withContext,
2354
+ internalValue
2355
+ }) {
2356
+ if (ctx) {
2357
+ return (...args) => withContext(childCallContext(ctx))(...args);
2358
+ }
2359
+ if (frameworkOrigin) {
2360
+ return (...args) => withContext(rootCallContext({ callOrigin: "internal" }))(...args);
2361
+ }
2362
+ return internalValue;
2363
+ }
2072
2364
  function mirrorLegacyRootKeys(context, rootKeys, meta) {
2073
2365
  const exports = {};
2074
2366
  for (const [name, value] of Object.entries(rootKeys)) {
@@ -2132,7 +2424,11 @@ function bindResolver(resolver, plugins) {
2132
2424
  case "info":
2133
2425
  return { type: "info", text: resolver.text };
2134
2426
  case "object": {
2135
- const imports = buildImports(plugins, resolver.importBindings);
2427
+ const imports = buildImports({
2428
+ plugins,
2429
+ importBindings: resolver.importBindings,
2430
+ frameworkOrigin: true
2431
+ });
2136
2432
  const bound = {
2137
2433
  type: "object",
2138
2434
  requireParameters: resolver.requireParameters
@@ -2173,7 +2469,11 @@ function bindResolver(resolver, plugins) {
2173
2469
  return bound;
2174
2470
  }
2175
2471
  case "dynamic": {
2176
- const imports = buildImports(plugins, resolver.importBindings);
2472
+ const imports = buildImports({
2473
+ plugins,
2474
+ importBindings: resolver.importBindings,
2475
+ frameworkOrigin: true
2476
+ });
2177
2477
  const {
2178
2478
  getContext: getContext2,
2179
2479
  listItems,
@@ -2228,7 +2528,11 @@ function bindDefinitions(definitions, plugins) {
2228
2528
  return out;
2229
2529
  }
2230
2530
  function bindFormatter(formatter, plugins) {
2231
- const imports = buildImports(plugins, formatter.importBindings);
2531
+ const imports = buildImports({
2532
+ plugins,
2533
+ importBindings: formatter.importBindings,
2534
+ frameworkOrigin: true
2535
+ });
2232
2536
  const bound = { format: formatter.format };
2233
2537
  const { getContext: getContext2 } = formatter;
2234
2538
  if (getContext2)
@@ -2302,6 +2606,7 @@ function buildMethodEntries(descriptors, context, states) {
2302
2606
  const plugins = context.plugins;
2303
2607
  for (const [id, descriptor] of descriptors) {
2304
2608
  if (descriptor.pluginType !== "method") continue;
2609
+ if (isStandIn(descriptor)) continue;
2305
2610
  const out = normalizeOutput(descriptor.output);
2306
2611
  const entry = {
2307
2612
  pluginType: "method",
@@ -2316,17 +2621,32 @@ function buildMethodEntries(descriptors, context, states) {
2316
2621
  // Replaced below; never called.
2317
2622
  value: () => void 0
2318
2623
  };
2319
- const callRun = (input, ctx) => descriptor.run({
2320
- imports: buildImports(plugins, descriptor.importBindings, ctx),
2321
- state: states.get(id),
2322
- input
2323
- });
2624
+ const callRun = (input, ctx) => {
2625
+ const callContext = ctx ?? rootCallContext();
2626
+ return descriptor.run({
2627
+ imports: buildImports({
2628
+ plugins,
2629
+ importBindings: descriptor.importBindings,
2630
+ ctx: callContext
2631
+ }),
2632
+ state: states.get(id),
2633
+ input,
2634
+ callContext,
2635
+ annotate: (metadata) => {
2636
+ Object.assign(callContext.annotations, metadata);
2637
+ }
2638
+ });
2639
+ };
2324
2640
  const fold = (coreFn) => (input, ctx) => {
2325
2641
  let next = (i) => coreFn(i, ctx);
2326
2642
  for (const wrap of entry.chain) {
2327
2643
  const inner = next;
2328
2644
  next = (i) => wrap.run({
2329
- imports: buildImports(plugins, wrap.owner.importBindings, ctx),
2645
+ imports: buildImports({
2646
+ plugins,
2647
+ importBindings: wrap.owner.importBindings,
2648
+ ctx
2649
+ }),
2330
2650
  next: inner,
2331
2651
  input: i,
2332
2652
  // Overwritten by the chain item's own closure with the owning
@@ -2337,6 +2657,18 @@ function buildMethodEntries(descriptors, context, states) {
2337
2657
  return next(input);
2338
2658
  };
2339
2659
  const sdk = { context };
2660
+ const methodAnnotator = descriptor.annotator;
2661
+ const boundAnnotator = methodAnnotator ? (input) => methodAnnotator({ input }) : void 0;
2662
+ const outputPolicy = () => {
2663
+ const core = resolveCoreOptions(context);
2664
+ return {
2665
+ outputSchema: descriptor.meta?.outputSchema,
2666
+ skipOutputValidation: descriptor.skipOutputValidation,
2667
+ includeOutputValidationDroppedPaths: core?.includeOutputValidationDroppedPaths,
2668
+ methodName: descriptor.name,
2669
+ adaptError: core?.adaptError
2670
+ };
2671
+ };
2340
2672
  if (out.type === "list") {
2341
2673
  entry.value = createPaginatedFunction(
2342
2674
  fold(callRun),
@@ -2346,17 +2678,23 @@ function buildMethodEntries(descriptors, context, states) {
2346
2678
  name: descriptor.name,
2347
2679
  defaultPageSize: out.defaultPageSize,
2348
2680
  adaptPage: out.adaptPage,
2681
+ annotator: boundAnnotator,
2682
+ // Validate + strip each item against the item `outputSchema`
2683
+ // (item mode's sibling); dropped paths surface as `[].x` in the page's
2684
+ // `meta`, unioned across items.
2685
+ finalizePage: (page) => applyListOutputPolicy(page, outputPolicy()),
2349
2686
  getDeprecation: () => entry.meta?.deprecation
2350
2687
  }
2351
2688
  );
2352
2689
  } else if (out.type === "item") {
2353
- const itemCore = async (input, ctx) => callRun(input, ctx);
2690
+ const itemCore = async (input, ctx) => applyItemOutputPolicy(await callRun(input, ctx), outputPolicy());
2354
2691
  entry.value = createFunction(
2355
2692
  fold(itemCore),
2356
2693
  {
2357
2694
  sdk,
2358
2695
  schema: descriptor.inputSchema,
2359
2696
  name: descriptor.name,
2697
+ annotator: boundAnnotator,
2360
2698
  getDeprecation: () => entry.meta?.deprecation
2361
2699
  }
2362
2700
  );
@@ -2368,6 +2706,7 @@ function buildMethodEntries(descriptors, context, states) {
2368
2706
  name: descriptor.name,
2369
2707
  schema: descriptor.skipInputValidation ? void 0 : descriptor.inputSchema,
2370
2708
  positional: descriptor.positional,
2709
+ annotator: boundAnnotator,
2371
2710
  // The boundary reads the deprecation LIVE off the entry, so a
2372
2711
  // deprecation merged after build (defineMethodOverride, addPlugin)
2373
2712
  // fires too.
@@ -2388,12 +2727,22 @@ function buildMethodEntries(descriptors, context, states) {
2388
2727
  const internalValue = (...args) => canonicalValue(pack(args), INTERNAL_CALL);
2389
2728
  entry.value = (...args) => canonicalValue(pack(args));
2390
2729
  entry.internalValue = internalValue;
2391
- entry.bindInternal = (ctx) => ctx ? (...args) => canonicalValue(pack(args), childCallContext(ctx)) : internalValue;
2730
+ entry.bindInternal = (opts) => bindInternalTwin({
2731
+ ...opts,
2732
+ withContext: (context2) => {
2733
+ return (...args) => canonicalValue(pack(args), context2);
2734
+ },
2735
+ internalValue
2736
+ });
2392
2737
  entry.positional = names;
2393
2738
  } else {
2394
2739
  const internalValue = (input) => canonicalValue(input, INTERNAL_CALL);
2395
2740
  entry.internalValue = internalValue;
2396
- entry.bindInternal = (ctx) => ctx ? (input) => canonicalValue(input, childCallContext(ctx)) : internalValue;
2741
+ entry.bindInternal = (opts) => bindInternalTwin({
2742
+ ...opts,
2743
+ withContext: (context2) => (input) => canonicalValue(input, context2),
2744
+ internalValue
2745
+ });
2397
2746
  }
2398
2747
  plugins[id] = entry;
2399
2748
  }
@@ -2419,8 +2768,14 @@ function buildEagerArtifacts(descriptors, context, states) {
2419
2768
  if (!dispose) return;
2420
2769
  context.disposers?.push({
2421
2770
  id,
2771
+ // Teardown is framework-internal: an SDK method a `dispose` calls runs
2772
+ // on an internal-origin root (dropped from telemetry).
2422
2773
  dispose: (input) => dispose({
2423
- imports: buildImports(plugins, descriptor.importBindings),
2774
+ imports: buildImports({
2775
+ plugins,
2776
+ importBindings: descriptor.importBindings,
2777
+ frameworkOrigin: true
2778
+ }),
2424
2779
  state: states.get(id),
2425
2780
  input
2426
2781
  })
@@ -2430,7 +2785,10 @@ function buildEagerArtifacts(descriptors, context, states) {
2430
2785
  states.set(
2431
2786
  id,
2432
2787
  descriptor.setup ? descriptor.setup({
2433
- imports: buildImports(plugins, descriptor.importBindings)
2788
+ imports: buildImports({
2789
+ plugins,
2790
+ importBindings: descriptor.importBindings
2791
+ })
2434
2792
  }) : void 0
2435
2793
  );
2436
2794
  recordDisposer();
@@ -2442,14 +2800,20 @@ function buildEagerArtifacts(descriptors, context, states) {
2442
2800
  states.set(
2443
2801
  id,
2444
2802
  descriptor.setup ? descriptor.setup({
2445
- imports: buildImports(plugins, descriptor.importBindings)
2803
+ imports: buildImports({
2804
+ plugins,
2805
+ importBindings: descriptor.importBindings
2806
+ })
2446
2807
  }) : void 0
2447
2808
  );
2448
2809
  } else {
2449
2810
  states.set(
2450
2811
  id,
2451
2812
  descriptor.setup ? descriptor.setup({
2452
- imports: buildImports(plugins, descriptor.importBindings)
2813
+ imports: buildImports({
2814
+ plugins,
2815
+ importBindings: descriptor.importBindings
2816
+ })
2453
2817
  }) : void 0
2454
2818
  );
2455
2819
  if (descriptor.privileged) {
@@ -2467,7 +2831,7 @@ function buildEagerArtifacts(descriptors, context, states) {
2467
2831
  pluginType: "property",
2468
2832
  name: descriptor.name,
2469
2833
  getValue: () => get({
2470
- imports: buildImports(plugins, importBindings),
2834
+ imports: buildImports({ plugins, importBindings }),
2471
2835
  state: states.get(id)
2472
2836
  }),
2473
2837
  meta: descriptor.meta,
@@ -2503,7 +2867,7 @@ function resolvePlugin(sdk, ref) {
2503
2867
  return entry.getValue();
2504
2868
  }
2505
2869
  if (entry.pluginType === "method" && entry.internalValue) {
2506
- return entry.internalValue;
2870
+ return entry.bindInternal?.({ frameworkOrigin: true }) ?? entry.internalValue;
2507
2871
  }
2508
2872
  return entry.value;
2509
2873
  }
@@ -2538,7 +2902,7 @@ function resolveAggregates(descriptors, context) {
2538
2902
  if (descriptor.pluginType !== "aggregate") continue;
2539
2903
  const exports = {};
2540
2904
  for (const [binding, child] of Object.entries(descriptor.exports)) {
2541
- bindValue(exports, binding, plugins[child.id]);
2905
+ bindValue({ target: exports, key: binding, entry: plugins[child.id] });
2542
2906
  }
2543
2907
  plugins[id] = { pluginType: "aggregate", name: descriptor.name, exports };
2544
2908
  }
@@ -2581,23 +2945,39 @@ function assembleHooks(descriptors, context, states) {
2581
2945
  const plugins = context.plugins;
2582
2946
  for (const id of topoOrder(descriptors)) {
2583
2947
  const descriptor = descriptors.get(id);
2584
- if (!descriptor || descriptor.pluginType !== "hook" || !descriptor.observe) {
2948
+ if (!descriptor || descriptor.pluginType !== "hook" || !descriptor.observe && !descriptor.annotator) {
2585
2949
  continue;
2586
2950
  }
2587
- const { observe } = descriptor;
2588
- const imports = buildImports(plugins, descriptor.importBindings);
2951
+ const { observe, annotator } = descriptor;
2589
2952
  const state = states.get(id);
2590
2953
  const contributed = {};
2591
- if (observe.onMethodStart) {
2592
- const onStart = observe.onMethodStart;
2593
- contributed.onMethodStart = (input) => {
2594
- runIsolatedObserver(() => onStart({ imports, input, state }));
2595
- };
2954
+ if (observe?.onMethodStart || observe?.onMethodEnd) {
2955
+ const imports = buildImports({
2956
+ plugins,
2957
+ importBindings: descriptor.importBindings,
2958
+ frameworkOrigin: true
2959
+ });
2960
+ if (observe.onMethodStart) {
2961
+ const onStart = observe.onMethodStart;
2962
+ contributed.onMethodStart = (input) => {
2963
+ runIsolatedObserver(() => onStart({ imports, input, state }));
2964
+ };
2965
+ }
2966
+ if (observe.onMethodEnd) {
2967
+ const onEnd = observe.onMethodEnd;
2968
+ contributed.onMethodEnd = (input) => {
2969
+ runIsolatedObserver(() => onEnd({ imports, input, state }));
2970
+ };
2971
+ }
2596
2972
  }
2597
- if (observe.onMethodEnd) {
2598
- const onEnd = observe.onMethodEnd;
2599
- contributed.onMethodEnd = (input) => {
2600
- runIsolatedObserver(() => onEnd({ imports, input, state }));
2973
+ if (annotator) {
2974
+ const annotatorFn = annotator;
2975
+ contributed.annotator = ({ methodName, input }) => {
2976
+ try {
2977
+ return annotatorFn({ methodName, input, state });
2978
+ } catch {
2979
+ return {};
2980
+ }
2601
2981
  };
2602
2982
  }
2603
2983
  context.hooks = buildHooks(context.hooks, contributed);
@@ -2631,7 +3011,11 @@ function createSdk(root, options) {
2631
3011
  pluginSurface = plugins2[plugin.id].exports;
2632
3012
  } else {
2633
3013
  pluginSurface = {};
2634
- bindValue(pluginSurface, plugin.name, plugins2[plugin.id]);
3014
+ bindValue({
3015
+ target: pluginSurface,
3016
+ key: plugin.name,
3017
+ entry: plugins2[plugin.id]
3018
+ });
2635
3019
  }
2636
3020
  for (const key of Object.keys(legacyExports)) context.surface[key] = key;
2637
3021
  if (plugin.pluginType === "aggregate") {
@@ -2648,7 +3032,7 @@ function createSdk(root, options) {
2648
3032
  if (root.pluginType === "method" || root.pluginType === "property") {
2649
3033
  context.surface[root.name] = root.id;
2650
3034
  const sdk = buildSurface(context);
2651
- bindValue(sdk, root.name, plugins[root.id]);
3035
+ bindValue({ target: sdk, key: root.name, entry: plugins[root.id] });
2652
3036
  return sdk;
2653
3037
  }
2654
3038
  if (root.pluginType === "aggregate")
@@ -2680,7 +3064,7 @@ function addModelPlugin(sdk, plugin, options = {}) {
2680
3064
  context.surface[binding] = child.id;
2681
3065
  }
2682
3066
  } else {
2683
- bindValue(sdk, plugin.name, entry);
3067
+ bindValue({ target: sdk, key: plugin.name, entry });
2684
3068
  context.surface[plugin.name] = plugin.id;
2685
3069
  }
2686
3070
  }
@@ -4019,7 +4403,9 @@ export {
4019
4403
  createSdk,
4020
4404
  createValidator,
4021
4405
  dangerousContextPlugin,
4406
+ declareDefault,
4022
4407
  declareMethod,
4408
+ declareOptionalMethod,
4023
4409
  declareOptionalProperty,
4024
4410
  declarePlugin,
4025
4411
  declareProperty,