@zapier/kitcore 0.11.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # @zapier/kitcore
2
2
 
3
+ ## 0.13.0
4
+
5
+ ### Minor Changes
6
+
7
+ - bc7405e: `@zapier/kitcore`: added `NegatableMetadata` and a `getNegatable` reader for marking optional boolean schema fields where omission is distinct from false (omitting means "use the server default" or "keep the current value"). Tooling built on kitcore can read the marker to offer an explicit disable affordance — for example, the CLI generates a `--disabled` flag from it.
8
+
9
+ `@zapier/zapier-sdk`: the `publishWorkflowVersion` and `publishWorkflowDraft` `enabled` parameter descriptions now state what omitting the parameter does (uses the server default / preserves the current state) without naming CLI-specific flags. `NegatableMetadata` and `getNegatable` are re-exported from `@zapier/kitcore` for schema-metadata consumers.
10
+
11
+ ## 0.12.0
12
+
13
+ ### Minor Changes
14
+
15
+ - 281513d: Added `declareDefault`, a helper that imports the capability a plugin provides and defaults to that plugin when nothing else provides its id. Unlike `declareMethod` / `declareProperty`, it carries a real plugin, so its id, type, and kind come from the plugin (one kind-agnostic helper, no method/property split). At materialization an explicit provider of the same id preempts the default with no duplicate-id error, a lone default is used, and two different defaults for one id error only when nothing else provides it.
16
+
17
+ Added `declareOptionalMethod`, the method twin of `declareOptionalProperty`: an optional stand-in for a method registered elsewhere. When nothing provides the id it is not a missing dependency; the import binds `undefined` (typed `callable | undefined`), so a consumer can reference a foreign method that may or may not be present and call it as `imports.method?.(...)`.
18
+
19
+ - 87e634f: A method's `outputSchema` is now enforced at runtime instead of being documentation-only: the framework validates and strips the method's output against it, an `item` method's `{ data }` envelope as a whole and a `list` method's page element by element. A failure names the method and routes through the head's `adaptError`.
20
+ - `skipOutputValidation` on `defineMethod` opts a method out, the output-side partner of `skipInputValidation`. Set it when a schema is meant for projection only (registry, CLI, MCP, docs) and must not shape the response.
21
+ - The `includeOutputValidationDroppedPaths` core option reports what the strip removed, as `meta.outputValidation.droppedPaths`. Off by default; turn it on while reconciling a schema against what an API actually returns, so an over-narrow schema surfaces as data loss instead of looking like the API stopped returning a field.
22
+ - `ResponseMeta` types that sidecar. An `item` method's declared return is now `{ data, meta? }` and `SdkPage` gains the same optional `meta`, so a report reads the same whichever mode produced it. Both stay framework-set: a handler or `adaptPage` returns only `data` / `nextCursor`.
23
+
24
+ Existing methods that declare an `outputSchema` need to either confirm it matches what the API returns or set `skipOutputValidation: true`, since a schema that was previously ignored now shapes the response.
25
+
3
26
  ## 0.11.0
4
27
 
5
28
  ### Minor Changes
package/README.md CHANGED
@@ -443,9 +443,36 @@ await controller.listChoices({
443
443
  // { data: [{ label: "Slack", value: "slack" }], nextCursor }
444
444
  ```
445
445
 
446
- ## Stand-ins
446
+ ## Plugin references
447
447
 
448
- When a plugin depends on something supplied elsewhere (a configured client, a test double), declare a typed stand-in with `declareMethod` / `declareProperty` (a single leaf) or `declarePlugin` (a whole module, with its export surface declared as leaf stand-ins). Dependents reference it for typing; at `createSdk` it is satisfied by whatever real plugin is registered under the same id, and an unsatisfied stand-in is a **compile-time** error (with a runtime backstop). You reference a stand-in by `id` (`namespace/name`, or a bare name), and the contract is provided as explicit type arguments (`declareMethod<"fetch", Input, Output>({ id: "fetch" })`), because the id must be a literal the dependency ledger can read. The binding is the id's last segment.
448
+ A plugin _references_ a capability supplied elsewhere by its id, without providing it. Use `declareMethod` / `declareProperty` for a single leaf, or `declarePlugin` for a whole module (its export surface declared as leaf references). A reference binds, at `createSdk`, to whatever real plugin is registered under the same id. You reference by `id` (`namespace/name`, or a bare name), with the contract as explicit type arguments (`declareMethod<"fetch", Input, Output>({ id: "fetch" })`), because the id must be a literal the dependency ledger can read; the binding is the id's last segment.
449
+
450
+ Two flavors, by what happens when nothing provides the id:
451
+
452
+ - **Required** (`declareMethod` / `declareProperty`): an unsatisfied reference is a **compile-time** error (with a runtime backstop). Use when the capability must be present.
453
+ - **Optional** (`declareOptionalProperty` / `declareOptionalMethod`): binds `undefined` instead of failing, so the consumer handles absence in code (`{ ...DEFAULTS, ...imports.config }` for a value, `imports.track?.(...)` for a method). Use to reference a _foreign_ capability userland may or may not import, without claiming its slot.
454
+
455
+ ## Defaults
456
+
457
+ When you _own_ a capability slot and can ship a working implementation, register it as a default with `declareDefault({ plugin })` rather than referencing it. A default is a real, single node in the graph, so it works out of the box, hooks and middleware can wrap it, and an explicit provider of the same id silently preempts it (that is the seam for userland or another plugin to replace it).
458
+
459
+ ```ts
460
+ import { declareDefault } from "@zapier/kitcore";
461
+
462
+ const httpPlugin = defineMethod({
463
+ name: "http",
464
+ // Ship a working default pipeline stage; userland can wrap or replace it.
465
+ imports: [declareDefault({ plugin: defaultAuthStagePlugin })],
466
+ run: ({ imports }) => imports.authStage(/* ... */),
467
+ });
468
+ ```
469
+
470
+ Default versus optional reference is an ownership question:
471
+
472
+ - **Own the slot** → default. It is yours to provide, so provide the fallback.
473
+ - **Don't own it** → optional reference. Point at it without claiming it.
474
+
475
+ The collision rules follow from ownership: two plugins defaulting one id to _different_ implementations conflict (both claimed the slot), while two optional references to the same foreign id never conflict (neither did). So a default on an id you do not own is a latent conflict, triggered the moment a second plugin does the same.
449
476
 
450
477
  ## Namespaces
451
478
 
package/dist/index.cjs CHANGED
@@ -49,7 +49,9 @@ __export(index_exports, {
49
49
  createSdk: () => createSdk,
50
50
  createValidator: () => createValidator,
51
51
  dangerousContextPlugin: () => dangerousContextPlugin,
52
+ declareDefault: () => declareDefault,
52
53
  declareMethod: () => declareMethod,
54
+ declareOptionalMethod: () => declareOptionalMethod,
53
55
  declareOptionalProperty: () => declareOptionalProperty,
54
56
  declarePlugin: () => declarePlugin,
55
57
  declareProperty: () => declareProperty,
@@ -71,6 +73,7 @@ __export(index_exports, {
71
73
  getCurrentDepth: () => getCurrentDepth,
72
74
  getCurrentScope: () => getCurrentScope,
73
75
  getFieldDescriptions: () => getFieldDescriptions,
76
+ getNegatable: () => getNegatable,
74
77
  getOutputSchema: () => getOutputSchema,
75
78
  getRegistryPlugin: () => getRegistryPlugin,
76
79
  getSchemaDescription: () => getSchemaDescription,
@@ -178,6 +181,15 @@ function isPositional(schema) {
178
181
  }
179
182
  return false;
180
183
  }
184
+ function getNegatable(schema) {
185
+ const negatable = schema.meta?.()?.negatable;
186
+ if (negatable === true) return true;
187
+ if (typeof negatable === "string" && negatable.length > 0) return negatable;
188
+ if (schema instanceof import_zod.z.ZodOptional || schema instanceof import_zod.z.ZodDefault) {
189
+ return getNegatable(schema._zod.def.innerType);
190
+ }
191
+ return void 0;
192
+ }
181
193
  function openEnum(values, description) {
182
194
  return import_zod.z.union([import_zod.z.enum(values), import_zod.z.string()]).describe(description);
183
195
  }
@@ -1024,7 +1036,8 @@ function isSdkPage(value) {
1024
1036
  }
1025
1037
  function createPageFunction(coreFn, {
1026
1038
  sdk,
1027
- adaptPage
1039
+ adaptPage,
1040
+ finalizePage
1028
1041
  }) {
1029
1042
  const functionName = coreFn.name + "Page";
1030
1043
  const namedFunctions = {
@@ -1037,7 +1050,7 @@ function createPageFunction(coreFn, {
1037
1050
  `${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\`.`
1038
1051
  );
1039
1052
  }
1040
- return page;
1053
+ return finalizePage ? finalizePage(page) : page;
1041
1054
  } catch (error) {
1042
1055
  throw normalizeError(
1043
1056
  error,
@@ -1056,9 +1069,14 @@ function createPaginatedFunction(coreFn, options) {
1056
1069
  defaultPageSize,
1057
1070
  adaptPage,
1058
1071
  annotator,
1072
+ finalizePage,
1059
1073
  getDeprecation
1060
1074
  } = options;
1061
- const pageFunction = createPageFunction(coreFn, { sdk, adaptPage });
1075
+ const pageFunction = createPageFunction(coreFn, {
1076
+ sdk,
1077
+ adaptPage,
1078
+ finalizePage
1079
+ });
1062
1080
  const functionName = name || coreFn.name;
1063
1081
  const namedFunctions = {
1064
1082
  [functionName]: function(callOptions) {
@@ -1584,6 +1602,7 @@ function defineMethod(config) {
1584
1602
  importBindings: deps.bindings,
1585
1603
  inputSchema: config.inputSchema,
1586
1604
  skipInputValidation: config.skipInputValidation,
1605
+ skipOutputValidation: config.skipOutputValidation,
1587
1606
  meta: collectLeafMeta(config),
1588
1607
  resolvers: config.resolvers,
1589
1608
  formatter: config.formatter,
@@ -1690,6 +1709,29 @@ function declareMethod(config) {
1690
1709
  }
1691
1710
  };
1692
1711
  }
1712
+ function declareOptionalMethod(config) {
1713
+ const { name, namespace } = parseId(config.id);
1714
+ const id = makeId(name, namespace);
1715
+ return {
1716
+ pluginType: "method",
1717
+ name,
1718
+ namespace,
1719
+ id,
1720
+ standIn: true,
1721
+ optional: true,
1722
+ imports: [],
1723
+ importBindings: [],
1724
+ run: () => {
1725
+ throw new Error(
1726
+ `Plugin "${id}" is an optional stand-in (declareOptionalMethod) with no implementation. Its binding is \`undefined\` unless a real plugin is registered under this id.`
1727
+ );
1728
+ }
1729
+ // Requires nothing (phantom carrier `<never, never>`): a consumer that
1730
+ // imports it still passes `createSdk`'s completeness check unprovided. The
1731
+ // `optional: true` literal drives `PluginSurface` to type the binding
1732
+ // `| undefined`.
1733
+ };
1734
+ }
1693
1735
  function defineProperty(config) {
1694
1736
  const deps = normalizeImports(config.imports);
1695
1737
  return {
@@ -1735,6 +1777,11 @@ function declareOptionalProperty(config) {
1735
1777
  // import binding is still typed `TValue | undefined` from the descriptor.
1736
1778
  };
1737
1779
  }
1780
+ function declareDefault({
1781
+ plugin
1782
+ }) {
1783
+ return { ...plugin, defaultSource: plugin };
1784
+ }
1738
1785
  function defineHook(config) {
1739
1786
  const deps = normalizeImports(config.imports);
1740
1787
  return {
@@ -2036,6 +2083,97 @@ var getRegistryPlugin = defineMethod({
2036
2083
  run: ({ imports, input }) => buildSurfaceRegistry(imports.context, input?.package)
2037
2084
  });
2038
2085
 
2086
+ // src/utils/output-policy.ts
2087
+ function isRecord(value) {
2088
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2089
+ }
2090
+ function diffDroppedPaths(raw, parsed, prefix = "") {
2091
+ const paths = [];
2092
+ walkDroppedPaths(raw, parsed, prefix, paths);
2093
+ return paths;
2094
+ }
2095
+ function walkDroppedPaths(raw, parsed, prefix, out) {
2096
+ if (Array.isArray(raw) && Array.isArray(parsed)) {
2097
+ const seen = /* @__PURE__ */ new Set();
2098
+ const length = Math.min(raw.length, parsed.length);
2099
+ for (let index = 0; index < length; index++) {
2100
+ const elementPaths = [];
2101
+ walkDroppedPaths(raw[index], parsed[index], `${prefix}[]`, elementPaths);
2102
+ for (const path of elementPaths) {
2103
+ if (seen.has(path)) continue;
2104
+ seen.add(path);
2105
+ out.push(path);
2106
+ }
2107
+ }
2108
+ return;
2109
+ }
2110
+ if (isRecord(raw) && isRecord(parsed)) {
2111
+ for (const key of Object.keys(raw)) {
2112
+ const path = prefix ? `${prefix}.${key}` : key;
2113
+ if (!(key in parsed)) {
2114
+ out.push(path);
2115
+ continue;
2116
+ }
2117
+ walkDroppedPaths(raw[key], parsed[key], path, out);
2118
+ }
2119
+ return;
2120
+ }
2121
+ }
2122
+ function parseOutput(schema, value, policy, locator) {
2123
+ const result = schema.safeParse(value);
2124
+ if (result.success) return result.data;
2125
+ const issues = result.error.issues.map((issue) => {
2126
+ const path = issue.path.length > 0 ? issue.path.join(".") : "data";
2127
+ return `${path}: ${issue.message}`;
2128
+ });
2129
+ const subject = policy.methodName ? ` for "${policy.methodName}"` : "";
2130
+ const at = locator ? ` at ${locator}` : "";
2131
+ throw createCoreError(
2132
+ {
2133
+ code: CoreErrorCode.Validation,
2134
+ message: `Output validation failed${subject}${at}:
2135
+ ${issues.join("\n ")}
2136
+
2137
+ 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.`,
2138
+ details: { zodErrors: result.error.issues, output: value }
2139
+ },
2140
+ policy.adaptError
2141
+ );
2142
+ }
2143
+ function applyItemOutputPolicy(result, policy) {
2144
+ const schema = policy.outputSchema;
2145
+ if (!schema || policy.skipOutputValidation) return result;
2146
+ if (!isRecord(result) || !("data" in result)) return result;
2147
+ const data = parseOutput(schema, result.data, policy);
2148
+ const next = { ...result, data };
2149
+ if (policy.includeOutputValidationDroppedPaths) {
2150
+ const droppedPaths = diffDroppedPaths(result.data, data);
2151
+ if (droppedPaths.length > 0) {
2152
+ next.meta = withOutputValidation(result.meta, droppedPaths);
2153
+ }
2154
+ }
2155
+ return next;
2156
+ }
2157
+ function withOutputValidation(existing, droppedPaths) {
2158
+ const base = isRecord(existing) ? existing : {};
2159
+ return { ...base, outputValidation: { droppedPaths } };
2160
+ }
2161
+ function applyListOutputPolicy(page, policy) {
2162
+ const schema = policy.outputSchema;
2163
+ if (!schema || policy.skipOutputValidation) return page;
2164
+ const data = page.data.map(
2165
+ (item, index) => parseOutput(schema, item, policy, `data[${index}]`)
2166
+ );
2167
+ const next = { ...page, data };
2168
+ if (policy.includeOutputValidationDroppedPaths) {
2169
+ const droppedPaths = diffDroppedPaths(page.data, data);
2170
+ if (droppedPaths.length > 0) {
2171
+ next.meta = { ...page.meta, outputValidation: { droppedPaths } };
2172
+ }
2173
+ }
2174
+ return next;
2175
+ }
2176
+
2039
2177
  // src/model/materialize.ts
2040
2178
  var FRAMEWORK_CONFIGURATION_IDS = /* @__PURE__ */ new Set([
2041
2179
  CORE_OPTIONS_ID
@@ -2099,6 +2237,9 @@ function edgesOf(plugin) {
2099
2237
  function isStandIn(plugin) {
2100
2238
  return (plugin.pluginType === "method" || plugin.pluginType === "property" || plugin.pluginType === "aggregate") && plugin.standIn === true;
2101
2239
  }
2240
+ function isDefault(plugin) {
2241
+ return (plugin.pluginType === "method" || plugin.pluginType === "property") && plugin.defaultSource !== void 0;
2242
+ }
2102
2243
  function topoOrder(descriptors) {
2103
2244
  const order = [];
2104
2245
  const visited = /* @__PURE__ */ new Set();
@@ -2113,28 +2254,89 @@ function topoOrder(descriptors) {
2113
2254
  return order;
2114
2255
  }
2115
2256
  function collectPlugins(root, materialized = /* @__PURE__ */ new Set(), configuration) {
2257
+ const rank = (plugin) => isStandIn(plugin) ? 0 : isDefault(plugin) ? 1 : 2;
2258
+ const allNodes = [];
2259
+ const seen = /* @__PURE__ */ new Set();
2260
+ const collect = (plugin) => {
2261
+ if (materialized.has(plugin.id) || seen.has(plugin)) return;
2262
+ seen.add(plugin);
2263
+ allNodes.push(plugin);
2264
+ for (const edge of edgesOf(plugin)) collect(edge);
2265
+ };
2266
+ collect(root);
2267
+ const childrenOf = /* @__PURE__ */ new Map();
2268
+ const candidatesById = /* @__PURE__ */ new Map();
2269
+ for (const node of allNodes) {
2270
+ childrenOf.set(
2271
+ node,
2272
+ edgesOf(node).filter((edge) => seen.has(edge))
2273
+ );
2274
+ const candidates = candidatesById.get(node.id);
2275
+ if (candidates) candidates.push(node);
2276
+ else candidatesById.set(node.id, [node]);
2277
+ }
2278
+ const live = new Set(allNodes);
2279
+ for (; ; ) {
2280
+ const reachable = /* @__PURE__ */ new Set();
2281
+ if (live.has(root)) reachable.add(root);
2282
+ const queue = reachable.has(root) ? [root] : [];
2283
+ while (queue.length) {
2284
+ const node = queue.pop();
2285
+ for (const child of childrenOf.get(node) ?? []) {
2286
+ if (reachable.has(child)) continue;
2287
+ reachable.add(child);
2288
+ if (live.has(child)) queue.push(child);
2289
+ }
2290
+ }
2291
+ let changed = false;
2292
+ for (const node of live) {
2293
+ if (!reachable.has(node)) {
2294
+ live.delete(node);
2295
+ changed = true;
2296
+ }
2297
+ }
2298
+ for (const candidates of candidatesById.values()) {
2299
+ let maxRank = -1;
2300
+ for (const candidate of candidates) {
2301
+ if (live.has(candidate)) maxRank = Math.max(maxRank, rank(candidate));
2302
+ }
2303
+ if (maxRank < 0) continue;
2304
+ for (const candidate of candidates) {
2305
+ if (live.has(candidate) && rank(candidate) < maxRank) {
2306
+ live.delete(candidate);
2307
+ changed = true;
2308
+ }
2309
+ }
2310
+ }
2311
+ if (!changed) break;
2312
+ }
2116
2313
  const byId = /* @__PURE__ */ new Map();
2117
- const visit = (plugin) => {
2118
- if (materialized.has(plugin.id)) return;
2119
- const existing = byId.get(plugin.id);
2120
- if (existing === plugin) return;
2121
- if (existing) {
2122
- const bothReal = !isStandIn(existing) && !isStandIn(plugin);
2123
- if (bothReal) {
2314
+ const conflictedDefaults = /* @__PURE__ */ new Set();
2315
+ const isOptional = (plugin) => "optional" in plugin && plugin.optional === true;
2316
+ for (const [id, candidates] of candidatesById) {
2317
+ const liveCandidates = candidates.filter(
2318
+ (candidate) => live.has(candidate)
2319
+ );
2320
+ const winner = liveCandidates.find((candidate) => !isOptional(candidate)) ?? liveCandidates[0];
2321
+ if (!winner) continue;
2322
+ if (liveCandidates.length > 1) {
2323
+ const winnerRank = rank(winner);
2324
+ if (winnerRank === 2) {
2124
2325
  throw new Error(
2125
- `createSdk: duplicate plugin id "${plugin.id}". Two different plugins registered under the same id.`
2326
+ `createSdk: duplicate plugin id "${id}". Two different plugins registered under the same id.`
2126
2327
  );
2127
2328
  }
2128
- if (isStandIn(existing) && !isStandIn(plugin)) {
2129
- byId.set(plugin.id, plugin);
2130
- for (const edge of edgesOf(plugin)) visit(edge);
2329
+ if (winnerRank === 1) {
2330
+ const sources = new Set(
2331
+ liveCandidates.map(
2332
+ (candidate) => candidate.defaultSource
2333
+ )
2334
+ );
2335
+ if (sources.size > 1) conflictedDefaults.add(id);
2131
2336
  }
2132
- return;
2133
2337
  }
2134
- byId.set(plugin.id, plugin);
2135
- for (const edge of edgesOf(plugin)) visit(edge);
2136
- };
2137
- visit(root);
2338
+ byId.set(id, winner);
2339
+ }
2138
2340
  if (configuration) {
2139
2341
  for (const [id, value] of Object.entries(configuration)) {
2140
2342
  const existing = byId.get(id);
@@ -2185,6 +2387,14 @@ function collectPlugins(root, materialized = /* @__PURE__ */ new Set(), configur
2185
2387
  );
2186
2388
  }
2187
2389
  }
2390
+ for (const id of conflictedDefaults) {
2391
+ const winner = byId.get(id);
2392
+ if (winner && isDefault(winner)) {
2393
+ throw new Error(
2394
+ `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.`
2395
+ );
2396
+ }
2397
+ }
2188
2398
  return byId;
2189
2399
  }
2190
2400
  function bindValue({
@@ -2508,6 +2718,7 @@ function buildMethodEntries(descriptors, context, states) {
2508
2718
  const plugins = context.plugins;
2509
2719
  for (const [id, descriptor] of descriptors) {
2510
2720
  if (descriptor.pluginType !== "method") continue;
2721
+ if (isStandIn(descriptor)) continue;
2511
2722
  const out = normalizeOutput(descriptor.output);
2512
2723
  const entry = {
2513
2724
  pluginType: "method",
@@ -2560,6 +2771,16 @@ function buildMethodEntries(descriptors, context, states) {
2560
2771
  const sdk = { context };
2561
2772
  const methodAnnotator = descriptor.annotator;
2562
2773
  const boundAnnotator = methodAnnotator ? (input) => methodAnnotator({ input }) : void 0;
2774
+ const outputPolicy = () => {
2775
+ const core = resolveCoreOptions(context);
2776
+ return {
2777
+ outputSchema: descriptor.meta?.outputSchema,
2778
+ skipOutputValidation: descriptor.skipOutputValidation,
2779
+ includeOutputValidationDroppedPaths: core?.includeOutputValidationDroppedPaths,
2780
+ methodName: descriptor.name,
2781
+ adaptError: core?.adaptError
2782
+ };
2783
+ };
2563
2784
  if (out.type === "list") {
2564
2785
  entry.value = createPaginatedFunction(
2565
2786
  fold(callRun),
@@ -2570,11 +2791,15 @@ function buildMethodEntries(descriptors, context, states) {
2570
2791
  defaultPageSize: out.defaultPageSize,
2571
2792
  adaptPage: out.adaptPage,
2572
2793
  annotator: boundAnnotator,
2794
+ // Validate + strip each item against the item `outputSchema`
2795
+ // (item mode's sibling); dropped paths surface as `[].x` in the page's
2796
+ // `meta`, unioned across items.
2797
+ finalizePage: (page) => applyListOutputPolicy(page, outputPolicy()),
2573
2798
  getDeprecation: () => entry.meta?.deprecation
2574
2799
  }
2575
2800
  );
2576
2801
  } else if (out.type === "item") {
2577
- const itemCore = async (input, ctx) => callRun(input, ctx);
2802
+ const itemCore = async (input, ctx) => applyItemOutputPolicy(await callRun(input, ctx), outputPolicy());
2578
2803
  entry.value = createFunction(
2579
2804
  fold(itemCore),
2580
2805
  {
@@ -4291,7 +4516,9 @@ function createCorePlugin(options) {
4291
4516
  createSdk,
4292
4517
  createValidator,
4293
4518
  dangerousContextPlugin,
4519
+ declareDefault,
4294
4520
  declareMethod,
4521
+ declareOptionalMethod,
4295
4522
  declareOptionalProperty,
4296
4523
  declarePlugin,
4297
4524
  declareProperty,
@@ -4313,6 +4540,7 @@ function createCorePlugin(options) {
4313
4540
  getCurrentDepth,
4314
4541
  getCurrentScope,
4315
4542
  getFieldDescriptions,
4543
+ getNegatable,
4316
4544
  getOutputSchema,
4317
4545
  getRegistryPlugin,
4318
4546
  getSchemaDescription,