@zapier/kitcore 0.0.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +37 -0
- package/LICENSE +2 -0
- package/README.md +382 -2
- package/dist/index.cjs +3050 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.mts +2907 -0
- package/dist/index.d.ts +2907 -0
- package/dist/index.mjs +2962 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +53 -5
- package/index.mjs +0 -3
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,3050 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key2 of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key2) && key2 !== except)
|
|
14
|
+
__defProp(to, key2, { get: () => from[key2], enumerable: !(desc = __getOwnPropDesc(from, key2)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
CONTEXT: () => CONTEXT,
|
|
24
|
+
CORE_ERROR_SYMBOL: () => CORE_ERROR_SYMBOL,
|
|
25
|
+
CORE_SIGNAL_SYMBOL: () => CORE_SIGNAL_SYMBOL,
|
|
26
|
+
CoreCancelledSignal: () => CoreCancelledSignal,
|
|
27
|
+
CoreError: () => CoreError,
|
|
28
|
+
CoreErrorCode: () => CoreErrorCode,
|
|
29
|
+
CoreSignal: () => CoreSignal,
|
|
30
|
+
addPlugin: () => addPlugin,
|
|
31
|
+
composePlugins: () => composePlugins,
|
|
32
|
+
concatPaginated: () => concatPaginated,
|
|
33
|
+
createAsyncContext: () => createAsyncContext,
|
|
34
|
+
createController: () => createController,
|
|
35
|
+
createCoreError: () => createCoreError,
|
|
36
|
+
createCorePlugin: () => createCorePlugin,
|
|
37
|
+
createDeprecationLogger: () => createDeprecationLogger,
|
|
38
|
+
createFunction: () => createFunction,
|
|
39
|
+
createPaginatedFunction: () => createPaginatedFunction,
|
|
40
|
+
createPaginatedPluginMethod: () => createPaginatedPluginMethod,
|
|
41
|
+
createPluginMethod: () => createPluginMethod,
|
|
42
|
+
createPluginStack: () => createPluginStack,
|
|
43
|
+
createPrefixedCursor: () => createPrefixedCursor,
|
|
44
|
+
createSdk: () => createSdk,
|
|
45
|
+
createValidator: () => createValidator,
|
|
46
|
+
dangerousContextPlugin: () => dangerousContextPlugin,
|
|
47
|
+
declareMethod: () => declareMethod,
|
|
48
|
+
declarePlugin: () => declarePlugin,
|
|
49
|
+
declareProperty: () => declareProperty,
|
|
50
|
+
decodeIncomingCursor: () => decodeIncomingCursor,
|
|
51
|
+
defineFormatter: () => defineFormatter,
|
|
52
|
+
defineLegacyMerge: () => defineLegacyMerge,
|
|
53
|
+
defineMethod: () => defineMethod,
|
|
54
|
+
definePlugin: () => definePlugin,
|
|
55
|
+
defineProperty: () => defineProperty,
|
|
56
|
+
defineResolver: () => defineResolver,
|
|
57
|
+
fromFunctionPlugin: () => fromFunctionPlugin,
|
|
58
|
+
getContext: () => getContext,
|
|
59
|
+
getCoreErrorCause: () => getCoreErrorCause,
|
|
60
|
+
getCoreErrorCode: () => getCoreErrorCode,
|
|
61
|
+
getCurrentDepth: () => getCurrentDepth,
|
|
62
|
+
getCurrentScope: () => getCurrentScope,
|
|
63
|
+
getFieldDescriptions: () => getFieldDescriptions,
|
|
64
|
+
getOutputSchema: () => getOutputSchema,
|
|
65
|
+
getRegistryPlugin: () => getRegistryPlugin,
|
|
66
|
+
getSchemaDescription: () => getSchemaDescription,
|
|
67
|
+
isCoreError: () => isCoreError,
|
|
68
|
+
isCoreSignal: () => isCoreSignal,
|
|
69
|
+
isNestedMethodCall: () => isNestedMethodCall,
|
|
70
|
+
isPositional: () => isPositional,
|
|
71
|
+
isTelemetryNested: () => isTelemetryNested,
|
|
72
|
+
openEnum: () => openEnum,
|
|
73
|
+
paginate: () => paginate,
|
|
74
|
+
paginateBuffered: () => paginateBuffered,
|
|
75
|
+
paginateMaxItems: () => paginateMaxItems,
|
|
76
|
+
runInMethodScope: () => runInMethodScope,
|
|
77
|
+
runWithTelemetryContext: () => runWithTelemetryContext,
|
|
78
|
+
selectExports: () => selectExports,
|
|
79
|
+
splitPrefixedCursor: () => splitPrefixedCursor,
|
|
80
|
+
toIterable: () => toIterable,
|
|
81
|
+
toSnakeCase: () => toSnakeCase,
|
|
82
|
+
toTitleCase: () => toTitleCase,
|
|
83
|
+
validateOptions: () => validateOptions,
|
|
84
|
+
withOutputSchema: () => withOutputSchema,
|
|
85
|
+
withPositional: () => withPositional,
|
|
86
|
+
withResolver: () => withResolver
|
|
87
|
+
});
|
|
88
|
+
module.exports = __toCommonJS(index_exports);
|
|
89
|
+
|
|
90
|
+
// src/registry.ts
|
|
91
|
+
var import_zod = require("zod");
|
|
92
|
+
|
|
93
|
+
// src/utils/string-utils.ts
|
|
94
|
+
function toTitleCase(input) {
|
|
95
|
+
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(" ");
|
|
96
|
+
}
|
|
97
|
+
function toSnakeCase(input) {
|
|
98
|
+
let result = input.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[\s\-]+/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "").toLowerCase();
|
|
99
|
+
if (/^[0-9]/.test(result)) {
|
|
100
|
+
result = "_" + result;
|
|
101
|
+
}
|
|
102
|
+
return result;
|
|
103
|
+
}
|
|
104
|
+
function pluralize(word) {
|
|
105
|
+
if (/s$/i.test(word)) return word;
|
|
106
|
+
if (/[bcdfghjklmnpqrstvwxz]y$/i.test(word)) {
|
|
107
|
+
return word.slice(0, -1) + "ies";
|
|
108
|
+
}
|
|
109
|
+
return word + "s";
|
|
110
|
+
}
|
|
111
|
+
function pluralizeLastWord(title) {
|
|
112
|
+
const words = title.split(" ");
|
|
113
|
+
return [...words.slice(0, -1), pluralize(words[words.length - 1])].join(" ");
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// src/registry.ts
|
|
117
|
+
function resolveCategoryDefinition(ref) {
|
|
118
|
+
const def = typeof ref === "string" ? { key: ref } : ref;
|
|
119
|
+
const title = def.title ?? toTitleCase(def.key);
|
|
120
|
+
return {
|
|
121
|
+
key: def.key,
|
|
122
|
+
title,
|
|
123
|
+
titlePlural: def.titlePlural ?? pluralizeLastWord(title)
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
function canonicalInputSchema(schema) {
|
|
127
|
+
if (schema instanceof import_zod.z.ZodUnion) {
|
|
128
|
+
return schema.options[0];
|
|
129
|
+
}
|
|
130
|
+
return schema;
|
|
131
|
+
}
|
|
132
|
+
function buildRegistry({
|
|
133
|
+
sdk,
|
|
134
|
+
meta,
|
|
135
|
+
formatters,
|
|
136
|
+
boundResolvers,
|
|
137
|
+
positional,
|
|
138
|
+
packageFilter
|
|
139
|
+
}) {
|
|
140
|
+
const definitionsByKey = /* @__PURE__ */ new Map();
|
|
141
|
+
const objectDeclaredKeys = /* @__PURE__ */ new Set();
|
|
142
|
+
for (const m of Object.values(meta)) {
|
|
143
|
+
for (const ref of m.categories ?? []) {
|
|
144
|
+
const key2 = typeof ref === "string" ? ref : ref.key;
|
|
145
|
+
if (typeof ref === "object") {
|
|
146
|
+
objectDeclaredKeys.add(key2);
|
|
147
|
+
definitionsByKey.set(key2, resolveCategoryDefinition(ref));
|
|
148
|
+
} else if (!objectDeclaredKeys.has(key2)) {
|
|
149
|
+
definitionsByKey.set(key2, resolveCategoryDefinition(ref));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if (!definitionsByKey.has("other")) {
|
|
154
|
+
definitionsByKey.set("other", resolveCategoryDefinition("other"));
|
|
155
|
+
}
|
|
156
|
+
const knownCategories = Array.from(definitionsByKey.keys());
|
|
157
|
+
const functions = Object.keys(meta).filter((key2) => {
|
|
158
|
+
const property = sdk[key2];
|
|
159
|
+
if (typeof property === "function") return true;
|
|
160
|
+
const [rootKey] = key2.split(".");
|
|
161
|
+
const rootProperty = sdk[rootKey];
|
|
162
|
+
return typeof rootProperty === "object" && rootProperty !== null;
|
|
163
|
+
}).map((key2) => {
|
|
164
|
+
const m = meta[key2];
|
|
165
|
+
return {
|
|
166
|
+
name: key2,
|
|
167
|
+
description: m.description,
|
|
168
|
+
type: m.type,
|
|
169
|
+
itemType: m.itemType,
|
|
170
|
+
returnType: m.returnType,
|
|
171
|
+
inputSchema: canonicalInputSchema(m.inputSchema),
|
|
172
|
+
inputParameters: m.inputParameters,
|
|
173
|
+
outputSchema: m.outputSchema,
|
|
174
|
+
positional: positional?.[key2],
|
|
175
|
+
categories: (m.categories ?? []).map(
|
|
176
|
+
(c) => typeof c === "string" ? c : c.key
|
|
177
|
+
),
|
|
178
|
+
resolvers: m.resolvers,
|
|
179
|
+
boundResolvers: boundResolvers?.[key2],
|
|
180
|
+
formatter: formatters?.[key2],
|
|
181
|
+
experimental: m.experimental,
|
|
182
|
+
packages: m.packages,
|
|
183
|
+
confirm: m.confirm ?? (m.type === "delete" ? "delete" : void 0),
|
|
184
|
+
deprecation: m.deprecation,
|
|
185
|
+
aliases: m.aliases,
|
|
186
|
+
supportsJsonOutput: m.supportsJsonOutput ?? true
|
|
187
|
+
};
|
|
188
|
+
}).sort((a, b) => a.name.localeCompare(b.name));
|
|
189
|
+
const filteredFunctions = packageFilter ? functions.filter((f) => !f.packages || f.packages.includes(packageFilter)) : functions;
|
|
190
|
+
const filteredCategories = knownCategories.slice().sort((a, b) => {
|
|
191
|
+
if (a === "other") return 1;
|
|
192
|
+
if (b === "other") return -1;
|
|
193
|
+
return definitionsByKey.get(a).title.localeCompare(definitionsByKey.get(b).title);
|
|
194
|
+
}).map((categoryKey) => {
|
|
195
|
+
const categoryFunctions = filteredFunctions.filter(
|
|
196
|
+
(f) => f.categories.includes(categoryKey) || categoryKey === "other" && !f.categories.some((c) => knownCategories.includes(c))
|
|
197
|
+
).map((f) => f.name).sort();
|
|
198
|
+
const def = definitionsByKey.get(categoryKey);
|
|
199
|
+
return {
|
|
200
|
+
key: categoryKey,
|
|
201
|
+
title: def.title,
|
|
202
|
+
titlePlural: def.titlePlural,
|
|
203
|
+
functions: categoryFunctions
|
|
204
|
+
};
|
|
205
|
+
}).filter((category) => category.functions.length > 0);
|
|
206
|
+
return { functions: filteredFunctions, categories: filteredCategories };
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// src/utils/build-hooks.ts
|
|
210
|
+
function composeVoid(existing, added) {
|
|
211
|
+
if (!existing) return added;
|
|
212
|
+
if (!added) return existing;
|
|
213
|
+
return (ctx) => {
|
|
214
|
+
existing(ctx);
|
|
215
|
+
added(ctx);
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
function buildHooks(existing, added) {
|
|
219
|
+
const result = {};
|
|
220
|
+
const start2 = composeVoid(existing.onMethodStart, added.onMethodStart);
|
|
221
|
+
if (start2) result.onMethodStart = start2;
|
|
222
|
+
const end = composeVoid(existing.onMethodEnd, added.onMethodEnd);
|
|
223
|
+
if (end) result.onMethodEnd = end;
|
|
224
|
+
return result;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// src/utils/logging.ts
|
|
228
|
+
function createDeprecationLogger(tag) {
|
|
229
|
+
const loggedDeprecations = /* @__PURE__ */ new Set();
|
|
230
|
+
return {
|
|
231
|
+
logDeprecation(message) {
|
|
232
|
+
if (loggedDeprecations.has(message)) return;
|
|
233
|
+
loggedDeprecations.add(message);
|
|
234
|
+
console.warn(`[${tag}] Deprecation: ${message}`);
|
|
235
|
+
},
|
|
236
|
+
resetDeprecationWarnings() {
|
|
237
|
+
loggedDeprecations.clear();
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
var { logDeprecation, resetDeprecationWarnings } = createDeprecationLogger("core");
|
|
242
|
+
|
|
243
|
+
// src/types/errors.ts
|
|
244
|
+
var CORE_ERROR_SYMBOL = Symbol.for("kitcore.error");
|
|
245
|
+
var CoreErrorCode = {
|
|
246
|
+
Validation: "VALIDATION_ERROR",
|
|
247
|
+
Unknown: "UNKNOWN_ERROR"
|
|
248
|
+
};
|
|
249
|
+
var CoreError = class extends Error {
|
|
250
|
+
constructor(message, options = {}) {
|
|
251
|
+
super(message);
|
|
252
|
+
this.name = "CoreError";
|
|
253
|
+
if (options.statusCode !== void 0) this.statusCode = options.statusCode;
|
|
254
|
+
if (options.errors !== void 0) this.errors = options.errors;
|
|
255
|
+
if (options.cause !== void 0) this.cause = options.cause;
|
|
256
|
+
if (options.response !== void 0) this.response = options.response;
|
|
257
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
function createCoreError(options, adaptError) {
|
|
261
|
+
const error = adaptError?.(options) ?? new CoreError(options.message, { cause: options.cause });
|
|
262
|
+
Object.defineProperty(error, CORE_ERROR_SYMBOL, {
|
|
263
|
+
value: true,
|
|
264
|
+
enumerable: false,
|
|
265
|
+
configurable: true,
|
|
266
|
+
writable: false
|
|
267
|
+
});
|
|
268
|
+
Object.defineProperty(error, "coreCode", {
|
|
269
|
+
value: options.code,
|
|
270
|
+
enumerable: false,
|
|
271
|
+
configurable: true,
|
|
272
|
+
writable: false
|
|
273
|
+
});
|
|
274
|
+
return error;
|
|
275
|
+
}
|
|
276
|
+
function isCoreError(value) {
|
|
277
|
+
return Boolean(
|
|
278
|
+
value && typeof value === "object" && value[CORE_ERROR_SYMBOL] === true
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
function getCoreErrorCode(value) {
|
|
282
|
+
if (!isCoreError(value)) return void 0;
|
|
283
|
+
return value.coreCode;
|
|
284
|
+
}
|
|
285
|
+
function getCoreErrorCause(value) {
|
|
286
|
+
if (!isCoreError(value)) return void 0;
|
|
287
|
+
return value.cause;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// src/utils/pagination-utils.ts
|
|
291
|
+
var CURSOR_VERSION = 1;
|
|
292
|
+
var CURSOR_SOURCE = {
|
|
293
|
+
API: "api",
|
|
294
|
+
SDK: "sdk"
|
|
295
|
+
};
|
|
296
|
+
function encodeBase64(str) {
|
|
297
|
+
return btoa(
|
|
298
|
+
Array.from(
|
|
299
|
+
new TextEncoder().encode(str),
|
|
300
|
+
(b) => String.fromCharCode(b)
|
|
301
|
+
).join("")
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
function decodeBase64(str) {
|
|
305
|
+
return new TextDecoder().decode(
|
|
306
|
+
Uint8Array.from(atob(str), (c) => c.charCodeAt(0))
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
function encodeApiCursor(cursor) {
|
|
310
|
+
const envelope = {
|
|
311
|
+
v: CURSOR_VERSION,
|
|
312
|
+
source: CURSOR_SOURCE.API,
|
|
313
|
+
cursor
|
|
314
|
+
};
|
|
315
|
+
return encodeBase64(JSON.stringify(envelope));
|
|
316
|
+
}
|
|
317
|
+
function encodeSdkCursor(offset, cursor) {
|
|
318
|
+
const envelope = {
|
|
319
|
+
v: CURSOR_VERSION,
|
|
320
|
+
source: CURSOR_SOURCE.SDK,
|
|
321
|
+
cursor,
|
|
322
|
+
offset
|
|
323
|
+
};
|
|
324
|
+
return encodeBase64(JSON.stringify(envelope));
|
|
325
|
+
}
|
|
326
|
+
function decodeIncomingCursor(incoming) {
|
|
327
|
+
if (!incoming) {
|
|
328
|
+
return { offset: 0, cursor: void 0 };
|
|
329
|
+
}
|
|
330
|
+
try {
|
|
331
|
+
const decoded = decodeBase64(incoming);
|
|
332
|
+
const envelope = JSON.parse(decoded);
|
|
333
|
+
if (envelope.v !== CURSOR_VERSION) {
|
|
334
|
+
return { offset: 0, cursor: incoming };
|
|
335
|
+
}
|
|
336
|
+
if (envelope.source === CURSOR_SOURCE.SDK) {
|
|
337
|
+
return { offset: envelope.offset ?? 0, cursor: envelope.cursor };
|
|
338
|
+
}
|
|
339
|
+
if (envelope.source === CURSOR_SOURCE.API) {
|
|
340
|
+
return { offset: 0, cursor: envelope.cursor };
|
|
341
|
+
}
|
|
342
|
+
return { offset: 0, cursor: incoming };
|
|
343
|
+
} catch {
|
|
344
|
+
return { offset: 0, cursor: incoming };
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
function createPrefixedCursor(prefix, cursor) {
|
|
348
|
+
if (!cursor) {
|
|
349
|
+
return `${prefix}::`;
|
|
350
|
+
}
|
|
351
|
+
return `${prefix}::${cursor}`;
|
|
352
|
+
}
|
|
353
|
+
function splitPrefixedCursor(cursor, prefixes) {
|
|
354
|
+
if (!cursor) {
|
|
355
|
+
return [void 0, void 0];
|
|
356
|
+
}
|
|
357
|
+
const [prefix, ...rest] = cursor.split("::");
|
|
358
|
+
if (prefixes && !prefixes.includes(prefix)) {
|
|
359
|
+
return [void 0, cursor];
|
|
360
|
+
}
|
|
361
|
+
cursor = rest.join("::");
|
|
362
|
+
if (!cursor) {
|
|
363
|
+
return [prefix, void 0];
|
|
364
|
+
}
|
|
365
|
+
return [prefix, cursor];
|
|
366
|
+
}
|
|
367
|
+
async function* paginateMaxItemsWithUnencodedCursor(pageFunction, pageOptions) {
|
|
368
|
+
let cursor = pageOptions?.cursor;
|
|
369
|
+
let totalItemsYielded = 0;
|
|
370
|
+
const maxItems = pageOptions?.maxItems;
|
|
371
|
+
const pageSize = pageOptions?.pageSize;
|
|
372
|
+
do {
|
|
373
|
+
const options = {
|
|
374
|
+
...pageOptions || {},
|
|
375
|
+
cursor,
|
|
376
|
+
pageSize: maxItems !== void 0 && pageSize !== void 0 ? Math.min(pageSize, maxItems) : pageSize
|
|
377
|
+
};
|
|
378
|
+
const page = await pageFunction(options);
|
|
379
|
+
if (maxItems !== void 0) {
|
|
380
|
+
const remainingItems = maxItems - totalItemsYielded;
|
|
381
|
+
if (page.data.length >= remainingItems) {
|
|
382
|
+
yield {
|
|
383
|
+
...page,
|
|
384
|
+
data: page.data.slice(0, remainingItems),
|
|
385
|
+
nextCursor: void 0
|
|
386
|
+
};
|
|
387
|
+
break;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
yield page;
|
|
391
|
+
totalItemsYielded += page.data.length;
|
|
392
|
+
cursor = page.nextCursor;
|
|
393
|
+
} while (cursor);
|
|
394
|
+
}
|
|
395
|
+
async function* paginateMaxItems(pageFunction, pageOptions) {
|
|
396
|
+
const { cursor } = decodeIncomingCursor(pageOptions?.cursor);
|
|
397
|
+
const options = {
|
|
398
|
+
...pageOptions || {},
|
|
399
|
+
cursor
|
|
400
|
+
};
|
|
401
|
+
for await (const page of paginateMaxItemsWithUnencodedCursor(
|
|
402
|
+
pageFunction,
|
|
403
|
+
options
|
|
404
|
+
)) {
|
|
405
|
+
yield {
|
|
406
|
+
...page,
|
|
407
|
+
nextCursor: page.nextCursor ? encodeApiCursor(page.nextCursor) : void 0
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
async function* paginateBuffered(pageFunction, pageOptions) {
|
|
412
|
+
const pageSize = pageOptions?.pageSize;
|
|
413
|
+
const { offset: cursorOffset, cursor: initialCursor } = decodeIncomingCursor(
|
|
414
|
+
pageOptions?.cursor
|
|
415
|
+
);
|
|
416
|
+
const requestedMaxItems = pageOptions?.maxItems;
|
|
417
|
+
const options = {
|
|
418
|
+
...pageOptions || {},
|
|
419
|
+
cursor: initialCursor,
|
|
420
|
+
// SDK cursors can carry an offset into a raw backend page. Since maxItems
|
|
421
|
+
// is expected to be relative to the resumed position, we add that offset
|
|
422
|
+
// so raw pagination still yields enough items after offset slicing.
|
|
423
|
+
maxItems: requestedMaxItems !== void 0 && cursorOffset > 0 ? requestedMaxItems + cursorOffset : requestedMaxItems
|
|
424
|
+
};
|
|
425
|
+
if (!pageSize) {
|
|
426
|
+
for await (const page of paginateMaxItemsWithUnencodedCursor(
|
|
427
|
+
pageFunction,
|
|
428
|
+
options
|
|
429
|
+
)) {
|
|
430
|
+
yield {
|
|
431
|
+
...page,
|
|
432
|
+
nextCursor: page.nextCursor ? encodeApiCursor(page.nextCursor) : void 0
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
let bufferedPages = [];
|
|
438
|
+
let isFirstPage = true;
|
|
439
|
+
let rawCursor;
|
|
440
|
+
for await (let page of paginateMaxItemsWithUnencodedCursor(
|
|
441
|
+
pageFunction,
|
|
442
|
+
options
|
|
443
|
+
)) {
|
|
444
|
+
const nextRawCursor = page.nextCursor;
|
|
445
|
+
if (isFirstPage) {
|
|
446
|
+
isFirstPage = false;
|
|
447
|
+
if (cursorOffset) {
|
|
448
|
+
page = {
|
|
449
|
+
...page,
|
|
450
|
+
data: page.data.slice(cursorOffset)
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
const bufferedLength = bufferedPages.reduce(
|
|
455
|
+
(acc, p) => acc + p.data.length,
|
|
456
|
+
0
|
|
457
|
+
);
|
|
458
|
+
if (bufferedLength + page.data.length < pageSize) {
|
|
459
|
+
bufferedPages.push(page);
|
|
460
|
+
rawCursor = nextRawCursor;
|
|
461
|
+
continue;
|
|
462
|
+
}
|
|
463
|
+
const bufferedItems = bufferedPages.map((p) => p.data).flat();
|
|
464
|
+
const allItems = [...bufferedItems, ...page.data];
|
|
465
|
+
const pageItems = allItems.slice(0, pageSize);
|
|
466
|
+
const remainingItems = allItems.slice(pageItems.length);
|
|
467
|
+
if (remainingItems.length === 0) {
|
|
468
|
+
yield {
|
|
469
|
+
...page,
|
|
470
|
+
data: pageItems,
|
|
471
|
+
nextCursor: nextRawCursor ? encodeApiCursor(nextRawCursor) : void 0
|
|
472
|
+
};
|
|
473
|
+
bufferedPages = [];
|
|
474
|
+
rawCursor = nextRawCursor;
|
|
475
|
+
continue;
|
|
476
|
+
}
|
|
477
|
+
yield {
|
|
478
|
+
...page,
|
|
479
|
+
data: pageItems,
|
|
480
|
+
nextCursor: encodeSdkCursor(
|
|
481
|
+
page.data.length - remainingItems.length,
|
|
482
|
+
rawCursor
|
|
483
|
+
)
|
|
484
|
+
};
|
|
485
|
+
while (remainingItems.length > pageSize) {
|
|
486
|
+
const chunkItems = remainingItems.splice(0, pageSize);
|
|
487
|
+
yield {
|
|
488
|
+
...page,
|
|
489
|
+
data: chunkItems,
|
|
490
|
+
nextCursor: encodeSdkCursor(
|
|
491
|
+
page.data.length - remainingItems.length,
|
|
492
|
+
rawCursor
|
|
493
|
+
)
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
bufferedPages = [
|
|
497
|
+
{
|
|
498
|
+
...page,
|
|
499
|
+
data: remainingItems
|
|
500
|
+
}
|
|
501
|
+
];
|
|
502
|
+
rawCursor = nextRawCursor;
|
|
503
|
+
}
|
|
504
|
+
if (bufferedPages.length > 0) {
|
|
505
|
+
const lastBufferedPage = bufferedPages.slice(-1)[0];
|
|
506
|
+
const bufferedItems = bufferedPages.map((p) => p.data).flat();
|
|
507
|
+
yield {
|
|
508
|
+
...lastBufferedPage,
|
|
509
|
+
data: bufferedItems
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
var paginate = paginateBuffered;
|
|
514
|
+
function concatPaginated({
|
|
515
|
+
sources,
|
|
516
|
+
dedupe,
|
|
517
|
+
pageSize = 100
|
|
518
|
+
}) {
|
|
519
|
+
if (sources.length === 0) {
|
|
520
|
+
const empty = { data: [] };
|
|
521
|
+
return Object.assign(Promise.resolve(empty), {
|
|
522
|
+
[Symbol.asyncIterator]: async function* () {
|
|
523
|
+
yield empty;
|
|
524
|
+
}
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
let sourceIndex = 0;
|
|
528
|
+
let currentIterator = null;
|
|
529
|
+
const seen = /* @__PURE__ */ new Set();
|
|
530
|
+
const pageFunction = async (_options) => {
|
|
531
|
+
while (sourceIndex < sources.length) {
|
|
532
|
+
if (!currentIterator) {
|
|
533
|
+
const result = sources[sourceIndex]();
|
|
534
|
+
currentIterator = result[Symbol.asyncIterator]();
|
|
535
|
+
}
|
|
536
|
+
const next = await currentIterator.next();
|
|
537
|
+
if (next.done) {
|
|
538
|
+
sourceIndex++;
|
|
539
|
+
currentIterator = null;
|
|
540
|
+
continue;
|
|
541
|
+
}
|
|
542
|
+
let items = next.value.data;
|
|
543
|
+
if (dedupe) {
|
|
544
|
+
if (sourceIndex > 0) {
|
|
545
|
+
items = items.filter((item) => !seen.has(dedupe(item)));
|
|
546
|
+
}
|
|
547
|
+
for (const item of items) {
|
|
548
|
+
seen.add(dedupe(item));
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
const hasMoreInSource = next.value.nextCursor != null;
|
|
552
|
+
const hasMoreSources = sourceIndex < sources.length - 1;
|
|
553
|
+
return {
|
|
554
|
+
data: items,
|
|
555
|
+
nextCursor: hasMoreInSource || hasMoreSources ? "__has_more__" : void 0
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
return { data: [] };
|
|
559
|
+
};
|
|
560
|
+
const iterator = paginateBuffered(pageFunction, { pageSize });
|
|
561
|
+
const firstPagePromise = iterator.next().then((result) => {
|
|
562
|
+
if (result.done) {
|
|
563
|
+
return { data: [] };
|
|
564
|
+
}
|
|
565
|
+
return result.value;
|
|
566
|
+
});
|
|
567
|
+
return Object.assign(firstPagePromise, {
|
|
568
|
+
[Symbol.asyncIterator]: async function* () {
|
|
569
|
+
yield await firstPagePromise;
|
|
570
|
+
for await (const page of { [Symbol.asyncIterator]: () => iterator }) {
|
|
571
|
+
yield page;
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
function toIterable(source) {
|
|
577
|
+
return { [Symbol.asyncIterator]: () => source[Symbol.asyncIterator]() };
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// src/utils/validation.ts
|
|
581
|
+
var parseOrThrow = (schema, input, { adaptError } = {}) => {
|
|
582
|
+
const result = schema.safeParse(input);
|
|
583
|
+
if (!result.success) {
|
|
584
|
+
const errorMessages = result.error.issues.map((issue) => {
|
|
585
|
+
const path = issue.path.length > 0 ? issue.path.join(".") : "input";
|
|
586
|
+
return `${path}: ${issue.message}`;
|
|
587
|
+
});
|
|
588
|
+
throw createCoreError(
|
|
589
|
+
{
|
|
590
|
+
code: CoreErrorCode.Validation,
|
|
591
|
+
message: `Validation failed:
|
|
592
|
+
${errorMessages.join("\n ")}`,
|
|
593
|
+
details: {
|
|
594
|
+
zodErrors: result.error.issues,
|
|
595
|
+
input
|
|
596
|
+
}
|
|
597
|
+
},
|
|
598
|
+
adaptError
|
|
599
|
+
);
|
|
600
|
+
}
|
|
601
|
+
return result.data;
|
|
602
|
+
};
|
|
603
|
+
function createValidator(schema, { adaptError } = {}) {
|
|
604
|
+
return function validateFn(input) {
|
|
605
|
+
return parseOrThrow(schema, input, { adaptError });
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
var validateOptions = (schema, options, { adaptError } = {}) => parseOrThrow(schema, options, { adaptError });
|
|
609
|
+
|
|
610
|
+
// src/utils/async-context.ts
|
|
611
|
+
var import_node_async_hooks = require("async_hooks");
|
|
612
|
+
function createAsyncContext() {
|
|
613
|
+
let store = null;
|
|
614
|
+
try {
|
|
615
|
+
store = new import_node_async_hooks.AsyncLocalStorage();
|
|
616
|
+
} catch {
|
|
617
|
+
store = null;
|
|
618
|
+
}
|
|
619
|
+
return {
|
|
620
|
+
available: store !== null,
|
|
621
|
+
run(value, fn) {
|
|
622
|
+
return store ? store.run(value, fn) : fn();
|
|
623
|
+
},
|
|
624
|
+
get() {
|
|
625
|
+
return store?.getStore();
|
|
626
|
+
}
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
// src/utils/method-scope.ts
|
|
631
|
+
var scope = createAsyncContext();
|
|
632
|
+
function getCurrentScope() {
|
|
633
|
+
return scope.get();
|
|
634
|
+
}
|
|
635
|
+
function getCurrentDepth() {
|
|
636
|
+
return getCurrentScope()?.depth ?? 0;
|
|
637
|
+
}
|
|
638
|
+
function isNestedMethodCall() {
|
|
639
|
+
if (!scope.available) return true;
|
|
640
|
+
const store = scope.get();
|
|
641
|
+
return store !== void 0 && store.depth > 0;
|
|
642
|
+
}
|
|
643
|
+
function runInMethodScope(fn) {
|
|
644
|
+
if (!scope.available) return fn();
|
|
645
|
+
const currentDepth = scope.get()?.depth ?? -1;
|
|
646
|
+
return scope.run({ depth: currentDepth + 1 }, fn);
|
|
647
|
+
}
|
|
648
|
+
var runWithTelemetryContext = runInMethodScope;
|
|
649
|
+
var isTelemetryNested = isNestedMethodCall;
|
|
650
|
+
|
|
651
|
+
// src/utils/function-utils.ts
|
|
652
|
+
function normalizeError(error, adaptError) {
|
|
653
|
+
if (error instanceof Error) return error;
|
|
654
|
+
const message = typeof error === "object" && error !== null && "message" in error && typeof error.message === "string" ? error.message : String(error);
|
|
655
|
+
return createCoreError(
|
|
656
|
+
{
|
|
657
|
+
code: CoreErrorCode.Unknown,
|
|
658
|
+
message,
|
|
659
|
+
cause: error
|
|
660
|
+
},
|
|
661
|
+
adaptError
|
|
662
|
+
);
|
|
663
|
+
}
|
|
664
|
+
function createFunction(coreFn, options) {
|
|
665
|
+
const { sdk, schema, name } = options;
|
|
666
|
+
const functionName = name || coreFn.name;
|
|
667
|
+
const namedFunctions = {
|
|
668
|
+
[functionName]: async function(callOptions) {
|
|
669
|
+
return runInMethodScope(async () => {
|
|
670
|
+
const startTime = Date.now();
|
|
671
|
+
const normalizedOptions = callOptions ?? {};
|
|
672
|
+
const args = [normalizedOptions];
|
|
673
|
+
const depth = getCurrentDepth();
|
|
674
|
+
const hooks = sdk.context.hooks;
|
|
675
|
+
const adaptError = sdk.context.core?.adaptError;
|
|
676
|
+
hooks?.onMethodStart?.({
|
|
677
|
+
methodName: functionName,
|
|
678
|
+
args,
|
|
679
|
+
isPaginated: false,
|
|
680
|
+
depth
|
|
681
|
+
});
|
|
682
|
+
try {
|
|
683
|
+
let result;
|
|
684
|
+
if (schema) {
|
|
685
|
+
const validatedOptions = validateOptions(
|
|
686
|
+
schema,
|
|
687
|
+
normalizedOptions,
|
|
688
|
+
{
|
|
689
|
+
adaptError
|
|
690
|
+
}
|
|
691
|
+
);
|
|
692
|
+
result = await coreFn({
|
|
693
|
+
...normalizedOptions,
|
|
694
|
+
...validatedOptions
|
|
695
|
+
});
|
|
696
|
+
} else {
|
|
697
|
+
result = await coreFn(normalizedOptions);
|
|
698
|
+
}
|
|
699
|
+
hooks?.onMethodEnd?.({
|
|
700
|
+
methodName: functionName,
|
|
701
|
+
args,
|
|
702
|
+
isPaginated: false,
|
|
703
|
+
depth,
|
|
704
|
+
durationMs: Date.now() - startTime
|
|
705
|
+
});
|
|
706
|
+
return result;
|
|
707
|
+
} catch (error) {
|
|
708
|
+
const normalizedError = normalizeError(error, adaptError);
|
|
709
|
+
hooks?.onMethodEnd?.({
|
|
710
|
+
methodName: functionName,
|
|
711
|
+
args,
|
|
712
|
+
isPaginated: false,
|
|
713
|
+
depth,
|
|
714
|
+
durationMs: Date.now() - startTime,
|
|
715
|
+
error: normalizedError
|
|
716
|
+
});
|
|
717
|
+
throw normalizedError;
|
|
718
|
+
}
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
};
|
|
722
|
+
return namedFunctions[functionName];
|
|
723
|
+
}
|
|
724
|
+
function isSdkPage(value) {
|
|
725
|
+
if (typeof value !== "object" || value === null) return false;
|
|
726
|
+
const page = value;
|
|
727
|
+
if (!Array.isArray(page.data)) return false;
|
|
728
|
+
if (page.nextCursor !== void 0 && typeof page.nextCursor !== "string") {
|
|
729
|
+
return false;
|
|
730
|
+
}
|
|
731
|
+
return Object.keys(page).every((k) => k === "data" || k === "nextCursor");
|
|
732
|
+
}
|
|
733
|
+
function createPageFunction(coreFn, {
|
|
734
|
+
sdk,
|
|
735
|
+
adaptPage
|
|
736
|
+
}) {
|
|
737
|
+
const functionName = coreFn.name + "Page";
|
|
738
|
+
const namedFunctions = {
|
|
739
|
+
[functionName]: async function(options) {
|
|
740
|
+
try {
|
|
741
|
+
const response = await coreFn(options);
|
|
742
|
+
const page = adaptPage ? adaptPage(response) : response;
|
|
743
|
+
if (!isSdkPage(page)) {
|
|
744
|
+
throw new Error(
|
|
745
|
+
`${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\`.`
|
|
746
|
+
);
|
|
747
|
+
}
|
|
748
|
+
return page;
|
|
749
|
+
} catch (error) {
|
|
750
|
+
throw normalizeError(
|
|
751
|
+
error,
|
|
752
|
+
sdk.context.core?.adaptError
|
|
753
|
+
);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
};
|
|
757
|
+
return namedFunctions[functionName];
|
|
758
|
+
}
|
|
759
|
+
function createPaginatedFunction(coreFn, options) {
|
|
760
|
+
const { sdk, schema, name, defaultPageSize, adaptPage } = options;
|
|
761
|
+
const pageFunction = createPageFunction(coreFn, { sdk, adaptPage });
|
|
762
|
+
const functionName = name || coreFn.name;
|
|
763
|
+
const namedFunctions = {
|
|
764
|
+
[functionName]: function(callOptions) {
|
|
765
|
+
return runInMethodScope(() => {
|
|
766
|
+
const startTime = Date.now();
|
|
767
|
+
const normalizedOptions = callOptions ?? {};
|
|
768
|
+
const args = [normalizedOptions];
|
|
769
|
+
const depth = getCurrentDepth();
|
|
770
|
+
const hooks = sdk.context.hooks;
|
|
771
|
+
const adaptError = sdk.context.core?.adaptError;
|
|
772
|
+
hooks?.onMethodStart?.({
|
|
773
|
+
methodName: functionName,
|
|
774
|
+
args,
|
|
775
|
+
isPaginated: true,
|
|
776
|
+
depth
|
|
777
|
+
});
|
|
778
|
+
const validatedOptions = {
|
|
779
|
+
...normalizedOptions,
|
|
780
|
+
...schema ? createValidator(schema, { adaptError })(normalizedOptions) : normalizedOptions
|
|
781
|
+
};
|
|
782
|
+
const pageSize = validatedOptions.pageSize ?? defaultPageSize;
|
|
783
|
+
const optimizedOptions = {
|
|
784
|
+
...validatedOptions,
|
|
785
|
+
pageSize
|
|
786
|
+
};
|
|
787
|
+
const iterator = paginate(pageFunction, optimizedOptions);
|
|
788
|
+
const firstPagePromise = iterator.next().then((result) => {
|
|
789
|
+
if (result.done) {
|
|
790
|
+
throw new Error("Paginate should always iterate at least once");
|
|
791
|
+
}
|
|
792
|
+
return result.value;
|
|
793
|
+
});
|
|
794
|
+
if (hooks?.onMethodEnd) {
|
|
795
|
+
firstPagePromise.then(() => {
|
|
796
|
+
hooks.onMethodEnd({
|
|
797
|
+
methodName: functionName,
|
|
798
|
+
args,
|
|
799
|
+
isPaginated: true,
|
|
800
|
+
depth,
|
|
801
|
+
durationMs: Date.now() - startTime
|
|
802
|
+
});
|
|
803
|
+
}).catch((error) => {
|
|
804
|
+
hooks.onMethodEnd({
|
|
805
|
+
methodName: functionName,
|
|
806
|
+
args,
|
|
807
|
+
isPaginated: true,
|
|
808
|
+
depth,
|
|
809
|
+
durationMs: Date.now() - startTime,
|
|
810
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
811
|
+
});
|
|
812
|
+
});
|
|
813
|
+
}
|
|
814
|
+
const pageStream = async function* () {
|
|
815
|
+
yield await firstPagePromise;
|
|
816
|
+
for await (const page of iterator) {
|
|
817
|
+
yield page;
|
|
818
|
+
}
|
|
819
|
+
}();
|
|
820
|
+
return Object.assign(firstPagePromise, {
|
|
821
|
+
[Symbol.asyncIterator]() {
|
|
822
|
+
return pageStream;
|
|
823
|
+
},
|
|
824
|
+
items: function() {
|
|
825
|
+
return {
|
|
826
|
+
[Symbol.asyncIterator]: async function* () {
|
|
827
|
+
for await (const page of pageStream) {
|
|
828
|
+
for (const item of page.data) {
|
|
829
|
+
yield item;
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
};
|
|
834
|
+
}
|
|
835
|
+
});
|
|
836
|
+
});
|
|
837
|
+
}
|
|
838
|
+
};
|
|
839
|
+
return namedFunctions[functionName];
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
// src/utils/plugin-utils.ts
|
|
843
|
+
function createPluginMethod(sdk, config) {
|
|
844
|
+
const { name, inputSchema, handler, ...metaFields } = config;
|
|
845
|
+
const namedHandlers = {
|
|
846
|
+
[name]: async function(options) {
|
|
847
|
+
return handler({ sdk, options });
|
|
848
|
+
}
|
|
849
|
+
};
|
|
850
|
+
const wrappedFn = createFunction(namedHandlers[name], {
|
|
851
|
+
sdk,
|
|
852
|
+
schema: inputSchema
|
|
853
|
+
});
|
|
854
|
+
return {
|
|
855
|
+
[name]: wrappedFn,
|
|
856
|
+
context: {
|
|
857
|
+
meta: {
|
|
858
|
+
[name]: {
|
|
859
|
+
...metaFields,
|
|
860
|
+
...inputSchema ? { inputSchema } : {}
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
};
|
|
865
|
+
}
|
|
866
|
+
function createPaginatedPluginMethod(sdk, config) {
|
|
867
|
+
const {
|
|
868
|
+
name,
|
|
869
|
+
inputSchema,
|
|
870
|
+
handler,
|
|
871
|
+
adaptPage,
|
|
872
|
+
defaultPageSize,
|
|
873
|
+
...metaFields
|
|
874
|
+
} = config;
|
|
875
|
+
const namedHandlers = {
|
|
876
|
+
[name]: function(options) {
|
|
877
|
+
return handler({ sdk, options });
|
|
878
|
+
}
|
|
879
|
+
};
|
|
880
|
+
const wrappedFn = createPaginatedFunction(namedHandlers[name], {
|
|
881
|
+
sdk,
|
|
882
|
+
schema: inputSchema,
|
|
883
|
+
name,
|
|
884
|
+
defaultPageSize,
|
|
885
|
+
adaptPage
|
|
886
|
+
});
|
|
887
|
+
return {
|
|
888
|
+
[name]: wrappedFn,
|
|
889
|
+
context: {
|
|
890
|
+
meta: {
|
|
891
|
+
[name]: {
|
|
892
|
+
...metaFields,
|
|
893
|
+
...inputSchema ? { inputSchema } : {}
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
};
|
|
898
|
+
}
|
|
899
|
+
function splitPluginContribution(result) {
|
|
900
|
+
const { context, ...rootKeys } = result;
|
|
901
|
+
const { meta, hooks, ...contextRest } = context ?? {};
|
|
902
|
+
return {
|
|
903
|
+
rootKeys,
|
|
904
|
+
meta: meta ?? {},
|
|
905
|
+
hooks: hooks ?? {},
|
|
906
|
+
contextRest
|
|
907
|
+
};
|
|
908
|
+
}
|
|
909
|
+
var RESERVED_ROOT_KEYS = /* @__PURE__ */ new Set([
|
|
910
|
+
"context",
|
|
911
|
+
"getRegistry"
|
|
912
|
+
]);
|
|
913
|
+
function hasOwn(obj, key2) {
|
|
914
|
+
return Object.prototype.hasOwnProperty.call(obj, key2);
|
|
915
|
+
}
|
|
916
|
+
function setOwn(target, key2, value) {
|
|
917
|
+
Object.defineProperty(target, key2, {
|
|
918
|
+
value,
|
|
919
|
+
enumerable: true,
|
|
920
|
+
configurable: true,
|
|
921
|
+
writable: true
|
|
922
|
+
});
|
|
923
|
+
}
|
|
924
|
+
function checkCollisions(target, source, kind, callerLabel, override) {
|
|
925
|
+
if (kind === "root key") {
|
|
926
|
+
checkRootKeyCollisions(target, Object.keys(source), override, callerLabel);
|
|
927
|
+
return;
|
|
928
|
+
}
|
|
929
|
+
for (const key2 of Object.keys(source)) {
|
|
930
|
+
if (!override && hasOwn(target, key2)) {
|
|
931
|
+
throw new Error(
|
|
932
|
+
`${callerLabel}: duplicate ${kind} "${key2}". If the override is intentional, pass { override: true } in the options.`
|
|
933
|
+
);
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
function checkRootKeyCollisions(target, keys, override, callerLabel) {
|
|
938
|
+
for (const key2 of keys) {
|
|
939
|
+
if (RESERVED_ROOT_KEYS.has(key2)) {
|
|
940
|
+
throw new Error(
|
|
941
|
+
`${callerLabel}: plugin attempted to register reserved root key "${key2}". The SDK uses this key for its own accessor; rename the plugin's method.`
|
|
942
|
+
);
|
|
943
|
+
}
|
|
944
|
+
if (!override && hasOwn(target, key2)) {
|
|
945
|
+
throw new Error(
|
|
946
|
+
`${callerLabel}: duplicate root key "${key2}". If the override is intentional, pass { override: true } in the options.`
|
|
947
|
+
);
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
function applyOwnProperties(target, source) {
|
|
952
|
+
for (const key2 of Object.keys(source)) {
|
|
953
|
+
setOwn(target, key2, source[key2]);
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
function createPluginAccumulator(initialProperties = {}, initialContext = {}) {
|
|
957
|
+
const initialMeta = initialContext.meta ?? {};
|
|
958
|
+
const initialHooks = initialContext.hooks ?? {};
|
|
959
|
+
const context = {
|
|
960
|
+
...initialContext,
|
|
961
|
+
meta: { ...initialMeta },
|
|
962
|
+
hooks: { ...initialHooks }
|
|
963
|
+
};
|
|
964
|
+
const view = { ...initialProperties, context };
|
|
965
|
+
return { view, context };
|
|
966
|
+
}
|
|
967
|
+
function mergeContribution(propertiesTarget, contextTarget, contribution, options) {
|
|
968
|
+
checkCollisions(
|
|
969
|
+
propertiesTarget,
|
|
970
|
+
contribution.rootKeys,
|
|
971
|
+
"root key",
|
|
972
|
+
options.callerLabel,
|
|
973
|
+
options.override
|
|
974
|
+
);
|
|
975
|
+
checkCollisions(
|
|
976
|
+
contextTarget.meta,
|
|
977
|
+
contribution.meta,
|
|
978
|
+
"context.meta key",
|
|
979
|
+
options.callerLabel,
|
|
980
|
+
options.override
|
|
981
|
+
);
|
|
982
|
+
checkCollisions(
|
|
983
|
+
contextTarget,
|
|
984
|
+
contribution.contextRest,
|
|
985
|
+
"context key",
|
|
986
|
+
options.callerLabel,
|
|
987
|
+
options.override
|
|
988
|
+
);
|
|
989
|
+
applyOwnProperties(propertiesTarget, contribution.rootKeys);
|
|
990
|
+
applyOwnProperties(contextTarget.meta, contribution.meta);
|
|
991
|
+
applyOwnProperties(contextTarget, contribution.contextRest);
|
|
992
|
+
contextTarget.hooks = buildHooks(contextTarget.hooks, contribution.hooks);
|
|
993
|
+
}
|
|
994
|
+
function applyPluginContribution(acc, contribution, options) {
|
|
995
|
+
mergeContribution(acc.view, acc.context, contribution, options);
|
|
996
|
+
}
|
|
997
|
+
function wrapAsSdk(properties, context) {
|
|
998
|
+
const sdk = {
|
|
999
|
+
...properties,
|
|
1000
|
+
context,
|
|
1001
|
+
getRegistry(qopts) {
|
|
1002
|
+
return buildRegistry({
|
|
1003
|
+
sdk,
|
|
1004
|
+
meta: context.meta,
|
|
1005
|
+
packageFilter: qopts?.package
|
|
1006
|
+
});
|
|
1007
|
+
}
|
|
1008
|
+
};
|
|
1009
|
+
return sdk;
|
|
1010
|
+
}
|
|
1011
|
+
function wrapAccumulatorAsSdk(acc) {
|
|
1012
|
+
const { context: _ctx, ...rootKeys } = acc.view;
|
|
1013
|
+
return wrapAsSdk(
|
|
1014
|
+
rootKeys,
|
|
1015
|
+
acc.context
|
|
1016
|
+
);
|
|
1017
|
+
}
|
|
1018
|
+
function applyPluginToSdk(sdk, plugin, options) {
|
|
1019
|
+
const context = sdk.context;
|
|
1020
|
+
const contribution = splitPluginContribution(
|
|
1021
|
+
plugin(sdk)
|
|
1022
|
+
);
|
|
1023
|
+
mergeContribution(sdk, context, contribution, {
|
|
1024
|
+
callerLabel: "addPlugin",
|
|
1025
|
+
override: options.override === true
|
|
1026
|
+
});
|
|
1027
|
+
return contribution;
|
|
1028
|
+
}
|
|
1029
|
+
function resolveStack(head) {
|
|
1030
|
+
const entries = [];
|
|
1031
|
+
let node = head;
|
|
1032
|
+
while (node) {
|
|
1033
|
+
entries.unshift({ apply: node.entry, override: node.override });
|
|
1034
|
+
node = node.prev;
|
|
1035
|
+
}
|
|
1036
|
+
return entries;
|
|
1037
|
+
}
|
|
1038
|
+
function composeStackHooks(hooks) {
|
|
1039
|
+
let composed = {};
|
|
1040
|
+
for (const h of hooks) composed = buildHooks(composed, h);
|
|
1041
|
+
return composed;
|
|
1042
|
+
}
|
|
1043
|
+
function collapseStackEntries(entries, callerLabel) {
|
|
1044
|
+
return (outerSdk) => {
|
|
1045
|
+
const { context: outerContext, ...outerProperties } = outerSdk ?? {};
|
|
1046
|
+
const viewAcc = createPluginAccumulator(outerProperties, outerContext);
|
|
1047
|
+
const contribsAcc = createPluginAccumulator();
|
|
1048
|
+
const hooks = [];
|
|
1049
|
+
for (const { apply, override } of entries) {
|
|
1050
|
+
const contribution = splitPluginContribution(
|
|
1051
|
+
apply(viewAcc.view)
|
|
1052
|
+
);
|
|
1053
|
+
const hookless = { ...contribution, hooks: {} };
|
|
1054
|
+
applyPluginContribution(viewAcc, hookless, { callerLabel, override });
|
|
1055
|
+
applyPluginContribution(contribsAcc, hookless, { callerLabel, override });
|
|
1056
|
+
hooks.push(contribution.hooks);
|
|
1057
|
+
}
|
|
1058
|
+
const stackHooks = composeStackHooks(hooks);
|
|
1059
|
+
viewAcc.context.hooks = buildHooks(viewAcc.context.hooks, stackHooks);
|
|
1060
|
+
contribsAcc.context.hooks = stackHooks;
|
|
1061
|
+
const { context: _ignored, ...contributedRoot } = contribsAcc.view;
|
|
1062
|
+
return {
|
|
1063
|
+
...contributedRoot,
|
|
1064
|
+
context: contribsAcc.context
|
|
1065
|
+
};
|
|
1066
|
+
};
|
|
1067
|
+
}
|
|
1068
|
+
function buildStackAccumulator(head, callerLabel) {
|
|
1069
|
+
const entries = resolveStack(head);
|
|
1070
|
+
const acc = createPluginAccumulator();
|
|
1071
|
+
const hooks = [];
|
|
1072
|
+
for (const { apply, override } of entries) {
|
|
1073
|
+
const contribution = splitPluginContribution(
|
|
1074
|
+
apply(acc.view)
|
|
1075
|
+
);
|
|
1076
|
+
applyPluginContribution(
|
|
1077
|
+
acc,
|
|
1078
|
+
{ ...contribution, hooks: {} },
|
|
1079
|
+
{ callerLabel, override }
|
|
1080
|
+
);
|
|
1081
|
+
hooks.push(contribution.hooks);
|
|
1082
|
+
}
|
|
1083
|
+
acc.context.hooks = composeStackHooks(hooks);
|
|
1084
|
+
return acc;
|
|
1085
|
+
}
|
|
1086
|
+
function composePlugins(...plugins) {
|
|
1087
|
+
logDeprecation(
|
|
1088
|
+
"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."
|
|
1089
|
+
);
|
|
1090
|
+
let head = null;
|
|
1091
|
+
for (const plugin of plugins) {
|
|
1092
|
+
head = { entry: plugin, override: false, prev: head };
|
|
1093
|
+
}
|
|
1094
|
+
const entries = resolveStack(head);
|
|
1095
|
+
return collapseStackEntries(entries, "composePlugins");
|
|
1096
|
+
}
|
|
1097
|
+
function createPluginStack() {
|
|
1098
|
+
return buildPluginStack(null, "createPluginStack");
|
|
1099
|
+
}
|
|
1100
|
+
function buildPluginStack(head, callerLabel) {
|
|
1101
|
+
const stack = {
|
|
1102
|
+
use(plugin, options) {
|
|
1103
|
+
const next = {
|
|
1104
|
+
entry: plugin,
|
|
1105
|
+
override: options?.override === true,
|
|
1106
|
+
prev: head
|
|
1107
|
+
};
|
|
1108
|
+
return buildPluginStack(next, callerLabel);
|
|
1109
|
+
},
|
|
1110
|
+
toPlugin() {
|
|
1111
|
+
const entries = resolveStack(head);
|
|
1112
|
+
return collapseStackEntries(entries, callerLabel);
|
|
1113
|
+
},
|
|
1114
|
+
toSdk() {
|
|
1115
|
+
return wrapAccumulatorAsSdk(
|
|
1116
|
+
buildStackAccumulator(head, callerLabel)
|
|
1117
|
+
);
|
|
1118
|
+
}
|
|
1119
|
+
};
|
|
1120
|
+
return stack;
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
// src/model/shared.ts
|
|
1124
|
+
function parseId(id) {
|
|
1125
|
+
const at = id.lastIndexOf("/");
|
|
1126
|
+
return at === -1 ? { name: id, namespace: void 0 } : { name: id.slice(at + 1), namespace: id.slice(0, at) };
|
|
1127
|
+
}
|
|
1128
|
+
function makeId(name, namespace, kind = "leaf") {
|
|
1129
|
+
validateName(name, kind);
|
|
1130
|
+
if (namespace !== void 0) validateNamespace(namespace);
|
|
1131
|
+
return namespace ? `${namespace}/${name}` : name;
|
|
1132
|
+
}
|
|
1133
|
+
var NAME_RE = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
1134
|
+
var SEGMENT_RE = /^@?[A-Za-z0-9._-]+$/;
|
|
1135
|
+
function validateName(name, kind) {
|
|
1136
|
+
if (name === "") throw new Error("Plugin name must not be empty.");
|
|
1137
|
+
if (kind === "leaf") {
|
|
1138
|
+
if (!NAME_RE.test(name)) {
|
|
1139
|
+
throw new Error(
|
|
1140
|
+
`Plugin name "${name}" must be a valid JS identifier (it is the binding name).`
|
|
1141
|
+
);
|
|
1142
|
+
}
|
|
1143
|
+
} else if (!SEGMENT_RE.test(name)) {
|
|
1144
|
+
throw new Error(
|
|
1145
|
+
`Plugin name "${name}" must be package-like (letters, digits, ".", "_", "-", optional leading "@") with no "/".`
|
|
1146
|
+
);
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
function validateNamespace(namespace) {
|
|
1150
|
+
if (namespace === "") throw new Error("Plugin namespace must not be empty.");
|
|
1151
|
+
for (const segment of namespace.split("/")) {
|
|
1152
|
+
if (!SEGMENT_RE.test(segment)) {
|
|
1153
|
+
throw new Error(
|
|
1154
|
+
`Plugin namespace "${namespace}" is invalid: each "/"-separated segment must be package-like (letters, digits, ".", "_", "-", optional leading "@").`
|
|
1155
|
+
);
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
// src/model/types.ts
|
|
1161
|
+
var LEAF_META_KEYS = [
|
|
1162
|
+
"description",
|
|
1163
|
+
"categories",
|
|
1164
|
+
"type",
|
|
1165
|
+
"itemType",
|
|
1166
|
+
"returnType",
|
|
1167
|
+
"outputSchema",
|
|
1168
|
+
"inputParameters",
|
|
1169
|
+
"packages",
|
|
1170
|
+
"experimental",
|
|
1171
|
+
"confirm",
|
|
1172
|
+
"deprecation",
|
|
1173
|
+
"aliases",
|
|
1174
|
+
"supportsJsonOutput"
|
|
1175
|
+
];
|
|
1176
|
+
|
|
1177
|
+
// src/model/define.ts
|
|
1178
|
+
function normalizeImports(deps) {
|
|
1179
|
+
if (!deps) return { plugins: [], bindings: [] };
|
|
1180
|
+
const seen = /* @__PURE__ */ new Map();
|
|
1181
|
+
const bindings = [];
|
|
1182
|
+
const add = (binding, id) => {
|
|
1183
|
+
const priorId = seen.get(binding);
|
|
1184
|
+
if (priorId !== void 0 && priorId !== id) {
|
|
1185
|
+
throw new Error(
|
|
1186
|
+
`Import binding "${binding}" is declared twice. Two different plugins ("${priorId}" and "${id}") bind the same name; wrap one in selectExports to rename it.`
|
|
1187
|
+
);
|
|
1188
|
+
}
|
|
1189
|
+
if (priorId === void 0) {
|
|
1190
|
+
seen.set(binding, id);
|
|
1191
|
+
bindings.push({ binding, id });
|
|
1192
|
+
}
|
|
1193
|
+
};
|
|
1194
|
+
for (const plugin of deps) {
|
|
1195
|
+
if (plugin.pluginType === "aggregate") {
|
|
1196
|
+
for (const [binding, child] of Object.entries(plugin.exports)) {
|
|
1197
|
+
add(binding, child.id);
|
|
1198
|
+
}
|
|
1199
|
+
} else {
|
|
1200
|
+
add(plugin.name, plugin.id);
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
return { plugins: deps, bindings };
|
|
1204
|
+
}
|
|
1205
|
+
function collectLeafMeta(config) {
|
|
1206
|
+
let meta;
|
|
1207
|
+
for (const key2 of LEAF_META_KEYS) {
|
|
1208
|
+
if (config[key2] !== void 0) (meta ?? (meta = {}))[key2] = config[key2];
|
|
1209
|
+
}
|
|
1210
|
+
return meta;
|
|
1211
|
+
}
|
|
1212
|
+
function defineMethod(config) {
|
|
1213
|
+
const deps = normalizeImports(config.imports);
|
|
1214
|
+
return {
|
|
1215
|
+
pluginType: "method",
|
|
1216
|
+
name: config.name,
|
|
1217
|
+
namespace: config.namespace,
|
|
1218
|
+
id: makeId(config.name, config.namespace),
|
|
1219
|
+
imports: deps.plugins,
|
|
1220
|
+
importBindings: deps.bindings,
|
|
1221
|
+
inputSchema: config.inputSchema,
|
|
1222
|
+
meta: collectLeafMeta(config),
|
|
1223
|
+
resolvers: config.resolvers,
|
|
1224
|
+
formatter: config.formatter,
|
|
1225
|
+
output: config.output,
|
|
1226
|
+
positional: config.positional,
|
|
1227
|
+
setup: config.setup,
|
|
1228
|
+
run: config.run
|
|
1229
|
+
};
|
|
1230
|
+
}
|
|
1231
|
+
function defineResolver(config) {
|
|
1232
|
+
const deps = normalizeImports(config.imports);
|
|
1233
|
+
const base = { imports: deps.plugins, importBindings: deps.bindings };
|
|
1234
|
+
const gates = {
|
|
1235
|
+
requireParameters: config.requireParameters
|
|
1236
|
+
};
|
|
1237
|
+
switch (config.type) {
|
|
1238
|
+
case "static":
|
|
1239
|
+
return {
|
|
1240
|
+
...base,
|
|
1241
|
+
...gates,
|
|
1242
|
+
type: "static",
|
|
1243
|
+
inputType: config.inputType,
|
|
1244
|
+
placeholder: config.placeholder
|
|
1245
|
+
};
|
|
1246
|
+
case "constant":
|
|
1247
|
+
return { ...base, ...gates, type: "constant", value: config.value };
|
|
1248
|
+
case "info":
|
|
1249
|
+
return { ...base, type: "info", text: config.text ?? "" };
|
|
1250
|
+
case "object":
|
|
1251
|
+
return {
|
|
1252
|
+
...base,
|
|
1253
|
+
...gates,
|
|
1254
|
+
type: "object",
|
|
1255
|
+
properties: config.properties,
|
|
1256
|
+
definitions: config.definitions,
|
|
1257
|
+
getProperties: config.getProperties
|
|
1258
|
+
};
|
|
1259
|
+
case "array":
|
|
1260
|
+
return {
|
|
1261
|
+
...base,
|
|
1262
|
+
...gates,
|
|
1263
|
+
type: "array",
|
|
1264
|
+
items: config.items,
|
|
1265
|
+
minItems: config.minItems,
|
|
1266
|
+
maxItems: config.maxItems,
|
|
1267
|
+
itemValueType: config.itemValueType,
|
|
1268
|
+
definitions: config.definitions
|
|
1269
|
+
};
|
|
1270
|
+
default:
|
|
1271
|
+
return {
|
|
1272
|
+
...base,
|
|
1273
|
+
...gates,
|
|
1274
|
+
type: "dynamic",
|
|
1275
|
+
inputType: config.inputType,
|
|
1276
|
+
placeholder: config.placeholder,
|
|
1277
|
+
getContext: config.getContext,
|
|
1278
|
+
listItems: config.listItems,
|
|
1279
|
+
prompt: config.prompt,
|
|
1280
|
+
tryResolveWithoutPrompt: config.tryResolveWithoutPrompt,
|
|
1281
|
+
tryResolveFromSearch: config.tryResolveFromSearch
|
|
1282
|
+
};
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
function defineFormatter(config) {
|
|
1286
|
+
const deps = normalizeImports(config.imports);
|
|
1287
|
+
return {
|
|
1288
|
+
imports: deps.plugins,
|
|
1289
|
+
importBindings: deps.bindings,
|
|
1290
|
+
getContext: config.getContext,
|
|
1291
|
+
format: config.format
|
|
1292
|
+
};
|
|
1293
|
+
}
|
|
1294
|
+
function declareMethod(config) {
|
|
1295
|
+
const { name, namespace } = parseId(config.id);
|
|
1296
|
+
const id = makeId(name, namespace);
|
|
1297
|
+
return {
|
|
1298
|
+
pluginType: "method",
|
|
1299
|
+
name,
|
|
1300
|
+
namespace,
|
|
1301
|
+
id,
|
|
1302
|
+
standIn: true,
|
|
1303
|
+
imports: [],
|
|
1304
|
+
importBindings: [],
|
|
1305
|
+
run: () => {
|
|
1306
|
+
throw new Error(
|
|
1307
|
+
`Plugin "${id}" is a stand-in (declareMethod) with no implementation. Register the real plugin under this id.`
|
|
1308
|
+
);
|
|
1309
|
+
}
|
|
1310
|
+
};
|
|
1311
|
+
}
|
|
1312
|
+
function defineProperty(config) {
|
|
1313
|
+
const deps = normalizeImports(config.imports);
|
|
1314
|
+
return {
|
|
1315
|
+
pluginType: "property",
|
|
1316
|
+
name: config.name,
|
|
1317
|
+
namespace: config.namespace,
|
|
1318
|
+
id: makeId(config.name, config.namespace),
|
|
1319
|
+
imports: deps.plugins,
|
|
1320
|
+
importBindings: deps.bindings,
|
|
1321
|
+
setup: config.setup,
|
|
1322
|
+
value: config.value,
|
|
1323
|
+
get: config.get,
|
|
1324
|
+
meta: collectLeafMeta(config)
|
|
1325
|
+
};
|
|
1326
|
+
}
|
|
1327
|
+
function declareProperty(config) {
|
|
1328
|
+
const { name, namespace } = parseId(config.id);
|
|
1329
|
+
return {
|
|
1330
|
+
pluginType: "property",
|
|
1331
|
+
name,
|
|
1332
|
+
namespace,
|
|
1333
|
+
id: makeId(name, namespace),
|
|
1334
|
+
standIn: true,
|
|
1335
|
+
imports: [],
|
|
1336
|
+
importBindings: []
|
|
1337
|
+
};
|
|
1338
|
+
}
|
|
1339
|
+
function declarePlugin(config) {
|
|
1340
|
+
const { name, namespace } = parseId(config.id);
|
|
1341
|
+
return {
|
|
1342
|
+
pluginType: "aggregate",
|
|
1343
|
+
name,
|
|
1344
|
+
namespace,
|
|
1345
|
+
id: makeId(name, namespace, "aggregate"),
|
|
1346
|
+
standIn: true,
|
|
1347
|
+
imports: [],
|
|
1348
|
+
importBindings: [],
|
|
1349
|
+
exports: normalizeExports(config.exports)
|
|
1350
|
+
};
|
|
1351
|
+
}
|
|
1352
|
+
function definePlugin(fnOrConfig) {
|
|
1353
|
+
if (typeof fnOrConfig === "function") return fnOrConfig;
|
|
1354
|
+
const config = fnOrConfig;
|
|
1355
|
+
const deps = normalizeImports(config.imports);
|
|
1356
|
+
return {
|
|
1357
|
+
pluginType: "aggregate",
|
|
1358
|
+
name: config.name,
|
|
1359
|
+
namespace: config.namespace,
|
|
1360
|
+
id: makeId(config.name, config.namespace, "aggregate"),
|
|
1361
|
+
imports: deps.plugins,
|
|
1362
|
+
importBindings: deps.bindings,
|
|
1363
|
+
exports: normalizeExports(config.exports),
|
|
1364
|
+
middleware: config.middleware
|
|
1365
|
+
};
|
|
1366
|
+
}
|
|
1367
|
+
function normalizeExports(exports2) {
|
|
1368
|
+
if (!exports2) return {};
|
|
1369
|
+
const out = {};
|
|
1370
|
+
const add = (binding, leaf) => {
|
|
1371
|
+
const existing = out[binding];
|
|
1372
|
+
if (existing && existing.id !== leaf.id) {
|
|
1373
|
+
throw new Error(
|
|
1374
|
+
`definePlugin: duplicate export binding "${binding}". Two different plugins ("${existing.id}" and "${leaf.id}") bind the same name; wrap one in selectExports to rename it.`
|
|
1375
|
+
);
|
|
1376
|
+
}
|
|
1377
|
+
out[binding] = leaf;
|
|
1378
|
+
};
|
|
1379
|
+
for (const element of exports2) {
|
|
1380
|
+
if (element.pluginType === "aggregate") {
|
|
1381
|
+
for (const [binding, child] of Object.entries(element.exports)) {
|
|
1382
|
+
add(binding, child);
|
|
1383
|
+
}
|
|
1384
|
+
} else {
|
|
1385
|
+
add(element.name, element);
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
return out;
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
// src/model/exports.ts
|
|
1392
|
+
var selectSeq = 0;
|
|
1393
|
+
function selectExports(source, ...specs) {
|
|
1394
|
+
const selected = {};
|
|
1395
|
+
const pick = (binding, fromName) => {
|
|
1396
|
+
const child = source.exports[fromName];
|
|
1397
|
+
if (!child) {
|
|
1398
|
+
throw new Error(
|
|
1399
|
+
`selectExports: "${source.id}" has no export "${fromName}".`
|
|
1400
|
+
);
|
|
1401
|
+
}
|
|
1402
|
+
selected[binding] = child;
|
|
1403
|
+
};
|
|
1404
|
+
for (const spec of specs) {
|
|
1405
|
+
if (typeof spec === "string") {
|
|
1406
|
+
pick(spec, spec);
|
|
1407
|
+
} else {
|
|
1408
|
+
for (const [newName, fromName] of Object.entries(spec)) {
|
|
1409
|
+
pick(newName, fromName);
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
const id = `${source.id}#select:${selectSeq++}`;
|
|
1414
|
+
return {
|
|
1415
|
+
pluginType: "aggregate",
|
|
1416
|
+
name: makeId(`select`, source.name, "aggregate"),
|
|
1417
|
+
id,
|
|
1418
|
+
// Depend on the source so it is materialized; the selected bindings resolve
|
|
1419
|
+
// to the source's own leaves (kept identity).
|
|
1420
|
+
imports: [source],
|
|
1421
|
+
importBindings: [],
|
|
1422
|
+
exports: selected
|
|
1423
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1424
|
+
};
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
// src/model/legacy.ts
|
|
1428
|
+
function fromFunctionPlugin(fn, config) {
|
|
1429
|
+
return {
|
|
1430
|
+
pluginType: "legacy",
|
|
1431
|
+
name: config.name,
|
|
1432
|
+
namespace: config.namespace,
|
|
1433
|
+
id: makeId(config.name, config.namespace, "aggregate"),
|
|
1434
|
+
imports: [],
|
|
1435
|
+
importBindings: [],
|
|
1436
|
+
run: fn
|
|
1437
|
+
};
|
|
1438
|
+
}
|
|
1439
|
+
function defineLegacyMerge(args) {
|
|
1440
|
+
return {
|
|
1441
|
+
pluginType: "legacy-merge",
|
|
1442
|
+
name: args.name,
|
|
1443
|
+
namespace: args.namespace,
|
|
1444
|
+
id: makeId(args.name, args.namespace, "aggregate"),
|
|
1445
|
+
legacy: fromFunctionPlugin(args.legacy, {
|
|
1446
|
+
name: args.name,
|
|
1447
|
+
namespace: args.namespace
|
|
1448
|
+
}),
|
|
1449
|
+
plugin: args.plugin
|
|
1450
|
+
};
|
|
1451
|
+
}
|
|
1452
|
+
function legacyGraphEntry(name, value, pluginMeta) {
|
|
1453
|
+
const { inputSchema, ...rest } = pluginMeta ?? {};
|
|
1454
|
+
const meta = Object.keys(rest).length ? rest : void 0;
|
|
1455
|
+
if (typeof value === "function") {
|
|
1456
|
+
return {
|
|
1457
|
+
pluginType: "method",
|
|
1458
|
+
name,
|
|
1459
|
+
value,
|
|
1460
|
+
chain: [],
|
|
1461
|
+
...inputSchema ? { inputSchema } : {},
|
|
1462
|
+
...meta ? { meta } : {}
|
|
1463
|
+
};
|
|
1464
|
+
}
|
|
1465
|
+
return { pluginType: "property", name, value, ...meta ? { meta } : {} };
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
// src/model/builtins.ts
|
|
1469
|
+
var import_zod2 = require("zod");
|
|
1470
|
+
|
|
1471
|
+
// src/model/registry-support.ts
|
|
1472
|
+
function adaptLegacyFormatter(legacy, sdk) {
|
|
1473
|
+
const legacyFetch = legacy.fetch;
|
|
1474
|
+
return {
|
|
1475
|
+
getContext: legacyFetch ? async ({ items, input, context }) => {
|
|
1476
|
+
let ctx = context;
|
|
1477
|
+
for (const item of items) {
|
|
1478
|
+
ctx = await legacyFetch(sdk, input, item, ctx);
|
|
1479
|
+
}
|
|
1480
|
+
return ctx;
|
|
1481
|
+
} : void 0,
|
|
1482
|
+
format: ({ item, context }) => legacy.format(item, context)
|
|
1483
|
+
};
|
|
1484
|
+
}
|
|
1485
|
+
function normalizeFormatter(entry, sdk) {
|
|
1486
|
+
if (entry.pluginType !== "method") return void 0;
|
|
1487
|
+
if (entry.formatter) return entry.formatter;
|
|
1488
|
+
const legacy = entry.meta?.formatter;
|
|
1489
|
+
return legacy ? adaptLegacyFormatter(legacy, sdk) : void 0;
|
|
1490
|
+
}
|
|
1491
|
+
function normalizeBoundResolvers(entry) {
|
|
1492
|
+
if (entry.pluginType !== "method") return void 0;
|
|
1493
|
+
return entry.resolvers;
|
|
1494
|
+
}
|
|
1495
|
+
function methodPositional(entry) {
|
|
1496
|
+
if (entry.pluginType !== "method") return void 0;
|
|
1497
|
+
return entry.positional;
|
|
1498
|
+
}
|
|
1499
|
+
function pluginEntryMeta(entry) {
|
|
1500
|
+
if (entry.pluginType === "method" && entry.meta) {
|
|
1501
|
+
return entry.inputSchema ? { ...entry.meta, inputSchema: entry.inputSchema } : entry.meta;
|
|
1502
|
+
}
|
|
1503
|
+
if (entry.pluginType === "property" && entry.meta) return entry.meta;
|
|
1504
|
+
return void 0;
|
|
1505
|
+
}
|
|
1506
|
+
function buildSurfaceRegistry(context, packageFilter) {
|
|
1507
|
+
const meta = {};
|
|
1508
|
+
const surface = {};
|
|
1509
|
+
const entries = {};
|
|
1510
|
+
for (const [binding, id] of Object.entries(context.surface)) {
|
|
1511
|
+
const entry = context.plugins[id];
|
|
1512
|
+
if (!entry || entry.pluginType === "aggregate") continue;
|
|
1513
|
+
surface[binding] = entry.value;
|
|
1514
|
+
entries[binding] = entry;
|
|
1515
|
+
const m = pluginEntryMeta(entry);
|
|
1516
|
+
if (m) meta[binding] = m;
|
|
1517
|
+
}
|
|
1518
|
+
const formatters = {};
|
|
1519
|
+
const boundResolvers = {};
|
|
1520
|
+
const positional = {};
|
|
1521
|
+
for (const [binding, entry] of Object.entries(entries)) {
|
|
1522
|
+
const f = normalizeFormatter(entry, surface);
|
|
1523
|
+
if (f) formatters[binding] = f;
|
|
1524
|
+
const r = normalizeBoundResolvers(entry);
|
|
1525
|
+
if (r) boundResolvers[binding] = r;
|
|
1526
|
+
const p = methodPositional(entry);
|
|
1527
|
+
if (p) positional[binding] = p;
|
|
1528
|
+
}
|
|
1529
|
+
return buildRegistry({
|
|
1530
|
+
sdk: surface,
|
|
1531
|
+
meta,
|
|
1532
|
+
formatters,
|
|
1533
|
+
boundResolvers,
|
|
1534
|
+
positional,
|
|
1535
|
+
packageFilter
|
|
1536
|
+
});
|
|
1537
|
+
}
|
|
1538
|
+
|
|
1539
|
+
// src/model/builtins.ts
|
|
1540
|
+
var dangerousContextPlugin = {
|
|
1541
|
+
pluginType: "property",
|
|
1542
|
+
name: "context",
|
|
1543
|
+
namespace: "kitcore",
|
|
1544
|
+
id: "kitcore/context",
|
|
1545
|
+
imports: [],
|
|
1546
|
+
importBindings: [],
|
|
1547
|
+
privileged: true
|
|
1548
|
+
};
|
|
1549
|
+
var getRegistryPlugin = defineMethod({
|
|
1550
|
+
name: "getRegistry",
|
|
1551
|
+
namespace: "kitcore",
|
|
1552
|
+
imports: [dangerousContextPlugin],
|
|
1553
|
+
inputSchema: import_zod2.z.object({ package: import_zod2.z.string().optional() }).optional(),
|
|
1554
|
+
run: ({ imports, input }) => buildSurfaceRegistry(imports.context, input?.package)
|
|
1555
|
+
});
|
|
1556
|
+
|
|
1557
|
+
// src/model/materialize.ts
|
|
1558
|
+
function normalizeOutput(output) {
|
|
1559
|
+
if (output === void 0) return { type: "raw" };
|
|
1560
|
+
if (typeof output === "string") return { type: output };
|
|
1561
|
+
return output;
|
|
1562
|
+
}
|
|
1563
|
+
var CONTEXT = Symbol("kitcore.context");
|
|
1564
|
+
function getContext(sdk) {
|
|
1565
|
+
return sdk[CONTEXT];
|
|
1566
|
+
}
|
|
1567
|
+
function isResolverRef(value) {
|
|
1568
|
+
return "ref" in value;
|
|
1569
|
+
}
|
|
1570
|
+
function nestedResolvers(resolver) {
|
|
1571
|
+
const out = [];
|
|
1572
|
+
if (resolver.type === "object") {
|
|
1573
|
+
for (const field of Object.values(resolver.properties ?? {})) {
|
|
1574
|
+
if (!isResolverRef(field.resolver)) out.push(field.resolver);
|
|
1575
|
+
}
|
|
1576
|
+
out.push(...Object.values(resolver.definitions ?? {}));
|
|
1577
|
+
} else if (resolver.type === "array") {
|
|
1578
|
+
if (!isResolverRef(resolver.items)) out.push(resolver.items);
|
|
1579
|
+
out.push(...Object.values(resolver.definitions ?? {}));
|
|
1580
|
+
}
|
|
1581
|
+
return out;
|
|
1582
|
+
}
|
|
1583
|
+
function resolverImportEdges(resolver) {
|
|
1584
|
+
const out = [...resolver.imports];
|
|
1585
|
+
for (const child of nestedResolvers(resolver)) {
|
|
1586
|
+
out.push(...resolverImportEdges(child));
|
|
1587
|
+
}
|
|
1588
|
+
return out;
|
|
1589
|
+
}
|
|
1590
|
+
function methodAttachmentEdges(plugin) {
|
|
1591
|
+
const out = [];
|
|
1592
|
+
if (plugin.resolvers) {
|
|
1593
|
+
for (const resolver of Object.values(plugin.resolvers)) {
|
|
1594
|
+
out.push(...resolverImportEdges(resolver));
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
if (plugin.formatter) out.push(...plugin.formatter.imports);
|
|
1598
|
+
return out;
|
|
1599
|
+
}
|
|
1600
|
+
function edgesOf(plugin) {
|
|
1601
|
+
if (plugin.pluginType === "aggregate") {
|
|
1602
|
+
return [...Object.values(plugin.exports), ...plugin.imports];
|
|
1603
|
+
}
|
|
1604
|
+
if (plugin.pluginType === "method") {
|
|
1605
|
+
return [...plugin.imports, ...methodAttachmentEdges(plugin)];
|
|
1606
|
+
}
|
|
1607
|
+
return plugin.imports;
|
|
1608
|
+
}
|
|
1609
|
+
function isStandIn(plugin) {
|
|
1610
|
+
return (plugin.pluginType === "method" || plugin.pluginType === "property" || plugin.pluginType === "aggregate") && plugin.standIn === true;
|
|
1611
|
+
}
|
|
1612
|
+
function topoOrder(descriptors) {
|
|
1613
|
+
const order = [];
|
|
1614
|
+
const visited = /* @__PURE__ */ new Set();
|
|
1615
|
+
const visit = (id) => {
|
|
1616
|
+
if (visited.has(id)) return;
|
|
1617
|
+
visited.add(id);
|
|
1618
|
+
const descriptor = descriptors.get(id);
|
|
1619
|
+
if (descriptor) for (const edge of edgesOf(descriptor)) visit(edge.id);
|
|
1620
|
+
order.push(id);
|
|
1621
|
+
};
|
|
1622
|
+
for (const id of descriptors.keys()) visit(id);
|
|
1623
|
+
return order;
|
|
1624
|
+
}
|
|
1625
|
+
function collectPlugins(root, materialized = /* @__PURE__ */ new Set()) {
|
|
1626
|
+
const byId = /* @__PURE__ */ new Map();
|
|
1627
|
+
const visit = (plugin) => {
|
|
1628
|
+
if (materialized.has(plugin.id)) return;
|
|
1629
|
+
const existing = byId.get(plugin.id);
|
|
1630
|
+
if (existing === plugin) return;
|
|
1631
|
+
if (existing) {
|
|
1632
|
+
const bothReal = !isStandIn(existing) && !isStandIn(plugin);
|
|
1633
|
+
if (bothReal) {
|
|
1634
|
+
throw new Error(
|
|
1635
|
+
`createSdk: duplicate plugin id "${plugin.id}". Two different plugins registered under the same id.`
|
|
1636
|
+
);
|
|
1637
|
+
}
|
|
1638
|
+
if (isStandIn(existing) && !isStandIn(plugin)) {
|
|
1639
|
+
byId.set(plugin.id, plugin);
|
|
1640
|
+
for (const edge of edgesOf(plugin)) visit(edge);
|
|
1641
|
+
}
|
|
1642
|
+
return;
|
|
1643
|
+
}
|
|
1644
|
+
byId.set(plugin.id, plugin);
|
|
1645
|
+
for (const edge of edgesOf(plugin)) visit(edge);
|
|
1646
|
+
};
|
|
1647
|
+
visit(root);
|
|
1648
|
+
for (const [id, plugin] of byId) {
|
|
1649
|
+
if (isStandIn(plugin)) {
|
|
1650
|
+
throw new Error(
|
|
1651
|
+
`createSdk: missing dependency "${id}". A plugin depends on it (via a stand-in) but no implementation was registered.`
|
|
1652
|
+
);
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1655
|
+
return byId;
|
|
1656
|
+
}
|
|
1657
|
+
function bindValue(target, key2, entry) {
|
|
1658
|
+
if (entry.pluginType === "property" && entry.getValue) {
|
|
1659
|
+
Object.defineProperty(target, key2, {
|
|
1660
|
+
get: entry.getValue,
|
|
1661
|
+
enumerable: true,
|
|
1662
|
+
configurable: true
|
|
1663
|
+
});
|
|
1664
|
+
} else {
|
|
1665
|
+
Object.defineProperty(target, key2, {
|
|
1666
|
+
value: entry.value,
|
|
1667
|
+
writable: true,
|
|
1668
|
+
enumerable: true,
|
|
1669
|
+
configurable: true
|
|
1670
|
+
});
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
function buildSurface(context, ...maps) {
|
|
1674
|
+
const sdk = {};
|
|
1675
|
+
for (const map of maps) {
|
|
1676
|
+
Object.defineProperties(sdk, Object.getOwnPropertyDescriptors(map));
|
|
1677
|
+
}
|
|
1678
|
+
sdk.context = context;
|
|
1679
|
+
sdk[CONTEXT] = context;
|
|
1680
|
+
return sdk;
|
|
1681
|
+
}
|
|
1682
|
+
function buildImports(plugins, importBindings) {
|
|
1683
|
+
const imports = {};
|
|
1684
|
+
for (const { binding, id } of importBindings) {
|
|
1685
|
+
bindValue(imports, binding, plugins[id]);
|
|
1686
|
+
}
|
|
1687
|
+
return imports;
|
|
1688
|
+
}
|
|
1689
|
+
function mirrorLegacyRootKeys(context, rootKeys, meta) {
|
|
1690
|
+
const exports2 = {};
|
|
1691
|
+
for (const [name, value] of Object.entries(rootKeys)) {
|
|
1692
|
+
context.plugins[name] = legacyGraphEntry(name, value, meta[name]);
|
|
1693
|
+
exports2[name] = value;
|
|
1694
|
+
}
|
|
1695
|
+
return exports2;
|
|
1696
|
+
}
|
|
1697
|
+
function recordExportSurface(context, exports2) {
|
|
1698
|
+
for (const [binding, child] of Object.entries(exports2)) {
|
|
1699
|
+
context.surface[binding] = child.id;
|
|
1700
|
+
}
|
|
1701
|
+
}
|
|
1702
|
+
function materialize(descriptors, context) {
|
|
1703
|
+
const states = /* @__PURE__ */ new Map();
|
|
1704
|
+
runLegacyPass(descriptors, context);
|
|
1705
|
+
buildMethodEntries(descriptors, context, states);
|
|
1706
|
+
buildEagerArtifacts(descriptors, context, states);
|
|
1707
|
+
bindAttachments(descriptors, context);
|
|
1708
|
+
resolveAggregates(descriptors, context);
|
|
1709
|
+
assembleMiddleware(descriptors, context);
|
|
1710
|
+
return context.plugins;
|
|
1711
|
+
}
|
|
1712
|
+
function bindResolver(resolver, plugins) {
|
|
1713
|
+
switch (resolver.type) {
|
|
1714
|
+
case "static":
|
|
1715
|
+
return {
|
|
1716
|
+
type: "static",
|
|
1717
|
+
requireParameters: resolver.requireParameters,
|
|
1718
|
+
inputType: resolver.inputType,
|
|
1719
|
+
placeholder: resolver.placeholder
|
|
1720
|
+
};
|
|
1721
|
+
case "constant":
|
|
1722
|
+
return {
|
|
1723
|
+
type: "constant",
|
|
1724
|
+
value: resolver.value,
|
|
1725
|
+
requireParameters: resolver.requireParameters
|
|
1726
|
+
};
|
|
1727
|
+
case "info":
|
|
1728
|
+
return { type: "info", text: resolver.text };
|
|
1729
|
+
case "object": {
|
|
1730
|
+
const imports = buildImports(plugins, resolver.importBindings);
|
|
1731
|
+
const bound = {
|
|
1732
|
+
type: "object",
|
|
1733
|
+
requireParameters: resolver.requireParameters
|
|
1734
|
+
};
|
|
1735
|
+
if (resolver.properties)
|
|
1736
|
+
bound.properties = bindFields(resolver.properties, plugins);
|
|
1737
|
+
if (resolver.definitions)
|
|
1738
|
+
bound.definitions = bindDefinitions(resolver.definitions, plugins);
|
|
1739
|
+
const { getProperties } = resolver;
|
|
1740
|
+
if (getProperties)
|
|
1741
|
+
bound.getProperties = ({ input }) => getProperties({ imports, input });
|
|
1742
|
+
return bound;
|
|
1743
|
+
}
|
|
1744
|
+
case "array": {
|
|
1745
|
+
const bound = {
|
|
1746
|
+
type: "array",
|
|
1747
|
+
requireParameters: resolver.requireParameters,
|
|
1748
|
+
minItems: resolver.minItems,
|
|
1749
|
+
maxItems: resolver.maxItems,
|
|
1750
|
+
itemValueType: resolver.itemValueType,
|
|
1751
|
+
items: isResolverRef(resolver.items) ? resolver.items : bindResolver(resolver.items, plugins)
|
|
1752
|
+
};
|
|
1753
|
+
if (resolver.definitions)
|
|
1754
|
+
bound.definitions = bindDefinitions(resolver.definitions, plugins);
|
|
1755
|
+
return bound;
|
|
1756
|
+
}
|
|
1757
|
+
default: {
|
|
1758
|
+
const imports = buildImports(plugins, resolver.importBindings);
|
|
1759
|
+
const bound = {
|
|
1760
|
+
type: "dynamic",
|
|
1761
|
+
requireParameters: resolver.requireParameters,
|
|
1762
|
+
inputType: resolver.inputType,
|
|
1763
|
+
placeholder: resolver.placeholder,
|
|
1764
|
+
prompt: resolver.prompt
|
|
1765
|
+
};
|
|
1766
|
+
const {
|
|
1767
|
+
getContext: getContext2,
|
|
1768
|
+
listItems,
|
|
1769
|
+
tryResolveWithoutPrompt,
|
|
1770
|
+
tryResolveFromSearch
|
|
1771
|
+
} = resolver;
|
|
1772
|
+
if (getContext2)
|
|
1773
|
+
bound.getContext = ({ input }) => getContext2({ imports, input });
|
|
1774
|
+
if (listItems)
|
|
1775
|
+
bound.listItems = ({ input, context, search, cursor }) => listItems({ imports, input, context, search, cursor });
|
|
1776
|
+
if (tryResolveWithoutPrompt) {
|
|
1777
|
+
bound.tryResolveWithoutPrompt = ({ input }) => tryResolveWithoutPrompt({ imports, input });
|
|
1778
|
+
}
|
|
1779
|
+
if (tryResolveFromSearch) {
|
|
1780
|
+
bound.tryResolveFromSearch = ({ input, search }) => tryResolveFromSearch({ imports, input, search });
|
|
1781
|
+
}
|
|
1782
|
+
return bound;
|
|
1783
|
+
}
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1786
|
+
function bindFields(fields, plugins) {
|
|
1787
|
+
const out = {};
|
|
1788
|
+
for (const [key2, field] of Object.entries(fields)) {
|
|
1789
|
+
out[key2] = {
|
|
1790
|
+
...field,
|
|
1791
|
+
resolver: isResolverRef(field.resolver) ? field.resolver : bindResolver(field.resolver, plugins)
|
|
1792
|
+
};
|
|
1793
|
+
}
|
|
1794
|
+
return out;
|
|
1795
|
+
}
|
|
1796
|
+
function bindDefinitions(definitions, plugins) {
|
|
1797
|
+
const out = {};
|
|
1798
|
+
for (const [key2, def] of Object.entries(definitions)) {
|
|
1799
|
+
out[key2] = bindResolver(def, plugins);
|
|
1800
|
+
}
|
|
1801
|
+
return out;
|
|
1802
|
+
}
|
|
1803
|
+
function bindFormatter(formatter, plugins) {
|
|
1804
|
+
const imports = buildImports(plugins, formatter.importBindings);
|
|
1805
|
+
const bound = { format: formatter.format };
|
|
1806
|
+
const { getContext: getContext2 } = formatter;
|
|
1807
|
+
if (getContext2)
|
|
1808
|
+
bound.getContext = ({ items, input, context }) => getContext2({ imports, items, input, context });
|
|
1809
|
+
return bound;
|
|
1810
|
+
}
|
|
1811
|
+
function bindAttachments(descriptors, context) {
|
|
1812
|
+
const plugins = context.plugins;
|
|
1813
|
+
for (const [id, descriptor] of descriptors) {
|
|
1814
|
+
if (descriptor.pluginType !== "method") continue;
|
|
1815
|
+
const entry = plugins[id];
|
|
1816
|
+
if (!entry || entry.pluginType !== "method") continue;
|
|
1817
|
+
if (descriptor.resolvers) {
|
|
1818
|
+
const bound = {};
|
|
1819
|
+
for (const [param, resolver] of Object.entries(descriptor.resolvers)) {
|
|
1820
|
+
bound[param] = bindResolver(resolver, plugins);
|
|
1821
|
+
}
|
|
1822
|
+
entry.resolvers = bound;
|
|
1823
|
+
}
|
|
1824
|
+
if (descriptor.formatter) {
|
|
1825
|
+
entry.formatter = bindFormatter(descriptor.formatter, plugins);
|
|
1826
|
+
}
|
|
1827
|
+
}
|
|
1828
|
+
}
|
|
1829
|
+
function runLegacyPass(descriptors, context) {
|
|
1830
|
+
const plugins = context.plugins;
|
|
1831
|
+
const compatView = new Proxy(
|
|
1832
|
+
{},
|
|
1833
|
+
{
|
|
1834
|
+
get: (_target, prop) => {
|
|
1835
|
+
if (prop === "context") return context;
|
|
1836
|
+
const entry = plugins[prop];
|
|
1837
|
+
return entry?.value;
|
|
1838
|
+
}
|
|
1839
|
+
}
|
|
1840
|
+
);
|
|
1841
|
+
for (const id of topoOrder(descriptors)) {
|
|
1842
|
+
const descriptor = descriptors.get(id);
|
|
1843
|
+
if (!descriptor || descriptor.pluginType !== "legacy") continue;
|
|
1844
|
+
const { rootKeys, meta, hooks, contextRest } = splitPluginContribution(
|
|
1845
|
+
descriptor.run(compatView)
|
|
1846
|
+
);
|
|
1847
|
+
Object.assign(context.meta, meta);
|
|
1848
|
+
Object.assign(context, contextRest);
|
|
1849
|
+
context.hooks = buildHooks(context.hooks, hooks);
|
|
1850
|
+
const exports2 = mirrorLegacyRootKeys(context, rootKeys, meta);
|
|
1851
|
+
if (!("getRegistry" in exports2)) {
|
|
1852
|
+
let getRegistry2 = function(options) {
|
|
1853
|
+
const sdk = this ?? exports2;
|
|
1854
|
+
const meta2 = {};
|
|
1855
|
+
const formatters = {};
|
|
1856
|
+
const boundResolvers = {};
|
|
1857
|
+
for (const [binding, id2] of Object.entries(context.surface)) {
|
|
1858
|
+
const entry = context.plugins[id2];
|
|
1859
|
+
if (!entry || entry.pluginType === "aggregate") continue;
|
|
1860
|
+
const m = pluginEntryMeta(entry);
|
|
1861
|
+
if (m) meta2[binding] = m;
|
|
1862
|
+
const f = normalizeFormatter(entry, sdk);
|
|
1863
|
+
if (f) formatters[binding] = f;
|
|
1864
|
+
const r = normalizeBoundResolvers(entry);
|
|
1865
|
+
if (r) boundResolvers[binding] = r;
|
|
1866
|
+
}
|
|
1867
|
+
Object.assign(meta2, context.meta);
|
|
1868
|
+
return buildRegistry({
|
|
1869
|
+
sdk,
|
|
1870
|
+
meta: meta2,
|
|
1871
|
+
formatters,
|
|
1872
|
+
boundResolvers,
|
|
1873
|
+
packageFilter: options?.package
|
|
1874
|
+
});
|
|
1875
|
+
};
|
|
1876
|
+
var getRegistry = getRegistry2;
|
|
1877
|
+
exports2.getRegistry = getRegistry2;
|
|
1878
|
+
plugins.getRegistry = {
|
|
1879
|
+
pluginType: "method",
|
|
1880
|
+
name: "getRegistry",
|
|
1881
|
+
value: getRegistry2,
|
|
1882
|
+
chain: []
|
|
1883
|
+
};
|
|
1884
|
+
}
|
|
1885
|
+
plugins[id] = { pluginType: "aggregate", name: descriptor.name, exports: exports2 };
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
function buildMethodEntries(descriptors, context, states) {
|
|
1889
|
+
const plugins = context.plugins;
|
|
1890
|
+
for (const [id, descriptor] of descriptors) {
|
|
1891
|
+
if (descriptor.pluginType !== "method") continue;
|
|
1892
|
+
const out = normalizeOutput(descriptor.output);
|
|
1893
|
+
const entry = {
|
|
1894
|
+
pluginType: "method",
|
|
1895
|
+
name: descriptor.name,
|
|
1896
|
+
chain: [],
|
|
1897
|
+
inputSchema: descriptor.inputSchema,
|
|
1898
|
+
// Derive the presentation type from the output mode when the author did
|
|
1899
|
+
// not set one; an explicit meta.type (e.g. "create") still wins.
|
|
1900
|
+
meta: out.type === "raw" || descriptor.meta?.type ? descriptor.meta : { ...descriptor.meta, type: out.type },
|
|
1901
|
+
output: out,
|
|
1902
|
+
// Replaced below; never called.
|
|
1903
|
+
value: () => void 0
|
|
1904
|
+
};
|
|
1905
|
+
const callRun = (input) => descriptor.run({
|
|
1906
|
+
imports: buildImports(plugins, descriptor.importBindings),
|
|
1907
|
+
state: states.get(id),
|
|
1908
|
+
input
|
|
1909
|
+
});
|
|
1910
|
+
const fold = (coreFn) => (input) => {
|
|
1911
|
+
let next = coreFn;
|
|
1912
|
+
for (const wrap of entry.chain) {
|
|
1913
|
+
const inner = next;
|
|
1914
|
+
next = (i) => wrap.run({
|
|
1915
|
+
imports: buildImports(plugins, wrap.owner.importBindings),
|
|
1916
|
+
next: inner,
|
|
1917
|
+
input: i
|
|
1918
|
+
});
|
|
1919
|
+
}
|
|
1920
|
+
return next(input);
|
|
1921
|
+
};
|
|
1922
|
+
const sdk = { context };
|
|
1923
|
+
if (out.type === "list") {
|
|
1924
|
+
entry.value = createPaginatedFunction(
|
|
1925
|
+
fold(callRun),
|
|
1926
|
+
{
|
|
1927
|
+
sdk,
|
|
1928
|
+
schema: descriptor.inputSchema,
|
|
1929
|
+
name: descriptor.name,
|
|
1930
|
+
defaultPageSize: out.defaultPageSize,
|
|
1931
|
+
adaptPage: out.adaptPage
|
|
1932
|
+
}
|
|
1933
|
+
);
|
|
1934
|
+
} else if (out.type === "item") {
|
|
1935
|
+
const itemCore = async (input) => ({
|
|
1936
|
+
data: await callRun(input)
|
|
1937
|
+
});
|
|
1938
|
+
entry.value = createFunction(
|
|
1939
|
+
fold(itemCore),
|
|
1940
|
+
{ sdk, schema: descriptor.inputSchema, name: descriptor.name }
|
|
1941
|
+
);
|
|
1942
|
+
} else {
|
|
1943
|
+
entry.value = (rawInput) => {
|
|
1944
|
+
const input = descriptor.inputSchema ? descriptor.inputSchema.parse(rawInput) : rawInput;
|
|
1945
|
+
return fold(callRun)(input);
|
|
1946
|
+
};
|
|
1947
|
+
}
|
|
1948
|
+
if (descriptor.positional) {
|
|
1949
|
+
const names = descriptor.positional;
|
|
1950
|
+
const canonicalValue = entry.value;
|
|
1951
|
+
entry.value = (...args) => {
|
|
1952
|
+
const packed = {};
|
|
1953
|
+
names.forEach((name, i) => {
|
|
1954
|
+
if (i < args.length) packed[name] = args[i];
|
|
1955
|
+
});
|
|
1956
|
+
return canonicalValue(packed);
|
|
1957
|
+
};
|
|
1958
|
+
entry.positional = names;
|
|
1959
|
+
}
|
|
1960
|
+
plugins[id] = entry;
|
|
1961
|
+
}
|
|
1962
|
+
}
|
|
1963
|
+
function buildEagerArtifacts(descriptors, context, states) {
|
|
1964
|
+
const plugins = context.plugins;
|
|
1965
|
+
const built = /* @__PURE__ */ new Set();
|
|
1966
|
+
const building = /* @__PURE__ */ new Set();
|
|
1967
|
+
const ensureBuilt = (id) => {
|
|
1968
|
+
if (built.has(id)) return;
|
|
1969
|
+
const descriptor = descriptors.get(id);
|
|
1970
|
+
if (!descriptor || descriptor.pluginType === "aggregate" || descriptor.pluginType === "legacy") {
|
|
1971
|
+
built.add(id);
|
|
1972
|
+
return;
|
|
1973
|
+
}
|
|
1974
|
+
if (building.has(id)) {
|
|
1975
|
+
throw new Error(`createSdk: dependency cycle at "${id}".`);
|
|
1976
|
+
}
|
|
1977
|
+
building.add(id);
|
|
1978
|
+
for (const { id: depId } of descriptor.importBindings) ensureBuilt(depId);
|
|
1979
|
+
if (descriptor.pluginType === "method") {
|
|
1980
|
+
states.set(
|
|
1981
|
+
id,
|
|
1982
|
+
descriptor.setup ? descriptor.setup({
|
|
1983
|
+
imports: buildImports(plugins, descriptor.importBindings)
|
|
1984
|
+
}) : void 0
|
|
1985
|
+
);
|
|
1986
|
+
} else {
|
|
1987
|
+
states.set(
|
|
1988
|
+
id,
|
|
1989
|
+
descriptor.setup ? descriptor.setup({
|
|
1990
|
+
imports: buildImports(plugins, descriptor.importBindings)
|
|
1991
|
+
}) : void 0
|
|
1992
|
+
);
|
|
1993
|
+
if (descriptor.privileged) {
|
|
1994
|
+
plugins[id] = {
|
|
1995
|
+
pluginType: "property",
|
|
1996
|
+
name: descriptor.name,
|
|
1997
|
+
value: context,
|
|
1998
|
+
meta: descriptor.meta
|
|
1999
|
+
};
|
|
2000
|
+
} else if (descriptor.get) {
|
|
2001
|
+
const get = descriptor.get;
|
|
2002
|
+
const importBindings = descriptor.importBindings;
|
|
2003
|
+
plugins[id] = {
|
|
2004
|
+
pluginType: "property",
|
|
2005
|
+
name: descriptor.name,
|
|
2006
|
+
getValue: () => get({
|
|
2007
|
+
imports: buildImports(plugins, importBindings),
|
|
2008
|
+
state: states.get(id)
|
|
2009
|
+
}),
|
|
2010
|
+
meta: descriptor.meta
|
|
2011
|
+
};
|
|
2012
|
+
} else {
|
|
2013
|
+
plugins[id] = {
|
|
2014
|
+
pluginType: "property",
|
|
2015
|
+
name: descriptor.name,
|
|
2016
|
+
value: descriptor.value,
|
|
2017
|
+
meta: descriptor.meta
|
|
2018
|
+
};
|
|
2019
|
+
}
|
|
2020
|
+
}
|
|
2021
|
+
building.delete(id);
|
|
2022
|
+
built.add(id);
|
|
2023
|
+
};
|
|
2024
|
+
for (const id of descriptors.keys()) ensureBuilt(id);
|
|
2025
|
+
}
|
|
2026
|
+
function resolveAggregates(descriptors, context) {
|
|
2027
|
+
const plugins = context.plugins;
|
|
2028
|
+
for (const [id, descriptor] of descriptors) {
|
|
2029
|
+
if (descriptor.pluginType !== "aggregate") continue;
|
|
2030
|
+
const exports2 = {};
|
|
2031
|
+
for (const [binding, child] of Object.entries(descriptor.exports)) {
|
|
2032
|
+
bindValue(exports2, binding, plugins[child.id]);
|
|
2033
|
+
}
|
|
2034
|
+
plugins[id] = { pluginType: "aggregate", name: descriptor.name, exports: exports2 };
|
|
2035
|
+
}
|
|
2036
|
+
}
|
|
2037
|
+
function assembleMiddleware(descriptors, context) {
|
|
2038
|
+
const plugins = context.plugins;
|
|
2039
|
+
for (const id of topoOrder(descriptors)) {
|
|
2040
|
+
const descriptor = descriptors.get(id);
|
|
2041
|
+
if (!descriptor || descriptor.pluginType !== "aggregate" || !descriptor.middleware) {
|
|
2042
|
+
continue;
|
|
2043
|
+
}
|
|
2044
|
+
for (const [targetBinding, fn] of Object.entries(descriptor.middleware)) {
|
|
2045
|
+
const edge = descriptor.importBindings.find(
|
|
2046
|
+
(b) => b.binding === targetBinding
|
|
2047
|
+
);
|
|
2048
|
+
if (!edge) {
|
|
2049
|
+
throw new Error(
|
|
2050
|
+
`createSdk: middleware target "${targetBinding}" in plugin "${id}" is not a direct dependency. A middleware target must be a declared dependency of the wrapping plugin.`
|
|
2051
|
+
);
|
|
2052
|
+
}
|
|
2053
|
+
const target = plugins[edge.id];
|
|
2054
|
+
if (!target || target.pluginType !== "method") {
|
|
2055
|
+
throw new Error(
|
|
2056
|
+
`createSdk: middleware target "${targetBinding}" in plugin "${id}" does not resolve to a method.`
|
|
2057
|
+
);
|
|
2058
|
+
}
|
|
2059
|
+
if (target.output?.type === "list") {
|
|
2060
|
+
throw new Error(
|
|
2061
|
+
`createSdk: middleware target "${targetBinding}" in plugin "${id}" resolves to a list-output method, which does not support middleware yet.`
|
|
2062
|
+
);
|
|
2063
|
+
}
|
|
2064
|
+
target.chain.push({ run: fn, owner: descriptor });
|
|
2065
|
+
}
|
|
2066
|
+
}
|
|
2067
|
+
}
|
|
2068
|
+
function createSdk(root) {
|
|
2069
|
+
const context = { plugins: {}, meta: {}, hooks: {}, surface: {} };
|
|
2070
|
+
if (root.pluginType === "legacy-merge") {
|
|
2071
|
+
const { legacy, plugin } = root;
|
|
2072
|
+
const collectRoot = {
|
|
2073
|
+
pluginType: "aggregate",
|
|
2074
|
+
name: root.name,
|
|
2075
|
+
id: `${root.id}:merge`,
|
|
2076
|
+
imports: [legacy, plugin],
|
|
2077
|
+
importBindings: [],
|
|
2078
|
+
exports: {}
|
|
2079
|
+
};
|
|
2080
|
+
const plugins2 = materialize(collectPlugins(collectRoot), context);
|
|
2081
|
+
const legacyExports = plugins2[legacy.id].exports;
|
|
2082
|
+
let pluginSurface;
|
|
2083
|
+
if (plugin.pluginType === "aggregate") {
|
|
2084
|
+
pluginSurface = plugins2[plugin.id].exports;
|
|
2085
|
+
} else {
|
|
2086
|
+
pluginSurface = {};
|
|
2087
|
+
bindValue(pluginSurface, plugin.name, plugins2[plugin.id]);
|
|
2088
|
+
}
|
|
2089
|
+
for (const key2 of Object.keys(legacyExports)) context.surface[key2] = key2;
|
|
2090
|
+
if (plugin.pluginType === "aggregate") {
|
|
2091
|
+
recordExportSurface(context, plugin.exports);
|
|
2092
|
+
} else {
|
|
2093
|
+
context.surface[plugin.name] = plugin.id;
|
|
2094
|
+
}
|
|
2095
|
+
return buildSurface(context, legacyExports, pluginSurface);
|
|
2096
|
+
}
|
|
2097
|
+
const plugins = materialize(collectPlugins(root), context);
|
|
2098
|
+
if (root.pluginType === "method" || root.pluginType === "property") {
|
|
2099
|
+
context.surface[root.name] = root.id;
|
|
2100
|
+
const sdk = buildSurface(context);
|
|
2101
|
+
bindValue(sdk, root.name, plugins[root.id]);
|
|
2102
|
+
return sdk;
|
|
2103
|
+
}
|
|
2104
|
+
if (root.pluginType === "aggregate")
|
|
2105
|
+
recordExportSurface(context, root.exports);
|
|
2106
|
+
return buildSurface(context, plugins[root.id].exports);
|
|
2107
|
+
}
|
|
2108
|
+
function addModelPlugin(sdk, plugin, options = {}) {
|
|
2109
|
+
const override = options.override === true;
|
|
2110
|
+
const context = getContext(sdk);
|
|
2111
|
+
const surfaceKeys = plugin.pluginType === "aggregate" ? Object.keys(plugin.exports) : [plugin.name];
|
|
2112
|
+
checkRootKeyCollisions(sdk, surfaceKeys, override, "addPlugin");
|
|
2113
|
+
const materialized = new Set(Object.keys(context.plugins));
|
|
2114
|
+
if (override && materialized.has(plugin.id)) {
|
|
2115
|
+
throw new Error(
|
|
2116
|
+
`addPlugin: cannot override already-materialized plugin "${plugin.id}" on the incremental path. Rebuild the SDK with the replacement via createSdk.`
|
|
2117
|
+
);
|
|
2118
|
+
}
|
|
2119
|
+
materialize(collectPlugins(plugin, materialized), context);
|
|
2120
|
+
const entry = context.plugins[plugin.id];
|
|
2121
|
+
if (entry.pluginType === "aggregate") {
|
|
2122
|
+
Object.defineProperties(
|
|
2123
|
+
sdk,
|
|
2124
|
+
Object.getOwnPropertyDescriptors(entry.exports)
|
|
2125
|
+
);
|
|
2126
|
+
for (const [binding, child] of Object.entries(
|
|
2127
|
+
plugin.exports
|
|
2128
|
+
)) {
|
|
2129
|
+
context.surface[binding] = child.id;
|
|
2130
|
+
}
|
|
2131
|
+
} else {
|
|
2132
|
+
bindValue(sdk, plugin.name, entry);
|
|
2133
|
+
context.surface[plugin.name] = plugin.id;
|
|
2134
|
+
}
|
|
2135
|
+
}
|
|
2136
|
+
function addPlugin(sdk, plugin, options) {
|
|
2137
|
+
if (typeof plugin === "function") {
|
|
2138
|
+
const record = sdk;
|
|
2139
|
+
const contribution = applyPluginToSdk(
|
|
2140
|
+
record,
|
|
2141
|
+
plugin,
|
|
2142
|
+
options ?? {}
|
|
2143
|
+
);
|
|
2144
|
+
const context = record[CONTEXT];
|
|
2145
|
+
if (context) {
|
|
2146
|
+
mirrorLegacyRootKeys(context, contribution.rootKeys, contribution.meta);
|
|
2147
|
+
for (const name of Object.keys(contribution.rootKeys)) {
|
|
2148
|
+
context.surface[name] = name;
|
|
2149
|
+
}
|
|
2150
|
+
}
|
|
2151
|
+
return;
|
|
2152
|
+
}
|
|
2153
|
+
addModelPlugin(
|
|
2154
|
+
sdk,
|
|
2155
|
+
plugin,
|
|
2156
|
+
options ?? {}
|
|
2157
|
+
);
|
|
2158
|
+
}
|
|
2159
|
+
|
|
2160
|
+
// src/model/resolution/controller.ts
|
|
2161
|
+
var import_zod4 = require("zod");
|
|
2162
|
+
|
|
2163
|
+
// src/types/signals.ts
|
|
2164
|
+
var CORE_SIGNAL_SYMBOL = Symbol.for("kitcore.signal");
|
|
2165
|
+
var CoreSignal = class extends Error {
|
|
2166
|
+
constructor(message) {
|
|
2167
|
+
super(message);
|
|
2168
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
2169
|
+
Object.defineProperty(this, CORE_SIGNAL_SYMBOL, {
|
|
2170
|
+
value: true,
|
|
2171
|
+
enumerable: false,
|
|
2172
|
+
configurable: true,
|
|
2173
|
+
writable: false
|
|
2174
|
+
});
|
|
2175
|
+
}
|
|
2176
|
+
};
|
|
2177
|
+
function isCoreSignal(value) {
|
|
2178
|
+
return Boolean(
|
|
2179
|
+
value && typeof value === "object" && value[CORE_SIGNAL_SYMBOL] === true
|
|
2180
|
+
);
|
|
2181
|
+
}
|
|
2182
|
+
var CoreCancelledSignal = class extends CoreSignal {
|
|
2183
|
+
constructor(message = "resolution cancelled") {
|
|
2184
|
+
super(message);
|
|
2185
|
+
this.name = "CoreCancelledSignal";
|
|
2186
|
+
this.code = "CANCELLED";
|
|
2187
|
+
}
|
|
2188
|
+
};
|
|
2189
|
+
|
|
2190
|
+
// src/model/resolution/plan.ts
|
|
2191
|
+
var import_zod3 = require("zod");
|
|
2192
|
+
function unwrap(schema) {
|
|
2193
|
+
let inner = schema;
|
|
2194
|
+
let required = true;
|
|
2195
|
+
for (; ; ) {
|
|
2196
|
+
if (inner instanceof import_zod3.z.ZodOptional) {
|
|
2197
|
+
required = false;
|
|
2198
|
+
inner = inner._zod.def.innerType;
|
|
2199
|
+
} else if (inner instanceof import_zod3.z.ZodDefault) {
|
|
2200
|
+
required = false;
|
|
2201
|
+
inner = inner._zod.def.innerType;
|
|
2202
|
+
} else if (inner instanceof import_zod3.z.ZodNullable) {
|
|
2203
|
+
inner = inner._zod.def.innerType;
|
|
2204
|
+
} else {
|
|
2205
|
+
break;
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2208
|
+
return { inner, required };
|
|
2209
|
+
}
|
|
2210
|
+
function valueTypeOf(inner) {
|
|
2211
|
+
if (inner instanceof import_zod3.z.ZodString) return "string";
|
|
2212
|
+
if (inner instanceof import_zod3.z.ZodNumber) return "number";
|
|
2213
|
+
if (inner instanceof import_zod3.z.ZodBoolean) return "boolean";
|
|
2214
|
+
if (inner instanceof import_zod3.z.ZodEnum) return "string";
|
|
2215
|
+
if (inner instanceof import_zod3.z.ZodArray) return "array";
|
|
2216
|
+
if (inner instanceof import_zod3.z.ZodObject) return "object";
|
|
2217
|
+
return void 0;
|
|
2218
|
+
}
|
|
2219
|
+
function staticChoicesOf(inner) {
|
|
2220
|
+
if (inner instanceof import_zod3.z.ZodEnum) {
|
|
2221
|
+
const values = inner.options;
|
|
2222
|
+
return values.map((value) => ({ label: value, value }));
|
|
2223
|
+
}
|
|
2224
|
+
return void 0;
|
|
2225
|
+
}
|
|
2226
|
+
function objectShape(schema) {
|
|
2227
|
+
const { inner } = schema ? unwrap(schema) : { inner: void 0 };
|
|
2228
|
+
if (inner instanceof import_zod3.z.ZodObject) {
|
|
2229
|
+
return inner.shape;
|
|
2230
|
+
}
|
|
2231
|
+
return void 0;
|
|
2232
|
+
}
|
|
2233
|
+
function topoOrder2(specs) {
|
|
2234
|
+
const byName = new Map(specs.map((s) => [s.name, s]));
|
|
2235
|
+
const ordered = [];
|
|
2236
|
+
const placed = /* @__PURE__ */ new Set();
|
|
2237
|
+
let progressed = true;
|
|
2238
|
+
while (ordered.length < specs.length && progressed) {
|
|
2239
|
+
progressed = false;
|
|
2240
|
+
for (const spec of specs) {
|
|
2241
|
+
if (placed.has(spec.name)) continue;
|
|
2242
|
+
const ready = spec.requires.every((r) => !byName.has(r) || placed.has(r));
|
|
2243
|
+
if (ready) {
|
|
2244
|
+
ordered.push(spec);
|
|
2245
|
+
placed.add(spec.name);
|
|
2246
|
+
progressed = true;
|
|
2247
|
+
}
|
|
2248
|
+
}
|
|
2249
|
+
}
|
|
2250
|
+
for (const spec of specs) if (!placed.has(spec.name)) ordered.push(spec);
|
|
2251
|
+
return ordered;
|
|
2252
|
+
}
|
|
2253
|
+
function planParameters(entry) {
|
|
2254
|
+
const shape = objectShape(entry.inputSchema);
|
|
2255
|
+
const resolvers = entry.boundResolvers ?? {};
|
|
2256
|
+
const names = shape ? Object.keys(shape) : Object.keys(resolvers);
|
|
2257
|
+
const specs = names.map((name) => {
|
|
2258
|
+
const field = shape?.[name];
|
|
2259
|
+
const { inner, required } = field ? unwrap(field) : { inner: void 0, required: false };
|
|
2260
|
+
const resolver = resolvers[name];
|
|
2261
|
+
return {
|
|
2262
|
+
name,
|
|
2263
|
+
required,
|
|
2264
|
+
valueType: inner ? valueTypeOf(inner) : void 0,
|
|
2265
|
+
staticChoices: inner ? staticChoicesOf(inner) : void 0,
|
|
2266
|
+
resolver,
|
|
2267
|
+
requires: resolver?.requireParameters ?? []
|
|
2268
|
+
};
|
|
2269
|
+
});
|
|
2270
|
+
return { parameters: topoOrder2(specs) };
|
|
2271
|
+
}
|
|
2272
|
+
|
|
2273
|
+
// src/model/resolution/engine.ts
|
|
2274
|
+
function getAtPath(root, path) {
|
|
2275
|
+
let node = root;
|
|
2276
|
+
for (const seg of path) {
|
|
2277
|
+
if (node == null || typeof node !== "object") return void 0;
|
|
2278
|
+
node = node[seg];
|
|
2279
|
+
}
|
|
2280
|
+
return node;
|
|
2281
|
+
}
|
|
2282
|
+
function setAtPath(root, path, value) {
|
|
2283
|
+
let node = root;
|
|
2284
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
2285
|
+
const seg = path[i];
|
|
2286
|
+
if (node[seg] == null || typeof node[seg] !== "object") node[seg] = {};
|
|
2287
|
+
node = node[seg];
|
|
2288
|
+
}
|
|
2289
|
+
node[path[path.length - 1]] = value;
|
|
2290
|
+
}
|
|
2291
|
+
var key = (path) => path.join(".");
|
|
2292
|
+
function isSettled(state, path) {
|
|
2293
|
+
return state.settled.includes(key(path));
|
|
2294
|
+
}
|
|
2295
|
+
function settle(state, path) {
|
|
2296
|
+
const k = key(path);
|
|
2297
|
+
if (!state.settled.includes(k)) state.settled.push(k);
|
|
2298
|
+
}
|
|
2299
|
+
function clone(state) {
|
|
2300
|
+
return JSON.parse(JSON.stringify(state));
|
|
2301
|
+
}
|
|
2302
|
+
function coerce(leaf, raw) {
|
|
2303
|
+
if (typeof raw !== "string") return raw;
|
|
2304
|
+
if (leaf.valueType === "number") {
|
|
2305
|
+
const n = Number(raw);
|
|
2306
|
+
return raw.trim() !== "" && !Number.isNaN(n) ? n : raw;
|
|
2307
|
+
}
|
|
2308
|
+
if (leaf.valueType === "boolean") {
|
|
2309
|
+
if (raw === "true") return true;
|
|
2310
|
+
if (raw === "false") return false;
|
|
2311
|
+
}
|
|
2312
|
+
return raw;
|
|
2313
|
+
}
|
|
2314
|
+
async function validationError(leaf, value, state) {
|
|
2315
|
+
const context = await resolveContext(leaf, state.resolved);
|
|
2316
|
+
const config = leaf.resolver?.prompt?.({
|
|
2317
|
+
items: state.listing?.items ?? [],
|
|
2318
|
+
input: mergeInput(state.resolved, leaf.extraInput),
|
|
2319
|
+
context
|
|
2320
|
+
});
|
|
2321
|
+
if (!config?.validate) return null;
|
|
2322
|
+
const verdict = config.validate(value);
|
|
2323
|
+
if (verdict === true) return null;
|
|
2324
|
+
return typeof verdict === "string" ? verdict : `${leaf.name}: invalid value`;
|
|
2325
|
+
}
|
|
2326
|
+
function toChoice(c) {
|
|
2327
|
+
const label = "label" in c ? c.label : c.name;
|
|
2328
|
+
const hint = Array.isArray(c.hint) ? c.hint.join(", ") : c.hint;
|
|
2329
|
+
return { label, value: String(c.value), hint };
|
|
2330
|
+
}
|
|
2331
|
+
function isRef(r) {
|
|
2332
|
+
return typeof r === "object" && r !== null && "ref" in r;
|
|
2333
|
+
}
|
|
2334
|
+
function toLeaf(name, field, definitions) {
|
|
2335
|
+
let resolver;
|
|
2336
|
+
let extraInput;
|
|
2337
|
+
if (isRef(field.resolver)) {
|
|
2338
|
+
resolver = definitions?.[field.resolver.ref];
|
|
2339
|
+
extraInput = field.resolver.input;
|
|
2340
|
+
} else {
|
|
2341
|
+
resolver = field.resolver;
|
|
2342
|
+
}
|
|
2343
|
+
return {
|
|
2344
|
+
name,
|
|
2345
|
+
required: field.required ?? false,
|
|
2346
|
+
resolver,
|
|
2347
|
+
extraInput,
|
|
2348
|
+
// Carry the field's value type so nested answers coerce (number/boolean)
|
|
2349
|
+
// the same way top-level params do. Without this a nested `z.number()`
|
|
2350
|
+
// field's typed answer stays a string and fails final validation.
|
|
2351
|
+
valueType: field.valueType,
|
|
2352
|
+
requires: resolver?.requireParameters ?? []
|
|
2353
|
+
};
|
|
2354
|
+
}
|
|
2355
|
+
async function objectChildren(resolver, input) {
|
|
2356
|
+
if (resolver.properties) {
|
|
2357
|
+
return Object.entries(resolver.properties).map(
|
|
2358
|
+
([name, field]) => toLeaf(name, field, resolver.definitions)
|
|
2359
|
+
);
|
|
2360
|
+
}
|
|
2361
|
+
if (!resolver.getProperties) return [];
|
|
2362
|
+
const fields = await resolver.getProperties({ input });
|
|
2363
|
+
return Object.entries(fields).map(([name, field]) => {
|
|
2364
|
+
if (!isRef(field.resolver) && (field.resolver.imports?.length ?? 0) > 0) {
|
|
2365
|
+
throw new Error(
|
|
2366
|
+
`dynamic object field "${name}" inlines an import-bearing resolver; use { ref } into the object's definitions so its imports bind`
|
|
2367
|
+
);
|
|
2368
|
+
}
|
|
2369
|
+
return toLeaf(name, field, resolver.definitions);
|
|
2370
|
+
});
|
|
2371
|
+
}
|
|
2372
|
+
function arrayItem(resolver) {
|
|
2373
|
+
const items = resolver.items;
|
|
2374
|
+
const valueType = resolver.itemValueType;
|
|
2375
|
+
if (isRef(items)) {
|
|
2376
|
+
return {
|
|
2377
|
+
name: "",
|
|
2378
|
+
required: true,
|
|
2379
|
+
resolver: resolver.definitions?.[items.ref],
|
|
2380
|
+
extraInput: items.input,
|
|
2381
|
+
valueType,
|
|
2382
|
+
requires: []
|
|
2383
|
+
};
|
|
2384
|
+
}
|
|
2385
|
+
return {
|
|
2386
|
+
name: "",
|
|
2387
|
+
required: true,
|
|
2388
|
+
resolver: items,
|
|
2389
|
+
valueType,
|
|
2390
|
+
requires: []
|
|
2391
|
+
};
|
|
2392
|
+
}
|
|
2393
|
+
function mergeInput(input, extra) {
|
|
2394
|
+
return extra ? { ...input, ...extra } : input;
|
|
2395
|
+
}
|
|
2396
|
+
async function childrenAt(ctx, path, resolved) {
|
|
2397
|
+
if (path.length === 0) return ctx.parameters;
|
|
2398
|
+
const leaf = await leafAt(ctx, path, resolved);
|
|
2399
|
+
if (!leaf?.resolver || leaf.resolver.type !== "object") return [];
|
|
2400
|
+
return objectChildren(leaf.resolver, mergeInput(resolved, leaf.extraInput));
|
|
2401
|
+
}
|
|
2402
|
+
async function leafAt(ctx, path, resolved) {
|
|
2403
|
+
let children = ctx.parameters;
|
|
2404
|
+
let leaf;
|
|
2405
|
+
for (let i = 0; i < path.length; i++) {
|
|
2406
|
+
const seg = path[i];
|
|
2407
|
+
if (typeof seg === "number") {
|
|
2408
|
+
if (leaf?.resolver?.type !== "array") return void 0;
|
|
2409
|
+
leaf = arrayItem(leaf.resolver);
|
|
2410
|
+
} else {
|
|
2411
|
+
leaf = children.find((c) => c.name === seg);
|
|
2412
|
+
if (!leaf) return void 0;
|
|
2413
|
+
}
|
|
2414
|
+
if (i < path.length - 1 && typeof path[i + 1] === "string") {
|
|
2415
|
+
if (leaf?.resolver?.type !== "object") return void 0;
|
|
2416
|
+
children = await objectChildren(
|
|
2417
|
+
leaf.resolver,
|
|
2418
|
+
mergeInput(resolved, leaf.extraInput)
|
|
2419
|
+
);
|
|
2420
|
+
}
|
|
2421
|
+
}
|
|
2422
|
+
return leaf;
|
|
2423
|
+
}
|
|
2424
|
+
async function arrayInfoAt(ctx, path, resolved) {
|
|
2425
|
+
const leaf = await leafAt(ctx, path, resolved);
|
|
2426
|
+
const resolver = leaf?.resolver;
|
|
2427
|
+
if (resolver?.type !== "array") {
|
|
2428
|
+
throw new Error(`expected an array resolver at "${key(path)}"`);
|
|
2429
|
+
}
|
|
2430
|
+
return {
|
|
2431
|
+
min: resolver.minItems ?? 0,
|
|
2432
|
+
max: resolver.maxItems ?? Infinity,
|
|
2433
|
+
item: { ...arrayItem(resolver), name: String(path[path.length - 1]) }
|
|
2434
|
+
};
|
|
2435
|
+
}
|
|
2436
|
+
var AFFORDANCE = {
|
|
2437
|
+
choose: { action: "choose", description: "Pick one of the listed options" },
|
|
2438
|
+
custom: {
|
|
2439
|
+
action: "custom",
|
|
2440
|
+
description: "Provide a value directly",
|
|
2441
|
+
supply: "value"
|
|
2442
|
+
},
|
|
2443
|
+
search: {
|
|
2444
|
+
action: "search",
|
|
2445
|
+
description: "Filter the options by a search term",
|
|
2446
|
+
supply: "term"
|
|
2447
|
+
},
|
|
2448
|
+
more: { action: "more", description: "Load more options" },
|
|
2449
|
+
skip: { action: "skip", description: "Omit this optional parameter" },
|
|
2450
|
+
add: { action: "add", description: "Add another item" },
|
|
2451
|
+
done: { action: "done", description: "Finish the list" },
|
|
2452
|
+
retry: { action: "retry", description: "Retry loading the options" },
|
|
2453
|
+
cancel: { action: "cancel", description: "Cancel resolution" }
|
|
2454
|
+
};
|
|
2455
|
+
async function firstPage(result) {
|
|
2456
|
+
const page = await result;
|
|
2457
|
+
return {
|
|
2458
|
+
data: Array.isArray(page?.data) ? page.data : [],
|
|
2459
|
+
nextCursor: page?.nextCursor
|
|
2460
|
+
};
|
|
2461
|
+
}
|
|
2462
|
+
async function resolveContext(leaf, input) {
|
|
2463
|
+
return leaf.resolver?.getContext?.({
|
|
2464
|
+
input: mergeInput(input, leaf.extraInput)
|
|
2465
|
+
});
|
|
2466
|
+
}
|
|
2467
|
+
async function fetchListing(leaf, input, opts = {}) {
|
|
2468
|
+
const page = await firstPage(
|
|
2469
|
+
leaf.resolver?.listItems?.({
|
|
2470
|
+
input: mergeInput(input, leaf.extraInput),
|
|
2471
|
+
context: opts.context,
|
|
2472
|
+
search: opts.search,
|
|
2473
|
+
cursor: opts.cursor
|
|
2474
|
+
})
|
|
2475
|
+
);
|
|
2476
|
+
return {
|
|
2477
|
+
items: [...opts.priorItems ?? [], ...page.data],
|
|
2478
|
+
cursor: page.nextCursor,
|
|
2479
|
+
search: opts.search,
|
|
2480
|
+
exhausted: page.nextCursor == null
|
|
2481
|
+
};
|
|
2482
|
+
}
|
|
2483
|
+
function selectActions(leaf, listing, multiple) {
|
|
2484
|
+
const actions = multiple ? [AFFORDANCE.choose] : [AFFORDANCE.choose, AFFORDANCE.custom];
|
|
2485
|
+
if (leaf.resolver?.inputType === "search") actions.push(AFFORDANCE.search);
|
|
2486
|
+
if (listing.cursor) actions.push(AFFORDANCE.more);
|
|
2487
|
+
if (!leaf.required) actions.push(AFFORDANCE.skip);
|
|
2488
|
+
return actions;
|
|
2489
|
+
}
|
|
2490
|
+
function selectQuestion(leaf, input, listing, context) {
|
|
2491
|
+
const config = leaf.resolver?.prompt?.({
|
|
2492
|
+
items: listing.items,
|
|
2493
|
+
input: mergeInput(input, leaf.extraInput),
|
|
2494
|
+
context
|
|
2495
|
+
});
|
|
2496
|
+
const multiple = config?.type === "checkbox";
|
|
2497
|
+
return {
|
|
2498
|
+
type: "select",
|
|
2499
|
+
message: config?.message ?? `Select ${leaf.name}:`,
|
|
2500
|
+
choices: (config?.choices ?? []).map(toChoice),
|
|
2501
|
+
...multiple ? { multiple: true } : {},
|
|
2502
|
+
...config?.notes?.length ? { notes: config.notes } : {},
|
|
2503
|
+
actions: selectActions(leaf, listing, multiple)
|
|
2504
|
+
};
|
|
2505
|
+
}
|
|
2506
|
+
function toControllerError(error) {
|
|
2507
|
+
if (error instanceof Error) {
|
|
2508
|
+
const code = error.code;
|
|
2509
|
+
return {
|
|
2510
|
+
name: error.name,
|
|
2511
|
+
message: error.message,
|
|
2512
|
+
...typeof code === "string" ? { code } : {}
|
|
2513
|
+
};
|
|
2514
|
+
}
|
|
2515
|
+
return { name: "Error", message: String(error) };
|
|
2516
|
+
}
|
|
2517
|
+
function failedResult(state, name, error) {
|
|
2518
|
+
return {
|
|
2519
|
+
state,
|
|
2520
|
+
result: {
|
|
2521
|
+
status: "failed",
|
|
2522
|
+
error: toControllerError(error),
|
|
2523
|
+
question: {
|
|
2524
|
+
type: "select",
|
|
2525
|
+
message: `Could not load options for ${name}.`,
|
|
2526
|
+
choices: [],
|
|
2527
|
+
actions: [AFFORDANCE.retry, AFFORDANCE.cancel]
|
|
2528
|
+
}
|
|
2529
|
+
}
|
|
2530
|
+
};
|
|
2531
|
+
}
|
|
2532
|
+
async function buildQuestion(leaf, input) {
|
|
2533
|
+
const optional = !leaf.required;
|
|
2534
|
+
const resolver = leaf.resolver;
|
|
2535
|
+
if (resolver?.listItems) {
|
|
2536
|
+
const context = await resolveContext(leaf, input);
|
|
2537
|
+
const listing = await fetchListing(leaf, input, { context });
|
|
2538
|
+
return {
|
|
2539
|
+
question: selectQuestion(leaf, input, listing, context),
|
|
2540
|
+
listing
|
|
2541
|
+
};
|
|
2542
|
+
}
|
|
2543
|
+
if (leaf.staticChoices) {
|
|
2544
|
+
const actions2 = [AFFORDANCE.choose];
|
|
2545
|
+
if (optional) actions2.push(AFFORDANCE.skip);
|
|
2546
|
+
return {
|
|
2547
|
+
question: {
|
|
2548
|
+
type: "select",
|
|
2549
|
+
message: `Select ${leaf.name}:`,
|
|
2550
|
+
choices: leaf.staticChoices,
|
|
2551
|
+
actions: actions2
|
|
2552
|
+
}
|
|
2553
|
+
};
|
|
2554
|
+
}
|
|
2555
|
+
const inputType = resolver?.inputType && resolver.inputType !== "search" ? resolver.inputType : "text";
|
|
2556
|
+
const actions = [AFFORDANCE.custom];
|
|
2557
|
+
if (optional) actions.push(AFFORDANCE.skip);
|
|
2558
|
+
return {
|
|
2559
|
+
question: {
|
|
2560
|
+
type: "input",
|
|
2561
|
+
message: `Enter ${leaf.name}:`,
|
|
2562
|
+
inputType,
|
|
2563
|
+
placeholder: resolver?.placeholder,
|
|
2564
|
+
actions
|
|
2565
|
+
}
|
|
2566
|
+
};
|
|
2567
|
+
}
|
|
2568
|
+
function finalize(ctx, resolved) {
|
|
2569
|
+
if (!ctx.schema) return { status: "done", value: resolved };
|
|
2570
|
+
const parsed = ctx.schema.safeParse(resolved);
|
|
2571
|
+
if (parsed.success) {
|
|
2572
|
+
return { status: "done", value: parsed.data };
|
|
2573
|
+
}
|
|
2574
|
+
const issues = parsed.error.issues.map((i) => ({
|
|
2575
|
+
parameter: i.path.map(String).join(".") || void 0,
|
|
2576
|
+
message: i.message
|
|
2577
|
+
}));
|
|
2578
|
+
return { status: "invalid", issues };
|
|
2579
|
+
}
|
|
2580
|
+
function collectionQuestion(t) {
|
|
2581
|
+
const actions = [AFFORDANCE.add];
|
|
2582
|
+
if (t.count >= t.min) actions.push(AFFORDANCE.done);
|
|
2583
|
+
return {
|
|
2584
|
+
type: "collection",
|
|
2585
|
+
message: `Add another ${t.path[t.path.length - 1]}? (${t.count} so far)`,
|
|
2586
|
+
count: t.count,
|
|
2587
|
+
min: t.min,
|
|
2588
|
+
// An unbounded array's max is Infinity, which JSON.stringify turns to null;
|
|
2589
|
+
// omit it so the question round-trips across the wall as plain data.
|
|
2590
|
+
...Number.isFinite(t.max) ? { max: t.max } : {},
|
|
2591
|
+
actions
|
|
2592
|
+
};
|
|
2593
|
+
}
|
|
2594
|
+
async function findInArray(ctx, state, path) {
|
|
2595
|
+
if (isSettled(state, path)) return null;
|
|
2596
|
+
if (getAtPath(state.resolved, path) == null)
|
|
2597
|
+
setAtPath(state.resolved, path, []);
|
|
2598
|
+
if (!state.interactive) {
|
|
2599
|
+
settle(state, path);
|
|
2600
|
+
return null;
|
|
2601
|
+
}
|
|
2602
|
+
const { min, max, item } = await arrayInfoAt(ctx, path, state.resolved);
|
|
2603
|
+
const items = getAtPath(state.resolved, path);
|
|
2604
|
+
const len = items.length;
|
|
2605
|
+
const itemType = item.resolver?.type;
|
|
2606
|
+
if (len > 0 && (itemType === "object" || itemType === "array")) {
|
|
2607
|
+
const inner = await findNext(ctx, state, [...path, len - 1]);
|
|
2608
|
+
if (inner) return inner;
|
|
2609
|
+
}
|
|
2610
|
+
if (len < min) return descendItem(ctx, state, path, len, item);
|
|
2611
|
+
if (len < max) return { kind: "array", path, count: len, min, max };
|
|
2612
|
+
settle(state, path);
|
|
2613
|
+
return null;
|
|
2614
|
+
}
|
|
2615
|
+
function seedItemSlot(state, itemPath, item) {
|
|
2616
|
+
const type = item.resolver?.type;
|
|
2617
|
+
if (type === "object") {
|
|
2618
|
+
setAtPath(state.resolved, itemPath, {});
|
|
2619
|
+
return "object";
|
|
2620
|
+
}
|
|
2621
|
+
if (type === "array") {
|
|
2622
|
+
setAtPath(state.resolved, itemPath, []);
|
|
2623
|
+
return "array";
|
|
2624
|
+
}
|
|
2625
|
+
return "leaf";
|
|
2626
|
+
}
|
|
2627
|
+
async function descendItem(ctx, state, arrayPath, index, item) {
|
|
2628
|
+
const itemPath = [...arrayPath, index];
|
|
2629
|
+
const kind = seedItemSlot(state, itemPath, item);
|
|
2630
|
+
if (kind === "object") return findNext(ctx, state, itemPath);
|
|
2631
|
+
if (kind === "array") return findInArray(ctx, state, itemPath);
|
|
2632
|
+
return { kind: "leaf", path: itemPath, leaf: item };
|
|
2633
|
+
}
|
|
2634
|
+
async function findNext(ctx, state, path = []) {
|
|
2635
|
+
const container = getAtPath(state.resolved, path) ?? {};
|
|
2636
|
+
for (const leaf of await childrenAt(ctx, path, state.resolved)) {
|
|
2637
|
+
const childPath = [...path, leaf.name];
|
|
2638
|
+
if (!leaf.requires.every((r) => container[r] !== void 0)) continue;
|
|
2639
|
+
if (leaf.resolver?.type === "object") {
|
|
2640
|
+
if (getAtPath(state.resolved, childPath) == null)
|
|
2641
|
+
setAtPath(state.resolved, childPath, {});
|
|
2642
|
+
const inner = await findNext(ctx, state, childPath);
|
|
2643
|
+
if (inner) return inner;
|
|
2644
|
+
continue;
|
|
2645
|
+
}
|
|
2646
|
+
if (leaf.resolver?.type === "array") {
|
|
2647
|
+
const inner = await findInArray(ctx, state, childPath);
|
|
2648
|
+
if (inner) return inner;
|
|
2649
|
+
continue;
|
|
2650
|
+
}
|
|
2651
|
+
if (container[leaf.name] !== void 0 || isSettled(state, childPath))
|
|
2652
|
+
continue;
|
|
2653
|
+
return { kind: "leaf", path: childPath, leaf };
|
|
2654
|
+
}
|
|
2655
|
+
return null;
|
|
2656
|
+
}
|
|
2657
|
+
async function askLeaf(state, path, leaf, opts = {}) {
|
|
2658
|
+
state.current = path;
|
|
2659
|
+
try {
|
|
2660
|
+
const { question, listing } = await buildQuestion(leaf, state.resolved);
|
|
2661
|
+
state.listing = listing;
|
|
2662
|
+
return {
|
|
2663
|
+
state,
|
|
2664
|
+
result: {
|
|
2665
|
+
status: "ask",
|
|
2666
|
+
question,
|
|
2667
|
+
...opts.error ? { error: opts.error } : {}
|
|
2668
|
+
}
|
|
2669
|
+
};
|
|
2670
|
+
} catch (error) {
|
|
2671
|
+
state.listing = { items: [], exhausted: false };
|
|
2672
|
+
return failedResult(state, leaf.name, error);
|
|
2673
|
+
}
|
|
2674
|
+
}
|
|
2675
|
+
async function advance(ctx, state) {
|
|
2676
|
+
for (; ; ) {
|
|
2677
|
+
const target = await findNext(ctx, state);
|
|
2678
|
+
if (!target) {
|
|
2679
|
+
delete state.current;
|
|
2680
|
+
delete state.listing;
|
|
2681
|
+
return { state, result: finalize(ctx, state.resolved) };
|
|
2682
|
+
}
|
|
2683
|
+
if (target.kind === "array") {
|
|
2684
|
+
state.current = target.path;
|
|
2685
|
+
delete state.listing;
|
|
2686
|
+
return {
|
|
2687
|
+
state,
|
|
2688
|
+
result: { status: "ask", question: collectionQuestion(target) }
|
|
2689
|
+
};
|
|
2690
|
+
}
|
|
2691
|
+
const { path, leaf } = target;
|
|
2692
|
+
const auto = await leaf.resolver?.tryResolveWithoutPrompt?.({
|
|
2693
|
+
input: mergeInput(state.resolved, leaf.extraInput)
|
|
2694
|
+
});
|
|
2695
|
+
if (auto) {
|
|
2696
|
+
if (auto.resolvedValue !== void 0)
|
|
2697
|
+
setAtPath(state.resolved, path, auto.resolvedValue);
|
|
2698
|
+
settle(state, path);
|
|
2699
|
+
continue;
|
|
2700
|
+
}
|
|
2701
|
+
if (!state.interactive) {
|
|
2702
|
+
if (!leaf.required) {
|
|
2703
|
+
settle(state, path);
|
|
2704
|
+
continue;
|
|
2705
|
+
}
|
|
2706
|
+
} else if (!leaf.required && !leaf.resolver) {
|
|
2707
|
+
settle(state, path);
|
|
2708
|
+
continue;
|
|
2709
|
+
}
|
|
2710
|
+
return askLeaf(state, path, leaf);
|
|
2711
|
+
}
|
|
2712
|
+
}
|
|
2713
|
+
async function start(ctx, input = {}, interactive = true) {
|
|
2714
|
+
const resolved = {};
|
|
2715
|
+
for (const spec of ctx.parameters) {
|
|
2716
|
+
if (spec.resolver?.type === "constant")
|
|
2717
|
+
resolved[spec.name] = spec.resolver.value;
|
|
2718
|
+
}
|
|
2719
|
+
Object.assign(resolved, input);
|
|
2720
|
+
return advance(ctx, {
|
|
2721
|
+
method: ctx.method,
|
|
2722
|
+
resolved,
|
|
2723
|
+
settled: [],
|
|
2724
|
+
interactive
|
|
2725
|
+
});
|
|
2726
|
+
}
|
|
2727
|
+
async function step(ctx, prior, action) {
|
|
2728
|
+
const state = clone(prior);
|
|
2729
|
+
if (action.type === "cancel") {
|
|
2730
|
+
delete state.current;
|
|
2731
|
+
delete state.listing;
|
|
2732
|
+
return { state, result: { status: "cancelled" } };
|
|
2733
|
+
}
|
|
2734
|
+
const path = state.current;
|
|
2735
|
+
if (!path) throw new Error("step called with no outstanding question");
|
|
2736
|
+
const leaf = await leafAt(ctx, path, state.resolved);
|
|
2737
|
+
if (leaf && (action.type === "search" || action.type === "more" || action.type === "retry")) {
|
|
2738
|
+
return refine(ctx, state, leaf, path, action);
|
|
2739
|
+
}
|
|
2740
|
+
if (action.type === "add" || action.type === "done") {
|
|
2741
|
+
delete state.current;
|
|
2742
|
+
delete state.listing;
|
|
2743
|
+
if (action.type === "done") {
|
|
2744
|
+
settle(state, path);
|
|
2745
|
+
return advance(ctx, state);
|
|
2746
|
+
}
|
|
2747
|
+
const items = getAtPath(state.resolved, path) ?? [];
|
|
2748
|
+
const { item } = await arrayInfoAt(ctx, path, state.resolved);
|
|
2749
|
+
const itemPath = [...path, items.length];
|
|
2750
|
+
if (seedItemSlot(state, itemPath, item) === "leaf")
|
|
2751
|
+
return askLeaf(state, itemPath, item);
|
|
2752
|
+
return advance(ctx, state);
|
|
2753
|
+
}
|
|
2754
|
+
switch (action.type) {
|
|
2755
|
+
case "choose":
|
|
2756
|
+
case "custom": {
|
|
2757
|
+
if (leaf) {
|
|
2758
|
+
const error = await validationError(leaf, action.value, state);
|
|
2759
|
+
if (error) return askLeaf(state, path, leaf, { error });
|
|
2760
|
+
}
|
|
2761
|
+
setAtPath(
|
|
2762
|
+
state.resolved,
|
|
2763
|
+
path,
|
|
2764
|
+
leaf ? coerce(leaf, action.value) : action.value
|
|
2765
|
+
);
|
|
2766
|
+
break;
|
|
2767
|
+
}
|
|
2768
|
+
case "skip":
|
|
2769
|
+
settle(state, path);
|
|
2770
|
+
break;
|
|
2771
|
+
default:
|
|
2772
|
+
throw new Error(`action "${action.type}" is not supported here`);
|
|
2773
|
+
}
|
|
2774
|
+
delete state.current;
|
|
2775
|
+
delete state.listing;
|
|
2776
|
+
return advance(ctx, state);
|
|
2777
|
+
}
|
|
2778
|
+
async function refine(ctx, state, leaf, path, action) {
|
|
2779
|
+
const attempt = action.type === "search" ? { search: action.term, cursor: void 0, priorItems: [] } : {
|
|
2780
|
+
search: state.listing?.search,
|
|
2781
|
+
cursor: state.listing?.cursor,
|
|
2782
|
+
priorItems: state.listing?.items ?? []
|
|
2783
|
+
};
|
|
2784
|
+
try {
|
|
2785
|
+
if (action.type === "search") {
|
|
2786
|
+
const exact = await leaf.resolver?.tryResolveFromSearch?.({
|
|
2787
|
+
input: mergeInput(state.resolved, leaf.extraInput),
|
|
2788
|
+
search: action.term
|
|
2789
|
+
});
|
|
2790
|
+
if (exact) {
|
|
2791
|
+
setAtPath(state.resolved, path, coerce(leaf, exact.resolvedValue));
|
|
2792
|
+
delete state.current;
|
|
2793
|
+
delete state.listing;
|
|
2794
|
+
return advance(ctx, state);
|
|
2795
|
+
}
|
|
2796
|
+
}
|
|
2797
|
+
const context = await resolveContext(leaf, state.resolved);
|
|
2798
|
+
state.listing = await fetchListing(leaf, state.resolved, {
|
|
2799
|
+
...attempt,
|
|
2800
|
+
context
|
|
2801
|
+
});
|
|
2802
|
+
return {
|
|
2803
|
+
state,
|
|
2804
|
+
result: {
|
|
2805
|
+
status: "ask",
|
|
2806
|
+
question: selectQuestion(leaf, state.resolved, state.listing, context)
|
|
2807
|
+
}
|
|
2808
|
+
};
|
|
2809
|
+
} catch (error) {
|
|
2810
|
+
state.listing = {
|
|
2811
|
+
items: attempt.priorItems,
|
|
2812
|
+
search: attempt.search,
|
|
2813
|
+
cursor: attempt.cursor,
|
|
2814
|
+
exhausted: false
|
|
2815
|
+
};
|
|
2816
|
+
return failedResult(state, leaf.name, error);
|
|
2817
|
+
}
|
|
2818
|
+
}
|
|
2819
|
+
|
|
2820
|
+
// src/model/resolution/controller.ts
|
|
2821
|
+
function toJsonSchema(schema) {
|
|
2822
|
+
if (!schema) return void 0;
|
|
2823
|
+
try {
|
|
2824
|
+
return import_zod4.z.toJSONSchema(schema);
|
|
2825
|
+
} catch {
|
|
2826
|
+
return void 0;
|
|
2827
|
+
}
|
|
2828
|
+
}
|
|
2829
|
+
function projectSummary(entry) {
|
|
2830
|
+
return {
|
|
2831
|
+
name: entry.name,
|
|
2832
|
+
...entry.description ? { description: entry.description } : {},
|
|
2833
|
+
...entry.categories?.length ? { categories: entry.categories } : {}
|
|
2834
|
+
};
|
|
2835
|
+
}
|
|
2836
|
+
function projectMethod(entry) {
|
|
2837
|
+
const inputProperties = toJsonSchema(entry.inputSchema)?.properties;
|
|
2838
|
+
const parameters = {};
|
|
2839
|
+
for (const spec of planParameters(entry).parameters) {
|
|
2840
|
+
parameters[spec.name] = {
|
|
2841
|
+
required: spec.required,
|
|
2842
|
+
dynamic: Boolean(spec.resolver?.listItems),
|
|
2843
|
+
...spec.resolver?.inputType === "search" ? { searchable: true } : {},
|
|
2844
|
+
...inputProperties?.[spec.name] ? { schema: inputProperties[spec.name] } : {},
|
|
2845
|
+
...spec.staticChoices ? { choices: spec.staticChoices } : {},
|
|
2846
|
+
...spec.requires.length ? { requireParameters: spec.requires } : {}
|
|
2847
|
+
};
|
|
2848
|
+
}
|
|
2849
|
+
const output = toJsonSchema(entry.outputSchema);
|
|
2850
|
+
return {
|
|
2851
|
+
name: entry.name,
|
|
2852
|
+
...entry.description ? { description: entry.description } : {},
|
|
2853
|
+
...entry.categories?.length ? { categories: entry.categories } : {},
|
|
2854
|
+
parameters,
|
|
2855
|
+
...entry.positional?.length ? { positional: entry.positional } : {},
|
|
2856
|
+
...output ? { output } : {}
|
|
2857
|
+
};
|
|
2858
|
+
}
|
|
2859
|
+
function createController(sdk) {
|
|
2860
|
+
function entryFor(method) {
|
|
2861
|
+
const entry = sdk.getRegistry().functions.find((f) => f.name === method);
|
|
2862
|
+
if (!entry) throw new Error(`unknown method "${method}"`);
|
|
2863
|
+
return entry;
|
|
2864
|
+
}
|
|
2865
|
+
function contextFor(method) {
|
|
2866
|
+
const entry = entryFor(method);
|
|
2867
|
+
return {
|
|
2868
|
+
method,
|
|
2869
|
+
schema: entry.inputSchema,
|
|
2870
|
+
parameters: planParameters(entry).parameters
|
|
2871
|
+
};
|
|
2872
|
+
}
|
|
2873
|
+
const start2 = ({ method, input, interactive }) => start(contextFor(method), input, interactive);
|
|
2874
|
+
const step2 = ({ state, action }) => step(contextFor(state.method), state, action);
|
|
2875
|
+
const resolve = async ({
|
|
2876
|
+
method,
|
|
2877
|
+
input,
|
|
2878
|
+
answer,
|
|
2879
|
+
interactive
|
|
2880
|
+
}) => {
|
|
2881
|
+
const ctx = contextFor(method);
|
|
2882
|
+
let { state, result } = await start(ctx, input, interactive);
|
|
2883
|
+
while (result.status === "ask" || result.status === "failed") {
|
|
2884
|
+
const action = await answer({ state, result });
|
|
2885
|
+
({ state, result } = await step(ctx, state, action));
|
|
2886
|
+
}
|
|
2887
|
+
if (result.status === "done") return result.value;
|
|
2888
|
+
if (result.status === "cancelled") {
|
|
2889
|
+
throw new CoreCancelledSignal(`resolution cancelled for "${method}"`);
|
|
2890
|
+
}
|
|
2891
|
+
const detail = result.issues.map((i) => i.parameter ? `${i.parameter}: ${i.message}` : i.message).join("; ");
|
|
2892
|
+
throw new Error(`invalid input for "${method}": ${detail}`);
|
|
2893
|
+
};
|
|
2894
|
+
const listMethods = () => ({
|
|
2895
|
+
data: sdk.getRegistry().functions.map(projectSummary)
|
|
2896
|
+
});
|
|
2897
|
+
const getMethod = ({ method }) => ({
|
|
2898
|
+
data: projectMethod(entryFor(method))
|
|
2899
|
+
});
|
|
2900
|
+
const listChoices = async ({
|
|
2901
|
+
method,
|
|
2902
|
+
parameter,
|
|
2903
|
+
input = {},
|
|
2904
|
+
search,
|
|
2905
|
+
cursor
|
|
2906
|
+
}) => {
|
|
2907
|
+
const spec = contextFor(method).parameters.find(
|
|
2908
|
+
(p) => p.name === parameter
|
|
2909
|
+
);
|
|
2910
|
+
if (!spec?.resolver?.listItems) return { data: [] };
|
|
2911
|
+
const context = await spec.resolver.getContext?.({ input });
|
|
2912
|
+
const page = await firstPage(
|
|
2913
|
+
spec.resolver.listItems({ input, context, search, cursor })
|
|
2914
|
+
);
|
|
2915
|
+
const config = spec.resolver.prompt?.({ items: page.data, input, context });
|
|
2916
|
+
const data = (config?.choices ?? []).map(toChoice);
|
|
2917
|
+
return { data, nextCursor: page.nextCursor };
|
|
2918
|
+
};
|
|
2919
|
+
return { resolve, start: start2, step: step2, listMethods, getMethod, listChoices };
|
|
2920
|
+
}
|
|
2921
|
+
|
|
2922
|
+
// src/utils/core-plugin.ts
|
|
2923
|
+
function createCorePlugin(options) {
|
|
2924
|
+
return () => ({
|
|
2925
|
+
context: {
|
|
2926
|
+
core: options
|
|
2927
|
+
}
|
|
2928
|
+
});
|
|
2929
|
+
}
|
|
2930
|
+
|
|
2931
|
+
// src/utils/schema-utils.ts
|
|
2932
|
+
var import_zod5 = require("zod");
|
|
2933
|
+
function getOutputSchema(inputSchema) {
|
|
2934
|
+
return inputSchema._zod.def.outputSchema;
|
|
2935
|
+
}
|
|
2936
|
+
function withOutputSchema(inputSchema, outputSchema) {
|
|
2937
|
+
Object.assign(inputSchema._zod.def, {
|
|
2938
|
+
outputSchema
|
|
2939
|
+
});
|
|
2940
|
+
return inputSchema;
|
|
2941
|
+
}
|
|
2942
|
+
function withResolver(schema, config) {
|
|
2943
|
+
schema._zod.def.resolverMeta = config;
|
|
2944
|
+
return schema;
|
|
2945
|
+
}
|
|
2946
|
+
function getSchemaDescription(schema) {
|
|
2947
|
+
return schema.description;
|
|
2948
|
+
}
|
|
2949
|
+
function getFieldDescriptions(schema) {
|
|
2950
|
+
const descriptions = {};
|
|
2951
|
+
const shape = schema.shape;
|
|
2952
|
+
for (const [key2, fieldSchema] of Object.entries(shape)) {
|
|
2953
|
+
if (fieldSchema instanceof import_zod5.z.ZodType && fieldSchema.description) {
|
|
2954
|
+
descriptions[key2] = fieldSchema.description;
|
|
2955
|
+
}
|
|
2956
|
+
}
|
|
2957
|
+
return descriptions;
|
|
2958
|
+
}
|
|
2959
|
+
function withPositional(schema) {
|
|
2960
|
+
Object.assign(schema._zod.def, {
|
|
2961
|
+
positionalMeta: { positional: true }
|
|
2962
|
+
});
|
|
2963
|
+
return schema;
|
|
2964
|
+
}
|
|
2965
|
+
function schemaHasPositionalMeta(schema) {
|
|
2966
|
+
return "positionalMeta" in schema._zod.def;
|
|
2967
|
+
}
|
|
2968
|
+
function isPositional(schema) {
|
|
2969
|
+
if (schemaHasPositionalMeta(schema) && schema._zod.def.positionalMeta?.positional) {
|
|
2970
|
+
return true;
|
|
2971
|
+
}
|
|
2972
|
+
if (schema instanceof import_zod5.z.ZodOptional) {
|
|
2973
|
+
return isPositional(schema._zod.def.innerType);
|
|
2974
|
+
}
|
|
2975
|
+
if (schema instanceof import_zod5.z.ZodDefault) {
|
|
2976
|
+
return isPositional(schema._zod.def.innerType);
|
|
2977
|
+
}
|
|
2978
|
+
return false;
|
|
2979
|
+
}
|
|
2980
|
+
function openEnum(values, description) {
|
|
2981
|
+
return import_zod5.z.union([import_zod5.z.enum(values), import_zod5.z.string()]).describe(description);
|
|
2982
|
+
}
|
|
2983
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
2984
|
+
0 && (module.exports = {
|
|
2985
|
+
CONTEXT,
|
|
2986
|
+
CORE_ERROR_SYMBOL,
|
|
2987
|
+
CORE_SIGNAL_SYMBOL,
|
|
2988
|
+
CoreCancelledSignal,
|
|
2989
|
+
CoreError,
|
|
2990
|
+
CoreErrorCode,
|
|
2991
|
+
CoreSignal,
|
|
2992
|
+
addPlugin,
|
|
2993
|
+
composePlugins,
|
|
2994
|
+
concatPaginated,
|
|
2995
|
+
createAsyncContext,
|
|
2996
|
+
createController,
|
|
2997
|
+
createCoreError,
|
|
2998
|
+
createCorePlugin,
|
|
2999
|
+
createDeprecationLogger,
|
|
3000
|
+
createFunction,
|
|
3001
|
+
createPaginatedFunction,
|
|
3002
|
+
createPaginatedPluginMethod,
|
|
3003
|
+
createPluginMethod,
|
|
3004
|
+
createPluginStack,
|
|
3005
|
+
createPrefixedCursor,
|
|
3006
|
+
createSdk,
|
|
3007
|
+
createValidator,
|
|
3008
|
+
dangerousContextPlugin,
|
|
3009
|
+
declareMethod,
|
|
3010
|
+
declarePlugin,
|
|
3011
|
+
declareProperty,
|
|
3012
|
+
decodeIncomingCursor,
|
|
3013
|
+
defineFormatter,
|
|
3014
|
+
defineLegacyMerge,
|
|
3015
|
+
defineMethod,
|
|
3016
|
+
definePlugin,
|
|
3017
|
+
defineProperty,
|
|
3018
|
+
defineResolver,
|
|
3019
|
+
fromFunctionPlugin,
|
|
3020
|
+
getContext,
|
|
3021
|
+
getCoreErrorCause,
|
|
3022
|
+
getCoreErrorCode,
|
|
3023
|
+
getCurrentDepth,
|
|
3024
|
+
getCurrentScope,
|
|
3025
|
+
getFieldDescriptions,
|
|
3026
|
+
getOutputSchema,
|
|
3027
|
+
getRegistryPlugin,
|
|
3028
|
+
getSchemaDescription,
|
|
3029
|
+
isCoreError,
|
|
3030
|
+
isCoreSignal,
|
|
3031
|
+
isNestedMethodCall,
|
|
3032
|
+
isPositional,
|
|
3033
|
+
isTelemetryNested,
|
|
3034
|
+
openEnum,
|
|
3035
|
+
paginate,
|
|
3036
|
+
paginateBuffered,
|
|
3037
|
+
paginateMaxItems,
|
|
3038
|
+
runInMethodScope,
|
|
3039
|
+
runWithTelemetryContext,
|
|
3040
|
+
selectExports,
|
|
3041
|
+
splitPrefixedCursor,
|
|
3042
|
+
toIterable,
|
|
3043
|
+
toSnakeCase,
|
|
3044
|
+
toTitleCase,
|
|
3045
|
+
validateOptions,
|
|
3046
|
+
withOutputSchema,
|
|
3047
|
+
withPositional,
|
|
3048
|
+
withResolver
|
|
3049
|
+
});
|
|
3050
|
+
//# sourceMappingURL=index.cjs.map
|