@zapier/kitcore 0.20.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,902 +1,913 @@
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
- }
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
584
- };
585
- return encodeBase64(JSON.stringify(envelope));
586
- }
587
- function decodeConcatCursor(incoming) {
588
- if (!incoming) {
589
- return { index: 0, cursor: void 0 };
590
- }
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 };
595
- }
596
- } catch {
597
- }
598
- return { index: 0, cursor: incoming };
599
429
  }
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;
617
- }
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
- }
623
- return { data: [] };
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.
624
447
  };
625
- const result = await paginateBuffered(pageFunction, {
626
- pageSize,
627
- cursor
628
- }).next();
629
- return result.done ? { data: [] } : result.value;
630
448
  }
631
- function concatPaginated({
632
- sources,
633
- pageSize,
634
- cursor
449
+ function declareDefault({
450
+ plugin
635
451
  }) {
636
- logDeprecation("concatPaginated() is deprecated. Use concatLists() instead.");
637
- return concatLists({ sources, pageSize, cursor });
452
+ return { ...plugin, defaultSource: plugin };
638
453
  }
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]() };
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
+ };
644
472
  }
645
-
646
- // src/utils/promise-utils.ts
647
- function isPromiseLike(value) {
648
- return value !== null && typeof value === "object" && typeof value.then === "function";
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
+ };
649
485
  }
650
-
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}`;
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"
658
493
  });
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
- );
671
- }
672
- return result.data;
673
- };
674
- function createValidator(schema, { adaptError } = {}) {
675
- return function validateFn(input) {
676
- return parseOrThrow(schema, input, { adaptError });
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)
677
508
  };
678
509
  }
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);
711
- }
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);
718
- }
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);
719
515
  }
720
- return [...refused];
516
+ return out;
721
517
  }
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;
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);
537
+ }
726
538
  }
727
- return next;
539
+ return out;
728
540
  }
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];
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);
561
+ }
741
562
  }
742
- framework = parseOrThrow2(CallFrameworkOptionsSchema, present, adaptError);
743
563
  }
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() };
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
575
+ };
576
+ }
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
+ }
748
583
  }
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);
584
+ const kept = {};
585
+ for (const [binding, child] of Object.entries(source.exports)) {
586
+ if (!omitSet.has(binding)) kept[binding] = child;
754
587
  }
755
- return { framework, domain: retry.data, supplied: new Set(refused) };
756
- }
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) };
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
+ };
764
597
  }
765
- function withheldFromRun(policy) {
766
- return new Set(policy.claims.filter((key) => !policy.injects.includes(key)));
598
+
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(" ");
767
605
  }
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)));
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;
773
612
  }
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);
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";
778
619
  }
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}`;
783
- });
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
- );
620
+ function pluralizeLastWord(title) {
621
+ const words = title.split(" ");
622
+ return [...words.slice(0, -1), pluralize(words[words.length - 1])].join(" ");
793
623
  }
794
624
 
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;
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];
805
630
  }
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
- };
815
- }
816
-
817
- // src/utils/method-scope.ts
818
- var scope = createAsyncContext();
819
- function getCurrentScope() {
820
- return scope.get();
821
- }
822
- function getCurrentDepth() {
823
- return getCurrentScope()?.depth ?? 0;
631
+ return schema;
824
632
  }
825
- function isNestedMethodCall() {
826
- if (!scope.available) return true;
827
- const store = scope.get();
828
- return store !== void 0 && store.depth > 0;
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;
644
+ }
645
+ }
646
+ return { inner, required };
829
647
  }
830
- var observerReentrancy = 0;
831
- function runIsolatedObserver(fn) {
832
- observerReentrancy++;
833
- try {
834
- fn();
835
- } catch {
836
- } finally {
837
- observerReentrancy--;
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;
838
654
  }
655
+ return void 0;
839
656
  }
840
- function isInsideObserver() {
841
- return observerReentrancy > 0;
657
+ function getOutputSchema(inputSchema) {
658
+ return inputSchema._zod.def.outputSchema;
842
659
  }
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);
660
+ function withOutputSchema(inputSchema, outputSchema) {
661
+ Object.assign(inputSchema._zod.def, {
662
+ outputSchema
663
+ });
664
+ return inputSchema;
847
665
  }
848
- var runWithTelemetryContext = runInMethodScope;
849
- var isTelemetryNested = isNestedMethodCall;
850
-
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;
666
+ function withResolver(schema, config) {
667
+ schema._zod.def.resolverMeta = config;
668
+ return schema;
855
669
  }
856
- function generateCallId() {
857
- try {
858
- const webCrypto = globalThis.crypto;
859
- if (webCrypto?.randomUUID) {
860
- return webCrypto.randomUUID();
861
- }
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("-");
670
+ function getSchemaDescription(schema) {
671
+ return schema.description;
672
+ }
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;
875
679
  }
876
- } catch {
877
680
  }
878
- return null;
681
+ return descriptions;
879
682
  }
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
- };
683
+ function withPositional(schema) {
684
+ Object.assign(schema._zod.def, {
685
+ positionalMeta: { positional: true }
686
+ });
687
+ return schema;
890
688
  }
891
- function childCallContext(parent) {
689
+ function schemaHasPositionalMeta(schema) {
690
+ return "positionalMeta" in schema._zod.def;
691
+ }
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;
703
+ }
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;
712
+ }
713
+ function openEnum(values, description) {
714
+ return z.union([z.enum(values), z.string()]).describe(description);
715
+ }
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";
727
+ }
728
+ return meta.experimental ? "experimental" : "stable";
729
+ }
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})`;
737
+ }
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);
892
743
  return {
893
- callId: parent.callId,
894
- depth: parent.depth + 1,
895
- annotations: {},
896
- callOrigin: parent.callOrigin,
897
- [CALL_CONTEXT_BRAND]: true
744
+ key: def.key,
745
+ title,
746
+ titlePlural: def.titlePlural ?? pluralizeLastWord(title)
898
747
  };
899
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 };
825
+ }
826
+
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;
831
+ }
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
+ );
847
+ }
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;
858
+ }
859
+ entries.push(entry);
860
+ if (isDescribed(entry)) sources[binding] = entry;
861
+ }
862
+ const surfaceBindings = new Set(Object.keys(context.surface));
863
+ for (const entry of entries) {
864
+ foldDynamicMembers(entry, surfaceBindings, sources);
865
+ }
866
+ return sources;
867
+ }
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);
877
+ }
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
+ });
910
+ }
900
911
 
901
912
  // src/utils/core-options.ts
902
913
  function defaultLogDeprecation({
@@ -920,1380 +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
1403
  }
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
- }
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;
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) };
1699
1484
  }
1700
- function formatDynamicMemberName(path) {
1701
- return path.map((seg) => typeof seg === "string" ? seg : `{${seg.param}}`).join(".");
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) };
1702
1492
  }
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
- });
1493
+ function withheldFromRun(policy) {
1494
+ return new Set(policy.claims.filter((key) => !policy.injects.includes(key)));
1495
+ }
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)));
1719
1501
  }
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
- };
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);
1747
1506
  }
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(", ")}.`
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: a consumer that imports it still passes `createSdk`'s
1902
- // completeness check unprovided. The contract is still carried, so a
1903
- // provider that DOES appear under the id is checked against it. The
1904
- // `optional: true` literal drives `PluginSurface` to type the binding
1905
- // `| undefined`.
1906
- };
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;
1907
1583
  }
1908
- function defineProperty(config, refConfig) {
1909
- const cfg = refConfig === void 0 ? config : {
1910
- ...refConfig,
1911
- name: config.name,
1912
- namespace: config.namespace
1913
- };
1914
- const deps = normalizeImports(cfg.imports);
1915
- return {
1916
- pluginType: "property",
1917
- name: cfg.name,
1918
- namespace: cfg.namespace,
1919
- id: makeId(cfg.name, cfg.namespace),
1920
- imports: deps.plugins,
1921
- importBindings: deps.bindings,
1922
- setup: cfg.setup,
1923
- dispose: cfg.dispose,
1924
- value: cfg.value,
1925
- get: cfg.get,
1926
- meta: collectLeafMeta(cfg),
1927
- dynamicMembers: collectDynamicMembers(cfg.dynamicMembers)
1928
- };
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;
1929
1607
  }
1930
- function declareProperty(config) {
1931
- const { name, namespace } = parseId(config.id);
1608
+ function rootCallContext({
1609
+ callOrigin = "surface"
1610
+ } = {}) {
1932
1611
  return {
1933
- pluginType: "property",
1934
- name,
1935
- namespace,
1936
- id: makeId(name, namespace),
1937
- standIn: true,
1938
- imports: [],
1939
- importBindings: []
1612
+ callId: generateCallId(),
1613
+ depth: 0,
1614
+ annotations: {},
1615
+ callOrigin,
1616
+ [CALL_CONTEXT_BRAND]: true
1940
1617
  };
1941
1618
  }
1942
- function declareOptionalProperty(config) {
1943
- const { name, namespace } = parseId(config.id);
1619
+ function childCallContext(parent) {
1944
1620
  return {
1945
- pluginType: "property",
1946
- name,
1947
- namespace,
1948
- id: makeId(name, namespace),
1949
- standIn: true,
1950
- optional: true,
1951
- imports: [],
1952
- importBindings: []
1953
- // Requires nothing: a consumer that imports it still passes `createSdk`'s
1954
- // completeness check unprovided. The contract is the binding a consumer
1955
- // sees, `TValue | undefined`, which is also what a by-reference provider
1956
- // (`defineProperty(ref, { value })`) is allowed to pass. A consumer of an
1957
- // optional reference has to handle the absent case either way, so an
1958
- // explicit `undefined` breaks nothing a narrower contract would protect.
1621
+ callId: parent.callId,
1622
+ depth: parent.depth + 1,
1623
+ annotations: {},
1624
+ callOrigin: parent.callOrigin,
1625
+ [CALL_CONTEXT_BRAND]: true
1959
1626
  };
1960
1627
  }
1961
- function declareDefault({
1962
- plugin
1963
- }) {
1964
- return { ...plugin, defaultSource: plugin };
1965
- }
1966
- function defineHook(config) {
1967
- const deps = normalizeImports(config.imports);
1968
- return {
1969
- pluginType: "hook",
1970
- name: config.name,
1971
- namespace: config.namespace,
1972
- id: makeId(config.name, config.namespace),
1973
- imports: deps.plugins,
1974
- importBindings: deps.bindings,
1975
- setup: config.setup,
1976
- dispose: config.dispose,
1977
- wrap: config.wrap,
1978
- observe: config.observe,
1979
- annotator: config.annotator
1980
- };
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;
1981
1634
  }
1982
- function declarePlugin(config) {
1983
- const { name, namespace } = parseId(config.id);
1984
- return {
1985
- pluginType: "aggregate",
1986
- name,
1987
- namespace,
1988
- id: makeId(name, namespace, "aggregate"),
1989
- standIn: true,
1990
- imports: [],
1991
- importBindings: [],
1992
- exports: normalizeExports(config.exports)
1993
- };
1635
+ var INTERNAL_CALL = Symbol("kitcore.internalCall");
1636
+ function resolveCallContext(secondArg) {
1637
+ return isCallContext(secondArg) ? secondArg : rootCallContext();
1994
1638
  }
1995
- function definePlugin(fnOrConfig) {
1996
- if (typeof fnOrConfig === "function") {
1997
- logDeprecation(
1998
- "definePlugin(fn) (the function form) is deprecated. Author plugins with defineMethod/defineProperty/definePlugin({ ... }) instead."
1999
- );
2000
- 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
+ }
2001
1655
  }
2002
- const config = fnOrConfig;
2003
- const deps = normalizeImports(config.imports);
2004
- return {
2005
- pluginType: "aggregate",
2006
- name: config.name,
2007
- namespace: config.namespace,
2008
- id: makeId(config.name, config.namespace, "aggregate"),
2009
- // A re-export synthetic (`selectExports` / `omitExports`) is flattened by
2010
- // `normalizeExports` into bare bindings, which drops its own `imports:
2011
- // [source]`. That edge is how `omitExports` keeps an omitted (unbound) leaf
2012
- // materialized + addressable by id, so preserve every exported aggregate's
2013
- // imports as extra reachability edges here (bindings unaffected).
2014
- imports: [...deps.plugins, ...exportedAggregateImports(config.exports)],
2015
- importBindings: deps.bindings,
2016
- exports: normalizeExports(config.exports)
2017
- };
2018
- }
2019
- function exportedAggregateImports(exports) {
2020
- if (!exports) return [];
2021
- const out = [];
2022
- for (const element of exports) {
2023
- if (element.pluginType === "aggregate") out.push(...element.imports);
1656
+ try {
1657
+ Object.assign(context.annotations, methodAnnotator?.(input));
1658
+ } catch {
2024
1659
  }
2025
- return out;
2026
1660
  }
2027
- function normalizeExports(exports) {
2028
- if (!exports) return {};
2029
- const out = {};
2030
- const add = (binding, leaf) => {
2031
- const existing = out[binding];
2032
- if (existing && existing.id !== leaf.id) {
2033
- throw new Error(
2034
- `definePlugin: duplicate export binding "${binding}". Two different plugins ("${existing.id}" and "${leaf.id}") bind the same name; wrap one in selectExports to rename it.`
2035
- );
2036
- }
2037
- 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
2038
1669
  };
2039
- for (const element of exports) {
2040
- if (element.pluginType === "aggregate") {
2041
- for (const [binding, child] of Object.entries(element.exports)) {
2042
- add(binding, child);
2043
- }
2044
- } else {
2045
- add(element.name, element);
2046
- }
2047
- }
2048
- return out;
1670
+ const handler = resolveCoreOptions(context)?.logDeprecation ?? defaultLogDeprecation;
1671
+ runIsolatedObserver(() => handler(warning));
2049
1672
  }
2050
-
2051
- // src/model/exports.ts
2052
- var selectSeq = 0;
2053
- function selectExports(source, ...specs) {
2054
- const selected = {};
2055
- const pick = (binding, fromName) => {
2056
- const child = source.exports[fromName];
2057
- if (!child) {
2058
- throw new Error(
2059
- `selectExports: "${source.id}" has no export "${fromName}".`
2060
- );
2061
- }
2062
- 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
2063
1681
  };
2064
- for (const spec of specs) {
2065
- if (typeof spec === "string") {
2066
- pick(spec, spec);
2067
- } else {
2068
- for (const [newName, fromName] of Object.entries(spec)) {
2069
- 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);
2070
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
+ });
2071
1766
  }
2072
- }
2073
- const id = `${source.id}#select:${selectSeq++}`;
2074
- return {
2075
- pluginType: "aggregate",
2076
- name: makeId(`select`, source.name, "aggregate"),
2077
- id,
2078
- // Depend on the source so it is materialized; the selected bindings resolve
2079
- // to the source's own leaves (kept identity).
2080
- imports: [source],
2081
- importBindings: [],
2082
- exports: selected
2083
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
2084
1767
  };
1768
+ return namedFunctions[functionName];
2085
1769
  }
2086
- function omitExports(source, omit) {
2087
- const omitSet = new Set(omit);
2088
- for (const name of omit) {
2089
- if (!(name in source.exports)) {
2090
- 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);
2091
1786
  }
2092
- }
2093
- const kept = {};
2094
- for (const [binding, child] of Object.entries(source.exports)) {
2095
- if (!omitSet.has(binding)) kept[binding] = child;
2096
- }
2097
- return {
2098
- pluginType: "aggregate",
2099
- name: makeId(`omit`, source.name, "aggregate"),
2100
- id: `${source.id}#omit:${selectSeq++}`,
2101
- imports: [source],
2102
- importBindings: [],
2103
- exports: kept
2104
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
2105
- };
2106
- }
2107
-
2108
- // src/model/legacy.ts
2109
- function fromFunctionPlugin(fn, config) {
2110
- logDeprecation(
2111
- "fromFunctionPlugin() is deprecated. Author plugins with defineMethod/definePlugin instead."
2112
- );
2113
- return {
2114
- pluginType: "legacy",
2115
- name: config.name,
2116
- namespace: config.namespace,
2117
- id: makeId(config.name, config.namespace, "aggregate"),
2118
- imports: [],
2119
- importBindings: [],
2120
- run: fn
2121
- };
2122
- }
2123
- function defineLegacyMerge(args) {
2124
- logDeprecation(
2125
- "defineLegacyMerge() is deprecated. Build directly with createSdk(root, { configuration }) instead."
2126
- );
2127
- return {
2128
- pluginType: "legacy-merge",
2129
- name: args.name,
2130
- namespace: args.namespace,
2131
- id: makeId(args.name, args.namespace, "aggregate"),
2132
- legacy: fromFunctionPlugin(args.legacy, {
2133
- name: args.name,
2134
- namespace: args.namespace
2135
- }),
2136
- 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
+ });
2137
1844
  };
2138
1845
  }
2139
- function legacyGraphEntry(name, value, pluginMeta) {
2140
- const { inputSchema, ...rest } = pluginMeta ?? {};
2141
- const meta = Object.keys(rest).length ? rest : void 0;
2142
- if (typeof value === "function") {
2143
- return {
2144
- pluginType: "method",
2145
- name,
2146
- value,
2147
- chain: [],
2148
- ...inputSchema ? { inputSchema } : {},
2149
- ...meta ? { meta } : {}
2150
- };
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;
2151
1852
  }
2152
- return { pluginType: "property", name, value, ...meta ? { meta } : {} };
1853
+ return Object.keys(page).every((k) => k === "data" || k === "nextCursor");
2153
1854
  }
2154
-
2155
- // src/model/builtins.ts
2156
- import { z as z3 } from "zod";
2157
-
2158
- // src/model/registry-support.ts
2159
- function adaptLegacyFormatter(legacy, sdk) {
2160
- const legacyFetch = legacy.fetch;
2161
- return {
2162
- getContext: legacyFetch ? async ({ items, input, context }) => {
2163
- let ctx = context;
2164
- for (const item of items) {
2165
- 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);
2166
1874
  }
2167
- return ctx;
2168
- } : void 0,
2169
- format: ({ item, context }) => legacy.format(item, context)
1875
+ }
2170
1876
  };
1877
+ return namedFunctions[functionName];
2171
1878
  }
2172
- function normalizeFormatter(entry, sdk) {
2173
- if (entry.pluginType !== "method") return void 0;
2174
- if (entry.formatter) return entry.formatter;
2175
- const legacy = entry.meta?.formatter;
2176
- return legacy ? adaptLegacyFormatter(legacy, sdk) : void 0;
2177
- }
2178
- function normalizeResolvers(entry) {
2179
- if (entry.pluginType !== "method") return void 0;
2180
- return entry.resolvers;
2181
- }
2182
- function methodPositional(entry) {
2183
- if (entry.pluginType !== "method") return void 0;
2184
- return entry.positional;
2185
- }
2186
- function pluginEntryMeta(entry) {
2187
- if (entry.pluginType === "method" && entry.meta) {
2188
- return entry.inputSchema ? { ...entry.meta, inputSchema: entry.inputSchema } : entry.meta;
2189
- }
2190
- if (entry.pluginType === "property" && entry.meta) return entry.meta;
2191
- return void 0;
2192
- }
2193
- function foldDynamicMembers(entry, surfaceBindings, meta) {
2194
- if (entry.pluginType !== "property" || !entry.dynamicMembers) return;
2195
- for (const member of entry.dynamicMembers) {
2196
- if (!surfaceBindings.has(member.rootBinding)) {
2197
- throw new Error(
2198
- `dynamicMember "${member.name}": its root "${member.rootBinding}" is not a surfaced member. A dynamic member's path must start with a real binding.`
2199
- );
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
+ });
2200
2011
  }
2201
- meta[member.name] = member.meta;
2202
- }
2203
- }
2204
- function collectSurfaceProjection(context, formatterSdk) {
2205
- const meta = {};
2206
- const entries = {};
2207
- for (const [binding, id] of Object.entries(context.surface)) {
2208
- const entry = context.plugins[id];
2209
- if (!entry || entry.pluginType === "aggregate") continue;
2210
- entries[binding] = entry;
2211
- const m = pluginEntryMeta(entry);
2212
- if (m) meta[binding] = m;
2213
- }
2214
- const surfaceBindings = new Set(Object.keys(context.surface));
2215
- for (const entry of Object.values(entries)) {
2216
- foldDynamicMembers(entry, surfaceBindings, meta);
2217
- }
2218
- const formatters = {};
2219
- const resolvers = {};
2220
- const positional = {};
2221
- const skipInputValidation = {};
2222
- for (const [binding, entry] of Object.entries(entries)) {
2223
- const f = normalizeFormatter(entry, formatterSdk);
2224
- if (f) formatters[binding] = f;
2225
- const r = normalizeResolvers(entry);
2226
- if (r) resolvers[binding] = r;
2227
- const p = methodPositional(entry);
2228
- if (p) positional[binding] = p;
2229
- if (entry.pluginType === "method" && entry.skipInputValidation)
2230
- skipInputValidation[binding] = true;
2231
- }
2232
- return { meta, formatters, resolvers, positional, skipInputValidation };
2233
- }
2234
- var REGISTRY_CACHE = Symbol.for("kitcore.registryCache");
2235
- function freezeContainers(registry) {
2236
- Object.freeze(registry.functions);
2237
- for (const category of registry.categories) {
2238
- Object.freeze(category.functions);
2239
- Object.freeze(category);
2240
- }
2241
- Object.freeze(registry.categories);
2242
- return Object.freeze(registry);
2243
- }
2244
- function getCachedRegistry(context, packageFilter) {
2245
- const key = packageFilter ?? "";
2246
- const caching = context;
2247
- let byFilter = caching[REGISTRY_CACHE];
2248
- if (!byFilter) {
2249
- byFilter = /* @__PURE__ */ new Map();
2250
- caching[REGISTRY_CACHE] = byFilter;
2251
- }
2252
- let registry = byFilter.get(key);
2253
- if (!registry) {
2254
- registry = freezeContainers(buildSurfaceRegistry(context, packageFilter));
2255
- byFilter.set(key, registry);
2256
- }
2257
- return registry;
2258
- }
2259
- function invalidateRegistryCache(context) {
2260
- delete context[REGISTRY_CACHE];
2261
- }
2262
- function buildSurfaceRegistry(context, packageFilter) {
2263
- const surface = {};
2264
- for (const [binding, id] of Object.entries(context.surface)) {
2265
- const entry = context.plugins[id];
2266
- if (!entry || entry.pluginType === "aggregate") continue;
2267
- surface[binding] = entry.pluginType === "property" && entry.getValue ? entry.getValue() : entry.value;
2268
- }
2269
- const projection = collectSurfaceProjection(context, surface);
2270
- Object.assign(projection.meta, context.meta);
2271
- return buildRegistry({
2272
- sdk: surface,
2273
- ...projection,
2274
- packageFilter
2275
- });
2012
+ };
2013
+ return namedFunctions[functionName];
2276
2014
  }
2277
2015
 
2278
- // src/model/builtins.ts
2279
- var coreOptionsPluginRef = declareOptionalProperty({ id: CORE_OPTIONS_ID });
2280
- var dangerousContextPlugin = {
2281
- pluginType: "property",
2282
- name: "context",
2283
- namespace: "kitcore",
2284
- id: "kitcore/context",
2285
- imports: [],
2286
- importBindings: [],
2287
- privileged: true
2288
- };
2289
- var getRegistryPlugin = defineMethod({
2290
- name: "getRegistry",
2291
- namespace: "kitcore",
2292
- imports: [dangerousContextPlugin],
2293
- inputSchema: z3.object({ package: z3.string().optional() }).optional(),
2294
- run: ({ imports, input }) => getCachedRegistry(imports.context, input?.package)
2295
- });
2296
-
2297
2016
  // src/utils/output-policy.ts
2298
2017
  function isRecord2(value) {
2299
2018
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -2448,6 +2167,62 @@ function applyListOutputPolicy(page, policy) {
2448
2167
  return next;
2449
2168
  }
2450
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
+
2451
2226
  // src/model/materialize.ts
2452
2227
  var FRAMEWORK_CONFIGURATION_IDS = /* @__PURE__ */ new Set([
2453
2228
  CORE_OPTIONS_ID
@@ -2458,6 +2233,19 @@ function normalizeOutput(output) {
2458
2233
  return output;
2459
2234
  }
2460
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
+ }
2461
2249
  return sdk[CONTEXT];
2462
2250
  }
2463
2251
  function assertDynamicMemberRoot(entry) {
@@ -2469,11 +2257,9 @@ function assertDynamicMemberRoot(entry) {
2469
2257
  );
2470
2258
  }
2471
2259
  function getRegistry(sdk, packageFilter) {
2472
- if (typeof sdk !== "object" && typeof sdk !== "function" || sdk === null)
2473
- throw createNoRegistryError();
2474
- const context = getContext(sdk);
2260
+ const context = tryGetContext(sdk);
2475
2261
  if (context?.surface) return getCachedRegistry(context, packageFilter);
2476
- const surfaced = sdk.getRegistry;
2262
+ const surfaced = sdk?.getRegistry;
2477
2263
  if (typeof surfaced === "function") {
2478
2264
  return surfaced.call(
2479
2265
  sdk,
@@ -2483,9 +2269,10 @@ function getRegistry(sdk, packageFilter) {
2483
2269
  throw createNoRegistryError();
2484
2270
  }
2485
2271
  function createNoRegistryError() {
2486
- return new Error(
2487
- "getRegistry: sdk has no kitcore context and no surfaced getRegistry()."
2488
- );
2272
+ return createCoreError({
2273
+ code: CoreErrorCode.NoSdkContext,
2274
+ message: "getRegistry: sdk has no kitcore context and no surfaced getRegistry()."
2275
+ });
2489
2276
  }
2490
2277
  function isResolverRef(value) {
2491
2278
  return "ref" in value;
@@ -2534,8 +2321,14 @@ function edgesOf(plugin) {
2534
2321
  }
2535
2322
  return plugin.imports;
2536
2323
  }
2537
- function isStandIn(plugin) {
2538
- 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
+ );
2539
2332
  }
2540
2333
  function isDefault(plugin) {
2541
2334
  return (plugin.pluginType === "method" || plugin.pluginType === "property") && plugin.defaultSource !== void 0;
@@ -2553,12 +2346,25 @@ function topoOrder(descriptors) {
2553
2346
  for (const id of descriptors.keys()) visit(id);
2554
2347
  return order;
2555
2348
  }
2556
- function collectPlugins(root, materialized = /* @__PURE__ */ new Set(), configuration) {
2349
+ function collectPlugins({
2350
+ root,
2351
+ caller,
2352
+ applied,
2353
+ configuration
2354
+ }) {
2557
2355
  const rank = (plugin) => isStandIn(plugin) ? 0 : isDefault(plugin) ? 1 : 2;
2558
2356
  const allNodes = [];
2559
2357
  const seen = /* @__PURE__ */ new Set();
2560
2358
  const collect = (plugin) => {
2561
- 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;
2562
2368
  seen.add(plugin);
2563
2369
  allNodes.push(plugin);
2564
2370
  for (const edge of edgesOf(plugin)) collect(edge);
@@ -2623,7 +2429,7 @@ function collectPlugins(root, materialized = /* @__PURE__ */ new Set(), configur
2623
2429
  const winnerRank = rank(winner);
2624
2430
  if (winnerRank === 2) {
2625
2431
  throw new Error(
2626
- `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.`
2627
2433
  );
2628
2434
  }
2629
2435
  if (winnerRank === 1) {
@@ -2683,7 +2489,7 @@ function collectPlugins(root, materialized = /* @__PURE__ */ new Set(), configur
2683
2489
  if (isStandIn(plugin)) {
2684
2490
  if ("optional" in plugin && plugin.optional) continue;
2685
2491
  throw new Error(
2686
- `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.`
2687
2493
  );
2688
2494
  }
2689
2495
  }
@@ -2691,7 +2497,7 @@ function collectPlugins(root, materialized = /* @__PURE__ */ new Set(), configur
2691
2497
  const winner = byId.get(id);
2692
2498
  if (winner && isDefault(winner)) {
2693
2499
  throw new Error(
2694
- `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.`
2695
2501
  );
2696
2502
  }
2697
2503
  }
@@ -2713,7 +2519,7 @@ function bindValue({
2713
2519
  configurable: true
2714
2520
  });
2715
2521
  } else {
2716
- 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;
2717
2523
  Object.defineProperty(target, key, {
2718
2524
  value,
2719
2525
  writable: true,
@@ -2749,10 +2555,15 @@ function buildImports({
2749
2555
  });
2750
2556
  continue;
2751
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
+ }
2752
2563
  bindValue({
2753
2564
  target: imports,
2754
2565
  key: binding,
2755
- entry,
2566
+ entry: valueEntryOf(entry, id),
2756
2567
  bindMode: "internal",
2757
2568
  ctx,
2758
2569
  frameworkOrigin
@@ -2774,14 +2585,6 @@ function bindInternalTwin({
2774
2585
  }
2775
2586
  return internalValue;
2776
2587
  }
2777
- function mirrorLegacyRootKeys(context, rootKeys, meta) {
2778
- const exports = {};
2779
- for (const [name, value] of Object.entries(rootKeys)) {
2780
- context.plugins[name] = legacyGraphEntry(name, value, meta[name]);
2781
- exports[name] = value;
2782
- }
2783
- return exports;
2784
- }
2785
2588
  function recordExportSurface(context, exports) {
2786
2589
  for (const [binding, child] of Object.entries(exports)) {
2787
2590
  context.surface[binding] = child.id;
@@ -2789,9 +2592,8 @@ function recordExportSurface(context, exports) {
2789
2592
  }
2790
2593
  function materialize(descriptors, context) {
2791
2594
  const states = /* @__PURE__ */ new Map();
2792
- runLegacyPass(descriptors, context);
2793
- buildMethodEntries(descriptors, context, states);
2794
- buildEagerArtifacts(descriptors, context, states);
2595
+ buildLeafEntries(descriptors, context, states);
2596
+ runSetup(descriptors, context, states);
2795
2597
  bindAttachments(descriptors, context);
2796
2598
  resolveAggregates(descriptors, context);
2797
2599
  assembleMiddleware(descriptors, context, states);
@@ -2803,22 +2605,35 @@ function applyMethodOverride(context, override) {
2803
2605
  const entry = context.plugins[override.target];
2804
2606
  if (!entry) {
2805
2607
  throw new Error(
2806
- `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.`
2807
2609
  );
2808
2610
  }
2809
2611
  if (entry.pluginType !== "method") {
2810
2612
  throw new Error(
2811
- `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.`
2812
2614
  );
2813
2615
  }
2814
- 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);
2815
2626
  }
2816
2627
  function applyMethodOverrides(descriptors, context) {
2817
2628
  for (const descriptor of descriptors.values()) {
2818
2629
  if (descriptor.pluginType !== "method-override") continue;
2819
2630
  applyMethodOverride(context, descriptor);
2631
+ context.plugins[descriptor.id] = overrideEntry(descriptor);
2820
2632
  }
2821
2633
  }
2634
+ function overrideEntry(descriptor) {
2635
+ return { pluginType: "method-override", name: descriptor.name, descriptor };
2636
+ }
2822
2637
  function bindResolver(resolver, plugins) {
2823
2638
  switch (resolver.type) {
2824
2639
  case "static":
@@ -2888,7 +2703,9 @@ function bindResolver(resolver, plugins) {
2888
2703
  frameworkOrigin: true
2889
2704
  });
2890
2705
  const {
2891
- getContext: getContext2,
2706
+ // Named apart from the module's exported `getContext`, which throws when
2707
+ // an object carries no kitcore context.
2708
+ getContext: getResolverContext,
2892
2709
  listItems,
2893
2710
  validate,
2894
2711
  tryResolveWithoutPrompt,
@@ -2902,8 +2719,8 @@ function bindResolver(resolver, plugins) {
2902
2719
  prompt: resolver.prompt,
2903
2720
  listItems: ({ input, context, search, cursor }) => listItems({ imports, input, context, search, cursor })
2904
2721
  };
2905
- if (getContext2)
2906
- bound.getContext = ({ input }) => getContext2({ imports, input });
2722
+ if (getResolverContext)
2723
+ bound.getContext = ({ input }) => getResolverContext({ imports, input });
2907
2724
  if (validate) {
2908
2725
  bound.validate = ({ value, input, context }) => validate({ imports, value, input, context });
2909
2726
  }
@@ -2947,9 +2764,9 @@ function bindFormatter(formatter, plugins) {
2947
2764
  frameworkOrigin: true
2948
2765
  });
2949
2766
  const bound = { format: formatter.format };
2950
- const { getContext: getContext2 } = formatter;
2951
- if (getContext2)
2952
- 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 });
2953
2770
  return bound;
2954
2771
  }
2955
2772
  function bindAttachments(descriptors, context) {
@@ -2970,64 +2787,35 @@ function bindAttachments(descriptors, context) {
2970
2787
  }
2971
2788
  }
2972
2789
  }
2973
- function runLegacyPass(descriptors, context) {
2790
+ function buildLeafEntries(descriptors, context, states) {
2974
2791
  const plugins = context.plugins;
2975
- const compatView = new Proxy(
2976
- {},
2977
- {
2978
- get: (_target, prop) => {
2979
- if (prop === "context") return context;
2980
- const entry = plugins[prop];
2981
- 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);
2982
2796
  }
2797
+ continue;
2983
2798
  }
2984
- );
2985
- for (const id of topoOrder(descriptors)) {
2986
- const descriptor = descriptors.get(id);
2987
- if (!descriptor || descriptor.pluginType !== "legacy") continue;
2988
- const { rootKeys, meta, hooks, contextRest } = splitPluginContribution(
2989
- descriptor.run(compatView)
2990
- );
2991
- Object.assign(context.meta, meta);
2992
- Object.assign(context, contextRest);
2993
- context.hooks = buildHooks(context.hooks, hooks);
2994
- const exports = mirrorLegacyRootKeys(context, rootKeys, meta);
2995
- for (const name of Object.keys(rootKeys)) context.surface[name] = name;
2996
- if (!("getRegistry" in exports)) {
2997
- let getRegistry3 = function(options) {
2998
- return getCachedRegistry(context, options?.package);
2999
- };
3000
- var getRegistry2 = getRegistry3;
3001
- exports.getRegistry = getRegistry3;
3002
- plugins.getRegistry = {
3003
- pluginType: "method",
3004
- name: "getRegistry",
3005
- value: getRegistry3,
3006
- chain: []
3007
- };
3008
- }
3009
- plugins[id] = { pluginType: "aggregate", name: descriptor.name, exports };
3010
- }
3011
- }
3012
- function buildMethodEntries(descriptors, context, states) {
3013
- const plugins = context.plugins;
3014
- for (const [id, descriptor] of descriptors) {
3015
2799
  if (descriptor.pluginType !== "method") continue;
3016
2800
  if (isStandIn(descriptor)) continue;
3017
2801
  const out = normalizeOutput(descriptor.output);
3018
2802
  const entry = {
3019
2803
  pluginType: "method",
3020
2804
  name: descriptor.name,
2805
+ descriptor,
3021
2806
  chain: [],
2807
+ ...pickDefined(descriptor, METHOD_META_KEYS),
3022
2808
  inputSchema: descriptor.inputSchema,
3023
2809
  skipInputValidation: descriptor.skipInputValidation,
3024
- // Derive the presentation type from the output mode when the author did
3025
- // not set one; an explicit meta.type (e.g. "create") still wins.
3026
- meta: out.type === "raw" || descriptor.meta?.type ? descriptor.meta : { ...descriptor.meta, type: out.type },
2810
+ outputSchema: descriptor.outputSchema,
3027
2811
  output: out,
3028
- // Replaced below; never called.
3029
- 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
3030
2817
  };
2818
+ if (entry.type === void 0 && out.type !== "raw") entry.type = out.type;
3031
2819
  const callRun = (input, ctx) => {
3032
2820
  const callContext = ctx ?? rootCallContext();
3033
2821
  return descriptor.run({
@@ -3063,13 +2851,12 @@ function buildMethodEntries(descriptors, context, states) {
3063
2851
  }
3064
2852
  return next(input);
3065
2853
  };
3066
- const sdk = { context };
3067
2854
  const methodAnnotator = descriptor.annotator;
3068
2855
  const boundAnnotator = methodAnnotator ? (input) => methodAnnotator({ input }) : void 0;
3069
2856
  const outputPolicy = (callOptions) => {
3070
2857
  const core = resolveCoreOptions(context);
3071
2858
  return {
3072
- outputSchema: descriptor.meta?.outputSchema,
2859
+ outputSchema: descriptor.outputSchema,
3073
2860
  skipOutputValidation: descriptor.skipOutputValidation,
3074
2861
  skippedByCaller: readSkipOutputDataValidation(callOptions),
3075
2862
  includeOutputValidationDroppedPaths: core?.includeOutputValidationDroppedPaths,
@@ -3085,7 +2872,7 @@ function buildMethodEntries(descriptors, context, states) {
3085
2872
  (input, ctx) => callRun(stripFrameworkOnlyOptions(input, withheld), ctx)
3086
2873
  ),
3087
2874
  {
3088
- sdk,
2875
+ sdkContext: context,
3089
2876
  schema: descriptor.inputSchema,
3090
2877
  name: descriptor.name,
3091
2878
  frameworkOptions,
@@ -3096,8 +2883,8 @@ function buildMethodEntries(descriptors, context, states) {
3096
2883
  // (item mode's sibling); dropped paths surface as `[].x` in the page's
3097
2884
  // `meta`, unioned across items.
3098
2885
  finalizePage: (page, callOptions) => applyListOutputPolicy(page, outputPolicy(callOptions)),
3099
- getDeprecation: () => entry.meta?.deprecation,
3100
- getStability: () => entry.meta ? normalizeStability(entry.meta) : void 0
2886
+ getDeprecation: () => entry.deprecation,
2887
+ getStability: () => normalizeStability(entry)
3101
2888
  }
3102
2889
  );
3103
2890
  } else if (out.type === "item") {
@@ -3108,17 +2895,17 @@ function buildMethodEntries(descriptors, context, states) {
3108
2895
  entry.value = createFunction(
3109
2896
  fold(itemCore),
3110
2897
  {
3111
- sdk,
2898
+ sdkContext: context,
3112
2899
  schema: descriptor.inputSchema,
3113
2900
  name: descriptor.name,
3114
2901
  frameworkOptions,
3115
2902
  annotator: boundAnnotator,
3116
- getDeprecation: () => entry.meta?.deprecation,
3117
- getStability: () => entry.meta ? normalizeStability(entry.meta) : void 0
2903
+ getDeprecation: () => entry.deprecation,
2904
+ getStability: () => normalizeStability(entry)
3118
2905
  }
3119
2906
  );
3120
2907
  } else {
3121
- const rawValidates = descriptor.meta?.outputSchema !== void 0 && !descriptor.skipOutputValidation;
2908
+ const rawValidates = descriptor.outputSchema !== void 0 && !descriptor.skipOutputValidation;
3122
2909
  const validateRaw = (out2) => {
3123
2910
  const policy = outputPolicy(void 0);
3124
2911
  if (isPromiseLike(out2)) {
@@ -3134,7 +2921,7 @@ function buildMethodEntries(descriptors, context, states) {
3134
2921
  return rawValidates ? validateRaw(out2) : out2;
3135
2922
  },
3136
2923
  {
3137
- sdk,
2924
+ sdkContext: context,
3138
2925
  name: descriptor.name,
3139
2926
  schema: descriptor.skipInputValidation ? void 0 : descriptor.inputSchema,
3140
2927
  positional: descriptor.positional,
@@ -3142,9 +2929,9 @@ function buildMethodEntries(descriptors, context, states) {
3142
2929
  // The boundary reads the deprecation LIVE off the entry, so a
3143
2930
  // deprecation merged after build (defineMethodOverride, addPlugin)
3144
2931
  // fires too. Same for the stability level, normalized from the
3145
- // entry meta (declared level or legacy `experimental` boolean).
3146
- getDeprecation: () => entry.meta?.deprecation,
3147
- 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)
3148
2935
  }
3149
2936
  );
3150
2937
  }
@@ -3163,8 +2950,8 @@ function buildMethodEntries(descriptors, context, states) {
3163
2950
  entry.internalValue = internalValue;
3164
2951
  entry.bindInternal = (opts) => bindInternalTwin({
3165
2952
  ...opts,
3166
- withContext: (context2) => {
3167
- return (...args) => canonicalValue(pack(args), context2);
2953
+ withContext: (ctx) => {
2954
+ return (...args) => canonicalValue(pack(args), ctx);
3168
2955
  },
3169
2956
  internalValue
3170
2957
  });
@@ -3174,29 +2961,53 @@ function buildMethodEntries(descriptors, context, states) {
3174
2961
  entry.internalValue = internalValue;
3175
2962
  entry.bindInternal = (opts) => bindInternalTwin({
3176
2963
  ...opts,
3177
- withContext: (context2) => (input) => canonicalValue(input, context2),
2964
+ withContext: (ctx) => (input) => canonicalValue(input, ctx),
3178
2965
  internalValue
3179
2966
  });
3180
2967
  }
3181
2968
  plugins[id] = entry;
3182
2969
  }
3183
2970
  }
3184
- 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) {
3185
2996
  const plugins = context.plugins;
3186
- const built = /* @__PURE__ */ new Set();
3187
- const building = /* @__PURE__ */ new Set();
3188
- const ensureBuilt = (id) => {
3189
- 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;
3190
3001
  const descriptor = descriptors.get(id);
3191
- if (!descriptor || descriptor.pluginType === "aggregate" || descriptor.pluginType === "legacy" || descriptor.pluginType === "method-override" || isStandIn(descriptor)) {
3192
- built.add(id);
3002
+ if (!descriptor || descriptor.pluginType === "aggregate" || descriptor.pluginType === "method-override" || isStandIn(descriptor)) {
3003
+ done.add(id);
3193
3004
  return;
3194
3005
  }
3195
- if (building.has(id)) {
3006
+ if (running.has(id)) {
3196
3007
  throw new Error(`createSdk: dependency cycle at "${id}".`);
3197
3008
  }
3198
- building.add(id);
3199
- for (const { id: depId } of descriptor.importBindings) ensureBuilt(depId);
3009
+ running.add(id);
3010
+ for (const { id: depId } of descriptor.importBindings) ensureSetup(depId);
3200
3011
  const recordDisposer = () => {
3201
3012
  const dispose = descriptor.dispose;
3202
3013
  if (!dispose) return;
@@ -3215,83 +3026,23 @@ function buildEagerArtifacts(descriptors, context, states) {
3215
3026
  })
3216
3027
  });
3217
3028
  };
3218
- if (descriptor.pluginType === "hook") {
3219
- states.set(
3220
- id,
3221
- descriptor.setup ? descriptor.setup({
3222
- imports: buildImports({
3223
- plugins,
3224
- importBindings: descriptor.importBindings
3225
- })
3226
- }) : void 0
3227
- );
3228
- recordDisposer();
3229
- building.delete(id);
3230
- built.add(id);
3231
- return;
3232
- }
3233
- if (descriptor.pluginType === "method") {
3234
- states.set(
3235
- id,
3236
- descriptor.setup ? descriptor.setup({
3237
- imports: buildImports({
3238
- plugins,
3239
- importBindings: descriptor.importBindings
3240
- })
3241
- }) : void 0
3242
- );
3243
- } else {
3244
- states.set(
3245
- id,
3246
- descriptor.setup ? descriptor.setup({
3247
- imports: buildImports({
3248
- plugins,
3249
- importBindings: descriptor.importBindings
3250
- })
3251
- }) : void 0
3252
- );
3253
- if (descriptor.privileged) {
3254
- plugins[id] = {
3255
- pluginType: "property",
3256
- name: descriptor.name,
3257
- value: context,
3258
- meta: descriptor.meta,
3259
- dynamicMembers: descriptor.dynamicMembers
3260
- };
3261
- } else if (descriptor.get) {
3262
- const get = descriptor.get;
3263
- const importBindings = descriptor.importBindings;
3264
- plugins[id] = {
3265
- pluginType: "property",
3266
- name: descriptor.name,
3267
- getValue: (callContext) => get({
3268
- imports: buildImports({
3269
- plugins,
3270
- importBindings,
3271
- ctx: callContext
3272
- }),
3273
- state: states.get(id),
3274
- callContext
3275
- }),
3276
- meta: descriptor.meta,
3277
- dynamicMembers: descriptor.dynamicMembers
3278
- };
3279
- } else {
3280
- plugins[id] = {
3281
- pluginType: "property",
3282
- name: descriptor.name,
3283
- value: descriptor.value,
3284
- meta: descriptor.meta,
3285
- dynamicMembers: descriptor.dynamicMembers
3286
- };
3287
- }
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") {
3288
3040
  assertDynamicMemberRoot(plugins[id]);
3289
3041
  }
3290
- recordDisposer();
3291
- building.delete(id);
3292
- built.add(id);
3042
+ running.delete(id);
3043
+ done.add(id);
3293
3044
  };
3294
- for (const id of descriptors.keys()) ensureBuilt(id);
3045
+ for (const id of descriptors.keys()) ensureSetup(id);
3295
3046
  }
3296
3047
  function resolvePlugin(sdk, ref) {
3297
3048
  const entry = getContext(sdk).plugins[ref.id];
@@ -3306,11 +3057,26 @@ function resolvePlugin(sdk, ref) {
3306
3057
  if (entry.pluginType === "property" && entry.getValue) {
3307
3058
  return entry.getValue();
3308
3059
  }
3309
- if (entry.pluginType === "method" && entry.internalValue) {
3310
- 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
+ );
3311
3069
  }
3312
3070
  return entry.value;
3313
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
+ }
3314
3080
  var CoreDisposeError = class extends Error {
3315
3081
  constructor(errors) {
3316
3082
  super(`disposeSdk: ${errors.length} dispose callback(s) failed.`);
@@ -3342,9 +3108,20 @@ function resolveAggregates(descriptors, context) {
3342
3108
  if (descriptor.pluginType !== "aggregate") continue;
3343
3109
  const exports = {};
3344
3110
  for (const [binding, child] of Object.entries(descriptor.exports)) {
3345
- 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 });
3346
3118
  }
3347
- plugins[id] = { pluginType: "aggregate", name: descriptor.name, exports };
3119
+ plugins[id] = {
3120
+ pluginType: "aggregate",
3121
+ name: descriptor.name,
3122
+ descriptor,
3123
+ exports
3124
+ };
3348
3125
  }
3349
3126
  }
3350
3127
  function assembleMiddleware(descriptors, context, states) {
@@ -3385,9 +3162,9 @@ function assembleHooks(descriptors, context, states) {
3385
3162
  const plugins = context.plugins;
3386
3163
  for (const id of topoOrder(descriptors)) {
3387
3164
  const descriptor = descriptors.get(id);
3388
- if (!descriptor || descriptor.pluginType !== "hook" || !descriptor.observe && !descriptor.annotator) {
3389
- continue;
3390
- }
3165
+ if (!descriptor || descriptor.pluginType !== "hook") continue;
3166
+ plugins[id] = { pluginType: "hook", name: descriptor.name, descriptor };
3167
+ if (!descriptor.observe && !descriptor.annotator) continue;
3391
3168
  const { observe, annotator } = descriptor;
3392
3169
  const state = states.get(id);
3393
3170
  const contributed = {};
@@ -3424,55 +3201,34 @@ function assembleHooks(descriptors, context, states) {
3424
3201
  }
3425
3202
  }
3426
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
+ });
3427
3209
  const context = {
3428
- plugins: {},
3429
- meta: {},
3210
+ plugins: /* @__PURE__ */ Object.create(null),
3430
3211
  hooks: {},
3431
- surface: {},
3212
+ surface: /* @__PURE__ */ Object.create(null),
3432
3213
  disposers: []
3433
3214
  };
3434
- if (root.pluginType === "legacy-merge") {
3435
- const { legacy, plugin } = root;
3436
- const collectRoot = {
3437
- pluginType: "aggregate",
3438
- name: root.name,
3439
- id: `${root.id}:merge`,
3440
- imports: [legacy, plugin],
3441
- importBindings: [],
3442
- exports: {}
3443
- };
3444
- const plugins2 = materialize(
3445
- collectPlugins(collectRoot, void 0, options?.configuration),
3446
- context
3447
- );
3448
- const legacyExports = plugins2[legacy.id].exports;
3449
- let pluginSurface;
3450
- if (plugin.pluginType === "aggregate") {
3451
- pluginSurface = plugins2[plugin.id].exports;
3452
- } else {
3453
- pluginSurface = {};
3454
- bindValue({
3455
- target: pluginSurface,
3456
- key: plugin.name,
3457
- entry: plugins2[plugin.id]
3458
- });
3459
- }
3460
- for (const key of Object.keys(legacyExports)) context.surface[key] = key;
3461
- if (plugin.pluginType === "aggregate") {
3462
- recordExportSurface(context, plugin.exports);
3463
- } else {
3464
- context.surface[plugin.name] = plugin.id;
3465
- }
3466
- return buildSurface(context, legacyExports, pluginSurface);
3467
- }
3468
3215
  const plugins = materialize(
3469
- collectPlugins(root, void 0, options?.configuration),
3216
+ collectPlugins({
3217
+ root,
3218
+ caller: "createSdk",
3219
+ applied: context.plugins,
3220
+ configuration: options?.configuration
3221
+ }),
3470
3222
  context
3471
3223
  );
3472
3224
  if (root.pluginType === "method" || root.pluginType === "property") {
3473
3225
  context.surface[root.name] = root.id;
3474
3226
  const sdk = buildSurface(context);
3475
- 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
+ });
3476
3232
  return sdk;
3477
3233
  }
3478
3234
  if (root.pluginType === "aggregate")
@@ -3483,54 +3239,67 @@ function addModelPlugin(sdk, plugin, options = {}) {
3483
3239
  const override = options.override === true;
3484
3240
  const context = getContext(sdk);
3485
3241
  const surfaceKeys = plugin.pluginType === "aggregate" ? Object.keys(plugin.exports) : plugin.pluginType === "hook" ? [] : [plugin.name];
3486
- checkRootKeyCollisions(sdk, surfaceKeys, override, "addPlugin");
3487
- const materialized = new Set(Object.keys(context.plugins));
3488
- 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) {
3489
3249
  throw new Error(
3490
- `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.`
3491
3251
  );
3492
3252
  }
3493
- materialize(collectPlugins(plugin, materialized), context);
3494
- if (plugin.pluginType === "hook") return;
3495
- const entry = context.plugins[plugin.id];
3496
- if (entry.pluginType === "aggregate") {
3497
- Object.defineProperties(
3498
- sdk,
3499
- 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
3500
3262
  );
3501
- for (const [binding, child] of Object.entries(
3502
- plugin.exports
3503
- )) {
3504
- 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;
3505
3278
  }
3506
- } else {
3507
- bindValue({ target: sdk, key: plugin.name, entry });
3508
- 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;
3509
3289
  }
3510
3290
  }
3511
3291
  function addPlugin(sdk, plugin, options) {
3512
3292
  const record = sdk;
3293
+ assertPluginArgument(plugin, { caller: "addPlugin", asRoot: false });
3513
3294
  const context = getContext(record);
3514
3295
  try {
3515
- if (typeof plugin === "function") {
3516
- const contribution = applyPluginToSdk(
3517
- record,
3518
- plugin,
3519
- options ?? {}
3520
- );
3521
- if (context) {
3522
- mirrorLegacyRootKeys(context, contribution.rootKeys, contribution.meta);
3523
- for (const name of Object.keys(contribution.rootKeys)) {
3524
- context.surface[name] = name;
3525
- }
3526
- }
3527
- } else if (plugin.pluginType === "method-override") {
3528
- applyMethodOverride(context, plugin);
3296
+ if (plugin.pluginType === "method-override") {
3297
+ addOverridePlugin(context, plugin);
3529
3298
  } else {
3530
3299
  addModelPlugin(record, plugin, options ?? {});
3531
3300
  }
3532
3301
  } finally {
3533
- if (context) invalidateRegistryCache(context);
3302
+ invalidateRegistryCache(context);
3534
3303
  }
3535
3304
  }
3536
3305
 
@@ -4779,18 +4548,6 @@ function createController(sdk) {
4779
4548
  return { resolve, start: start2, step: step2, listMethods, getMethod, listChoices };
4780
4549
  }
4781
4550
 
4782
- // src/utils/core-plugin.ts
4783
- function createCorePlugin(options) {
4784
- logDeprecation(
4785
- "createCorePlugin() is deprecated. Inject the options under CORE_OPTIONS_ID via createSdk's configuration instead."
4786
- );
4787
- return () => ({
4788
- context: {
4789
- core: options
4790
- }
4791
- });
4792
- }
4793
-
4794
4551
  // src/transport/attempt-http-request.ts
4795
4552
  import { z as z10 } from "zod";
4796
4553
 
@@ -5225,20 +4982,13 @@ export {
5225
4982
  attemptHttpRequestPlugin,
5226
4983
  authorizeHttpRequestPlugin,
5227
4984
  canonicalInputSchema,
5228
- composePlugins,
5229
4985
  concatLists,
5230
4986
  concatPaginated,
5231
4987
  coreOptionsPluginRef,
5232
4988
  createAsyncContext,
5233
4989
  createController,
5234
4990
  createCoreError,
5235
- createCorePlugin,
5236
4991
  createDeprecationLogger,
5237
- createFunction,
5238
- createPaginatedFunction,
5239
- createPaginatedPluginMethod,
5240
- createPluginMethod,
5241
- createPluginStack,
5242
4992
  createPrefixedCursor,
5243
4993
  createSdk,
5244
4994
  createStabilityNoticeLogger,
@@ -5255,7 +5005,6 @@ export {
5255
5005
  defaultLogDeprecation,
5256
5006
  defineFormatter,
5257
5007
  defineHook,
5258
- defineLegacyMerge,
5259
5008
  defineMethod,
5260
5009
  defineMethodOverride,
5261
5010
  defineOverride,
@@ -5265,7 +5014,6 @@ export {
5265
5014
  dispatchHttpRequestPlugin,
5266
5015
  disposeSdk,
5267
5016
  fetchPlugin,
5268
- fromFunctionPlugin,
5269
5017
  getContext,
5270
5018
  getCoreErrorCause,
5271
5019
  getCoreErrorCode,