@zapier/kitcore 0.19.0 → 0.21.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
@@ -1,901 +1,912 @@
1
- // src/utils/string-utils.ts
2
- function toTitleCase(input) {
3
- return input.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_\-]+/g, " ").replace(/\s+/g, " ").trim().split(" ").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
4
- }
5
- function toSnakeCase(input) {
6
- let result = input.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[\s\-]+/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "").toLowerCase();
7
- if (/^[0-9]/.test(result)) {
8
- result = "_" + result;
9
- }
10
- return result;
11
- }
12
- function pluralize(word) {
13
- if (/s$/i.test(word)) return word;
14
- if (/[bcdfghjklmnpqrstvwxz]y$/i.test(word)) {
15
- return word.slice(0, -1) + "ies";
16
- }
17
- return word + "s";
1
+ // src/utils/logging.ts
2
+ function createDeprecationLogger(tag) {
3
+ const loggedDeprecations = /* @__PURE__ */ new Set();
4
+ return {
5
+ logDeprecation(message) {
6
+ if (loggedDeprecations.has(message)) return;
7
+ loggedDeprecations.add(message);
8
+ console.warn(`[${tag}] Deprecation: ${message}`);
9
+ },
10
+ resetDeprecationWarnings() {
11
+ loggedDeprecations.clear();
12
+ }
13
+ };
18
14
  }
19
- function pluralizeLastWord(title) {
20
- const words = title.split(" ");
21
- return [...words.slice(0, -1), pluralize(words[words.length - 1])].join(" ");
15
+ var { logDeprecation, resetDeprecationWarnings } = createDeprecationLogger("core");
16
+ function createStabilityNoticeLogger(tag) {
17
+ const loggedNotices = /* @__PURE__ */ new Set();
18
+ return {
19
+ logStabilityNotice(message) {
20
+ if (loggedNotices.has(message)) return;
21
+ loggedNotices.add(message);
22
+ console.warn(`[${tag}] ${message}`);
23
+ },
24
+ resetStabilityNotices() {
25
+ loggedNotices.clear();
26
+ }
27
+ };
22
28
  }
29
+ var { logStabilityNotice, resetStabilityNotices } = createStabilityNoticeLogger("core");
23
30
 
24
- // src/utils/schema-utils.ts
25
- import { z } from "zod";
26
- function canonicalInputSchema(schema) {
27
- if (schema instanceof z.ZodUnion) {
28
- return schema.options[0];
29
- }
30
- return schema;
31
+ // src/model/shared.ts
32
+ var CONTEXT = Symbol.for("kitcore.context");
33
+ function parseId(id) {
34
+ const at = id.lastIndexOf("/");
35
+ return at === -1 ? { name: id, namespace: void 0 } : { name: id.slice(at + 1), namespace: id.slice(0, at) };
31
36
  }
32
- function unwrapSchema(schema) {
33
- let inner = schema;
34
- let required = true;
35
- for (; ; ) {
36
- if (inner instanceof z.ZodOptional || inner instanceof z.ZodDefault) {
37
- required = false;
38
- inner = inner.unwrap();
39
- } else if (inner instanceof z.ZodNullable) {
40
- inner = inner.unwrap();
41
- } else {
42
- break;
37
+ function makeId(name, namespace, kind = "leaf") {
38
+ validateName(name, kind);
39
+ if (namespace !== void 0) validateNamespace(namespace);
40
+ return namespace ? `${namespace}/${name}` : name;
41
+ }
42
+ var NAME_RE = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
43
+ var SEGMENT_RE = /^@?[A-Za-z0-9._-]+$/;
44
+ function validateName(name, kind) {
45
+ if (name === "") throw new Error("Plugin name must not be empty.");
46
+ if (kind === "leaf") {
47
+ if (!NAME_RE.test(name)) {
48
+ throw new Error(
49
+ `Plugin name "${name}" must be a valid JS identifier (it is the binding name).`
50
+ );
43
51
  }
52
+ } else if (!SEGMENT_RE.test(name)) {
53
+ throw new Error(
54
+ `Plugin name "${name}" must be package-like (letters, digits, ".", "_", "-", optional leading "@") with no "/".`
55
+ );
44
56
  }
45
- return { inner, required };
46
57
  }
47
- function objectShapeOf(schema) {
48
- const canonical = canonicalInputSchema(schema);
49
- if (!canonical) return void 0;
50
- const { inner } = unwrapSchema(canonical);
51
- if (inner instanceof z.ZodObject) {
52
- return inner.shape;
58
+ function validateNamespace(namespace) {
59
+ if (namespace === "") throw new Error("Plugin namespace must not be empty.");
60
+ for (const segment of namespace.split("/")) {
61
+ if (!SEGMENT_RE.test(segment)) {
62
+ throw new Error(
63
+ `Plugin namespace "${namespace}" is invalid: each "/"-separated segment must be package-like (letters, digits, ".", "_", "-", optional leading "@").`
64
+ );
65
+ }
53
66
  }
54
- return void 0;
55
67
  }
56
- function getOutputSchema(inputSchema) {
57
- return inputSchema._zod.def.outputSchema;
58
- }
59
- function withOutputSchema(inputSchema, outputSchema) {
60
- Object.assign(inputSchema._zod.def, {
61
- outputSchema
62
- });
63
- return inputSchema;
68
+ function isStandIn(plugin) {
69
+ return (plugin.pluginType === "method" || plugin.pluginType === "property" || plugin.pluginType === "aggregate") && plugin.standIn === true;
64
70
  }
65
- function withResolver(schema, config) {
66
- schema._zod.def.resolverMeta = config;
67
- return schema;
71
+ function pickDefined(source, keys) {
72
+ const out = {};
73
+ for (const key of keys) {
74
+ if (source[key] !== void 0) out[key] = source[key];
75
+ }
76
+ return out;
68
77
  }
69
- function getSchemaDescription(schema) {
70
- return schema.description;
78
+
79
+ // src/model/types.ts
80
+ var OVERRIDABLE_META_KEYS = [
81
+ "description",
82
+ "categories",
83
+ "itemType",
84
+ "returnType",
85
+ "packages",
86
+ "experimental",
87
+ "deprecation",
88
+ "supportsJsonOutput"
89
+ ];
90
+ var METHOD_META_KEYS = Object.keys({
91
+ description: true,
92
+ categories: true,
93
+ packages: true,
94
+ stability: true,
95
+ experimental: true,
96
+ deprecation: true,
97
+ type: true,
98
+ itemType: true,
99
+ returnType: true,
100
+ confirm: true,
101
+ aliases: true,
102
+ supportsJsonOutput: true
103
+ });
104
+ var PROPERTY_META_KEYS = Object.keys({
105
+ description: true,
106
+ categories: true,
107
+ packages: true,
108
+ stability: true,
109
+ experimental: true,
110
+ deprecation: true
111
+ });
112
+ var PLUGIN_TYPES = new Set(
113
+ Object.keys({
114
+ method: true,
115
+ property: true,
116
+ aggregate: true,
117
+ hook: true,
118
+ "method-override": true
119
+ })
120
+ );
121
+
122
+ // src/model/plugin-argument.ts
123
+ function assertKnownPluginType({
124
+ plugin,
125
+ where
126
+ }) {
127
+ const { pluginType, id } = plugin;
128
+ if (PLUGIN_TYPES.has(pluginType)) return;
129
+ throw new Error(
130
+ `${where}: "${id}" has unknown pluginType "${pluginType}". A descriptor comes from a \`define*\` factory.`
131
+ );
71
132
  }
72
- function getFieldDescriptions(schema) {
73
- const descriptions = {};
74
- const shape = schema.shape;
75
- for (const [key, fieldSchema] of Object.entries(shape)) {
76
- if (fieldSchema instanceof z.ZodType && fieldSchema.description) {
77
- descriptions[key] = fieldSchema.description;
78
- }
133
+ function assertDescriptorShape(value, { where, arrayFix }) {
134
+ const pluginType = value?.pluginType;
135
+ if (typeof pluginType !== "string") {
136
+ const got = Array.isArray(value) ? `an array of ${value.length}. ${arrayFix}` : typeof value === "function" ? "a function, which was the old `(sdk) => provides` plugin shape" : `${value === null ? "null" : typeof value} with no \`pluginType\``;
137
+ throw new Error(
138
+ `${where}: expected a plugin descriptor built by a \`define*\` factory, but got ${got}.`
139
+ );
79
140
  }
80
- return descriptions;
141
+ assertKnownPluginType({ plugin: value, where });
81
142
  }
82
- function withPositional(schema) {
83
- Object.assign(schema._zod.def, {
84
- positionalMeta: { positional: true }
143
+ function assertPluginArgument(plugin, { caller, asRoot }) {
144
+ assertDescriptorShape(plugin, {
145
+ where: caller,
146
+ arrayFix: asRoot ? "Wrap them in definePlugin({ exports })" : "Add each entry in its own call"
85
147
  });
86
- return schema;
87
- }
88
- function schemaHasPositionalMeta(schema) {
89
- return "positionalMeta" in schema._zod.def;
90
- }
91
- function isPositional(schema) {
92
- if (schemaHasPositionalMeta(schema) && schema._zod.def.positionalMeta?.positional) {
93
- return true;
94
- }
95
- if (schema instanceof z.ZodOptional) {
96
- return isPositional(schema._zod.def.innerType);
148
+ const { id, pluginType } = plugin;
149
+ if (isStandIn(plugin)) {
150
+ throw new Error(
151
+ `${caller}: "${id}" is a declare* stand-in, which declares a dependency rather than providing one, so there is nothing to ${asRoot ? "build" : "add"}. Pass the real plugin that implements this id.`
152
+ );
97
153
  }
98
- if (schema instanceof z.ZodDefault) {
99
- return isPositional(schema._zod.def.innerType);
154
+ if (!asRoot) return;
155
+ if (pluginType === "hook") {
156
+ throw new Error(
157
+ `createSdk: "${id}" is a defineHook. A hook contributes wraps and observers and surfaces nothing, so it cannot be a root. Export it from a definePlugin and build that instead.`
158
+ );
100
159
  }
101
- return false;
102
- }
103
- function getNegatable(schema) {
104
- const negatable = schema.meta?.()?.negatable;
105
- if (negatable === true) return true;
106
- if (typeof negatable === "string" && negatable.length > 0) return negatable;
107
- if (schema instanceof z.ZodOptional || schema instanceof z.ZodDefault) {
108
- return getNegatable(schema._zod.def.innerType);
160
+ if (pluginType === "method-override") {
161
+ throw new Error(
162
+ `createSdk: "${id}" is an override. It patches a method that another plugin defines, so it cannot be a root. Put it in a definePlugin's \`imports\` alongside the plugin it patches.`
163
+ );
109
164
  }
110
- return void 0;
111
- }
112
- function openEnum(values, description) {
113
- return z.union([z.enum(values), z.string()]).describe(description);
114
165
  }
115
166
 
116
- // src/utils/stability.ts
117
- var STABILITY_LEVELS = ["stable", "beta", "experimental"];
118
- var STABILITY_TITLES = {
119
- stable: "Stable",
120
- beta: "Beta",
121
- experimental: "Experimental"
122
- };
123
- function normalizeStability(meta) {
124
- if (meta.stability !== void 0) {
125
- return STABILITY_LEVELS.includes(meta.stability) ? meta.stability : "experimental";
126
- }
127
- return meta.experimental ? "experimental" : "stable";
128
- }
129
- function applyStabilityLabel({
130
- description,
131
- stability,
132
- placement = "suffix"
167
+ // src/model/define.ts
168
+ function normalizeImports({
169
+ imports: deps,
170
+ owner
133
171
  }) {
134
- if (stability === void 0 || stability === "stable") return description;
135
- return placement === "prefix" ? `[${STABILITY_TITLES[stability]}] ${description}` : `${description} (${stability})`;
136
- }
137
-
138
- // src/registry.ts
139
- function resolveCategoryDefinition(ref) {
140
- const def = typeof ref === "string" ? { key: ref } : ref;
141
- const title = def.title ?? toTitleCase(def.key);
142
- return {
143
- key: def.key,
144
- title,
145
- titlePlural: def.titlePlural ?? pluralizeLastWord(title)
172
+ if (!deps) return { plugins: [], bindings: [] };
173
+ const seen = /* @__PURE__ */ new Map();
174
+ const bindings = [];
175
+ const add = (binding, id, optional) => {
176
+ const priorId = seen.get(binding);
177
+ if (priorId !== void 0 && priorId !== id) {
178
+ throw new Error(
179
+ `Import binding "${binding}" is declared twice. Two different plugins ("${priorId}" and "${id}") bind the same name; wrap one in selectExports to rename it.`
180
+ );
181
+ }
182
+ if (priorId === void 0) {
183
+ seen.set(binding, id);
184
+ bindings.push(optional ? { binding, id, optional } : { binding, id });
185
+ }
146
186
  };
147
- }
148
- function buildRegistry({
149
- sdk,
150
- meta,
151
- formatters,
152
- resolvers,
153
- positional,
154
- skipInputValidation,
155
- packageFilter
156
- }) {
157
- const definitionsByKey = /* @__PURE__ */ new Map();
158
- const objectDeclaredKeys = /* @__PURE__ */ new Set();
159
- for (const m of Object.values(meta)) {
160
- for (const ref of m.categories ?? []) {
161
- const key = typeof ref === "string" ? ref : ref.key;
162
- if (typeof ref === "object") {
163
- objectDeclaredKeys.add(key);
164
- definitionsByKey.set(key, resolveCategoryDefinition(ref));
165
- } else if (!objectDeclaredKeys.has(key)) {
166
- definitionsByKey.set(key, resolveCategoryDefinition(ref));
187
+ deps.forEach((plugin, index) => {
188
+ assertDescriptorShape(plugin, {
189
+ where: `${owner}, imports[${index}]`,
190
+ arrayFix: "Spread it into the list"
191
+ });
192
+ });
193
+ for (const plugin of deps) {
194
+ if (plugin.pluginType === "aggregate") {
195
+ for (const [binding, child] of Object.entries(plugin.exports)) {
196
+ add(binding, child.id);
167
197
  }
198
+ } else if (plugin.pluginType === "hook" || plugin.pluginType === "method-override") {
199
+ } else {
200
+ add(plugin.name, plugin.id, plugin.optional);
168
201
  }
169
202
  }
170
- if (!definitionsByKey.has("other")) {
171
- definitionsByKey.set("other", resolveCategoryDefinition("other"));
172
- }
173
- const knownCategories = Array.from(definitionsByKey.keys());
174
- const functions = Object.keys(meta).filter((key) => {
175
- const property = sdk[key];
176
- if (typeof property === "function") return true;
177
- const [rootKey] = key.split(".");
178
- const rootProperty = sdk[rootKey];
179
- return typeof rootProperty === "object" && rootProperty !== null;
180
- }).map((key) => {
181
- const m = meta[key];
182
- const stability = normalizeStability(m);
183
- return {
184
- name: key,
185
- description: m.description,
186
- type: m.type,
187
- itemType: m.itemType,
188
- returnType: m.returnType,
189
- inputSchema: canonicalInputSchema(m.inputSchema),
190
- outputSchema: m.outputSchema,
191
- positional: positional?.[key],
192
- skipInputValidation: skipInputValidation?.[key],
193
- categories: (m.categories ?? []).map(
194
- (c) => typeof c === "string" ? c : c.key
195
- ),
196
- resolvers: resolvers?.[key],
197
- formatter: formatters?.[key],
198
- stability,
199
- // Deprecated derived read, literal by name: only the experimental
200
- // tier reads true. Beta reads false — the "not stable" warning duty
201
- // lives in `stability` and the runtime notice, not this boolean.
202
- experimental: stability === "experimental",
203
- packages: m.packages,
204
- confirm: m.confirm ?? (m.type === "delete" ? "delete" : void 0),
205
- deprecation: m.deprecation,
206
- aliases: m.aliases,
207
- supportsJsonOutput: m.supportsJsonOutput ?? true
208
- };
209
- }).sort((a, b) => a.name.localeCompare(b.name));
210
- const filteredFunctions = packageFilter ? functions.filter((f) => !f.packages || f.packages.includes(packageFilter)) : functions;
211
- const filteredCategories = knownCategories.slice().sort((a, b) => {
212
- if (a === "other") return 1;
213
- if (b === "other") return -1;
214
- return definitionsByKey.get(a).title.localeCompare(definitionsByKey.get(b).title);
215
- }).map((categoryKey) => {
216
- const categoryFunctions = filteredFunctions.filter(
217
- (f) => f.categories.includes(categoryKey) || categoryKey === "other" && !f.categories.some((c) => knownCategories.includes(c))
218
- ).map((f) => f.name).sort();
219
- const def = definitionsByKey.get(categoryKey);
220
- return {
221
- key: categoryKey,
222
- title: def.title,
223
- titlePlural: def.titlePlural,
224
- functions: categoryFunctions
225
- };
226
- }).filter((category) => category.functions.length > 0);
227
- return { functions: filteredFunctions, categories: filteredCategories };
203
+ return { plugins: deps, bindings };
228
204
  }
229
-
230
- // src/utils/build-hooks.ts
231
- var isolated = /* @__PURE__ */ new WeakSet();
232
- function isolate(observer) {
233
- if (!observer) return void 0;
234
- if (isolated.has(observer)) return observer;
235
- const wrapped = (ctx) => {
236
- try {
237
- observer(ctx);
238
- } catch (error) {
239
- console.error(
240
- "[core] A method-lifecycle observer threw and was ignored. Observers are fire-and-forget and must not throw.",
241
- error
205
+ function formatDynamicMemberName(path) {
206
+ return path.map((seg) => typeof seg === "string" ? seg : `{${seg.param}}`).join(".");
207
+ }
208
+ function collectDynamicMembers(members) {
209
+ if (!members?.length) return void 0;
210
+ return members.map((member) => {
211
+ const root = member.path[0];
212
+ if (typeof root !== "string") {
213
+ throw new Error(
214
+ "defineProperty: a dynamicMember path must start with a literal segment (the owning binding), not a { param }."
242
215
  );
243
216
  }
244
- };
245
- isolated.add(wrapped);
246
- return wrapped;
247
- }
248
- function composeVoid(existing, added) {
249
- const wrappedExisting = isolate(existing);
250
- const wrappedAdded = isolate(added);
251
- if (!wrappedExisting) return wrappedAdded;
252
- if (!wrappedAdded) return wrappedExisting;
253
- const composed = (ctx) => {
254
- wrappedExisting(ctx);
255
- wrappedAdded(ctx);
256
- };
257
- isolated.add(composed);
258
- return composed;
259
- }
260
- function composeAnnotators(existing, added) {
261
- if (!existing) return added;
262
- if (!added) return existing;
263
- return (ctx) => ({ ...existing(ctx), ...added(ctx) });
264
- }
265
- function buildHooks(existing, added) {
266
- const result = {};
267
- const start2 = composeVoid(existing.onMethodStart, added.onMethodStart);
268
- if (start2) result.onMethodStart = start2;
269
- const end = composeVoid(existing.onMethodEnd, added.onMethodEnd);
270
- if (end) result.onMethodEnd = end;
271
- const annotator = composeAnnotators(existing.annotator, added.annotator);
272
- if (annotator) result.annotator = annotator;
273
- return result;
217
+ const { path, ...fields } = member;
218
+ return {
219
+ ...fields,
220
+ name: formatDynamicMemberName(path),
221
+ rootBinding: root
222
+ };
223
+ });
274
224
  }
275
-
276
- // src/utils/logging.ts
277
- function createDeprecationLogger(tag) {
278
- const loggedDeprecations = /* @__PURE__ */ new Set();
279
- return {
280
- logDeprecation(message) {
281
- if (loggedDeprecations.has(message)) return;
282
- loggedDeprecations.add(message);
283
- console.warn(`[${tag}] Deprecation: ${message}`);
284
- },
285
- resetDeprecationWarnings() {
286
- loggedDeprecations.clear();
287
- }
225
+ function defineMethod(configOrRef, refConfig) {
226
+ const config = refConfig === void 0 ? configOrRef : {
227
+ ...refConfig,
228
+ name: configOrRef.name,
229
+ namespace: configOrRef.namespace
288
230
  };
289
- }
290
- var { logDeprecation, resetDeprecationWarnings } = createDeprecationLogger("core");
291
- function createStabilityNoticeLogger(tag) {
292
- const loggedNotices = /* @__PURE__ */ new Set();
231
+ const deps = normalizeImports({
232
+ imports: config.imports,
233
+ owner: `defineMethod "${config.name}"`
234
+ });
293
235
  return {
294
- logStabilityNotice(message) {
295
- if (loggedNotices.has(message)) return;
296
- loggedNotices.add(message);
297
- console.warn(`[${tag}] ${message}`);
298
- },
299
- resetStabilityNotices() {
300
- loggedNotices.clear();
301
- }
236
+ ...config,
237
+ pluginType: "method",
238
+ id: makeId(config.name, config.namespace),
239
+ imports: deps.plugins,
240
+ importBindings: deps.bindings
302
241
  };
303
242
  }
304
- var { logStabilityNotice, resetStabilityNotices } = createStabilityNoticeLogger("core");
305
-
306
- // src/types/errors.ts
307
- var CORE_ERROR_SYMBOL = Symbol.for("kitcore.error");
308
- var CoreErrorCode = {
309
- Validation: "VALIDATION_ERROR",
310
- Unknown: "UNKNOWN_ERROR"
311
- };
312
- var CoreError = class extends Error {
313
- constructor(message, options = {}) {
314
- super(message);
315
- this.name = "CoreError";
316
- if (options.statusCode !== void 0) this.statusCode = options.statusCode;
317
- if (options.errors !== void 0) this.errors = options.errors;
318
- if (options.cause !== void 0) this.cause = options.cause;
319
- if (options.response !== void 0) this.response = options.response;
320
- Object.setPrototypeOf(this, new.target.prototype);
321
- }
322
- };
323
- function createCoreError(options, adaptError) {
324
- const error = adaptError?.(options) ?? new CoreError(options.message, { cause: options.cause });
325
- Object.defineProperty(error, CORE_ERROR_SYMBOL, {
326
- value: true,
327
- enumerable: false,
328
- configurable: true,
329
- writable: false
330
- });
331
- Object.defineProperty(error, "coreCode", {
332
- value: options.code,
333
- enumerable: false,
334
- configurable: true,
335
- writable: false
336
- });
337
- return error;
338
- }
339
- function isCoreError(value) {
340
- return Boolean(
341
- value && typeof value === "object" && value[CORE_ERROR_SYMBOL] === true
243
+ function assertOverridable(target, fields) {
244
+ const offered = Object.keys(fields).filter(
245
+ (key) => !OVERRIDABLE_META_KEYS.includes(key)
246
+ );
247
+ if (offered.length === 0) return;
248
+ throw new Error(
249
+ `defineOverride("${target}"): cannot override ${offered.join(", ")}. An override changes how a surface presents a method, never what it does. The method's declared type is fixed at \`defineMethod\` and nothing re-checks it afterwards, so patching behavior here would let a call fail against a contract its own return type says it satisfies. Overridable: ${OVERRIDABLE_META_KEYS.join(", ")}.`
342
250
  );
343
251
  }
344
- function getCoreErrorCode(value) {
345
- if (!isCoreError(value)) return void 0;
346
- return value.coreCode;
347
- }
348
- function getCoreErrorCause(value) {
349
- if (!isCoreError(value)) return void 0;
350
- return value.cause;
252
+ function buildOverride(target, namespace, fields) {
253
+ assertOverridable(target, fields);
254
+ return {
255
+ pluginType: "method-override",
256
+ name: `override:${target}`,
257
+ id: namespace ? `${namespace}/override:${target}` : `override:${target}`,
258
+ target,
259
+ imports: [],
260
+ importBindings: [],
261
+ patch: pickDefined(fields, OVERRIDABLE_META_KEYS)
262
+ };
351
263
  }
352
-
353
- // src/utils/pagination-utils.ts
354
- var CURSOR_VERSION = 1;
355
- var CURSOR_SOURCE = {
356
- API: "api",
357
- SDK: "sdk",
358
- CONCAT: "concat"
359
- };
360
- function encodeBase64(str) {
361
- return btoa(
362
- Array.from(
363
- new TextEncoder().encode(str),
364
- (b) => String.fromCharCode(b)
365
- ).join("")
366
- );
264
+ function defineOverride(ref, config = {}) {
265
+ const { namespace, ...fields } = config;
266
+ return buildOverride(ref.id, namespace, fields);
367
267
  }
368
- function decodeBase64(str) {
369
- return new TextDecoder().decode(
370
- Uint8Array.from(atob(str), (c) => c.charCodeAt(0))
268
+ function defineMethodOverride(config) {
269
+ logDeprecation(
270
+ "defineMethodOverride({ target }) is deprecated. Use defineOverride(method, { ... }), which takes the method or its declareMethod stand-in."
371
271
  );
272
+ const { target, namespace, ...fields } = config;
273
+ return buildOverride(target, namespace, fields);
372
274
  }
373
- function encodeApiCursor(cursor) {
374
- const envelope = {
375
- v: CURSOR_VERSION,
376
- source: CURSOR_SOURCE.API,
377
- cursor
378
- };
379
- return encodeBase64(JSON.stringify(envelope));
275
+ function assertRequirementPaths(requirements) {
276
+ if (!requirements) return;
277
+ for (const requirement of requirements) {
278
+ if (typeof requirement !== "string" && requirement.length === 0) {
279
+ throw new Error(
280
+ "defineResolver: a requireParameters path must name at least one segment. An empty path names no parameter, and the engine would read it as already satisfied."
281
+ );
282
+ }
283
+ }
380
284
  }
381
- function encodeSdkCursor(offset, cursor) {
382
- const envelope = {
383
- v: CURSOR_VERSION,
384
- source: CURSOR_SOURCE.SDK,
385
- cursor,
386
- offset
285
+ function defineResolver(config) {
286
+ const deps = normalizeImports({
287
+ imports: config.imports,
288
+ owner: "defineResolver"
289
+ });
290
+ const base = { imports: deps.plugins, importBindings: deps.bindings };
291
+ assertRequirementPaths(config.requireParameters);
292
+ const gates = {
293
+ requireParameters: config.requireParameters
387
294
  };
388
- return encodeBase64(JSON.stringify(envelope));
389
- }
390
- function decodeIncomingCursor(incoming) {
391
- if (!incoming) {
392
- return { offset: 0, cursor: void 0 };
393
- }
394
- try {
395
- const decoded = decodeBase64(incoming);
396
- const envelope = JSON.parse(decoded);
397
- if (envelope.v !== CURSOR_VERSION) {
398
- return { offset: 0, cursor: incoming };
399
- }
400
- if (envelope.source === CURSOR_SOURCE.SDK) {
401
- return { offset: envelope.offset ?? 0, cursor: envelope.cursor };
402
- }
403
- if (envelope.source === CURSOR_SOURCE.API) {
404
- return { offset: 0, cursor: envelope.cursor };
405
- }
406
- return { offset: 0, cursor: incoming };
407
- } catch {
408
- return { offset: 0, cursor: incoming };
295
+ switch (config.type) {
296
+ case "static":
297
+ return {
298
+ ...base,
299
+ ...gates,
300
+ type: "static",
301
+ inputType: config.inputType,
302
+ placeholder: config.placeholder
303
+ };
304
+ case "constant":
305
+ return { ...base, ...gates, type: "constant", value: config.value };
306
+ case "info":
307
+ return { ...base, type: "info", text: config.text ?? "" };
308
+ case "object":
309
+ return {
310
+ ...base,
311
+ ...gates,
312
+ type: "object",
313
+ properties: config.properties,
314
+ definitions: config.definitions,
315
+ getProperties: config.getProperties,
316
+ additionalKeys: config.additionalKeys
317
+ };
318
+ case "array":
319
+ return {
320
+ ...base,
321
+ ...gates,
322
+ type: "array",
323
+ items: config.items,
324
+ minItems: config.minItems,
325
+ maxItems: config.maxItems,
326
+ itemValueType: config.itemValueType,
327
+ definitions: config.definitions
328
+ };
329
+ default:
330
+ return {
331
+ ...base,
332
+ ...gates,
333
+ type: "dynamic",
334
+ inputType: config.inputType,
335
+ placeholder: config.placeholder,
336
+ getContext: config.getContext,
337
+ listItems: config.listItems,
338
+ prompt: config.prompt,
339
+ validate: config.validate,
340
+ tryResolveWithoutPrompt: config.tryResolveWithoutPrompt,
341
+ tryResolveFromSearch: config.tryResolveFromSearch
342
+ };
409
343
  }
410
344
  }
411
- function createPrefixedCursor(prefix, cursor) {
412
- if (!cursor) {
413
- return `${prefix}::`;
414
- }
415
- return `${prefix}::${cursor}`;
345
+ function defineFormatter(config) {
346
+ const deps = normalizeImports({
347
+ imports: config.imports,
348
+ owner: "defineFormatter"
349
+ });
350
+ return {
351
+ imports: deps.plugins,
352
+ importBindings: deps.bindings,
353
+ getContext: config.getContext,
354
+ format: config.format
355
+ };
416
356
  }
417
- function splitPrefixedCursor(cursor, prefixes) {
418
- if (!cursor) {
419
- return [void 0, void 0];
420
- }
421
- const [prefix, ...rest] = cursor.split("::");
422
- if (prefixes && !prefixes.includes(prefix)) {
423
- return [void 0, cursor];
424
- }
425
- cursor = rest.join("::");
426
- if (!cursor) {
427
- return [prefix, void 0];
428
- }
429
- return [prefix, cursor];
357
+ function declareMethod(config) {
358
+ const { name, namespace } = parseId(config.id);
359
+ const id = makeId(name, namespace);
360
+ return {
361
+ pluginType: "method",
362
+ name,
363
+ namespace,
364
+ id,
365
+ standIn: true,
366
+ imports: [],
367
+ importBindings: [],
368
+ run: () => {
369
+ throw new Error(
370
+ `Plugin "${id}" is a stand-in (declareMethod) with no implementation. Register the real plugin under this id.`
371
+ );
372
+ }
373
+ };
430
374
  }
431
- async function* paginateMaxItemsWithUnencodedCursor(pageFunction, pageOptions) {
432
- let cursor = pageOptions?.cursor;
433
- let totalItemsYielded = 0;
434
- const maxItems = pageOptions?.maxItems;
435
- const pageSize = pageOptions?.pageSize;
436
- do {
437
- const options = {
438
- ...pageOptions || {},
439
- cursor,
440
- pageSize: maxItems !== void 0 && pageSize !== void 0 ? Math.min(pageSize, maxItems) : pageSize
441
- };
442
- const page = await pageFunction(options);
443
- if (maxItems !== void 0) {
444
- const remainingItems = maxItems - totalItemsYielded;
445
- if (page.data.length >= remainingItems) {
446
- yield {
447
- ...page,
448
- data: page.data.slice(0, remainingItems),
449
- nextCursor: void 0
450
- };
451
- break;
452
- }
375
+ function declareOptionalMethod(config) {
376
+ const { name, namespace } = parseId(config.id);
377
+ const id = makeId(name, namespace);
378
+ return {
379
+ pluginType: "method",
380
+ name,
381
+ namespace,
382
+ id,
383
+ standIn: true,
384
+ optional: true,
385
+ imports: [],
386
+ importBindings: [],
387
+ run: () => {
388
+ throw new Error(
389
+ `Plugin "${id}" is an optional stand-in (declareOptionalMethod) with no implementation. Its binding is \`undefined\` unless a real plugin is registered under this id.`
390
+ );
453
391
  }
454
- yield page;
455
- totalItemsYielded += page.data.length;
456
- cursor = page.nextCursor;
457
- } while (cursor);
392
+ // Requires nothing: a consumer that imports it still passes `createSdk`'s
393
+ // completeness check unprovided. The contract is still carried, so a
394
+ // provider that DOES appear under the id is checked against it. The
395
+ // `optional: true` literal drives `PluginSurface` to type the binding
396
+ // `| undefined`.
397
+ };
458
398
  }
459
- async function* paginateMaxItems(pageFunction, pageOptions) {
460
- const { cursor } = decodeIncomingCursor(pageOptions?.cursor);
461
- const options = {
462
- ...pageOptions || {},
463
- cursor
399
+ function defineProperty(config, refConfig) {
400
+ const cfg = refConfig === void 0 ? config : {
401
+ ...refConfig,
402
+ name: config.name,
403
+ namespace: config.namespace
404
+ };
405
+ const deps = normalizeImports({
406
+ imports: cfg.imports,
407
+ owner: `defineProperty "${cfg.name}"`
408
+ });
409
+ return {
410
+ ...cfg,
411
+ pluginType: "property",
412
+ id: makeId(cfg.name, cfg.namespace),
413
+ imports: deps.plugins,
414
+ importBindings: deps.bindings,
415
+ dynamicMembers: collectDynamicMembers(cfg.dynamicMembers)
464
416
  };
465
- for await (const page of paginateMaxItemsWithUnencodedCursor(
466
- pageFunction,
467
- options
468
- )) {
469
- yield {
470
- ...page,
471
- nextCursor: page.nextCursor ? encodeApiCursor(page.nextCursor) : void 0
472
- };
473
- }
474
417
  }
475
- async function* paginateBuffered(pageFunction, pageOptions) {
476
- const pageSize = pageOptions?.pageSize;
477
- const { offset: cursorOffset, cursor: initialCursor } = decodeIncomingCursor(
478
- pageOptions?.cursor
479
- );
480
- const requestedMaxItems = pageOptions?.maxItems;
481
- const options = {
482
- ...pageOptions || {},
483
- cursor: initialCursor,
484
- // SDK cursors can carry an offset into a raw backend page. Since maxItems
485
- // is expected to be relative to the resumed position, we add that offset
486
- // so raw pagination still yields enough items after offset slicing.
487
- maxItems: requestedMaxItems !== void 0 && cursorOffset > 0 ? requestedMaxItems + cursorOffset : requestedMaxItems
418
+ function declareProperty(config) {
419
+ const { name, namespace } = parseId(config.id);
420
+ return {
421
+ pluginType: "property",
422
+ name,
423
+ namespace,
424
+ id: makeId(name, namespace),
425
+ standIn: true,
426
+ imports: [],
427
+ importBindings: []
488
428
  };
489
- if (!pageSize) {
490
- for await (const page of paginateMaxItemsWithUnencodedCursor(
491
- pageFunction,
492
- options
493
- )) {
494
- yield {
495
- ...page,
496
- nextCursor: page.nextCursor ? encodeApiCursor(page.nextCursor) : void 0
497
- };
498
- }
499
- return;
500
- }
501
- let bufferedPages = [];
502
- let isFirstPage = true;
503
- let rawCursor;
504
- for await (let page of paginateMaxItemsWithUnencodedCursor(
505
- pageFunction,
506
- options
507
- )) {
508
- const nextRawCursor = page.nextCursor;
509
- if (isFirstPage) {
510
- isFirstPage = false;
511
- if (cursorOffset) {
512
- page = {
513
- ...page,
514
- data: page.data.slice(cursorOffset)
515
- };
516
- }
517
- }
518
- const bufferedLength = bufferedPages.reduce(
519
- (acc, p) => acc + p.data.length,
520
- 0
521
- );
522
- if (bufferedLength + page.data.length < pageSize) {
523
- bufferedPages.push(page);
524
- rawCursor = nextRawCursor;
525
- continue;
526
- }
527
- const bufferedItems = bufferedPages.map((p) => p.data).flat();
528
- const allItems = [...bufferedItems, ...page.data];
529
- const pageItems = allItems.slice(0, pageSize);
530
- const remainingItems = allItems.slice(pageItems.length);
531
- if (remainingItems.length === 0) {
532
- yield {
533
- ...page,
534
- data: pageItems,
535
- nextCursor: nextRawCursor ? encodeApiCursor(nextRawCursor) : void 0
536
- };
537
- bufferedPages = [];
538
- rawCursor = nextRawCursor;
539
- continue;
540
- }
541
- yield {
542
- ...page,
543
- data: pageItems,
544
- nextCursor: encodeSdkCursor(
545
- page.data.length - remainingItems.length,
546
- rawCursor
547
- )
548
- };
549
- while (remainingItems.length > pageSize) {
550
- const chunkItems = remainingItems.splice(0, pageSize);
551
- yield {
552
- ...page,
553
- data: chunkItems,
554
- nextCursor: encodeSdkCursor(
555
- page.data.length - remainingItems.length,
556
- rawCursor
557
- )
558
- };
559
- }
560
- bufferedPages = [
561
- {
562
- ...page,
563
- data: remainingItems
564
- }
565
- ];
566
- rawCursor = nextRawCursor;
567
- }
568
- if (bufferedPages.length > 0) {
569
- const lastBufferedPage = bufferedPages.slice(-1)[0];
570
- const bufferedItems = bufferedPages.map((p) => p.data).flat();
571
- yield {
572
- ...lastBufferedPage,
573
- data: bufferedItems
574
- };
575
- }
576
429
  }
577
- var paginate = paginateBuffered;
578
- function encodeConcatCursor(index, cursor) {
579
- const envelope = {
580
- v: CURSOR_VERSION,
581
- source: CURSOR_SOURCE.CONCAT,
582
- index,
583
- cursor
430
+ function declareOptionalProperty(config) {
431
+ const { name, namespace } = parseId(config.id);
432
+ return {
433
+ pluginType: "property",
434
+ name,
435
+ namespace,
436
+ id: makeId(name, namespace),
437
+ standIn: true,
438
+ optional: true,
439
+ imports: [],
440
+ importBindings: []
441
+ // Requires nothing: a consumer that imports it still passes `createSdk`'s
442
+ // completeness check unprovided. The contract is the binding a consumer
443
+ // sees, `TValue | undefined`, which is also what a by-reference provider
444
+ // (`defineProperty(ref, { value })`) is allowed to pass. A consumer of an
445
+ // optional reference has to handle the absent case either way, so an
446
+ // explicit `undefined` breaks nothing a narrower contract would protect.
584
447
  };
585
- return encodeBase64(JSON.stringify(envelope));
586
448
  }
587
- function decodeConcatCursor(incoming) {
588
- if (!incoming) {
589
- return { index: 0, cursor: void 0 };
449
+ function declareDefault({
450
+ plugin
451
+ }) {
452
+ return { ...plugin, defaultSource: plugin };
453
+ }
454
+ function defineHook(config) {
455
+ const deps = normalizeImports({
456
+ imports: config.imports,
457
+ owner: `defineHook "${config.name}"`
458
+ });
459
+ return {
460
+ pluginType: "hook",
461
+ name: config.name,
462
+ namespace: config.namespace,
463
+ id: makeId(config.name, config.namespace),
464
+ imports: deps.plugins,
465
+ importBindings: deps.bindings,
466
+ setup: config.setup,
467
+ dispose: config.dispose,
468
+ wrap: config.wrap,
469
+ observe: config.observe,
470
+ annotator: config.annotator
471
+ };
472
+ }
473
+ function declarePlugin(config) {
474
+ const { name, namespace } = parseId(config.id);
475
+ return {
476
+ pluginType: "aggregate",
477
+ name,
478
+ namespace,
479
+ id: makeId(name, namespace, "aggregate"),
480
+ standIn: true,
481
+ imports: [],
482
+ importBindings: [],
483
+ exports: normalizeExports(config.exports)
484
+ };
485
+ }
486
+ function definePlugin(config) {
487
+ const owner = `definePlugin "${config.name}"`;
488
+ const deps = normalizeImports({ imports: config.imports, owner });
489
+ config.exports?.forEach((element, index) => {
490
+ assertDescriptorShape(element, {
491
+ where: `${owner}, exports[${index}]`,
492
+ arrayFix: "Spread it into the list"
493
+ });
494
+ });
495
+ return {
496
+ pluginType: "aggregate",
497
+ name: config.name,
498
+ namespace: config.namespace,
499
+ id: makeId(config.name, config.namespace, "aggregate"),
500
+ // A re-export synthetic (`selectExports` / `omitExports`) is flattened by
501
+ // `normalizeExports` into bare bindings, which drops its own `imports:
502
+ // [source]`. That edge is how `omitExports` keeps an omitted (unbound) leaf
503
+ // materialized + addressable by id, so preserve every exported aggregate's
504
+ // imports as extra reachability edges here (bindings unaffected).
505
+ imports: [...deps.plugins, ...exportedAggregateImports(config.exports)],
506
+ importBindings: deps.bindings,
507
+ exports: normalizeExports(config.exports)
508
+ };
509
+ }
510
+ function exportedAggregateImports(exports) {
511
+ if (!exports) return [];
512
+ const out = [];
513
+ for (const element of exports) {
514
+ if (element.pluginType === "aggregate") out.push(...element.imports);
590
515
  }
591
- try {
592
- const envelope = JSON.parse(decodeBase64(incoming));
593
- if (envelope.v === CURSOR_VERSION && envelope.source === CURSOR_SOURCE.CONCAT && typeof envelope.index === "number") {
594
- return { index: envelope.index, cursor: envelope.cursor };
516
+ return out;
517
+ }
518
+ function normalizeExports(exports) {
519
+ const out = /* @__PURE__ */ Object.create(null);
520
+ if (!exports) return out;
521
+ const add = (binding, leaf) => {
522
+ const existing = out[binding];
523
+ if (existing && existing.id !== leaf.id) {
524
+ throw new Error(
525
+ `definePlugin: duplicate export binding "${binding}". Two different plugins ("${existing.id}" and "${leaf.id}") bind the same name; wrap one in selectExports to rename it.`
526
+ );
527
+ }
528
+ out[binding] = leaf;
529
+ };
530
+ for (const element of exports) {
531
+ if (element.pluginType === "aggregate") {
532
+ for (const [binding, child] of Object.entries(element.exports)) {
533
+ add(binding, child);
534
+ }
535
+ } else {
536
+ add(element.name, element);
595
537
  }
596
- } catch {
597
538
  }
598
- return { index: 0, cursor: incoming };
539
+ return out;
599
540
  }
600
- async function concatLists({
601
- sources,
602
- pageSize = 100,
603
- cursor
604
- }) {
605
- if (sources.length === 0) {
606
- return { data: [] };
607
- }
608
- const pageFunction = async (options) => {
609
- let { index, cursor: listCursor } = decodeConcatCursor(options.cursor);
610
- while (index < sources.length) {
611
- const page = await sources[index]({ cursor: listCursor });
612
- const hasMoreInList = page.nextCursor != null;
613
- if (page.data.length === 0 && !hasMoreInList) {
614
- index++;
615
- listCursor = void 0;
616
- continue;
541
+
542
+ // src/model/exports.ts
543
+ var selectSeq = 0;
544
+ function selectExports(source, ...specs) {
545
+ const selected = {};
546
+ const pick = (binding, fromName) => {
547
+ const child = source.exports[fromName];
548
+ if (!child) {
549
+ throw new Error(
550
+ `selectExports: "${source.id}" has no export "${fromName}".`
551
+ );
552
+ }
553
+ selected[binding] = child;
554
+ };
555
+ for (const spec of specs) {
556
+ if (typeof spec === "string") {
557
+ pick(spec, spec);
558
+ } else {
559
+ for (const [newName, fromName] of Object.entries(spec)) {
560
+ pick(newName, fromName);
617
561
  }
618
- return {
619
- data: page.data,
620
- nextCursor: hasMoreInList ? encodeConcatCursor(index, page.nextCursor) : index < sources.length - 1 ? encodeConcatCursor(index + 1, void 0) : void 0
621
- };
622
562
  }
623
- return { data: [] };
563
+ }
564
+ const id = `${source.id}#select:${selectSeq++}`;
565
+ return {
566
+ pluginType: "aggregate",
567
+ name: makeId(`select`, source.name, "aggregate"),
568
+ id,
569
+ // Depend on the source so it is materialized; the selected bindings resolve
570
+ // to the source's own leaves (kept identity).
571
+ imports: [source],
572
+ importBindings: [],
573
+ exports: selected
574
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
624
575
  };
625
- const result = await paginateBuffered(pageFunction, {
626
- pageSize,
627
- cursor
628
- }).next();
629
- return result.done ? { data: [] } : result.value;
630
- }
631
- function concatPaginated({
632
- sources,
633
- pageSize,
634
- cursor
635
- }) {
636
- logDeprecation("concatPaginated() is deprecated. Use concatLists() instead.");
637
- return concatLists({ sources, pageSize, cursor });
638
576
  }
639
- function toIterable(source) {
640
- logDeprecation(
641
- "toIterable() is deprecated. Call .pages() on the paginated result instead."
642
- );
643
- return { [Symbol.asyncIterator]: () => source[Symbol.asyncIterator]() };
577
+ function omitExports(source, omit) {
578
+ const omitSet = new Set(omit);
579
+ for (const name of omit) {
580
+ if (!(name in source.exports)) {
581
+ throw new Error(`omitExports: "${source.id}" has no export "${name}".`);
582
+ }
583
+ }
584
+ const kept = {};
585
+ for (const [binding, child] of Object.entries(source.exports)) {
586
+ if (!omitSet.has(binding)) kept[binding] = child;
587
+ }
588
+ return {
589
+ pluginType: "aggregate",
590
+ name: makeId(`omit`, source.name, "aggregate"),
591
+ id: `${source.id}#omit:${selectSeq++}`,
592
+ imports: [source],
593
+ importBindings: [],
594
+ exports: kept
595
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
596
+ };
644
597
  }
645
598
 
646
- // src/utils/promise-utils.ts
647
- function isPromiseLike(value) {
648
- return value !== null && typeof value === "object" && typeof value.then === "function";
599
+ // src/model/builtins.ts
600
+ import { z as z2 } from "zod";
601
+
602
+ // src/utils/string-utils.ts
603
+ function toTitleCase(input) {
604
+ return input.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_\-]+/g, " ").replace(/\s+/g, " ").trim().split(" ").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
605
+ }
606
+ function toSnakeCase(input) {
607
+ let result = input.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[\s\-]+/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "").toLowerCase();
608
+ if (/^[0-9]/.test(result)) {
609
+ result = "_" + result;
610
+ }
611
+ return result;
612
+ }
613
+ function pluralize(word) {
614
+ if (/s$/i.test(word)) return word;
615
+ if (/[bcdfghjklmnpqrstvwxz]y$/i.test(word)) {
616
+ return word.slice(0, -1) + "ies";
617
+ }
618
+ return word + "s";
619
+ }
620
+ function pluralizeLastWord(title) {
621
+ const words = title.split(" ");
622
+ return [...words.slice(0, -1), pluralize(words[words.length - 1])].join(" ");
649
623
  }
650
624
 
651
- // src/utils/validation.ts
652
- var parseOrThrow = (schema, input, { adaptError } = {}) => {
653
- const result = schema.safeParse(input);
654
- if (!result.success) {
655
- const errorMessages = result.error.issues.map((issue) => {
656
- const path = issue.path.length > 0 ? issue.path.join(".") : "input";
657
- return `${path}: ${issue.message}`;
658
- });
659
- throw createCoreError(
660
- {
661
- code: CoreErrorCode.Validation,
662
- message: `Validation failed:
663
- ${errorMessages.join("\n ")}`,
664
- details: {
665
- zodErrors: result.error.issues,
666
- input
667
- }
668
- },
669
- adaptError
670
- );
625
+ // src/utils/schema-utils.ts
626
+ import { z } from "zod";
627
+ function canonicalInputSchema(schema) {
628
+ if (schema instanceof z.ZodUnion) {
629
+ return schema.options[0];
671
630
  }
672
- return result.data;
673
- };
674
- function createValidator(schema, { adaptError } = {}) {
675
- return function validateFn(input) {
676
- return parseOrThrow(schema, input, { adaptError });
677
- };
678
- }
679
- var validateOptions = (schema, options, { adaptError } = {}) => parseOrThrow(schema, options, { adaptError });
680
-
681
- // src/utils/call-options.ts
682
- import { z as z2 } from "zod";
683
- var CallFrameworkOptionsSchema = z2.object({
684
- /** Page to fetch. Opaque to kitcore: the head's API defines the format. */
685
- cursor: z2.string().optional(),
686
- /** Items per page. */
687
- pageSize: z2.number().int().min(1).optional(),
688
- /** Stop after this many items, across pages. */
689
- maxItems: z2.number().int().min(0).optional(),
690
- /** Bypass output validation for this one call. */
691
- skipOutputDataValidation: z2.boolean().optional()
692
- });
693
- var ITEM_FRAMEWORK_OPTIONS = {
694
- claims: ["skipOutputDataValidation"],
695
- injects: []
696
- };
697
- var LIST_FRAMEWORK_OPTIONS = {
698
- claims: ["cursor", "pageSize", "maxItems", "skipOutputDataValidation"],
699
- injects: ["cursor", "pageSize"]
700
- };
701
- var PAGE_FRAMEWORK_OPTIONS = {
702
- claims: ["cursor", "pageSize", "maxItems"],
703
- injects: ["cursor", "pageSize", "maxItems"]
704
- };
705
- var NO_FRAMEWORK_OPTIONS = {
706
- claims: [],
707
- injects: []
708
- };
709
- function isRecord(value) {
710
- return typeof value === "object" && value !== null && !Array.isArray(value);
631
+ return schema;
711
632
  }
712
- function strictlyRefused(error, claims) {
713
- const refused = /* @__PURE__ */ new Set();
714
- for (const issue of error.issues) {
715
- if (issue.code !== "unrecognized_keys" || issue.path.length > 0) continue;
716
- for (const key of issue.keys) {
717
- if (claims.includes(key)) refused.add(key);
633
+ function unwrapSchema(schema) {
634
+ let inner = schema;
635
+ let required = true;
636
+ for (; ; ) {
637
+ if (inner instanceof z.ZodOptional || inner instanceof z.ZodDefault) {
638
+ required = false;
639
+ inner = inner.unwrap();
640
+ } else if (inner instanceof z.ZodNullable) {
641
+ inner = inner.unwrap();
642
+ } else {
643
+ break;
718
644
  }
719
645
  }
720
- return [...refused];
646
+ return { inner, required };
721
647
  }
722
- function withoutKeys(options, keys) {
723
- const next = {};
724
- for (const [key, value] of Object.entries(options)) {
725
- if (!keys.includes(key)) next[key] = value;
648
+ function objectShapeOf(schema) {
649
+ const canonical = canonicalInputSchema(schema);
650
+ if (!canonical) return void 0;
651
+ const { inner } = unwrapSchema(canonical);
652
+ if (inner instanceof z.ZodObject) {
653
+ return inner.shape;
726
654
  }
727
- return next;
655
+ return void 0;
728
656
  }
729
- function parseCallOptions(options, {
730
- schema,
731
- policy = NO_FRAMEWORK_OPTIONS,
732
- adaptError
733
- } = {}) {
734
- const claims = policy.claims;
735
- const call = isRecord(options) ? options : void 0;
736
- let framework = {};
737
- if (call && claims.length > 0) {
738
- const present = {};
739
- for (const key of claims) {
740
- if (key in call) present[key] = call[key];
741
- }
742
- framework = parseOrThrow2(CallFrameworkOptionsSchema, present, adaptError);
743
- }
744
- if (!schema) return { framework, domain: options, supplied: /* @__PURE__ */ new Set() };
745
- const first = schema.safeParse(options);
746
- if (first.success) {
747
- return { framework, domain: first.data, supplied: /* @__PURE__ */ new Set() };
748
- }
749
- const refused = call ? strictlyRefused(first.error, claims) : [];
750
- if (refused.length === 0) throw toCoreError(first.error, options, adaptError);
751
- const retry = schema.safeParse(withoutKeys(call, refused));
752
- if (!retry.success) {
753
- throw toCoreError(retry.error, options, adaptError);
754
- }
755
- return { framework, domain: retry.data, supplied: new Set(refused) };
657
+ function getOutputSchema(inputSchema) {
658
+ return inputSchema._zod.def.outputSchema;
756
659
  }
757
- function mergeCallOptions({
758
- framework,
759
- domain
760
- }) {
761
- const claimed = Object.entries(framework);
762
- if (!isRecord(domain) || claimed.length === 0) return domain;
763
- return { ...domain, ...Object.fromEntries(claimed) };
660
+ function withOutputSchema(inputSchema, outputSchema) {
661
+ Object.assign(inputSchema._zod.def, {
662
+ outputSchema
663
+ });
664
+ return inputSchema;
764
665
  }
765
- function withheldFromRun(policy) {
766
- return new Set(policy.claims.filter((key) => !policy.injects.includes(key)));
666
+ function withResolver(schema, config) {
667
+ schema._zod.def.resolverMeta = config;
668
+ return schema;
767
669
  }
768
- function stripFrameworkOnlyOptions(options, withheld) {
769
- if (withheld.size === 0 || !isRecord(options)) return options;
770
- const entries = Object.entries(options);
771
- if (!entries.some(([key]) => withheld.has(key))) return options;
772
- return Object.fromEntries(entries.filter(([key]) => !withheld.has(key)));
670
+ function getSchemaDescription(schema) {
671
+ return schema.description;
773
672
  }
774
- function parseOrThrow2(schema, input, adaptError) {
775
- const result = schema.safeParse(input);
776
- if (result.success) return result.data;
777
- throw toCoreError(result.error, input, adaptError);
673
+ function getFieldDescriptions(schema) {
674
+ const descriptions = {};
675
+ const shape = schema.shape;
676
+ for (const [key, fieldSchema] of Object.entries(shape)) {
677
+ if (fieldSchema instanceof z.ZodType && fieldSchema.description) {
678
+ descriptions[key] = fieldSchema.description;
679
+ }
680
+ }
681
+ return descriptions;
778
682
  }
779
- function toCoreError(error, input, adaptError) {
780
- const messages = error.issues.map((issue) => {
781
- const path = issue.path.length > 0 ? issue.path.join(".") : "input";
782
- return `${path}: ${issue.message}`;
683
+ function withPositional(schema) {
684
+ Object.assign(schema._zod.def, {
685
+ positionalMeta: { positional: true }
783
686
  });
784
- return createCoreError(
785
- {
786
- code: CoreErrorCode.Validation,
787
- message: `Validation failed:
788
- ${messages.join("\n ")}`,
789
- details: { zodErrors: error.issues, input }
790
- },
791
- adaptError
792
- );
687
+ return schema;
793
688
  }
794
-
795
- // src/utils/async-context.ts
796
- import {
797
- AsyncLocalStorage
798
- } from "async_hooks";
799
- function createAsyncContext() {
800
- let store = null;
801
- try {
802
- store = new AsyncLocalStorage();
803
- } catch {
804
- store = null;
805
- }
806
- return {
807
- available: store !== null,
808
- run(value, fn) {
809
- return store ? store.run(value, fn) : fn();
810
- },
811
- get() {
812
- return store?.getStore();
813
- }
814
- };
689
+ function schemaHasPositionalMeta(schema) {
690
+ return "positionalMeta" in schema._zod.def;
815
691
  }
816
-
817
- // src/utils/method-scope.ts
818
- var scope = createAsyncContext();
819
- function getCurrentScope() {
820
- return scope.get();
692
+ function isPositional(schema) {
693
+ if (schemaHasPositionalMeta(schema) && schema._zod.def.positionalMeta?.positional) {
694
+ return true;
695
+ }
696
+ if (schema instanceof z.ZodOptional) {
697
+ return isPositional(schema._zod.def.innerType);
698
+ }
699
+ if (schema instanceof z.ZodDefault) {
700
+ return isPositional(schema._zod.def.innerType);
701
+ }
702
+ return false;
821
703
  }
822
- function getCurrentDepth() {
823
- return getCurrentScope()?.depth ?? 0;
704
+ function getNegatable(schema) {
705
+ const negatable = schema.meta?.()?.negatable;
706
+ if (negatable === true) return true;
707
+ if (typeof negatable === "string" && negatable.length > 0) return negatable;
708
+ if (schema instanceof z.ZodOptional || schema instanceof z.ZodDefault) {
709
+ return getNegatable(schema._zod.def.innerType);
710
+ }
711
+ return void 0;
824
712
  }
825
- function isNestedMethodCall() {
826
- if (!scope.available) return true;
827
- const store = scope.get();
828
- return store !== void 0 && store.depth > 0;
713
+ function openEnum(values, description) {
714
+ return z.union([z.enum(values), z.string()]).describe(description);
829
715
  }
830
- var observerReentrancy = 0;
831
- function runIsolatedObserver(fn) {
832
- observerReentrancy++;
833
- try {
834
- fn();
835
- } catch {
836
- } finally {
837
- observerReentrancy--;
716
+
717
+ // src/utils/stability.ts
718
+ var STABILITY_LEVELS = ["stable", "beta", "experimental"];
719
+ var STABILITY_TITLES = {
720
+ stable: "Stable",
721
+ beta: "Beta",
722
+ experimental: "Experimental"
723
+ };
724
+ function normalizeStability(meta) {
725
+ if (meta.stability !== void 0) {
726
+ return STABILITY_LEVELS.includes(meta.stability) ? meta.stability : "experimental";
838
727
  }
728
+ return meta.experimental ? "experimental" : "stable";
839
729
  }
840
- function isInsideObserver() {
841
- return observerReentrancy > 0;
730
+ function applyStabilityLabel({
731
+ description,
732
+ stability,
733
+ placement = "suffix"
734
+ }) {
735
+ if (stability === void 0 || stability === "stable") return description;
736
+ return placement === "prefix" ? `[${STABILITY_TITLES[stability]}] ${description}` : `${description} (${stability})`;
842
737
  }
843
- function runInMethodScope(fn) {
844
- if (!scope.available) return fn();
845
- const currentDepth = scope.get()?.depth ?? -1;
846
- return scope.run({ depth: currentDepth + 1 }, fn);
738
+
739
+ // src/registry.ts
740
+ function resolveCategoryDefinition(ref) {
741
+ const def = typeof ref === "string" ? { key: ref } : ref;
742
+ const title = def.title ?? toTitleCase(def.key);
743
+ return {
744
+ key: def.key,
745
+ title,
746
+ titlePlural: def.titlePlural ?? pluralizeLastWord(title)
747
+ };
748
+ }
749
+ function buildRegistry({
750
+ sdk,
751
+ sources,
752
+ packageFilter
753
+ }) {
754
+ const definitionsByKey = /* @__PURE__ */ new Map();
755
+ const objectDeclaredKeys = /* @__PURE__ */ new Set();
756
+ for (const m of Object.values(sources)) {
757
+ for (const ref of m.categories ?? []) {
758
+ const key = typeof ref === "string" ? ref : ref.key;
759
+ if (typeof ref === "object") {
760
+ objectDeclaredKeys.add(key);
761
+ definitionsByKey.set(key, resolveCategoryDefinition(ref));
762
+ } else if (!objectDeclaredKeys.has(key)) {
763
+ definitionsByKey.set(key, resolveCategoryDefinition(ref));
764
+ }
765
+ }
766
+ }
767
+ if (!definitionsByKey.has("other")) {
768
+ definitionsByKey.set("other", resolveCategoryDefinition("other"));
769
+ }
770
+ const knownCategories = Array.from(definitionsByKey.keys());
771
+ const functions = Object.keys(sources).filter((key) => {
772
+ const property = sdk[key];
773
+ if (typeof property === "function") return true;
774
+ const [rootKey] = key.split(".");
775
+ const rootProperty = sdk[rootKey];
776
+ return typeof rootProperty === "object" && rootProperty !== null;
777
+ }).map((key) => {
778
+ const m = sources[key];
779
+ const stability = normalizeStability(m);
780
+ return {
781
+ name: key,
782
+ description: m.description,
783
+ type: m.type,
784
+ itemType: m.itemType,
785
+ returnType: m.returnType,
786
+ inputSchema: canonicalInputSchema(m.inputSchema),
787
+ outputSchema: m.outputSchema,
788
+ positional: m.positional,
789
+ skipInputValidation: m.skipInputValidation,
790
+ categories: (m.categories ?? []).map(
791
+ (c) => typeof c === "string" ? c : c.key
792
+ ),
793
+ resolvers: m.resolvers,
794
+ formatter: m.formatter,
795
+ stability,
796
+ // Deprecated derived read, literal by name: only the experimental
797
+ // tier reads true. Beta reads false — the "not stable" warning duty
798
+ // lives in `stability` and the runtime notice, not this boolean.
799
+ experimental: stability === "experimental",
800
+ packages: m.packages,
801
+ confirm: m.confirm ?? (m.type === "delete" ? "delete" : void 0),
802
+ deprecation: m.deprecation,
803
+ aliases: m.aliases,
804
+ supportsJsonOutput: m.supportsJsonOutput ?? true
805
+ };
806
+ }).sort((a, b) => a.name.localeCompare(b.name));
807
+ const filteredFunctions = packageFilter ? functions.filter((f) => !f.packages || f.packages.includes(packageFilter)) : functions;
808
+ const filteredCategories = knownCategories.slice().sort((a, b) => {
809
+ if (a === "other") return 1;
810
+ if (b === "other") return -1;
811
+ return definitionsByKey.get(a).title.localeCompare(definitionsByKey.get(b).title);
812
+ }).map((categoryKey) => {
813
+ const categoryFunctions = filteredFunctions.filter(
814
+ (f) => f.categories.includes(categoryKey) || categoryKey === "other" && !f.categories.some((c) => knownCategories.includes(c))
815
+ ).map((f) => f.name).sort();
816
+ const def = definitionsByKey.get(categoryKey);
817
+ return {
818
+ key: categoryKey,
819
+ title: def.title,
820
+ titlePlural: def.titlePlural,
821
+ functions: categoryFunctions
822
+ };
823
+ }).filter((category) => category.functions.length > 0);
824
+ return { functions: filteredFunctions, categories: filteredCategories };
847
825
  }
848
- var runWithTelemetryContext = runInMethodScope;
849
- var isTelemetryNested = isNestedMethodCall;
850
826
 
851
- // src/utils/call-context.ts
852
- var CALL_CONTEXT_BRAND = Symbol("kitcore.callContext");
853
- function isCallContext(value) {
854
- return typeof value === "object" && value !== null && value[CALL_CONTEXT_BRAND] === true;
827
+ // src/model/registry-support.ts
828
+ function methodMetaOf(entry) {
829
+ const meta = pickDefined(entry, METHOD_META_KEYS);
830
+ return Object.keys(meta).length > 0 ? meta : void 0;
855
831
  }
856
- function generateCallId() {
857
- try {
858
- const webCrypto = globalThis.crypto;
859
- if (webCrypto?.randomUUID) {
860
- return webCrypto.randomUUID();
832
+ function propertyMetaOf(entry) {
833
+ const meta = pickDefined(entry, PROPERTY_META_KEYS);
834
+ return Object.keys(meta).length > 0 ? meta : void 0;
835
+ }
836
+ function isDescribed(entry) {
837
+ const meta = entry.pluginType === "method" ? methodMetaOf(entry) : propertyMetaOf(entry);
838
+ return meta !== void 0;
839
+ }
840
+ function foldDynamicMembers(entry, surfaceBindings, sources) {
841
+ if (entry.pluginType !== "property" || !entry.dynamicMembers) return;
842
+ for (const member of entry.dynamicMembers) {
843
+ if (!surfaceBindings.has(member.rootBinding)) {
844
+ throw new Error(
845
+ `dynamicMember "${member.name}": its root "${member.rootBinding}" is not a surfaced member. A dynamic member's path must start with a real binding.`
846
+ );
861
847
  }
862
- if (webCrypto?.getRandomValues) {
863
- const bytes = webCrypto.getRandomValues(new Uint8Array(16));
864
- const hex = Array.from(bytes, (byte, i) => {
865
- const value = i === 6 ? byte & 15 | 64 : i === 8 ? byte & 63 | 128 : byte;
866
- return value.toString(16).padStart(2, "0");
867
- });
868
- return [
869
- hex.slice(0, 4).join(""),
870
- hex.slice(4, 6).join(""),
871
- hex.slice(6, 8).join(""),
872
- hex.slice(8, 10).join(""),
873
- hex.slice(10, 16).join("")
874
- ].join("-");
848
+ sources[member.name] = member;
849
+ }
850
+ }
851
+ function collectRegistrySources(context) {
852
+ const sources = /* @__PURE__ */ Object.create(null);
853
+ const entries = [];
854
+ for (const [binding, id] of Object.entries(context.surface)) {
855
+ const entry = context.plugins[id];
856
+ if (entry?.pluginType !== "method" && entry?.pluginType !== "property") {
857
+ continue;
875
858
  }
876
- } catch {
859
+ entries.push(entry);
860
+ if (isDescribed(entry)) sources[binding] = entry;
877
861
  }
878
- return null;
862
+ const surfaceBindings = new Set(Object.keys(context.surface));
863
+ for (const entry of entries) {
864
+ foldDynamicMembers(entry, surfaceBindings, sources);
865
+ }
866
+ return sources;
879
867
  }
880
- function rootCallContext({
881
- callOrigin = "surface"
882
- } = {}) {
883
- return {
884
- callId: generateCallId(),
885
- depth: 0,
886
- annotations: {},
887
- callOrigin,
888
- [CALL_CONTEXT_BRAND]: true
889
- };
868
+ var REGISTRY_CACHE = Symbol.for("kitcore.registryCache");
869
+ function freezeContainers(registry) {
870
+ Object.freeze(registry.functions);
871
+ for (const category of registry.categories) {
872
+ Object.freeze(category.functions);
873
+ Object.freeze(category);
874
+ }
875
+ Object.freeze(registry.categories);
876
+ return Object.freeze(registry);
890
877
  }
891
- function childCallContext(parent) {
892
- return {
893
- callId: parent.callId,
894
- depth: parent.depth + 1,
895
- annotations: {},
896
- callOrigin: parent.callOrigin,
897
- [CALL_CONTEXT_BRAND]: true
898
- };
878
+ function getCachedRegistry(context, packageFilter) {
879
+ const key = packageFilter ?? "";
880
+ const caching = context;
881
+ let byFilter = caching[REGISTRY_CACHE];
882
+ if (!byFilter) {
883
+ byFilter = /* @__PURE__ */ new Map();
884
+ caching[REGISTRY_CACHE] = byFilter;
885
+ }
886
+ let registry = byFilter.get(key);
887
+ if (!registry) {
888
+ registry = freezeContainers(buildSurfaceRegistry(context, packageFilter));
889
+ byFilter.set(key, registry);
890
+ }
891
+ return registry;
892
+ }
893
+ function invalidateRegistryCache(context) {
894
+ delete context[REGISTRY_CACHE];
895
+ }
896
+ function buildSurfaceRegistry(context, packageFilter) {
897
+ const surface = {};
898
+ for (const [binding, id] of Object.entries(context.surface)) {
899
+ const entry = context.plugins[id];
900
+ if (entry?.pluginType !== "method" && entry?.pluginType !== "property") {
901
+ continue;
902
+ }
903
+ surface[binding] = entry.pluginType === "property" && entry.getValue ? entry.getValue() : entry.value;
904
+ }
905
+ return buildRegistry({
906
+ sdk: surface,
907
+ sources: collectRegistrySources(context),
908
+ packageFilter
909
+ });
899
910
  }
900
911
 
901
912
  // src/utils/core-options.ts
@@ -920,1376 +931,1088 @@ function defaultLogStabilityNotice({
920
931
  }
921
932
  var CORE_OPTIONS_ID = "kitcore/coreOptions";
922
933
 
923
- // src/utils/function-utils.ts
924
- function resolveCoreOptions(context) {
925
- const entry = context.plugins?.[CORE_OPTIONS_ID];
926
- if (entry) {
927
- return entry.getValue ? entry.getValue() : entry.value;
928
- }
929
- return context.core;
930
- }
931
- var INTERNAL_CALL = Symbol("kitcore.internalCall");
932
- function resolveCallContext(secondArg) {
933
- return isCallContext(secondArg) ? secondArg : rootCallContext();
934
- }
935
- var hookAnnotatorReentrancy = 0;
936
- function applyAnnotations({
937
- context,
938
- methodName,
939
- input,
940
- hookAnnotator,
941
- methodAnnotator
942
- }) {
943
- if (hookAnnotator && !isInsideObserver() && context.depth === 0 && context.callOrigin !== "internal" && hookAnnotatorReentrancy === 0) {
944
- hookAnnotatorReentrancy++;
934
+ // src/model/builtins.ts
935
+ var coreOptionsPluginRef = declareOptionalProperty({ id: CORE_OPTIONS_ID });
936
+ var dangerousContextPlugin = {
937
+ pluginType: "property",
938
+ name: "context",
939
+ namespace: "kitcore",
940
+ id: "kitcore/context",
941
+ imports: [],
942
+ importBindings: [],
943
+ privileged: true
944
+ };
945
+ var getRegistryPlugin = defineMethod({
946
+ name: "getRegistry",
947
+ namespace: "kitcore",
948
+ imports: [dangerousContextPlugin],
949
+ inputSchema: z2.object({ package: z2.string().optional() }).optional(),
950
+ run: ({ imports, input }) => getCachedRegistry(imports.context, input?.package)
951
+ });
952
+
953
+ // src/utils/build-hooks.ts
954
+ var isolated = /* @__PURE__ */ new WeakSet();
955
+ function isolate(observer) {
956
+ if (!observer) return void 0;
957
+ if (isolated.has(observer)) return observer;
958
+ const wrapped = (ctx) => {
945
959
  try {
946
- Object.assign(context.annotations, hookAnnotator({ methodName, input }));
947
- } catch {
948
- } finally {
949
- hookAnnotatorReentrancy--;
960
+ observer(ctx);
961
+ } catch (error) {
962
+ console.error(
963
+ "[core] A method-lifecycle observer threw and was ignored. Observers are fire-and-forget and must not throw.",
964
+ error
965
+ );
950
966
  }
951
- }
952
- try {
953
- Object.assign(context.annotations, methodAnnotator?.(input));
954
- } catch {
955
- }
956
- }
957
- function signalDeprecation(context, methodName, getDeprecation) {
958
- if (isInsideObserver()) return;
959
- const deprecation = getDeprecation?.();
960
- if (!deprecation?.message) return;
961
- const warning = {
962
- type: "deprecation",
963
- methodName,
964
- deprecation
965
967
  };
966
- const handler = resolveCoreOptions(context)?.logDeprecation ?? defaultLogDeprecation;
967
- runIsolatedObserver(() => handler(warning));
968
+ isolated.add(wrapped);
969
+ return wrapped;
968
970
  }
969
- function signalStability(context, methodName, getStability) {
970
- if (isInsideObserver()) return;
971
- const stability = getStability?.();
972
- if (!stability || stability === "stable") return;
973
- const notice = {
974
- type: "stability",
975
- methodName,
976
- stability
971
+ function composeVoid(existing, added) {
972
+ const wrappedExisting = isolate(existing);
973
+ const wrappedAdded = isolate(added);
974
+ if (!wrappedExisting) return wrappedAdded;
975
+ if (!wrappedAdded) return wrappedExisting;
976
+ const composed = (ctx) => {
977
+ wrappedExisting(ctx);
978
+ wrappedAdded(ctx);
977
979
  };
978
- const handler = resolveCoreOptions(context)?.logStabilityNotice ?? defaultLogStabilityNotice;
979
- runIsolatedObserver(() => handler(notice));
980
+ isolated.add(composed);
981
+ return composed;
980
982
  }
981
- function normalizeError(error, adaptError) {
982
- if (error instanceof Error) return error;
983
- const message = typeof error === "object" && error !== null && "message" in error && typeof error.message === "string" ? error.message : String(error);
984
- return createCoreError(
985
- {
986
- code: CoreErrorCode.Unknown,
987
- message,
988
- cause: error
989
- },
990
- adaptError
991
- );
983
+ function composeAnnotators(existing, added) {
984
+ if (!existing) return added;
985
+ if (!added) return existing;
986
+ return (ctx) => ({ ...existing(ctx), ...added(ctx) });
992
987
  }
993
- function createFunction(coreFn, options) {
994
- const {
995
- sdk,
996
- schema,
997
- name,
998
- annotator,
999
- frameworkOptions,
1000
- getDeprecation,
1001
- getStability
1002
- } = options;
1003
- const functionName = name || coreFn.name;
1004
- const namedFunctions = {
1005
- [functionName]: async function(callOptions) {
1006
- const internal = arguments[1];
1007
- const context = resolveCallContext(internal);
1008
- if (!isCallContext(internal) && internal !== INTERNAL_CALL) {
1009
- signalDeprecation(sdk.context, functionName, getDeprecation);
1010
- signalStability(sdk.context, functionName, getStability);
1011
- }
1012
- return runInMethodScope(async () => {
1013
- const startTime = Date.now();
1014
- const normalizedOptions = callOptions ?? {};
1015
- const args = [normalizedOptions];
1016
- const depth = Math.max(context.depth, getCurrentDepth());
1017
- const insideObserver = isInsideObserver();
1018
- const hooks = insideObserver ? void 0 : sdk.context.hooks;
1019
- const adaptError = resolveCoreOptions(sdk.context)?.adaptError;
1020
- applyAnnotations({
1021
- context,
1022
- methodName: functionName,
1023
- input: normalizedOptions,
1024
- hookAnnotator: hooks?.annotator,
1025
- methodAnnotator: annotator
1026
- });
1027
- const hookBase = {
1028
- methodName: functionName,
1029
- args,
1030
- isPaginated: false,
1031
- depth,
1032
- callId: context.callId,
1033
- callOrigin: context.callOrigin,
1034
- annotations: context.annotations
1035
- };
1036
- hooks?.onMethodStart?.({ ...hookBase });
1037
- try {
1038
- const parsed = parseCallOptions(normalizedOptions, {
1039
- schema,
1040
- policy: frameworkOptions,
1041
- adaptError
1042
- });
1043
- const result = await coreFn(
1044
- mergeCallOptions(parsed),
1045
- context
1046
- );
1047
- hooks?.onMethodEnd?.({
1048
- ...hookBase,
1049
- durationMs: Date.now() - startTime
1050
- });
1051
- return result;
1052
- } catch (error) {
1053
- const normalizedError = normalizeError(error, adaptError);
1054
- hooks?.onMethodEnd?.({
1055
- ...hookBase,
1056
- durationMs: Date.now() - startTime,
1057
- error: normalizedError
1058
- });
1059
- throw normalizedError;
1060
- }
1061
- });
1062
- }
1063
- };
1064
- return namedFunctions[functionName];
988
+ function buildHooks(existing, added) {
989
+ const result = {};
990
+ const start2 = composeVoid(existing.onMethodStart, added.onMethodStart);
991
+ if (start2) result.onMethodStart = start2;
992
+ const end = composeVoid(existing.onMethodEnd, added.onMethodEnd);
993
+ if (end) result.onMethodEnd = end;
994
+ const annotator = composeAnnotators(existing.annotator, added.annotator);
995
+ if (annotator) result.annotator = annotator;
996
+ return result;
1065
997
  }
1066
- function createRawFunction(coreFn, options) {
1067
- const {
1068
- sdk,
1069
- name,
1070
- schema,
1071
- positional,
1072
- annotator,
1073
- getDeprecation,
1074
- getStability
1075
- } = options;
1076
- return function(rawInput) {
1077
- const internal = arguments[1];
1078
- const context = resolveCallContext(internal);
1079
- if (!isCallContext(internal) && internal !== INTERNAL_CALL) {
1080
- signalDeprecation(sdk.context, name, getDeprecation);
1081
- signalStability(sdk.context, name, getStability);
1082
- }
1083
- return runInMethodScope(() => {
1084
- const startTime = Date.now();
1085
- const depth = Math.max(context.depth, getCurrentDepth());
1086
- const insideObserver = isInsideObserver();
1087
- const hooks = insideObserver ? void 0 : sdk.context.hooks;
1088
- const adaptError = resolveCoreOptions(sdk.context)?.adaptError;
1089
- const input = schema ? rawInput ?? {} : rawInput;
1090
- applyAnnotations({
1091
- context,
1092
- methodName: name,
1093
- input,
1094
- hookAnnotator: hooks?.annotator,
1095
- methodAnnotator: annotator
1096
- });
1097
- const record = input;
1098
- const args = positional ? positional.filter((key) => record?.[key] !== void 0).map((key) => record?.[key]) : [input];
1099
- const hookBase = {
1100
- methodName: name,
1101
- args,
1102
- isPaginated: false,
1103
- depth,
1104
- callId: context.callId,
1105
- callOrigin: context.callOrigin,
1106
- annotations: context.annotations
1107
- };
1108
- hooks?.onMethodStart?.({ ...hookBase });
1109
- const fireEnd = (error) => {
1110
- hooks?.onMethodEnd?.({
1111
- ...hookBase,
1112
- durationMs: Date.now() - startTime,
1113
- ...error ? { error } : {}
1114
- });
1115
- };
1116
- try {
1117
- const parsed = schema ? validateOptions(schema, input, { adaptError }) : input;
1118
- const result = coreFn(parsed, context);
1119
- if (isPromiseLike(result)) {
1120
- return result.then(
1121
- (value) => {
1122
- fireEnd();
1123
- return value;
1124
- },
1125
- (error) => {
1126
- fireEnd(
1127
- error instanceof Error ? error : new Error(String(error))
1128
- );
1129
- throw error;
1130
- }
1131
- );
1132
- }
1133
- fireEnd();
1134
- return result;
1135
- } catch (error) {
1136
- fireEnd(error instanceof Error ? error : new Error(String(error)));
1137
- throw error;
1138
- }
1139
- });
1140
- };
998
+
999
+ // src/model/root-keys.ts
1000
+ var RESERVED_ROOT_KEYS = /* @__PURE__ */ new Set(["context"]);
1001
+ function hasOwn(obj, key) {
1002
+ return Object.prototype.hasOwnProperty.call(obj, key);
1141
1003
  }
1142
- function isSdkPage(value) {
1143
- if (typeof value !== "object" || value === null) return false;
1144
- const page = value;
1145
- if (!Array.isArray(page.data)) return false;
1146
- if (page.nextCursor !== void 0 && typeof page.nextCursor !== "string") {
1147
- return false;
1004
+ function assertNotReservedKeys({
1005
+ keys,
1006
+ caller
1007
+ }) {
1008
+ for (const key of keys) {
1009
+ if (!RESERVED_ROOT_KEYS.has(key)) continue;
1010
+ throw new Error(
1011
+ `${caller}: plugin attempted to register reserved root key "${key}". The framework writes this key itself, so the plugin's own value would never be reachable. Rename it.`
1012
+ );
1148
1013
  }
1149
- return Object.keys(page).every((k) => k === "data" || k === "nextCursor");
1150
1014
  }
1151
- function createPageFunction(coreFn, {
1152
- sdk,
1153
- adaptPage,
1154
- finalizePage
1015
+ function checkRootKeyCollisions({
1016
+ target,
1017
+ keys,
1018
+ override,
1019
+ caller
1155
1020
  }) {
1156
- const functionName = coreFn.name + "Page";
1157
- const namedFunctions = {
1158
- [functionName]: async function(options, callContext) {
1159
- try {
1160
- const response = await coreFn(options, callContext);
1161
- const page = adaptPage ? adaptPage(response) : response;
1162
- if (!isSdkPage(page)) {
1163
- throw new Error(
1164
- `${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\`.`
1165
- );
1166
- }
1167
- return finalizePage ? finalizePage(page, options) : page;
1168
- } catch (error) {
1169
- throw normalizeError(
1170
- error,
1171
- resolveCoreOptions(sdk.context)?.adaptError
1172
- );
1173
- }
1021
+ assertNotReservedKeys({ keys, caller });
1022
+ for (const key of keys) {
1023
+ if (!override && hasOwn(target, key)) {
1024
+ throw new Error(
1025
+ `${caller}: duplicate root key "${key}". If the override is intentional, pass { override: true } in the options.`
1026
+ );
1174
1027
  }
1175
- };
1176
- return namedFunctions[functionName];
1028
+ }
1177
1029
  }
1178
- function createPaginatedFunction(coreFn, options) {
1179
- const {
1180
- sdk,
1181
- schema,
1182
- name,
1183
- defaultPageSize,
1184
- adaptPage,
1185
- annotator,
1186
- finalizePage,
1187
- frameworkOptions,
1188
- getDeprecation,
1189
- getStability
1190
- } = options;
1191
- const pageFunction = createPageFunction(coreFn, {
1192
- sdk,
1193
- adaptPage,
1194
- finalizePage
1030
+
1031
+ // src/types/errors.ts
1032
+ var CORE_ERROR_SYMBOL = Symbol.for("kitcore.error");
1033
+ var CoreErrorCode = {
1034
+ Validation: "VALIDATION_ERROR",
1035
+ Unknown: "UNKNOWN_ERROR",
1036
+ /**
1037
+ * The object handed to a framework reader is not an SDK `createSdk` built, so
1038
+ * there is no plugin graph to read. A code rather than prose because a caller
1039
+ * distinguishing "no registry here" from "the registry failed to build" has to
1040
+ * match on something stable, and the message is not that.
1041
+ */
1042
+ NoSdkContext: "NO_SDK_CONTEXT_ERROR"
1043
+ };
1044
+ var CoreError = class extends Error {
1045
+ constructor(message, options = {}) {
1046
+ super(message);
1047
+ this.name = "CoreError";
1048
+ if (options.statusCode !== void 0) this.statusCode = options.statusCode;
1049
+ if (options.errors !== void 0) this.errors = options.errors;
1050
+ if (options.cause !== void 0) this.cause = options.cause;
1051
+ if (options.response !== void 0) this.response = options.response;
1052
+ Object.setPrototypeOf(this, new.target.prototype);
1053
+ }
1054
+ };
1055
+ function createCoreError(options, adaptError) {
1056
+ const error = adaptError?.(options) ?? new CoreError(options.message, { cause: options.cause });
1057
+ Object.defineProperty(error, CORE_ERROR_SYMBOL, {
1058
+ value: true,
1059
+ enumerable: false,
1060
+ configurable: true,
1061
+ writable: false
1195
1062
  });
1196
- const functionName = name || coreFn.name;
1197
- const namedFunctions = {
1198
- [functionName]: function(callOptions) {
1199
- const internal = arguments[1];
1200
- const context = resolveCallContext(internal);
1201
- if (!isCallContext(internal) && internal !== INTERNAL_CALL) {
1202
- signalDeprecation(sdk.context, functionName, getDeprecation);
1203
- signalStability(sdk.context, functionName, getStability);
1204
- }
1205
- return runInMethodScope(() => {
1206
- const startTime = Date.now();
1207
- const normalizedOptions = callOptions ?? {};
1208
- const args = [normalizedOptions];
1209
- const depth = Math.max(context.depth, getCurrentDepth());
1210
- const insideObserver = isInsideObserver();
1211
- const hooks = insideObserver ? void 0 : sdk.context.hooks;
1212
- const adaptError = resolveCoreOptions(sdk.context)?.adaptError;
1213
- applyAnnotations({
1214
- context,
1215
- methodName: functionName,
1216
- input: normalizedOptions,
1217
- hookAnnotator: hooks?.annotator,
1218
- methodAnnotator: annotator
1219
- });
1220
- const hookBase = {
1221
- methodName: functionName,
1222
- args,
1223
- isPaginated: true,
1224
- depth,
1225
- callId: context.callId,
1226
- callOrigin: context.callOrigin,
1227
- annotations: context.annotations
1228
- };
1229
- hooks?.onMethodStart?.({ ...hookBase });
1230
- try {
1231
- const validatedOptions = mergeCallOptions(
1232
- parseCallOptions(normalizedOptions, {
1233
- schema,
1234
- policy: frameworkOptions,
1235
- adaptError
1236
- })
1237
- );
1238
- const pageSize = validatedOptions.pageSize ?? defaultPageSize;
1239
- const optimizedOptions = {
1240
- ...validatedOptions,
1241
- pageSize
1242
- };
1243
- const iterator = paginate(
1244
- (pageOptions) => pageFunction(pageOptions, context),
1245
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1246
- optimizedOptions
1247
- );
1248
- const firstPagePromise = iterator.next().then((result) => {
1249
- if (result.done) {
1250
- throw new Error("Paginate should always iterate at least once");
1251
- }
1252
- return result.value;
1253
- });
1254
- if (hooks?.onMethodEnd) {
1255
- firstPagePromise.then(
1256
- () => {
1257
- hooks.onMethodEnd({
1258
- ...hookBase,
1259
- durationMs: Date.now() - startTime
1260
- });
1261
- },
1262
- (error) => {
1263
- hooks.onMethodEnd({
1264
- ...hookBase,
1265
- durationMs: Date.now() - startTime,
1266
- error: error instanceof Error ? error : new Error(String(error))
1267
- });
1268
- }
1269
- );
1270
- }
1271
- const pageStream = async function* () {
1272
- yield await firstPagePromise;
1273
- for await (const page of iterator) {
1274
- yield page;
1275
- }
1276
- }();
1277
- return Object.assign(firstPagePromise, {
1278
- [Symbol.asyncIterator]() {
1279
- return pageStream;
1280
- },
1281
- pages: function() {
1282
- return {
1283
- [Symbol.asyncIterator]() {
1284
- return pageStream;
1285
- }
1286
- };
1287
- },
1288
- items: function() {
1289
- return {
1290
- [Symbol.asyncIterator]: async function* () {
1291
- for await (const page of pageStream) {
1292
- for (const item of page.data) {
1293
- yield item;
1294
- }
1295
- }
1296
- }
1297
- };
1298
- }
1299
- });
1300
- } catch (error) {
1301
- const normalizedError = normalizeError(error, adaptError);
1302
- hooks?.onMethodEnd?.({
1303
- ...hookBase,
1304
- durationMs: Date.now() - startTime,
1305
- error: normalizedError
1306
- });
1307
- throw normalizedError;
1308
- }
1309
- });
1310
- }
1311
- };
1312
- return namedFunctions[functionName];
1063
+ Object.defineProperty(error, "coreCode", {
1064
+ value: options.code,
1065
+ enumerable: false,
1066
+ configurable: true,
1067
+ writable: false
1068
+ });
1069
+ return error;
1070
+ }
1071
+ function isCoreError(value) {
1072
+ return Boolean(
1073
+ value && typeof value === "object" && value[CORE_ERROR_SYMBOL] === true
1074
+ );
1075
+ }
1076
+ function getCoreErrorCode(value) {
1077
+ if (!isCoreError(value)) return void 0;
1078
+ return value.coreCode;
1079
+ }
1080
+ function getCoreErrorCause(value) {
1081
+ if (!isCoreError(value)) return void 0;
1082
+ return value.cause;
1083
+ }
1084
+
1085
+ // src/utils/pagination-utils.ts
1086
+ var CURSOR_VERSION = 1;
1087
+ var CURSOR_SOURCE = {
1088
+ API: "api",
1089
+ SDK: "sdk",
1090
+ CONCAT: "concat"
1091
+ };
1092
+ function encodeBase64(str) {
1093
+ return btoa(
1094
+ Array.from(
1095
+ new TextEncoder().encode(str),
1096
+ (b) => String.fromCharCode(b)
1097
+ ).join("")
1098
+ );
1313
1099
  }
1314
-
1315
- // src/utils/plugin-utils.ts
1316
- function createPluginMethod(sdk, config) {
1317
- logDeprecation(
1318
- "createPluginMethod() is deprecated. Author methods with defineMethod instead."
1100
+ function decodeBase64(str) {
1101
+ return new TextDecoder().decode(
1102
+ Uint8Array.from(atob(str), (c) => c.charCodeAt(0))
1319
1103
  );
1320
- const { name, inputSchema, handler, ...metaFields } = config;
1321
- const namedHandlers = {
1322
- [name]: async function(options) {
1323
- return handler({ sdk, options });
1324
- }
1104
+ }
1105
+ function encodeApiCursor(cursor) {
1106
+ const envelope = {
1107
+ v: CURSOR_VERSION,
1108
+ source: CURSOR_SOURCE.API,
1109
+ cursor
1325
1110
  };
1326
- const wrappedFn = createFunction(namedHandlers[name], {
1327
- sdk,
1328
- schema: inputSchema
1329
- });
1330
- return {
1331
- [name]: wrappedFn,
1332
- context: {
1333
- meta: {
1334
- [name]: {
1335
- ...metaFields,
1336
- ...inputSchema ? { inputSchema } : {}
1337
- }
1338
- }
1339
- }
1111
+ return encodeBase64(JSON.stringify(envelope));
1112
+ }
1113
+ function encodeSdkCursor(offset, cursor) {
1114
+ const envelope = {
1115
+ v: CURSOR_VERSION,
1116
+ source: CURSOR_SOURCE.SDK,
1117
+ cursor,
1118
+ offset
1340
1119
  };
1120
+ return encodeBase64(JSON.stringify(envelope));
1341
1121
  }
1342
- function createPaginatedPluginMethod(sdk, config) {
1343
- logDeprecation(
1344
- 'createPaginatedPluginMethod() is deprecated. Author list methods with defineMethod output "list" instead.'
1345
- );
1346
- const {
1347
- name,
1348
- inputSchema,
1349
- handler,
1350
- adaptPage,
1351
- defaultPageSize,
1352
- ...metaFields
1353
- } = config;
1354
- const namedHandlers = {
1355
- [name]: function(options) {
1356
- return handler({ sdk, options });
1122
+ function decodeIncomingCursor(incoming) {
1123
+ if (!incoming) {
1124
+ return { offset: 0, cursor: void 0 };
1125
+ }
1126
+ try {
1127
+ const decoded = decodeBase64(incoming);
1128
+ const envelope = JSON.parse(decoded);
1129
+ if (envelope.v !== CURSOR_VERSION) {
1130
+ return { offset: 0, cursor: incoming };
1357
1131
  }
1358
- };
1359
- const wrappedFn = createPaginatedFunction(namedHandlers[name], {
1360
- sdk,
1361
- schema: inputSchema,
1362
- name,
1363
- // The page loop reads the page controls out of the call object, so a
1364
- // handler's schema does not have to declare them. It reads nothing else:
1365
- // no legacy handler honors the caller's output skip.
1366
- frameworkOptions: PAGE_FRAMEWORK_OPTIONS,
1367
- defaultPageSize,
1368
- adaptPage
1369
- });
1370
- return {
1371
- [name]: wrappedFn,
1372
- context: {
1373
- meta: {
1374
- [name]: {
1375
- ...metaFields,
1376
- ...inputSchema ? { inputSchema } : {}
1377
- }
1378
- }
1132
+ if (envelope.source === CURSOR_SOURCE.SDK) {
1133
+ return { offset: envelope.offset ?? 0, cursor: envelope.cursor };
1379
1134
  }
1380
- };
1135
+ if (envelope.source === CURSOR_SOURCE.API) {
1136
+ return { offset: 0, cursor: envelope.cursor };
1137
+ }
1138
+ return { offset: 0, cursor: incoming };
1139
+ } catch {
1140
+ return { offset: 0, cursor: incoming };
1141
+ }
1381
1142
  }
1382
- function splitPluginContribution(result) {
1383
- const { context, ...rootKeys } = result;
1384
- const { meta, hooks, ...contextRest } = context ?? {};
1385
- return {
1386
- rootKeys,
1387
- meta: meta ?? {},
1388
- hooks: hooks ?? {},
1389
- contextRest
1390
- };
1143
+ function createPrefixedCursor(prefix, cursor) {
1144
+ if (!cursor) {
1145
+ return `${prefix}::`;
1146
+ }
1147
+ return `${prefix}::${cursor}`;
1391
1148
  }
1392
- var RESERVED_ROOT_KEYS = /* @__PURE__ */ new Set([
1393
- "context",
1394
- "getRegistry"
1395
- ]);
1396
- function hasOwn(obj, key) {
1397
- return Object.prototype.hasOwnProperty.call(obj, key);
1149
+ function splitPrefixedCursor(cursor, prefixes) {
1150
+ if (!cursor) {
1151
+ return [void 0, void 0];
1152
+ }
1153
+ const [prefix, ...rest] = cursor.split("::");
1154
+ if (prefixes && !prefixes.includes(prefix)) {
1155
+ return [void 0, cursor];
1156
+ }
1157
+ cursor = rest.join("::");
1158
+ if (!cursor) {
1159
+ return [prefix, void 0];
1160
+ }
1161
+ return [prefix, cursor];
1398
1162
  }
1399
- function setOwn(target, key, value) {
1400
- Object.defineProperty(target, key, {
1401
- value,
1402
- enumerable: true,
1403
- configurable: true,
1404
- writable: true
1405
- });
1163
+ async function* paginateMaxItemsWithUnencodedCursor(pageFunction, pageOptions) {
1164
+ let cursor = pageOptions?.cursor;
1165
+ let totalItemsYielded = 0;
1166
+ const maxItems = pageOptions?.maxItems;
1167
+ const pageSize = pageOptions?.pageSize;
1168
+ do {
1169
+ const options = {
1170
+ ...pageOptions || {},
1171
+ cursor,
1172
+ pageSize: maxItems !== void 0 && pageSize !== void 0 ? Math.min(pageSize, maxItems) : pageSize
1173
+ };
1174
+ const page = await pageFunction(options);
1175
+ if (maxItems !== void 0) {
1176
+ const remainingItems = maxItems - totalItemsYielded;
1177
+ if (page.data.length >= remainingItems) {
1178
+ yield {
1179
+ ...page,
1180
+ data: page.data.slice(0, remainingItems),
1181
+ nextCursor: void 0
1182
+ };
1183
+ break;
1184
+ }
1185
+ }
1186
+ yield page;
1187
+ totalItemsYielded += page.data.length;
1188
+ cursor = page.nextCursor;
1189
+ } while (cursor);
1190
+ }
1191
+ async function* paginateMaxItems(pageFunction, pageOptions) {
1192
+ const { cursor } = decodeIncomingCursor(pageOptions?.cursor);
1193
+ const options = {
1194
+ ...pageOptions || {},
1195
+ cursor
1196
+ };
1197
+ for await (const page of paginateMaxItemsWithUnencodedCursor(
1198
+ pageFunction,
1199
+ options
1200
+ )) {
1201
+ yield {
1202
+ ...page,
1203
+ nextCursor: page.nextCursor ? encodeApiCursor(page.nextCursor) : void 0
1204
+ };
1205
+ }
1406
1206
  }
1407
- function checkCollisions(target, source, kind, callerLabel, override) {
1408
- if (kind === "root key") {
1409
- checkRootKeyCollisions(target, Object.keys(source), override, callerLabel);
1207
+ async function* paginateBuffered(pageFunction, pageOptions) {
1208
+ const pageSize = pageOptions?.pageSize;
1209
+ const { offset: cursorOffset, cursor: initialCursor } = decodeIncomingCursor(
1210
+ pageOptions?.cursor
1211
+ );
1212
+ const requestedMaxItems = pageOptions?.maxItems;
1213
+ const options = {
1214
+ ...pageOptions || {},
1215
+ cursor: initialCursor,
1216
+ // SDK cursors can carry an offset into a raw backend page. Since maxItems
1217
+ // is expected to be relative to the resumed position, we add that offset
1218
+ // so raw pagination still yields enough items after offset slicing.
1219
+ maxItems: requestedMaxItems !== void 0 && cursorOffset > 0 ? requestedMaxItems + cursorOffset : requestedMaxItems
1220
+ };
1221
+ if (!pageSize) {
1222
+ for await (const page of paginateMaxItemsWithUnencodedCursor(
1223
+ pageFunction,
1224
+ options
1225
+ )) {
1226
+ yield {
1227
+ ...page,
1228
+ nextCursor: page.nextCursor ? encodeApiCursor(page.nextCursor) : void 0
1229
+ };
1230
+ }
1410
1231
  return;
1411
1232
  }
1412
- for (const key of Object.keys(source)) {
1413
- if (!override && hasOwn(target, key)) {
1414
- throw new Error(
1415
- `${callerLabel}: duplicate ${kind} "${key}". If the override is intentional, pass { override: true } in the options.`
1416
- );
1233
+ let bufferedPages = [];
1234
+ let isFirstPage = true;
1235
+ let rawCursor;
1236
+ for await (let page of paginateMaxItemsWithUnencodedCursor(
1237
+ pageFunction,
1238
+ options
1239
+ )) {
1240
+ const nextRawCursor = page.nextCursor;
1241
+ if (isFirstPage) {
1242
+ isFirstPage = false;
1243
+ if (cursorOffset) {
1244
+ page = {
1245
+ ...page,
1246
+ data: page.data.slice(cursorOffset)
1247
+ };
1248
+ }
1249
+ }
1250
+ const bufferedLength = bufferedPages.reduce(
1251
+ (acc, p) => acc + p.data.length,
1252
+ 0
1253
+ );
1254
+ if (bufferedLength + page.data.length < pageSize) {
1255
+ bufferedPages.push(page);
1256
+ rawCursor = nextRawCursor;
1257
+ continue;
1258
+ }
1259
+ const bufferedItems = bufferedPages.map((p) => p.data).flat();
1260
+ const allItems = [...bufferedItems, ...page.data];
1261
+ const pageItems = allItems.slice(0, pageSize);
1262
+ const remainingItems = allItems.slice(pageItems.length);
1263
+ if (remainingItems.length === 0) {
1264
+ yield {
1265
+ ...page,
1266
+ data: pageItems,
1267
+ nextCursor: nextRawCursor ? encodeApiCursor(nextRawCursor) : void 0
1268
+ };
1269
+ bufferedPages = [];
1270
+ rawCursor = nextRawCursor;
1271
+ continue;
1272
+ }
1273
+ yield {
1274
+ ...page,
1275
+ data: pageItems,
1276
+ nextCursor: encodeSdkCursor(
1277
+ page.data.length - remainingItems.length,
1278
+ rawCursor
1279
+ )
1280
+ };
1281
+ while (remainingItems.length > pageSize) {
1282
+ const chunkItems = remainingItems.splice(0, pageSize);
1283
+ yield {
1284
+ ...page,
1285
+ data: chunkItems,
1286
+ nextCursor: encodeSdkCursor(
1287
+ page.data.length - remainingItems.length,
1288
+ rawCursor
1289
+ )
1290
+ };
1417
1291
  }
1292
+ bufferedPages = [
1293
+ {
1294
+ ...page,
1295
+ data: remainingItems
1296
+ }
1297
+ ];
1298
+ rawCursor = nextRawCursor;
1299
+ }
1300
+ if (bufferedPages.length > 0) {
1301
+ const lastBufferedPage = bufferedPages.slice(-1)[0];
1302
+ const bufferedItems = bufferedPages.map((p) => p.data).flat();
1303
+ yield {
1304
+ ...lastBufferedPage,
1305
+ data: bufferedItems
1306
+ };
1418
1307
  }
1419
1308
  }
1420
- function checkRootKeyCollisions(target, keys, override, callerLabel) {
1421
- for (const key of keys) {
1422
- if (RESERVED_ROOT_KEYS.has(key)) {
1423
- throw new Error(
1424
- `${callerLabel}: plugin attempted to register reserved root key "${key}". The SDK uses this key for its own accessor; rename the plugin's method.`
1425
- );
1426
- }
1427
- if (!override && hasOwn(target, key)) {
1428
- throw new Error(
1429
- `${callerLabel}: duplicate root key "${key}". If the override is intentional, pass { override: true } in the options.`
1430
- );
1309
+ var paginate = paginateBuffered;
1310
+ function encodeConcatCursor(index, cursor) {
1311
+ const envelope = {
1312
+ v: CURSOR_VERSION,
1313
+ source: CURSOR_SOURCE.CONCAT,
1314
+ index,
1315
+ cursor
1316
+ };
1317
+ return encodeBase64(JSON.stringify(envelope));
1318
+ }
1319
+ function decodeConcatCursor(incoming) {
1320
+ if (!incoming) {
1321
+ return { index: 0, cursor: void 0 };
1322
+ }
1323
+ try {
1324
+ const envelope = JSON.parse(decodeBase64(incoming));
1325
+ if (envelope.v === CURSOR_VERSION && envelope.source === CURSOR_SOURCE.CONCAT && typeof envelope.index === "number") {
1326
+ return { index: envelope.index, cursor: envelope.cursor };
1431
1327
  }
1328
+ } catch {
1432
1329
  }
1330
+ return { index: 0, cursor: incoming };
1433
1331
  }
1434
- function applyOwnProperties(target, source) {
1435
- for (const key of Object.keys(source)) {
1436
- setOwn(target, key, source[key]);
1332
+ async function concatLists({
1333
+ sources,
1334
+ pageSize = 100,
1335
+ cursor
1336
+ }) {
1337
+ if (sources.length === 0) {
1338
+ return { data: [] };
1437
1339
  }
1438
- }
1439
- function createPluginAccumulator(initialProperties = {}, initialContext = {}) {
1440
- const initialMeta = initialContext.meta ?? {};
1441
- const initialHooks = initialContext.hooks ?? {};
1442
- const context = {
1443
- ...initialContext,
1444
- meta: { ...initialMeta },
1445
- hooks: { ...initialHooks }
1446
- };
1447
- const view = { ...initialProperties, context };
1448
- return { view, context };
1449
- }
1450
- function mergeContribution(propertiesTarget, contextTarget, contribution, options) {
1451
- checkCollisions(
1452
- propertiesTarget,
1453
- contribution.rootKeys,
1454
- "root key",
1455
- options.callerLabel,
1456
- options.override
1457
- );
1458
- checkCollisions(
1459
- contextTarget.meta,
1460
- contribution.meta,
1461
- "context.meta key",
1462
- options.callerLabel,
1463
- options.override
1464
- );
1465
- checkCollisions(
1466
- contextTarget,
1467
- contribution.contextRest,
1468
- "context key",
1469
- options.callerLabel,
1470
- options.override
1471
- );
1472
- applyOwnProperties(propertiesTarget, contribution.rootKeys);
1473
- applyOwnProperties(contextTarget.meta, contribution.meta);
1474
- applyOwnProperties(contextTarget, contribution.contextRest);
1475
- contextTarget.hooks = buildHooks(contextTarget.hooks, contribution.hooks);
1476
- }
1477
- function applyPluginContribution(acc, contribution, options) {
1478
- mergeContribution(acc.view, acc.context, contribution, options);
1479
- }
1480
- function wrapAsSdk(properties, context) {
1481
- const sdk = {
1482
- ...properties,
1483
- context,
1484
- getRegistry(qopts) {
1485
- return buildRegistry({
1486
- sdk,
1487
- meta: context.meta,
1488
- packageFilter: qopts?.package
1489
- });
1340
+ const pageFunction = async (options) => {
1341
+ let { index, cursor: listCursor } = decodeConcatCursor(options.cursor);
1342
+ while (index < sources.length) {
1343
+ const page = await sources[index]({ cursor: listCursor });
1344
+ const hasMoreInList = page.nextCursor != null;
1345
+ if (page.data.length === 0 && !hasMoreInList) {
1346
+ index++;
1347
+ listCursor = void 0;
1348
+ continue;
1349
+ }
1350
+ return {
1351
+ data: page.data,
1352
+ nextCursor: hasMoreInList ? encodeConcatCursor(index, page.nextCursor) : index < sources.length - 1 ? encodeConcatCursor(index + 1, void 0) : void 0
1353
+ };
1490
1354
  }
1355
+ return { data: [] };
1491
1356
  };
1492
- return sdk;
1357
+ const result = await paginateBuffered(pageFunction, {
1358
+ pageSize,
1359
+ cursor
1360
+ }).next();
1361
+ return result.done ? { data: [] } : result.value;
1493
1362
  }
1494
- function wrapAccumulatorAsSdk(acc) {
1495
- const { context: _ctx, ...rootKeys } = acc.view;
1496
- return wrapAsSdk(
1497
- rootKeys,
1498
- acc.context
1499
- );
1363
+ function concatPaginated({
1364
+ sources,
1365
+ pageSize,
1366
+ cursor
1367
+ }) {
1368
+ logDeprecation("concatPaginated() is deprecated. Use concatLists() instead.");
1369
+ return concatLists({ sources, pageSize, cursor });
1500
1370
  }
1501
- function applyPluginToSdk(sdk, plugin, options) {
1502
- const context = sdk.context;
1503
- const contribution = splitPluginContribution(
1504
- plugin(sdk)
1371
+ function toIterable(source) {
1372
+ logDeprecation(
1373
+ "toIterable() is deprecated. Call .pages() on the paginated result instead."
1505
1374
  );
1506
- mergeContribution(sdk, context, contribution, {
1507
- callerLabel: "addPlugin",
1508
- override: options.override === true
1509
- });
1510
- return contribution;
1511
- }
1512
- function resolveStack(head) {
1513
- const entries = [];
1514
- let node = head;
1515
- while (node) {
1516
- entries.unshift({ apply: node.entry, override: node.override });
1517
- node = node.prev;
1518
- }
1519
- return entries;
1520
- }
1521
- function composeStackHooks(hooks) {
1522
- let composed = {};
1523
- for (const h of hooks) composed = buildHooks(composed, h);
1524
- return composed;
1375
+ return { [Symbol.asyncIterator]: () => source[Symbol.asyncIterator]() };
1525
1376
  }
1526
- function collapseStackEntries(entries, callerLabel) {
1527
- return (outerSdk) => {
1528
- const { context: outerContext, ...outerProperties } = outerSdk ?? {};
1529
- const viewAcc = createPluginAccumulator(outerProperties, outerContext);
1530
- const contribsAcc = createPluginAccumulator();
1531
- const hooks = [];
1532
- for (const { apply, override } of entries) {
1533
- const contribution = splitPluginContribution(
1534
- apply(viewAcc.view)
1535
- );
1536
- const hookless = { ...contribution, hooks: {} };
1537
- applyPluginContribution(viewAcc, hookless, { callerLabel, override });
1538
- applyPluginContribution(contribsAcc, hookless, { callerLabel, override });
1539
- hooks.push(contribution.hooks);
1540
- }
1541
- const stackHooks = composeStackHooks(hooks);
1542
- viewAcc.context.hooks = buildHooks(viewAcc.context.hooks, stackHooks);
1543
- contribsAcc.context.hooks = stackHooks;
1544
- const { context: _ignored, ...contributedRoot } = contribsAcc.view;
1545
- return {
1546
- ...contributedRoot,
1547
- context: contribsAcc.context
1548
- };
1549
- };
1377
+
1378
+ // src/utils/promise-utils.ts
1379
+ function isPromiseLike(value) {
1380
+ return value !== null && typeof value === "object" && typeof value.then === "function";
1550
1381
  }
1551
- function buildStackAccumulator(head, callerLabel) {
1552
- const entries = resolveStack(head);
1553
- const acc = createPluginAccumulator();
1554
- const hooks = [];
1555
- for (const { apply, override } of entries) {
1556
- const contribution = splitPluginContribution(
1557
- apply(acc.view)
1558
- );
1559
- applyPluginContribution(
1560
- acc,
1561
- { ...contribution, hooks: {} },
1562
- { callerLabel, override }
1382
+
1383
+ // src/utils/validation.ts
1384
+ var parseOrThrow = (schema, input, { adaptError } = {}) => {
1385
+ const result = schema.safeParse(input);
1386
+ if (!result.success) {
1387
+ const errorMessages = result.error.issues.map((issue) => {
1388
+ const path = issue.path.length > 0 ? issue.path.join(".") : "input";
1389
+ return `${path}: ${issue.message}`;
1390
+ });
1391
+ throw createCoreError(
1392
+ {
1393
+ code: CoreErrorCode.Validation,
1394
+ message: `Validation failed:
1395
+ ${errorMessages.join("\n ")}`,
1396
+ details: {
1397
+ zodErrors: result.error.issues,
1398
+ input
1399
+ }
1400
+ },
1401
+ adaptError
1563
1402
  );
1564
- hooks.push(contribution.hooks);
1565
- }
1566
- acc.context.hooks = composeStackHooks(hooks);
1567
- return acc;
1568
- }
1569
- function composePlugins(...plugins) {
1570
- logDeprecation(
1571
- "composePlugins(...) is deprecated. Use createPluginStack().use(a).use(b).use(c).toPlugin({ name }) instead. The stack carries the same collision-detection and hook-composition behavior and supports per-step { override: true } for intentional duplicates."
1572
- );
1573
- let head = null;
1574
- for (const plugin of plugins) {
1575
- head = { entry: plugin, override: false, prev: head };
1576
1403
  }
1577
- const entries = resolveStack(head);
1578
- return collapseStackEntries(entries, "composePlugins");
1579
- }
1580
- function createPluginStack() {
1581
- logDeprecation(
1582
- "createPluginStack() is deprecated. Compose with definePlugin and build with createSdk instead."
1583
- );
1584
- return buildPluginStack(null, "createPluginStack");
1585
- }
1586
- function buildPluginStack(head, callerLabel) {
1587
- const stack = {
1588
- use(plugin, options) {
1589
- const next = {
1590
- entry: plugin,
1591
- override: options?.override === true,
1592
- prev: head
1593
- };
1594
- return buildPluginStack(next, callerLabel);
1595
- },
1596
- toPlugin() {
1597
- const entries = resolveStack(head);
1598
- return collapseStackEntries(entries, callerLabel);
1599
- },
1600
- toSdk() {
1601
- return wrapAccumulatorAsSdk(
1602
- buildStackAccumulator(head, callerLabel)
1603
- );
1604
- }
1404
+ return result.data;
1405
+ };
1406
+ function createValidator(schema, { adaptError } = {}) {
1407
+ return function validateFn(input) {
1408
+ return parseOrThrow(schema, input, { adaptError });
1605
1409
  };
1606
- return stack;
1607
1410
  }
1411
+ var validateOptions = (schema, options, { adaptError } = {}) => parseOrThrow(schema, options, { adaptError });
1608
1412
 
1609
- // src/model/shared.ts
1610
- var CONTEXT = Symbol.for("kitcore.context");
1611
- function parseId(id) {
1612
- const at = id.lastIndexOf("/");
1613
- return at === -1 ? { name: id, namespace: void 0 } : { name: id.slice(at + 1), namespace: id.slice(0, at) };
1614
- }
1615
- function makeId(name, namespace, kind = "leaf") {
1616
- validateName(name, kind);
1617
- if (namespace !== void 0) validateNamespace(namespace);
1618
- return namespace ? `${namespace}/${name}` : name;
1413
+ // src/utils/call-options.ts
1414
+ import { z as z3 } from "zod";
1415
+ var CallFrameworkOptionsSchema = z3.object({
1416
+ /** Page to fetch. Opaque to kitcore: the head's API defines the format. */
1417
+ cursor: z3.string().optional(),
1418
+ /** Items per page. */
1419
+ pageSize: z3.number().int().min(1).optional(),
1420
+ /** Stop after this many items, across pages. */
1421
+ maxItems: z3.number().int().min(0).optional(),
1422
+ /** Bypass output validation for this one call. */
1423
+ skipOutputDataValidation: z3.boolean().optional()
1424
+ });
1425
+ var ITEM_FRAMEWORK_OPTIONS = {
1426
+ claims: ["skipOutputDataValidation"],
1427
+ injects: []
1428
+ };
1429
+ var LIST_FRAMEWORK_OPTIONS = {
1430
+ claims: ["cursor", "pageSize", "maxItems", "skipOutputDataValidation"],
1431
+ injects: ["cursor", "pageSize"]
1432
+ };
1433
+ var NO_FRAMEWORK_OPTIONS = {
1434
+ claims: [],
1435
+ injects: []
1436
+ };
1437
+ function isRecord(value) {
1438
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1619
1439
  }
1620
- var NAME_RE = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
1621
- var SEGMENT_RE = /^@?[A-Za-z0-9._-]+$/;
1622
- function validateName(name, kind) {
1623
- if (name === "") throw new Error("Plugin name must not be empty.");
1624
- if (kind === "leaf") {
1625
- if (!NAME_RE.test(name)) {
1626
- throw new Error(
1627
- `Plugin name "${name}" must be a valid JS identifier (it is the binding name).`
1628
- );
1440
+ function strictlyRefused(error, claims) {
1441
+ const refused = /* @__PURE__ */ new Set();
1442
+ for (const issue of error.issues) {
1443
+ if (issue.code !== "unrecognized_keys" || issue.path.length > 0) continue;
1444
+ for (const key of issue.keys) {
1445
+ if (claims.includes(key)) refused.add(key);
1629
1446
  }
1630
- } else if (!SEGMENT_RE.test(name)) {
1631
- throw new Error(
1632
- `Plugin name "${name}" must be package-like (letters, digits, ".", "_", "-", optional leading "@") with no "/".`
1633
- );
1634
1447
  }
1448
+ return [...refused];
1635
1449
  }
1636
- function validateNamespace(namespace) {
1637
- if (namespace === "") throw new Error("Plugin namespace must not be empty.");
1638
- for (const segment of namespace.split("/")) {
1639
- if (!SEGMENT_RE.test(segment)) {
1640
- throw new Error(
1641
- `Plugin namespace "${namespace}" is invalid: each "/"-separated segment must be package-like (letters, digits, ".", "_", "-", optional leading "@").`
1642
- );
1643
- }
1450
+ function withoutKeys(options, keys) {
1451
+ const next = {};
1452
+ for (const [key, value] of Object.entries(options)) {
1453
+ if (!keys.includes(key)) next[key] = value;
1644
1454
  }
1455
+ return next;
1645
1456
  }
1646
-
1647
- // src/model/types.ts
1648
- var LEAF_META_KEYS = [
1649
- "description",
1650
- "categories",
1651
- "type",
1652
- "itemType",
1653
- "returnType",
1654
- "outputSchema",
1655
- "packages",
1656
- "stability",
1657
- "experimental",
1658
- "confirm",
1659
- "deprecation",
1660
- "aliases",
1661
- "supportsJsonOutput"
1662
- ];
1663
-
1664
- // src/model/define.ts
1665
- function normalizeImports(deps) {
1666
- if (!deps) return { plugins: [], bindings: [] };
1667
- const seen = /* @__PURE__ */ new Map();
1668
- const bindings = [];
1669
- const add = (binding, id, optional) => {
1670
- const priorId = seen.get(binding);
1671
- if (priorId !== void 0 && priorId !== id) {
1672
- throw new Error(
1673
- `Import binding "${binding}" is declared twice. Two different plugins ("${priorId}" and "${id}") bind the same name; wrap one in selectExports to rename it.`
1674
- );
1675
- }
1676
- if (priorId === void 0) {
1677
- seen.set(binding, id);
1678
- bindings.push(optional ? { binding, id, optional } : { binding, id });
1679
- }
1680
- };
1681
- for (const plugin of deps) {
1682
- if (plugin.pluginType === "aggregate") {
1683
- for (const [binding, child] of Object.entries(plugin.exports)) {
1684
- add(binding, child.id);
1685
- }
1686
- } else if (plugin.pluginType === "hook") {
1687
- } else {
1688
- add(plugin.name, plugin.id, plugin.optional);
1457
+ function parseCallOptions(options, {
1458
+ schema,
1459
+ policy = NO_FRAMEWORK_OPTIONS,
1460
+ adaptError
1461
+ } = {}) {
1462
+ const claims = policy.claims;
1463
+ const call = isRecord(options) ? options : void 0;
1464
+ let framework = {};
1465
+ if (call && claims.length > 0) {
1466
+ const present = {};
1467
+ for (const key of claims) {
1468
+ if (key in call) present[key] = call[key];
1689
1469
  }
1470
+ framework = parseOrThrow2(CallFrameworkOptionsSchema, present, adaptError);
1690
1471
  }
1691
- return { plugins: deps, bindings };
1692
- }
1693
- function collectLeafMeta(config) {
1694
- let meta;
1695
- for (const key of LEAF_META_KEYS) {
1696
- if (config[key] !== void 0) (meta ?? (meta = {}))[key] = config[key];
1472
+ if (!schema) return { framework, domain: options, supplied: /* @__PURE__ */ new Set() };
1473
+ const first = schema.safeParse(options);
1474
+ if (first.success) {
1475
+ return { framework, domain: first.data, supplied: /* @__PURE__ */ new Set() };
1697
1476
  }
1698
- return meta;
1699
- }
1700
- function formatDynamicMemberName(path) {
1701
- return path.map((seg) => typeof seg === "string" ? seg : `{${seg.param}}`).join(".");
1477
+ const refused = call ? strictlyRefused(first.error, claims) : [];
1478
+ if (refused.length === 0) throw toCoreError(first.error, options, adaptError);
1479
+ const retry = schema.safeParse(withoutKeys(call, refused));
1480
+ if (!retry.success) {
1481
+ throw toCoreError(retry.error, options, adaptError);
1482
+ }
1483
+ return { framework, domain: retry.data, supplied: new Set(refused) };
1702
1484
  }
1703
- function collectDynamicMembers(members) {
1704
- if (!members?.length) return void 0;
1705
- return members.map((member) => {
1706
- const root = member.path[0];
1707
- if (typeof root !== "string") {
1708
- throw new Error(
1709
- "defineProperty: a dynamicMember path must start with a literal segment (the owning binding), not a { param }."
1710
- );
1711
- }
1712
- const leaf = collectLeafMeta(member) ?? {};
1713
- return {
1714
- name: formatDynamicMemberName(member.path),
1715
- rootBinding: root,
1716
- meta: member.inputSchema ? { ...leaf, inputSchema: member.inputSchema } : leaf
1717
- };
1718
- });
1485
+ function mergeCallOptions({
1486
+ framework,
1487
+ domain
1488
+ }) {
1489
+ const claimed = Object.entries(framework);
1490
+ if (!isRecord(domain) || claimed.length === 0) return domain;
1491
+ return { ...domain, ...Object.fromEntries(claimed) };
1719
1492
  }
1720
- function defineMethod(configOrRef, refConfig) {
1721
- const config = refConfig === void 0 ? configOrRef : {
1722
- ...refConfig,
1723
- name: configOrRef.name,
1724
- namespace: configOrRef.namespace
1725
- };
1726
- const deps = normalizeImports(config.imports);
1727
- return {
1728
- pluginType: "method",
1729
- name: config.name,
1730
- namespace: config.namespace,
1731
- id: makeId(config.name, config.namespace),
1732
- imports: deps.plugins,
1733
- importBindings: deps.bindings,
1734
- inputSchema: config.inputSchema,
1735
- skipInputValidation: config.skipInputValidation,
1736
- skipOutputValidation: config.skipOutputValidation,
1737
- meta: collectLeafMeta(config),
1738
- resolvers: config.resolvers,
1739
- formatter: config.formatter,
1740
- annotator: config.annotator,
1741
- output: config.output,
1742
- positional: config.positional,
1743
- setup: config.setup,
1744
- dispose: config.dispose,
1745
- run: config.run
1746
- };
1493
+ function withheldFromRun(policy) {
1494
+ return new Set(policy.claims.filter((key) => !policy.injects.includes(key)));
1747
1495
  }
1748
- var OVERRIDABLE = [
1749
- "description",
1750
- "categories",
1751
- "itemType",
1752
- "returnType",
1753
- "packages",
1754
- "experimental",
1755
- "deprecation",
1756
- "supportsJsonOutput"
1757
- ];
1758
- function assertOverridable(target, fields) {
1759
- const offered = Object.keys(fields).filter(
1760
- (key) => !OVERRIDABLE.includes(key)
1761
- );
1762
- if (offered.length === 0) return;
1763
- throw new Error(
1764
- `defineOverride("${target}"): cannot override ${offered.join(", ")}. An override changes how a surface presents a method, never what it does. The method's declared type is fixed at \`defineMethod\` and nothing re-checks it afterwards, so patching behavior here would let a call fail against a contract its own return type says it satisfies. Overridable: ${OVERRIDABLE.join(", ")}.`
1496
+ function stripFrameworkOnlyOptions(options, withheld) {
1497
+ if (withheld.size === 0 || !isRecord(options)) return options;
1498
+ const entries = Object.entries(options);
1499
+ if (!entries.some(([key]) => withheld.has(key))) return options;
1500
+ return Object.fromEntries(entries.filter(([key]) => !withheld.has(key)));
1501
+ }
1502
+ function parseOrThrow2(schema, input, adaptError) {
1503
+ const result = schema.safeParse(input);
1504
+ if (result.success) return result.data;
1505
+ throw toCoreError(result.error, input, adaptError);
1506
+ }
1507
+ function toCoreError(error, input, adaptError) {
1508
+ const messages = error.issues.map((issue) => {
1509
+ const path = issue.path.length > 0 ? issue.path.join(".") : "input";
1510
+ return `${path}: ${issue.message}`;
1511
+ });
1512
+ return createCoreError(
1513
+ {
1514
+ code: CoreErrorCode.Validation,
1515
+ message: `Validation failed:
1516
+ ${messages.join("\n ")}`,
1517
+ details: { zodErrors: error.issues, input }
1518
+ },
1519
+ adaptError
1765
1520
  );
1766
1521
  }
1767
- function buildOverride(target, namespace, fields) {
1768
- assertOverridable(target, fields);
1522
+
1523
+ // src/utils/async-context.ts
1524
+ import {
1525
+ AsyncLocalStorage
1526
+ } from "async_hooks";
1527
+ function createAsyncContext() {
1528
+ let store = null;
1529
+ try {
1530
+ store = new AsyncLocalStorage();
1531
+ } catch {
1532
+ store = null;
1533
+ }
1769
1534
  return {
1770
- pluginType: "method-override",
1771
- name: `override:${target}`,
1772
- id: namespace ? `${namespace}/override:${target}` : `override:${target}`,
1773
- target,
1774
- imports: [],
1775
- importBindings: [],
1776
- meta: collectLeafMeta(fields)
1535
+ available: store !== null,
1536
+ run(value, fn) {
1537
+ return store ? store.run(value, fn) : fn();
1538
+ },
1539
+ get() {
1540
+ return store?.getStore();
1541
+ }
1777
1542
  };
1778
1543
  }
1779
- function defineOverride(ref, config = {}) {
1780
- const { namespace, ...fields } = config;
1781
- return buildOverride(ref.id, namespace, fields);
1544
+
1545
+ // src/utils/method-scope.ts
1546
+ var scope = createAsyncContext();
1547
+ function getCurrentScope() {
1548
+ return scope.get();
1782
1549
  }
1783
- function defineMethodOverride(config) {
1784
- logDeprecation(
1785
- "defineMethodOverride({ target }) is deprecated. Use defineOverride(method, { ... }), which takes the method or its declareMethod stand-in."
1786
- );
1787
- const { target, namespace, ...fields } = config;
1788
- return buildOverride(target, namespace, fields);
1550
+ function getCurrentDepth() {
1551
+ return getCurrentScope()?.depth ?? 0;
1789
1552
  }
1790
- function assertRequirementPaths(requirements) {
1791
- if (!requirements) return;
1792
- for (const requirement of requirements) {
1793
- if (typeof requirement !== "string" && requirement.length === 0) {
1794
- throw new Error(
1795
- "defineResolver: a requireParameters path must name at least one segment. An empty path names no parameter, and the engine would read it as already satisfied."
1796
- );
1797
- }
1798
- }
1553
+ function isNestedMethodCall() {
1554
+ if (!scope.available) return true;
1555
+ const store = scope.get();
1556
+ return store !== void 0 && store.depth > 0;
1799
1557
  }
1800
- function defineResolver(config) {
1801
- const deps = normalizeImports(config.imports);
1802
- const base = { imports: deps.plugins, importBindings: deps.bindings };
1803
- assertRequirementPaths(config.requireParameters);
1804
- const gates = {
1805
- requireParameters: config.requireParameters
1806
- };
1807
- switch (config.type) {
1808
- case "static":
1809
- return {
1810
- ...base,
1811
- ...gates,
1812
- type: "static",
1813
- inputType: config.inputType,
1814
- placeholder: config.placeholder
1815
- };
1816
- case "constant":
1817
- return { ...base, ...gates, type: "constant", value: config.value };
1818
- case "info":
1819
- return { ...base, type: "info", text: config.text ?? "" };
1820
- case "object":
1821
- return {
1822
- ...base,
1823
- ...gates,
1824
- type: "object",
1825
- properties: config.properties,
1826
- definitions: config.definitions,
1827
- getProperties: config.getProperties,
1828
- additionalKeys: config.additionalKeys
1829
- };
1830
- case "array":
1831
- return {
1832
- ...base,
1833
- ...gates,
1834
- type: "array",
1835
- items: config.items,
1836
- minItems: config.minItems,
1837
- maxItems: config.maxItems,
1838
- itemValueType: config.itemValueType,
1839
- definitions: config.definitions
1840
- };
1841
- default:
1842
- return {
1843
- ...base,
1844
- ...gates,
1845
- type: "dynamic",
1846
- inputType: config.inputType,
1847
- placeholder: config.placeholder,
1848
- getContext: config.getContext,
1849
- listItems: config.listItems,
1850
- prompt: config.prompt,
1851
- validate: config.validate,
1852
- tryResolveWithoutPrompt: config.tryResolveWithoutPrompt,
1853
- tryResolveFromSearch: config.tryResolveFromSearch
1854
- };
1558
+ var observerReentrancy = 0;
1559
+ function runIsolatedObserver(fn) {
1560
+ observerReentrancy++;
1561
+ try {
1562
+ fn();
1563
+ } catch {
1564
+ } finally {
1565
+ observerReentrancy--;
1855
1566
  }
1856
1567
  }
1857
- function defineFormatter(config) {
1858
- const deps = normalizeImports(config.imports);
1859
- return {
1860
- imports: deps.plugins,
1861
- importBindings: deps.bindings,
1862
- getContext: config.getContext,
1863
- format: config.format
1864
- };
1568
+ function isInsideObserver() {
1569
+ return observerReentrancy > 0;
1865
1570
  }
1866
- function declareMethod(config) {
1867
- const { name, namespace } = parseId(config.id);
1868
- const id = makeId(name, namespace);
1869
- return {
1870
- pluginType: "method",
1871
- name,
1872
- namespace,
1873
- id,
1874
- standIn: true,
1875
- imports: [],
1876
- importBindings: [],
1877
- run: () => {
1878
- throw new Error(
1879
- `Plugin "${id}" is a stand-in (declareMethod) with no implementation. Register the real plugin under this id.`
1880
- );
1881
- }
1882
- };
1571
+ function runInMethodScope(fn) {
1572
+ if (!scope.available) return fn();
1573
+ const currentDepth = scope.get()?.depth ?? -1;
1574
+ return scope.run({ depth: currentDepth + 1 }, fn);
1883
1575
  }
1884
- function declareOptionalMethod(config) {
1885
- const { name, namespace } = parseId(config.id);
1886
- const id = makeId(name, namespace);
1887
- return {
1888
- pluginType: "method",
1889
- name,
1890
- namespace,
1891
- id,
1892
- standIn: true,
1893
- optional: true,
1894
- imports: [],
1895
- importBindings: [],
1896
- run: () => {
1897
- throw new Error(
1898
- `Plugin "${id}" is an optional stand-in (declareOptionalMethod) with no implementation. Its binding is \`undefined\` unless a real plugin is registered under this id.`
1899
- );
1900
- }
1901
- // Requires nothing (phantom carrier `<never, never>`): a consumer that
1902
- // imports it still passes `createSdk`'s completeness check unprovided. The
1903
- // `optional: true` literal drives `PluginSurface` to type the binding
1904
- // `| undefined`.
1905
- };
1576
+ var runWithTelemetryContext = runInMethodScope;
1577
+ var isTelemetryNested = isNestedMethodCall;
1578
+
1579
+ // src/utils/call-context.ts
1580
+ var CALL_CONTEXT_BRAND = Symbol("kitcore.callContext");
1581
+ function isCallContext(value) {
1582
+ return typeof value === "object" && value !== null && value[CALL_CONTEXT_BRAND] === true;
1906
1583
  }
1907
- function defineProperty(config, refConfig) {
1908
- const cfg = refConfig === void 0 ? config : {
1909
- ...refConfig,
1910
- name: config.name,
1911
- namespace: config.namespace
1912
- };
1913
- const deps = normalizeImports(cfg.imports);
1914
- return {
1915
- pluginType: "property",
1916
- name: cfg.name,
1917
- namespace: cfg.namespace,
1918
- id: makeId(cfg.name, cfg.namespace),
1919
- imports: deps.plugins,
1920
- importBindings: deps.bindings,
1921
- setup: cfg.setup,
1922
- dispose: cfg.dispose,
1923
- value: cfg.value,
1924
- get: cfg.get,
1925
- meta: collectLeafMeta(cfg),
1926
- dynamicMembers: collectDynamicMembers(cfg.dynamicMembers)
1927
- };
1584
+ function generateCallId() {
1585
+ try {
1586
+ const webCrypto = globalThis.crypto;
1587
+ if (webCrypto?.randomUUID) {
1588
+ return webCrypto.randomUUID();
1589
+ }
1590
+ if (webCrypto?.getRandomValues) {
1591
+ const bytes = webCrypto.getRandomValues(new Uint8Array(16));
1592
+ const hex = Array.from(bytes, (byte, i) => {
1593
+ const value = i === 6 ? byte & 15 | 64 : i === 8 ? byte & 63 | 128 : byte;
1594
+ return value.toString(16).padStart(2, "0");
1595
+ });
1596
+ return [
1597
+ hex.slice(0, 4).join(""),
1598
+ hex.slice(4, 6).join(""),
1599
+ hex.slice(6, 8).join(""),
1600
+ hex.slice(8, 10).join(""),
1601
+ hex.slice(10, 16).join("")
1602
+ ].join("-");
1603
+ }
1604
+ } catch {
1605
+ }
1606
+ return null;
1928
1607
  }
1929
- function declareProperty(config) {
1930
- const { name, namespace } = parseId(config.id);
1608
+ function rootCallContext({
1609
+ callOrigin = "surface"
1610
+ } = {}) {
1931
1611
  return {
1932
- pluginType: "property",
1933
- name,
1934
- namespace,
1935
- id: makeId(name, namespace),
1936
- standIn: true,
1937
- imports: [],
1938
- importBindings: []
1612
+ callId: generateCallId(),
1613
+ depth: 0,
1614
+ annotations: {},
1615
+ callOrigin,
1616
+ [CALL_CONTEXT_BRAND]: true
1939
1617
  };
1940
1618
  }
1941
- function declareOptionalProperty(config) {
1942
- const { name, namespace } = parseId(config.id);
1619
+ function childCallContext(parent) {
1943
1620
  return {
1944
- pluginType: "property",
1945
- name,
1946
- namespace,
1947
- id: makeId(name, namespace),
1948
- standIn: true,
1949
- optional: true,
1950
- imports: [],
1951
- importBindings: []
1952
- // Requires nothing (phantom carrier `<never, never>`): a consumer that
1953
- // imports it still passes `createSdk`'s completeness check unprovided. The
1954
- // import binding is still typed `TValue | undefined` from the descriptor.
1621
+ callId: parent.callId,
1622
+ depth: parent.depth + 1,
1623
+ annotations: {},
1624
+ callOrigin: parent.callOrigin,
1625
+ [CALL_CONTEXT_BRAND]: true
1955
1626
  };
1956
1627
  }
1957
- function declareDefault({
1958
- plugin
1959
- }) {
1960
- return { ...plugin, defaultSource: plugin };
1961
- }
1962
- function defineHook(config) {
1963
- const deps = normalizeImports(config.imports);
1964
- return {
1965
- pluginType: "hook",
1966
- name: config.name,
1967
- namespace: config.namespace,
1968
- id: makeId(config.name, config.namespace),
1969
- imports: deps.plugins,
1970
- importBindings: deps.bindings,
1971
- setup: config.setup,
1972
- dispose: config.dispose,
1973
- wrap: config.wrap,
1974
- observe: config.observe,
1975
- annotator: config.annotator
1976
- };
1628
+
1629
+ // src/utils/function-utils.ts
1630
+ function resolveCoreOptions(context) {
1631
+ const entry = context.plugins[CORE_OPTIONS_ID];
1632
+ if (!entry) return void 0;
1633
+ return entry.pluginType === "property" && entry.getValue ? entry.getValue() : entry.value;
1977
1634
  }
1978
- function declarePlugin(config) {
1979
- const { name, namespace } = parseId(config.id);
1980
- return {
1981
- pluginType: "aggregate",
1982
- name,
1983
- namespace,
1984
- id: makeId(name, namespace, "aggregate"),
1985
- standIn: true,
1986
- imports: [],
1987
- importBindings: [],
1988
- exports: normalizeExports(config.exports)
1989
- };
1635
+ var INTERNAL_CALL = Symbol("kitcore.internalCall");
1636
+ function resolveCallContext(secondArg) {
1637
+ return isCallContext(secondArg) ? secondArg : rootCallContext();
1990
1638
  }
1991
- function definePlugin(fnOrConfig) {
1992
- if (typeof fnOrConfig === "function") {
1993
- logDeprecation(
1994
- "definePlugin(fn) (the function form) is deprecated. Author plugins with defineMethod/defineProperty/definePlugin({ ... }) instead."
1995
- );
1996
- return fnOrConfig;
1639
+ var hookAnnotatorReentrancy = 0;
1640
+ function applyAnnotations({
1641
+ context,
1642
+ methodName,
1643
+ input,
1644
+ hookAnnotator,
1645
+ methodAnnotator
1646
+ }) {
1647
+ if (hookAnnotator && !isInsideObserver() && context.depth === 0 && context.callOrigin !== "internal" && hookAnnotatorReentrancy === 0) {
1648
+ hookAnnotatorReentrancy++;
1649
+ try {
1650
+ Object.assign(context.annotations, hookAnnotator({ methodName, input }));
1651
+ } catch {
1652
+ } finally {
1653
+ hookAnnotatorReentrancy--;
1654
+ }
1997
1655
  }
1998
- const config = fnOrConfig;
1999
- const deps = normalizeImports(config.imports);
2000
- return {
2001
- pluginType: "aggregate",
2002
- name: config.name,
2003
- namespace: config.namespace,
2004
- id: makeId(config.name, config.namespace, "aggregate"),
2005
- // A re-export synthetic (`selectExports` / `omitExports`) is flattened by
2006
- // `normalizeExports` into bare bindings, which drops its own `imports:
2007
- // [source]`. That edge is how `omitExports` keeps an omitted (unbound) leaf
2008
- // materialized + addressable by id, so preserve every exported aggregate's
2009
- // imports as extra reachability edges here (bindings unaffected).
2010
- imports: [...deps.plugins, ...exportedAggregateImports(config.exports)],
2011
- importBindings: deps.bindings,
2012
- exports: normalizeExports(config.exports)
2013
- };
2014
- }
2015
- function exportedAggregateImports(exports) {
2016
- if (!exports) return [];
2017
- const out = [];
2018
- for (const element of exports) {
2019
- if (element.pluginType === "aggregate") out.push(...element.imports);
1656
+ try {
1657
+ Object.assign(context.annotations, methodAnnotator?.(input));
1658
+ } catch {
2020
1659
  }
2021
- return out;
2022
1660
  }
2023
- function normalizeExports(exports) {
2024
- if (!exports) return {};
2025
- const out = {};
2026
- const add = (binding, leaf) => {
2027
- const existing = out[binding];
2028
- if (existing && existing.id !== leaf.id) {
2029
- throw new Error(
2030
- `definePlugin: duplicate export binding "${binding}". Two different plugins ("${existing.id}" and "${leaf.id}") bind the same name; wrap one in selectExports to rename it.`
2031
- );
2032
- }
2033
- out[binding] = leaf;
1661
+ function signalDeprecation(context, methodName, getDeprecation) {
1662
+ if (isInsideObserver()) return;
1663
+ const deprecation = getDeprecation?.();
1664
+ if (!deprecation?.message) return;
1665
+ const warning = {
1666
+ type: "deprecation",
1667
+ methodName,
1668
+ deprecation
2034
1669
  };
2035
- for (const element of exports) {
2036
- if (element.pluginType === "aggregate") {
2037
- for (const [binding, child] of Object.entries(element.exports)) {
2038
- add(binding, child);
2039
- }
2040
- } else {
2041
- add(element.name, element);
2042
- }
2043
- }
2044
- return out;
1670
+ const handler = resolveCoreOptions(context)?.logDeprecation ?? defaultLogDeprecation;
1671
+ runIsolatedObserver(() => handler(warning));
2045
1672
  }
2046
-
2047
- // src/model/exports.ts
2048
- var selectSeq = 0;
2049
- function selectExports(source, ...specs) {
2050
- const selected = {};
2051
- const pick = (binding, fromName) => {
2052
- const child = source.exports[fromName];
2053
- if (!child) {
2054
- throw new Error(
2055
- `selectExports: "${source.id}" has no export "${fromName}".`
2056
- );
2057
- }
2058
- selected[binding] = child;
1673
+ function signalStability(context, methodName, getStability) {
1674
+ if (isInsideObserver()) return;
1675
+ const stability = getStability?.();
1676
+ if (!stability || stability === "stable") return;
1677
+ const notice = {
1678
+ type: "stability",
1679
+ methodName,
1680
+ stability
2059
1681
  };
2060
- for (const spec of specs) {
2061
- if (typeof spec === "string") {
2062
- pick(spec, spec);
2063
- } else {
2064
- for (const [newName, fromName] of Object.entries(spec)) {
2065
- pick(newName, fromName);
1682
+ const handler = resolveCoreOptions(context)?.logStabilityNotice ?? defaultLogStabilityNotice;
1683
+ runIsolatedObserver(() => handler(notice));
1684
+ }
1685
+ function normalizeError(error, adaptError) {
1686
+ if (error instanceof Error) return error;
1687
+ const message = typeof error === "object" && error !== null && "message" in error && typeof error.message === "string" ? error.message : String(error);
1688
+ return createCoreError(
1689
+ {
1690
+ code: CoreErrorCode.Unknown,
1691
+ message,
1692
+ cause: error
1693
+ },
1694
+ adaptError
1695
+ );
1696
+ }
1697
+ function createFunction(coreFn, options) {
1698
+ const {
1699
+ sdkContext,
1700
+ schema,
1701
+ name,
1702
+ annotator,
1703
+ frameworkOptions,
1704
+ getDeprecation,
1705
+ getStability
1706
+ } = options;
1707
+ const functionName = name || coreFn.name;
1708
+ const namedFunctions = {
1709
+ [functionName]: async function(callOptions) {
1710
+ const internal = arguments[1];
1711
+ const context = resolveCallContext(internal);
1712
+ if (!isCallContext(internal) && internal !== INTERNAL_CALL) {
1713
+ signalDeprecation(sdkContext, functionName, getDeprecation);
1714
+ signalStability(sdkContext, functionName, getStability);
2066
1715
  }
1716
+ return runInMethodScope(async () => {
1717
+ const startTime = Date.now();
1718
+ const normalizedOptions = callOptions ?? {};
1719
+ const args = [normalizedOptions];
1720
+ const depth = Math.max(context.depth, getCurrentDepth());
1721
+ const insideObserver = isInsideObserver();
1722
+ const hooks = insideObserver ? void 0 : sdkContext.hooks;
1723
+ const adaptError = resolveCoreOptions(sdkContext)?.adaptError;
1724
+ applyAnnotations({
1725
+ context,
1726
+ methodName: functionName,
1727
+ input: normalizedOptions,
1728
+ hookAnnotator: hooks?.annotator,
1729
+ methodAnnotator: annotator
1730
+ });
1731
+ const hookBase = {
1732
+ methodName: functionName,
1733
+ args,
1734
+ isPaginated: false,
1735
+ depth,
1736
+ callId: context.callId,
1737
+ callOrigin: context.callOrigin,
1738
+ annotations: context.annotations
1739
+ };
1740
+ hooks?.onMethodStart?.({ ...hookBase });
1741
+ try {
1742
+ const parsed = parseCallOptions(normalizedOptions, {
1743
+ schema,
1744
+ policy: frameworkOptions,
1745
+ adaptError
1746
+ });
1747
+ const result = await coreFn(
1748
+ mergeCallOptions(parsed),
1749
+ context
1750
+ );
1751
+ hooks?.onMethodEnd?.({
1752
+ ...hookBase,
1753
+ durationMs: Date.now() - startTime
1754
+ });
1755
+ return result;
1756
+ } catch (error) {
1757
+ const normalizedError = normalizeError(error, adaptError);
1758
+ hooks?.onMethodEnd?.({
1759
+ ...hookBase,
1760
+ durationMs: Date.now() - startTime,
1761
+ error: normalizedError
1762
+ });
1763
+ throw normalizedError;
1764
+ }
1765
+ });
2067
1766
  }
2068
- }
2069
- const id = `${source.id}#select:${selectSeq++}`;
2070
- return {
2071
- pluginType: "aggregate",
2072
- name: makeId(`select`, source.name, "aggregate"),
2073
- id,
2074
- // Depend on the source so it is materialized; the selected bindings resolve
2075
- // to the source's own leaves (kept identity).
2076
- imports: [source],
2077
- importBindings: [],
2078
- exports: selected
2079
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
2080
1767
  };
1768
+ return namedFunctions[functionName];
2081
1769
  }
2082
- function omitExports(source, omit) {
2083
- const omitSet = new Set(omit);
2084
- for (const name of omit) {
2085
- if (!(name in source.exports)) {
2086
- throw new Error(`omitExports: "${source.id}" has no export "${name}".`);
1770
+ function createRawFunction(coreFn, options) {
1771
+ const {
1772
+ sdkContext,
1773
+ name,
1774
+ schema,
1775
+ positional,
1776
+ annotator,
1777
+ getDeprecation,
1778
+ getStability
1779
+ } = options;
1780
+ return function(rawInput) {
1781
+ const internal = arguments[1];
1782
+ const context = resolveCallContext(internal);
1783
+ if (!isCallContext(internal) && internal !== INTERNAL_CALL) {
1784
+ signalDeprecation(sdkContext, name, getDeprecation);
1785
+ signalStability(sdkContext, name, getStability);
2087
1786
  }
2088
- }
2089
- const kept = {};
2090
- for (const [binding, child] of Object.entries(source.exports)) {
2091
- if (!omitSet.has(binding)) kept[binding] = child;
2092
- }
2093
- return {
2094
- pluginType: "aggregate",
2095
- name: makeId(`omit`, source.name, "aggregate"),
2096
- id: `${source.id}#omit:${selectSeq++}`,
2097
- imports: [source],
2098
- importBindings: [],
2099
- exports: kept
2100
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
2101
- };
2102
- }
2103
-
2104
- // src/model/legacy.ts
2105
- function fromFunctionPlugin(fn, config) {
2106
- logDeprecation(
2107
- "fromFunctionPlugin() is deprecated. Author plugins with defineMethod/definePlugin instead."
2108
- );
2109
- return {
2110
- pluginType: "legacy",
2111
- name: config.name,
2112
- namespace: config.namespace,
2113
- id: makeId(config.name, config.namespace, "aggregate"),
2114
- imports: [],
2115
- importBindings: [],
2116
- run: fn
2117
- };
2118
- }
2119
- function defineLegacyMerge(args) {
2120
- logDeprecation(
2121
- "defineLegacyMerge() is deprecated. Build directly with createSdk(root, { configuration }) instead."
2122
- );
2123
- return {
2124
- pluginType: "legacy-merge",
2125
- name: args.name,
2126
- namespace: args.namespace,
2127
- id: makeId(args.name, args.namespace, "aggregate"),
2128
- legacy: fromFunctionPlugin(args.legacy, {
2129
- name: args.name,
2130
- namespace: args.namespace
2131
- }),
2132
- plugin: args.plugin
1787
+ return runInMethodScope(() => {
1788
+ const startTime = Date.now();
1789
+ const depth = Math.max(context.depth, getCurrentDepth());
1790
+ const insideObserver = isInsideObserver();
1791
+ const hooks = insideObserver ? void 0 : sdkContext.hooks;
1792
+ const adaptError = resolveCoreOptions(sdkContext)?.adaptError;
1793
+ const input = schema ? rawInput ?? {} : rawInput;
1794
+ applyAnnotations({
1795
+ context,
1796
+ methodName: name,
1797
+ input,
1798
+ hookAnnotator: hooks?.annotator,
1799
+ methodAnnotator: annotator
1800
+ });
1801
+ const record = input;
1802
+ const args = positional ? positional.filter((key) => record?.[key] !== void 0).map((key) => record?.[key]) : [input];
1803
+ const hookBase = {
1804
+ methodName: name,
1805
+ args,
1806
+ isPaginated: false,
1807
+ depth,
1808
+ callId: context.callId,
1809
+ callOrigin: context.callOrigin,
1810
+ annotations: context.annotations
1811
+ };
1812
+ hooks?.onMethodStart?.({ ...hookBase });
1813
+ const fireEnd = (error) => {
1814
+ hooks?.onMethodEnd?.({
1815
+ ...hookBase,
1816
+ durationMs: Date.now() - startTime,
1817
+ ...error ? { error } : {}
1818
+ });
1819
+ };
1820
+ try {
1821
+ const parsed = schema ? validateOptions(schema, input, { adaptError }) : input;
1822
+ const result = coreFn(parsed, context);
1823
+ if (isPromiseLike(result)) {
1824
+ return result.then(
1825
+ (value) => {
1826
+ fireEnd();
1827
+ return value;
1828
+ },
1829
+ (error) => {
1830
+ fireEnd(
1831
+ error instanceof Error ? error : new Error(String(error))
1832
+ );
1833
+ throw error;
1834
+ }
1835
+ );
1836
+ }
1837
+ fireEnd();
1838
+ return result;
1839
+ } catch (error) {
1840
+ fireEnd(error instanceof Error ? error : new Error(String(error)));
1841
+ throw error;
1842
+ }
1843
+ });
2133
1844
  };
2134
1845
  }
2135
- function legacyGraphEntry(name, value, pluginMeta) {
2136
- const { inputSchema, ...rest } = pluginMeta ?? {};
2137
- const meta = Object.keys(rest).length ? rest : void 0;
2138
- if (typeof value === "function") {
2139
- return {
2140
- pluginType: "method",
2141
- name,
2142
- value,
2143
- chain: [],
2144
- ...inputSchema ? { inputSchema } : {},
2145
- ...meta ? { meta } : {}
2146
- };
1846
+ function isSdkPage(value) {
1847
+ if (typeof value !== "object" || value === null) return false;
1848
+ const page = value;
1849
+ if (!Array.isArray(page.data)) return false;
1850
+ if (page.nextCursor !== void 0 && typeof page.nextCursor !== "string") {
1851
+ return false;
2147
1852
  }
2148
- return { pluginType: "property", name, value, ...meta ? { meta } : {} };
1853
+ return Object.keys(page).every((k) => k === "data" || k === "nextCursor");
2149
1854
  }
2150
-
2151
- // src/model/builtins.ts
2152
- import { z as z3 } from "zod";
2153
-
2154
- // src/model/registry-support.ts
2155
- function adaptLegacyFormatter(legacy, sdk) {
2156
- const legacyFetch = legacy.fetch;
2157
- return {
2158
- getContext: legacyFetch ? async ({ items, input, context }) => {
2159
- let ctx = context;
2160
- for (const item of items) {
2161
- ctx = await legacyFetch(sdk, input, item, ctx);
1855
+ function createPageFunction(coreFn, {
1856
+ sdkContext,
1857
+ adaptPage,
1858
+ finalizePage
1859
+ }) {
1860
+ const functionName = coreFn.name + "Page";
1861
+ const namedFunctions = {
1862
+ [functionName]: async function(options, callContext) {
1863
+ try {
1864
+ const response = await coreFn(options, callContext);
1865
+ const page = adaptPage ? adaptPage(response) : response;
1866
+ if (!isSdkPage(page)) {
1867
+ throw new Error(
1868
+ `${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\`.`
1869
+ );
1870
+ }
1871
+ return finalizePage ? finalizePage(page, options) : page;
1872
+ } catch (error) {
1873
+ throw normalizeError(error, resolveCoreOptions(sdkContext)?.adaptError);
2162
1874
  }
2163
- return ctx;
2164
- } : void 0,
2165
- format: ({ item, context }) => legacy.format(item, context)
1875
+ }
2166
1876
  };
1877
+ return namedFunctions[functionName];
2167
1878
  }
2168
- function normalizeFormatter(entry, sdk) {
2169
- if (entry.pluginType !== "method") return void 0;
2170
- if (entry.formatter) return entry.formatter;
2171
- const legacy = entry.meta?.formatter;
2172
- return legacy ? adaptLegacyFormatter(legacy, sdk) : void 0;
2173
- }
2174
- function normalizeResolvers(entry) {
2175
- if (entry.pluginType !== "method") return void 0;
2176
- return entry.resolvers;
2177
- }
2178
- function methodPositional(entry) {
2179
- if (entry.pluginType !== "method") return void 0;
2180
- return entry.positional;
2181
- }
2182
- function pluginEntryMeta(entry) {
2183
- if (entry.pluginType === "method" && entry.meta) {
2184
- return entry.inputSchema ? { ...entry.meta, inputSchema: entry.inputSchema } : entry.meta;
2185
- }
2186
- if (entry.pluginType === "property" && entry.meta) return entry.meta;
2187
- return void 0;
2188
- }
2189
- function foldDynamicMembers(entry, surfaceBindings, meta) {
2190
- if (entry.pluginType !== "property" || !entry.dynamicMembers) return;
2191
- for (const member of entry.dynamicMembers) {
2192
- if (!surfaceBindings.has(member.rootBinding)) {
2193
- throw new Error(
2194
- `dynamicMember "${member.name}": its root "${member.rootBinding}" is not a surfaced member. A dynamic member's path must start with a real binding.`
2195
- );
1879
+ function createPaginatedFunction(coreFn, options) {
1880
+ const {
1881
+ sdkContext,
1882
+ schema,
1883
+ name,
1884
+ defaultPageSize,
1885
+ adaptPage,
1886
+ annotator,
1887
+ finalizePage,
1888
+ frameworkOptions,
1889
+ getDeprecation,
1890
+ getStability
1891
+ } = options;
1892
+ const pageFunction = createPageFunction(coreFn, {
1893
+ sdkContext,
1894
+ adaptPage,
1895
+ finalizePage
1896
+ });
1897
+ const functionName = name || coreFn.name;
1898
+ const namedFunctions = {
1899
+ [functionName]: function(callOptions) {
1900
+ const internal = arguments[1];
1901
+ const context = resolveCallContext(internal);
1902
+ if (!isCallContext(internal) && internal !== INTERNAL_CALL) {
1903
+ signalDeprecation(sdkContext, functionName, getDeprecation);
1904
+ signalStability(sdkContext, functionName, getStability);
1905
+ }
1906
+ return runInMethodScope(() => {
1907
+ const startTime = Date.now();
1908
+ const normalizedOptions = callOptions ?? {};
1909
+ const args = [normalizedOptions];
1910
+ const depth = Math.max(context.depth, getCurrentDepth());
1911
+ const insideObserver = isInsideObserver();
1912
+ const hooks = insideObserver ? void 0 : sdkContext.hooks;
1913
+ const adaptError = resolveCoreOptions(sdkContext)?.adaptError;
1914
+ applyAnnotations({
1915
+ context,
1916
+ methodName: functionName,
1917
+ input: normalizedOptions,
1918
+ hookAnnotator: hooks?.annotator,
1919
+ methodAnnotator: annotator
1920
+ });
1921
+ const hookBase = {
1922
+ methodName: functionName,
1923
+ args,
1924
+ isPaginated: true,
1925
+ depth,
1926
+ callId: context.callId,
1927
+ callOrigin: context.callOrigin,
1928
+ annotations: context.annotations
1929
+ };
1930
+ hooks?.onMethodStart?.({ ...hookBase });
1931
+ try {
1932
+ const validatedOptions = mergeCallOptions(
1933
+ parseCallOptions(normalizedOptions, {
1934
+ schema,
1935
+ policy: frameworkOptions,
1936
+ adaptError
1937
+ })
1938
+ );
1939
+ const pageSize = validatedOptions.pageSize ?? defaultPageSize;
1940
+ const optimizedOptions = {
1941
+ ...validatedOptions,
1942
+ pageSize
1943
+ };
1944
+ const iterator = paginate(
1945
+ (pageOptions) => pageFunction(pageOptions, context),
1946
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1947
+ optimizedOptions
1948
+ );
1949
+ const firstPagePromise = iterator.next().then((result) => {
1950
+ if (result.done) {
1951
+ throw new Error("Paginate should always iterate at least once");
1952
+ }
1953
+ return result.value;
1954
+ });
1955
+ if (hooks?.onMethodEnd) {
1956
+ firstPagePromise.then(
1957
+ () => {
1958
+ hooks.onMethodEnd({
1959
+ ...hookBase,
1960
+ durationMs: Date.now() - startTime
1961
+ });
1962
+ },
1963
+ (error) => {
1964
+ hooks.onMethodEnd({
1965
+ ...hookBase,
1966
+ durationMs: Date.now() - startTime,
1967
+ error: error instanceof Error ? error : new Error(String(error))
1968
+ });
1969
+ }
1970
+ );
1971
+ }
1972
+ const pageStream = async function* () {
1973
+ yield await firstPagePromise;
1974
+ for await (const page of iterator) {
1975
+ yield page;
1976
+ }
1977
+ }();
1978
+ return Object.assign(firstPagePromise, {
1979
+ [Symbol.asyncIterator]() {
1980
+ return pageStream;
1981
+ },
1982
+ pages: function() {
1983
+ return {
1984
+ [Symbol.asyncIterator]() {
1985
+ return pageStream;
1986
+ }
1987
+ };
1988
+ },
1989
+ items: function() {
1990
+ return {
1991
+ [Symbol.asyncIterator]: async function* () {
1992
+ for await (const page of pageStream) {
1993
+ for (const item of page.data) {
1994
+ yield item;
1995
+ }
1996
+ }
1997
+ }
1998
+ };
1999
+ }
2000
+ });
2001
+ } catch (error) {
2002
+ const normalizedError = normalizeError(error, adaptError);
2003
+ hooks?.onMethodEnd?.({
2004
+ ...hookBase,
2005
+ durationMs: Date.now() - startTime,
2006
+ error: normalizedError
2007
+ });
2008
+ throw normalizedError;
2009
+ }
2010
+ });
2196
2011
  }
2197
- meta[member.name] = member.meta;
2198
- }
2199
- }
2200
- function collectSurfaceProjection(context, formatterSdk) {
2201
- const meta = {};
2202
- const entries = {};
2203
- for (const [binding, id] of Object.entries(context.surface)) {
2204
- const entry = context.plugins[id];
2205
- if (!entry || entry.pluginType === "aggregate") continue;
2206
- entries[binding] = entry;
2207
- const m = pluginEntryMeta(entry);
2208
- if (m) meta[binding] = m;
2209
- }
2210
- const surfaceBindings = new Set(Object.keys(context.surface));
2211
- for (const entry of Object.values(entries)) {
2212
- foldDynamicMembers(entry, surfaceBindings, meta);
2213
- }
2214
- const formatters = {};
2215
- const resolvers = {};
2216
- const positional = {};
2217
- const skipInputValidation = {};
2218
- for (const [binding, entry] of Object.entries(entries)) {
2219
- const f = normalizeFormatter(entry, formatterSdk);
2220
- if (f) formatters[binding] = f;
2221
- const r = normalizeResolvers(entry);
2222
- if (r) resolvers[binding] = r;
2223
- const p = methodPositional(entry);
2224
- if (p) positional[binding] = p;
2225
- if (entry.pluginType === "method" && entry.skipInputValidation)
2226
- skipInputValidation[binding] = true;
2227
- }
2228
- return { meta, formatters, resolvers, positional, skipInputValidation };
2229
- }
2230
- var REGISTRY_CACHE = Symbol.for("kitcore.registryCache");
2231
- function freezeContainers(registry) {
2232
- Object.freeze(registry.functions);
2233
- for (const category of registry.categories) {
2234
- Object.freeze(category.functions);
2235
- Object.freeze(category);
2236
- }
2237
- Object.freeze(registry.categories);
2238
- return Object.freeze(registry);
2239
- }
2240
- function getCachedRegistry(context, packageFilter) {
2241
- const key = packageFilter ?? "";
2242
- const caching = context;
2243
- let byFilter = caching[REGISTRY_CACHE];
2244
- if (!byFilter) {
2245
- byFilter = /* @__PURE__ */ new Map();
2246
- caching[REGISTRY_CACHE] = byFilter;
2247
- }
2248
- let registry = byFilter.get(key);
2249
- if (!registry) {
2250
- registry = freezeContainers(buildSurfaceRegistry(context, packageFilter));
2251
- byFilter.set(key, registry);
2252
- }
2253
- return registry;
2254
- }
2255
- function invalidateRegistryCache(context) {
2256
- delete context[REGISTRY_CACHE];
2257
- }
2258
- function buildSurfaceRegistry(context, packageFilter) {
2259
- const surface = {};
2260
- for (const [binding, id] of Object.entries(context.surface)) {
2261
- const entry = context.plugins[id];
2262
- if (!entry || entry.pluginType === "aggregate") continue;
2263
- surface[binding] = entry.pluginType === "property" && entry.getValue ? entry.getValue() : entry.value;
2264
- }
2265
- const projection = collectSurfaceProjection(context, surface);
2266
- Object.assign(projection.meta, context.meta);
2267
- return buildRegistry({
2268
- sdk: surface,
2269
- ...projection,
2270
- packageFilter
2271
- });
2012
+ };
2013
+ return namedFunctions[functionName];
2272
2014
  }
2273
2015
 
2274
- // src/model/builtins.ts
2275
- var coreOptionsPluginRef = declareOptionalProperty({ id: CORE_OPTIONS_ID });
2276
- var dangerousContextPlugin = {
2277
- pluginType: "property",
2278
- name: "context",
2279
- namespace: "kitcore",
2280
- id: "kitcore/context",
2281
- imports: [],
2282
- importBindings: [],
2283
- privileged: true
2284
- };
2285
- var getRegistryPlugin = defineMethod({
2286
- name: "getRegistry",
2287
- namespace: "kitcore",
2288
- imports: [dangerousContextPlugin],
2289
- inputSchema: z3.object({ package: z3.string().optional() }).optional(),
2290
- run: ({ imports, input }) => getCachedRegistry(imports.context, input?.package)
2291
- });
2292
-
2293
2016
  // src/utils/output-policy.ts
2294
2017
  function isRecord2(value) {
2295
2018
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -2444,6 +2167,62 @@ function applyListOutputPolicy(page, policy) {
2444
2167
  return next;
2445
2168
  }
2446
2169
 
2170
+ // src/model/add-plugin-transaction.ts
2171
+ function snapshotGraph({
2172
+ context,
2173
+ sdk,
2174
+ surfaceKeys
2175
+ }) {
2176
+ const ids = new Set(Object.keys(context.plugins));
2177
+ const surface = Object.assign(
2178
+ /* @__PURE__ */ Object.create(null),
2179
+ context.surface
2180
+ );
2181
+ const propertyDescriptors = /* @__PURE__ */ new Map();
2182
+ for (const key of surfaceKeys) {
2183
+ propertyDescriptors.set(key, Object.getOwnPropertyDescriptor(sdk, key));
2184
+ }
2185
+ const hooks = context.hooks;
2186
+ const disposerCount = context.disposers?.length ?? 0;
2187
+ const chainLengths = /* @__PURE__ */ new Map();
2188
+ const descriptions = /* @__PURE__ */ new Map();
2189
+ for (const [id, entry] of Object.entries(context.plugins)) {
2190
+ if (entry.pluginType !== "method") continue;
2191
+ chainLengths.set(id, entry.chain.length);
2192
+ descriptions.set(id, pickDefined(entry, METHOD_META_KEYS));
2193
+ }
2194
+ return () => {
2195
+ const dropped = context.disposers?.slice(disposerCount) ?? [];
2196
+ for (let i = dropped.length - 1; i >= 0; i--) {
2197
+ try {
2198
+ void Promise.resolve(dropped[i].dispose()).catch(() => {
2199
+ });
2200
+ } catch {
2201
+ }
2202
+ }
2203
+ for (const id of Object.keys(context.plugins)) {
2204
+ if (!ids.has(id)) delete context.plugins[id];
2205
+ }
2206
+ for (const [id, length] of chainLengths) {
2207
+ const entry = context.plugins[id];
2208
+ if (entry?.pluginType === "method") entry.chain.length = length;
2209
+ }
2210
+ for (const [id, description] of descriptions) {
2211
+ const entry = context.plugins[id];
2212
+ if (entry?.pluginType !== "method") continue;
2213
+ for (const key of METHOD_META_KEYS) delete entry[key];
2214
+ Object.assign(entry, description);
2215
+ }
2216
+ context.hooks = hooks;
2217
+ if (context.disposers) context.disposers.length = disposerCount;
2218
+ context.surface = surface;
2219
+ for (const [key, descriptor] of propertyDescriptors) {
2220
+ if (descriptor) Object.defineProperty(sdk, key, descriptor);
2221
+ else delete sdk[key];
2222
+ }
2223
+ };
2224
+ }
2225
+
2447
2226
  // src/model/materialize.ts
2448
2227
  var FRAMEWORK_CONFIGURATION_IDS = /* @__PURE__ */ new Set([
2449
2228
  CORE_OPTIONS_ID
@@ -2454,6 +2233,19 @@ function normalizeOutput(output) {
2454
2233
  return output;
2455
2234
  }
2456
2235
  function getContext(sdk) {
2236
+ const context = tryGetContext(sdk);
2237
+ if (!context) {
2238
+ throw createCoreError({
2239
+ code: CoreErrorCode.NoSdkContext,
2240
+ message: "getContext: object has no kitcore context. Only an SDK built by createSdk carries one."
2241
+ });
2242
+ }
2243
+ return context;
2244
+ }
2245
+ function tryGetContext(sdk) {
2246
+ if (typeof sdk !== "object" && typeof sdk !== "function" || sdk === null) {
2247
+ return void 0;
2248
+ }
2457
2249
  return sdk[CONTEXT];
2458
2250
  }
2459
2251
  function assertDynamicMemberRoot(entry) {
@@ -2465,11 +2257,9 @@ function assertDynamicMemberRoot(entry) {
2465
2257
  );
2466
2258
  }
2467
2259
  function getRegistry(sdk, packageFilter) {
2468
- if (typeof sdk !== "object" && typeof sdk !== "function" || sdk === null)
2469
- throw createNoRegistryError();
2470
- const context = getContext(sdk);
2260
+ const context = tryGetContext(sdk);
2471
2261
  if (context?.surface) return getCachedRegistry(context, packageFilter);
2472
- const surfaced = sdk.getRegistry;
2262
+ const surfaced = sdk?.getRegistry;
2473
2263
  if (typeof surfaced === "function") {
2474
2264
  return surfaced.call(
2475
2265
  sdk,
@@ -2479,9 +2269,10 @@ function getRegistry(sdk, packageFilter) {
2479
2269
  throw createNoRegistryError();
2480
2270
  }
2481
2271
  function createNoRegistryError() {
2482
- return new Error(
2483
- "getRegistry: sdk has no kitcore context and no surfaced getRegistry()."
2484
- );
2272
+ return createCoreError({
2273
+ code: CoreErrorCode.NoSdkContext,
2274
+ message: "getRegistry: sdk has no kitcore context and no surfaced getRegistry()."
2275
+ });
2485
2276
  }
2486
2277
  function isResolverRef(value) {
2487
2278
  return "ref" in value;
@@ -2530,8 +2321,14 @@ function edgesOf(plugin) {
2530
2321
  }
2531
2322
  return plugin.imports;
2532
2323
  }
2533
- function isStandIn(plugin) {
2534
- return (plugin.pluginType === "method" || plugin.pluginType === "property" || plugin.pluginType === "aggregate") && plugin.standIn === true;
2324
+ function sameIdError({
2325
+ plugin,
2326
+ existing
2327
+ }) {
2328
+ const fix = isDefault(existing) ? `The plugin in place is the declareDefault provider for this id, and its dependents already ran setup against it. Register the explicit provider in the graph you pass to createSdk, where it preempts the default.` : plugin.pluginType === "method-override" ? `An override's id comes from the method it patches, so put one of them under its own namespace, or merge both patches into a single override.` : `Ids are the identity in this graph: give it its own id, or rebuild with createSdk to replace the original.`;
2329
+ return new Error(
2330
+ `addPlugin: "${plugin.id}" is already applied by a different plugin. The add would leave the first one in place and contribute nothing, so it is refused. ${fix}`
2331
+ );
2535
2332
  }
2536
2333
  function isDefault(plugin) {
2537
2334
  return (plugin.pluginType === "method" || plugin.pluginType === "property") && plugin.defaultSource !== void 0;
@@ -2549,12 +2346,25 @@ function topoOrder(descriptors) {
2549
2346
  for (const id of descriptors.keys()) visit(id);
2550
2347
  return order;
2551
2348
  }
2552
- function collectPlugins(root, materialized = /* @__PURE__ */ new Set(), configuration) {
2349
+ function collectPlugins({
2350
+ root,
2351
+ caller,
2352
+ applied,
2353
+ configuration
2354
+ }) {
2553
2355
  const rank = (plugin) => isStandIn(plugin) ? 0 : isDefault(plugin) ? 1 : 2;
2554
2356
  const allNodes = [];
2555
2357
  const seen = /* @__PURE__ */ new Set();
2556
2358
  const collect = (plugin) => {
2557
- if (materialized.has(plugin.id) || seen.has(plugin)) return;
2359
+ assertKnownPluginType({ plugin, where: caller });
2360
+ const existing = applied[plugin.id];
2361
+ if (existing) {
2362
+ if (existing.descriptor !== plugin && rank(plugin) === 2) {
2363
+ throw sameIdError({ plugin, existing: existing.descriptor });
2364
+ }
2365
+ return;
2366
+ }
2367
+ if (seen.has(plugin)) return;
2558
2368
  seen.add(plugin);
2559
2369
  allNodes.push(plugin);
2560
2370
  for (const edge of edgesOf(plugin)) collect(edge);
@@ -2619,7 +2429,7 @@ function collectPlugins(root, materialized = /* @__PURE__ */ new Set(), configur
2619
2429
  const winnerRank = rank(winner);
2620
2430
  if (winnerRank === 2) {
2621
2431
  throw new Error(
2622
- `createSdk: duplicate plugin id "${id}". Two different plugins registered under the same id.`
2432
+ `${caller}: duplicate plugin id "${id}". Two different plugins registered under the same id.`
2623
2433
  );
2624
2434
  }
2625
2435
  if (winnerRank === 1) {
@@ -2679,7 +2489,7 @@ function collectPlugins(root, materialized = /* @__PURE__ */ new Set(), configur
2679
2489
  if (isStandIn(plugin)) {
2680
2490
  if ("optional" in plugin && plugin.optional) continue;
2681
2491
  throw new Error(
2682
- `createSdk: missing dependency "${id}". A plugin depends on it (via a stand-in) but no implementation was registered.`
2492
+ `${caller}: missing dependency "${id}". A plugin depends on it (via a stand-in) but no implementation was registered.`
2683
2493
  );
2684
2494
  }
2685
2495
  }
@@ -2687,7 +2497,7 @@ function collectPlugins(root, materialized = /* @__PURE__ */ new Set(), configur
2687
2497
  const winner = byId.get(id);
2688
2498
  if (winner && isDefault(winner)) {
2689
2499
  throw new Error(
2690
- `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.`
2500
+ `${caller}: 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.`
2691
2501
  );
2692
2502
  }
2693
2503
  }
@@ -2709,7 +2519,7 @@ function bindValue({
2709
2519
  configurable: true
2710
2520
  });
2711
2521
  } else {
2712
- const value = bindMode === "internal" && entry.pluginType === "method" ? entry.bindInternal?.({ ctx, frameworkOrigin }) ?? entry.internalValue ?? entry.value : entry.value;
2522
+ const value = bindMode === "internal" && entry.pluginType === "method" ? entry.bindInternal({ ctx, frameworkOrigin }) : entry.value;
2713
2523
  Object.defineProperty(target, key, {
2714
2524
  value,
2715
2525
  writable: true,
@@ -2745,10 +2555,15 @@ function buildImports({
2745
2555
  });
2746
2556
  continue;
2747
2557
  }
2558
+ if (!entry) {
2559
+ throw new Error(
2560
+ `buildImports: no materialized plugin for "${id}", bound as "${binding}". If this surfaced from a \`dispose\`, the SDK it ran against was rolled back, so release before the first \`await\`.`
2561
+ );
2562
+ }
2748
2563
  bindValue({
2749
2564
  target: imports,
2750
2565
  key: binding,
2751
- entry,
2566
+ entry: valueEntryOf(entry, id),
2752
2567
  bindMode: "internal",
2753
2568
  ctx,
2754
2569
  frameworkOrigin
@@ -2770,14 +2585,6 @@ function bindInternalTwin({
2770
2585
  }
2771
2586
  return internalValue;
2772
2587
  }
2773
- function mirrorLegacyRootKeys(context, rootKeys, meta) {
2774
- const exports = {};
2775
- for (const [name, value] of Object.entries(rootKeys)) {
2776
- context.plugins[name] = legacyGraphEntry(name, value, meta[name]);
2777
- exports[name] = value;
2778
- }
2779
- return exports;
2780
- }
2781
2588
  function recordExportSurface(context, exports) {
2782
2589
  for (const [binding, child] of Object.entries(exports)) {
2783
2590
  context.surface[binding] = child.id;
@@ -2785,9 +2592,8 @@ function recordExportSurface(context, exports) {
2785
2592
  }
2786
2593
  function materialize(descriptors, context) {
2787
2594
  const states = /* @__PURE__ */ new Map();
2788
- runLegacyPass(descriptors, context);
2789
- buildMethodEntries(descriptors, context, states);
2790
- buildEagerArtifacts(descriptors, context, states);
2595
+ buildLeafEntries(descriptors, context, states);
2596
+ runSetup(descriptors, context, states);
2791
2597
  bindAttachments(descriptors, context);
2792
2598
  resolveAggregates(descriptors, context);
2793
2599
  assembleMiddleware(descriptors, context, states);
@@ -2799,22 +2605,35 @@ function applyMethodOverride(context, override) {
2799
2605
  const entry = context.plugins[override.target];
2800
2606
  if (!entry) {
2801
2607
  throw new Error(
2802
- `defineMethodOverride: no method "${override.target}" to override. Include the target method in the SDK build.`
2608
+ `defineOverride: no method "${override.target}" to override. Include the target method in the SDK build.`
2803
2609
  );
2804
2610
  }
2805
2611
  if (entry.pluginType !== "method") {
2806
2612
  throw new Error(
2807
- `defineMethodOverride: "${override.target}" is a ${entry.pluginType}, not a method; only methods can be overridden.`
2613
+ `defineOverride: "${override.target}" is a ${entry.pluginType}, not a method; only methods can be overridden.`
2808
2614
  );
2809
2615
  }
2810
- entry.meta = { ...entry.meta, ...override.meta };
2616
+ Object.assign(entry, override.patch);
2617
+ }
2618
+ function addOverridePlugin(context, override) {
2619
+ const existing = context.plugins[override.id];
2620
+ if (existing?.descriptor === override) return;
2621
+ if (existing) {
2622
+ throw sameIdError({ plugin: override, existing: existing.descriptor });
2623
+ }
2624
+ applyMethodOverride(context, override);
2625
+ context.plugins[override.id] = overrideEntry(override);
2811
2626
  }
2812
2627
  function applyMethodOverrides(descriptors, context) {
2813
2628
  for (const descriptor of descriptors.values()) {
2814
2629
  if (descriptor.pluginType !== "method-override") continue;
2815
2630
  applyMethodOverride(context, descriptor);
2631
+ context.plugins[descriptor.id] = overrideEntry(descriptor);
2816
2632
  }
2817
2633
  }
2634
+ function overrideEntry(descriptor) {
2635
+ return { pluginType: "method-override", name: descriptor.name, descriptor };
2636
+ }
2818
2637
  function bindResolver(resolver, plugins) {
2819
2638
  switch (resolver.type) {
2820
2639
  case "static":
@@ -2884,7 +2703,9 @@ function bindResolver(resolver, plugins) {
2884
2703
  frameworkOrigin: true
2885
2704
  });
2886
2705
  const {
2887
- getContext: getContext2,
2706
+ // Named apart from the module's exported `getContext`, which throws when
2707
+ // an object carries no kitcore context.
2708
+ getContext: getResolverContext,
2888
2709
  listItems,
2889
2710
  validate,
2890
2711
  tryResolveWithoutPrompt,
@@ -2898,8 +2719,8 @@ function bindResolver(resolver, plugins) {
2898
2719
  prompt: resolver.prompt,
2899
2720
  listItems: ({ input, context, search, cursor }) => listItems({ imports, input, context, search, cursor })
2900
2721
  };
2901
- if (getContext2)
2902
- bound.getContext = ({ input }) => getContext2({ imports, input });
2722
+ if (getResolverContext)
2723
+ bound.getContext = ({ input }) => getResolverContext({ imports, input });
2903
2724
  if (validate) {
2904
2725
  bound.validate = ({ value, input, context }) => validate({ imports, value, input, context });
2905
2726
  }
@@ -2943,9 +2764,9 @@ function bindFormatter(formatter, plugins) {
2943
2764
  frameworkOrigin: true
2944
2765
  });
2945
2766
  const bound = { format: formatter.format };
2946
- const { getContext: getContext2 } = formatter;
2947
- if (getContext2)
2948
- bound.getContext = ({ items, input, context }) => getContext2({ imports, items, input, context });
2767
+ const { getContext: getFormatterContext } = formatter;
2768
+ if (getFormatterContext)
2769
+ bound.getContext = ({ items, input, context }) => getFormatterContext({ imports, items, input, context });
2949
2770
  return bound;
2950
2771
  }
2951
2772
  function bindAttachments(descriptors, context) {
@@ -2966,64 +2787,35 @@ function bindAttachments(descriptors, context) {
2966
2787
  }
2967
2788
  }
2968
2789
  }
2969
- function runLegacyPass(descriptors, context) {
2790
+ function buildLeafEntries(descriptors, context, states) {
2970
2791
  const plugins = context.plugins;
2971
- const compatView = new Proxy(
2972
- {},
2973
- {
2974
- get: (_target, prop) => {
2975
- if (prop === "context") return context;
2976
- const entry = plugins[prop];
2977
- return entry?.value;
2792
+ for (const [id, descriptor] of descriptors) {
2793
+ if (descriptor.pluginType === "property") {
2794
+ if (!isStandIn(descriptor)) {
2795
+ plugins[id] = buildPropertyEntry(descriptor, id, context, states);
2978
2796
  }
2797
+ continue;
2979
2798
  }
2980
- );
2981
- for (const id of topoOrder(descriptors)) {
2982
- const descriptor = descriptors.get(id);
2983
- if (!descriptor || descriptor.pluginType !== "legacy") continue;
2984
- const { rootKeys, meta, hooks, contextRest } = splitPluginContribution(
2985
- descriptor.run(compatView)
2986
- );
2987
- Object.assign(context.meta, meta);
2988
- Object.assign(context, contextRest);
2989
- context.hooks = buildHooks(context.hooks, hooks);
2990
- const exports = mirrorLegacyRootKeys(context, rootKeys, meta);
2991
- for (const name of Object.keys(rootKeys)) context.surface[name] = name;
2992
- if (!("getRegistry" in exports)) {
2993
- let getRegistry3 = function(options) {
2994
- return getCachedRegistry(context, options?.package);
2995
- };
2996
- var getRegistry2 = getRegistry3;
2997
- exports.getRegistry = getRegistry3;
2998
- plugins.getRegistry = {
2999
- pluginType: "method",
3000
- name: "getRegistry",
3001
- value: getRegistry3,
3002
- chain: []
3003
- };
3004
- }
3005
- plugins[id] = { pluginType: "aggregate", name: descriptor.name, exports };
3006
- }
3007
- }
3008
- function buildMethodEntries(descriptors, context, states) {
3009
- const plugins = context.plugins;
3010
- for (const [id, descriptor] of descriptors) {
3011
2799
  if (descriptor.pluginType !== "method") continue;
3012
2800
  if (isStandIn(descriptor)) continue;
3013
2801
  const out = normalizeOutput(descriptor.output);
3014
2802
  const entry = {
3015
2803
  pluginType: "method",
3016
2804
  name: descriptor.name,
2805
+ descriptor,
3017
2806
  chain: [],
2807
+ ...pickDefined(descriptor, METHOD_META_KEYS),
3018
2808
  inputSchema: descriptor.inputSchema,
3019
2809
  skipInputValidation: descriptor.skipInputValidation,
3020
- // Derive the presentation type from the output mode when the author did
3021
- // not set one; an explicit meta.type (e.g. "create") still wins.
3022
- meta: out.type === "raw" || descriptor.meta?.type ? descriptor.meta : { ...descriptor.meta, type: out.type },
2810
+ outputSchema: descriptor.outputSchema,
3023
2811
  output: out,
3024
- // Replaced below; never called.
3025
- value: () => void 0
2812
+ // All three are replaced below, once the boundary they wrap exists; never
2813
+ // called in this placeholder form.
2814
+ value: () => void 0,
2815
+ internalValue: () => void 0,
2816
+ bindInternal: () => () => void 0
3026
2817
  };
2818
+ if (entry.type === void 0 && out.type !== "raw") entry.type = out.type;
3027
2819
  const callRun = (input, ctx) => {
3028
2820
  const callContext = ctx ?? rootCallContext();
3029
2821
  return descriptor.run({
@@ -3059,13 +2851,12 @@ function buildMethodEntries(descriptors, context, states) {
3059
2851
  }
3060
2852
  return next(input);
3061
2853
  };
3062
- const sdk = { context };
3063
2854
  const methodAnnotator = descriptor.annotator;
3064
2855
  const boundAnnotator = methodAnnotator ? (input) => methodAnnotator({ input }) : void 0;
3065
2856
  const outputPolicy = (callOptions) => {
3066
2857
  const core = resolveCoreOptions(context);
3067
2858
  return {
3068
- outputSchema: descriptor.meta?.outputSchema,
2859
+ outputSchema: descriptor.outputSchema,
3069
2860
  skipOutputValidation: descriptor.skipOutputValidation,
3070
2861
  skippedByCaller: readSkipOutputDataValidation(callOptions),
3071
2862
  includeOutputValidationDroppedPaths: core?.includeOutputValidationDroppedPaths,
@@ -3081,7 +2872,7 @@ function buildMethodEntries(descriptors, context, states) {
3081
2872
  (input, ctx) => callRun(stripFrameworkOnlyOptions(input, withheld), ctx)
3082
2873
  ),
3083
2874
  {
3084
- sdk,
2875
+ sdkContext: context,
3085
2876
  schema: descriptor.inputSchema,
3086
2877
  name: descriptor.name,
3087
2878
  frameworkOptions,
@@ -3092,8 +2883,8 @@ function buildMethodEntries(descriptors, context, states) {
3092
2883
  // (item mode's sibling); dropped paths surface as `[].x` in the page's
3093
2884
  // `meta`, unioned across items.
3094
2885
  finalizePage: (page, callOptions) => applyListOutputPolicy(page, outputPolicy(callOptions)),
3095
- getDeprecation: () => entry.meta?.deprecation,
3096
- getStability: () => entry.meta ? normalizeStability(entry.meta) : void 0
2886
+ getDeprecation: () => entry.deprecation,
2887
+ getStability: () => normalizeStability(entry)
3097
2888
  }
3098
2889
  );
3099
2890
  } else if (out.type === "item") {
@@ -3104,17 +2895,17 @@ function buildMethodEntries(descriptors, context, states) {
3104
2895
  entry.value = createFunction(
3105
2896
  fold(itemCore),
3106
2897
  {
3107
- sdk,
2898
+ sdkContext: context,
3108
2899
  schema: descriptor.inputSchema,
3109
2900
  name: descriptor.name,
3110
2901
  frameworkOptions,
3111
2902
  annotator: boundAnnotator,
3112
- getDeprecation: () => entry.meta?.deprecation,
3113
- getStability: () => entry.meta ? normalizeStability(entry.meta) : void 0
2903
+ getDeprecation: () => entry.deprecation,
2904
+ getStability: () => normalizeStability(entry)
3114
2905
  }
3115
2906
  );
3116
2907
  } else {
3117
- const rawValidates = descriptor.meta?.outputSchema !== void 0 && !descriptor.skipOutputValidation;
2908
+ const rawValidates = descriptor.outputSchema !== void 0 && !descriptor.skipOutputValidation;
3118
2909
  const validateRaw = (out2) => {
3119
2910
  const policy = outputPolicy(void 0);
3120
2911
  if (isPromiseLike(out2)) {
@@ -3130,7 +2921,7 @@ function buildMethodEntries(descriptors, context, states) {
3130
2921
  return rawValidates ? validateRaw(out2) : out2;
3131
2922
  },
3132
2923
  {
3133
- sdk,
2924
+ sdkContext: context,
3134
2925
  name: descriptor.name,
3135
2926
  schema: descriptor.skipInputValidation ? void 0 : descriptor.inputSchema,
3136
2927
  positional: descriptor.positional,
@@ -3138,9 +2929,9 @@ function buildMethodEntries(descriptors, context, states) {
3138
2929
  // The boundary reads the deprecation LIVE off the entry, so a
3139
2930
  // deprecation merged after build (defineMethodOverride, addPlugin)
3140
2931
  // fires too. Same for the stability level, normalized from the
3141
- // entry meta (declared level or legacy `experimental` boolean).
3142
- getDeprecation: () => entry.meta?.deprecation,
3143
- getStability: () => entry.meta ? normalizeStability(entry.meta) : void 0
2932
+ // descriptor, with an override's patch on top.
2933
+ getDeprecation: () => entry.deprecation,
2934
+ getStability: () => normalizeStability(entry)
3144
2935
  }
3145
2936
  );
3146
2937
  }
@@ -3159,8 +2950,8 @@ function buildMethodEntries(descriptors, context, states) {
3159
2950
  entry.internalValue = internalValue;
3160
2951
  entry.bindInternal = (opts) => bindInternalTwin({
3161
2952
  ...opts,
3162
- withContext: (context2) => {
3163
- return (...args) => canonicalValue(pack(args), context2);
2953
+ withContext: (ctx) => {
2954
+ return (...args) => canonicalValue(pack(args), ctx);
3164
2955
  },
3165
2956
  internalValue
3166
2957
  });
@@ -3170,29 +2961,53 @@ function buildMethodEntries(descriptors, context, states) {
3170
2961
  entry.internalValue = internalValue;
3171
2962
  entry.bindInternal = (opts) => bindInternalTwin({
3172
2963
  ...opts,
3173
- withContext: (context2) => (input) => canonicalValue(input, context2),
2964
+ withContext: (ctx) => (input) => canonicalValue(input, ctx),
3174
2965
  internalValue
3175
2966
  });
3176
2967
  }
3177
2968
  plugins[id] = entry;
3178
2969
  }
3179
2970
  }
3180
- function buildEagerArtifacts(descriptors, context, states) {
2971
+ function buildPropertyEntry(descriptor, id, context, states) {
2972
+ const plugins = context.plugins;
2973
+ const base = {
2974
+ pluginType: "property",
2975
+ name: descriptor.name,
2976
+ descriptor,
2977
+ ...pickDefined(descriptor, PROPERTY_META_KEYS),
2978
+ dynamicMembers: descriptor.dynamicMembers
2979
+ };
2980
+ if (descriptor.privileged) return { ...base, value: context };
2981
+ if (descriptor.get) {
2982
+ const get = descriptor.get;
2983
+ const importBindings = descriptor.importBindings;
2984
+ return {
2985
+ ...base,
2986
+ getValue: (callContext) => get({
2987
+ imports: buildImports({ plugins, importBindings, ctx: callContext }),
2988
+ state: states.get(id),
2989
+ callContext
2990
+ })
2991
+ };
2992
+ }
2993
+ return { ...base, value: descriptor.value };
2994
+ }
2995
+ function runSetup(descriptors, context, states) {
3181
2996
  const plugins = context.plugins;
3182
- const built = /* @__PURE__ */ new Set();
3183
- const building = /* @__PURE__ */ new Set();
3184
- const ensureBuilt = (id) => {
3185
- if (built.has(id)) return;
2997
+ const done = /* @__PURE__ */ new Set();
2998
+ const running = /* @__PURE__ */ new Set();
2999
+ const ensureSetup = (id) => {
3000
+ if (done.has(id)) return;
3186
3001
  const descriptor = descriptors.get(id);
3187
- if (!descriptor || descriptor.pluginType === "aggregate" || descriptor.pluginType === "legacy" || descriptor.pluginType === "method-override" || isStandIn(descriptor)) {
3188
- built.add(id);
3002
+ if (!descriptor || descriptor.pluginType === "aggregate" || descriptor.pluginType === "method-override" || isStandIn(descriptor)) {
3003
+ done.add(id);
3189
3004
  return;
3190
3005
  }
3191
- if (building.has(id)) {
3006
+ if (running.has(id)) {
3192
3007
  throw new Error(`createSdk: dependency cycle at "${id}".`);
3193
3008
  }
3194
- building.add(id);
3195
- for (const { id: depId } of descriptor.importBindings) ensureBuilt(depId);
3009
+ running.add(id);
3010
+ for (const { id: depId } of descriptor.importBindings) ensureSetup(depId);
3196
3011
  const recordDisposer = () => {
3197
3012
  const dispose = descriptor.dispose;
3198
3013
  if (!dispose) return;
@@ -3211,83 +3026,23 @@ function buildEagerArtifacts(descriptors, context, states) {
3211
3026
  })
3212
3027
  });
3213
3028
  };
3214
- if (descriptor.pluginType === "hook") {
3215
- states.set(
3216
- id,
3217
- descriptor.setup ? descriptor.setup({
3218
- imports: buildImports({
3219
- plugins,
3220
- importBindings: descriptor.importBindings
3221
- })
3222
- }) : void 0
3223
- );
3224
- recordDisposer();
3225
- building.delete(id);
3226
- built.add(id);
3227
- return;
3228
- }
3229
- if (descriptor.pluginType === "method") {
3230
- states.set(
3231
- id,
3232
- descriptor.setup ? descriptor.setup({
3233
- imports: buildImports({
3234
- plugins,
3235
- importBindings: descriptor.importBindings
3236
- })
3237
- }) : void 0
3238
- );
3239
- } else {
3240
- states.set(
3241
- id,
3242
- descriptor.setup ? descriptor.setup({
3243
- imports: buildImports({
3244
- plugins,
3245
- importBindings: descriptor.importBindings
3246
- })
3247
- }) : void 0
3248
- );
3249
- if (descriptor.privileged) {
3250
- plugins[id] = {
3251
- pluginType: "property",
3252
- name: descriptor.name,
3253
- value: context,
3254
- meta: descriptor.meta,
3255
- dynamicMembers: descriptor.dynamicMembers
3256
- };
3257
- } else if (descriptor.get) {
3258
- const get = descriptor.get;
3259
- const importBindings = descriptor.importBindings;
3260
- plugins[id] = {
3261
- pluginType: "property",
3262
- name: descriptor.name,
3263
- getValue: (callContext) => get({
3264
- imports: buildImports({
3265
- plugins,
3266
- importBindings,
3267
- ctx: callContext
3268
- }),
3269
- state: states.get(id),
3270
- callContext
3271
- }),
3272
- meta: descriptor.meta,
3273
- dynamicMembers: descriptor.dynamicMembers
3274
- };
3275
- } else {
3276
- plugins[id] = {
3277
- pluginType: "property",
3278
- name: descriptor.name,
3279
- value: descriptor.value,
3280
- meta: descriptor.meta,
3281
- dynamicMembers: descriptor.dynamicMembers
3282
- };
3283
- }
3029
+ states.set(
3030
+ id,
3031
+ descriptor.setup?.({
3032
+ imports: buildImports({
3033
+ plugins,
3034
+ importBindings: descriptor.importBindings
3035
+ })
3036
+ })
3037
+ );
3038
+ recordDisposer();
3039
+ if (descriptor.pluginType === "property") {
3284
3040
  assertDynamicMemberRoot(plugins[id]);
3285
3041
  }
3286
- recordDisposer();
3287
- building.delete(id);
3288
- built.add(id);
3042
+ running.delete(id);
3043
+ done.add(id);
3289
3044
  };
3290
- for (const id of descriptors.keys()) ensureBuilt(id);
3045
+ for (const id of descriptors.keys()) ensureSetup(id);
3291
3046
  }
3292
3047
  function resolvePlugin(sdk, ref) {
3293
3048
  const entry = getContext(sdk).plugins[ref.id];
@@ -3302,11 +3057,26 @@ function resolvePlugin(sdk, ref) {
3302
3057
  if (entry.pluginType === "property" && entry.getValue) {
3303
3058
  return entry.getValue();
3304
3059
  }
3305
- if (entry.pluginType === "method" && entry.internalValue) {
3306
- return entry.bindInternal?.({ frameworkOrigin: true }) ?? entry.internalValue;
3060
+ if (entry.pluginType === "method") {
3061
+ return entry.bindInternal({
3062
+ frameworkOrigin: true
3063
+ });
3064
+ }
3065
+ if (entry.pluginType === "hook" || entry.pluginType === "method-override") {
3066
+ throw new Error(
3067
+ `resolvePlugin: "${ref.id}" is a ${entry.pluginType}, which has no value to resolve. Resolve a method or property.`
3068
+ );
3307
3069
  }
3308
3070
  return entry.value;
3309
3071
  }
3072
+ function valueEntryOf(entry, id) {
3073
+ if (entry.pluginType === "hook" || entry.pluginType === "method-override") {
3074
+ throw new Error(
3075
+ `"${id}" is a ${entry.pluginType} and has no value to bind.`
3076
+ );
3077
+ }
3078
+ return entry;
3079
+ }
3310
3080
  var CoreDisposeError = class extends Error {
3311
3081
  constructor(errors) {
3312
3082
  super(`disposeSdk: ${errors.length} dispose callback(s) failed.`);
@@ -3338,9 +3108,20 @@ function resolveAggregates(descriptors, context) {
3338
3108
  if (descriptor.pluginType !== "aggregate") continue;
3339
3109
  const exports = {};
3340
3110
  for (const [binding, child] of Object.entries(descriptor.exports)) {
3341
- bindValue({ target: exports, key: binding, entry: plugins[child.id] });
3111
+ const entry = plugins[child.id];
3112
+ if (!entry || entry.pluginType === "hook" || entry.pluginType === "method-override") {
3113
+ throw new Error(
3114
+ `createSdk: export "${binding}" resolves to "${child.id}", which has no value. A defineHook or defineOverride belongs in \`imports\`, not \`exports\`: neither surfaces a value.`
3115
+ );
3116
+ }
3117
+ bindValue({ target: exports, key: binding, entry });
3342
3118
  }
3343
- plugins[id] = { pluginType: "aggregate", name: descriptor.name, exports };
3119
+ plugins[id] = {
3120
+ pluginType: "aggregate",
3121
+ name: descriptor.name,
3122
+ descriptor,
3123
+ exports
3124
+ };
3344
3125
  }
3345
3126
  }
3346
3127
  function assembleMiddleware(descriptors, context, states) {
@@ -3381,9 +3162,9 @@ function assembleHooks(descriptors, context, states) {
3381
3162
  const plugins = context.plugins;
3382
3163
  for (const id of topoOrder(descriptors)) {
3383
3164
  const descriptor = descriptors.get(id);
3384
- if (!descriptor || descriptor.pluginType !== "hook" || !descriptor.observe && !descriptor.annotator) {
3385
- continue;
3386
- }
3165
+ if (!descriptor || descriptor.pluginType !== "hook") continue;
3166
+ plugins[id] = { pluginType: "hook", name: descriptor.name, descriptor };
3167
+ if (!descriptor.observe && !descriptor.annotator) continue;
3387
3168
  const { observe, annotator } = descriptor;
3388
3169
  const state = states.get(id);
3389
3170
  const contributed = {};
@@ -3420,55 +3201,34 @@ function assembleHooks(descriptors, context, states) {
3420
3201
  }
3421
3202
  }
3422
3203
  function createSdk(root, options) {
3204
+ assertPluginArgument(root, { caller: "createSdk", asRoot: true });
3205
+ assertNotReservedKeys({
3206
+ keys: root.pluginType === "aggregate" ? Object.keys(root.exports) : [root.name],
3207
+ caller: "createSdk"
3208
+ });
3423
3209
  const context = {
3424
- plugins: {},
3425
- meta: {},
3210
+ plugins: /* @__PURE__ */ Object.create(null),
3426
3211
  hooks: {},
3427
- surface: {},
3212
+ surface: /* @__PURE__ */ Object.create(null),
3428
3213
  disposers: []
3429
3214
  };
3430
- if (root.pluginType === "legacy-merge") {
3431
- const { legacy, plugin } = root;
3432
- const collectRoot = {
3433
- pluginType: "aggregate",
3434
- name: root.name,
3435
- id: `${root.id}:merge`,
3436
- imports: [legacy, plugin],
3437
- importBindings: [],
3438
- exports: {}
3439
- };
3440
- const plugins2 = materialize(
3441
- collectPlugins(collectRoot, void 0, options?.configuration),
3442
- context
3443
- );
3444
- const legacyExports = plugins2[legacy.id].exports;
3445
- let pluginSurface;
3446
- if (plugin.pluginType === "aggregate") {
3447
- pluginSurface = plugins2[plugin.id].exports;
3448
- } else {
3449
- pluginSurface = {};
3450
- bindValue({
3451
- target: pluginSurface,
3452
- key: plugin.name,
3453
- entry: plugins2[plugin.id]
3454
- });
3455
- }
3456
- for (const key of Object.keys(legacyExports)) context.surface[key] = key;
3457
- if (plugin.pluginType === "aggregate") {
3458
- recordExportSurface(context, plugin.exports);
3459
- } else {
3460
- context.surface[plugin.name] = plugin.id;
3461
- }
3462
- return buildSurface(context, legacyExports, pluginSurface);
3463
- }
3464
3215
  const plugins = materialize(
3465
- collectPlugins(root, void 0, options?.configuration),
3216
+ collectPlugins({
3217
+ root,
3218
+ caller: "createSdk",
3219
+ applied: context.plugins,
3220
+ configuration: options?.configuration
3221
+ }),
3466
3222
  context
3467
3223
  );
3468
3224
  if (root.pluginType === "method" || root.pluginType === "property") {
3469
3225
  context.surface[root.name] = root.id;
3470
3226
  const sdk = buildSurface(context);
3471
- bindValue({ target: sdk, key: root.name, entry: plugins[root.id] });
3227
+ bindValue({
3228
+ target: sdk,
3229
+ key: root.name,
3230
+ entry: valueEntryOf(plugins[root.id], root.id)
3231
+ });
3472
3232
  return sdk;
3473
3233
  }
3474
3234
  if (root.pluginType === "aggregate")
@@ -3479,54 +3239,67 @@ function addModelPlugin(sdk, plugin, options = {}) {
3479
3239
  const override = options.override === true;
3480
3240
  const context = getContext(sdk);
3481
3241
  const surfaceKeys = plugin.pluginType === "aggregate" ? Object.keys(plugin.exports) : plugin.pluginType === "hook" ? [] : [plugin.name];
3482
- checkRootKeyCollisions(sdk, surfaceKeys, override, "addPlugin");
3483
- const materialized = new Set(Object.keys(context.plugins));
3484
- if (override && materialized.has(plugin.id)) {
3242
+ checkRootKeyCollisions({
3243
+ target: sdk,
3244
+ keys: surfaceKeys,
3245
+ override,
3246
+ caller: "addPlugin"
3247
+ });
3248
+ if (override && surfaceKeys.length > 0 && plugin.id in context.plugins) {
3485
3249
  throw new Error(
3486
- `addPlugin: cannot override already-materialized plugin "${plugin.id}" on the incremental path. Rebuild the SDK with the replacement via createSdk.`
3250
+ `addPlugin: cannot override plugin "${plugin.id}", which is already applied to this SDK, on the incremental path. Rebuild the SDK with the replacement via createSdk.`
3487
3251
  );
3488
3252
  }
3489
- materialize(collectPlugins(plugin, materialized), context);
3490
- if (plugin.pluginType === "hook") return;
3491
- const entry = context.plugins[plugin.id];
3492
- if (entry.pluginType === "aggregate") {
3493
- Object.defineProperties(
3494
- sdk,
3495
- Object.getOwnPropertyDescriptors(entry.exports)
3253
+ const undo = snapshotGraph({ context, sdk, surfaceKeys });
3254
+ try {
3255
+ materialize(
3256
+ collectPlugins({
3257
+ root: plugin,
3258
+ caller: "addPlugin",
3259
+ applied: context.plugins
3260
+ }),
3261
+ context
3496
3262
  );
3497
- for (const [binding, child] of Object.entries(
3498
- plugin.exports
3499
- )) {
3500
- context.surface[binding] = child.id;
3263
+ if (plugin.pluginType === "hook") return;
3264
+ const entry = valueEntryOf(context.plugins[plugin.id], plugin.id);
3265
+ if (entry.pluginType === "aggregate") {
3266
+ Object.defineProperties(
3267
+ sdk,
3268
+ Object.getOwnPropertyDescriptors(entry.exports)
3269
+ );
3270
+ for (const [binding, child] of Object.entries(
3271
+ plugin.exports
3272
+ )) {
3273
+ context.surface[binding] = child.id;
3274
+ }
3275
+ } else {
3276
+ bindValue({ target: sdk, key: plugin.name, entry });
3277
+ context.surface[plugin.name] = plugin.id;
3501
3278
  }
3502
- } else {
3503
- bindValue({ target: sdk, key: plugin.name, entry });
3504
- context.surface[plugin.name] = plugin.id;
3279
+ } catch (cause) {
3280
+ try {
3281
+ undo();
3282
+ } catch (rollbackFailure) {
3283
+ const original = cause;
3284
+ if (original && typeof original === "object" && original.cause === void 0) {
3285
+ original.cause = rollbackFailure;
3286
+ }
3287
+ }
3288
+ throw cause;
3505
3289
  }
3506
3290
  }
3507
3291
  function addPlugin(sdk, plugin, options) {
3508
3292
  const record = sdk;
3293
+ assertPluginArgument(plugin, { caller: "addPlugin", asRoot: false });
3509
3294
  const context = getContext(record);
3510
3295
  try {
3511
- if (typeof plugin === "function") {
3512
- const contribution = applyPluginToSdk(
3513
- record,
3514
- plugin,
3515
- options ?? {}
3516
- );
3517
- if (context) {
3518
- mirrorLegacyRootKeys(context, contribution.rootKeys, contribution.meta);
3519
- for (const name of Object.keys(contribution.rootKeys)) {
3520
- context.surface[name] = name;
3521
- }
3522
- }
3523
- } else if (plugin.pluginType === "method-override") {
3524
- applyMethodOverride(context, plugin);
3296
+ if (plugin.pluginType === "method-override") {
3297
+ addOverridePlugin(context, plugin);
3525
3298
  } else {
3526
3299
  addModelPlugin(record, plugin, options ?? {});
3527
3300
  }
3528
3301
  } finally {
3529
- if (context) invalidateRegistryCache(context);
3302
+ invalidateRegistryCache(context);
3530
3303
  }
3531
3304
  }
3532
3305
 
@@ -4775,18 +4548,6 @@ function createController(sdk) {
4775
4548
  return { resolve, start: start2, step: step2, listMethods, getMethod, listChoices };
4776
4549
  }
4777
4550
 
4778
- // src/utils/core-plugin.ts
4779
- function createCorePlugin(options) {
4780
- logDeprecation(
4781
- "createCorePlugin() is deprecated. Inject the options under CORE_OPTIONS_ID via createSdk's configuration instead."
4782
- );
4783
- return () => ({
4784
- context: {
4785
- core: options
4786
- }
4787
- });
4788
- }
4789
-
4790
4551
  // src/transport/attempt-http-request.ts
4791
4552
  import { z as z10 } from "zod";
4792
4553
 
@@ -5221,20 +4982,13 @@ export {
5221
4982
  attemptHttpRequestPlugin,
5222
4983
  authorizeHttpRequestPlugin,
5223
4984
  canonicalInputSchema,
5224
- composePlugins,
5225
4985
  concatLists,
5226
4986
  concatPaginated,
5227
4987
  coreOptionsPluginRef,
5228
4988
  createAsyncContext,
5229
4989
  createController,
5230
4990
  createCoreError,
5231
- createCorePlugin,
5232
4991
  createDeprecationLogger,
5233
- createFunction,
5234
- createPaginatedFunction,
5235
- createPaginatedPluginMethod,
5236
- createPluginMethod,
5237
- createPluginStack,
5238
4992
  createPrefixedCursor,
5239
4993
  createSdk,
5240
4994
  createStabilityNoticeLogger,
@@ -5251,7 +5005,6 @@ export {
5251
5005
  defaultLogDeprecation,
5252
5006
  defineFormatter,
5253
5007
  defineHook,
5254
- defineLegacyMerge,
5255
5008
  defineMethod,
5256
5009
  defineMethodOverride,
5257
5010
  defineOverride,
@@ -5261,7 +5014,6 @@ export {
5261
5014
  dispatchHttpRequestPlugin,
5262
5015
  disposeSdk,
5263
5016
  fetchPlugin,
5264
- fromFunctionPlugin,
5265
5017
  getContext,
5266
5018
  getCoreErrorCause,
5267
5019
  getCoreErrorCode,