@zapier/kitcore 0.19.0 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -38,20 +38,13 @@ __export(index_exports, {
38
38
  attemptHttpRequestPlugin: () => attemptHttpRequestPlugin,
39
39
  authorizeHttpRequestPlugin: () => authorizeHttpRequestPlugin,
40
40
  canonicalInputSchema: () => canonicalInputSchema,
41
- composePlugins: () => composePlugins,
42
41
  concatLists: () => concatLists,
43
42
  concatPaginated: () => concatPaginated,
44
43
  coreOptionsPluginRef: () => coreOptionsPluginRef,
45
44
  createAsyncContext: () => createAsyncContext,
46
45
  createController: () => createController,
47
46
  createCoreError: () => createCoreError,
48
- createCorePlugin: () => createCorePlugin,
49
47
  createDeprecationLogger: () => createDeprecationLogger,
50
- createFunction: () => createFunction,
51
- createPaginatedFunction: () => createPaginatedFunction,
52
- createPaginatedPluginMethod: () => createPaginatedPluginMethod,
53
- createPluginMethod: () => createPluginMethod,
54
- createPluginStack: () => createPluginStack,
55
48
  createPrefixedCursor: () => createPrefixedCursor,
56
49
  createSdk: () => createSdk,
57
50
  createStabilityNoticeLogger: () => createStabilityNoticeLogger,
@@ -68,7 +61,6 @@ __export(index_exports, {
68
61
  defaultLogDeprecation: () => defaultLogDeprecation,
69
62
  defineFormatter: () => defineFormatter,
70
63
  defineHook: () => defineHook,
71
- defineLegacyMerge: () => defineLegacyMerge,
72
64
  defineMethod: () => defineMethod,
73
65
  defineMethodOverride: () => defineMethodOverride,
74
66
  defineOverride: () => defineOverride,
@@ -78,7 +70,6 @@ __export(index_exports, {
78
70
  dispatchHttpRequestPlugin: () => dispatchHttpRequestPlugin,
79
71
  disposeSdk: () => disposeSdk,
80
72
  fetchPlugin: () => fetchPlugin,
81
- fromFunctionPlugin: () => fromFunctionPlugin,
82
73
  getContext: () => getContext,
83
74
  getCoreErrorCause: () => getCoreErrorCause,
84
75
  getCoreErrorCode: () => getCoreErrorCode,
@@ -130,902 +121,915 @@ __export(index_exports, {
130
121
  });
131
122
  module.exports = __toCommonJS(index_exports);
132
123
 
133
- // src/utils/string-utils.ts
134
- function toTitleCase(input) {
135
- 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(" ");
136
- }
137
- function toSnakeCase(input) {
138
- let result = input.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[\s\-]+/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "").toLowerCase();
139
- if (/^[0-9]/.test(result)) {
140
- result = "_" + result;
141
- }
142
- return result;
143
- }
144
- function pluralize(word) {
145
- if (/s$/i.test(word)) return word;
146
- if (/[bcdfghjklmnpqrstvwxz]y$/i.test(word)) {
147
- return word.slice(0, -1) + "ies";
148
- }
149
- return word + "s";
124
+ // src/utils/logging.ts
125
+ function createDeprecationLogger(tag) {
126
+ const loggedDeprecations = /* @__PURE__ */ new Set();
127
+ return {
128
+ logDeprecation(message) {
129
+ if (loggedDeprecations.has(message)) return;
130
+ loggedDeprecations.add(message);
131
+ console.warn(`[${tag}] Deprecation: ${message}`);
132
+ },
133
+ resetDeprecationWarnings() {
134
+ loggedDeprecations.clear();
135
+ }
136
+ };
150
137
  }
151
- function pluralizeLastWord(title) {
152
- const words = title.split(" ");
153
- return [...words.slice(0, -1), pluralize(words[words.length - 1])].join(" ");
138
+ var { logDeprecation, resetDeprecationWarnings } = createDeprecationLogger("core");
139
+ function createStabilityNoticeLogger(tag) {
140
+ const loggedNotices = /* @__PURE__ */ new Set();
141
+ return {
142
+ logStabilityNotice(message) {
143
+ if (loggedNotices.has(message)) return;
144
+ loggedNotices.add(message);
145
+ console.warn(`[${tag}] ${message}`);
146
+ },
147
+ resetStabilityNotices() {
148
+ loggedNotices.clear();
149
+ }
150
+ };
154
151
  }
152
+ var { logStabilityNotice, resetStabilityNotices } = createStabilityNoticeLogger("core");
155
153
 
156
- // src/utils/schema-utils.ts
157
- var import_zod = require("zod");
158
- function canonicalInputSchema(schema) {
159
- if (schema instanceof import_zod.z.ZodUnion) {
160
- return schema.options[0];
161
- }
162
- return schema;
154
+ // src/model/shared.ts
155
+ var CONTEXT = Symbol.for("kitcore.context");
156
+ function parseId(id) {
157
+ const at = id.lastIndexOf("/");
158
+ return at === -1 ? { name: id, namespace: void 0 } : { name: id.slice(at + 1), namespace: id.slice(0, at) };
163
159
  }
164
- function unwrapSchema(schema) {
165
- let inner = schema;
166
- let required = true;
167
- for (; ; ) {
168
- if (inner instanceof import_zod.z.ZodOptional || inner instanceof import_zod.z.ZodDefault) {
169
- required = false;
170
- inner = inner.unwrap();
171
- } else if (inner instanceof import_zod.z.ZodNullable) {
172
- inner = inner.unwrap();
173
- } else {
174
- break;
160
+ function makeId(name, namespace, kind = "leaf") {
161
+ validateName(name, kind);
162
+ if (namespace !== void 0) validateNamespace(namespace);
163
+ return namespace ? `${namespace}/${name}` : name;
164
+ }
165
+ var NAME_RE = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
166
+ var SEGMENT_RE = /^@?[A-Za-z0-9._-]+$/;
167
+ function validateName(name, kind) {
168
+ if (name === "") throw new Error("Plugin name must not be empty.");
169
+ if (kind === "leaf") {
170
+ if (!NAME_RE.test(name)) {
171
+ throw new Error(
172
+ `Plugin name "${name}" must be a valid JS identifier (it is the binding name).`
173
+ );
175
174
  }
175
+ } else if (!SEGMENT_RE.test(name)) {
176
+ throw new Error(
177
+ `Plugin name "${name}" must be package-like (letters, digits, ".", "_", "-", optional leading "@") with no "/".`
178
+ );
176
179
  }
177
- return { inner, required };
178
180
  }
179
- function objectShapeOf(schema) {
180
- const canonical = canonicalInputSchema(schema);
181
- if (!canonical) return void 0;
182
- const { inner } = unwrapSchema(canonical);
183
- if (inner instanceof import_zod.z.ZodObject) {
184
- return inner.shape;
181
+ function validateNamespace(namespace) {
182
+ if (namespace === "") throw new Error("Plugin namespace must not be empty.");
183
+ for (const segment of namespace.split("/")) {
184
+ if (!SEGMENT_RE.test(segment)) {
185
+ throw new Error(
186
+ `Plugin namespace "${namespace}" is invalid: each "/"-separated segment must be package-like (letters, digits, ".", "_", "-", optional leading "@").`
187
+ );
188
+ }
185
189
  }
186
- return void 0;
187
190
  }
188
- function getOutputSchema(inputSchema) {
189
- return inputSchema._zod.def.outputSchema;
190
- }
191
- function withOutputSchema(inputSchema, outputSchema) {
192
- Object.assign(inputSchema._zod.def, {
193
- outputSchema
194
- });
195
- return inputSchema;
191
+ function isStandIn(plugin) {
192
+ return (plugin.pluginType === "method" || plugin.pluginType === "property" || plugin.pluginType === "aggregate") && plugin.standIn === true;
196
193
  }
197
- function withResolver(schema, config) {
198
- schema._zod.def.resolverMeta = config;
199
- return schema;
194
+ function pickDefined(source, keys) {
195
+ const out = {};
196
+ for (const key of keys) {
197
+ if (source[key] !== void 0) out[key] = source[key];
198
+ }
199
+ return out;
200
200
  }
201
- function getSchemaDescription(schema) {
202
- return schema.description;
201
+
202
+ // src/model/types.ts
203
+ var OVERRIDABLE_META_KEYS = [
204
+ "description",
205
+ "categories",
206
+ "itemType",
207
+ "returnType",
208
+ "packages",
209
+ "experimental",
210
+ "deprecation",
211
+ "supportsJsonOutput"
212
+ ];
213
+ var METHOD_META_KEYS = Object.keys({
214
+ description: true,
215
+ categories: true,
216
+ packages: true,
217
+ stability: true,
218
+ experimental: true,
219
+ deprecation: true,
220
+ type: true,
221
+ itemType: true,
222
+ returnType: true,
223
+ confirm: true,
224
+ aliases: true,
225
+ supportsJsonOutput: true
226
+ });
227
+ var PROPERTY_META_KEYS = Object.keys({
228
+ description: true,
229
+ categories: true,
230
+ packages: true,
231
+ stability: true,
232
+ experimental: true,
233
+ deprecation: true
234
+ });
235
+ var PLUGIN_TYPES = new Set(
236
+ Object.keys({
237
+ method: true,
238
+ property: true,
239
+ aggregate: true,
240
+ hook: true,
241
+ "method-override": true
242
+ })
243
+ );
244
+
245
+ // src/model/plugin-argument.ts
246
+ function assertKnownPluginType({
247
+ plugin,
248
+ where
249
+ }) {
250
+ const { pluginType, id } = plugin;
251
+ if (PLUGIN_TYPES.has(pluginType)) return;
252
+ throw new Error(
253
+ `${where}: "${id}" has unknown pluginType "${pluginType}". A descriptor comes from a \`define*\` factory.`
254
+ );
203
255
  }
204
- function getFieldDescriptions(schema) {
205
- const descriptions = {};
206
- const shape = schema.shape;
207
- for (const [key, fieldSchema] of Object.entries(shape)) {
208
- if (fieldSchema instanceof import_zod.z.ZodType && fieldSchema.description) {
209
- descriptions[key] = fieldSchema.description;
210
- }
256
+ function assertDescriptorShape(value, { where, arrayFix }) {
257
+ const pluginType = value?.pluginType;
258
+ if (typeof pluginType !== "string") {
259
+ 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\``;
260
+ throw new Error(
261
+ `${where}: expected a plugin descriptor built by a \`define*\` factory, but got ${got}.`
262
+ );
211
263
  }
212
- return descriptions;
264
+ assertKnownPluginType({ plugin: value, where });
213
265
  }
214
- function withPositional(schema) {
215
- Object.assign(schema._zod.def, {
216
- positionalMeta: { positional: true }
266
+ function assertPluginArgument(plugin, { caller, asRoot }) {
267
+ assertDescriptorShape(plugin, {
268
+ where: caller,
269
+ arrayFix: asRoot ? "Wrap them in definePlugin({ exports })" : "Add each entry in its own call"
217
270
  });
218
- return schema;
219
- }
220
- function schemaHasPositionalMeta(schema) {
221
- return "positionalMeta" in schema._zod.def;
222
- }
223
- function isPositional(schema) {
224
- if (schemaHasPositionalMeta(schema) && schema._zod.def.positionalMeta?.positional) {
225
- return true;
226
- }
227
- if (schema instanceof import_zod.z.ZodOptional) {
228
- return isPositional(schema._zod.def.innerType);
271
+ const { id, pluginType } = plugin;
272
+ if (isStandIn(plugin)) {
273
+ throw new Error(
274
+ `${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.`
275
+ );
229
276
  }
230
- if (schema instanceof import_zod.z.ZodDefault) {
231
- return isPositional(schema._zod.def.innerType);
277
+ if (!asRoot) return;
278
+ if (pluginType === "hook") {
279
+ throw new Error(
280
+ `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.`
281
+ );
232
282
  }
233
- return false;
234
- }
235
- function getNegatable(schema) {
236
- const negatable = schema.meta?.()?.negatable;
237
- if (negatable === true) return true;
238
- if (typeof negatable === "string" && negatable.length > 0) return negatable;
239
- if (schema instanceof import_zod.z.ZodOptional || schema instanceof import_zod.z.ZodDefault) {
240
- return getNegatable(schema._zod.def.innerType);
283
+ if (pluginType === "method-override") {
284
+ throw new Error(
285
+ `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.`
286
+ );
241
287
  }
242
- return void 0;
243
- }
244
- function openEnum(values, description) {
245
- return import_zod.z.union([import_zod.z.enum(values), import_zod.z.string()]).describe(description);
246
288
  }
247
289
 
248
- // src/utils/stability.ts
249
- var STABILITY_LEVELS = ["stable", "beta", "experimental"];
250
- var STABILITY_TITLES = {
251
- stable: "Stable",
252
- beta: "Beta",
253
- experimental: "Experimental"
254
- };
255
- function normalizeStability(meta) {
256
- if (meta.stability !== void 0) {
257
- return STABILITY_LEVELS.includes(meta.stability) ? meta.stability : "experimental";
258
- }
259
- return meta.experimental ? "experimental" : "stable";
260
- }
261
- function applyStabilityLabel({
262
- description,
263
- stability,
264
- placement = "suffix"
290
+ // src/model/define.ts
291
+ function normalizeImports({
292
+ imports: deps,
293
+ owner
265
294
  }) {
266
- if (stability === void 0 || stability === "stable") return description;
267
- return placement === "prefix" ? `[${STABILITY_TITLES[stability]}] ${description}` : `${description} (${stability})`;
268
- }
269
-
270
- // src/registry.ts
271
- function resolveCategoryDefinition(ref) {
272
- const def = typeof ref === "string" ? { key: ref } : ref;
273
- const title = def.title ?? toTitleCase(def.key);
274
- return {
275
- key: def.key,
276
- title,
277
- titlePlural: def.titlePlural ?? pluralizeLastWord(title)
295
+ if (!deps) return { plugins: [], bindings: [] };
296
+ const seen = /* @__PURE__ */ new Map();
297
+ const bindings = [];
298
+ const add = (binding, id, optional) => {
299
+ const priorId = seen.get(binding);
300
+ if (priorId !== void 0 && priorId !== id) {
301
+ throw new Error(
302
+ `Import binding "${binding}" is declared twice. Two different plugins ("${priorId}" and "${id}") bind the same name; wrap one in selectExports to rename it.`
303
+ );
304
+ }
305
+ if (priorId === void 0) {
306
+ seen.set(binding, id);
307
+ bindings.push(optional ? { binding, id, optional } : { binding, id });
308
+ }
278
309
  };
279
- }
280
- function buildRegistry({
281
- sdk,
282
- meta,
283
- formatters,
284
- resolvers,
285
- positional,
286
- skipInputValidation,
287
- packageFilter
288
- }) {
289
- const definitionsByKey = /* @__PURE__ */ new Map();
290
- const objectDeclaredKeys = /* @__PURE__ */ new Set();
291
- for (const m of Object.values(meta)) {
292
- for (const ref of m.categories ?? []) {
293
- const key = typeof ref === "string" ? ref : ref.key;
294
- if (typeof ref === "object") {
295
- objectDeclaredKeys.add(key);
296
- definitionsByKey.set(key, resolveCategoryDefinition(ref));
297
- } else if (!objectDeclaredKeys.has(key)) {
298
- definitionsByKey.set(key, resolveCategoryDefinition(ref));
310
+ deps.forEach((plugin, index) => {
311
+ assertDescriptorShape(plugin, {
312
+ where: `${owner}, imports[${index}]`,
313
+ arrayFix: "Spread it into the list"
314
+ });
315
+ });
316
+ for (const plugin of deps) {
317
+ if (plugin.pluginType === "aggregate") {
318
+ for (const [binding, child] of Object.entries(plugin.exports)) {
319
+ add(binding, child.id);
299
320
  }
321
+ } else if (plugin.pluginType === "hook" || plugin.pluginType === "method-override") {
322
+ } else {
323
+ add(plugin.name, plugin.id, plugin.optional);
300
324
  }
301
325
  }
302
- if (!definitionsByKey.has("other")) {
303
- definitionsByKey.set("other", resolveCategoryDefinition("other"));
304
- }
305
- const knownCategories = Array.from(definitionsByKey.keys());
306
- const functions = Object.keys(meta).filter((key) => {
307
- const property = sdk[key];
308
- if (typeof property === "function") return true;
309
- const [rootKey] = key.split(".");
310
- const rootProperty = sdk[rootKey];
311
- return typeof rootProperty === "object" && rootProperty !== null;
312
- }).map((key) => {
313
- const m = meta[key];
314
- const stability = normalizeStability(m);
315
- return {
316
- name: key,
317
- description: m.description,
318
- type: m.type,
319
- itemType: m.itemType,
320
- returnType: m.returnType,
321
- inputSchema: canonicalInputSchema(m.inputSchema),
322
- outputSchema: m.outputSchema,
323
- positional: positional?.[key],
324
- skipInputValidation: skipInputValidation?.[key],
325
- categories: (m.categories ?? []).map(
326
- (c) => typeof c === "string" ? c : c.key
327
- ),
328
- resolvers: resolvers?.[key],
329
- formatter: formatters?.[key],
330
- stability,
331
- // Deprecated derived read, literal by name: only the experimental
332
- // tier reads true. Beta reads false — the "not stable" warning duty
333
- // lives in `stability` and the runtime notice, not this boolean.
334
- experimental: stability === "experimental",
335
- packages: m.packages,
336
- confirm: m.confirm ?? (m.type === "delete" ? "delete" : void 0),
337
- deprecation: m.deprecation,
338
- aliases: m.aliases,
339
- supportsJsonOutput: m.supportsJsonOutput ?? true
340
- };
341
- }).sort((a, b) => a.name.localeCompare(b.name));
342
- const filteredFunctions = packageFilter ? functions.filter((f) => !f.packages || f.packages.includes(packageFilter)) : functions;
343
- const filteredCategories = knownCategories.slice().sort((a, b) => {
344
- if (a === "other") return 1;
345
- if (b === "other") return -1;
346
- return definitionsByKey.get(a).title.localeCompare(definitionsByKey.get(b).title);
347
- }).map((categoryKey) => {
348
- const categoryFunctions = filteredFunctions.filter(
349
- (f) => f.categories.includes(categoryKey) || categoryKey === "other" && !f.categories.some((c) => knownCategories.includes(c))
350
- ).map((f) => f.name).sort();
351
- const def = definitionsByKey.get(categoryKey);
352
- return {
353
- key: categoryKey,
354
- title: def.title,
355
- titlePlural: def.titlePlural,
356
- functions: categoryFunctions
357
- };
358
- }).filter((category) => category.functions.length > 0);
359
- return { functions: filteredFunctions, categories: filteredCategories };
326
+ return { plugins: deps, bindings };
360
327
  }
361
-
362
- // src/utils/build-hooks.ts
363
- var isolated = /* @__PURE__ */ new WeakSet();
364
- function isolate(observer) {
365
- if (!observer) return void 0;
366
- if (isolated.has(observer)) return observer;
367
- const wrapped = (ctx) => {
368
- try {
369
- observer(ctx);
370
- } catch (error) {
371
- console.error(
372
- "[core] A method-lifecycle observer threw and was ignored. Observers are fire-and-forget and must not throw.",
373
- error
328
+ function formatDynamicMemberName(path) {
329
+ return path.map((seg) => typeof seg === "string" ? seg : `{${seg.param}}`).join(".");
330
+ }
331
+ function collectDynamicMembers(members) {
332
+ if (!members?.length) return void 0;
333
+ return members.map((member) => {
334
+ const root = member.path[0];
335
+ if (typeof root !== "string") {
336
+ throw new Error(
337
+ "defineProperty: a dynamicMember path must start with a literal segment (the owning binding), not a { param }."
374
338
  );
375
339
  }
376
- };
377
- isolated.add(wrapped);
378
- return wrapped;
379
- }
380
- function composeVoid(existing, added) {
381
- const wrappedExisting = isolate(existing);
382
- const wrappedAdded = isolate(added);
383
- if (!wrappedExisting) return wrappedAdded;
384
- if (!wrappedAdded) return wrappedExisting;
385
- const composed = (ctx) => {
386
- wrappedExisting(ctx);
387
- wrappedAdded(ctx);
388
- };
389
- isolated.add(composed);
390
- return composed;
391
- }
392
- function composeAnnotators(existing, added) {
393
- if (!existing) return added;
394
- if (!added) return existing;
395
- return (ctx) => ({ ...existing(ctx), ...added(ctx) });
396
- }
397
- function buildHooks(existing, added) {
398
- const result = {};
399
- const start2 = composeVoid(existing.onMethodStart, added.onMethodStart);
400
- if (start2) result.onMethodStart = start2;
401
- const end = composeVoid(existing.onMethodEnd, added.onMethodEnd);
402
- if (end) result.onMethodEnd = end;
403
- const annotator = composeAnnotators(existing.annotator, added.annotator);
404
- if (annotator) result.annotator = annotator;
405
- return result;
340
+ const { path, ...fields } = member;
341
+ return {
342
+ ...fields,
343
+ name: formatDynamicMemberName(path),
344
+ rootBinding: root
345
+ };
346
+ });
406
347
  }
407
-
408
- // src/utils/logging.ts
409
- function createDeprecationLogger(tag) {
410
- const loggedDeprecations = /* @__PURE__ */ new Set();
411
- return {
412
- logDeprecation(message) {
413
- if (loggedDeprecations.has(message)) return;
414
- loggedDeprecations.add(message);
415
- console.warn(`[${tag}] Deprecation: ${message}`);
416
- },
417
- resetDeprecationWarnings() {
418
- loggedDeprecations.clear();
419
- }
348
+ function defineMethod(configOrRef, refConfig) {
349
+ const config = refConfig === void 0 ? configOrRef : {
350
+ ...refConfig,
351
+ name: configOrRef.name,
352
+ namespace: configOrRef.namespace
420
353
  };
421
- }
422
- var { logDeprecation, resetDeprecationWarnings } = createDeprecationLogger("core");
423
- function createStabilityNoticeLogger(tag) {
424
- const loggedNotices = /* @__PURE__ */ new Set();
354
+ const deps = normalizeImports({
355
+ imports: config.imports,
356
+ owner: `defineMethod "${config.name}"`
357
+ });
425
358
  return {
426
- logStabilityNotice(message) {
427
- if (loggedNotices.has(message)) return;
428
- loggedNotices.add(message);
429
- console.warn(`[${tag}] ${message}`);
430
- },
431
- resetStabilityNotices() {
432
- loggedNotices.clear();
433
- }
359
+ ...config,
360
+ pluginType: "method",
361
+ id: makeId(config.name, config.namespace),
362
+ imports: deps.plugins,
363
+ importBindings: deps.bindings
434
364
  };
435
365
  }
436
- var { logStabilityNotice, resetStabilityNotices } = createStabilityNoticeLogger("core");
437
-
438
- // src/types/errors.ts
439
- var CORE_ERROR_SYMBOL = Symbol.for("kitcore.error");
440
- var CoreErrorCode = {
441
- Validation: "VALIDATION_ERROR",
442
- Unknown: "UNKNOWN_ERROR"
443
- };
444
- var CoreError = class extends Error {
445
- constructor(message, options = {}) {
446
- super(message);
447
- this.name = "CoreError";
448
- if (options.statusCode !== void 0) this.statusCode = options.statusCode;
449
- if (options.errors !== void 0) this.errors = options.errors;
450
- if (options.cause !== void 0) this.cause = options.cause;
451
- if (options.response !== void 0) this.response = options.response;
452
- Object.setPrototypeOf(this, new.target.prototype);
453
- }
454
- };
455
- function createCoreError(options, adaptError) {
456
- const error = adaptError?.(options) ?? new CoreError(options.message, { cause: options.cause });
457
- Object.defineProperty(error, CORE_ERROR_SYMBOL, {
458
- value: true,
459
- enumerable: false,
460
- configurable: true,
461
- writable: false
462
- });
463
- Object.defineProperty(error, "coreCode", {
464
- value: options.code,
465
- enumerable: false,
466
- configurable: true,
467
- writable: false
468
- });
469
- return error;
470
- }
471
- function isCoreError(value) {
472
- return Boolean(
473
- value && typeof value === "object" && value[CORE_ERROR_SYMBOL] === true
366
+ function assertOverridable(target, fields) {
367
+ const offered = Object.keys(fields).filter(
368
+ (key) => !OVERRIDABLE_META_KEYS.includes(key)
369
+ );
370
+ if (offered.length === 0) return;
371
+ throw new Error(
372
+ `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(", ")}.`
474
373
  );
475
374
  }
476
- function getCoreErrorCode(value) {
477
- if (!isCoreError(value)) return void 0;
478
- return value.coreCode;
479
- }
480
- function getCoreErrorCause(value) {
481
- if (!isCoreError(value)) return void 0;
482
- return value.cause;
375
+ function buildOverride(target, namespace, fields) {
376
+ assertOverridable(target, fields);
377
+ return {
378
+ pluginType: "method-override",
379
+ name: `override:${target}`,
380
+ id: namespace ? `${namespace}/override:${target}` : `override:${target}`,
381
+ target,
382
+ imports: [],
383
+ importBindings: [],
384
+ patch: pickDefined(fields, OVERRIDABLE_META_KEYS)
385
+ };
483
386
  }
484
-
485
- // src/utils/pagination-utils.ts
486
- var CURSOR_VERSION = 1;
487
- var CURSOR_SOURCE = {
488
- API: "api",
489
- SDK: "sdk",
490
- CONCAT: "concat"
491
- };
492
- function encodeBase64(str) {
493
- return btoa(
494
- Array.from(
495
- new TextEncoder().encode(str),
496
- (b) => String.fromCharCode(b)
497
- ).join("")
498
- );
387
+ function defineOverride(ref, config = {}) {
388
+ const { namespace, ...fields } = config;
389
+ return buildOverride(ref.id, namespace, fields);
499
390
  }
500
- function decodeBase64(str) {
501
- return new TextDecoder().decode(
502
- Uint8Array.from(atob(str), (c) => c.charCodeAt(0))
391
+ function defineMethodOverride(config) {
392
+ logDeprecation(
393
+ "defineMethodOverride({ target }) is deprecated. Use defineOverride(method, { ... }), which takes the method or its declareMethod stand-in."
503
394
  );
395
+ const { target, namespace, ...fields } = config;
396
+ return buildOverride(target, namespace, fields);
504
397
  }
505
- function encodeApiCursor(cursor) {
506
- const envelope = {
507
- v: CURSOR_VERSION,
508
- source: CURSOR_SOURCE.API,
509
- cursor
510
- };
511
- return encodeBase64(JSON.stringify(envelope));
398
+ function assertRequirementPaths(requirements) {
399
+ if (!requirements) return;
400
+ for (const requirement of requirements) {
401
+ if (typeof requirement !== "string" && requirement.length === 0) {
402
+ throw new Error(
403
+ "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."
404
+ );
405
+ }
406
+ }
512
407
  }
513
- function encodeSdkCursor(offset, cursor) {
514
- const envelope = {
515
- v: CURSOR_VERSION,
516
- source: CURSOR_SOURCE.SDK,
517
- cursor,
518
- offset
408
+ function defineResolver(config) {
409
+ const deps = normalizeImports({
410
+ imports: config.imports,
411
+ owner: "defineResolver"
412
+ });
413
+ const base = { imports: deps.plugins, importBindings: deps.bindings };
414
+ assertRequirementPaths(config.requireParameters);
415
+ const gates = {
416
+ requireParameters: config.requireParameters
519
417
  };
520
- return encodeBase64(JSON.stringify(envelope));
521
- }
522
- function decodeIncomingCursor(incoming) {
523
- if (!incoming) {
524
- return { offset: 0, cursor: void 0 };
525
- }
526
- try {
527
- const decoded = decodeBase64(incoming);
528
- const envelope = JSON.parse(decoded);
529
- if (envelope.v !== CURSOR_VERSION) {
530
- return { offset: 0, cursor: incoming };
531
- }
532
- if (envelope.source === CURSOR_SOURCE.SDK) {
533
- return { offset: envelope.offset ?? 0, cursor: envelope.cursor };
534
- }
535
- if (envelope.source === CURSOR_SOURCE.API) {
536
- return { offset: 0, cursor: envelope.cursor };
537
- }
538
- return { offset: 0, cursor: incoming };
539
- } catch {
540
- return { offset: 0, cursor: incoming };
418
+ switch (config.type) {
419
+ case "static":
420
+ return {
421
+ ...base,
422
+ ...gates,
423
+ type: "static",
424
+ inputType: config.inputType,
425
+ placeholder: config.placeholder
426
+ };
427
+ case "constant":
428
+ return { ...base, ...gates, type: "constant", value: config.value };
429
+ case "info":
430
+ return { ...base, type: "info", text: config.text ?? "" };
431
+ case "object":
432
+ return {
433
+ ...base,
434
+ ...gates,
435
+ type: "object",
436
+ properties: config.properties,
437
+ definitions: config.definitions,
438
+ getProperties: config.getProperties,
439
+ additionalKeys: config.additionalKeys
440
+ };
441
+ case "array":
442
+ return {
443
+ ...base,
444
+ ...gates,
445
+ type: "array",
446
+ items: config.items,
447
+ minItems: config.minItems,
448
+ maxItems: config.maxItems,
449
+ itemValueType: config.itemValueType,
450
+ definitions: config.definitions
451
+ };
452
+ default:
453
+ return {
454
+ ...base,
455
+ ...gates,
456
+ type: "dynamic",
457
+ inputType: config.inputType,
458
+ placeholder: config.placeholder,
459
+ getContext: config.getContext,
460
+ listItems: config.listItems,
461
+ prompt: config.prompt,
462
+ validate: config.validate,
463
+ tryResolveWithoutPrompt: config.tryResolveWithoutPrompt,
464
+ tryResolveFromSearch: config.tryResolveFromSearch
465
+ };
541
466
  }
542
467
  }
543
- function createPrefixedCursor(prefix, cursor) {
544
- if (!cursor) {
545
- return `${prefix}::`;
546
- }
547
- return `${prefix}::${cursor}`;
468
+ function defineFormatter(config) {
469
+ const deps = normalizeImports({
470
+ imports: config.imports,
471
+ owner: "defineFormatter"
472
+ });
473
+ return {
474
+ imports: deps.plugins,
475
+ importBindings: deps.bindings,
476
+ getContext: config.getContext,
477
+ format: config.format
478
+ };
548
479
  }
549
- function splitPrefixedCursor(cursor, prefixes) {
550
- if (!cursor) {
551
- return [void 0, void 0];
552
- }
553
- const [prefix, ...rest] = cursor.split("::");
554
- if (prefixes && !prefixes.includes(prefix)) {
555
- return [void 0, cursor];
556
- }
557
- cursor = rest.join("::");
558
- if (!cursor) {
559
- return [prefix, void 0];
560
- }
561
- return [prefix, cursor];
480
+ function declareMethod(config) {
481
+ const { name, namespace } = parseId(config.id);
482
+ const id = makeId(name, namespace);
483
+ return {
484
+ pluginType: "method",
485
+ name,
486
+ namespace,
487
+ id,
488
+ standIn: true,
489
+ imports: [],
490
+ importBindings: [],
491
+ run: () => {
492
+ throw new Error(
493
+ `Plugin "${id}" is a stand-in (declareMethod) with no implementation. Register the real plugin under this id.`
494
+ );
495
+ }
496
+ };
562
497
  }
563
- async function* paginateMaxItemsWithUnencodedCursor(pageFunction, pageOptions) {
564
- let cursor = pageOptions?.cursor;
565
- let totalItemsYielded = 0;
566
- const maxItems = pageOptions?.maxItems;
567
- const pageSize = pageOptions?.pageSize;
568
- do {
569
- const options = {
570
- ...pageOptions || {},
571
- cursor,
572
- pageSize: maxItems !== void 0 && pageSize !== void 0 ? Math.min(pageSize, maxItems) : pageSize
573
- };
574
- const page = await pageFunction(options);
575
- if (maxItems !== void 0) {
576
- const remainingItems = maxItems - totalItemsYielded;
577
- if (page.data.length >= remainingItems) {
578
- yield {
579
- ...page,
580
- data: page.data.slice(0, remainingItems),
581
- nextCursor: void 0
582
- };
583
- break;
584
- }
498
+ function declareOptionalMethod(config) {
499
+ const { name, namespace } = parseId(config.id);
500
+ const id = makeId(name, namespace);
501
+ return {
502
+ pluginType: "method",
503
+ name,
504
+ namespace,
505
+ id,
506
+ standIn: true,
507
+ optional: true,
508
+ imports: [],
509
+ importBindings: [],
510
+ run: () => {
511
+ throw new Error(
512
+ `Plugin "${id}" is an optional stand-in (declareOptionalMethod) with no implementation. Its binding is \`undefined\` unless a real plugin is registered under this id.`
513
+ );
585
514
  }
586
- yield page;
587
- totalItemsYielded += page.data.length;
588
- cursor = page.nextCursor;
589
- } while (cursor);
515
+ // Requires nothing: a consumer that imports it still passes `createSdk`'s
516
+ // completeness check unprovided. The contract is still carried, so a
517
+ // provider that DOES appear under the id is checked against it. The
518
+ // `optional: true` literal drives `PluginSurface` to type the binding
519
+ // `| undefined`.
520
+ };
590
521
  }
591
- async function* paginateMaxItems(pageFunction, pageOptions) {
592
- const { cursor } = decodeIncomingCursor(pageOptions?.cursor);
593
- const options = {
594
- ...pageOptions || {},
595
- cursor
522
+ function defineProperty(config, refConfig) {
523
+ const cfg = refConfig === void 0 ? config : {
524
+ ...refConfig,
525
+ name: config.name,
526
+ namespace: config.namespace
527
+ };
528
+ const deps = normalizeImports({
529
+ imports: cfg.imports,
530
+ owner: `defineProperty "${cfg.name}"`
531
+ });
532
+ return {
533
+ ...cfg,
534
+ pluginType: "property",
535
+ id: makeId(cfg.name, cfg.namespace),
536
+ imports: deps.plugins,
537
+ importBindings: deps.bindings,
538
+ dynamicMembers: collectDynamicMembers(cfg.dynamicMembers)
596
539
  };
597
- for await (const page of paginateMaxItemsWithUnencodedCursor(
598
- pageFunction,
599
- options
600
- )) {
601
- yield {
602
- ...page,
603
- nextCursor: page.nextCursor ? encodeApiCursor(page.nextCursor) : void 0
604
- };
605
- }
606
540
  }
607
- async function* paginateBuffered(pageFunction, pageOptions) {
608
- const pageSize = pageOptions?.pageSize;
609
- const { offset: cursorOffset, cursor: initialCursor } = decodeIncomingCursor(
610
- pageOptions?.cursor
611
- );
612
- const requestedMaxItems = pageOptions?.maxItems;
613
- const options = {
614
- ...pageOptions || {},
615
- cursor: initialCursor,
616
- // SDK cursors can carry an offset into a raw backend page. Since maxItems
617
- // is expected to be relative to the resumed position, we add that offset
618
- // so raw pagination still yields enough items after offset slicing.
619
- maxItems: requestedMaxItems !== void 0 && cursorOffset > 0 ? requestedMaxItems + cursorOffset : requestedMaxItems
541
+ function declareProperty(config) {
542
+ const { name, namespace } = parseId(config.id);
543
+ return {
544
+ pluginType: "property",
545
+ name,
546
+ namespace,
547
+ id: makeId(name, namespace),
548
+ standIn: true,
549
+ imports: [],
550
+ importBindings: []
620
551
  };
621
- if (!pageSize) {
622
- for await (const page of paginateMaxItemsWithUnencodedCursor(
623
- pageFunction,
624
- options
625
- )) {
626
- yield {
627
- ...page,
628
- nextCursor: page.nextCursor ? encodeApiCursor(page.nextCursor) : void 0
629
- };
630
- }
631
- return;
632
- }
633
- let bufferedPages = [];
634
- let isFirstPage = true;
635
- let rawCursor;
636
- for await (let page of paginateMaxItemsWithUnencodedCursor(
637
- pageFunction,
638
- options
639
- )) {
640
- const nextRawCursor = page.nextCursor;
641
- if (isFirstPage) {
642
- isFirstPage = false;
643
- if (cursorOffset) {
644
- page = {
645
- ...page,
646
- data: page.data.slice(cursorOffset)
647
- };
648
- }
649
- }
650
- const bufferedLength = bufferedPages.reduce(
651
- (acc, p) => acc + p.data.length,
652
- 0
653
- );
654
- if (bufferedLength + page.data.length < pageSize) {
655
- bufferedPages.push(page);
656
- rawCursor = nextRawCursor;
657
- continue;
658
- }
659
- const bufferedItems = bufferedPages.map((p) => p.data).flat();
660
- const allItems = [...bufferedItems, ...page.data];
661
- const pageItems = allItems.slice(0, pageSize);
662
- const remainingItems = allItems.slice(pageItems.length);
663
- if (remainingItems.length === 0) {
664
- yield {
665
- ...page,
666
- data: pageItems,
667
- nextCursor: nextRawCursor ? encodeApiCursor(nextRawCursor) : void 0
668
- };
669
- bufferedPages = [];
670
- rawCursor = nextRawCursor;
671
- continue;
672
- }
673
- yield {
674
- ...page,
675
- data: pageItems,
676
- nextCursor: encodeSdkCursor(
677
- page.data.length - remainingItems.length,
678
- rawCursor
679
- )
680
- };
681
- while (remainingItems.length > pageSize) {
682
- const chunkItems = remainingItems.splice(0, pageSize);
683
- yield {
684
- ...page,
685
- data: chunkItems,
686
- nextCursor: encodeSdkCursor(
687
- page.data.length - remainingItems.length,
688
- rawCursor
689
- )
690
- };
691
- }
692
- bufferedPages = [
693
- {
694
- ...page,
695
- data: remainingItems
696
- }
697
- ];
698
- rawCursor = nextRawCursor;
699
- }
700
- if (bufferedPages.length > 0) {
701
- const lastBufferedPage = bufferedPages.slice(-1)[0];
702
- const bufferedItems = bufferedPages.map((p) => p.data).flat();
703
- yield {
704
- ...lastBufferedPage,
705
- data: bufferedItems
706
- };
707
- }
708
552
  }
709
- var paginate = paginateBuffered;
710
- function encodeConcatCursor(index, cursor) {
711
- const envelope = {
712
- v: CURSOR_VERSION,
713
- source: CURSOR_SOURCE.CONCAT,
714
- index,
715
- cursor
553
+ function declareOptionalProperty(config) {
554
+ const { name, namespace } = parseId(config.id);
555
+ return {
556
+ pluginType: "property",
557
+ name,
558
+ namespace,
559
+ id: makeId(name, namespace),
560
+ standIn: true,
561
+ optional: true,
562
+ imports: [],
563
+ importBindings: []
564
+ // Requires nothing: a consumer that imports it still passes `createSdk`'s
565
+ // completeness check unprovided. The contract is the binding a consumer
566
+ // sees, `TValue | undefined`, which is also what a by-reference provider
567
+ // (`defineProperty(ref, { value })`) is allowed to pass. A consumer of an
568
+ // optional reference has to handle the absent case either way, so an
569
+ // explicit `undefined` breaks nothing a narrower contract would protect.
716
570
  };
717
- return encodeBase64(JSON.stringify(envelope));
718
571
  }
719
- function decodeConcatCursor(incoming) {
720
- if (!incoming) {
721
- return { index: 0, cursor: void 0 };
572
+ function declareDefault({
573
+ plugin
574
+ }) {
575
+ return { ...plugin, defaultSource: plugin };
576
+ }
577
+ function defineHook(config) {
578
+ const deps = normalizeImports({
579
+ imports: config.imports,
580
+ owner: `defineHook "${config.name}"`
581
+ });
582
+ return {
583
+ pluginType: "hook",
584
+ name: config.name,
585
+ namespace: config.namespace,
586
+ id: makeId(config.name, config.namespace),
587
+ imports: deps.plugins,
588
+ importBindings: deps.bindings,
589
+ setup: config.setup,
590
+ dispose: config.dispose,
591
+ wrap: config.wrap,
592
+ observe: config.observe,
593
+ annotator: config.annotator
594
+ };
595
+ }
596
+ function declarePlugin(config) {
597
+ const { name, namespace } = parseId(config.id);
598
+ return {
599
+ pluginType: "aggregate",
600
+ name,
601
+ namespace,
602
+ id: makeId(name, namespace, "aggregate"),
603
+ standIn: true,
604
+ imports: [],
605
+ importBindings: [],
606
+ exports: normalizeExports(config.exports)
607
+ };
608
+ }
609
+ function definePlugin(config) {
610
+ const owner = `definePlugin "${config.name}"`;
611
+ const deps = normalizeImports({ imports: config.imports, owner });
612
+ config.exports?.forEach((element, index) => {
613
+ assertDescriptorShape(element, {
614
+ where: `${owner}, exports[${index}]`,
615
+ arrayFix: "Spread it into the list"
616
+ });
617
+ });
618
+ return {
619
+ pluginType: "aggregate",
620
+ name: config.name,
621
+ namespace: config.namespace,
622
+ id: makeId(config.name, config.namespace, "aggregate"),
623
+ // A re-export synthetic (`selectExports` / `omitExports`) is flattened by
624
+ // `normalizeExports` into bare bindings, which drops its own `imports:
625
+ // [source]`. That edge is how `omitExports` keeps an omitted (unbound) leaf
626
+ // materialized + addressable by id, so preserve every exported aggregate's
627
+ // imports as extra reachability edges here (bindings unaffected).
628
+ imports: [...deps.plugins, ...exportedAggregateImports(config.exports)],
629
+ importBindings: deps.bindings,
630
+ exports: normalizeExports(config.exports)
631
+ };
632
+ }
633
+ function exportedAggregateImports(exports2) {
634
+ if (!exports2) return [];
635
+ const out = [];
636
+ for (const element of exports2) {
637
+ if (element.pluginType === "aggregate") out.push(...element.imports);
722
638
  }
723
- try {
724
- const envelope = JSON.parse(decodeBase64(incoming));
725
- if (envelope.v === CURSOR_VERSION && envelope.source === CURSOR_SOURCE.CONCAT && typeof envelope.index === "number") {
726
- return { index: envelope.index, cursor: envelope.cursor };
639
+ return out;
640
+ }
641
+ function normalizeExports(exports2) {
642
+ const out = /* @__PURE__ */ Object.create(null);
643
+ if (!exports2) return out;
644
+ const add = (binding, leaf) => {
645
+ const existing = out[binding];
646
+ if (existing && existing.id !== leaf.id) {
647
+ throw new Error(
648
+ `definePlugin: duplicate export binding "${binding}". Two different plugins ("${existing.id}" and "${leaf.id}") bind the same name; wrap one in selectExports to rename it.`
649
+ );
650
+ }
651
+ out[binding] = leaf;
652
+ };
653
+ for (const element of exports2) {
654
+ if (element.pluginType === "aggregate") {
655
+ for (const [binding, child] of Object.entries(element.exports)) {
656
+ add(binding, child);
657
+ }
658
+ } else {
659
+ add(element.name, element);
727
660
  }
728
- } catch {
729
661
  }
730
- return { index: 0, cursor: incoming };
662
+ return out;
731
663
  }
732
- async function concatLists({
733
- sources,
734
- pageSize = 100,
735
- cursor
736
- }) {
737
- if (sources.length === 0) {
738
- return { data: [] };
739
- }
740
- const pageFunction = async (options) => {
741
- let { index, cursor: listCursor } = decodeConcatCursor(options.cursor);
742
- while (index < sources.length) {
743
- const page = await sources[index]({ cursor: listCursor });
744
- const hasMoreInList = page.nextCursor != null;
745
- if (page.data.length === 0 && !hasMoreInList) {
746
- index++;
747
- listCursor = void 0;
748
- continue;
664
+
665
+ // src/model/exports.ts
666
+ var selectSeq = 0;
667
+ function selectExports(source, ...specs) {
668
+ const selected = {};
669
+ const pick = (binding, fromName) => {
670
+ const child = source.exports[fromName];
671
+ if (!child) {
672
+ throw new Error(
673
+ `selectExports: "${source.id}" has no export "${fromName}".`
674
+ );
675
+ }
676
+ selected[binding] = child;
677
+ };
678
+ for (const spec of specs) {
679
+ if (typeof spec === "string") {
680
+ pick(spec, spec);
681
+ } else {
682
+ for (const [newName, fromName] of Object.entries(spec)) {
683
+ pick(newName, fromName);
749
684
  }
750
- return {
751
- data: page.data,
752
- nextCursor: hasMoreInList ? encodeConcatCursor(index, page.nextCursor) : index < sources.length - 1 ? encodeConcatCursor(index + 1, void 0) : void 0
753
- };
754
685
  }
755
- return { data: [] };
686
+ }
687
+ const id = `${source.id}#select:${selectSeq++}`;
688
+ return {
689
+ pluginType: "aggregate",
690
+ name: makeId(`select`, source.name, "aggregate"),
691
+ id,
692
+ // Depend on the source so it is materialized; the selected bindings resolve
693
+ // to the source's own leaves (kept identity).
694
+ imports: [source],
695
+ importBindings: [],
696
+ exports: selected
697
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
756
698
  };
757
- const result = await paginateBuffered(pageFunction, {
758
- pageSize,
759
- cursor
760
- }).next();
761
- return result.done ? { data: [] } : result.value;
762
- }
763
- function concatPaginated({
764
- sources,
765
- pageSize,
766
- cursor
767
- }) {
768
- logDeprecation("concatPaginated() is deprecated. Use concatLists() instead.");
769
- return concatLists({ sources, pageSize, cursor });
770
699
  }
771
- function toIterable(source) {
772
- logDeprecation(
773
- "toIterable() is deprecated. Call .pages() on the paginated result instead."
774
- );
775
- return { [Symbol.asyncIterator]: () => source[Symbol.asyncIterator]() };
700
+ function omitExports(source, omit) {
701
+ const omitSet = new Set(omit);
702
+ for (const name of omit) {
703
+ if (!(name in source.exports)) {
704
+ throw new Error(`omitExports: "${source.id}" has no export "${name}".`);
705
+ }
706
+ }
707
+ const kept = {};
708
+ for (const [binding, child] of Object.entries(source.exports)) {
709
+ if (!omitSet.has(binding)) kept[binding] = child;
710
+ }
711
+ return {
712
+ pluginType: "aggregate",
713
+ name: makeId(`omit`, source.name, "aggregate"),
714
+ id: `${source.id}#omit:${selectSeq++}`,
715
+ imports: [source],
716
+ importBindings: [],
717
+ exports: kept
718
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
719
+ };
776
720
  }
777
721
 
778
- // src/utils/promise-utils.ts
779
- function isPromiseLike(value) {
780
- return value !== null && typeof value === "object" && typeof value.then === "function";
722
+ // src/model/builtins.ts
723
+ var import_zod2 = require("zod");
724
+
725
+ // src/utils/string-utils.ts
726
+ function toTitleCase(input) {
727
+ 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(" ");
728
+ }
729
+ function toSnakeCase(input) {
730
+ let result = input.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[\s\-]+/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "").toLowerCase();
731
+ if (/^[0-9]/.test(result)) {
732
+ result = "_" + result;
733
+ }
734
+ return result;
735
+ }
736
+ function pluralize(word) {
737
+ if (/s$/i.test(word)) return word;
738
+ if (/[bcdfghjklmnpqrstvwxz]y$/i.test(word)) {
739
+ return word.slice(0, -1) + "ies";
740
+ }
741
+ return word + "s";
742
+ }
743
+ function pluralizeLastWord(title) {
744
+ const words = title.split(" ");
745
+ return [...words.slice(0, -1), pluralize(words[words.length - 1])].join(" ");
781
746
  }
782
747
 
783
- // src/utils/validation.ts
784
- var parseOrThrow = (schema, input, { adaptError } = {}) => {
785
- const result = schema.safeParse(input);
786
- if (!result.success) {
787
- const errorMessages = result.error.issues.map((issue) => {
788
- const path = issue.path.length > 0 ? issue.path.join(".") : "input";
789
- return `${path}: ${issue.message}`;
790
- });
791
- throw createCoreError(
792
- {
793
- code: CoreErrorCode.Validation,
794
- message: `Validation failed:
795
- ${errorMessages.join("\n ")}`,
796
- details: {
797
- zodErrors: result.error.issues,
798
- input
799
- }
800
- },
801
- adaptError
802
- );
748
+ // src/utils/schema-utils.ts
749
+ var import_zod = require("zod");
750
+ function canonicalInputSchema(schema) {
751
+ if (schema instanceof import_zod.z.ZodUnion) {
752
+ return schema.options[0];
803
753
  }
804
- return result.data;
805
- };
806
- function createValidator(schema, { adaptError } = {}) {
807
- return function validateFn(input) {
808
- return parseOrThrow(schema, input, { adaptError });
809
- };
810
- }
811
- var validateOptions = (schema, options, { adaptError } = {}) => parseOrThrow(schema, options, { adaptError });
812
-
813
- // src/utils/call-options.ts
814
- var import_zod2 = require("zod");
815
- var CallFrameworkOptionsSchema = import_zod2.z.object({
816
- /** Page to fetch. Opaque to kitcore: the head's API defines the format. */
817
- cursor: import_zod2.z.string().optional(),
818
- /** Items per page. */
819
- pageSize: import_zod2.z.number().int().min(1).optional(),
820
- /** Stop after this many items, across pages. */
821
- maxItems: import_zod2.z.number().int().min(0).optional(),
822
- /** Bypass output validation for this one call. */
823
- skipOutputDataValidation: import_zod2.z.boolean().optional()
824
- });
825
- var ITEM_FRAMEWORK_OPTIONS = {
826
- claims: ["skipOutputDataValidation"],
827
- injects: []
828
- };
829
- var LIST_FRAMEWORK_OPTIONS = {
830
- claims: ["cursor", "pageSize", "maxItems", "skipOutputDataValidation"],
831
- injects: ["cursor", "pageSize"]
832
- };
833
- var PAGE_FRAMEWORK_OPTIONS = {
834
- claims: ["cursor", "pageSize", "maxItems"],
835
- injects: ["cursor", "pageSize", "maxItems"]
836
- };
837
- var NO_FRAMEWORK_OPTIONS = {
838
- claims: [],
839
- injects: []
840
- };
841
- function isRecord(value) {
842
- return typeof value === "object" && value !== null && !Array.isArray(value);
754
+ return schema;
843
755
  }
844
- function strictlyRefused(error, claims) {
845
- const refused = /* @__PURE__ */ new Set();
846
- for (const issue of error.issues) {
847
- if (issue.code !== "unrecognized_keys" || issue.path.length > 0) continue;
848
- for (const key of issue.keys) {
849
- if (claims.includes(key)) refused.add(key);
756
+ function unwrapSchema(schema) {
757
+ let inner = schema;
758
+ let required = true;
759
+ for (; ; ) {
760
+ if (inner instanceof import_zod.z.ZodOptional || inner instanceof import_zod.z.ZodDefault) {
761
+ required = false;
762
+ inner = inner.unwrap();
763
+ } else if (inner instanceof import_zod.z.ZodNullable) {
764
+ inner = inner.unwrap();
765
+ } else {
766
+ break;
850
767
  }
851
768
  }
852
- return [...refused];
769
+ return { inner, required };
853
770
  }
854
- function withoutKeys(options, keys) {
855
- const next = {};
856
- for (const [key, value] of Object.entries(options)) {
857
- if (!keys.includes(key)) next[key] = value;
771
+ function objectShapeOf(schema) {
772
+ const canonical = canonicalInputSchema(schema);
773
+ if (!canonical) return void 0;
774
+ const { inner } = unwrapSchema(canonical);
775
+ if (inner instanceof import_zod.z.ZodObject) {
776
+ return inner.shape;
858
777
  }
859
- return next;
778
+ return void 0;
860
779
  }
861
- function parseCallOptions(options, {
862
- schema,
863
- policy = NO_FRAMEWORK_OPTIONS,
864
- adaptError
865
- } = {}) {
866
- const claims = policy.claims;
867
- const call = isRecord(options) ? options : void 0;
868
- let framework = {};
869
- if (call && claims.length > 0) {
870
- const present = {};
871
- for (const key of claims) {
872
- if (key in call) present[key] = call[key];
873
- }
874
- framework = parseOrThrow2(CallFrameworkOptionsSchema, present, adaptError);
875
- }
876
- if (!schema) return { framework, domain: options, supplied: /* @__PURE__ */ new Set() };
877
- const first = schema.safeParse(options);
878
- if (first.success) {
879
- return { framework, domain: first.data, supplied: /* @__PURE__ */ new Set() };
880
- }
881
- const refused = call ? strictlyRefused(first.error, claims) : [];
882
- if (refused.length === 0) throw toCoreError(first.error, options, adaptError);
883
- const retry = schema.safeParse(withoutKeys(call, refused));
884
- if (!retry.success) {
885
- throw toCoreError(retry.error, options, adaptError);
886
- }
887
- return { framework, domain: retry.data, supplied: new Set(refused) };
780
+ function getOutputSchema(inputSchema) {
781
+ return inputSchema._zod.def.outputSchema;
888
782
  }
889
- function mergeCallOptions({
890
- framework,
891
- domain
892
- }) {
893
- const claimed = Object.entries(framework);
894
- if (!isRecord(domain) || claimed.length === 0) return domain;
895
- return { ...domain, ...Object.fromEntries(claimed) };
783
+ function withOutputSchema(inputSchema, outputSchema) {
784
+ Object.assign(inputSchema._zod.def, {
785
+ outputSchema
786
+ });
787
+ return inputSchema;
896
788
  }
897
- function withheldFromRun(policy) {
898
- return new Set(policy.claims.filter((key) => !policy.injects.includes(key)));
789
+ function withResolver(schema, config) {
790
+ schema._zod.def.resolverMeta = config;
791
+ return schema;
899
792
  }
900
- function stripFrameworkOnlyOptions(options, withheld) {
901
- if (withheld.size === 0 || !isRecord(options)) return options;
902
- const entries = Object.entries(options);
903
- if (!entries.some(([key]) => withheld.has(key))) return options;
904
- return Object.fromEntries(entries.filter(([key]) => !withheld.has(key)));
793
+ function getSchemaDescription(schema) {
794
+ return schema.description;
905
795
  }
906
- function parseOrThrow2(schema, input, adaptError) {
907
- const result = schema.safeParse(input);
908
- if (result.success) return result.data;
909
- throw toCoreError(result.error, input, adaptError);
796
+ function getFieldDescriptions(schema) {
797
+ const descriptions = {};
798
+ const shape = schema.shape;
799
+ for (const [key, fieldSchema] of Object.entries(shape)) {
800
+ if (fieldSchema instanceof import_zod.z.ZodType && fieldSchema.description) {
801
+ descriptions[key] = fieldSchema.description;
802
+ }
803
+ }
804
+ return descriptions;
910
805
  }
911
- function toCoreError(error, input, adaptError) {
912
- const messages = error.issues.map((issue) => {
913
- const path = issue.path.length > 0 ? issue.path.join(".") : "input";
914
- return `${path}: ${issue.message}`;
806
+ function withPositional(schema) {
807
+ Object.assign(schema._zod.def, {
808
+ positionalMeta: { positional: true }
915
809
  });
916
- return createCoreError(
917
- {
918
- code: CoreErrorCode.Validation,
919
- message: `Validation failed:
920
- ${messages.join("\n ")}`,
921
- details: { zodErrors: error.issues, input }
922
- },
923
- adaptError
924
- );
810
+ return schema;
925
811
  }
926
-
927
- // src/utils/async-context.ts
928
- var import_node_async_hooks = require("async_hooks");
929
- function createAsyncContext() {
930
- let store = null;
931
- try {
932
- store = new import_node_async_hooks.AsyncLocalStorage();
933
- } catch {
934
- store = null;
935
- }
936
- return {
937
- available: store !== null,
938
- run(value, fn) {
939
- return store ? store.run(value, fn) : fn();
940
- },
941
- get() {
942
- return store?.getStore();
943
- }
944
- };
812
+ function schemaHasPositionalMeta(schema) {
813
+ return "positionalMeta" in schema._zod.def;
945
814
  }
946
-
947
- // src/utils/method-scope.ts
948
- var scope = createAsyncContext();
949
- function getCurrentScope() {
950
- return scope.get();
815
+ function isPositional(schema) {
816
+ if (schemaHasPositionalMeta(schema) && schema._zod.def.positionalMeta?.positional) {
817
+ return true;
818
+ }
819
+ if (schema instanceof import_zod.z.ZodOptional) {
820
+ return isPositional(schema._zod.def.innerType);
821
+ }
822
+ if (schema instanceof import_zod.z.ZodDefault) {
823
+ return isPositional(schema._zod.def.innerType);
824
+ }
825
+ return false;
951
826
  }
952
- function getCurrentDepth() {
953
- return getCurrentScope()?.depth ?? 0;
827
+ function getNegatable(schema) {
828
+ const negatable = schema.meta?.()?.negatable;
829
+ if (negatable === true) return true;
830
+ if (typeof negatable === "string" && negatable.length > 0) return negatable;
831
+ if (schema instanceof import_zod.z.ZodOptional || schema instanceof import_zod.z.ZodDefault) {
832
+ return getNegatable(schema._zod.def.innerType);
833
+ }
834
+ return void 0;
954
835
  }
955
- function isNestedMethodCall() {
956
- if (!scope.available) return true;
957
- const store = scope.get();
958
- return store !== void 0 && store.depth > 0;
836
+ function openEnum(values, description) {
837
+ return import_zod.z.union([import_zod.z.enum(values), import_zod.z.string()]).describe(description);
959
838
  }
960
- var observerReentrancy = 0;
961
- function runIsolatedObserver(fn) {
962
- observerReentrancy++;
963
- try {
964
- fn();
965
- } catch {
966
- } finally {
967
- observerReentrancy--;
839
+
840
+ // src/utils/stability.ts
841
+ var STABILITY_LEVELS = ["stable", "beta", "experimental"];
842
+ var STABILITY_TITLES = {
843
+ stable: "Stable",
844
+ beta: "Beta",
845
+ experimental: "Experimental"
846
+ };
847
+ function normalizeStability(meta) {
848
+ if (meta.stability !== void 0) {
849
+ return STABILITY_LEVELS.includes(meta.stability) ? meta.stability : "experimental";
968
850
  }
851
+ return meta.experimental ? "experimental" : "stable";
969
852
  }
970
- function isInsideObserver() {
971
- return observerReentrancy > 0;
853
+ function applyStabilityLabel({
854
+ description,
855
+ stability,
856
+ placement = "suffix"
857
+ }) {
858
+ if (stability === void 0 || stability === "stable") return description;
859
+ return placement === "prefix" ? `[${STABILITY_TITLES[stability]}] ${description}` : `${description} (${stability})`;
972
860
  }
973
- function runInMethodScope(fn) {
974
- if (!scope.available) return fn();
975
- const currentDepth = scope.get()?.depth ?? -1;
976
- return scope.run({ depth: currentDepth + 1 }, fn);
861
+
862
+ // src/registry.ts
863
+ function resolveCategoryDefinition(ref) {
864
+ const def = typeof ref === "string" ? { key: ref } : ref;
865
+ const title = def.title ?? toTitleCase(def.key);
866
+ return {
867
+ key: def.key,
868
+ title,
869
+ titlePlural: def.titlePlural ?? pluralizeLastWord(title)
870
+ };
871
+ }
872
+ function buildRegistry({
873
+ sdk,
874
+ sources,
875
+ packageFilter
876
+ }) {
877
+ const definitionsByKey = /* @__PURE__ */ new Map();
878
+ const objectDeclaredKeys = /* @__PURE__ */ new Set();
879
+ for (const m of Object.values(sources)) {
880
+ for (const ref of m.categories ?? []) {
881
+ const key = typeof ref === "string" ? ref : ref.key;
882
+ if (typeof ref === "object") {
883
+ objectDeclaredKeys.add(key);
884
+ definitionsByKey.set(key, resolveCategoryDefinition(ref));
885
+ } else if (!objectDeclaredKeys.has(key)) {
886
+ definitionsByKey.set(key, resolveCategoryDefinition(ref));
887
+ }
888
+ }
889
+ }
890
+ if (!definitionsByKey.has("other")) {
891
+ definitionsByKey.set("other", resolveCategoryDefinition("other"));
892
+ }
893
+ const knownCategories = Array.from(definitionsByKey.keys());
894
+ const functions = Object.keys(sources).filter((key) => {
895
+ const property = sdk[key];
896
+ if (typeof property === "function") return true;
897
+ const [rootKey] = key.split(".");
898
+ const rootProperty = sdk[rootKey];
899
+ return typeof rootProperty === "object" && rootProperty !== null;
900
+ }).map((key) => {
901
+ const m = sources[key];
902
+ const stability = normalizeStability(m);
903
+ return {
904
+ name: key,
905
+ description: m.description,
906
+ type: m.type,
907
+ itemType: m.itemType,
908
+ returnType: m.returnType,
909
+ inputSchema: canonicalInputSchema(m.inputSchema),
910
+ outputSchema: m.outputSchema,
911
+ positional: m.positional,
912
+ skipInputValidation: m.skipInputValidation,
913
+ categories: (m.categories ?? []).map(
914
+ (c) => typeof c === "string" ? c : c.key
915
+ ),
916
+ resolvers: m.resolvers,
917
+ formatter: m.formatter,
918
+ stability,
919
+ // Deprecated derived read, literal by name: only the experimental
920
+ // tier reads true. Beta reads false — the "not stable" warning duty
921
+ // lives in `stability` and the runtime notice, not this boolean.
922
+ experimental: stability === "experimental",
923
+ packages: m.packages,
924
+ confirm: m.confirm ?? (m.type === "delete" ? "delete" : void 0),
925
+ deprecation: m.deprecation,
926
+ aliases: m.aliases,
927
+ supportsJsonOutput: m.supportsJsonOutput ?? true
928
+ };
929
+ }).sort((a, b) => a.name.localeCompare(b.name));
930
+ const filteredFunctions = packageFilter ? functions.filter((f) => !f.packages || f.packages.includes(packageFilter)) : functions;
931
+ const filteredCategories = knownCategories.slice().sort((a, b) => {
932
+ if (a === "other") return 1;
933
+ if (b === "other") return -1;
934
+ return definitionsByKey.get(a).title.localeCompare(definitionsByKey.get(b).title);
935
+ }).map((categoryKey) => {
936
+ const categoryFunctions = filteredFunctions.filter(
937
+ (f) => f.categories.includes(categoryKey) || categoryKey === "other" && !f.categories.some((c) => knownCategories.includes(c))
938
+ ).map((f) => f.name).sort();
939
+ const def = definitionsByKey.get(categoryKey);
940
+ return {
941
+ key: categoryKey,
942
+ title: def.title,
943
+ titlePlural: def.titlePlural,
944
+ functions: categoryFunctions
945
+ };
946
+ }).filter((category) => category.functions.length > 0);
947
+ return { functions: filteredFunctions, categories: filteredCategories };
977
948
  }
978
- var runWithTelemetryContext = runInMethodScope;
979
- var isTelemetryNested = isNestedMethodCall;
980
949
 
981
- // src/utils/call-context.ts
982
- var CALL_CONTEXT_BRAND = Symbol("kitcore.callContext");
983
- function isCallContext(value) {
984
- return typeof value === "object" && value !== null && value[CALL_CONTEXT_BRAND] === true;
950
+ // src/model/registry-support.ts
951
+ function methodMetaOf(entry) {
952
+ const meta = pickDefined(entry, METHOD_META_KEYS);
953
+ return Object.keys(meta).length > 0 ? meta : void 0;
985
954
  }
986
- function generateCallId() {
987
- try {
988
- const webCrypto = globalThis.crypto;
989
- if (webCrypto?.randomUUID) {
990
- return webCrypto.randomUUID();
955
+ function propertyMetaOf(entry) {
956
+ const meta = pickDefined(entry, PROPERTY_META_KEYS);
957
+ return Object.keys(meta).length > 0 ? meta : void 0;
958
+ }
959
+ function isDescribed(entry) {
960
+ const meta = entry.pluginType === "method" ? methodMetaOf(entry) : propertyMetaOf(entry);
961
+ return meta !== void 0;
962
+ }
963
+ function foldDynamicMembers(entry, surfaceBindings, sources) {
964
+ if (entry.pluginType !== "property" || !entry.dynamicMembers) return;
965
+ for (const member of entry.dynamicMembers) {
966
+ if (!surfaceBindings.has(member.rootBinding)) {
967
+ throw new Error(
968
+ `dynamicMember "${member.name}": its root "${member.rootBinding}" is not a surfaced member. A dynamic member's path must start with a real binding.`
969
+ );
991
970
  }
992
- if (webCrypto?.getRandomValues) {
993
- const bytes = webCrypto.getRandomValues(new Uint8Array(16));
994
- const hex = Array.from(bytes, (byte, i) => {
995
- const value = i === 6 ? byte & 15 | 64 : i === 8 ? byte & 63 | 128 : byte;
996
- return value.toString(16).padStart(2, "0");
997
- });
998
- return [
999
- hex.slice(0, 4).join(""),
1000
- hex.slice(4, 6).join(""),
1001
- hex.slice(6, 8).join(""),
1002
- hex.slice(8, 10).join(""),
1003
- hex.slice(10, 16).join("")
1004
- ].join("-");
971
+ sources[member.name] = member;
972
+ }
973
+ }
974
+ function collectRegistrySources(context) {
975
+ const sources = /* @__PURE__ */ Object.create(null);
976
+ const entries = [];
977
+ for (const [binding, id] of Object.entries(context.surface)) {
978
+ const entry = context.plugins[id];
979
+ if (entry?.pluginType !== "method" && entry?.pluginType !== "property") {
980
+ continue;
1005
981
  }
1006
- } catch {
982
+ entries.push(entry);
983
+ if (isDescribed(entry)) sources[binding] = entry;
1007
984
  }
1008
- return null;
985
+ const surfaceBindings = new Set(Object.keys(context.surface));
986
+ for (const entry of entries) {
987
+ foldDynamicMembers(entry, surfaceBindings, sources);
988
+ }
989
+ return sources;
1009
990
  }
1010
- function rootCallContext({
1011
- callOrigin = "surface"
1012
- } = {}) {
1013
- return {
1014
- callId: generateCallId(),
1015
- depth: 0,
1016
- annotations: {},
1017
- callOrigin,
1018
- [CALL_CONTEXT_BRAND]: true
1019
- };
991
+ var REGISTRY_CACHE = Symbol.for("kitcore.registryCache");
992
+ function freezeContainers(registry) {
993
+ Object.freeze(registry.functions);
994
+ for (const category of registry.categories) {
995
+ Object.freeze(category.functions);
996
+ Object.freeze(category);
997
+ }
998
+ Object.freeze(registry.categories);
999
+ return Object.freeze(registry);
1020
1000
  }
1021
- function childCallContext(parent) {
1022
- return {
1023
- callId: parent.callId,
1024
- depth: parent.depth + 1,
1025
- annotations: {},
1026
- callOrigin: parent.callOrigin,
1027
- [CALL_CONTEXT_BRAND]: true
1028
- };
1001
+ function getCachedRegistry(context, packageFilter) {
1002
+ const key = packageFilter ?? "";
1003
+ const caching = context;
1004
+ let byFilter = caching[REGISTRY_CACHE];
1005
+ if (!byFilter) {
1006
+ byFilter = /* @__PURE__ */ new Map();
1007
+ caching[REGISTRY_CACHE] = byFilter;
1008
+ }
1009
+ let registry = byFilter.get(key);
1010
+ if (!registry) {
1011
+ registry = freezeContainers(buildSurfaceRegistry(context, packageFilter));
1012
+ byFilter.set(key, registry);
1013
+ }
1014
+ return registry;
1015
+ }
1016
+ function invalidateRegistryCache(context) {
1017
+ delete context[REGISTRY_CACHE];
1018
+ }
1019
+ function buildSurfaceRegistry(context, packageFilter) {
1020
+ const surface = {};
1021
+ for (const [binding, id] of Object.entries(context.surface)) {
1022
+ const entry = context.plugins[id];
1023
+ if (entry?.pluginType !== "method" && entry?.pluginType !== "property") {
1024
+ continue;
1025
+ }
1026
+ surface[binding] = entry.pluginType === "property" && entry.getValue ? entry.getValue() : entry.value;
1027
+ }
1028
+ return buildRegistry({
1029
+ sdk: surface,
1030
+ sources: collectRegistrySources(context),
1031
+ packageFilter
1032
+ });
1029
1033
  }
1030
1034
 
1031
1035
  // src/utils/core-options.ts
@@ -1050,1376 +1054,1086 @@ function defaultLogStabilityNotice({
1050
1054
  }
1051
1055
  var CORE_OPTIONS_ID = "kitcore/coreOptions";
1052
1056
 
1053
- // src/utils/function-utils.ts
1054
- function resolveCoreOptions(context) {
1055
- const entry = context.plugins?.[CORE_OPTIONS_ID];
1056
- if (entry) {
1057
- return entry.getValue ? entry.getValue() : entry.value;
1058
- }
1059
- return context.core;
1060
- }
1061
- var INTERNAL_CALL = Symbol("kitcore.internalCall");
1062
- function resolveCallContext(secondArg) {
1063
- return isCallContext(secondArg) ? secondArg : rootCallContext();
1064
- }
1065
- var hookAnnotatorReentrancy = 0;
1066
- function applyAnnotations({
1067
- context,
1068
- methodName,
1069
- input,
1070
- hookAnnotator,
1071
- methodAnnotator
1072
- }) {
1073
- if (hookAnnotator && !isInsideObserver() && context.depth === 0 && context.callOrigin !== "internal" && hookAnnotatorReentrancy === 0) {
1074
- hookAnnotatorReentrancy++;
1057
+ // src/model/builtins.ts
1058
+ var coreOptionsPluginRef = declareOptionalProperty({ id: CORE_OPTIONS_ID });
1059
+ var dangerousContextPlugin = {
1060
+ pluginType: "property",
1061
+ name: "context",
1062
+ namespace: "kitcore",
1063
+ id: "kitcore/context",
1064
+ imports: [],
1065
+ importBindings: [],
1066
+ privileged: true
1067
+ };
1068
+ var getRegistryPlugin = defineMethod({
1069
+ name: "getRegistry",
1070
+ namespace: "kitcore",
1071
+ imports: [dangerousContextPlugin],
1072
+ inputSchema: import_zod2.z.object({ package: import_zod2.z.string().optional() }).optional(),
1073
+ run: ({ imports, input }) => getCachedRegistry(imports.context, input?.package)
1074
+ });
1075
+
1076
+ // src/utils/build-hooks.ts
1077
+ var isolated = /* @__PURE__ */ new WeakSet();
1078
+ function isolate(observer) {
1079
+ if (!observer) return void 0;
1080
+ if (isolated.has(observer)) return observer;
1081
+ const wrapped = (ctx) => {
1075
1082
  try {
1076
- Object.assign(context.annotations, hookAnnotator({ methodName, input }));
1077
- } catch {
1078
- } finally {
1079
- hookAnnotatorReentrancy--;
1083
+ observer(ctx);
1084
+ } catch (error) {
1085
+ console.error(
1086
+ "[core] A method-lifecycle observer threw and was ignored. Observers are fire-and-forget and must not throw.",
1087
+ error
1088
+ );
1080
1089
  }
1081
- }
1082
- try {
1083
- Object.assign(context.annotations, methodAnnotator?.(input));
1084
- } catch {
1085
- }
1086
- }
1087
- function signalDeprecation(context, methodName, getDeprecation) {
1088
- if (isInsideObserver()) return;
1089
- const deprecation = getDeprecation?.();
1090
- if (!deprecation?.message) return;
1091
- const warning = {
1092
- type: "deprecation",
1093
- methodName,
1094
- deprecation
1095
1090
  };
1096
- const handler = resolveCoreOptions(context)?.logDeprecation ?? defaultLogDeprecation;
1097
- runIsolatedObserver(() => handler(warning));
1091
+ isolated.add(wrapped);
1092
+ return wrapped;
1098
1093
  }
1099
- function signalStability(context, methodName, getStability) {
1100
- if (isInsideObserver()) return;
1101
- const stability = getStability?.();
1102
- if (!stability || stability === "stable") return;
1103
- const notice = {
1104
- type: "stability",
1105
- methodName,
1106
- stability
1094
+ function composeVoid(existing, added) {
1095
+ const wrappedExisting = isolate(existing);
1096
+ const wrappedAdded = isolate(added);
1097
+ if (!wrappedExisting) return wrappedAdded;
1098
+ if (!wrappedAdded) return wrappedExisting;
1099
+ const composed = (ctx) => {
1100
+ wrappedExisting(ctx);
1101
+ wrappedAdded(ctx);
1107
1102
  };
1108
- const handler = resolveCoreOptions(context)?.logStabilityNotice ?? defaultLogStabilityNotice;
1109
- runIsolatedObserver(() => handler(notice));
1103
+ isolated.add(composed);
1104
+ return composed;
1110
1105
  }
1111
- function normalizeError(error, adaptError) {
1112
- if (error instanceof Error) return error;
1113
- const message = typeof error === "object" && error !== null && "message" in error && typeof error.message === "string" ? error.message : String(error);
1114
- return createCoreError(
1115
- {
1116
- code: CoreErrorCode.Unknown,
1117
- message,
1118
- cause: error
1119
- },
1120
- adaptError
1121
- );
1106
+ function composeAnnotators(existing, added) {
1107
+ if (!existing) return added;
1108
+ if (!added) return existing;
1109
+ return (ctx) => ({ ...existing(ctx), ...added(ctx) });
1122
1110
  }
1123
- function createFunction(coreFn, options) {
1124
- const {
1125
- sdk,
1126
- schema,
1127
- name,
1128
- annotator,
1129
- frameworkOptions,
1130
- getDeprecation,
1131
- getStability
1132
- } = options;
1133
- const functionName = name || coreFn.name;
1134
- const namedFunctions = {
1135
- [functionName]: async function(callOptions) {
1136
- const internal = arguments[1];
1137
- const context = resolveCallContext(internal);
1138
- if (!isCallContext(internal) && internal !== INTERNAL_CALL) {
1139
- signalDeprecation(sdk.context, functionName, getDeprecation);
1140
- signalStability(sdk.context, functionName, getStability);
1141
- }
1142
- return runInMethodScope(async () => {
1143
- const startTime = Date.now();
1144
- const normalizedOptions = callOptions ?? {};
1145
- const args = [normalizedOptions];
1146
- const depth = Math.max(context.depth, getCurrentDepth());
1147
- const insideObserver = isInsideObserver();
1148
- const hooks = insideObserver ? void 0 : sdk.context.hooks;
1149
- const adaptError = resolveCoreOptions(sdk.context)?.adaptError;
1150
- applyAnnotations({
1151
- context,
1152
- methodName: functionName,
1153
- input: normalizedOptions,
1154
- hookAnnotator: hooks?.annotator,
1155
- methodAnnotator: annotator
1156
- });
1157
- const hookBase = {
1158
- methodName: functionName,
1159
- args,
1160
- isPaginated: false,
1161
- depth,
1162
- callId: context.callId,
1163
- callOrigin: context.callOrigin,
1164
- annotations: context.annotations
1165
- };
1166
- hooks?.onMethodStart?.({ ...hookBase });
1167
- try {
1168
- const parsed = parseCallOptions(normalizedOptions, {
1169
- schema,
1170
- policy: frameworkOptions,
1171
- adaptError
1172
- });
1173
- const result = await coreFn(
1174
- mergeCallOptions(parsed),
1175
- context
1176
- );
1177
- hooks?.onMethodEnd?.({
1178
- ...hookBase,
1179
- durationMs: Date.now() - startTime
1180
- });
1181
- return result;
1182
- } catch (error) {
1183
- const normalizedError = normalizeError(error, adaptError);
1184
- hooks?.onMethodEnd?.({
1185
- ...hookBase,
1186
- durationMs: Date.now() - startTime,
1187
- error: normalizedError
1188
- });
1189
- throw normalizedError;
1190
- }
1191
- });
1192
- }
1193
- };
1194
- return namedFunctions[functionName];
1111
+ function buildHooks(existing, added) {
1112
+ const result = {};
1113
+ const start2 = composeVoid(existing.onMethodStart, added.onMethodStart);
1114
+ if (start2) result.onMethodStart = start2;
1115
+ const end = composeVoid(existing.onMethodEnd, added.onMethodEnd);
1116
+ if (end) result.onMethodEnd = end;
1117
+ const annotator = composeAnnotators(existing.annotator, added.annotator);
1118
+ if (annotator) result.annotator = annotator;
1119
+ return result;
1195
1120
  }
1196
- function createRawFunction(coreFn, options) {
1197
- const {
1198
- sdk,
1199
- name,
1200
- schema,
1201
- positional,
1202
- annotator,
1203
- getDeprecation,
1204
- getStability
1205
- } = options;
1206
- return function(rawInput) {
1207
- const internal = arguments[1];
1208
- const context = resolveCallContext(internal);
1209
- if (!isCallContext(internal) && internal !== INTERNAL_CALL) {
1210
- signalDeprecation(sdk.context, name, getDeprecation);
1211
- signalStability(sdk.context, name, getStability);
1212
- }
1213
- return runInMethodScope(() => {
1214
- const startTime = Date.now();
1215
- const depth = Math.max(context.depth, getCurrentDepth());
1216
- const insideObserver = isInsideObserver();
1217
- const hooks = insideObserver ? void 0 : sdk.context.hooks;
1218
- const adaptError = resolveCoreOptions(sdk.context)?.adaptError;
1219
- const input = schema ? rawInput ?? {} : rawInput;
1220
- applyAnnotations({
1221
- context,
1222
- methodName: name,
1223
- input,
1224
- hookAnnotator: hooks?.annotator,
1225
- methodAnnotator: annotator
1226
- });
1227
- const record = input;
1228
- const args = positional ? positional.filter((key) => record?.[key] !== void 0).map((key) => record?.[key]) : [input];
1229
- const hookBase = {
1230
- methodName: name,
1231
- args,
1232
- isPaginated: false,
1233
- depth,
1234
- callId: context.callId,
1235
- callOrigin: context.callOrigin,
1236
- annotations: context.annotations
1237
- };
1238
- hooks?.onMethodStart?.({ ...hookBase });
1239
- const fireEnd = (error) => {
1240
- hooks?.onMethodEnd?.({
1241
- ...hookBase,
1242
- durationMs: Date.now() - startTime,
1243
- ...error ? { error } : {}
1244
- });
1245
- };
1246
- try {
1247
- const parsed = schema ? validateOptions(schema, input, { adaptError }) : input;
1248
- const result = coreFn(parsed, context);
1249
- if (isPromiseLike(result)) {
1250
- return result.then(
1251
- (value) => {
1252
- fireEnd();
1253
- return value;
1254
- },
1255
- (error) => {
1256
- fireEnd(
1257
- error instanceof Error ? error : new Error(String(error))
1258
- );
1259
- throw error;
1260
- }
1261
- );
1262
- }
1263
- fireEnd();
1264
- return result;
1265
- } catch (error) {
1266
- fireEnd(error instanceof Error ? error : new Error(String(error)));
1267
- throw error;
1268
- }
1269
- });
1270
- };
1121
+
1122
+ // src/model/root-keys.ts
1123
+ var RESERVED_ROOT_KEYS = /* @__PURE__ */ new Set(["context"]);
1124
+ function hasOwn(obj, key) {
1125
+ return Object.prototype.hasOwnProperty.call(obj, key);
1271
1126
  }
1272
- function isSdkPage(value) {
1273
- if (typeof value !== "object" || value === null) return false;
1274
- const page = value;
1275
- if (!Array.isArray(page.data)) return false;
1276
- if (page.nextCursor !== void 0 && typeof page.nextCursor !== "string") {
1277
- return false;
1127
+ function assertNotReservedKeys({
1128
+ keys,
1129
+ caller
1130
+ }) {
1131
+ for (const key of keys) {
1132
+ if (!RESERVED_ROOT_KEYS.has(key)) continue;
1133
+ throw new Error(
1134
+ `${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.`
1135
+ );
1278
1136
  }
1279
- return Object.keys(page).every((k) => k === "data" || k === "nextCursor");
1280
1137
  }
1281
- function createPageFunction(coreFn, {
1282
- sdk,
1283
- adaptPage,
1284
- finalizePage
1138
+ function checkRootKeyCollisions({
1139
+ target,
1140
+ keys,
1141
+ override,
1142
+ caller
1285
1143
  }) {
1286
- const functionName = coreFn.name + "Page";
1287
- const namedFunctions = {
1288
- [functionName]: async function(options, callContext) {
1289
- try {
1290
- const response = await coreFn(options, callContext);
1291
- const page = adaptPage ? adaptPage(response) : response;
1292
- if (!isSdkPage(page)) {
1293
- throw new Error(
1294
- `${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\`.`
1295
- );
1296
- }
1297
- return finalizePage ? finalizePage(page, options) : page;
1298
- } catch (error) {
1299
- throw normalizeError(
1300
- error,
1301
- resolveCoreOptions(sdk.context)?.adaptError
1302
- );
1303
- }
1144
+ assertNotReservedKeys({ keys, caller });
1145
+ for (const key of keys) {
1146
+ if (!override && hasOwn(target, key)) {
1147
+ throw new Error(
1148
+ `${caller}: duplicate root key "${key}". If the override is intentional, pass { override: true } in the options.`
1149
+ );
1304
1150
  }
1305
- };
1306
- return namedFunctions[functionName];
1151
+ }
1307
1152
  }
1308
- function createPaginatedFunction(coreFn, options) {
1309
- const {
1310
- sdk,
1311
- schema,
1312
- name,
1313
- defaultPageSize,
1314
- adaptPage,
1315
- annotator,
1316
- finalizePage,
1317
- frameworkOptions,
1318
- getDeprecation,
1319
- getStability
1320
- } = options;
1321
- const pageFunction = createPageFunction(coreFn, {
1322
- sdk,
1323
- adaptPage,
1324
- finalizePage
1153
+
1154
+ // src/types/errors.ts
1155
+ var CORE_ERROR_SYMBOL = Symbol.for("kitcore.error");
1156
+ var CoreErrorCode = {
1157
+ Validation: "VALIDATION_ERROR",
1158
+ Unknown: "UNKNOWN_ERROR",
1159
+ /**
1160
+ * The object handed to a framework reader is not an SDK `createSdk` built, so
1161
+ * there is no plugin graph to read. A code rather than prose because a caller
1162
+ * distinguishing "no registry here" from "the registry failed to build" has to
1163
+ * match on something stable, and the message is not that.
1164
+ */
1165
+ NoSdkContext: "NO_SDK_CONTEXT_ERROR"
1166
+ };
1167
+ var CoreError = class extends Error {
1168
+ constructor(message, options = {}) {
1169
+ super(message);
1170
+ this.name = "CoreError";
1171
+ if (options.statusCode !== void 0) this.statusCode = options.statusCode;
1172
+ if (options.errors !== void 0) this.errors = options.errors;
1173
+ if (options.cause !== void 0) this.cause = options.cause;
1174
+ if (options.response !== void 0) this.response = options.response;
1175
+ Object.setPrototypeOf(this, new.target.prototype);
1176
+ }
1177
+ };
1178
+ function createCoreError(options, adaptError) {
1179
+ const error = adaptError?.(options) ?? new CoreError(options.message, { cause: options.cause });
1180
+ Object.defineProperty(error, CORE_ERROR_SYMBOL, {
1181
+ value: true,
1182
+ enumerable: false,
1183
+ configurable: true,
1184
+ writable: false
1325
1185
  });
1326
- const functionName = name || coreFn.name;
1327
- const namedFunctions = {
1328
- [functionName]: function(callOptions) {
1329
- const internal = arguments[1];
1330
- const context = resolveCallContext(internal);
1331
- if (!isCallContext(internal) && internal !== INTERNAL_CALL) {
1332
- signalDeprecation(sdk.context, functionName, getDeprecation);
1333
- signalStability(sdk.context, functionName, getStability);
1334
- }
1335
- return runInMethodScope(() => {
1336
- const startTime = Date.now();
1337
- const normalizedOptions = callOptions ?? {};
1338
- const args = [normalizedOptions];
1339
- const depth = Math.max(context.depth, getCurrentDepth());
1340
- const insideObserver = isInsideObserver();
1341
- const hooks = insideObserver ? void 0 : sdk.context.hooks;
1342
- const adaptError = resolveCoreOptions(sdk.context)?.adaptError;
1343
- applyAnnotations({
1344
- context,
1345
- methodName: functionName,
1346
- input: normalizedOptions,
1347
- hookAnnotator: hooks?.annotator,
1348
- methodAnnotator: annotator
1349
- });
1350
- const hookBase = {
1351
- methodName: functionName,
1352
- args,
1353
- isPaginated: true,
1354
- depth,
1355
- callId: context.callId,
1356
- callOrigin: context.callOrigin,
1357
- annotations: context.annotations
1358
- };
1359
- hooks?.onMethodStart?.({ ...hookBase });
1360
- try {
1361
- const validatedOptions = mergeCallOptions(
1362
- parseCallOptions(normalizedOptions, {
1363
- schema,
1364
- policy: frameworkOptions,
1365
- adaptError
1366
- })
1367
- );
1368
- const pageSize = validatedOptions.pageSize ?? defaultPageSize;
1369
- const optimizedOptions = {
1370
- ...validatedOptions,
1371
- pageSize
1372
- };
1373
- const iterator = paginate(
1374
- (pageOptions) => pageFunction(pageOptions, context),
1375
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1376
- optimizedOptions
1377
- );
1378
- const firstPagePromise = iterator.next().then((result) => {
1379
- if (result.done) {
1380
- throw new Error("Paginate should always iterate at least once");
1381
- }
1382
- return result.value;
1383
- });
1384
- if (hooks?.onMethodEnd) {
1385
- firstPagePromise.then(
1386
- () => {
1387
- hooks.onMethodEnd({
1388
- ...hookBase,
1389
- durationMs: Date.now() - startTime
1390
- });
1391
- },
1392
- (error) => {
1393
- hooks.onMethodEnd({
1394
- ...hookBase,
1395
- durationMs: Date.now() - startTime,
1396
- error: error instanceof Error ? error : new Error(String(error))
1397
- });
1398
- }
1399
- );
1400
- }
1401
- const pageStream = async function* () {
1402
- yield await firstPagePromise;
1403
- for await (const page of iterator) {
1404
- yield page;
1405
- }
1406
- }();
1407
- return Object.assign(firstPagePromise, {
1408
- [Symbol.asyncIterator]() {
1409
- return pageStream;
1410
- },
1411
- pages: function() {
1412
- return {
1413
- [Symbol.asyncIterator]() {
1414
- return pageStream;
1415
- }
1416
- };
1417
- },
1418
- items: function() {
1419
- return {
1420
- [Symbol.asyncIterator]: async function* () {
1421
- for await (const page of pageStream) {
1422
- for (const item of page.data) {
1423
- yield item;
1424
- }
1425
- }
1426
- }
1427
- };
1428
- }
1429
- });
1430
- } catch (error) {
1431
- const normalizedError = normalizeError(error, adaptError);
1432
- hooks?.onMethodEnd?.({
1433
- ...hookBase,
1434
- durationMs: Date.now() - startTime,
1435
- error: normalizedError
1436
- });
1437
- throw normalizedError;
1438
- }
1439
- });
1440
- }
1441
- };
1442
- return namedFunctions[functionName];
1186
+ Object.defineProperty(error, "coreCode", {
1187
+ value: options.code,
1188
+ enumerable: false,
1189
+ configurable: true,
1190
+ writable: false
1191
+ });
1192
+ return error;
1193
+ }
1194
+ function isCoreError(value) {
1195
+ return Boolean(
1196
+ value && typeof value === "object" && value[CORE_ERROR_SYMBOL] === true
1197
+ );
1198
+ }
1199
+ function getCoreErrorCode(value) {
1200
+ if (!isCoreError(value)) return void 0;
1201
+ return value.coreCode;
1202
+ }
1203
+ function getCoreErrorCause(value) {
1204
+ if (!isCoreError(value)) return void 0;
1205
+ return value.cause;
1206
+ }
1207
+
1208
+ // src/utils/pagination-utils.ts
1209
+ var CURSOR_VERSION = 1;
1210
+ var CURSOR_SOURCE = {
1211
+ API: "api",
1212
+ SDK: "sdk",
1213
+ CONCAT: "concat"
1214
+ };
1215
+ function encodeBase64(str) {
1216
+ return btoa(
1217
+ Array.from(
1218
+ new TextEncoder().encode(str),
1219
+ (b) => String.fromCharCode(b)
1220
+ ).join("")
1221
+ );
1443
1222
  }
1444
-
1445
- // src/utils/plugin-utils.ts
1446
- function createPluginMethod(sdk, config) {
1447
- logDeprecation(
1448
- "createPluginMethod() is deprecated. Author methods with defineMethod instead."
1223
+ function decodeBase64(str) {
1224
+ return new TextDecoder().decode(
1225
+ Uint8Array.from(atob(str), (c) => c.charCodeAt(0))
1449
1226
  );
1450
- const { name, inputSchema, handler, ...metaFields } = config;
1451
- const namedHandlers = {
1452
- [name]: async function(options) {
1453
- return handler({ sdk, options });
1454
- }
1227
+ }
1228
+ function encodeApiCursor(cursor) {
1229
+ const envelope = {
1230
+ v: CURSOR_VERSION,
1231
+ source: CURSOR_SOURCE.API,
1232
+ cursor
1455
1233
  };
1456
- const wrappedFn = createFunction(namedHandlers[name], {
1457
- sdk,
1458
- schema: inputSchema
1459
- });
1460
- return {
1461
- [name]: wrappedFn,
1462
- context: {
1463
- meta: {
1464
- [name]: {
1465
- ...metaFields,
1466
- ...inputSchema ? { inputSchema } : {}
1467
- }
1468
- }
1469
- }
1234
+ return encodeBase64(JSON.stringify(envelope));
1235
+ }
1236
+ function encodeSdkCursor(offset, cursor) {
1237
+ const envelope = {
1238
+ v: CURSOR_VERSION,
1239
+ source: CURSOR_SOURCE.SDK,
1240
+ cursor,
1241
+ offset
1470
1242
  };
1243
+ return encodeBase64(JSON.stringify(envelope));
1471
1244
  }
1472
- function createPaginatedPluginMethod(sdk, config) {
1473
- logDeprecation(
1474
- 'createPaginatedPluginMethod() is deprecated. Author list methods with defineMethod output "list" instead.'
1475
- );
1476
- const {
1477
- name,
1478
- inputSchema,
1479
- handler,
1480
- adaptPage,
1481
- defaultPageSize,
1482
- ...metaFields
1483
- } = config;
1484
- const namedHandlers = {
1485
- [name]: function(options) {
1486
- return handler({ sdk, options });
1245
+ function decodeIncomingCursor(incoming) {
1246
+ if (!incoming) {
1247
+ return { offset: 0, cursor: void 0 };
1248
+ }
1249
+ try {
1250
+ const decoded = decodeBase64(incoming);
1251
+ const envelope = JSON.parse(decoded);
1252
+ if (envelope.v !== CURSOR_VERSION) {
1253
+ return { offset: 0, cursor: incoming };
1487
1254
  }
1488
- };
1489
- const wrappedFn = createPaginatedFunction(namedHandlers[name], {
1490
- sdk,
1491
- schema: inputSchema,
1492
- name,
1493
- // The page loop reads the page controls out of the call object, so a
1494
- // handler's schema does not have to declare them. It reads nothing else:
1495
- // no legacy handler honors the caller's output skip.
1496
- frameworkOptions: PAGE_FRAMEWORK_OPTIONS,
1497
- defaultPageSize,
1498
- adaptPage
1499
- });
1500
- return {
1501
- [name]: wrappedFn,
1502
- context: {
1503
- meta: {
1504
- [name]: {
1505
- ...metaFields,
1506
- ...inputSchema ? { inputSchema } : {}
1507
- }
1508
- }
1255
+ if (envelope.source === CURSOR_SOURCE.SDK) {
1256
+ return { offset: envelope.offset ?? 0, cursor: envelope.cursor };
1509
1257
  }
1510
- };
1258
+ if (envelope.source === CURSOR_SOURCE.API) {
1259
+ return { offset: 0, cursor: envelope.cursor };
1260
+ }
1261
+ return { offset: 0, cursor: incoming };
1262
+ } catch {
1263
+ return { offset: 0, cursor: incoming };
1264
+ }
1511
1265
  }
1512
- function splitPluginContribution(result) {
1513
- const { context, ...rootKeys } = result;
1514
- const { meta, hooks, ...contextRest } = context ?? {};
1515
- return {
1516
- rootKeys,
1517
- meta: meta ?? {},
1518
- hooks: hooks ?? {},
1519
- contextRest
1520
- };
1266
+ function createPrefixedCursor(prefix, cursor) {
1267
+ if (!cursor) {
1268
+ return `${prefix}::`;
1269
+ }
1270
+ return `${prefix}::${cursor}`;
1521
1271
  }
1522
- var RESERVED_ROOT_KEYS = /* @__PURE__ */ new Set([
1523
- "context",
1524
- "getRegistry"
1525
- ]);
1526
- function hasOwn(obj, key) {
1527
- return Object.prototype.hasOwnProperty.call(obj, key);
1272
+ function splitPrefixedCursor(cursor, prefixes) {
1273
+ if (!cursor) {
1274
+ return [void 0, void 0];
1275
+ }
1276
+ const [prefix, ...rest] = cursor.split("::");
1277
+ if (prefixes && !prefixes.includes(prefix)) {
1278
+ return [void 0, cursor];
1279
+ }
1280
+ cursor = rest.join("::");
1281
+ if (!cursor) {
1282
+ return [prefix, void 0];
1283
+ }
1284
+ return [prefix, cursor];
1528
1285
  }
1529
- function setOwn(target, key, value) {
1530
- Object.defineProperty(target, key, {
1531
- value,
1532
- enumerable: true,
1533
- configurable: true,
1534
- writable: true
1535
- });
1286
+ async function* paginateMaxItemsWithUnencodedCursor(pageFunction, pageOptions) {
1287
+ let cursor = pageOptions?.cursor;
1288
+ let totalItemsYielded = 0;
1289
+ const maxItems = pageOptions?.maxItems;
1290
+ const pageSize = pageOptions?.pageSize;
1291
+ do {
1292
+ const options = {
1293
+ ...pageOptions || {},
1294
+ cursor,
1295
+ pageSize: maxItems !== void 0 && pageSize !== void 0 ? Math.min(pageSize, maxItems) : pageSize
1296
+ };
1297
+ const page = await pageFunction(options);
1298
+ if (maxItems !== void 0) {
1299
+ const remainingItems = maxItems - totalItemsYielded;
1300
+ if (page.data.length >= remainingItems) {
1301
+ yield {
1302
+ ...page,
1303
+ data: page.data.slice(0, remainingItems),
1304
+ nextCursor: void 0
1305
+ };
1306
+ break;
1307
+ }
1308
+ }
1309
+ yield page;
1310
+ totalItemsYielded += page.data.length;
1311
+ cursor = page.nextCursor;
1312
+ } while (cursor);
1313
+ }
1314
+ async function* paginateMaxItems(pageFunction, pageOptions) {
1315
+ const { cursor } = decodeIncomingCursor(pageOptions?.cursor);
1316
+ const options = {
1317
+ ...pageOptions || {},
1318
+ cursor
1319
+ };
1320
+ for await (const page of paginateMaxItemsWithUnencodedCursor(
1321
+ pageFunction,
1322
+ options
1323
+ )) {
1324
+ yield {
1325
+ ...page,
1326
+ nextCursor: page.nextCursor ? encodeApiCursor(page.nextCursor) : void 0
1327
+ };
1328
+ }
1536
1329
  }
1537
- function checkCollisions(target, source, kind, callerLabel, override) {
1538
- if (kind === "root key") {
1539
- checkRootKeyCollisions(target, Object.keys(source), override, callerLabel);
1330
+ async function* paginateBuffered(pageFunction, pageOptions) {
1331
+ const pageSize = pageOptions?.pageSize;
1332
+ const { offset: cursorOffset, cursor: initialCursor } = decodeIncomingCursor(
1333
+ pageOptions?.cursor
1334
+ );
1335
+ const requestedMaxItems = pageOptions?.maxItems;
1336
+ const options = {
1337
+ ...pageOptions || {},
1338
+ cursor: initialCursor,
1339
+ // SDK cursors can carry an offset into a raw backend page. Since maxItems
1340
+ // is expected to be relative to the resumed position, we add that offset
1341
+ // so raw pagination still yields enough items after offset slicing.
1342
+ maxItems: requestedMaxItems !== void 0 && cursorOffset > 0 ? requestedMaxItems + cursorOffset : requestedMaxItems
1343
+ };
1344
+ if (!pageSize) {
1345
+ for await (const page of paginateMaxItemsWithUnencodedCursor(
1346
+ pageFunction,
1347
+ options
1348
+ )) {
1349
+ yield {
1350
+ ...page,
1351
+ nextCursor: page.nextCursor ? encodeApiCursor(page.nextCursor) : void 0
1352
+ };
1353
+ }
1540
1354
  return;
1541
1355
  }
1542
- for (const key of Object.keys(source)) {
1543
- if (!override && hasOwn(target, key)) {
1544
- throw new Error(
1545
- `${callerLabel}: duplicate ${kind} "${key}". If the override is intentional, pass { override: true } in the options.`
1546
- );
1356
+ let bufferedPages = [];
1357
+ let isFirstPage = true;
1358
+ let rawCursor;
1359
+ for await (let page of paginateMaxItemsWithUnencodedCursor(
1360
+ pageFunction,
1361
+ options
1362
+ )) {
1363
+ const nextRawCursor = page.nextCursor;
1364
+ if (isFirstPage) {
1365
+ isFirstPage = false;
1366
+ if (cursorOffset) {
1367
+ page = {
1368
+ ...page,
1369
+ data: page.data.slice(cursorOffset)
1370
+ };
1371
+ }
1372
+ }
1373
+ const bufferedLength = bufferedPages.reduce(
1374
+ (acc, p) => acc + p.data.length,
1375
+ 0
1376
+ );
1377
+ if (bufferedLength + page.data.length < pageSize) {
1378
+ bufferedPages.push(page);
1379
+ rawCursor = nextRawCursor;
1380
+ continue;
1381
+ }
1382
+ const bufferedItems = bufferedPages.map((p) => p.data).flat();
1383
+ const allItems = [...bufferedItems, ...page.data];
1384
+ const pageItems = allItems.slice(0, pageSize);
1385
+ const remainingItems = allItems.slice(pageItems.length);
1386
+ if (remainingItems.length === 0) {
1387
+ yield {
1388
+ ...page,
1389
+ data: pageItems,
1390
+ nextCursor: nextRawCursor ? encodeApiCursor(nextRawCursor) : void 0
1391
+ };
1392
+ bufferedPages = [];
1393
+ rawCursor = nextRawCursor;
1394
+ continue;
1395
+ }
1396
+ yield {
1397
+ ...page,
1398
+ data: pageItems,
1399
+ nextCursor: encodeSdkCursor(
1400
+ page.data.length - remainingItems.length,
1401
+ rawCursor
1402
+ )
1403
+ };
1404
+ while (remainingItems.length > pageSize) {
1405
+ const chunkItems = remainingItems.splice(0, pageSize);
1406
+ yield {
1407
+ ...page,
1408
+ data: chunkItems,
1409
+ nextCursor: encodeSdkCursor(
1410
+ page.data.length - remainingItems.length,
1411
+ rawCursor
1412
+ )
1413
+ };
1547
1414
  }
1415
+ bufferedPages = [
1416
+ {
1417
+ ...page,
1418
+ data: remainingItems
1419
+ }
1420
+ ];
1421
+ rawCursor = nextRawCursor;
1422
+ }
1423
+ if (bufferedPages.length > 0) {
1424
+ const lastBufferedPage = bufferedPages.slice(-1)[0];
1425
+ const bufferedItems = bufferedPages.map((p) => p.data).flat();
1426
+ yield {
1427
+ ...lastBufferedPage,
1428
+ data: bufferedItems
1429
+ };
1430
+ }
1431
+ }
1432
+ var paginate = paginateBuffered;
1433
+ function encodeConcatCursor(index, cursor) {
1434
+ const envelope = {
1435
+ v: CURSOR_VERSION,
1436
+ source: CURSOR_SOURCE.CONCAT,
1437
+ index,
1438
+ cursor
1439
+ };
1440
+ return encodeBase64(JSON.stringify(envelope));
1441
+ }
1442
+ function decodeConcatCursor(incoming) {
1443
+ if (!incoming) {
1444
+ return { index: 0, cursor: void 0 };
1548
1445
  }
1549
- }
1550
- function checkRootKeyCollisions(target, keys, override, callerLabel) {
1551
- for (const key of keys) {
1552
- if (RESERVED_ROOT_KEYS.has(key)) {
1553
- throw new Error(
1554
- `${callerLabel}: plugin attempted to register reserved root key "${key}". The SDK uses this key for its own accessor; rename the plugin's method.`
1555
- );
1556
- }
1557
- if (!override && hasOwn(target, key)) {
1558
- throw new Error(
1559
- `${callerLabel}: duplicate root key "${key}". If the override is intentional, pass { override: true } in the options.`
1560
- );
1446
+ try {
1447
+ const envelope = JSON.parse(decodeBase64(incoming));
1448
+ if (envelope.v === CURSOR_VERSION && envelope.source === CURSOR_SOURCE.CONCAT && typeof envelope.index === "number") {
1449
+ return { index: envelope.index, cursor: envelope.cursor };
1561
1450
  }
1451
+ } catch {
1562
1452
  }
1453
+ return { index: 0, cursor: incoming };
1563
1454
  }
1564
- function applyOwnProperties(target, source) {
1565
- for (const key of Object.keys(source)) {
1566
- setOwn(target, key, source[key]);
1455
+ async function concatLists({
1456
+ sources,
1457
+ pageSize = 100,
1458
+ cursor
1459
+ }) {
1460
+ if (sources.length === 0) {
1461
+ return { data: [] };
1567
1462
  }
1568
- }
1569
- function createPluginAccumulator(initialProperties = {}, initialContext = {}) {
1570
- const initialMeta = initialContext.meta ?? {};
1571
- const initialHooks = initialContext.hooks ?? {};
1572
- const context = {
1573
- ...initialContext,
1574
- meta: { ...initialMeta },
1575
- hooks: { ...initialHooks }
1576
- };
1577
- const view = { ...initialProperties, context };
1578
- return { view, context };
1579
- }
1580
- function mergeContribution(propertiesTarget, contextTarget, contribution, options) {
1581
- checkCollisions(
1582
- propertiesTarget,
1583
- contribution.rootKeys,
1584
- "root key",
1585
- options.callerLabel,
1586
- options.override
1587
- );
1588
- checkCollisions(
1589
- contextTarget.meta,
1590
- contribution.meta,
1591
- "context.meta key",
1592
- options.callerLabel,
1593
- options.override
1594
- );
1595
- checkCollisions(
1596
- contextTarget,
1597
- contribution.contextRest,
1598
- "context key",
1599
- options.callerLabel,
1600
- options.override
1601
- );
1602
- applyOwnProperties(propertiesTarget, contribution.rootKeys);
1603
- applyOwnProperties(contextTarget.meta, contribution.meta);
1604
- applyOwnProperties(contextTarget, contribution.contextRest);
1605
- contextTarget.hooks = buildHooks(contextTarget.hooks, contribution.hooks);
1606
- }
1607
- function applyPluginContribution(acc, contribution, options) {
1608
- mergeContribution(acc.view, acc.context, contribution, options);
1609
- }
1610
- function wrapAsSdk(properties, context) {
1611
- const sdk = {
1612
- ...properties,
1613
- context,
1614
- getRegistry(qopts) {
1615
- return buildRegistry({
1616
- sdk,
1617
- meta: context.meta,
1618
- packageFilter: qopts?.package
1619
- });
1463
+ const pageFunction = async (options) => {
1464
+ let { index, cursor: listCursor } = decodeConcatCursor(options.cursor);
1465
+ while (index < sources.length) {
1466
+ const page = await sources[index]({ cursor: listCursor });
1467
+ const hasMoreInList = page.nextCursor != null;
1468
+ if (page.data.length === 0 && !hasMoreInList) {
1469
+ index++;
1470
+ listCursor = void 0;
1471
+ continue;
1472
+ }
1473
+ return {
1474
+ data: page.data,
1475
+ nextCursor: hasMoreInList ? encodeConcatCursor(index, page.nextCursor) : index < sources.length - 1 ? encodeConcatCursor(index + 1, void 0) : void 0
1476
+ };
1620
1477
  }
1478
+ return { data: [] };
1621
1479
  };
1622
- return sdk;
1480
+ const result = await paginateBuffered(pageFunction, {
1481
+ pageSize,
1482
+ cursor
1483
+ }).next();
1484
+ return result.done ? { data: [] } : result.value;
1623
1485
  }
1624
- function wrapAccumulatorAsSdk(acc) {
1625
- const { context: _ctx, ...rootKeys } = acc.view;
1626
- return wrapAsSdk(
1627
- rootKeys,
1628
- acc.context
1629
- );
1486
+ function concatPaginated({
1487
+ sources,
1488
+ pageSize,
1489
+ cursor
1490
+ }) {
1491
+ logDeprecation("concatPaginated() is deprecated. Use concatLists() instead.");
1492
+ return concatLists({ sources, pageSize, cursor });
1630
1493
  }
1631
- function applyPluginToSdk(sdk, plugin, options) {
1632
- const context = sdk.context;
1633
- const contribution = splitPluginContribution(
1634
- plugin(sdk)
1494
+ function toIterable(source) {
1495
+ logDeprecation(
1496
+ "toIterable() is deprecated. Call .pages() on the paginated result instead."
1635
1497
  );
1636
- mergeContribution(sdk, context, contribution, {
1637
- callerLabel: "addPlugin",
1638
- override: options.override === true
1639
- });
1640
- return contribution;
1641
- }
1642
- function resolveStack(head) {
1643
- const entries = [];
1644
- let node = head;
1645
- while (node) {
1646
- entries.unshift({ apply: node.entry, override: node.override });
1647
- node = node.prev;
1648
- }
1649
- return entries;
1650
- }
1651
- function composeStackHooks(hooks) {
1652
- let composed = {};
1653
- for (const h of hooks) composed = buildHooks(composed, h);
1654
- return composed;
1498
+ return { [Symbol.asyncIterator]: () => source[Symbol.asyncIterator]() };
1655
1499
  }
1656
- function collapseStackEntries(entries, callerLabel) {
1657
- return (outerSdk) => {
1658
- const { context: outerContext, ...outerProperties } = outerSdk ?? {};
1659
- const viewAcc = createPluginAccumulator(outerProperties, outerContext);
1660
- const contribsAcc = createPluginAccumulator();
1661
- const hooks = [];
1662
- for (const { apply, override } of entries) {
1663
- const contribution = splitPluginContribution(
1664
- apply(viewAcc.view)
1665
- );
1666
- const hookless = { ...contribution, hooks: {} };
1667
- applyPluginContribution(viewAcc, hookless, { callerLabel, override });
1668
- applyPluginContribution(contribsAcc, hookless, { callerLabel, override });
1669
- hooks.push(contribution.hooks);
1670
- }
1671
- const stackHooks = composeStackHooks(hooks);
1672
- viewAcc.context.hooks = buildHooks(viewAcc.context.hooks, stackHooks);
1673
- contribsAcc.context.hooks = stackHooks;
1674
- const { context: _ignored, ...contributedRoot } = contribsAcc.view;
1675
- return {
1676
- ...contributedRoot,
1677
- context: contribsAcc.context
1678
- };
1679
- };
1500
+
1501
+ // src/utils/promise-utils.ts
1502
+ function isPromiseLike(value) {
1503
+ return value !== null && typeof value === "object" && typeof value.then === "function";
1680
1504
  }
1681
- function buildStackAccumulator(head, callerLabel) {
1682
- const entries = resolveStack(head);
1683
- const acc = createPluginAccumulator();
1684
- const hooks = [];
1685
- for (const { apply, override } of entries) {
1686
- const contribution = splitPluginContribution(
1687
- apply(acc.view)
1688
- );
1689
- applyPluginContribution(
1690
- acc,
1691
- { ...contribution, hooks: {} },
1692
- { callerLabel, override }
1505
+
1506
+ // src/utils/validation.ts
1507
+ var parseOrThrow = (schema, input, { adaptError } = {}) => {
1508
+ const result = schema.safeParse(input);
1509
+ if (!result.success) {
1510
+ const errorMessages = result.error.issues.map((issue) => {
1511
+ const path = issue.path.length > 0 ? issue.path.join(".") : "input";
1512
+ return `${path}: ${issue.message}`;
1513
+ });
1514
+ throw createCoreError(
1515
+ {
1516
+ code: CoreErrorCode.Validation,
1517
+ message: `Validation failed:
1518
+ ${errorMessages.join("\n ")}`,
1519
+ details: {
1520
+ zodErrors: result.error.issues,
1521
+ input
1522
+ }
1523
+ },
1524
+ adaptError
1693
1525
  );
1694
- hooks.push(contribution.hooks);
1695
- }
1696
- acc.context.hooks = composeStackHooks(hooks);
1697
- return acc;
1698
- }
1699
- function composePlugins(...plugins) {
1700
- logDeprecation(
1701
- "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."
1702
- );
1703
- let head = null;
1704
- for (const plugin of plugins) {
1705
- head = { entry: plugin, override: false, prev: head };
1706
1526
  }
1707
- const entries = resolveStack(head);
1708
- return collapseStackEntries(entries, "composePlugins");
1709
- }
1710
- function createPluginStack() {
1711
- logDeprecation(
1712
- "createPluginStack() is deprecated. Compose with definePlugin and build with createSdk instead."
1713
- );
1714
- return buildPluginStack(null, "createPluginStack");
1715
- }
1716
- function buildPluginStack(head, callerLabel) {
1717
- const stack = {
1718
- use(plugin, options) {
1719
- const next = {
1720
- entry: plugin,
1721
- override: options?.override === true,
1722
- prev: head
1723
- };
1724
- return buildPluginStack(next, callerLabel);
1725
- },
1726
- toPlugin() {
1727
- const entries = resolveStack(head);
1728
- return collapseStackEntries(entries, callerLabel);
1729
- },
1730
- toSdk() {
1731
- return wrapAccumulatorAsSdk(
1732
- buildStackAccumulator(head, callerLabel)
1733
- );
1734
- }
1527
+ return result.data;
1528
+ };
1529
+ function createValidator(schema, { adaptError } = {}) {
1530
+ return function validateFn(input) {
1531
+ return parseOrThrow(schema, input, { adaptError });
1735
1532
  };
1736
- return stack;
1737
1533
  }
1534
+ var validateOptions = (schema, options, { adaptError } = {}) => parseOrThrow(schema, options, { adaptError });
1738
1535
 
1739
- // src/model/shared.ts
1740
- var CONTEXT = Symbol.for("kitcore.context");
1741
- function parseId(id) {
1742
- const at = id.lastIndexOf("/");
1743
- return at === -1 ? { name: id, namespace: void 0 } : { name: id.slice(at + 1), namespace: id.slice(0, at) };
1744
- }
1745
- function makeId(name, namespace, kind = "leaf") {
1746
- validateName(name, kind);
1747
- if (namespace !== void 0) validateNamespace(namespace);
1748
- return namespace ? `${namespace}/${name}` : name;
1536
+ // src/utils/call-options.ts
1537
+ var import_zod3 = require("zod");
1538
+ var CallFrameworkOptionsSchema = import_zod3.z.object({
1539
+ /** Page to fetch. Opaque to kitcore: the head's API defines the format. */
1540
+ cursor: import_zod3.z.string().optional(),
1541
+ /** Items per page. */
1542
+ pageSize: import_zod3.z.number().int().min(1).optional(),
1543
+ /** Stop after this many items, across pages. */
1544
+ maxItems: import_zod3.z.number().int().min(0).optional(),
1545
+ /** Bypass output validation for this one call. */
1546
+ skipOutputDataValidation: import_zod3.z.boolean().optional()
1547
+ });
1548
+ var ITEM_FRAMEWORK_OPTIONS = {
1549
+ claims: ["skipOutputDataValidation"],
1550
+ injects: []
1551
+ };
1552
+ var LIST_FRAMEWORK_OPTIONS = {
1553
+ claims: ["cursor", "pageSize", "maxItems", "skipOutputDataValidation"],
1554
+ injects: ["cursor", "pageSize"]
1555
+ };
1556
+ var NO_FRAMEWORK_OPTIONS = {
1557
+ claims: [],
1558
+ injects: []
1559
+ };
1560
+ function isRecord(value) {
1561
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1749
1562
  }
1750
- var NAME_RE = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
1751
- var SEGMENT_RE = /^@?[A-Za-z0-9._-]+$/;
1752
- function validateName(name, kind) {
1753
- if (name === "") throw new Error("Plugin name must not be empty.");
1754
- if (kind === "leaf") {
1755
- if (!NAME_RE.test(name)) {
1756
- throw new Error(
1757
- `Plugin name "${name}" must be a valid JS identifier (it is the binding name).`
1758
- );
1563
+ function strictlyRefused(error, claims) {
1564
+ const refused = /* @__PURE__ */ new Set();
1565
+ for (const issue of error.issues) {
1566
+ if (issue.code !== "unrecognized_keys" || issue.path.length > 0) continue;
1567
+ for (const key of issue.keys) {
1568
+ if (claims.includes(key)) refused.add(key);
1759
1569
  }
1760
- } else if (!SEGMENT_RE.test(name)) {
1761
- throw new Error(
1762
- `Plugin name "${name}" must be package-like (letters, digits, ".", "_", "-", optional leading "@") with no "/".`
1763
- );
1764
1570
  }
1571
+ return [...refused];
1765
1572
  }
1766
- function validateNamespace(namespace) {
1767
- if (namespace === "") throw new Error("Plugin namespace must not be empty.");
1768
- for (const segment of namespace.split("/")) {
1769
- if (!SEGMENT_RE.test(segment)) {
1770
- throw new Error(
1771
- `Plugin namespace "${namespace}" is invalid: each "/"-separated segment must be package-like (letters, digits, ".", "_", "-", optional leading "@").`
1772
- );
1773
- }
1573
+ function withoutKeys(options, keys) {
1574
+ const next = {};
1575
+ for (const [key, value] of Object.entries(options)) {
1576
+ if (!keys.includes(key)) next[key] = value;
1774
1577
  }
1578
+ return next;
1775
1579
  }
1776
-
1777
- // src/model/types.ts
1778
- var LEAF_META_KEYS = [
1779
- "description",
1780
- "categories",
1781
- "type",
1782
- "itemType",
1783
- "returnType",
1784
- "outputSchema",
1785
- "packages",
1786
- "stability",
1787
- "experimental",
1788
- "confirm",
1789
- "deprecation",
1790
- "aliases",
1791
- "supportsJsonOutput"
1792
- ];
1793
-
1794
- // src/model/define.ts
1795
- function normalizeImports(deps) {
1796
- if (!deps) return { plugins: [], bindings: [] };
1797
- const seen = /* @__PURE__ */ new Map();
1798
- const bindings = [];
1799
- const add = (binding, id, optional) => {
1800
- const priorId = seen.get(binding);
1801
- if (priorId !== void 0 && priorId !== id) {
1802
- throw new Error(
1803
- `Import binding "${binding}" is declared twice. Two different plugins ("${priorId}" and "${id}") bind the same name; wrap one in selectExports to rename it.`
1804
- );
1805
- }
1806
- if (priorId === void 0) {
1807
- seen.set(binding, id);
1808
- bindings.push(optional ? { binding, id, optional } : { binding, id });
1809
- }
1810
- };
1811
- for (const plugin of deps) {
1812
- if (plugin.pluginType === "aggregate") {
1813
- for (const [binding, child] of Object.entries(plugin.exports)) {
1814
- add(binding, child.id);
1815
- }
1816
- } else if (plugin.pluginType === "hook") {
1817
- } else {
1818
- add(plugin.name, plugin.id, plugin.optional);
1580
+ function parseCallOptions(options, {
1581
+ schema,
1582
+ policy = NO_FRAMEWORK_OPTIONS,
1583
+ adaptError
1584
+ } = {}) {
1585
+ const claims = policy.claims;
1586
+ const call = isRecord(options) ? options : void 0;
1587
+ let framework = {};
1588
+ if (call && claims.length > 0) {
1589
+ const present = {};
1590
+ for (const key of claims) {
1591
+ if (key in call) present[key] = call[key];
1819
1592
  }
1593
+ framework = parseOrThrow2(CallFrameworkOptionsSchema, present, adaptError);
1820
1594
  }
1821
- return { plugins: deps, bindings };
1822
- }
1823
- function collectLeafMeta(config) {
1824
- let meta;
1825
- for (const key of LEAF_META_KEYS) {
1826
- if (config[key] !== void 0) (meta ?? (meta = {}))[key] = config[key];
1595
+ if (!schema) return { framework, domain: options, supplied: /* @__PURE__ */ new Set() };
1596
+ const first = schema.safeParse(options);
1597
+ if (first.success) {
1598
+ return { framework, domain: first.data, supplied: /* @__PURE__ */ new Set() };
1599
+ }
1600
+ const refused = call ? strictlyRefused(first.error, claims) : [];
1601
+ if (refused.length === 0) throw toCoreError(first.error, options, adaptError);
1602
+ const retry = schema.safeParse(withoutKeys(call, refused));
1603
+ if (!retry.success) {
1604
+ throw toCoreError(retry.error, options, adaptError);
1827
1605
  }
1828
- return meta;
1606
+ return { framework, domain: retry.data, supplied: new Set(refused) };
1607
+ }
1608
+ function mergeCallOptions({
1609
+ framework,
1610
+ domain
1611
+ }) {
1612
+ const claimed = Object.entries(framework);
1613
+ if (!isRecord(domain) || claimed.length === 0) return domain;
1614
+ return { ...domain, ...Object.fromEntries(claimed) };
1615
+ }
1616
+ function withheldFromRun(policy) {
1617
+ return new Set(policy.claims.filter((key) => !policy.injects.includes(key)));
1618
+ }
1619
+ function stripFrameworkOnlyOptions(options, withheld) {
1620
+ if (withheld.size === 0 || !isRecord(options)) return options;
1621
+ const entries = Object.entries(options);
1622
+ if (!entries.some(([key]) => withheld.has(key))) return options;
1623
+ return Object.fromEntries(entries.filter(([key]) => !withheld.has(key)));
1829
1624
  }
1830
- function formatDynamicMemberName(path) {
1831
- return path.map((seg) => typeof seg === "string" ? seg : `{${seg.param}}`).join(".");
1625
+ function parseOrThrow2(schema, input, adaptError) {
1626
+ const result = schema.safeParse(input);
1627
+ if (result.success) return result.data;
1628
+ throw toCoreError(result.error, input, adaptError);
1832
1629
  }
1833
- function collectDynamicMembers(members) {
1834
- if (!members?.length) return void 0;
1835
- return members.map((member) => {
1836
- const root = member.path[0];
1837
- if (typeof root !== "string") {
1838
- throw new Error(
1839
- "defineProperty: a dynamicMember path must start with a literal segment (the owning binding), not a { param }."
1840
- );
1841
- }
1842
- const leaf = collectLeafMeta(member) ?? {};
1843
- return {
1844
- name: formatDynamicMemberName(member.path),
1845
- rootBinding: root,
1846
- meta: member.inputSchema ? { ...leaf, inputSchema: member.inputSchema } : leaf
1847
- };
1630
+ function toCoreError(error, input, adaptError) {
1631
+ const messages = error.issues.map((issue) => {
1632
+ const path = issue.path.length > 0 ? issue.path.join(".") : "input";
1633
+ return `${path}: ${issue.message}`;
1848
1634
  });
1849
- }
1850
- function defineMethod(configOrRef, refConfig) {
1851
- const config = refConfig === void 0 ? configOrRef : {
1852
- ...refConfig,
1853
- name: configOrRef.name,
1854
- namespace: configOrRef.namespace
1855
- };
1856
- const deps = normalizeImports(config.imports);
1857
- return {
1858
- pluginType: "method",
1859
- name: config.name,
1860
- namespace: config.namespace,
1861
- id: makeId(config.name, config.namespace),
1862
- imports: deps.plugins,
1863
- importBindings: deps.bindings,
1864
- inputSchema: config.inputSchema,
1865
- skipInputValidation: config.skipInputValidation,
1866
- skipOutputValidation: config.skipOutputValidation,
1867
- meta: collectLeafMeta(config),
1868
- resolvers: config.resolvers,
1869
- formatter: config.formatter,
1870
- annotator: config.annotator,
1871
- output: config.output,
1872
- positional: config.positional,
1873
- setup: config.setup,
1874
- dispose: config.dispose,
1875
- run: config.run
1876
- };
1877
- }
1878
- var OVERRIDABLE = [
1879
- "description",
1880
- "categories",
1881
- "itemType",
1882
- "returnType",
1883
- "packages",
1884
- "experimental",
1885
- "deprecation",
1886
- "supportsJsonOutput"
1887
- ];
1888
- function assertOverridable(target, fields) {
1889
- const offered = Object.keys(fields).filter(
1890
- (key) => !OVERRIDABLE.includes(key)
1891
- );
1892
- if (offered.length === 0) return;
1893
- throw new Error(
1894
- `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(", ")}.`
1635
+ return createCoreError(
1636
+ {
1637
+ code: CoreErrorCode.Validation,
1638
+ message: `Validation failed:
1639
+ ${messages.join("\n ")}`,
1640
+ details: { zodErrors: error.issues, input }
1641
+ },
1642
+ adaptError
1895
1643
  );
1896
1644
  }
1897
- function buildOverride(target, namespace, fields) {
1898
- assertOverridable(target, fields);
1645
+
1646
+ // src/utils/async-context.ts
1647
+ var import_node_async_hooks = require("async_hooks");
1648
+ function createAsyncContext() {
1649
+ let store = null;
1650
+ try {
1651
+ store = new import_node_async_hooks.AsyncLocalStorage();
1652
+ } catch {
1653
+ store = null;
1654
+ }
1899
1655
  return {
1900
- pluginType: "method-override",
1901
- name: `override:${target}`,
1902
- id: namespace ? `${namespace}/override:${target}` : `override:${target}`,
1903
- target,
1904
- imports: [],
1905
- importBindings: [],
1906
- meta: collectLeafMeta(fields)
1656
+ available: store !== null,
1657
+ run(value, fn) {
1658
+ return store ? store.run(value, fn) : fn();
1659
+ },
1660
+ get() {
1661
+ return store?.getStore();
1662
+ }
1907
1663
  };
1908
1664
  }
1909
- function defineOverride(ref, config = {}) {
1910
- const { namespace, ...fields } = config;
1911
- return buildOverride(ref.id, namespace, fields);
1665
+
1666
+ // src/utils/method-scope.ts
1667
+ var scope = createAsyncContext();
1668
+ function getCurrentScope() {
1669
+ return scope.get();
1912
1670
  }
1913
- function defineMethodOverride(config) {
1914
- logDeprecation(
1915
- "defineMethodOverride({ target }) is deprecated. Use defineOverride(method, { ... }), which takes the method or its declareMethod stand-in."
1916
- );
1917
- const { target, namespace, ...fields } = config;
1918
- return buildOverride(target, namespace, fields);
1671
+ function getCurrentDepth() {
1672
+ return getCurrentScope()?.depth ?? 0;
1919
1673
  }
1920
- function assertRequirementPaths(requirements) {
1921
- if (!requirements) return;
1922
- for (const requirement of requirements) {
1923
- if (typeof requirement !== "string" && requirement.length === 0) {
1924
- throw new Error(
1925
- "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."
1926
- );
1927
- }
1928
- }
1674
+ function isNestedMethodCall() {
1675
+ if (!scope.available) return true;
1676
+ const store = scope.get();
1677
+ return store !== void 0 && store.depth > 0;
1929
1678
  }
1930
- function defineResolver(config) {
1931
- const deps = normalizeImports(config.imports);
1932
- const base = { imports: deps.plugins, importBindings: deps.bindings };
1933
- assertRequirementPaths(config.requireParameters);
1934
- const gates = {
1935
- requireParameters: config.requireParameters
1936
- };
1937
- switch (config.type) {
1938
- case "static":
1939
- return {
1940
- ...base,
1941
- ...gates,
1942
- type: "static",
1943
- inputType: config.inputType,
1944
- placeholder: config.placeholder
1945
- };
1946
- case "constant":
1947
- return { ...base, ...gates, type: "constant", value: config.value };
1948
- case "info":
1949
- return { ...base, type: "info", text: config.text ?? "" };
1950
- case "object":
1951
- return {
1952
- ...base,
1953
- ...gates,
1954
- type: "object",
1955
- properties: config.properties,
1956
- definitions: config.definitions,
1957
- getProperties: config.getProperties,
1958
- additionalKeys: config.additionalKeys
1959
- };
1960
- case "array":
1961
- return {
1962
- ...base,
1963
- ...gates,
1964
- type: "array",
1965
- items: config.items,
1966
- minItems: config.minItems,
1967
- maxItems: config.maxItems,
1968
- itemValueType: config.itemValueType,
1969
- definitions: config.definitions
1970
- };
1971
- default:
1972
- return {
1973
- ...base,
1974
- ...gates,
1975
- type: "dynamic",
1976
- inputType: config.inputType,
1977
- placeholder: config.placeholder,
1978
- getContext: config.getContext,
1979
- listItems: config.listItems,
1980
- prompt: config.prompt,
1981
- validate: config.validate,
1982
- tryResolveWithoutPrompt: config.tryResolveWithoutPrompt,
1983
- tryResolveFromSearch: config.tryResolveFromSearch
1984
- };
1679
+ var observerReentrancy = 0;
1680
+ function runIsolatedObserver(fn) {
1681
+ observerReentrancy++;
1682
+ try {
1683
+ fn();
1684
+ } catch {
1685
+ } finally {
1686
+ observerReentrancy--;
1985
1687
  }
1986
1688
  }
1987
- function defineFormatter(config) {
1988
- const deps = normalizeImports(config.imports);
1989
- return {
1990
- imports: deps.plugins,
1991
- importBindings: deps.bindings,
1992
- getContext: config.getContext,
1993
- format: config.format
1994
- };
1689
+ function isInsideObserver() {
1690
+ return observerReentrancy > 0;
1995
1691
  }
1996
- function declareMethod(config) {
1997
- const { name, namespace } = parseId(config.id);
1998
- const id = makeId(name, namespace);
1999
- return {
2000
- pluginType: "method",
2001
- name,
2002
- namespace,
2003
- id,
2004
- standIn: true,
2005
- imports: [],
2006
- importBindings: [],
2007
- run: () => {
2008
- throw new Error(
2009
- `Plugin "${id}" is a stand-in (declareMethod) with no implementation. Register the real plugin under this id.`
2010
- );
2011
- }
2012
- };
1692
+ function runInMethodScope(fn) {
1693
+ if (!scope.available) return fn();
1694
+ const currentDepth = scope.get()?.depth ?? -1;
1695
+ return scope.run({ depth: currentDepth + 1 }, fn);
2013
1696
  }
2014
- function declareOptionalMethod(config) {
2015
- const { name, namespace } = parseId(config.id);
2016
- const id = makeId(name, namespace);
2017
- return {
2018
- pluginType: "method",
2019
- name,
2020
- namespace,
2021
- id,
2022
- standIn: true,
2023
- optional: true,
2024
- imports: [],
2025
- importBindings: [],
2026
- run: () => {
2027
- throw new Error(
2028
- `Plugin "${id}" is an optional stand-in (declareOptionalMethod) with no implementation. Its binding is \`undefined\` unless a real plugin is registered under this id.`
2029
- );
2030
- }
2031
- // Requires nothing (phantom carrier `<never, never>`): a consumer that
2032
- // imports it still passes `createSdk`'s completeness check unprovided. The
2033
- // `optional: true` literal drives `PluginSurface` to type the binding
2034
- // `| undefined`.
2035
- };
1697
+ var runWithTelemetryContext = runInMethodScope;
1698
+ var isTelemetryNested = isNestedMethodCall;
1699
+
1700
+ // src/utils/call-context.ts
1701
+ var CALL_CONTEXT_BRAND = Symbol("kitcore.callContext");
1702
+ function isCallContext(value) {
1703
+ return typeof value === "object" && value !== null && value[CALL_CONTEXT_BRAND] === true;
2036
1704
  }
2037
- function defineProperty(config, refConfig) {
2038
- const cfg = refConfig === void 0 ? config : {
2039
- ...refConfig,
2040
- name: config.name,
2041
- namespace: config.namespace
2042
- };
2043
- const deps = normalizeImports(cfg.imports);
2044
- return {
2045
- pluginType: "property",
2046
- name: cfg.name,
2047
- namespace: cfg.namespace,
2048
- id: makeId(cfg.name, cfg.namespace),
2049
- imports: deps.plugins,
2050
- importBindings: deps.bindings,
2051
- setup: cfg.setup,
2052
- dispose: cfg.dispose,
2053
- value: cfg.value,
2054
- get: cfg.get,
2055
- meta: collectLeafMeta(cfg),
2056
- dynamicMembers: collectDynamicMembers(cfg.dynamicMembers)
2057
- };
1705
+ function generateCallId() {
1706
+ try {
1707
+ const webCrypto = globalThis.crypto;
1708
+ if (webCrypto?.randomUUID) {
1709
+ return webCrypto.randomUUID();
1710
+ }
1711
+ if (webCrypto?.getRandomValues) {
1712
+ const bytes = webCrypto.getRandomValues(new Uint8Array(16));
1713
+ const hex = Array.from(bytes, (byte, i) => {
1714
+ const value = i === 6 ? byte & 15 | 64 : i === 8 ? byte & 63 | 128 : byte;
1715
+ return value.toString(16).padStart(2, "0");
1716
+ });
1717
+ return [
1718
+ hex.slice(0, 4).join(""),
1719
+ hex.slice(4, 6).join(""),
1720
+ hex.slice(6, 8).join(""),
1721
+ hex.slice(8, 10).join(""),
1722
+ hex.slice(10, 16).join("")
1723
+ ].join("-");
1724
+ }
1725
+ } catch {
1726
+ }
1727
+ return null;
2058
1728
  }
2059
- function declareProperty(config) {
2060
- const { name, namespace } = parseId(config.id);
1729
+ function rootCallContext({
1730
+ callOrigin = "surface"
1731
+ } = {}) {
2061
1732
  return {
2062
- pluginType: "property",
2063
- name,
2064
- namespace,
2065
- id: makeId(name, namespace),
2066
- standIn: true,
2067
- imports: [],
2068
- importBindings: []
1733
+ callId: generateCallId(),
1734
+ depth: 0,
1735
+ annotations: {},
1736
+ callOrigin,
1737
+ [CALL_CONTEXT_BRAND]: true
2069
1738
  };
2070
1739
  }
2071
- function declareOptionalProperty(config) {
2072
- const { name, namespace } = parseId(config.id);
1740
+ function childCallContext(parent) {
2073
1741
  return {
2074
- pluginType: "property",
2075
- name,
2076
- namespace,
2077
- id: makeId(name, namespace),
2078
- standIn: true,
2079
- optional: true,
2080
- imports: [],
2081
- importBindings: []
2082
- // Requires nothing (phantom carrier `<never, never>`): a consumer that
2083
- // imports it still passes `createSdk`'s completeness check unprovided. The
2084
- // import binding is still typed `TValue | undefined` from the descriptor.
1742
+ callId: parent.callId,
1743
+ depth: parent.depth + 1,
1744
+ annotations: {},
1745
+ callOrigin: parent.callOrigin,
1746
+ [CALL_CONTEXT_BRAND]: true
2085
1747
  };
2086
1748
  }
2087
- function declareDefault({
2088
- plugin
2089
- }) {
2090
- return { ...plugin, defaultSource: plugin };
2091
- }
2092
- function defineHook(config) {
2093
- const deps = normalizeImports(config.imports);
2094
- return {
2095
- pluginType: "hook",
2096
- name: config.name,
2097
- namespace: config.namespace,
2098
- id: makeId(config.name, config.namespace),
2099
- imports: deps.plugins,
2100
- importBindings: deps.bindings,
2101
- setup: config.setup,
2102
- dispose: config.dispose,
2103
- wrap: config.wrap,
2104
- observe: config.observe,
2105
- annotator: config.annotator
2106
- };
1749
+
1750
+ // src/utils/function-utils.ts
1751
+ function resolveCoreOptions(context) {
1752
+ const entry = context.plugins[CORE_OPTIONS_ID];
1753
+ if (!entry) return void 0;
1754
+ return entry.pluginType === "property" && entry.getValue ? entry.getValue() : entry.value;
2107
1755
  }
2108
- function declarePlugin(config) {
2109
- const { name, namespace } = parseId(config.id);
2110
- return {
2111
- pluginType: "aggregate",
2112
- name,
2113
- namespace,
2114
- id: makeId(name, namespace, "aggregate"),
2115
- standIn: true,
2116
- imports: [],
2117
- importBindings: [],
2118
- exports: normalizeExports(config.exports)
2119
- };
1756
+ var INTERNAL_CALL = Symbol("kitcore.internalCall");
1757
+ function resolveCallContext(secondArg) {
1758
+ return isCallContext(secondArg) ? secondArg : rootCallContext();
2120
1759
  }
2121
- function definePlugin(fnOrConfig) {
2122
- if (typeof fnOrConfig === "function") {
2123
- logDeprecation(
2124
- "definePlugin(fn) (the function form) is deprecated. Author plugins with defineMethod/defineProperty/definePlugin({ ... }) instead."
2125
- );
2126
- return fnOrConfig;
1760
+ var hookAnnotatorReentrancy = 0;
1761
+ function applyAnnotations({
1762
+ context,
1763
+ methodName,
1764
+ input,
1765
+ hookAnnotator,
1766
+ methodAnnotator
1767
+ }) {
1768
+ if (hookAnnotator && !isInsideObserver() && context.depth === 0 && context.callOrigin !== "internal" && hookAnnotatorReentrancy === 0) {
1769
+ hookAnnotatorReentrancy++;
1770
+ try {
1771
+ Object.assign(context.annotations, hookAnnotator({ methodName, input }));
1772
+ } catch {
1773
+ } finally {
1774
+ hookAnnotatorReentrancy--;
1775
+ }
2127
1776
  }
2128
- const config = fnOrConfig;
2129
- const deps = normalizeImports(config.imports);
2130
- return {
2131
- pluginType: "aggregate",
2132
- name: config.name,
2133
- namespace: config.namespace,
2134
- id: makeId(config.name, config.namespace, "aggregate"),
2135
- // A re-export synthetic (`selectExports` / `omitExports`) is flattened by
2136
- // `normalizeExports` into bare bindings, which drops its own `imports:
2137
- // [source]`. That edge is how `omitExports` keeps an omitted (unbound) leaf
2138
- // materialized + addressable by id, so preserve every exported aggregate's
2139
- // imports as extra reachability edges here (bindings unaffected).
2140
- imports: [...deps.plugins, ...exportedAggregateImports(config.exports)],
2141
- importBindings: deps.bindings,
2142
- exports: normalizeExports(config.exports)
2143
- };
2144
- }
2145
- function exportedAggregateImports(exports2) {
2146
- if (!exports2) return [];
2147
- const out = [];
2148
- for (const element of exports2) {
2149
- if (element.pluginType === "aggregate") out.push(...element.imports);
1777
+ try {
1778
+ Object.assign(context.annotations, methodAnnotator?.(input));
1779
+ } catch {
2150
1780
  }
2151
- return out;
2152
1781
  }
2153
- function normalizeExports(exports2) {
2154
- if (!exports2) return {};
2155
- const out = {};
2156
- const add = (binding, leaf) => {
2157
- const existing = out[binding];
2158
- if (existing && existing.id !== leaf.id) {
2159
- throw new Error(
2160
- `definePlugin: duplicate export binding "${binding}". Two different plugins ("${existing.id}" and "${leaf.id}") bind the same name; wrap one in selectExports to rename it.`
2161
- );
2162
- }
2163
- out[binding] = leaf;
1782
+ function signalDeprecation(context, methodName, getDeprecation) {
1783
+ if (isInsideObserver()) return;
1784
+ const deprecation = getDeprecation?.();
1785
+ if (!deprecation?.message) return;
1786
+ const warning = {
1787
+ type: "deprecation",
1788
+ methodName,
1789
+ deprecation
2164
1790
  };
2165
- for (const element of exports2) {
2166
- if (element.pluginType === "aggregate") {
2167
- for (const [binding, child] of Object.entries(element.exports)) {
2168
- add(binding, child);
2169
- }
2170
- } else {
2171
- add(element.name, element);
2172
- }
2173
- }
2174
- return out;
1791
+ const handler = resolveCoreOptions(context)?.logDeprecation ?? defaultLogDeprecation;
1792
+ runIsolatedObserver(() => handler(warning));
2175
1793
  }
2176
-
2177
- // src/model/exports.ts
2178
- var selectSeq = 0;
2179
- function selectExports(source, ...specs) {
2180
- const selected = {};
2181
- const pick = (binding, fromName) => {
2182
- const child = source.exports[fromName];
2183
- if (!child) {
2184
- throw new Error(
2185
- `selectExports: "${source.id}" has no export "${fromName}".`
2186
- );
2187
- }
2188
- selected[binding] = child;
1794
+ function signalStability(context, methodName, getStability) {
1795
+ if (isInsideObserver()) return;
1796
+ const stability = getStability?.();
1797
+ if (!stability || stability === "stable") return;
1798
+ const notice = {
1799
+ type: "stability",
1800
+ methodName,
1801
+ stability
2189
1802
  };
2190
- for (const spec of specs) {
2191
- if (typeof spec === "string") {
2192
- pick(spec, spec);
2193
- } else {
2194
- for (const [newName, fromName] of Object.entries(spec)) {
2195
- pick(newName, fromName);
1803
+ const handler = resolveCoreOptions(context)?.logStabilityNotice ?? defaultLogStabilityNotice;
1804
+ runIsolatedObserver(() => handler(notice));
1805
+ }
1806
+ function normalizeError(error, adaptError) {
1807
+ if (error instanceof Error) return error;
1808
+ const message = typeof error === "object" && error !== null && "message" in error && typeof error.message === "string" ? error.message : String(error);
1809
+ return createCoreError(
1810
+ {
1811
+ code: CoreErrorCode.Unknown,
1812
+ message,
1813
+ cause: error
1814
+ },
1815
+ adaptError
1816
+ );
1817
+ }
1818
+ function createFunction(coreFn, options) {
1819
+ const {
1820
+ sdkContext,
1821
+ schema,
1822
+ name,
1823
+ annotator,
1824
+ frameworkOptions,
1825
+ getDeprecation,
1826
+ getStability
1827
+ } = options;
1828
+ const functionName = name || coreFn.name;
1829
+ const namedFunctions = {
1830
+ [functionName]: async function(callOptions) {
1831
+ const internal = arguments[1];
1832
+ const context = resolveCallContext(internal);
1833
+ if (!isCallContext(internal) && internal !== INTERNAL_CALL) {
1834
+ signalDeprecation(sdkContext, functionName, getDeprecation);
1835
+ signalStability(sdkContext, functionName, getStability);
2196
1836
  }
1837
+ return runInMethodScope(async () => {
1838
+ const startTime = Date.now();
1839
+ const normalizedOptions = callOptions ?? {};
1840
+ const args = [normalizedOptions];
1841
+ const depth = Math.max(context.depth, getCurrentDepth());
1842
+ const insideObserver = isInsideObserver();
1843
+ const hooks = insideObserver ? void 0 : sdkContext.hooks;
1844
+ const adaptError = resolveCoreOptions(sdkContext)?.adaptError;
1845
+ applyAnnotations({
1846
+ context,
1847
+ methodName: functionName,
1848
+ input: normalizedOptions,
1849
+ hookAnnotator: hooks?.annotator,
1850
+ methodAnnotator: annotator
1851
+ });
1852
+ const hookBase = {
1853
+ methodName: functionName,
1854
+ args,
1855
+ isPaginated: false,
1856
+ depth,
1857
+ callId: context.callId,
1858
+ callOrigin: context.callOrigin,
1859
+ annotations: context.annotations
1860
+ };
1861
+ hooks?.onMethodStart?.({ ...hookBase });
1862
+ try {
1863
+ const parsed = parseCallOptions(normalizedOptions, {
1864
+ schema,
1865
+ policy: frameworkOptions,
1866
+ adaptError
1867
+ });
1868
+ const result = await coreFn(
1869
+ mergeCallOptions(parsed),
1870
+ context
1871
+ );
1872
+ hooks?.onMethodEnd?.({
1873
+ ...hookBase,
1874
+ durationMs: Date.now() - startTime
1875
+ });
1876
+ return result;
1877
+ } catch (error) {
1878
+ const normalizedError = normalizeError(error, adaptError);
1879
+ hooks?.onMethodEnd?.({
1880
+ ...hookBase,
1881
+ durationMs: Date.now() - startTime,
1882
+ error: normalizedError
1883
+ });
1884
+ throw normalizedError;
1885
+ }
1886
+ });
2197
1887
  }
2198
- }
2199
- const id = `${source.id}#select:${selectSeq++}`;
2200
- return {
2201
- pluginType: "aggregate",
2202
- name: makeId(`select`, source.name, "aggregate"),
2203
- id,
2204
- // Depend on the source so it is materialized; the selected bindings resolve
2205
- // to the source's own leaves (kept identity).
2206
- imports: [source],
2207
- importBindings: [],
2208
- exports: selected
2209
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
2210
1888
  };
1889
+ return namedFunctions[functionName];
2211
1890
  }
2212
- function omitExports(source, omit) {
2213
- const omitSet = new Set(omit);
2214
- for (const name of omit) {
2215
- if (!(name in source.exports)) {
2216
- throw new Error(`omitExports: "${source.id}" has no export "${name}".`);
1891
+ function createRawFunction(coreFn, options) {
1892
+ const {
1893
+ sdkContext,
1894
+ name,
1895
+ schema,
1896
+ positional,
1897
+ annotator,
1898
+ getDeprecation,
1899
+ getStability
1900
+ } = options;
1901
+ return function(rawInput) {
1902
+ const internal = arguments[1];
1903
+ const context = resolveCallContext(internal);
1904
+ if (!isCallContext(internal) && internal !== INTERNAL_CALL) {
1905
+ signalDeprecation(sdkContext, name, getDeprecation);
1906
+ signalStability(sdkContext, name, getStability);
2217
1907
  }
2218
- }
2219
- const kept = {};
2220
- for (const [binding, child] of Object.entries(source.exports)) {
2221
- if (!omitSet.has(binding)) kept[binding] = child;
2222
- }
2223
- return {
2224
- pluginType: "aggregate",
2225
- name: makeId(`omit`, source.name, "aggregate"),
2226
- id: `${source.id}#omit:${selectSeq++}`,
2227
- imports: [source],
2228
- importBindings: [],
2229
- exports: kept
2230
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
2231
- };
2232
- }
2233
-
2234
- // src/model/legacy.ts
2235
- function fromFunctionPlugin(fn, config) {
2236
- logDeprecation(
2237
- "fromFunctionPlugin() is deprecated. Author plugins with defineMethod/definePlugin instead."
2238
- );
2239
- return {
2240
- pluginType: "legacy",
2241
- name: config.name,
2242
- namespace: config.namespace,
2243
- id: makeId(config.name, config.namespace, "aggregate"),
2244
- imports: [],
2245
- importBindings: [],
2246
- run: fn
2247
- };
2248
- }
2249
- function defineLegacyMerge(args) {
2250
- logDeprecation(
2251
- "defineLegacyMerge() is deprecated. Build directly with createSdk(root, { configuration }) instead."
2252
- );
2253
- return {
2254
- pluginType: "legacy-merge",
2255
- name: args.name,
2256
- namespace: args.namespace,
2257
- id: makeId(args.name, args.namespace, "aggregate"),
2258
- legacy: fromFunctionPlugin(args.legacy, {
2259
- name: args.name,
2260
- namespace: args.namespace
2261
- }),
2262
- plugin: args.plugin
1908
+ return runInMethodScope(() => {
1909
+ const startTime = Date.now();
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
+ const input = schema ? rawInput ?? {} : rawInput;
1915
+ applyAnnotations({
1916
+ context,
1917
+ methodName: name,
1918
+ input,
1919
+ hookAnnotator: hooks?.annotator,
1920
+ methodAnnotator: annotator
1921
+ });
1922
+ const record = input;
1923
+ const args = positional ? positional.filter((key) => record?.[key] !== void 0).map((key) => record?.[key]) : [input];
1924
+ const hookBase = {
1925
+ methodName: name,
1926
+ args,
1927
+ isPaginated: false,
1928
+ depth,
1929
+ callId: context.callId,
1930
+ callOrigin: context.callOrigin,
1931
+ annotations: context.annotations
1932
+ };
1933
+ hooks?.onMethodStart?.({ ...hookBase });
1934
+ const fireEnd = (error) => {
1935
+ hooks?.onMethodEnd?.({
1936
+ ...hookBase,
1937
+ durationMs: Date.now() - startTime,
1938
+ ...error ? { error } : {}
1939
+ });
1940
+ };
1941
+ try {
1942
+ const parsed = schema ? validateOptions(schema, input, { adaptError }) : input;
1943
+ const result = coreFn(parsed, context);
1944
+ if (isPromiseLike(result)) {
1945
+ return result.then(
1946
+ (value) => {
1947
+ fireEnd();
1948
+ return value;
1949
+ },
1950
+ (error) => {
1951
+ fireEnd(
1952
+ error instanceof Error ? error : new Error(String(error))
1953
+ );
1954
+ throw error;
1955
+ }
1956
+ );
1957
+ }
1958
+ fireEnd();
1959
+ return result;
1960
+ } catch (error) {
1961
+ fireEnd(error instanceof Error ? error : new Error(String(error)));
1962
+ throw error;
1963
+ }
1964
+ });
2263
1965
  };
2264
1966
  }
2265
- function legacyGraphEntry(name, value, pluginMeta) {
2266
- const { inputSchema, ...rest } = pluginMeta ?? {};
2267
- const meta = Object.keys(rest).length ? rest : void 0;
2268
- if (typeof value === "function") {
2269
- return {
2270
- pluginType: "method",
2271
- name,
2272
- value,
2273
- chain: [],
2274
- ...inputSchema ? { inputSchema } : {},
2275
- ...meta ? { meta } : {}
2276
- };
1967
+ function isSdkPage(value) {
1968
+ if (typeof value !== "object" || value === null) return false;
1969
+ const page = value;
1970
+ if (!Array.isArray(page.data)) return false;
1971
+ if (page.nextCursor !== void 0 && typeof page.nextCursor !== "string") {
1972
+ return false;
2277
1973
  }
2278
- return { pluginType: "property", name, value, ...meta ? { meta } : {} };
1974
+ return Object.keys(page).every((k) => k === "data" || k === "nextCursor");
2279
1975
  }
2280
-
2281
- // src/model/builtins.ts
2282
- var import_zod3 = require("zod");
2283
-
2284
- // src/model/registry-support.ts
2285
- function adaptLegacyFormatter(legacy, sdk) {
2286
- const legacyFetch = legacy.fetch;
2287
- return {
2288
- getContext: legacyFetch ? async ({ items, input, context }) => {
2289
- let ctx = context;
2290
- for (const item of items) {
2291
- ctx = await legacyFetch(sdk, input, item, ctx);
1976
+ function createPageFunction(coreFn, {
1977
+ sdkContext,
1978
+ adaptPage,
1979
+ finalizePage
1980
+ }) {
1981
+ const functionName = coreFn.name + "Page";
1982
+ const namedFunctions = {
1983
+ [functionName]: async function(options, callContext) {
1984
+ try {
1985
+ const response = await coreFn(options, callContext);
1986
+ const page = adaptPage ? adaptPage(response) : response;
1987
+ if (!isSdkPage(page)) {
1988
+ throw new Error(
1989
+ `${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\`.`
1990
+ );
1991
+ }
1992
+ return finalizePage ? finalizePage(page, options) : page;
1993
+ } catch (error) {
1994
+ throw normalizeError(error, resolveCoreOptions(sdkContext)?.adaptError);
2292
1995
  }
2293
- return ctx;
2294
- } : void 0,
2295
- format: ({ item, context }) => legacy.format(item, context)
1996
+ }
2296
1997
  };
1998
+ return namedFunctions[functionName];
2297
1999
  }
2298
- function normalizeFormatter(entry, sdk) {
2299
- if (entry.pluginType !== "method") return void 0;
2300
- if (entry.formatter) return entry.formatter;
2301
- const legacy = entry.meta?.formatter;
2302
- return legacy ? adaptLegacyFormatter(legacy, sdk) : void 0;
2303
- }
2304
- function normalizeResolvers(entry) {
2305
- if (entry.pluginType !== "method") return void 0;
2306
- return entry.resolvers;
2307
- }
2308
- function methodPositional(entry) {
2309
- if (entry.pluginType !== "method") return void 0;
2310
- return entry.positional;
2311
- }
2312
- function pluginEntryMeta(entry) {
2313
- if (entry.pluginType === "method" && entry.meta) {
2314
- return entry.inputSchema ? { ...entry.meta, inputSchema: entry.inputSchema } : entry.meta;
2315
- }
2316
- if (entry.pluginType === "property" && entry.meta) return entry.meta;
2317
- return void 0;
2318
- }
2319
- function foldDynamicMembers(entry, surfaceBindings, meta) {
2320
- if (entry.pluginType !== "property" || !entry.dynamicMembers) return;
2321
- for (const member of entry.dynamicMembers) {
2322
- if (!surfaceBindings.has(member.rootBinding)) {
2323
- throw new Error(
2324
- `dynamicMember "${member.name}": its root "${member.rootBinding}" is not a surfaced member. A dynamic member's path must start with a real binding.`
2325
- );
2000
+ function createPaginatedFunction(coreFn, options) {
2001
+ const {
2002
+ sdkContext,
2003
+ schema,
2004
+ name,
2005
+ defaultPageSize,
2006
+ adaptPage,
2007
+ annotator,
2008
+ finalizePage,
2009
+ frameworkOptions,
2010
+ getDeprecation,
2011
+ getStability
2012
+ } = options;
2013
+ const pageFunction = createPageFunction(coreFn, {
2014
+ sdkContext,
2015
+ adaptPage,
2016
+ finalizePage
2017
+ });
2018
+ const functionName = name || coreFn.name;
2019
+ const namedFunctions = {
2020
+ [functionName]: function(callOptions) {
2021
+ const internal = arguments[1];
2022
+ const context = resolveCallContext(internal);
2023
+ if (!isCallContext(internal) && internal !== INTERNAL_CALL) {
2024
+ signalDeprecation(sdkContext, functionName, getDeprecation);
2025
+ signalStability(sdkContext, functionName, getStability);
2026
+ }
2027
+ return runInMethodScope(() => {
2028
+ const startTime = Date.now();
2029
+ const normalizedOptions = callOptions ?? {};
2030
+ const args = [normalizedOptions];
2031
+ const depth = Math.max(context.depth, getCurrentDepth());
2032
+ const insideObserver = isInsideObserver();
2033
+ const hooks = insideObserver ? void 0 : sdkContext.hooks;
2034
+ const adaptError = resolveCoreOptions(sdkContext)?.adaptError;
2035
+ applyAnnotations({
2036
+ context,
2037
+ methodName: functionName,
2038
+ input: normalizedOptions,
2039
+ hookAnnotator: hooks?.annotator,
2040
+ methodAnnotator: annotator
2041
+ });
2042
+ const hookBase = {
2043
+ methodName: functionName,
2044
+ args,
2045
+ isPaginated: true,
2046
+ depth,
2047
+ callId: context.callId,
2048
+ callOrigin: context.callOrigin,
2049
+ annotations: context.annotations
2050
+ };
2051
+ hooks?.onMethodStart?.({ ...hookBase });
2052
+ try {
2053
+ const validatedOptions = mergeCallOptions(
2054
+ parseCallOptions(normalizedOptions, {
2055
+ schema,
2056
+ policy: frameworkOptions,
2057
+ adaptError
2058
+ })
2059
+ );
2060
+ const pageSize = validatedOptions.pageSize ?? defaultPageSize;
2061
+ const optimizedOptions = {
2062
+ ...validatedOptions,
2063
+ pageSize
2064
+ };
2065
+ const iterator = paginate(
2066
+ (pageOptions) => pageFunction(pageOptions, context),
2067
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2068
+ optimizedOptions
2069
+ );
2070
+ const firstPagePromise = iterator.next().then((result) => {
2071
+ if (result.done) {
2072
+ throw new Error("Paginate should always iterate at least once");
2073
+ }
2074
+ return result.value;
2075
+ });
2076
+ if (hooks?.onMethodEnd) {
2077
+ firstPagePromise.then(
2078
+ () => {
2079
+ hooks.onMethodEnd({
2080
+ ...hookBase,
2081
+ durationMs: Date.now() - startTime
2082
+ });
2083
+ },
2084
+ (error) => {
2085
+ hooks.onMethodEnd({
2086
+ ...hookBase,
2087
+ durationMs: Date.now() - startTime,
2088
+ error: error instanceof Error ? error : new Error(String(error))
2089
+ });
2090
+ }
2091
+ );
2092
+ }
2093
+ const pageStream = async function* () {
2094
+ yield await firstPagePromise;
2095
+ for await (const page of iterator) {
2096
+ yield page;
2097
+ }
2098
+ }();
2099
+ return Object.assign(firstPagePromise, {
2100
+ [Symbol.asyncIterator]() {
2101
+ return pageStream;
2102
+ },
2103
+ pages: function() {
2104
+ return {
2105
+ [Symbol.asyncIterator]() {
2106
+ return pageStream;
2107
+ }
2108
+ };
2109
+ },
2110
+ items: function() {
2111
+ return {
2112
+ [Symbol.asyncIterator]: async function* () {
2113
+ for await (const page of pageStream) {
2114
+ for (const item of page.data) {
2115
+ yield item;
2116
+ }
2117
+ }
2118
+ }
2119
+ };
2120
+ }
2121
+ });
2122
+ } catch (error) {
2123
+ const normalizedError = normalizeError(error, adaptError);
2124
+ hooks?.onMethodEnd?.({
2125
+ ...hookBase,
2126
+ durationMs: Date.now() - startTime,
2127
+ error: normalizedError
2128
+ });
2129
+ throw normalizedError;
2130
+ }
2131
+ });
2326
2132
  }
2327
- meta[member.name] = member.meta;
2328
- }
2329
- }
2330
- function collectSurfaceProjection(context, formatterSdk) {
2331
- const meta = {};
2332
- const entries = {};
2333
- for (const [binding, id] of Object.entries(context.surface)) {
2334
- const entry = context.plugins[id];
2335
- if (!entry || entry.pluginType === "aggregate") continue;
2336
- entries[binding] = entry;
2337
- const m = pluginEntryMeta(entry);
2338
- if (m) meta[binding] = m;
2339
- }
2340
- const surfaceBindings = new Set(Object.keys(context.surface));
2341
- for (const entry of Object.values(entries)) {
2342
- foldDynamicMembers(entry, surfaceBindings, meta);
2343
- }
2344
- const formatters = {};
2345
- const resolvers = {};
2346
- const positional = {};
2347
- const skipInputValidation = {};
2348
- for (const [binding, entry] of Object.entries(entries)) {
2349
- const f = normalizeFormatter(entry, formatterSdk);
2350
- if (f) formatters[binding] = f;
2351
- const r = normalizeResolvers(entry);
2352
- if (r) resolvers[binding] = r;
2353
- const p = methodPositional(entry);
2354
- if (p) positional[binding] = p;
2355
- if (entry.pluginType === "method" && entry.skipInputValidation)
2356
- skipInputValidation[binding] = true;
2357
- }
2358
- return { meta, formatters, resolvers, positional, skipInputValidation };
2359
- }
2360
- var REGISTRY_CACHE = Symbol.for("kitcore.registryCache");
2361
- function freezeContainers(registry) {
2362
- Object.freeze(registry.functions);
2363
- for (const category of registry.categories) {
2364
- Object.freeze(category.functions);
2365
- Object.freeze(category);
2366
- }
2367
- Object.freeze(registry.categories);
2368
- return Object.freeze(registry);
2369
- }
2370
- function getCachedRegistry(context, packageFilter) {
2371
- const key = packageFilter ?? "";
2372
- const caching = context;
2373
- let byFilter = caching[REGISTRY_CACHE];
2374
- if (!byFilter) {
2375
- byFilter = /* @__PURE__ */ new Map();
2376
- caching[REGISTRY_CACHE] = byFilter;
2377
- }
2378
- let registry = byFilter.get(key);
2379
- if (!registry) {
2380
- registry = freezeContainers(buildSurfaceRegistry(context, packageFilter));
2381
- byFilter.set(key, registry);
2382
- }
2383
- return registry;
2384
- }
2385
- function invalidateRegistryCache(context) {
2386
- delete context[REGISTRY_CACHE];
2387
- }
2388
- function buildSurfaceRegistry(context, packageFilter) {
2389
- const surface = {};
2390
- for (const [binding, id] of Object.entries(context.surface)) {
2391
- const entry = context.plugins[id];
2392
- if (!entry || entry.pluginType === "aggregate") continue;
2393
- surface[binding] = entry.pluginType === "property" && entry.getValue ? entry.getValue() : entry.value;
2394
- }
2395
- const projection = collectSurfaceProjection(context, surface);
2396
- Object.assign(projection.meta, context.meta);
2397
- return buildRegistry({
2398
- sdk: surface,
2399
- ...projection,
2400
- packageFilter
2401
- });
2133
+ };
2134
+ return namedFunctions[functionName];
2402
2135
  }
2403
2136
 
2404
- // src/model/builtins.ts
2405
- var coreOptionsPluginRef = declareOptionalProperty({ id: CORE_OPTIONS_ID });
2406
- var dangerousContextPlugin = {
2407
- pluginType: "property",
2408
- name: "context",
2409
- namespace: "kitcore",
2410
- id: "kitcore/context",
2411
- imports: [],
2412
- importBindings: [],
2413
- privileged: true
2414
- };
2415
- var getRegistryPlugin = defineMethod({
2416
- name: "getRegistry",
2417
- namespace: "kitcore",
2418
- imports: [dangerousContextPlugin],
2419
- inputSchema: import_zod3.z.object({ package: import_zod3.z.string().optional() }).optional(),
2420
- run: ({ imports, input }) => getCachedRegistry(imports.context, input?.package)
2421
- });
2422
-
2423
2137
  // src/utils/output-policy.ts
2424
2138
  function isRecord2(value) {
2425
2139
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -2574,6 +2288,62 @@ function applyListOutputPolicy(page, policy) {
2574
2288
  return next;
2575
2289
  }
2576
2290
 
2291
+ // src/model/add-plugin-transaction.ts
2292
+ function snapshotGraph({
2293
+ context,
2294
+ sdk,
2295
+ surfaceKeys
2296
+ }) {
2297
+ const ids = new Set(Object.keys(context.plugins));
2298
+ const surface = Object.assign(
2299
+ /* @__PURE__ */ Object.create(null),
2300
+ context.surface
2301
+ );
2302
+ const propertyDescriptors = /* @__PURE__ */ new Map();
2303
+ for (const key of surfaceKeys) {
2304
+ propertyDescriptors.set(key, Object.getOwnPropertyDescriptor(sdk, key));
2305
+ }
2306
+ const hooks = context.hooks;
2307
+ const disposerCount = context.disposers?.length ?? 0;
2308
+ const chainLengths = /* @__PURE__ */ new Map();
2309
+ const descriptions = /* @__PURE__ */ new Map();
2310
+ for (const [id, entry] of Object.entries(context.plugins)) {
2311
+ if (entry.pluginType !== "method") continue;
2312
+ chainLengths.set(id, entry.chain.length);
2313
+ descriptions.set(id, pickDefined(entry, METHOD_META_KEYS));
2314
+ }
2315
+ return () => {
2316
+ const dropped = context.disposers?.slice(disposerCount) ?? [];
2317
+ for (let i = dropped.length - 1; i >= 0; i--) {
2318
+ try {
2319
+ void Promise.resolve(dropped[i].dispose()).catch(() => {
2320
+ });
2321
+ } catch {
2322
+ }
2323
+ }
2324
+ for (const id of Object.keys(context.plugins)) {
2325
+ if (!ids.has(id)) delete context.plugins[id];
2326
+ }
2327
+ for (const [id, length] of chainLengths) {
2328
+ const entry = context.plugins[id];
2329
+ if (entry?.pluginType === "method") entry.chain.length = length;
2330
+ }
2331
+ for (const [id, description] of descriptions) {
2332
+ const entry = context.plugins[id];
2333
+ if (entry?.pluginType !== "method") continue;
2334
+ for (const key of METHOD_META_KEYS) delete entry[key];
2335
+ Object.assign(entry, description);
2336
+ }
2337
+ context.hooks = hooks;
2338
+ if (context.disposers) context.disposers.length = disposerCount;
2339
+ context.surface = surface;
2340
+ for (const [key, descriptor] of propertyDescriptors) {
2341
+ if (descriptor) Object.defineProperty(sdk, key, descriptor);
2342
+ else delete sdk[key];
2343
+ }
2344
+ };
2345
+ }
2346
+
2577
2347
  // src/model/materialize.ts
2578
2348
  var FRAMEWORK_CONFIGURATION_IDS = /* @__PURE__ */ new Set([
2579
2349
  CORE_OPTIONS_ID
@@ -2584,6 +2354,19 @@ function normalizeOutput(output) {
2584
2354
  return output;
2585
2355
  }
2586
2356
  function getContext(sdk) {
2357
+ const context = tryGetContext(sdk);
2358
+ if (!context) {
2359
+ throw createCoreError({
2360
+ code: CoreErrorCode.NoSdkContext,
2361
+ message: "getContext: object has no kitcore context. Only an SDK built by createSdk carries one."
2362
+ });
2363
+ }
2364
+ return context;
2365
+ }
2366
+ function tryGetContext(sdk) {
2367
+ if (typeof sdk !== "object" && typeof sdk !== "function" || sdk === null) {
2368
+ return void 0;
2369
+ }
2587
2370
  return sdk[CONTEXT];
2588
2371
  }
2589
2372
  function assertDynamicMemberRoot(entry) {
@@ -2595,11 +2378,9 @@ function assertDynamicMemberRoot(entry) {
2595
2378
  );
2596
2379
  }
2597
2380
  function getRegistry(sdk, packageFilter) {
2598
- if (typeof sdk !== "object" && typeof sdk !== "function" || sdk === null)
2599
- throw createNoRegistryError();
2600
- const context = getContext(sdk);
2381
+ const context = tryGetContext(sdk);
2601
2382
  if (context?.surface) return getCachedRegistry(context, packageFilter);
2602
- const surfaced = sdk.getRegistry;
2383
+ const surfaced = sdk?.getRegistry;
2603
2384
  if (typeof surfaced === "function") {
2604
2385
  return surfaced.call(
2605
2386
  sdk,
@@ -2609,9 +2390,10 @@ function getRegistry(sdk, packageFilter) {
2609
2390
  throw createNoRegistryError();
2610
2391
  }
2611
2392
  function createNoRegistryError() {
2612
- return new Error(
2613
- "getRegistry: sdk has no kitcore context and no surfaced getRegistry()."
2614
- );
2393
+ return createCoreError({
2394
+ code: CoreErrorCode.NoSdkContext,
2395
+ message: "getRegistry: sdk has no kitcore context and no surfaced getRegistry()."
2396
+ });
2615
2397
  }
2616
2398
  function isResolverRef(value) {
2617
2399
  return "ref" in value;
@@ -2660,8 +2442,14 @@ function edgesOf(plugin) {
2660
2442
  }
2661
2443
  return plugin.imports;
2662
2444
  }
2663
- function isStandIn(plugin) {
2664
- return (plugin.pluginType === "method" || plugin.pluginType === "property" || plugin.pluginType === "aggregate") && plugin.standIn === true;
2445
+ function sameIdError({
2446
+ plugin,
2447
+ existing
2448
+ }) {
2449
+ 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.`;
2450
+ return new Error(
2451
+ `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}`
2452
+ );
2665
2453
  }
2666
2454
  function isDefault(plugin) {
2667
2455
  return (plugin.pluginType === "method" || plugin.pluginType === "property") && plugin.defaultSource !== void 0;
@@ -2679,12 +2467,25 @@ function topoOrder(descriptors) {
2679
2467
  for (const id of descriptors.keys()) visit(id);
2680
2468
  return order;
2681
2469
  }
2682
- function collectPlugins(root, materialized = /* @__PURE__ */ new Set(), configuration) {
2470
+ function collectPlugins({
2471
+ root,
2472
+ caller,
2473
+ applied,
2474
+ configuration
2475
+ }) {
2683
2476
  const rank = (plugin) => isStandIn(plugin) ? 0 : isDefault(plugin) ? 1 : 2;
2684
2477
  const allNodes = [];
2685
2478
  const seen = /* @__PURE__ */ new Set();
2686
2479
  const collect = (plugin) => {
2687
- if (materialized.has(plugin.id) || seen.has(plugin)) return;
2480
+ assertKnownPluginType({ plugin, where: caller });
2481
+ const existing = applied[plugin.id];
2482
+ if (existing) {
2483
+ if (existing.descriptor !== plugin && rank(plugin) === 2) {
2484
+ throw sameIdError({ plugin, existing: existing.descriptor });
2485
+ }
2486
+ return;
2487
+ }
2488
+ if (seen.has(plugin)) return;
2688
2489
  seen.add(plugin);
2689
2490
  allNodes.push(plugin);
2690
2491
  for (const edge of edgesOf(plugin)) collect(edge);
@@ -2749,7 +2550,7 @@ function collectPlugins(root, materialized = /* @__PURE__ */ new Set(), configur
2749
2550
  const winnerRank = rank(winner);
2750
2551
  if (winnerRank === 2) {
2751
2552
  throw new Error(
2752
- `createSdk: duplicate plugin id "${id}". Two different plugins registered under the same id.`
2553
+ `${caller}: duplicate plugin id "${id}". Two different plugins registered under the same id.`
2753
2554
  );
2754
2555
  }
2755
2556
  if (winnerRank === 1) {
@@ -2809,7 +2610,7 @@ function collectPlugins(root, materialized = /* @__PURE__ */ new Set(), configur
2809
2610
  if (isStandIn(plugin)) {
2810
2611
  if ("optional" in plugin && plugin.optional) continue;
2811
2612
  throw new Error(
2812
- `createSdk: missing dependency "${id}". A plugin depends on it (via a stand-in) but no implementation was registered.`
2613
+ `${caller}: missing dependency "${id}". A plugin depends on it (via a stand-in) but no implementation was registered.`
2813
2614
  );
2814
2615
  }
2815
2616
  }
@@ -2817,7 +2618,7 @@ function collectPlugins(root, materialized = /* @__PURE__ */ new Set(), configur
2817
2618
  const winner = byId.get(id);
2818
2619
  if (winner && isDefault(winner)) {
2819
2620
  throw new Error(
2820
- `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.`
2621
+ `${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.`
2821
2622
  );
2822
2623
  }
2823
2624
  }
@@ -2839,7 +2640,7 @@ function bindValue({
2839
2640
  configurable: true
2840
2641
  });
2841
2642
  } else {
2842
- const value = bindMode === "internal" && entry.pluginType === "method" ? entry.bindInternal?.({ ctx, frameworkOrigin }) ?? entry.internalValue ?? entry.value : entry.value;
2643
+ const value = bindMode === "internal" && entry.pluginType === "method" ? entry.bindInternal({ ctx, frameworkOrigin }) : entry.value;
2843
2644
  Object.defineProperty(target, key, {
2844
2645
  value,
2845
2646
  writable: true,
@@ -2875,10 +2676,15 @@ function buildImports({
2875
2676
  });
2876
2677
  continue;
2877
2678
  }
2679
+ if (!entry) {
2680
+ throw new Error(
2681
+ `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\`.`
2682
+ );
2683
+ }
2878
2684
  bindValue({
2879
2685
  target: imports,
2880
2686
  key: binding,
2881
- entry,
2687
+ entry: valueEntryOf(entry, id),
2882
2688
  bindMode: "internal",
2883
2689
  ctx,
2884
2690
  frameworkOrigin
@@ -2900,14 +2706,6 @@ function bindInternalTwin({
2900
2706
  }
2901
2707
  return internalValue;
2902
2708
  }
2903
- function mirrorLegacyRootKeys(context, rootKeys, meta) {
2904
- const exports2 = {};
2905
- for (const [name, value] of Object.entries(rootKeys)) {
2906
- context.plugins[name] = legacyGraphEntry(name, value, meta[name]);
2907
- exports2[name] = value;
2908
- }
2909
- return exports2;
2910
- }
2911
2709
  function recordExportSurface(context, exports2) {
2912
2710
  for (const [binding, child] of Object.entries(exports2)) {
2913
2711
  context.surface[binding] = child.id;
@@ -2915,9 +2713,8 @@ function recordExportSurface(context, exports2) {
2915
2713
  }
2916
2714
  function materialize(descriptors, context) {
2917
2715
  const states = /* @__PURE__ */ new Map();
2918
- runLegacyPass(descriptors, context);
2919
- buildMethodEntries(descriptors, context, states);
2920
- buildEagerArtifacts(descriptors, context, states);
2716
+ buildLeafEntries(descriptors, context, states);
2717
+ runSetup(descriptors, context, states);
2921
2718
  bindAttachments(descriptors, context);
2922
2719
  resolveAggregates(descriptors, context);
2923
2720
  assembleMiddleware(descriptors, context, states);
@@ -2929,22 +2726,35 @@ function applyMethodOverride(context, override) {
2929
2726
  const entry = context.plugins[override.target];
2930
2727
  if (!entry) {
2931
2728
  throw new Error(
2932
- `defineMethodOverride: no method "${override.target}" to override. Include the target method in the SDK build.`
2729
+ `defineOverride: no method "${override.target}" to override. Include the target method in the SDK build.`
2933
2730
  );
2934
2731
  }
2935
2732
  if (entry.pluginType !== "method") {
2936
2733
  throw new Error(
2937
- `defineMethodOverride: "${override.target}" is a ${entry.pluginType}, not a method; only methods can be overridden.`
2734
+ `defineOverride: "${override.target}" is a ${entry.pluginType}, not a method; only methods can be overridden.`
2938
2735
  );
2939
2736
  }
2940
- entry.meta = { ...entry.meta, ...override.meta };
2737
+ Object.assign(entry, override.patch);
2738
+ }
2739
+ function addOverridePlugin(context, override) {
2740
+ const existing = context.plugins[override.id];
2741
+ if (existing?.descriptor === override) return;
2742
+ if (existing) {
2743
+ throw sameIdError({ plugin: override, existing: existing.descriptor });
2744
+ }
2745
+ applyMethodOverride(context, override);
2746
+ context.plugins[override.id] = overrideEntry(override);
2941
2747
  }
2942
2748
  function applyMethodOverrides(descriptors, context) {
2943
2749
  for (const descriptor of descriptors.values()) {
2944
2750
  if (descriptor.pluginType !== "method-override") continue;
2945
2751
  applyMethodOverride(context, descriptor);
2752
+ context.plugins[descriptor.id] = overrideEntry(descriptor);
2946
2753
  }
2947
2754
  }
2755
+ function overrideEntry(descriptor) {
2756
+ return { pluginType: "method-override", name: descriptor.name, descriptor };
2757
+ }
2948
2758
  function bindResolver(resolver, plugins) {
2949
2759
  switch (resolver.type) {
2950
2760
  case "static":
@@ -3014,7 +2824,9 @@ function bindResolver(resolver, plugins) {
3014
2824
  frameworkOrigin: true
3015
2825
  });
3016
2826
  const {
3017
- getContext: getContext2,
2827
+ // Named apart from the module's exported `getContext`, which throws when
2828
+ // an object carries no kitcore context.
2829
+ getContext: getResolverContext,
3018
2830
  listItems,
3019
2831
  validate,
3020
2832
  tryResolveWithoutPrompt,
@@ -3028,8 +2840,8 @@ function bindResolver(resolver, plugins) {
3028
2840
  prompt: resolver.prompt,
3029
2841
  listItems: ({ input, context, search, cursor }) => listItems({ imports, input, context, search, cursor })
3030
2842
  };
3031
- if (getContext2)
3032
- bound.getContext = ({ input }) => getContext2({ imports, input });
2843
+ if (getResolverContext)
2844
+ bound.getContext = ({ input }) => getResolverContext({ imports, input });
3033
2845
  if (validate) {
3034
2846
  bound.validate = ({ value, input, context }) => validate({ imports, value, input, context });
3035
2847
  }
@@ -3073,9 +2885,9 @@ function bindFormatter(formatter, plugins) {
3073
2885
  frameworkOrigin: true
3074
2886
  });
3075
2887
  const bound = { format: formatter.format };
3076
- const { getContext: getContext2 } = formatter;
3077
- if (getContext2)
3078
- bound.getContext = ({ items, input, context }) => getContext2({ imports, items, input, context });
2888
+ const { getContext: getFormatterContext } = formatter;
2889
+ if (getFormatterContext)
2890
+ bound.getContext = ({ items, input, context }) => getFormatterContext({ imports, items, input, context });
3079
2891
  return bound;
3080
2892
  }
3081
2893
  function bindAttachments(descriptors, context) {
@@ -3096,64 +2908,35 @@ function bindAttachments(descriptors, context) {
3096
2908
  }
3097
2909
  }
3098
2910
  }
3099
- function runLegacyPass(descriptors, context) {
2911
+ function buildLeafEntries(descriptors, context, states) {
3100
2912
  const plugins = context.plugins;
3101
- const compatView = new Proxy(
3102
- {},
3103
- {
3104
- get: (_target, prop) => {
3105
- if (prop === "context") return context;
3106
- const entry = plugins[prop];
3107
- return entry?.value;
2913
+ for (const [id, descriptor] of descriptors) {
2914
+ if (descriptor.pluginType === "property") {
2915
+ if (!isStandIn(descriptor)) {
2916
+ plugins[id] = buildPropertyEntry(descriptor, id, context, states);
3108
2917
  }
2918
+ continue;
3109
2919
  }
3110
- );
3111
- for (const id of topoOrder(descriptors)) {
3112
- const descriptor = descriptors.get(id);
3113
- if (!descriptor || descriptor.pluginType !== "legacy") continue;
3114
- const { rootKeys, meta, hooks, contextRest } = splitPluginContribution(
3115
- descriptor.run(compatView)
3116
- );
3117
- Object.assign(context.meta, meta);
3118
- Object.assign(context, contextRest);
3119
- context.hooks = buildHooks(context.hooks, hooks);
3120
- const exports2 = mirrorLegacyRootKeys(context, rootKeys, meta);
3121
- for (const name of Object.keys(rootKeys)) context.surface[name] = name;
3122
- if (!("getRegistry" in exports2)) {
3123
- let getRegistry3 = function(options) {
3124
- return getCachedRegistry(context, options?.package);
3125
- };
3126
- var getRegistry2 = getRegistry3;
3127
- exports2.getRegistry = getRegistry3;
3128
- plugins.getRegistry = {
3129
- pluginType: "method",
3130
- name: "getRegistry",
3131
- value: getRegistry3,
3132
- chain: []
3133
- };
3134
- }
3135
- plugins[id] = { pluginType: "aggregate", name: descriptor.name, exports: exports2 };
3136
- }
3137
- }
3138
- function buildMethodEntries(descriptors, context, states) {
3139
- const plugins = context.plugins;
3140
- for (const [id, descriptor] of descriptors) {
3141
2920
  if (descriptor.pluginType !== "method") continue;
3142
2921
  if (isStandIn(descriptor)) continue;
3143
2922
  const out = normalizeOutput(descriptor.output);
3144
2923
  const entry = {
3145
2924
  pluginType: "method",
3146
2925
  name: descriptor.name,
2926
+ descriptor,
3147
2927
  chain: [],
2928
+ ...pickDefined(descriptor, METHOD_META_KEYS),
3148
2929
  inputSchema: descriptor.inputSchema,
3149
2930
  skipInputValidation: descriptor.skipInputValidation,
3150
- // Derive the presentation type from the output mode when the author did
3151
- // not set one; an explicit meta.type (e.g. "create") still wins.
3152
- meta: out.type === "raw" || descriptor.meta?.type ? descriptor.meta : { ...descriptor.meta, type: out.type },
2931
+ outputSchema: descriptor.outputSchema,
3153
2932
  output: out,
3154
- // Replaced below; never called.
3155
- value: () => void 0
2933
+ // All three are replaced below, once the boundary they wrap exists; never
2934
+ // called in this placeholder form.
2935
+ value: () => void 0,
2936
+ internalValue: () => void 0,
2937
+ bindInternal: () => () => void 0
3156
2938
  };
2939
+ if (entry.type === void 0 && out.type !== "raw") entry.type = out.type;
3157
2940
  const callRun = (input, ctx) => {
3158
2941
  const callContext = ctx ?? rootCallContext();
3159
2942
  return descriptor.run({
@@ -3189,13 +2972,12 @@ function buildMethodEntries(descriptors, context, states) {
3189
2972
  }
3190
2973
  return next(input);
3191
2974
  };
3192
- const sdk = { context };
3193
2975
  const methodAnnotator = descriptor.annotator;
3194
2976
  const boundAnnotator = methodAnnotator ? (input) => methodAnnotator({ input }) : void 0;
3195
2977
  const outputPolicy = (callOptions) => {
3196
2978
  const core = resolveCoreOptions(context);
3197
2979
  return {
3198
- outputSchema: descriptor.meta?.outputSchema,
2980
+ outputSchema: descriptor.outputSchema,
3199
2981
  skipOutputValidation: descriptor.skipOutputValidation,
3200
2982
  skippedByCaller: readSkipOutputDataValidation(callOptions),
3201
2983
  includeOutputValidationDroppedPaths: core?.includeOutputValidationDroppedPaths,
@@ -3211,7 +2993,7 @@ function buildMethodEntries(descriptors, context, states) {
3211
2993
  (input, ctx) => callRun(stripFrameworkOnlyOptions(input, withheld), ctx)
3212
2994
  ),
3213
2995
  {
3214
- sdk,
2996
+ sdkContext: context,
3215
2997
  schema: descriptor.inputSchema,
3216
2998
  name: descriptor.name,
3217
2999
  frameworkOptions,
@@ -3222,8 +3004,8 @@ function buildMethodEntries(descriptors, context, states) {
3222
3004
  // (item mode's sibling); dropped paths surface as `[].x` in the page's
3223
3005
  // `meta`, unioned across items.
3224
3006
  finalizePage: (page, callOptions) => applyListOutputPolicy(page, outputPolicy(callOptions)),
3225
- getDeprecation: () => entry.meta?.deprecation,
3226
- getStability: () => entry.meta ? normalizeStability(entry.meta) : void 0
3007
+ getDeprecation: () => entry.deprecation,
3008
+ getStability: () => normalizeStability(entry)
3227
3009
  }
3228
3010
  );
3229
3011
  } else if (out.type === "item") {
@@ -3234,17 +3016,17 @@ function buildMethodEntries(descriptors, context, states) {
3234
3016
  entry.value = createFunction(
3235
3017
  fold(itemCore),
3236
3018
  {
3237
- sdk,
3019
+ sdkContext: context,
3238
3020
  schema: descriptor.inputSchema,
3239
3021
  name: descriptor.name,
3240
3022
  frameworkOptions,
3241
3023
  annotator: boundAnnotator,
3242
- getDeprecation: () => entry.meta?.deprecation,
3243
- getStability: () => entry.meta ? normalizeStability(entry.meta) : void 0
3024
+ getDeprecation: () => entry.deprecation,
3025
+ getStability: () => normalizeStability(entry)
3244
3026
  }
3245
3027
  );
3246
3028
  } else {
3247
- const rawValidates = descriptor.meta?.outputSchema !== void 0 && !descriptor.skipOutputValidation;
3029
+ const rawValidates = descriptor.outputSchema !== void 0 && !descriptor.skipOutputValidation;
3248
3030
  const validateRaw = (out2) => {
3249
3031
  const policy = outputPolicy(void 0);
3250
3032
  if (isPromiseLike(out2)) {
@@ -3260,7 +3042,7 @@ function buildMethodEntries(descriptors, context, states) {
3260
3042
  return rawValidates ? validateRaw(out2) : out2;
3261
3043
  },
3262
3044
  {
3263
- sdk,
3045
+ sdkContext: context,
3264
3046
  name: descriptor.name,
3265
3047
  schema: descriptor.skipInputValidation ? void 0 : descriptor.inputSchema,
3266
3048
  positional: descriptor.positional,
@@ -3268,9 +3050,9 @@ function buildMethodEntries(descriptors, context, states) {
3268
3050
  // The boundary reads the deprecation LIVE off the entry, so a
3269
3051
  // deprecation merged after build (defineMethodOverride, addPlugin)
3270
3052
  // fires too. Same for the stability level, normalized from the
3271
- // entry meta (declared level or legacy `experimental` boolean).
3272
- getDeprecation: () => entry.meta?.deprecation,
3273
- getStability: () => entry.meta ? normalizeStability(entry.meta) : void 0
3053
+ // descriptor, with an override's patch on top.
3054
+ getDeprecation: () => entry.deprecation,
3055
+ getStability: () => normalizeStability(entry)
3274
3056
  }
3275
3057
  );
3276
3058
  }
@@ -3289,8 +3071,8 @@ function buildMethodEntries(descriptors, context, states) {
3289
3071
  entry.internalValue = internalValue;
3290
3072
  entry.bindInternal = (opts) => bindInternalTwin({
3291
3073
  ...opts,
3292
- withContext: (context2) => {
3293
- return (...args) => canonicalValue(pack(args), context2);
3074
+ withContext: (ctx) => {
3075
+ return (...args) => canonicalValue(pack(args), ctx);
3294
3076
  },
3295
3077
  internalValue
3296
3078
  });
@@ -3300,29 +3082,53 @@ function buildMethodEntries(descriptors, context, states) {
3300
3082
  entry.internalValue = internalValue;
3301
3083
  entry.bindInternal = (opts) => bindInternalTwin({
3302
3084
  ...opts,
3303
- withContext: (context2) => (input) => canonicalValue(input, context2),
3085
+ withContext: (ctx) => (input) => canonicalValue(input, ctx),
3304
3086
  internalValue
3305
3087
  });
3306
3088
  }
3307
3089
  plugins[id] = entry;
3308
3090
  }
3309
3091
  }
3310
- function buildEagerArtifacts(descriptors, context, states) {
3092
+ function buildPropertyEntry(descriptor, id, context, states) {
3093
+ const plugins = context.plugins;
3094
+ const base = {
3095
+ pluginType: "property",
3096
+ name: descriptor.name,
3097
+ descriptor,
3098
+ ...pickDefined(descriptor, PROPERTY_META_KEYS),
3099
+ dynamicMembers: descriptor.dynamicMembers
3100
+ };
3101
+ if (descriptor.privileged) return { ...base, value: context };
3102
+ if (descriptor.get) {
3103
+ const get = descriptor.get;
3104
+ const importBindings = descriptor.importBindings;
3105
+ return {
3106
+ ...base,
3107
+ getValue: (callContext) => get({
3108
+ imports: buildImports({ plugins, importBindings, ctx: callContext }),
3109
+ state: states.get(id),
3110
+ callContext
3111
+ })
3112
+ };
3113
+ }
3114
+ return { ...base, value: descriptor.value };
3115
+ }
3116
+ function runSetup(descriptors, context, states) {
3311
3117
  const plugins = context.plugins;
3312
- const built = /* @__PURE__ */ new Set();
3313
- const building = /* @__PURE__ */ new Set();
3314
- const ensureBuilt = (id) => {
3315
- if (built.has(id)) return;
3118
+ const done = /* @__PURE__ */ new Set();
3119
+ const running = /* @__PURE__ */ new Set();
3120
+ const ensureSetup = (id) => {
3121
+ if (done.has(id)) return;
3316
3122
  const descriptor = descriptors.get(id);
3317
- if (!descriptor || descriptor.pluginType === "aggregate" || descriptor.pluginType === "legacy" || descriptor.pluginType === "method-override" || isStandIn(descriptor)) {
3318
- built.add(id);
3123
+ if (!descriptor || descriptor.pluginType === "aggregate" || descriptor.pluginType === "method-override" || isStandIn(descriptor)) {
3124
+ done.add(id);
3319
3125
  return;
3320
3126
  }
3321
- if (building.has(id)) {
3127
+ if (running.has(id)) {
3322
3128
  throw new Error(`createSdk: dependency cycle at "${id}".`);
3323
3129
  }
3324
- building.add(id);
3325
- for (const { id: depId } of descriptor.importBindings) ensureBuilt(depId);
3130
+ running.add(id);
3131
+ for (const { id: depId } of descriptor.importBindings) ensureSetup(depId);
3326
3132
  const recordDisposer = () => {
3327
3133
  const dispose = descriptor.dispose;
3328
3134
  if (!dispose) return;
@@ -3341,83 +3147,23 @@ function buildEagerArtifacts(descriptors, context, states) {
3341
3147
  })
3342
3148
  });
3343
3149
  };
3344
- if (descriptor.pluginType === "hook") {
3345
- states.set(
3346
- id,
3347
- descriptor.setup ? descriptor.setup({
3348
- imports: buildImports({
3349
- plugins,
3350
- importBindings: descriptor.importBindings
3351
- })
3352
- }) : void 0
3353
- );
3354
- recordDisposer();
3355
- building.delete(id);
3356
- built.add(id);
3357
- return;
3358
- }
3359
- if (descriptor.pluginType === "method") {
3360
- states.set(
3361
- id,
3362
- descriptor.setup ? descriptor.setup({
3363
- imports: buildImports({
3364
- plugins,
3365
- importBindings: descriptor.importBindings
3366
- })
3367
- }) : void 0
3368
- );
3369
- } else {
3370
- states.set(
3371
- id,
3372
- descriptor.setup ? descriptor.setup({
3373
- imports: buildImports({
3374
- plugins,
3375
- importBindings: descriptor.importBindings
3376
- })
3377
- }) : void 0
3378
- );
3379
- if (descriptor.privileged) {
3380
- plugins[id] = {
3381
- pluginType: "property",
3382
- name: descriptor.name,
3383
- value: context,
3384
- meta: descriptor.meta,
3385
- dynamicMembers: descriptor.dynamicMembers
3386
- };
3387
- } else if (descriptor.get) {
3388
- const get = descriptor.get;
3389
- const importBindings = descriptor.importBindings;
3390
- plugins[id] = {
3391
- pluginType: "property",
3392
- name: descriptor.name,
3393
- getValue: (callContext) => get({
3394
- imports: buildImports({
3395
- plugins,
3396
- importBindings,
3397
- ctx: callContext
3398
- }),
3399
- state: states.get(id),
3400
- callContext
3401
- }),
3402
- meta: descriptor.meta,
3403
- dynamicMembers: descriptor.dynamicMembers
3404
- };
3405
- } else {
3406
- plugins[id] = {
3407
- pluginType: "property",
3408
- name: descriptor.name,
3409
- value: descriptor.value,
3410
- meta: descriptor.meta,
3411
- dynamicMembers: descriptor.dynamicMembers
3412
- };
3413
- }
3150
+ states.set(
3151
+ id,
3152
+ descriptor.setup?.({
3153
+ imports: buildImports({
3154
+ plugins,
3155
+ importBindings: descriptor.importBindings
3156
+ })
3157
+ })
3158
+ );
3159
+ recordDisposer();
3160
+ if (descriptor.pluginType === "property") {
3414
3161
  assertDynamicMemberRoot(plugins[id]);
3415
3162
  }
3416
- recordDisposer();
3417
- building.delete(id);
3418
- built.add(id);
3163
+ running.delete(id);
3164
+ done.add(id);
3419
3165
  };
3420
- for (const id of descriptors.keys()) ensureBuilt(id);
3166
+ for (const id of descriptors.keys()) ensureSetup(id);
3421
3167
  }
3422
3168
  function resolvePlugin(sdk, ref) {
3423
3169
  const entry = getContext(sdk).plugins[ref.id];
@@ -3432,11 +3178,26 @@ function resolvePlugin(sdk, ref) {
3432
3178
  if (entry.pluginType === "property" && entry.getValue) {
3433
3179
  return entry.getValue();
3434
3180
  }
3435
- if (entry.pluginType === "method" && entry.internalValue) {
3436
- return entry.bindInternal?.({ frameworkOrigin: true }) ?? entry.internalValue;
3181
+ if (entry.pluginType === "method") {
3182
+ return entry.bindInternal({
3183
+ frameworkOrigin: true
3184
+ });
3185
+ }
3186
+ if (entry.pluginType === "hook" || entry.pluginType === "method-override") {
3187
+ throw new Error(
3188
+ `resolvePlugin: "${ref.id}" is a ${entry.pluginType}, which has no value to resolve. Resolve a method or property.`
3189
+ );
3437
3190
  }
3438
3191
  return entry.value;
3439
3192
  }
3193
+ function valueEntryOf(entry, id) {
3194
+ if (entry.pluginType === "hook" || entry.pluginType === "method-override") {
3195
+ throw new Error(
3196
+ `"${id}" is a ${entry.pluginType} and has no value to bind.`
3197
+ );
3198
+ }
3199
+ return entry;
3200
+ }
3440
3201
  var CoreDisposeError = class extends Error {
3441
3202
  constructor(errors) {
3442
3203
  super(`disposeSdk: ${errors.length} dispose callback(s) failed.`);
@@ -3468,9 +3229,20 @@ function resolveAggregates(descriptors, context) {
3468
3229
  if (descriptor.pluginType !== "aggregate") continue;
3469
3230
  const exports2 = {};
3470
3231
  for (const [binding, child] of Object.entries(descriptor.exports)) {
3471
- bindValue({ target: exports2, key: binding, entry: plugins[child.id] });
3232
+ const entry = plugins[child.id];
3233
+ if (!entry || entry.pluginType === "hook" || entry.pluginType === "method-override") {
3234
+ throw new Error(
3235
+ `createSdk: export "${binding}" resolves to "${child.id}", which has no value. A defineHook or defineOverride belongs in \`imports\`, not \`exports\`: neither surfaces a value.`
3236
+ );
3237
+ }
3238
+ bindValue({ target: exports2, key: binding, entry });
3472
3239
  }
3473
- plugins[id] = { pluginType: "aggregate", name: descriptor.name, exports: exports2 };
3240
+ plugins[id] = {
3241
+ pluginType: "aggregate",
3242
+ name: descriptor.name,
3243
+ descriptor,
3244
+ exports: exports2
3245
+ };
3474
3246
  }
3475
3247
  }
3476
3248
  function assembleMiddleware(descriptors, context, states) {
@@ -3511,9 +3283,9 @@ function assembleHooks(descriptors, context, states) {
3511
3283
  const plugins = context.plugins;
3512
3284
  for (const id of topoOrder(descriptors)) {
3513
3285
  const descriptor = descriptors.get(id);
3514
- if (!descriptor || descriptor.pluginType !== "hook" || !descriptor.observe && !descriptor.annotator) {
3515
- continue;
3516
- }
3286
+ if (!descriptor || descriptor.pluginType !== "hook") continue;
3287
+ plugins[id] = { pluginType: "hook", name: descriptor.name, descriptor };
3288
+ if (!descriptor.observe && !descriptor.annotator) continue;
3517
3289
  const { observe, annotator } = descriptor;
3518
3290
  const state = states.get(id);
3519
3291
  const contributed = {};
@@ -3550,55 +3322,34 @@ function assembleHooks(descriptors, context, states) {
3550
3322
  }
3551
3323
  }
3552
3324
  function createSdk(root, options) {
3325
+ assertPluginArgument(root, { caller: "createSdk", asRoot: true });
3326
+ assertNotReservedKeys({
3327
+ keys: root.pluginType === "aggregate" ? Object.keys(root.exports) : [root.name],
3328
+ caller: "createSdk"
3329
+ });
3553
3330
  const context = {
3554
- plugins: {},
3555
- meta: {},
3331
+ plugins: /* @__PURE__ */ Object.create(null),
3556
3332
  hooks: {},
3557
- surface: {},
3333
+ surface: /* @__PURE__ */ Object.create(null),
3558
3334
  disposers: []
3559
3335
  };
3560
- if (root.pluginType === "legacy-merge") {
3561
- const { legacy, plugin } = root;
3562
- const collectRoot = {
3563
- pluginType: "aggregate",
3564
- name: root.name,
3565
- id: `${root.id}:merge`,
3566
- imports: [legacy, plugin],
3567
- importBindings: [],
3568
- exports: {}
3569
- };
3570
- const plugins2 = materialize(
3571
- collectPlugins(collectRoot, void 0, options?.configuration),
3572
- context
3573
- );
3574
- const legacyExports = plugins2[legacy.id].exports;
3575
- let pluginSurface;
3576
- if (plugin.pluginType === "aggregate") {
3577
- pluginSurface = plugins2[plugin.id].exports;
3578
- } else {
3579
- pluginSurface = {};
3580
- bindValue({
3581
- target: pluginSurface,
3582
- key: plugin.name,
3583
- entry: plugins2[plugin.id]
3584
- });
3585
- }
3586
- for (const key of Object.keys(legacyExports)) context.surface[key] = key;
3587
- if (plugin.pluginType === "aggregate") {
3588
- recordExportSurface(context, plugin.exports);
3589
- } else {
3590
- context.surface[plugin.name] = plugin.id;
3591
- }
3592
- return buildSurface(context, legacyExports, pluginSurface);
3593
- }
3594
3336
  const plugins = materialize(
3595
- collectPlugins(root, void 0, options?.configuration),
3337
+ collectPlugins({
3338
+ root,
3339
+ caller: "createSdk",
3340
+ applied: context.plugins,
3341
+ configuration: options?.configuration
3342
+ }),
3596
3343
  context
3597
3344
  );
3598
3345
  if (root.pluginType === "method" || root.pluginType === "property") {
3599
3346
  context.surface[root.name] = root.id;
3600
3347
  const sdk = buildSurface(context);
3601
- bindValue({ target: sdk, key: root.name, entry: plugins[root.id] });
3348
+ bindValue({
3349
+ target: sdk,
3350
+ key: root.name,
3351
+ entry: valueEntryOf(plugins[root.id], root.id)
3352
+ });
3602
3353
  return sdk;
3603
3354
  }
3604
3355
  if (root.pluginType === "aggregate")
@@ -3609,54 +3360,67 @@ function addModelPlugin(sdk, plugin, options = {}) {
3609
3360
  const override = options.override === true;
3610
3361
  const context = getContext(sdk);
3611
3362
  const surfaceKeys = plugin.pluginType === "aggregate" ? Object.keys(plugin.exports) : plugin.pluginType === "hook" ? [] : [plugin.name];
3612
- checkRootKeyCollisions(sdk, surfaceKeys, override, "addPlugin");
3613
- const materialized = new Set(Object.keys(context.plugins));
3614
- if (override && materialized.has(plugin.id)) {
3363
+ checkRootKeyCollisions({
3364
+ target: sdk,
3365
+ keys: surfaceKeys,
3366
+ override,
3367
+ caller: "addPlugin"
3368
+ });
3369
+ if (override && surfaceKeys.length > 0 && plugin.id in context.plugins) {
3615
3370
  throw new Error(
3616
- `addPlugin: cannot override already-materialized plugin "${plugin.id}" on the incremental path. Rebuild the SDK with the replacement via createSdk.`
3371
+ `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.`
3617
3372
  );
3618
3373
  }
3619
- materialize(collectPlugins(plugin, materialized), context);
3620
- if (plugin.pluginType === "hook") return;
3621
- const entry = context.plugins[plugin.id];
3622
- if (entry.pluginType === "aggregate") {
3623
- Object.defineProperties(
3624
- sdk,
3625
- Object.getOwnPropertyDescriptors(entry.exports)
3374
+ const undo = snapshotGraph({ context, sdk, surfaceKeys });
3375
+ try {
3376
+ materialize(
3377
+ collectPlugins({
3378
+ root: plugin,
3379
+ caller: "addPlugin",
3380
+ applied: context.plugins
3381
+ }),
3382
+ context
3626
3383
  );
3627
- for (const [binding, child] of Object.entries(
3628
- plugin.exports
3629
- )) {
3630
- context.surface[binding] = child.id;
3384
+ if (plugin.pluginType === "hook") return;
3385
+ const entry = valueEntryOf(context.plugins[plugin.id], plugin.id);
3386
+ if (entry.pluginType === "aggregate") {
3387
+ Object.defineProperties(
3388
+ sdk,
3389
+ Object.getOwnPropertyDescriptors(entry.exports)
3390
+ );
3391
+ for (const [binding, child] of Object.entries(
3392
+ plugin.exports
3393
+ )) {
3394
+ context.surface[binding] = child.id;
3395
+ }
3396
+ } else {
3397
+ bindValue({ target: sdk, key: plugin.name, entry });
3398
+ context.surface[plugin.name] = plugin.id;
3631
3399
  }
3632
- } else {
3633
- bindValue({ target: sdk, key: plugin.name, entry });
3634
- context.surface[plugin.name] = plugin.id;
3400
+ } catch (cause) {
3401
+ try {
3402
+ undo();
3403
+ } catch (rollbackFailure) {
3404
+ const original = cause;
3405
+ if (original && typeof original === "object" && original.cause === void 0) {
3406
+ original.cause = rollbackFailure;
3407
+ }
3408
+ }
3409
+ throw cause;
3635
3410
  }
3636
3411
  }
3637
3412
  function addPlugin(sdk, plugin, options) {
3638
3413
  const record = sdk;
3414
+ assertPluginArgument(plugin, { caller: "addPlugin", asRoot: false });
3639
3415
  const context = getContext(record);
3640
3416
  try {
3641
- if (typeof plugin === "function") {
3642
- const contribution = applyPluginToSdk(
3643
- record,
3644
- plugin,
3645
- options ?? {}
3646
- );
3647
- if (context) {
3648
- mirrorLegacyRootKeys(context, contribution.rootKeys, contribution.meta);
3649
- for (const name of Object.keys(contribution.rootKeys)) {
3650
- context.surface[name] = name;
3651
- }
3652
- }
3653
- } else if (plugin.pluginType === "method-override") {
3654
- applyMethodOverride(context, plugin);
3417
+ if (plugin.pluginType === "method-override") {
3418
+ addOverridePlugin(context, plugin);
3655
3419
  } else {
3656
3420
  addModelPlugin(record, plugin, options ?? {});
3657
3421
  }
3658
3422
  } finally {
3659
- if (context) invalidateRegistryCache(context);
3423
+ invalidateRegistryCache(context);
3660
3424
  }
3661
3425
  }
3662
3426
 
@@ -4905,18 +4669,6 @@ function createController(sdk) {
4905
4669
  return { resolve, start: start2, step: step2, listMethods, getMethod, listChoices };
4906
4670
  }
4907
4671
 
4908
- // src/utils/core-plugin.ts
4909
- function createCorePlugin(options) {
4910
- logDeprecation(
4911
- "createCorePlugin() is deprecated. Inject the options under CORE_OPTIONS_ID via createSdk's configuration instead."
4912
- );
4913
- return () => ({
4914
- context: {
4915
- core: options
4916
- }
4917
- });
4918
- }
4919
-
4920
4672
  // src/transport/attempt-http-request.ts
4921
4673
  var import_zod10 = require("zod");
4922
4674
 
@@ -5352,20 +5104,13 @@ var resolveConnectionPlugin = defineMethod({
5352
5104
  attemptHttpRequestPlugin,
5353
5105
  authorizeHttpRequestPlugin,
5354
5106
  canonicalInputSchema,
5355
- composePlugins,
5356
5107
  concatLists,
5357
5108
  concatPaginated,
5358
5109
  coreOptionsPluginRef,
5359
5110
  createAsyncContext,
5360
5111
  createController,
5361
5112
  createCoreError,
5362
- createCorePlugin,
5363
5113
  createDeprecationLogger,
5364
- createFunction,
5365
- createPaginatedFunction,
5366
- createPaginatedPluginMethod,
5367
- createPluginMethod,
5368
- createPluginStack,
5369
5114
  createPrefixedCursor,
5370
5115
  createSdk,
5371
5116
  createStabilityNoticeLogger,
@@ -5382,7 +5127,6 @@ var resolveConnectionPlugin = defineMethod({
5382
5127
  defaultLogDeprecation,
5383
5128
  defineFormatter,
5384
5129
  defineHook,
5385
- defineLegacyMerge,
5386
5130
  defineMethod,
5387
5131
  defineMethodOverride,
5388
5132
  defineOverride,
@@ -5392,7 +5136,6 @@ var resolveConnectionPlugin = defineMethod({
5392
5136
  dispatchHttpRequestPlugin,
5393
5137
  disposeSdk,
5394
5138
  fetchPlugin,
5395
- fromFunctionPlugin,
5396
5139
  getContext,
5397
5140
  getCoreErrorCause,
5398
5141
  getCoreErrorCode,