capman 0.5.1 → 0.5.3
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 +57 -0
- package/CODEBASE.md +2 -1
- package/bin/lib/cmd-demo.js +2 -2
- package/dist/cjs/cache.d.ts +4 -1
- package/dist/cjs/cache.d.ts.map +1 -1
- package/dist/cjs/cache.js +42 -20
- package/dist/cjs/cache.js.map +1 -1
- package/dist/cjs/engine.d.ts +3 -1
- package/dist/cjs/engine.d.ts.map +1 -1
- package/dist/cjs/engine.js +103 -34
- package/dist/cjs/engine.js.map +1 -1
- package/dist/cjs/generator.d.ts.map +1 -1
- package/dist/cjs/generator.js +16 -1
- package/dist/cjs/generator.js.map +1 -1
- package/dist/cjs/index.d.ts +3 -2
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +3 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/learning.d.ts +20 -11
- package/dist/cjs/learning.d.ts.map +1 -1
- package/dist/cjs/learning.js +169 -135
- package/dist/cjs/learning.js.map +1 -1
- package/dist/cjs/matcher.d.ts +4 -1
- package/dist/cjs/matcher.d.ts.map +1 -1
- package/dist/cjs/matcher.js +33 -13
- package/dist/cjs/matcher.js.map +1 -1
- package/dist/cjs/parser.js +10 -4
- package/dist/cjs/parser.js.map +1 -1
- package/dist/cjs/resolver.d.ts +7 -0
- package/dist/cjs/resolver.d.ts.map +1 -1
- package/dist/cjs/resolver.js +63 -19
- package/dist/cjs/resolver.js.map +1 -1
- package/dist/cjs/schema.d.ts +106 -14
- package/dist/cjs/schema.d.ts.map +1 -1
- package/dist/cjs/schema.js +9 -4
- package/dist/cjs/schema.js.map +1 -1
- package/dist/cjs/types.d.ts +1 -0
- package/dist/cjs/types.d.ts.map +1 -1
- package/dist/cjs/version.d.ts +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/esm/cache.d.ts +4 -1
- package/dist/esm/cache.js +42 -20
- package/dist/esm/engine.d.ts +3 -1
- package/dist/esm/engine.js +104 -35
- package/dist/esm/generator.js +16 -1
- package/dist/esm/index.d.ts +3 -2
- package/dist/esm/index.js +1 -0
- package/dist/esm/learning.d.ts +20 -11
- package/dist/esm/learning.js +169 -135
- package/dist/esm/matcher.d.ts +4 -1
- package/dist/esm/matcher.js +31 -12
- package/dist/esm/parser.js +10 -4
- package/dist/esm/resolver.d.ts +7 -0
- package/dist/esm/resolver.js +63 -19
- package/dist/esm/schema.d.ts +106 -14
- package/dist/esm/schema.js +9 -4
- package/dist/esm/types.d.ts +1 -0
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/package.json +1 -1
package/dist/esm/resolver.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { logger } from './logger';
|
|
2
|
+
// ─── Constants ────────────────────────────────────────────────────────────────
|
|
3
|
+
const SAFE_METHODS = new Set(['GET', 'HEAD', 'OPTIONS']);
|
|
2
4
|
function redactParams(params) {
|
|
3
5
|
return Object.fromEntries(Object.entries(params).map(([k, v]) => [k, v != null ? '[REDACTED]' : 'null']));
|
|
4
6
|
}
|
|
@@ -49,12 +51,8 @@ export async function resolve(matchResult, params = {}, options = {}) {
|
|
|
49
51
|
// they must never leak into the query string as ?user_id=xyz
|
|
50
52
|
const enrichedParams = { ...params };
|
|
51
53
|
if (options.auth?.userId !== undefined && options.auth.userId !== '') {
|
|
52
|
-
const resolver = capability.resolver;
|
|
53
|
-
const pathTemplate = resolver.type === 'api' ? resolver.endpoints.map(e => e.path).join('') :
|
|
54
|
-
resolver.type === 'hybrid' ? resolver.api.endpoints.map(e => e.path).join('') :
|
|
55
|
-
resolver.type === 'nav' ? resolver.destination : '';
|
|
56
54
|
for (const param of capability.params) {
|
|
57
|
-
if (param.source === 'session'
|
|
55
|
+
if (param.source === 'session') {
|
|
58
56
|
enrichedParams[param.name] = options.auth.userId;
|
|
59
57
|
logger.debug(`Injected session param "${param.name}" (value redacted)`);
|
|
60
58
|
}
|
|
@@ -65,15 +63,18 @@ export async function resolve(matchResult, params = {}, options = {}) {
|
|
|
65
63
|
logger.debug(`Params: ${JSON.stringify(redactParams(params))}`);
|
|
66
64
|
logger.debug(`Options: baseUrl=${options.baseUrl} dryRun=${options.dryRun}`);
|
|
67
65
|
try {
|
|
66
|
+
const sessionParamNames = new Set(capability.params
|
|
67
|
+
.filter(p => p.source === 'session')
|
|
68
|
+
.map(p => p.name));
|
|
68
69
|
switch (resolver.type) {
|
|
69
70
|
case 'api':
|
|
70
|
-
return await resolveApi(resolver, enrichedParams, options);
|
|
71
|
+
return await resolveApi(resolver, enrichedParams, options, sessionParamNames);
|
|
71
72
|
case 'nav':
|
|
72
73
|
return resolveNav(resolver, enrichedParams);
|
|
73
74
|
case 'hybrid': {
|
|
74
75
|
logger.debug('Hybrid resolver — running API and nav in parallel');
|
|
75
76
|
const [apiResult, navResult] = await Promise.all([
|
|
76
|
-
resolveApi(resolver.api, enrichedParams, options),
|
|
77
|
+
resolveApi(resolver.api, enrichedParams, options, sessionParamNames),
|
|
77
78
|
Promise.resolve(resolveNav(resolver.nav, enrichedParams)),
|
|
78
79
|
]);
|
|
79
80
|
return {
|
|
@@ -95,15 +96,40 @@ export async function resolve(matchResult, params = {}, options = {}) {
|
|
|
95
96
|
};
|
|
96
97
|
}
|
|
97
98
|
}
|
|
98
|
-
|
|
99
|
+
/**
|
|
100
|
+
* Resolves an API capability by executing all configured endpoints.
|
|
101
|
+
*
|
|
102
|
+
* ⚠️ PARALLEL EXECUTION: All endpoints are fired simultaneously via Promise.all().
|
|
103
|
+
* If any endpoint fails, the entire result is marked as failed and partial results
|
|
104
|
+
* are discarded — but side effects from successful endpoints cannot be rolled back.
|
|
105
|
+
*
|
|
106
|
+
* Example: a capability with two endpoints [POST /reserve, POST /confirm] will
|
|
107
|
+
* fire both in parallel. If /confirm fails after /reserve succeeded, the reservation
|
|
108
|
+
* exists but the caller receives success: false with no indication that /reserve ran.
|
|
109
|
+
*
|
|
110
|
+
* For capabilities where ordering or rollback matters, define separate capabilities
|
|
111
|
+
* with single endpoints and orchestrate them at the application layer.
|
|
112
|
+
*/
|
|
113
|
+
async function resolveApi(resolver, params, options, sessionParamNames = new Set()) {
|
|
99
114
|
const startTime = Date.now();
|
|
100
115
|
const retries = options.retries ?? 0;
|
|
101
116
|
const timeoutMs = options.timeoutMs ?? 5000;
|
|
102
|
-
const apiCalls = resolver.endpoints.map(endpoint =>
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
117
|
+
const apiCalls = resolver.endpoints.map(endpoint => {
|
|
118
|
+
// Build per-endpoint params — only inject session params if this
|
|
119
|
+
// specific endpoint has the placeholder. Prevents userId leaking
|
|
120
|
+
// as ?user_id=xyz on endpoints that don't use it in their path.
|
|
121
|
+
const endpointParams = { ...params };
|
|
122
|
+
for (const name of sessionParamNames) {
|
|
123
|
+
if (!endpoint.path.includes(`{${name}}`)) {
|
|
124
|
+
delete endpointParams[name]; // strip session param — not in this endpoint's path
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return {
|
|
128
|
+
method: endpoint.method,
|
|
129
|
+
url: buildUrl(options.baseUrl ?? '', endpoint.path, endpointParams),
|
|
130
|
+
params: Object.fromEntries(Object.entries(endpointParams).filter(([, v]) => v !== null && v !== undefined)),
|
|
131
|
+
};
|
|
132
|
+
});
|
|
107
133
|
if (options.dryRun) {
|
|
108
134
|
return { success: true, resolverType: 'api', apiCalls, durationMs: Date.now() - startTime };
|
|
109
135
|
}
|
|
@@ -116,9 +142,14 @@ async function resolveApi(resolver, params, options) {
|
|
|
116
142
|
};
|
|
117
143
|
}
|
|
118
144
|
// ── Fetch with retry + timeout (iterative — no recursion) ────────────────
|
|
145
|
+
// Only retry safe/idempotent methods — retrying POST/PUT/PATCH/DELETE
|
|
146
|
+
// can cause duplicate side effects (e.g. duplicate orders, double charges).
|
|
119
147
|
async function fetchWithRetry(call) {
|
|
148
|
+
const effectiveRetries = (options.retryAllMethods || SAFE_METHODS.has(call.method))
|
|
149
|
+
? retries
|
|
150
|
+
: 0;
|
|
120
151
|
let lastErr;
|
|
121
|
-
for (let attempt = 0; attempt <=
|
|
152
|
+
for (let attempt = 0; attempt <= effectiveRetries; attempt++) {
|
|
122
153
|
const controller = new AbortController();
|
|
123
154
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
124
155
|
try {
|
|
@@ -127,7 +158,7 @@ async function resolveApi(resolver, params, options) {
|
|
|
127
158
|
headers: options.headers ?? {},
|
|
128
159
|
signal: controller.signal,
|
|
129
160
|
body: ['POST', 'PUT', 'PATCH'].includes(call.method)
|
|
130
|
-
? JSON.stringify(call.params)
|
|
161
|
+
? JSON.stringify(Object.fromEntries(Object.entries(call.params).filter(([, v]) => v !== null && v !== undefined)))
|
|
131
162
|
: undefined,
|
|
132
163
|
});
|
|
133
164
|
clearTimeout(timer);
|
|
@@ -137,8 +168,8 @@ async function resolveApi(resolver, params, options) {
|
|
|
137
168
|
clearTimeout(timer);
|
|
138
169
|
lastErr = err;
|
|
139
170
|
const isTimeout = err instanceof Error && err.name === 'AbortError';
|
|
140
|
-
if (attempt <
|
|
141
|
-
logger.warn(`Request failed (attempt ${attempt + 1}/${
|
|
171
|
+
if (attempt < effectiveRetries) {
|
|
172
|
+
logger.warn(`Request failed (attempt ${attempt + 1}/${effectiveRetries + 1}) — retrying: ${isTimeout ? 'timeout' : err}`);
|
|
142
173
|
}
|
|
143
174
|
else {
|
|
144
175
|
throw isTimeout ? new Error(`Request timed out after ${timeoutMs}ms`) : err;
|
|
@@ -191,10 +222,21 @@ function resolveNav(resolver, params) {
|
|
|
191
222
|
continue;
|
|
192
223
|
const str = String(value);
|
|
193
224
|
validateNavParam(key, str);
|
|
194
|
-
destination = destination.
|
|
225
|
+
destination = destination.replaceAll(`{${key}}`, encodeURIComponent(str));
|
|
195
226
|
}
|
|
196
227
|
return { success: true, resolverType: 'nav', navTarget: destination };
|
|
197
228
|
}
|
|
229
|
+
function validateApiPathParam(key, value) {
|
|
230
|
+
// Prevent path traversal via unencoded slashes — encodeURIComponent does not
|
|
231
|
+
// encode '/' so a value like '../../admin' would traverse the path hierarchy.
|
|
232
|
+
// This mirrors the allowlist validation already applied in resolveNav().
|
|
233
|
+
if (!/^[a-zA-Z0-9_\-.:@]+$/.test(value)) {
|
|
234
|
+
throw new Error(`API path param "${key}" contains invalid characters: "${value}". ` +
|
|
235
|
+
`Only alphanumeric, hyphens, underscores, dots, colons, and @ are allowed.`);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
// Both buildUrl (API) and resolveNav (nav) validate path param values against
|
|
239
|
+
// an allowlist before substitution — prevents path traversal via unencoded slashes.
|
|
198
240
|
function buildUrl(baseUrl, urlPath, params) {
|
|
199
241
|
let resolved = urlPath;
|
|
200
242
|
const unused = {};
|
|
@@ -202,7 +244,9 @@ function buildUrl(baseUrl, urlPath, params) {
|
|
|
202
244
|
if (value === null || value === undefined)
|
|
203
245
|
continue; // never write null into URLs
|
|
204
246
|
if (resolved.includes(`{${key}}`)) {
|
|
205
|
-
|
|
247
|
+
const str = String(value);
|
|
248
|
+
validateApiPathParam(key, str);
|
|
249
|
+
resolved = resolved.replaceAll(`{${key}}`, encodeURIComponent(str));
|
|
206
250
|
}
|
|
207
251
|
else {
|
|
208
252
|
unused[key] = value;
|
package/dist/esm/schema.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
export declare const CapmanConfigSchema: z.ZodObject<{
|
|
2
|
+
export declare const CapmanConfigSchema: z.ZodEffects<z.ZodObject<{
|
|
3
3
|
app: z.ZodString;
|
|
4
4
|
baseUrl: z.ZodOptional<z.ZodString>;
|
|
5
5
|
capabilities: z.ZodEffects<z.ZodArray<z.ZodObject<{
|
|
@@ -108,6 +108,7 @@ export declare const CapmanConfigSchema: z.ZodObject<{
|
|
|
108
108
|
hint?: string | undefined;
|
|
109
109
|
}>;
|
|
110
110
|
}, "strip", z.ZodTypeAny, {
|
|
111
|
+
type: "hybrid";
|
|
111
112
|
api: {
|
|
112
113
|
endpoints: {
|
|
113
114
|
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
@@ -119,8 +120,8 @@ export declare const CapmanConfigSchema: z.ZodObject<{
|
|
|
119
120
|
destination: string;
|
|
120
121
|
hint?: string | undefined;
|
|
121
122
|
};
|
|
122
|
-
type: "hybrid";
|
|
123
123
|
}, {
|
|
124
|
+
type: "hybrid";
|
|
124
125
|
api: {
|
|
125
126
|
endpoints: {
|
|
126
127
|
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
@@ -132,7 +133,6 @@ export declare const CapmanConfigSchema: z.ZodObject<{
|
|
|
132
133
|
destination: string;
|
|
133
134
|
hint?: string | undefined;
|
|
134
135
|
};
|
|
135
|
-
type: "hybrid";
|
|
136
136
|
}>]>;
|
|
137
137
|
privacy: z.ZodObject<{
|
|
138
138
|
level: z.ZodEnum<["public", "user_owned", "admin"]>;
|
|
@@ -168,6 +168,7 @@ export declare const CapmanConfigSchema: z.ZodObject<{
|
|
|
168
168
|
destination: string;
|
|
169
169
|
hint?: string | undefined;
|
|
170
170
|
} | {
|
|
171
|
+
type: "hybrid";
|
|
171
172
|
api: {
|
|
172
173
|
endpoints: {
|
|
173
174
|
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
@@ -179,7 +180,6 @@ export declare const CapmanConfigSchema: z.ZodObject<{
|
|
|
179
180
|
destination: string;
|
|
180
181
|
hint?: string | undefined;
|
|
181
182
|
};
|
|
182
|
-
type: "hybrid";
|
|
183
183
|
};
|
|
184
184
|
privacy: {
|
|
185
185
|
level: "public" | "user_owned" | "admin";
|
|
@@ -210,6 +210,7 @@ export declare const CapmanConfigSchema: z.ZodObject<{
|
|
|
210
210
|
destination: string;
|
|
211
211
|
hint?: string | undefined;
|
|
212
212
|
} | {
|
|
213
|
+
type: "hybrid";
|
|
213
214
|
api: {
|
|
214
215
|
endpoints: {
|
|
215
216
|
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
@@ -221,7 +222,6 @@ export declare const CapmanConfigSchema: z.ZodObject<{
|
|
|
221
222
|
destination: string;
|
|
222
223
|
hint?: string | undefined;
|
|
223
224
|
};
|
|
224
|
-
type: "hybrid";
|
|
225
225
|
};
|
|
226
226
|
privacy: {
|
|
227
227
|
level: "public" | "user_owned" | "admin";
|
|
@@ -252,6 +252,7 @@ export declare const CapmanConfigSchema: z.ZodObject<{
|
|
|
252
252
|
destination: string;
|
|
253
253
|
hint?: string | undefined;
|
|
254
254
|
} | {
|
|
255
|
+
type: "hybrid";
|
|
255
256
|
api: {
|
|
256
257
|
endpoints: {
|
|
257
258
|
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
@@ -263,7 +264,6 @@ export declare const CapmanConfigSchema: z.ZodObject<{
|
|
|
263
264
|
destination: string;
|
|
264
265
|
hint?: string | undefined;
|
|
265
266
|
};
|
|
266
|
-
type: "hybrid";
|
|
267
267
|
};
|
|
268
268
|
privacy: {
|
|
269
269
|
level: "public" | "user_owned" | "admin";
|
|
@@ -294,6 +294,7 @@ export declare const CapmanConfigSchema: z.ZodObject<{
|
|
|
294
294
|
destination: string;
|
|
295
295
|
hint?: string | undefined;
|
|
296
296
|
} | {
|
|
297
|
+
type: "hybrid";
|
|
297
298
|
api: {
|
|
298
299
|
endpoints: {
|
|
299
300
|
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
@@ -305,7 +306,6 @@ export declare const CapmanConfigSchema: z.ZodObject<{
|
|
|
305
306
|
destination: string;
|
|
306
307
|
hint?: string | undefined;
|
|
307
308
|
};
|
|
308
|
-
type: "hybrid";
|
|
309
309
|
};
|
|
310
310
|
privacy: {
|
|
311
311
|
level: "public" | "user_owned" | "admin";
|
|
@@ -339,6 +339,7 @@ export declare const CapmanConfigSchema: z.ZodObject<{
|
|
|
339
339
|
destination: string;
|
|
340
340
|
hint?: string | undefined;
|
|
341
341
|
} | {
|
|
342
|
+
type: "hybrid";
|
|
342
343
|
api: {
|
|
343
344
|
endpoints: {
|
|
344
345
|
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
@@ -350,7 +351,6 @@ export declare const CapmanConfigSchema: z.ZodObject<{
|
|
|
350
351
|
destination: string;
|
|
351
352
|
hint?: string | undefined;
|
|
352
353
|
};
|
|
353
|
-
type: "hybrid";
|
|
354
354
|
};
|
|
355
355
|
privacy: {
|
|
356
356
|
level: "public" | "user_owned" | "admin";
|
|
@@ -385,6 +385,7 @@ export declare const CapmanConfigSchema: z.ZodObject<{
|
|
|
385
385
|
destination: string;
|
|
386
386
|
hint?: string | undefined;
|
|
387
387
|
} | {
|
|
388
|
+
type: "hybrid";
|
|
388
389
|
api: {
|
|
389
390
|
endpoints: {
|
|
390
391
|
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
@@ -396,7 +397,98 @@ export declare const CapmanConfigSchema: z.ZodObject<{
|
|
|
396
397
|
destination: string;
|
|
397
398
|
hint?: string | undefined;
|
|
398
399
|
};
|
|
400
|
+
};
|
|
401
|
+
privacy: {
|
|
402
|
+
level: "public" | "user_owned" | "admin";
|
|
403
|
+
note?: string | undefined;
|
|
404
|
+
};
|
|
405
|
+
examples?: string[] | undefined;
|
|
406
|
+
}[];
|
|
407
|
+
baseUrl?: string | undefined;
|
|
408
|
+
}>, {
|
|
409
|
+
app: string;
|
|
410
|
+
capabilities: {
|
|
411
|
+
name: string;
|
|
412
|
+
id: string;
|
|
413
|
+
params: {
|
|
414
|
+
name: string;
|
|
415
|
+
required: boolean;
|
|
416
|
+
description: string;
|
|
417
|
+
source: "user_query" | "session" | "context" | "static";
|
|
418
|
+
default?: string | number | boolean | undefined;
|
|
419
|
+
}[];
|
|
420
|
+
description: string;
|
|
421
|
+
returns: string[];
|
|
422
|
+
resolver: {
|
|
423
|
+
type: "api";
|
|
424
|
+
endpoints: {
|
|
425
|
+
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
426
|
+
path: string;
|
|
427
|
+
params?: string[] | undefined;
|
|
428
|
+
}[];
|
|
429
|
+
} | {
|
|
430
|
+
type: "nav";
|
|
431
|
+
destination: string;
|
|
432
|
+
hint?: string | undefined;
|
|
433
|
+
} | {
|
|
399
434
|
type: "hybrid";
|
|
435
|
+
api: {
|
|
436
|
+
endpoints: {
|
|
437
|
+
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
438
|
+
path: string;
|
|
439
|
+
params?: string[] | undefined;
|
|
440
|
+
}[];
|
|
441
|
+
};
|
|
442
|
+
nav: {
|
|
443
|
+
destination: string;
|
|
444
|
+
hint?: string | undefined;
|
|
445
|
+
};
|
|
446
|
+
};
|
|
447
|
+
privacy: {
|
|
448
|
+
level: "public" | "user_owned" | "admin";
|
|
449
|
+
note?: string | undefined;
|
|
450
|
+
};
|
|
451
|
+
examples?: string[] | undefined;
|
|
452
|
+
}[];
|
|
453
|
+
baseUrl?: string | undefined;
|
|
454
|
+
}, {
|
|
455
|
+
app: string;
|
|
456
|
+
capabilities: {
|
|
457
|
+
name: string;
|
|
458
|
+
id: string;
|
|
459
|
+
params: {
|
|
460
|
+
name: string;
|
|
461
|
+
required: boolean;
|
|
462
|
+
description: string;
|
|
463
|
+
source: "user_query" | "session" | "context" | "static";
|
|
464
|
+
default?: string | number | boolean | undefined;
|
|
465
|
+
}[];
|
|
466
|
+
description: string;
|
|
467
|
+
returns: string[];
|
|
468
|
+
resolver: {
|
|
469
|
+
type: "api";
|
|
470
|
+
endpoints: {
|
|
471
|
+
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
472
|
+
path: string;
|
|
473
|
+
params?: string[] | undefined;
|
|
474
|
+
}[];
|
|
475
|
+
} | {
|
|
476
|
+
type: "nav";
|
|
477
|
+
destination: string;
|
|
478
|
+
hint?: string | undefined;
|
|
479
|
+
} | {
|
|
480
|
+
type: "hybrid";
|
|
481
|
+
api: {
|
|
482
|
+
endpoints: {
|
|
483
|
+
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
484
|
+
path: string;
|
|
485
|
+
params?: string[] | undefined;
|
|
486
|
+
}[];
|
|
487
|
+
};
|
|
488
|
+
nav: {
|
|
489
|
+
destination: string;
|
|
490
|
+
hint?: string | undefined;
|
|
491
|
+
};
|
|
400
492
|
};
|
|
401
493
|
privacy: {
|
|
402
494
|
level: "public" | "user_owned" | "admin";
|
|
@@ -516,6 +608,7 @@ export declare const ManifestSchema: z.ZodObject<{
|
|
|
516
608
|
hint?: string | undefined;
|
|
517
609
|
}>;
|
|
518
610
|
}, "strip", z.ZodTypeAny, {
|
|
611
|
+
type: "hybrid";
|
|
519
612
|
api: {
|
|
520
613
|
endpoints: {
|
|
521
614
|
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
@@ -527,8 +620,8 @@ export declare const ManifestSchema: z.ZodObject<{
|
|
|
527
620
|
destination: string;
|
|
528
621
|
hint?: string | undefined;
|
|
529
622
|
};
|
|
530
|
-
type: "hybrid";
|
|
531
623
|
}, {
|
|
624
|
+
type: "hybrid";
|
|
532
625
|
api: {
|
|
533
626
|
endpoints: {
|
|
534
627
|
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
@@ -540,7 +633,6 @@ export declare const ManifestSchema: z.ZodObject<{
|
|
|
540
633
|
destination: string;
|
|
541
634
|
hint?: string | undefined;
|
|
542
635
|
};
|
|
543
|
-
type: "hybrid";
|
|
544
636
|
}>]>;
|
|
545
637
|
privacy: z.ZodObject<{
|
|
546
638
|
level: z.ZodEnum<["public", "user_owned", "admin"]>;
|
|
@@ -576,6 +668,7 @@ export declare const ManifestSchema: z.ZodObject<{
|
|
|
576
668
|
destination: string;
|
|
577
669
|
hint?: string | undefined;
|
|
578
670
|
} | {
|
|
671
|
+
type: "hybrid";
|
|
579
672
|
api: {
|
|
580
673
|
endpoints: {
|
|
581
674
|
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
@@ -587,7 +680,6 @@ export declare const ManifestSchema: z.ZodObject<{
|
|
|
587
680
|
destination: string;
|
|
588
681
|
hint?: string | undefined;
|
|
589
682
|
};
|
|
590
|
-
type: "hybrid";
|
|
591
683
|
};
|
|
592
684
|
privacy: {
|
|
593
685
|
level: "public" | "user_owned" | "admin";
|
|
@@ -618,6 +710,7 @@ export declare const ManifestSchema: z.ZodObject<{
|
|
|
618
710
|
destination: string;
|
|
619
711
|
hint?: string | undefined;
|
|
620
712
|
} | {
|
|
713
|
+
type: "hybrid";
|
|
621
714
|
api: {
|
|
622
715
|
endpoints: {
|
|
623
716
|
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
@@ -629,7 +722,6 @@ export declare const ManifestSchema: z.ZodObject<{
|
|
|
629
722
|
destination: string;
|
|
630
723
|
hint?: string | undefined;
|
|
631
724
|
};
|
|
632
|
-
type: "hybrid";
|
|
633
725
|
};
|
|
634
726
|
privacy: {
|
|
635
727
|
level: "public" | "user_owned" | "admin";
|
|
@@ -664,6 +756,7 @@ export declare const ManifestSchema: z.ZodObject<{
|
|
|
664
756
|
destination: string;
|
|
665
757
|
hint?: string | undefined;
|
|
666
758
|
} | {
|
|
759
|
+
type: "hybrid";
|
|
667
760
|
api: {
|
|
668
761
|
endpoints: {
|
|
669
762
|
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
@@ -675,7 +768,6 @@ export declare const ManifestSchema: z.ZodObject<{
|
|
|
675
768
|
destination: string;
|
|
676
769
|
hint?: string | undefined;
|
|
677
770
|
};
|
|
678
|
-
type: "hybrid";
|
|
679
771
|
};
|
|
680
772
|
privacy: {
|
|
681
773
|
level: "public" | "user_owned" | "admin";
|
|
@@ -711,6 +803,7 @@ export declare const ManifestSchema: z.ZodObject<{
|
|
|
711
803
|
destination: string;
|
|
712
804
|
hint?: string | undefined;
|
|
713
805
|
} | {
|
|
806
|
+
type: "hybrid";
|
|
714
807
|
api: {
|
|
715
808
|
endpoints: {
|
|
716
809
|
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
@@ -722,7 +815,6 @@ export declare const ManifestSchema: z.ZodObject<{
|
|
|
722
815
|
destination: string;
|
|
723
816
|
hint?: string | undefined;
|
|
724
817
|
};
|
|
725
|
-
type: "hybrid";
|
|
726
818
|
};
|
|
727
819
|
privacy: {
|
|
728
820
|
level: "public" | "user_owned" | "admin";
|
package/dist/esm/schema.js
CHANGED
|
@@ -50,8 +50,10 @@ const CapabilitySchema = z.object({
|
|
|
50
50
|
id: z.string().min(1, 'capability id is required')
|
|
51
51
|
.regex(/^[a-z0-9_]+$/, 'id must be snake_case (lowercase, numbers, underscores only)'),
|
|
52
52
|
name: z.string().min(1, 'capability name is required'),
|
|
53
|
-
description: z.string()
|
|
54
|
-
|
|
53
|
+
description: z.string()
|
|
54
|
+
.min(10, 'description must be at least 10 characters for accurate matching')
|
|
55
|
+
.max(500, 'description must be 500 characters or fewer'),
|
|
56
|
+
examples: z.array(z.string().max(200, 'each example must be 200 characters or fewer')).optional(),
|
|
55
57
|
params: z.array(CapabilityParamSchema),
|
|
56
58
|
returns: z.array(z.string()),
|
|
57
59
|
resolver: ResolverSchema,
|
|
@@ -60,11 +62,14 @@ const CapabilitySchema = z.object({
|
|
|
60
62
|
// ─── Config Schema ────────────────────────────────────────────────────────────
|
|
61
63
|
export const CapmanConfigSchema = z.object({
|
|
62
64
|
app: z.string().min(1, 'app name is required'),
|
|
63
|
-
baseUrl: z.string().url(
|
|
65
|
+
baseUrl: z.string().url().optional(),
|
|
64
66
|
capabilities: z.array(CapabilitySchema)
|
|
65
67
|
.min(1, 'at least one capability is required')
|
|
66
68
|
.refine(caps => new Set(caps.map(c => c.id)).size === caps.length, 'capability ids must be unique'),
|
|
67
|
-
})
|
|
69
|
+
}).refine(cfg => {
|
|
70
|
+
const needsBaseUrl = cfg.capabilities.some(c => c.resolver.type === 'api' || c.resolver.type === 'hybrid');
|
|
71
|
+
return !needsBaseUrl || !!cfg.baseUrl;
|
|
72
|
+
}, { message: 'baseUrl is required when any capability uses an api or hybrid resolver' });
|
|
68
73
|
// ─── Manifest Schema ──────────────────────────────────────────────────────────
|
|
69
74
|
export const ManifestSchema = z.object({
|
|
70
75
|
version: z.string(),
|
package/dist/esm/types.d.ts
CHANGED
package/dist/esm/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "0.5.
|
|
1
|
+
export declare const VERSION = "0.5.3";
|
package/dist/esm/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Auto-generated by scripts/version.js — do not edit manually
|
|
2
|
-
export const VERSION = '0.5.
|
|
2
|
+
export const VERSION = '0.5.3';
|
package/package.json
CHANGED