@x12i/api-hub-core 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/capability.d.ts +11 -0
- package/dist/capability.js +15 -0
- package/dist/capability.js.map +1 -1
- package/dist/openapi.js +4 -0
- package/dist/openapi.js.map +1 -1
- package/dist/passthrough.d.ts +11 -3
- package/dist/passthrough.js +45 -20
- package/dist/passthrough.js.map +1 -1
- package/dist/registry-types.d.ts +6 -0
- package/dist/registry-types.js.map +1 -1
- package/dist/registry.d.ts +6 -1
- package/dist/registry.js +6 -3
- package/dist/registry.js.map +1 -1
- package/package.json +1 -1
package/dist/capability.d.ts
CHANGED
|
@@ -8,6 +8,13 @@ export interface CapabilityLimits {
|
|
|
8
8
|
timeoutMs: number;
|
|
9
9
|
maxInputBytes: number;
|
|
10
10
|
}
|
|
11
|
+
/** A hub that can serve this capability. `failover` is true when the runtime may call it with the same input. */
|
|
12
|
+
export interface CatalogUpstream {
|
|
13
|
+
hub: string;
|
|
14
|
+
api: string | null;
|
|
15
|
+
priceUsd: number | null;
|
|
16
|
+
failover: boolean;
|
|
17
|
+
}
|
|
11
18
|
export interface CapabilityDefinition<I extends z.ZodType = z.ZodType, O extends z.ZodType = z.ZodType> {
|
|
12
19
|
/** Exactly the workbook's Capability value. */
|
|
13
20
|
slug: string;
|
|
@@ -26,6 +33,8 @@ export interface CapabilityDefinition<I extends z.ZodType = z.ZodType, O extends
|
|
|
26
33
|
requires?: Requirement[];
|
|
27
34
|
/** Example input used by docs and the contract check. */
|
|
28
35
|
examples?: z.input<I>[];
|
|
36
|
+
/** Hubs that can serve this capability, including ones the runtime does not fail over to. */
|
|
37
|
+
upstreams?: CatalogUpstream[];
|
|
29
38
|
/** Upstream routes (from `proxy.ts`) when the capability is served by proxying to hubs. */
|
|
30
39
|
proxy?: ProxySpec<z.output<I>, z.input<O>>;
|
|
31
40
|
handler: (input: z.output<I>, ctx: Ctx) => Promise<z.input<O>>;
|
|
@@ -36,3 +45,5 @@ export type CapabilitySpec<I extends z.ZodType, O extends z.ZodType> = Omit<Capa
|
|
|
36
45
|
handler?: CapabilityDefinition<I, O>['handler'];
|
|
37
46
|
};
|
|
38
47
|
export declare function defineCapability<I extends z.ZodType, O extends z.ZodType>(spec: CapabilitySpec<I, O>): CapabilityDefinition<I, O>;
|
|
48
|
+
/** Hubs a served capability can call. Typed proxies fail over across every route; pass-through sets `upstreams` itself. */
|
|
49
|
+
export declare function upstreamsOf(impl: AnyCapability | undefined): CatalogUpstream[];
|
package/dist/capability.js
CHANGED
|
@@ -22,4 +22,19 @@ export function defineCapability(spec) {
|
|
|
22
22
|
}
|
|
23
23
|
return Object.freeze(definition);
|
|
24
24
|
}
|
|
25
|
+
/** Hubs a served capability can call. Typed proxies fail over across every route; pass-through sets `upstreams` itself. */
|
|
26
|
+
export function upstreamsOf(impl) {
|
|
27
|
+
if (!impl)
|
|
28
|
+
return [];
|
|
29
|
+
if (impl.upstreams)
|
|
30
|
+
return impl.upstreams;
|
|
31
|
+
const routes = impl.proxy?.routes ?? [];
|
|
32
|
+
const failover = routes.length > 1;
|
|
33
|
+
return routes.map((route) => ({
|
|
34
|
+
hub: route.hub,
|
|
35
|
+
api: route.api ?? null,
|
|
36
|
+
priceUsd: route.priceUsd ?? null,
|
|
37
|
+
failover,
|
|
38
|
+
}));
|
|
39
|
+
}
|
|
25
40
|
//# sourceMappingURL=capability.js.map
|
package/dist/capability.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"capability.js","sourceRoot":"","sources":["../../../../../packages/core/src/capability.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"capability.js","sourceRoot":"","sources":["../../../../../packages/core/src/capability.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAqDxB,MAAM,IAAI,GAAG,4BAA4B,CAAC;AAE1C,MAAM,UAAU,gBAAgB,CAA2C,IAA0B;IACnG,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAChC,IAAI,CAAC,OAAO,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,cAAc,IAAI,CAAC,IAAI,6BAA6B,CAAC,CAAC;IAC9F,IAAI,KAAK,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,cAAc,IAAI,CAAC,IAAI,6BAA6B,CAAC,CAAC;IAC9G,MAAM,UAAU,GAA+B;QAC7C,GAAG,IAAI;QACP,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK;QAClC,OAAO,EAAE,OAAO,IAAI,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,KAAM,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;KACtI,CAAC;IACF,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CAAC,4BAA4B,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC;IACjE,CAAC;IACD,IAAI,UAAU,CAAC,MAAM,CAAC,SAAS,IAAI,CAAC,IAAI,UAAU,CAAC,MAAM,CAAC,aAAa,IAAI,CAAC,EAAE,CAAC;QAC7E,MAAM,IAAI,KAAK,CAAC,cAAc,UAAU,CAAC,IAAI,2BAA2B,CAAC,CAAC;IAC5E,CAAC;IACD,IAAI,CAAC,CAAC,UAAU,CAAC,KAAK,YAAY,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC;QAC/C,MAAM,IAAI,KAAK,CAAC,cAAc,UAAU,CAAC,IAAI,qDAAqD,CAAC,CAAC;IACtG,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;AACnC,CAAC;AAED,2HAA2H;AAC3H,MAAM,UAAU,WAAW,CAAC,IAA+B;IACzD,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAC;IACrB,IAAI,IAAI,CAAC,SAAS;QAAE,OAAO,IAAI,CAAC,SAAS,CAAC;IAC1C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,EAAE,MAAM,IAAI,EAAE,CAAC;IACxC,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;IACnC,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAC5B,GAAG,EAAE,KAAK,CAAC,GAAG;QACd,GAAG,EAAE,KAAK,CAAC,GAAG,IAAI,IAAI;QACtB,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,IAAI;QAChC,QAAQ;KACT,CAAC,CAAC,CAAC;AACN,CAAC","sourcesContent":["import { z } from 'zod';\nimport type { Requirement } from './connections.ts';\nimport type { Ctx } from './context.ts';\nimport type { ProxySpec } from './upstream-types.ts';\nimport type { Sourcing } from './registry-types.ts';\n\nexport type CapabilityMode = 'sync' | 'job';\n\nexport interface CapabilityLimits {\n timeoutMs: number;\n maxInputBytes: number;\n}\n\n/** A hub that can serve this capability. `failover` is true when the runtime may call it with the same input. */\nexport interface CatalogUpstream {\n hub: string;\n api: string | null;\n priceUsd: number | null;\n failover: boolean;\n}\n\nexport interface CapabilityDefinition<I extends z.ZodType = z.ZodType, O extends z.ZodType = z.ZodType> {\n /** Exactly the workbook's Capability value. */\n slug: string;\n /** One sentence, in our own words. */\n summary: string;\n input: I;\n output: O;\n sourcing: Sourcing;\n mode: CapabilityMode;\n limits: CapabilityLimits;\n /** x12i packages actually used, after verification. */\n reuse: string[];\n /** Engineer-days actually spent, for the next sizing round. */\n actualDays?: number;\n /** Operator-configured services the handler needs; drives `connected` and `missing` in the catalog. */\n requires?: Requirement[];\n /** Example input used by docs and the contract check. */\n examples?: z.input<I>[];\n /** Hubs that can serve this capability, including ones the runtime does not fail over to. */\n upstreams?: CatalogUpstream[];\n /** Upstream routes (from `proxy.ts`) when the capability is served by proxying to hubs. */\n proxy?: ProxySpec<z.output<I>, z.input<O>>;\n handler: (input: z.output<I>, ctx: Ctx) => Promise<z.input<O>>;\n}\n\nexport type AnyCapability = CapabilityDefinition<z.ZodType, z.ZodType>;\n\n/** Our own `handler`, or a `proxy` spec whose routes `ctx.upstream` runs. A handler wins when both are given. */\nexport type CapabilitySpec<I extends z.ZodType, O extends z.ZodType> = Omit<CapabilityDefinition<I, O>, 'handler'> & {\n handler?: CapabilityDefinition<I, O>['handler'];\n};\n\nconst SLUG = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;\n\nexport function defineCapability<I extends z.ZodType, O extends z.ZodType>(spec: CapabilitySpec<I, O>): CapabilityDefinition<I, O> {\n const { proxy, handler } = spec;\n if (!handler && !proxy) throw new Error(`Capability ${spec.slug} needs a handler or a proxy`);\n if (proxy && proxy.routes.length === 0) throw new Error(`Capability ${spec.slug} has a proxy with no routes`);\n const definition: CapabilityDefinition<I, O> = {\n ...spec,\n proxy: handler ? undefined : proxy,\n handler: handler ?? ((input, ctx) => ctx.upstream.run(spec.slug, proxy!, input, { signal: ctx.signal, callerId: ctx.auth.callerId })),\n };\n if (!SLUG.test(definition.slug)) {\n throw new Error(`Invalid capability slug: ${definition.slug}`);\n }\n if (definition.limits.timeoutMs <= 0 || definition.limits.maxInputBytes <= 0) {\n throw new Error(`Capability ${definition.slug} must set positive limits`);\n }\n if (!(definition.input instanceof z.ZodObject)) {\n throw new Error(`Capability ${definition.slug} input must be a z.object so it maps to an MCP tool`);\n }\n return Object.freeze(definition);\n}\n\n/** Hubs a served capability can call. Typed proxies fail over across every route; pass-through sets `upstreams` itself. */\nexport function upstreamsOf(impl: AnyCapability | undefined): CatalogUpstream[] {\n if (!impl) return [];\n if (impl.upstreams) return impl.upstreams;\n const routes = impl.proxy?.routes ?? [];\n const failover = routes.length > 1;\n return routes.map((route) => ({\n hub: route.hub,\n api: route.api ?? null,\n priceUsd: route.priceUsd ?? null,\n failover,\n }));\n}\n"]}
|
package/dist/openapi.js
CHANGED
|
@@ -29,6 +29,8 @@ const catalogItemSchema = {
|
|
|
29
29
|
mode: { type: ['string', 'null'], enum: ['sync', 'job', null] },
|
|
30
30
|
hubs: { type: 'array', items: { type: 'string' } },
|
|
31
31
|
hubCount: { type: 'integer' },
|
|
32
|
+
aliasOf: { type: ['string', 'null'], description: 'Canonical slug when this listing is the same capability under another name' },
|
|
33
|
+
failover: { type: 'boolean', description: 'True when another hub is tried with the same input if the first cannot answer' },
|
|
32
34
|
effort: { type: ['string', 'null'] },
|
|
33
35
|
effortDays: { type: ['number', 'null'] },
|
|
34
36
|
wave: { type: ['integer', 'null'] },
|
|
@@ -94,6 +96,7 @@ export function buildOpenApi(capabilities, options = {}) {
|
|
|
94
96
|
{ name: 'wave', in: 'query', schema: { type: 'integer' } },
|
|
95
97
|
{ name: 'q', in: 'query', schema: { type: 'string' } },
|
|
96
98
|
{ name: 'connected', in: 'query', schema: { type: 'string', enum: ['true', 'false'] } },
|
|
99
|
+
{ name: 'unique', in: 'query', schema: { type: 'string', enum: ['true', 'false'] }, description: 'true hides slugs that alias another capability' },
|
|
97
100
|
{ name: 'limit', in: 'query', schema: { type: 'integer', minimum: 1 } },
|
|
98
101
|
{ name: 'offset', in: 'query', schema: { type: 'integer', minimum: 0 } },
|
|
99
102
|
],
|
|
@@ -106,6 +109,7 @@ export function buildOpenApi(capabilities, options = {}) {
|
|
|
106
109
|
type: 'object',
|
|
107
110
|
properties: {
|
|
108
111
|
total: { type: 'integer' },
|
|
112
|
+
unique: { type: 'integer', description: 'How many of the matches are canonical slugs' },
|
|
109
113
|
items: { type: 'array', items: { $ref: '#/components/schemas/CatalogItem' } },
|
|
110
114
|
},
|
|
111
115
|
},
|
package/dist/openapi.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openapi.js","sourceRoot":"","sources":["../../../../../packages/core/src/openapi.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AnyCapability } from './capability.ts';\nimport { ERROR_CODES } from './errors.ts';\nimport { PLANS, SOURCINGS, STATUSES } from './registry-types.ts';\nimport { toJsonSchema, type JsonSchema } from './schema.ts';\n\nconst errorSchema: JsonSchema = {\n type: 'object',\n required: ['error'],\n properties: {\n error: {\n type: 'object',\n required: ['code', 'message'],\n properties: {\n code: { type: 'string', enum: [...ERROR_CODES] },\n message: { type: 'string' },\n details: {},\n },\n },\n },\n};\n\nconst catalogItemSchema: JsonSchema = {\n type: 'object',\n required: ['slug', 'status', 'sourcing', 'plan'],\n properties: {\n slug: { type: 'string' },\n summary: { type: ['string', 'null'] },\n status: { type: 'string', enum: [...STATUSES] },\n statusReason: { type: ['string', 'null'] },\n sourcing: { type: 'string', enum: [...SOURCINGS] },\n plan: { type: 'string', enum: [...PLANS] },\n mode: { type: ['string', 'null'], enum: ['sync', 'job', null] },\n hubs: { type: 'array', items: { type: 'string' } },\n hubCount: { type: 'integer' },\n effort: { type: ['string', 'null'] },\n effortDays: { type: ['number', 'null'] },\n wave: { type: ['integer', 'null'] },\n connected: { type: ['boolean', 'null'], description: 'null when the capability is not served' },\n missing: {\n type: 'array',\n description: 'Providers or hubs the operator still has to connect (variable names only)',\n items: { type: 'object', required: ['kind'], properties: { kind: { type: 'string', enum: ['service', 'hub'] } } },\n },\n },\n};\n\nconst connectionSchema: JsonSchema = {\n type: 'object',\n required: ['kind', 'id', 'connected', 'env', 'unlocks'],\n properties: {\n kind: { type: 'string', enum: ['service', 'hub'] },\n id: { type: 'string' },\n title: { type: 'string' },\n connected: { type: 'boolean' },\n env: { type: 'array', items: { type: 'string' } },\n missingEnv: { type: 'array', items: { type: 'string' } },\n unlocks: { type: 'integer' },\n docs: { type: 'string' },\n note: { type: 'string' },\n },\n};\n\nconst jobSchema: JsonSchema = {\n type: 'object',\n required: ['id', 'status', 'statusUrl', 'expiresAt'],\n properties: {\n id: { type: 'string' },\n capability: { type: 'string' },\n status: { type: 'string', enum: ['queued', 'running', 'succeeded', 'failed'] },\n statusUrl: { type: 'string' },\n expiresAt: { type: 'string', format: 'date-time' },\n result: {},\n error: { $ref: '#/components/schemas/Error' },\n },\n};\n\nconst errorResponses = {\n '400': { description: 'invalid_input', content: { 'application/json': { schema: { $ref: '#/components/schemas/Error' } } } },\n '403': { description: 'not_permitted', content: { 'application/json': { schema: { $ref: '#/components/schemas/Error' } } } },\n '404': { description: 'Unknown capability', content: { 'application/json': { schema: { $ref: '#/components/schemas/Error' } } } },\n '413': { description: 'limit_exceeded', content: { 'application/json': { schema: { $ref: '#/components/schemas/Error' } } } },\n '429': { description: 'spend_limit_reached', content: { 'application/json': { schema: { $ref: '#/components/schemas/Error' } } } },\n '503': {\n description: 'service_not_connected or hub_not_connected (the operator has not configured a provider or hub)',\n content: { 'application/json': { schema: { $ref: '#/components/schemas/Error' } } },\n },\n '501': { description: 'unsupported (listed but not served)', content: { 'application/json': { schema: { $ref: '#/components/schemas/Error' } } } },\n '502': { description: 'upstream_unavailable', content: { 'application/json': { schema: { $ref: '#/components/schemas/Error' } } } },\n '504': { description: 'timeout', content: { 'application/json': { schema: { $ref: '#/components/schemas/Error' } } } },\n};\n\nexport interface OpenApiOptions {\n title?: string;\n version?: string;\n serverUrl?: string;\n}\n\nexport function buildOpenApi(capabilities: AnyCapability[], options: OpenApiOptions = {}): JsonSchema {\n const paths: Record<string, unknown> = {\n '/v1/capabilities': {\n get: {\n operationId: 'listCapabilities',\n summary: 'List every catalog capability with its status',\n parameters: [\n { name: 'status', in: 'query', schema: { type: 'string', enum: [...STATUSES] } },\n { name: 'sourcing', in: 'query', schema: { type: 'string', enum: [...SOURCINGS] } },\n { name: 'wave', in: 'query', schema: { type: 'integer' } },\n { name: 'q', in: 'query', schema: { type: 'string' } },\n { name: 'connected', in: 'query', schema: { type: 'string', enum: ['true', 'false'] } },\n { name: 'limit', in: 'query', schema: { type: 'integer', minimum: 1 } },\n { name: 'offset', in: 'query', schema: { type: 'integer', minimum: 0 } },\n ],\n responses: {\n '200': {\n description: 'Catalog page',\n content: {\n 'application/json': {\n schema: {\n type: 'object',\n properties: {\n total: { type: 'integer' },\n items: { type: 'array', items: { $ref: '#/components/schemas/CatalogItem' } },\n },\n },\n },\n },\n },\n },\n },\n },\n '/v1/connections': {\n get: {\n operationId: 'listConnections',\n summary: 'Providers and upstream hubs, whether each is connected, and what each unlocks',\n responses: {\n '200': {\n description: 'Connections',\n content: {\n 'application/json': {\n schema: { type: 'object', properties: { items: { type: 'array', items: { $ref: '#/components/schemas/Connection' } } } },\n },\n },\n },\n },\n },\n },\n '/v1/capabilities/{slug}': {\n get: {\n operationId: 'getCapability',\n summary: 'Describe one capability, including its schemas when served',\n parameters: [{ name: 'slug', in: 'path', required: true, schema: { type: 'string' } }],\n responses: {\n '200': { description: 'Capability', content: { 'application/json': { schema: { type: 'object' } } } },\n '404': errorResponses['404'],\n },\n },\n },\n '/v1/call/{slug}': {\n post: {\n operationId: 'callCapability',\n summary: 'Call any served capability by slug',\n parameters: [{ name: 'slug', in: 'path', required: true, schema: { type: 'string' } }],\n requestBody: { required: true, content: { 'application/json': { schema: { type: 'object' } } } },\n responses: {\n '200': { description: 'Capability output', content: { 'application/json': { schema: { type: 'object' } } } },\n '202': { description: 'Job accepted', content: { 'application/json': { schema: { $ref: '#/components/schemas/Job' } } } },\n ...errorResponses,\n },\n },\n },\n '/v1/jobs/{id}': {\n get: {\n operationId: 'getJob',\n summary: 'Poll an async job',\n parameters: [{ name: 'id', in: 'path', required: true, schema: { type: 'string' } }],\n responses: {\n '200': { description: 'Job', content: { 'application/json': { schema: { $ref: '#/components/schemas/Job' } } } },\n '404': errorResponses['404'],\n },\n },\n },\n };\n\n for (const cap of [...capabilities].sort((a, b) => a.slug.localeCompare(b.slug))) {\n const success =\n cap.mode === 'job'\n ? { '202': { description: 'Job accepted', content: { 'application/json': { schema: { $ref: '#/components/schemas/Job' } } } } }\n : { '200': { description: 'Result', content: { 'application/json': { schema: toJsonSchema(cap.output, 'output') } } } };\n paths[`/v1/call/${cap.slug}`] = {\n post: {\n operationId: `call_${cap.slug.replaceAll('-', '_')}`,\n summary: cap.summary,\n tags: [cap.sourcing],\n 'x-hub-sourcing': cap.sourcing,\n 'x-hub-mode': cap.mode,\n 'x-hub-limits': cap.limits,\n requestBody: {\n required: true,\n content: { 'application/json': { schema: toJsonSchema(cap.input, 'input') } },\n },\n responses: { ...success, ...errorResponses },\n },\n };\n }\n\n return {\n openapi: '3.1.0',\n info: {\n title: options.title ?? 'x12i API hub',\n version: options.version ?? '0.1.0',\n description: 'Pay-per-call capabilities. Every capability is also an MCP tool.',\n },\n servers: options.serverUrl ? [{ url: options.serverUrl }] : [],\n paths,\n components: {\n schemas: { Error: errorSchema, CatalogItem: catalogItemSchema, Connection: connectionSchema, Job: jobSchema },\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"openapi.js","sourceRoot":"","sources":["../../../../../packages/core/src/openapi.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AnyCapability } from './capability.ts';\nimport { ERROR_CODES } from './errors.ts';\nimport { PLANS, SOURCINGS, STATUSES } from './registry-types.ts';\nimport { toJsonSchema, type JsonSchema } from './schema.ts';\n\nconst errorSchema: JsonSchema = {\n type: 'object',\n required: ['error'],\n properties: {\n error: {\n type: 'object',\n required: ['code', 'message'],\n properties: {\n code: { type: 'string', enum: [...ERROR_CODES] },\n message: { type: 'string' },\n details: {},\n },\n },\n },\n};\n\nconst catalogItemSchema: JsonSchema = {\n type: 'object',\n required: ['slug', 'status', 'sourcing', 'plan'],\n properties: {\n slug: { type: 'string' },\n summary: { type: ['string', 'null'] },\n status: { type: 'string', enum: [...STATUSES] },\n statusReason: { type: ['string', 'null'] },\n sourcing: { type: 'string', enum: [...SOURCINGS] },\n plan: { type: 'string', enum: [...PLANS] },\n mode: { type: ['string', 'null'], enum: ['sync', 'job', null] },\n hubs: { type: 'array', items: { type: 'string' } },\n hubCount: { type: 'integer' },\n aliasOf: { type: ['string', 'null'], description: 'Canonical slug when this listing is the same capability under another name' },\n failover: { type: 'boolean', description: 'True when another hub is tried with the same input if the first cannot answer' },\n effort: { type: ['string', 'null'] },\n effortDays: { type: ['number', 'null'] },\n wave: { type: ['integer', 'null'] },\n connected: { type: ['boolean', 'null'], description: 'null when the capability is not served' },\n missing: {\n type: 'array',\n description: 'Providers or hubs the operator still has to connect (variable names only)',\n items: { type: 'object', required: ['kind'], properties: { kind: { type: 'string', enum: ['service', 'hub'] } } },\n },\n },\n};\n\nconst connectionSchema: JsonSchema = {\n type: 'object',\n required: ['kind', 'id', 'connected', 'env', 'unlocks'],\n properties: {\n kind: { type: 'string', enum: ['service', 'hub'] },\n id: { type: 'string' },\n title: { type: 'string' },\n connected: { type: 'boolean' },\n env: { type: 'array', items: { type: 'string' } },\n missingEnv: { type: 'array', items: { type: 'string' } },\n unlocks: { type: 'integer' },\n docs: { type: 'string' },\n note: { type: 'string' },\n },\n};\n\nconst jobSchema: JsonSchema = {\n type: 'object',\n required: ['id', 'status', 'statusUrl', 'expiresAt'],\n properties: {\n id: { type: 'string' },\n capability: { type: 'string' },\n status: { type: 'string', enum: ['queued', 'running', 'succeeded', 'failed'] },\n statusUrl: { type: 'string' },\n expiresAt: { type: 'string', format: 'date-time' },\n result: {},\n error: { $ref: '#/components/schemas/Error' },\n },\n};\n\nconst errorResponses = {\n '400': { description: 'invalid_input', content: { 'application/json': { schema: { $ref: '#/components/schemas/Error' } } } },\n '403': { description: 'not_permitted', content: { 'application/json': { schema: { $ref: '#/components/schemas/Error' } } } },\n '404': { description: 'Unknown capability', content: { 'application/json': { schema: { $ref: '#/components/schemas/Error' } } } },\n '413': { description: 'limit_exceeded', content: { 'application/json': { schema: { $ref: '#/components/schemas/Error' } } } },\n '429': { description: 'spend_limit_reached', content: { 'application/json': { schema: { $ref: '#/components/schemas/Error' } } } },\n '503': {\n description: 'service_not_connected or hub_not_connected (the operator has not configured a provider or hub)',\n content: { 'application/json': { schema: { $ref: '#/components/schemas/Error' } } },\n },\n '501': { description: 'unsupported (listed but not served)', content: { 'application/json': { schema: { $ref: '#/components/schemas/Error' } } } },\n '502': { description: 'upstream_unavailable', content: { 'application/json': { schema: { $ref: '#/components/schemas/Error' } } } },\n '504': { description: 'timeout', content: { 'application/json': { schema: { $ref: '#/components/schemas/Error' } } } },\n};\n\nexport interface OpenApiOptions {\n title?: string;\n version?: string;\n serverUrl?: string;\n}\n\nexport function buildOpenApi(capabilities: AnyCapability[], options: OpenApiOptions = {}): JsonSchema {\n const paths: Record<string, unknown> = {\n '/v1/capabilities': {\n get: {\n operationId: 'listCapabilities',\n summary: 'List every catalog capability with its status',\n parameters: [\n { name: 'status', in: 'query', schema: { type: 'string', enum: [...STATUSES] } },\n { name: 'sourcing', in: 'query', schema: { type: 'string', enum: [...SOURCINGS] } },\n { name: 'wave', in: 'query', schema: { type: 'integer' } },\n { name: 'q', in: 'query', schema: { type: 'string' } },\n { name: 'connected', in: 'query', schema: { type: 'string', enum: ['true', 'false'] } },\n { name: 'unique', in: 'query', schema: { type: 'string', enum: ['true', 'false'] }, description: 'true hides slugs that alias another capability' },\n { name: 'limit', in: 'query', schema: { type: 'integer', minimum: 1 } },\n { name: 'offset', in: 'query', schema: { type: 'integer', minimum: 0 } },\n ],\n responses: {\n '200': {\n description: 'Catalog page',\n content: {\n 'application/json': {\n schema: {\n type: 'object',\n properties: {\n total: { type: 'integer' },\n unique: { type: 'integer', description: 'How many of the matches are canonical slugs' },\n items: { type: 'array', items: { $ref: '#/components/schemas/CatalogItem' } },\n },\n },\n },\n },\n },\n },\n },\n },\n '/v1/connections': {\n get: {\n operationId: 'listConnections',\n summary: 'Providers and upstream hubs, whether each is connected, and what each unlocks',\n responses: {\n '200': {\n description: 'Connections',\n content: {\n 'application/json': {\n schema: { type: 'object', properties: { items: { type: 'array', items: { $ref: '#/components/schemas/Connection' } } } },\n },\n },\n },\n },\n },\n },\n '/v1/capabilities/{slug}': {\n get: {\n operationId: 'getCapability',\n summary: 'Describe one capability, including its schemas when served',\n parameters: [{ name: 'slug', in: 'path', required: true, schema: { type: 'string' } }],\n responses: {\n '200': { description: 'Capability', content: { 'application/json': { schema: { type: 'object' } } } },\n '404': errorResponses['404'],\n },\n },\n },\n '/v1/call/{slug}': {\n post: {\n operationId: 'callCapability',\n summary: 'Call any served capability by slug',\n parameters: [{ name: 'slug', in: 'path', required: true, schema: { type: 'string' } }],\n requestBody: { required: true, content: { 'application/json': { schema: { type: 'object' } } } },\n responses: {\n '200': { description: 'Capability output', content: { 'application/json': { schema: { type: 'object' } } } },\n '202': { description: 'Job accepted', content: { 'application/json': { schema: { $ref: '#/components/schemas/Job' } } } },\n ...errorResponses,\n },\n },\n },\n '/v1/jobs/{id}': {\n get: {\n operationId: 'getJob',\n summary: 'Poll an async job',\n parameters: [{ name: 'id', in: 'path', required: true, schema: { type: 'string' } }],\n responses: {\n '200': { description: 'Job', content: { 'application/json': { schema: { $ref: '#/components/schemas/Job' } } } },\n '404': errorResponses['404'],\n },\n },\n },\n };\n\n for (const cap of [...capabilities].sort((a, b) => a.slug.localeCompare(b.slug))) {\n const success =\n cap.mode === 'job'\n ? { '202': { description: 'Job accepted', content: { 'application/json': { schema: { $ref: '#/components/schemas/Job' } } } } }\n : { '200': { description: 'Result', content: { 'application/json': { schema: toJsonSchema(cap.output, 'output') } } } };\n paths[`/v1/call/${cap.slug}`] = {\n post: {\n operationId: `call_${cap.slug.replaceAll('-', '_')}`,\n summary: cap.summary,\n tags: [cap.sourcing],\n 'x-hub-sourcing': cap.sourcing,\n 'x-hub-mode': cap.mode,\n 'x-hub-limits': cap.limits,\n requestBody: {\n required: true,\n content: { 'application/json': { schema: toJsonSchema(cap.input, 'input') } },\n },\n responses: { ...success, ...errorResponses },\n },\n };\n }\n\n return {\n openapi: '3.1.0',\n info: {\n title: options.title ?? 'x12i API hub',\n version: options.version ?? '0.1.0',\n description: 'Pay-per-call capabilities. Every capability is also an MCP tool.',\n },\n servers: options.serverUrl ? [{ url: options.serverUrl }] : [],\n paths,\n components: {\n schemas: { Error: errorSchema, CatalogItem: catalogItemSchema, Connection: connectionSchema, Job: jobSchema },\n },\n };\n}\n"]}
|
package/dist/passthrough.d.ts
CHANGED
|
@@ -16,9 +16,16 @@ export interface PassthroughRoute {
|
|
|
16
16
|
export interface PassthroughEntry {
|
|
17
17
|
slug: string;
|
|
18
18
|
summary: string;
|
|
19
|
+
/** The hub the catalog names first. */
|
|
19
20
|
route: PassthroughRoute;
|
|
20
|
-
/** Other
|
|
21
|
+
/** Other hubs that sell it, in the same order as `routes`. */
|
|
21
22
|
alternatives: string[];
|
|
23
|
+
/**
|
|
24
|
+
* Every matched hub, primary first. Present when there is more than one.
|
|
25
|
+
* Endpoint routes that share a method are tried in order. Base routes are not:
|
|
26
|
+
* the caller picks a path that only exists on the primary hub.
|
|
27
|
+
*/
|
|
28
|
+
routes?: PassthroughRoute[];
|
|
22
29
|
}
|
|
23
30
|
export interface PassthroughFile {
|
|
24
31
|
note?: string;
|
|
@@ -37,8 +44,9 @@ export declare const passthroughOutput: z.ZodObject<{
|
|
|
37
44
|
data: z.ZodUnknown;
|
|
38
45
|
}, z.core.$strip>;
|
|
39
46
|
/**
|
|
40
|
-
* A capability served by forwarding the caller's params to
|
|
41
|
-
*
|
|
47
|
+
* A capability served by forwarding the caller's params to a hub. Endpoint hubs that take the
|
|
48
|
+
* same forwarded parameters are all registered, and `@x12i/api-hub-upstream` tries the cheapest connected
|
|
49
|
+
* one first. A base route stays on one hub: the caller's path belongs to that API.
|
|
42
50
|
*/
|
|
43
51
|
export declare function passthroughCapability(entry: PassthroughEntry, sourcing: Sourcing): AnyCapability;
|
|
44
52
|
/** True when `path` stays under the base path on the base host once a URL parser has normalised it. */
|
package/dist/passthrough.js
CHANGED
|
@@ -35,12 +35,36 @@ function decode(res, hub) {
|
|
|
35
35
|
}
|
|
36
36
|
return { ...base, encoding: 'base64', data: Buffer.from(res.body).toString('base64') };
|
|
37
37
|
}
|
|
38
|
+
const kindOf = (route) => route.kind ?? 'endpoint';
|
|
39
|
+
/** Primary first, then other hubs. A repeated hub is dropped. */
|
|
40
|
+
function orderedRoutes(entry) {
|
|
41
|
+
const extra = (entry.routes ?? []).filter((route) => route.hub !== entry.route.hub);
|
|
42
|
+
return [entry.route, ...extra];
|
|
43
|
+
}
|
|
44
|
+
/** Opaque params can be forwarded only to endpoint hubs that use the same method. */
|
|
45
|
+
function sameContract(primary, route) {
|
|
46
|
+
return kindOf(primary) === 'endpoint' && kindOf(route) === 'endpoint' && route.method === primary.method;
|
|
47
|
+
}
|
|
48
|
+
function upstreamsFor(routes) {
|
|
49
|
+
const primary = routes[0];
|
|
50
|
+
const shareable = routes.filter((route) => sameContract(primary, route));
|
|
51
|
+
const failover = shareable.length > 1;
|
|
52
|
+
return routes.map((route) => ({
|
|
53
|
+
hub: route.hub,
|
|
54
|
+
api: route.api,
|
|
55
|
+
priceUsd: route.priceUsd,
|
|
56
|
+
failover: failover && sameContract(primary, route),
|
|
57
|
+
}));
|
|
58
|
+
}
|
|
38
59
|
/**
|
|
39
|
-
* A capability served by forwarding the caller's params to
|
|
40
|
-
*
|
|
60
|
+
* A capability served by forwarding the caller's params to a hub. Endpoint hubs that take the
|
|
61
|
+
* same forwarded parameters are all registered, and `@x12i/api-hub-upstream` tries the cheapest connected
|
|
62
|
+
* one first. A base route stays on one hub: the caller's path belongs to that API.
|
|
41
63
|
*/
|
|
42
64
|
export function passthroughCapability(entry, sourcing) {
|
|
43
|
-
|
|
65
|
+
const routes = orderedRoutes(entry);
|
|
66
|
+
const upstreams = upstreamsFor(routes);
|
|
67
|
+
return kindOf(entry.route) === 'base' ? baseCapability(entry, sourcing, upstreams) : endpointCapability(entry, sourcing, routes, upstreams);
|
|
44
68
|
}
|
|
45
69
|
const PATH_CHARS = /^\/[A-Za-z0-9\-._~!$&'()*+,;=:@%/]*$/;
|
|
46
70
|
/** True when `path` stays under the base path on the base host once a URL parser has normalised it. */
|
|
@@ -58,7 +82,7 @@ export function pathStaysUnder(base, path) {
|
|
|
58
82
|
}
|
|
59
83
|
return target.origin === root.origin && (target.pathname === prefix || target.pathname.startsWith(`${prefix}/`));
|
|
60
84
|
}
|
|
61
|
-
function baseCapability(entry, sourcing) {
|
|
85
|
+
function baseCapability(entry, sourcing, upstreams) {
|
|
62
86
|
const { route } = entry;
|
|
63
87
|
const base = route.url.replace(/\/+$/, '');
|
|
64
88
|
const input = z.object({
|
|
@@ -81,6 +105,7 @@ function baseCapability(entry, sourcing) {
|
|
|
81
105
|
limits: { timeoutMs: 60_000, maxInputBytes: 1_000_000 },
|
|
82
106
|
reuse: [],
|
|
83
107
|
examples: [{ method: 'GET', path: '/', query: {} }],
|
|
108
|
+
upstreams,
|
|
84
109
|
proxy: {
|
|
85
110
|
routes: [
|
|
86
111
|
{
|
|
@@ -99,11 +124,12 @@ function baseCapability(entry, sourcing) {
|
|
|
99
124
|
},
|
|
100
125
|
});
|
|
101
126
|
}
|
|
102
|
-
function endpointCapability(entry, sourcing) {
|
|
103
|
-
const
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
127
|
+
function endpointCapability(entry, sourcing, routes, upstreams) {
|
|
128
|
+
const shareable = routes.filter((route) => sameContract(entry.route, route));
|
|
129
|
+
const fallbacks = shareable.slice(1).map((route) => route.hub);
|
|
130
|
+
const params = entry.route.method === 'GET'
|
|
131
|
+
? z.record(z.string(), scalar).describe(`Query parameters forwarded to ${entry.route.hub}${fallbacks.length ? `, then ${fallbacks.join(', ')}` : ''}`)
|
|
132
|
+
: z.record(z.string(), z.unknown()).describe(`JSON body forwarded to ${entry.route.hub}${fallbacks.length ? `, then ${fallbacks.join(', ')}` : ''}`);
|
|
107
133
|
const input = z.object({ params: params.default({}) });
|
|
108
134
|
return defineCapability({
|
|
109
135
|
slug: entry.slug,
|
|
@@ -115,18 +141,17 @@ function endpointCapability(entry, sourcing) {
|
|
|
115
141
|
limits: { timeoutMs: 60_000, maxInputBytes: 1_000_000 },
|
|
116
142
|
reuse: [],
|
|
117
143
|
examples: [{ params: {} }],
|
|
144
|
+
upstreams,
|
|
118
145
|
proxy: {
|
|
119
|
-
routes:
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
},
|
|
129
|
-
],
|
|
146
|
+
routes: shareable.map((route) => ({
|
|
147
|
+
hub: route.hub,
|
|
148
|
+
...(route.api ? { api: route.api } : {}),
|
|
149
|
+
priceUsd: route.priceUsd,
|
|
150
|
+
request: (i) => route.method === 'GET'
|
|
151
|
+
? { method: 'GET', url: route.url, query: i.params }
|
|
152
|
+
: { method: 'POST', url: route.url, json: i.params },
|
|
153
|
+
response: (res) => decode(res, route.hub),
|
|
154
|
+
})),
|
|
130
155
|
},
|
|
131
156
|
});
|
|
132
157
|
}
|
package/dist/passthrough.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"passthrough.js","sourceRoot":"","sources":["../../../../../packages/core/src/passthrough.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, readFileSync } from 'node:fs';\nimport { z } from 'zod';\nimport { defineCapability, type AnyCapability } from './capability.ts';\nimport { HubError } from './errors.ts';\nimport type { Sourcing } from './registry-types.ts';\nimport type { UpstreamResponse } from './upstream-types.ts';\n\nexport interface PassthroughRoute {\n hub: string;\n api: string | null;\n /** `endpoint`: one fixed URL (x402 tools). `base`: the caller picks method and path under `url` (key hubs). */\n kind?: 'endpoint' | 'base';\n method: 'GET' | 'POST';\n url: string;\n priceUsd: number | null;\n /** The listed API's documentation page, for callers of `base` routes. */\n docs?: string | null;\n}\n\n/** One generated entry of `catalog/proxy-routes.json`. */\nexport interface PassthroughEntry {\n slug: string;\n summary: string;\n route: PassthroughRoute;\n /** Other open hubs that sell it; not used for failover because their request schemas differ. */\n alternatives: string[];\n}\n\nexport interface PassthroughFile {\n note?: string;\n entries: PassthroughEntry[];\n}\n\nexport function readPassthroughFile(path: string): PassthroughFile {\n if (!existsSync(path)) return { entries: [] };\n return JSON.parse(readFileSync(path, 'utf8')) as PassthroughFile;\n}\n\nconst scalar = z.union([z.string(), z.number(), z.boolean()]);\nconst MAX_TEXT = 1_000_000;\n\nexport const passthroughOutput = z.object({\n hub: z.string().describe('Hub that answered'),\n status: z.number().int(),\n contentType: z.string().nullable(),\n encoding: z.enum(['json', 'text', 'base64']).describe('How `data` is encoded'),\n data: z.unknown().describe(\"The hub's answer: parsed JSON, text, or base64 for binary content\"),\n});\n\nfunction decode(res: UpstreamResponse, hub: string): z.input<typeof passthroughOutput> {\n const contentType = res.headers['content-type'] ?? null;\n const base = { hub, status: res.status, contentType };\n if (contentType && /json/i.test(contentType)) {\n try {\n return { ...base, encoding: 'json', data: res.json() };\n } catch {\n throw new HubError('upstream_unavailable', `${hub} returned invalid JSON`, { hub });\n }\n }\n if (!contentType || /^text\\/|xml|javascript|csv/i.test(contentType)) {\n const text = res.text();\n if (text.length > MAX_TEXT) throw new HubError('upstream_unavailable', `${hub} returned more text than this endpoint passes on`, { hub });\n return { ...base, encoding: 'text', data: text };\n }\n return { ...base, encoding: 'base64', data: Buffer.from(res.body).toString('base64') };\n}\n\n/**\n * A capability served by forwarding the caller's params to one hub endpoint. The hub catalogs carry\n * no request schemas, so the contract is deliberately thin until a typed `proxy.ts` replaces it.\n */\nexport function passthroughCapability(entry: PassthroughEntry, sourcing: Sourcing): AnyCapability {\n return entry.route.kind === 'base' ? baseCapability(entry, sourcing) : endpointCapability(entry, sourcing);\n}\n\nconst PATH_CHARS = /^\\/[A-Za-z0-9\\-._~!$&'()*+,;=:@%/]*$/;\n\n/** True when `path` stays under the base path on the base host once a URL parser has normalised it. */\nexport function pathStaysUnder(base: string, path: string): boolean {\n if (!PATH_CHARS.test(path) || path.startsWith('//') || /%2e|%2f|%5c/i.test(path)) return false;\n const root = new URL(base);\n const prefix = root.pathname.replace(/\\/+$/, '');\n let target: URL;\n try {\n target = new URL(`${prefix}${path}`, root.origin);\n } catch {\n return false;\n }\n return target.origin === root.origin && (target.pathname === prefix || target.pathname.startsWith(`${prefix}/`));\n}\n\nfunction baseCapability(entry: PassthroughEntry, sourcing: Sourcing): AnyCapability {\n const { route } = entry;\n const base = route.url.replace(/\\/+$/, '');\n const input = z.object({\n method: z.enum(['GET', 'POST']).default('GET'),\n path: z\n .string()\n .max(500)\n .refine((p) => pathStaysUnder(base, p), { message: 'path must start with / and stay under the API base path' })\n .describe(`Path under ${base}, as ${route.api ?? 'the API'}'s documentation${route.docs ? ` (${route.docs})` : ''} names it`),\n query: z.record(z.string(), scalar).default({}),\n body: z.unknown().optional().describe('JSON body for POST'),\n });\n return defineCapability({\n slug: entry.slug,\n summary: entry.summary,\n input,\n output: passthroughOutput,\n sourcing,\n mode: 'sync',\n limits: { timeoutMs: 60_000, maxInputBytes: 1_000_000 },\n reuse: [],\n examples: [{ method: 'GET', path: '/', query: {} }],\n proxy: {\n routes: [\n {\n hub: route.hub,\n ...(route.api ? { api: route.api } : {}),\n priceUsd: route.priceUsd,\n request: (i: z.output<typeof input>) => ({\n method: i.method,\n url: `${base}${i.path}`,\n query: i.query,\n ...(i.method === 'POST' && i.body !== undefined ? { json: i.body } : {}),\n }),\n response: (res: UpstreamResponse) => decode(res, route.hub),\n },\n ],\n },\n }) as unknown as AnyCapability;\n}\n\nfunction endpointCapability(entry: PassthroughEntry, sourcing: Sourcing): AnyCapability {\n const { route } = entry;\n const params =\n route.method === 'GET'\n ? z.record(z.string(), scalar).describe(`Query parameters for ${route.hub}'s endpoint, as its documentation names them`)\n : z.record(z.string(), z.unknown()).describe(`JSON body for ${route.hub}'s endpoint, as its documentation names it`);\n const input = z.object({ params: params.default({}) });\n return defineCapability({\n slug: entry.slug,\n summary: entry.summary,\n input,\n output: passthroughOutput,\n sourcing,\n mode: 'sync',\n limits: { timeoutMs: 60_000, maxInputBytes: 1_000_000 },\n reuse: [],\n examples: [{ params: {} }],\n proxy: {\n routes: [\n {\n hub: route.hub,\n ...(route.api ? { api: route.api } : {}),\n priceUsd: route.priceUsd,\n request: (i: z.output<typeof input>) =>\n route.method === 'GET'\n ? { method: 'GET' as const, url: route.url, query: i.params as Record<string, string | number | boolean> }\n : { method: 'POST' as const, url: route.url, json: i.params },\n response: (res: UpstreamResponse) => decode(res, route.hub),\n },\n ],\n },\n }) as unknown as AnyCapability;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"passthrough.js","sourceRoot":"","sources":["../../../../../packages/core/src/passthrough.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, readFileSync } from 'node:fs';\nimport { z } from 'zod';\nimport { defineCapability, type AnyCapability, type CatalogUpstream } from './capability.ts';\nimport { HubError } from './errors.ts';\nimport type { Sourcing } from './registry-types.ts';\nimport type { UpstreamResponse } from './upstream-types.ts';\n\nexport interface PassthroughRoute {\n hub: string;\n api: string | null;\n /** `endpoint`: one fixed URL (x402 tools). `base`: the caller picks method and path under `url` (key hubs). */\n kind?: 'endpoint' | 'base';\n method: 'GET' | 'POST';\n url: string;\n priceUsd: number | null;\n /** The listed API's documentation page, for callers of `base` routes. */\n docs?: string | null;\n}\n\n/** One generated entry of `catalog/proxy-routes.json`. */\nexport interface PassthroughEntry {\n slug: string;\n summary: string;\n /** The hub the catalog names first. */\n route: PassthroughRoute;\n /** Other hubs that sell it, in the same order as `routes`. */\n alternatives: string[];\n /**\n * Every matched hub, primary first. Present when there is more than one.\n * Endpoint routes that share a method are tried in order. Base routes are not:\n * the caller picks a path that only exists on the primary hub.\n */\n routes?: PassthroughRoute[];\n}\n\nexport interface PassthroughFile {\n note?: string;\n entries: PassthroughEntry[];\n}\n\nexport function readPassthroughFile(path: string): PassthroughFile {\n if (!existsSync(path)) return { entries: [] };\n return JSON.parse(readFileSync(path, 'utf8')) as PassthroughFile;\n}\n\nconst scalar = z.union([z.string(), z.number(), z.boolean()]);\nconst MAX_TEXT = 1_000_000;\n\nexport const passthroughOutput = z.object({\n hub: z.string().describe('Hub that answered'),\n status: z.number().int(),\n contentType: z.string().nullable(),\n encoding: z.enum(['json', 'text', 'base64']).describe('How `data` is encoded'),\n data: z.unknown().describe(\"The hub's answer: parsed JSON, text, or base64 for binary content\"),\n});\n\nfunction decode(res: UpstreamResponse, hub: string): z.input<typeof passthroughOutput> {\n const contentType = res.headers['content-type'] ?? null;\n const base = { hub, status: res.status, contentType };\n if (contentType && /json/i.test(contentType)) {\n try {\n return { ...base, encoding: 'json', data: res.json() };\n } catch {\n throw new HubError('upstream_unavailable', `${hub} returned invalid JSON`, { hub });\n }\n }\n if (!contentType || /^text\\/|xml|javascript|csv/i.test(contentType)) {\n const text = res.text();\n if (text.length > MAX_TEXT) throw new HubError('upstream_unavailable', `${hub} returned more text than this endpoint passes on`, { hub });\n return { ...base, encoding: 'text', data: text };\n }\n return { ...base, encoding: 'base64', data: Buffer.from(res.body).toString('base64') };\n}\n\nconst kindOf = (route: PassthroughRoute) => route.kind ?? 'endpoint';\n\n/** Primary first, then other hubs. A repeated hub is dropped. */\nfunction orderedRoutes(entry: PassthroughEntry): PassthroughRoute[] {\n const extra = (entry.routes ?? []).filter((route) => route.hub !== entry.route.hub);\n return [entry.route, ...extra];\n}\n\n/** Opaque params can be forwarded only to endpoint hubs that use the same method. */\nfunction sameContract(primary: PassthroughRoute, route: PassthroughRoute): boolean {\n return kindOf(primary) === 'endpoint' && kindOf(route) === 'endpoint' && route.method === primary.method;\n}\n\nfunction upstreamsFor(routes: PassthroughRoute[]): CatalogUpstream[] {\n const primary = routes[0]!;\n const shareable = routes.filter((route) => sameContract(primary, route));\n const failover = shareable.length > 1;\n return routes.map((route) => ({\n hub: route.hub,\n api: route.api,\n priceUsd: route.priceUsd,\n failover: failover && sameContract(primary, route),\n }));\n}\n\n/**\n * A capability served by forwarding the caller's params to a hub. Endpoint hubs that take the\n * same forwarded parameters are all registered, and `@x12i/api-hub-upstream` tries the cheapest connected\n * one first. A base route stays on one hub: the caller's path belongs to that API.\n */\nexport function passthroughCapability(entry: PassthroughEntry, sourcing: Sourcing): AnyCapability {\n const routes = orderedRoutes(entry);\n const upstreams = upstreamsFor(routes);\n return kindOf(entry.route) === 'base' ? baseCapability(entry, sourcing, upstreams) : endpointCapability(entry, sourcing, routes, upstreams);\n}\n\nconst PATH_CHARS = /^\\/[A-Za-z0-9\\-._~!$&'()*+,;=:@%/]*$/;\n\n/** True when `path` stays under the base path on the base host once a URL parser has normalised it. */\nexport function pathStaysUnder(base: string, path: string): boolean {\n if (!PATH_CHARS.test(path) || path.startsWith('//') || /%2e|%2f|%5c/i.test(path)) return false;\n const root = new URL(base);\n const prefix = root.pathname.replace(/\\/+$/, '');\n let target: URL;\n try {\n target = new URL(`${prefix}${path}`, root.origin);\n } catch {\n return false;\n }\n return target.origin === root.origin && (target.pathname === prefix || target.pathname.startsWith(`${prefix}/`));\n}\n\nfunction baseCapability(entry: PassthroughEntry, sourcing: Sourcing, upstreams: CatalogUpstream[]): AnyCapability {\n const { route } = entry;\n const base = route.url.replace(/\\/+$/, '');\n const input = z.object({\n method: z.enum(['GET', 'POST']).default('GET'),\n path: z\n .string()\n .max(500)\n .refine((p) => pathStaysUnder(base, p), { message: 'path must start with / and stay under the API base path' })\n .describe(`Path under ${base}, as ${route.api ?? 'the API'}'s documentation${route.docs ? ` (${route.docs})` : ''} names it`),\n query: z.record(z.string(), scalar).default({}),\n body: z.unknown().optional().describe('JSON body for POST'),\n });\n return defineCapability({\n slug: entry.slug,\n summary: entry.summary,\n input,\n output: passthroughOutput,\n sourcing,\n mode: 'sync',\n limits: { timeoutMs: 60_000, maxInputBytes: 1_000_000 },\n reuse: [],\n examples: [{ method: 'GET', path: '/', query: {} }],\n upstreams,\n proxy: {\n routes: [\n {\n hub: route.hub,\n ...(route.api ? { api: route.api } : {}),\n priceUsd: route.priceUsd,\n request: (i: z.output<typeof input>) => ({\n method: i.method,\n url: `${base}${i.path}`,\n query: i.query,\n ...(i.method === 'POST' && i.body !== undefined ? { json: i.body } : {}),\n }),\n response: (res: UpstreamResponse) => decode(res, route.hub),\n },\n ],\n },\n }) as unknown as AnyCapability;\n}\n\nfunction endpointCapability(entry: PassthroughEntry, sourcing: Sourcing, routes: PassthroughRoute[], upstreams: CatalogUpstream[]): AnyCapability {\n const shareable = routes.filter((route) => sameContract(entry.route, route));\n const fallbacks = shareable.slice(1).map((route) => route.hub);\n const params =\n entry.route.method === 'GET'\n ? z.record(z.string(), scalar).describe(`Query parameters forwarded to ${entry.route.hub}${fallbacks.length ? `, then ${fallbacks.join(', ')}` : ''}`)\n : z.record(z.string(), z.unknown()).describe(`JSON body forwarded to ${entry.route.hub}${fallbacks.length ? `, then ${fallbacks.join(', ')}` : ''}`);\n const input = z.object({ params: params.default({}) });\n return defineCapability({\n slug: entry.slug,\n summary: entry.summary,\n input,\n output: passthroughOutput,\n sourcing,\n mode: 'sync',\n limits: { timeoutMs: 60_000, maxInputBytes: 1_000_000 },\n reuse: [],\n examples: [{ params: {} }],\n upstreams,\n proxy: {\n routes: shareable.map((route) => ({\n hub: route.hub,\n ...(route.api ? { api: route.api } : {}),\n priceUsd: route.priceUsd,\n request: (i: z.output<typeof input>) =>\n route.method === 'GET'\n ? { method: 'GET' as const, url: route.url, query: i.params as Record<string, string | number | boolean> }\n : { method: 'POST' as const, url: route.url, json: i.params },\n response: (res: UpstreamResponse) => decode(res, route.hub),\n })),\n },\n }) as unknown as AnyCapability;\n}\n"]}
|
package/dist/registry-types.d.ts
CHANGED
|
@@ -44,6 +44,8 @@ export interface CapabilityEntry {
|
|
|
44
44
|
status: Status;
|
|
45
45
|
statusReason: string | null;
|
|
46
46
|
wave: Wave | null;
|
|
47
|
+
/** Another slug that is the same capability. Null when this slug is canonical. */
|
|
48
|
+
aliasOf: string | null;
|
|
47
49
|
listings: Listing[];
|
|
48
50
|
}
|
|
49
51
|
export interface HubEntry {
|
|
@@ -64,6 +66,10 @@ export interface Registry {
|
|
|
64
66
|
sourcesWithListings: number;
|
|
65
67
|
hubRows: number;
|
|
66
68
|
capabilities: number;
|
|
69
|
+
/** Slugs that are the same capability as `aliasOf`. They stay listed. */
|
|
70
|
+
aliasCapabilities: number;
|
|
71
|
+
/** `capabilities` minus `aliasCapabilities`. */
|
|
72
|
+
uniqueCapabilities: number;
|
|
67
73
|
multiSourceCapabilities: number;
|
|
68
74
|
listingSourcing: Record<Sourcing, number>;
|
|
69
75
|
plans: Record<Plan, number>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry-types.js","sourceRoot":"","sources":["../../../../../packages/core/src/registry-types.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAU,CAAC;AAG5D,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,MAAM;IACN,YAAY;IACZ,SAAS;IACT,eAAe;IACf,WAAW;IACX,YAAY;IACZ,UAAU;CACF,CAAC;AAGX,MAAM,CAAC,MAAM,KAAK,GAAG;IACnB,WAAW;IACX,aAAa;IACb,aAAa;IACb,wBAAwB;IACxB,YAAY;CACJ,CAAC;AAGX,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,CAAU,CAAC","sourcesContent":["export const SOURCINGS = ['NATIVE', 'AI', 'PROXY'] as const;\nexport type Sourcing = (typeof SOURCINGS)[number];\n\nexport const STATUSES = [\n 'live',\n 'live-proxy',\n 'planned',\n 'blocked-terms',\n 'connector',\n 'local-only',\n 'excluded',\n] as const;\nexport type Status = (typeof STATUSES)[number];\n\nexport const PLANS = [\n 'Build now',\n 'Proxy first',\n 'Build later',\n 'Bring your own account',\n 'Local only',\n] as const;\nexport type Plan = (typeof PLANS)[number];\n\nexport const EFFORTS = ['XS', 'S', 'M', 'L', 'XL'] as const;\nexport type Effort = (typeof EFFORTS)[number];\n\nexport type Wave = 2 | 3 | 4 | 5;\n\nexport interface Listing {\n hub: string;\n api: string;\n hubCategory: string | null;\n description: string | null;\n price: string | null;\n sourcing: Sourcing;\n effort: Effort | null;\n effortDays: number | null;\n builtOn: string | null;\n x12iToday: string | null;\n x12iPackages: string[];\n x12iBasis: string | null;\n apimesaEquivalent: string | null;\n confidence: string | null;\n hostable: string | null;\n url: string | null;\n}\n\nexport interface CapabilityEntry {\n slug: string;\n exampleApi: string | null;\n hubs: string[];\n hubCount: number;\n listingCount: number;\n sourcing: Sourcing;\n labelsDisagree: boolean;\n effort: Effort | null;\n effortDays: number | null;\n inApimesa: boolean;\n x12iToday: string | null;\n x12iPackages: string[];\n hostable: string | null;\n plan: Plan;\n hubCategories: string[];\n status: Status;\n statusReason: string | null;\n wave: Wave | null;\n listings: Listing[];\n}\n\nexport interface HubEntry {\n name: string;\n url: string | null;\n kind: string | null;\n listings: number;\n coverage: string | null;\n resaleTerms: string | null;\n}\n\nexport interface Registry {\n source: { file: string; sha256: string };\n totals: {\n listings: number;\n sourcesWithListings: number;\n hubRows: number;\n capabilities: number;\n multiSourceCapabilities: number;\n listingSourcing: Record<Sourcing, number>;\n plans: Record<Plan, number>;\n statuses: Record<Status, number>;\n waves: Record<string, number>;\n };\n hubs: HubEntry[];\n capabilities: CapabilityEntry[];\n}\n\nexport type CapabilitySummary = Omit<CapabilityEntry, 'listings'>;\n"]}
|
|
1
|
+
{"version":3,"file":"registry-types.js","sourceRoot":"","sources":["../../../../../packages/core/src/registry-types.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAU,CAAC;AAG5D,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,MAAM;IACN,YAAY;IACZ,SAAS;IACT,eAAe;IACf,WAAW;IACX,YAAY;IACZ,UAAU;CACF,CAAC;AAGX,MAAM,CAAC,MAAM,KAAK,GAAG;IACnB,WAAW;IACX,aAAa;IACb,aAAa;IACb,wBAAwB;IACxB,YAAY;CACJ,CAAC;AAGX,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,CAAU,CAAC","sourcesContent":["export const SOURCINGS = ['NATIVE', 'AI', 'PROXY'] as const;\nexport type Sourcing = (typeof SOURCINGS)[number];\n\nexport const STATUSES = [\n 'live',\n 'live-proxy',\n 'planned',\n 'blocked-terms',\n 'connector',\n 'local-only',\n 'excluded',\n] as const;\nexport type Status = (typeof STATUSES)[number];\n\nexport const PLANS = [\n 'Build now',\n 'Proxy first',\n 'Build later',\n 'Bring your own account',\n 'Local only',\n] as const;\nexport type Plan = (typeof PLANS)[number];\n\nexport const EFFORTS = ['XS', 'S', 'M', 'L', 'XL'] as const;\nexport type Effort = (typeof EFFORTS)[number];\n\nexport type Wave = 2 | 3 | 4 | 5;\n\nexport interface Listing {\n hub: string;\n api: string;\n hubCategory: string | null;\n description: string | null;\n price: string | null;\n sourcing: Sourcing;\n effort: Effort | null;\n effortDays: number | null;\n builtOn: string | null;\n x12iToday: string | null;\n x12iPackages: string[];\n x12iBasis: string | null;\n apimesaEquivalent: string | null;\n confidence: string | null;\n hostable: string | null;\n url: string | null;\n}\n\nexport interface CapabilityEntry {\n slug: string;\n exampleApi: string | null;\n hubs: string[];\n hubCount: number;\n listingCount: number;\n sourcing: Sourcing;\n labelsDisagree: boolean;\n effort: Effort | null;\n effortDays: number | null;\n inApimesa: boolean;\n x12iToday: string | null;\n x12iPackages: string[];\n hostable: string | null;\n plan: Plan;\n hubCategories: string[];\n status: Status;\n statusReason: string | null;\n wave: Wave | null;\n /** Another slug that is the same capability. Null when this slug is canonical. */\n aliasOf: string | null;\n listings: Listing[];\n}\n\nexport interface HubEntry {\n name: string;\n url: string | null;\n kind: string | null;\n listings: number;\n coverage: string | null;\n resaleTerms: string | null;\n}\n\nexport interface Registry {\n source: { file: string; sha256: string };\n totals: {\n listings: number;\n sourcesWithListings: number;\n hubRows: number;\n capabilities: number;\n /** Slugs that are the same capability as `aliasOf`. They stay listed. */\n aliasCapabilities: number;\n /** `capabilities` minus `aliasCapabilities`. */\n uniqueCapabilities: number;\n multiSourceCapabilities: number;\n listingSourcing: Record<Sourcing, number>;\n plans: Record<Plan, number>;\n statuses: Record<Status, number>;\n waves: Record<string, number>;\n };\n hubs: HubEntry[];\n capabilities: CapabilityEntry[];\n}\n\nexport type CapabilitySummary = Omit<CapabilityEntry, 'listings'>;\n"]}
|
package/dist/registry.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type AnyCapability } from './capability.js';
|
|
2
2
|
import type { CapabilityEntry, CapabilitySummary, Registry, Sourcing, Status } from './registry-types.js';
|
|
3
3
|
export declare function findRepoRoot(start?: string): string;
|
|
4
4
|
export declare function readRegistryFile(path: string): Registry;
|
|
@@ -9,12 +9,16 @@ export interface CapabilityFilter {
|
|
|
9
9
|
sourcing?: Sourcing | Sourcing[];
|
|
10
10
|
wave?: number;
|
|
11
11
|
q?: string;
|
|
12
|
+
/** Hide slugs that alias another capability. The full catalog stays available without this. */
|
|
13
|
+
unique?: boolean;
|
|
12
14
|
limit?: number;
|
|
13
15
|
offset?: number;
|
|
14
16
|
}
|
|
15
17
|
export interface CatalogItem extends CapabilitySummary {
|
|
16
18
|
summary: string | null;
|
|
17
19
|
mode: 'sync' | 'job' | null;
|
|
20
|
+
/** True when more than one hub is called with the same input if an earlier one fails. */
|
|
21
|
+
failover: boolean;
|
|
18
22
|
}
|
|
19
23
|
export declare class CapabilityRegistry {
|
|
20
24
|
readonly registry: Registry;
|
|
@@ -31,6 +35,7 @@ export declare class CapabilityRegistry {
|
|
|
31
35
|
private toItem;
|
|
32
36
|
list(filter?: CapabilityFilter): {
|
|
33
37
|
total: number;
|
|
38
|
+
unique: number;
|
|
34
39
|
items: CatalogItem[];
|
|
35
40
|
};
|
|
36
41
|
}
|
package/dist/registry.js
CHANGED
|
@@ -9,6 +9,7 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
|
|
|
9
9
|
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
10
10
|
import { dirname, join, resolve } from 'node:path';
|
|
11
11
|
import { pathToFileURL } from 'node:url';
|
|
12
|
+
import { upstreamsOf } from "./capability.js";
|
|
12
13
|
import { passthroughCapability, readPassthroughFile } from "./passthrough.js";
|
|
13
14
|
export function findRepoRoot(start = process.cwd()) {
|
|
14
15
|
let dir = resolve(start);
|
|
@@ -97,7 +98,7 @@ export class CapabilityRegistry {
|
|
|
97
98
|
toItem(entry) {
|
|
98
99
|
const { listings: _listings, ...summary } = entry;
|
|
99
100
|
const impl = this.served(entry.slug);
|
|
100
|
-
return { ...summary, summary: impl?.summary ?? null, mode: impl?.mode ?? null };
|
|
101
|
+
return { ...summary, summary: impl?.summary ?? null, mode: impl?.mode ?? null, failover: upstreamsOf(impl).some((hub) => hub.failover) };
|
|
101
102
|
}
|
|
102
103
|
list(filter = {}) {
|
|
103
104
|
const statuses = filter.status ? new Set([filter.status].flat()) : undefined;
|
|
@@ -116,9 +117,11 @@ export class CapabilityRegistry {
|
|
|
116
117
|
.sort((a, b) => b.score - a.score || b.c.hubCount - a.c.hubCount || a.c.slug.localeCompare(b.c.slug));
|
|
117
118
|
matched = scored.map((s) => s.c);
|
|
118
119
|
}
|
|
120
|
+
const unique = matched.filter((c) => !c.aliasOf).length;
|
|
121
|
+
const visible = filter.unique ? matched.filter((c) => !c.aliasOf) : matched;
|
|
119
122
|
const offset = filter.offset ?? 0;
|
|
120
|
-
const page = filter.limit === undefined ?
|
|
121
|
-
return { total:
|
|
123
|
+
const page = filter.limit === undefined ? visible.slice(offset) : visible.slice(offset, offset + filter.limit);
|
|
124
|
+
return { total: visible.length, unique: filter.unique ? visible.length : unique, items: page.map((c) => this.toItem(c)) };
|
|
122
125
|
}
|
|
123
126
|
}
|
|
124
127
|
function scoreEntry(entry, terms, summary) {
|
package/dist/registry.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../../../packages/core/src/registry.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, readdirSync, readFileSync } from 'node:fs';\nimport { dirname, join, resolve } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport type { AnyCapability } from './capability.ts';\nimport { passthroughCapability, readPassthroughFile } from './passthrough.ts';\nimport type { CapabilityEntry, CapabilitySummary, Registry, Sourcing, Status } from './registry-types.ts';\n\nexport function findRepoRoot(start: string = process.cwd()): string {\n let dir = resolve(start);\n for (;;) {\n if (existsSync(join(dir, 'pnpm-workspace.yaml'))) return dir;\n const parent = dirname(dir);\n if (parent === dir) throw new Error(`Could not find the repo root above ${start}`);\n dir = parent;\n }\n}\n\nexport function readRegistryFile(path: string): Registry {\n return JSON.parse(readFileSync(path, 'utf8')) as Registry;\n}\n\nexport function listCapabilityFolders(capabilitiesDir: string): string[] {\n if (!existsSync(capabilitiesDir)) return [];\n return readdirSync(capabilitiesDir, { withFileTypes: true })\n .filter((d) => d.isDirectory() && existsSync(join(capabilitiesDir, d.name, 'capability.ts')))\n .map((d) => d.name)\n .sort();\n}\n\nexport async function discoverCapabilities(capabilitiesDir: string): Promise<Map<string, AnyCapability>> {\n const found = new Map<string, AnyCapability>();\n for (const folder of listCapabilityFolders(capabilitiesDir)) {\n const mod = (await import(pathToFileURL(join(capabilitiesDir, folder, 'capability.ts')).href)) as {\n default?: AnyCapability;\n };\n const definition = mod.default;\n if (!definition) throw new Error(`capabilities/${folder}/capability.ts has no default export`);\n if (definition.slug !== folder) {\n throw new Error(`capabilities/${folder} defines slug \"${definition.slug}\"; the folder name must match`);\n }\n found.set(folder, definition);\n }\n return found;\n}\n\nexport interface CapabilityFilter {\n status?: Status | Status[];\n sourcing?: Sourcing | Sourcing[];\n wave?: number;\n q?: string;\n limit?: number;\n offset?: number;\n}\n\nexport interface CatalogItem extends CapabilitySummary {\n summary: string | null;\n mode: 'sync' | 'job' | null;\n}\n\nconst SERVED: ReadonlySet<Status> = new Set(['live', 'live-proxy']);\n\nexport class CapabilityRegistry {\n readonly registry: Registry;\n private readonly entries: Map<string, CapabilityEntry>;\n private readonly implementations: Map<string, AnyCapability>;\n\n constructor(registry: Registry, implementations: Map<string, AnyCapability>) {\n this.registry = registry;\n this.entries = new Map(registry.capabilities.map((c) => [c.slug, c]));\n this.implementations = implementations;\n for (const slug of implementations.keys()) {\n if (!this.entries.has(slug)) {\n throw new Error(`capabilities/${slug} is not a workbook capability; propose it in catalog/overrides.yaml first`);\n }\n }\n }\n\n static async load(root: string = findRepoRoot()): Promise<CapabilityRegistry> {\n const registry = readRegistryFile(join(root, 'catalog', 'registry.json'));\n const implementations = await discoverCapabilities(join(root, 'capabilities'));\n const sourcing = new Map(registry.capabilities.map((c) => [c.slug, c.sourcing]));\n for (const entry of readPassthroughFile(join(root, 'catalog', 'proxy-routes.json')).entries) {\n const known = sourcing.get(entry.slug);\n if (known && !implementations.has(entry.slug)) implementations.set(entry.slug, passthroughCapability(entry, known));\n }\n return new CapabilityRegistry(registry, implementations);\n }\n\n get size(): number {\n return this.entries.size;\n }\n\n entry(slug: string): CapabilityEntry | undefined {\n return this.entries.get(slug);\n }\n\n /** The implementation, only when the registry says the capability is served. */\n served(slug: string): AnyCapability | undefined {\n const entry = this.entries.get(slug);\n if (!entry || !SERVED.has(entry.status)) return undefined;\n return this.implementations.get(slug);\n }\n\n servedCapabilities(): AnyCapability[] {\n return [...this.implementations.values()].filter((c) => this.served(c.slug));\n }\n\n item(slug: string): CatalogItem | undefined {\n const entry = this.entries.get(slug);\n if (!entry) return undefined;\n return this.toItem(entry);\n }\n\n private toItem(entry: CapabilityEntry): CatalogItem {\n const { listings: _listings, ...summary } = entry;\n const impl = this.served(entry.slug);\n return { ...summary, summary: impl?.summary ?? null, mode: impl?.mode ?? null };\n }\n\n list(filter: CapabilityFilter = {}): { total: number; items: CatalogItem[] } {\n const statuses = filter.status ? new Set([filter.status].flat()) : undefined;\n const sourcings = filter.sourcing ? new Set([filter.sourcing].flat()) : undefined;\n const terms = (filter.q ?? '')\n .toLowerCase()\n .split(/[^a-z0-9]+/)\n .filter(Boolean);\n\n let matched = this.registry.capabilities.filter(\n (c) =>\n (!statuses || statuses.has(c.status)) &&\n (!sourcings || sourcings.has(c.sourcing)) &&\n (filter.wave === undefined || c.wave === filter.wave),\n );\n\n if (terms.length > 0) {\n const scored = matched\n .map((c) => ({ c, score: scoreEntry(c, terms, this.implementations.get(c.slug)?.summary) }))\n .filter((s) => s.score > 0)\n .sort((a, b) => b.score - a.score || b.c.hubCount - a.c.hubCount || a.c.slug.localeCompare(b.c.slug));\n matched = scored.map((s) => s.c);\n }\n\n const offset = filter.offset ?? 0;\n const page = filter.limit === undefined ? matched.slice(offset) : matched.slice(offset, offset + filter.limit);\n return { total: matched.length, items: page.map((c) => this.toItem(c)) };\n }\n}\n\nfunction scoreEntry(entry: CapabilityEntry, terms: string[], summary?: string): number {\n const slug = entry.slug.toLowerCase();\n const haystack = [entry.exampleApi ?? '', entry.hubCategories.join(' '), summary ?? ''].join(' ').toLowerCase();\n let score = 0;\n for (const term of terms) {\n if (slug === term) score += 10;\n else if (slug.split('-').includes(term)) score += 5;\n else if (slug.includes(term)) score += 3;\n else if (haystack.includes(term)) score += 1;\n else return 0;\n }\n return score;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../../../packages/core/src/registry.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, readdirSync, readFileSync } from 'node:fs';\nimport { dirname, join, resolve } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport { upstreamsOf, type AnyCapability } from './capability.ts';\nimport { passthroughCapability, readPassthroughFile } from './passthrough.ts';\nimport type { CapabilityEntry, CapabilitySummary, Registry, Sourcing, Status } from './registry-types.ts';\n\nexport function findRepoRoot(start: string = process.cwd()): string {\n let dir = resolve(start);\n for (;;) {\n if (existsSync(join(dir, 'pnpm-workspace.yaml'))) return dir;\n const parent = dirname(dir);\n if (parent === dir) throw new Error(`Could not find the repo root above ${start}`);\n dir = parent;\n }\n}\n\nexport function readRegistryFile(path: string): Registry {\n return JSON.parse(readFileSync(path, 'utf8')) as Registry;\n}\n\nexport function listCapabilityFolders(capabilitiesDir: string): string[] {\n if (!existsSync(capabilitiesDir)) return [];\n return readdirSync(capabilitiesDir, { withFileTypes: true })\n .filter((d) => d.isDirectory() && existsSync(join(capabilitiesDir, d.name, 'capability.ts')))\n .map((d) => d.name)\n .sort();\n}\n\nexport async function discoverCapabilities(capabilitiesDir: string): Promise<Map<string, AnyCapability>> {\n const found = new Map<string, AnyCapability>();\n for (const folder of listCapabilityFolders(capabilitiesDir)) {\n const mod = (await import(pathToFileURL(join(capabilitiesDir, folder, 'capability.ts')).href)) as {\n default?: AnyCapability;\n };\n const definition = mod.default;\n if (!definition) throw new Error(`capabilities/${folder}/capability.ts has no default export`);\n if (definition.slug !== folder) {\n throw new Error(`capabilities/${folder} defines slug \"${definition.slug}\"; the folder name must match`);\n }\n found.set(folder, definition);\n }\n return found;\n}\n\nexport interface CapabilityFilter {\n status?: Status | Status[];\n sourcing?: Sourcing | Sourcing[];\n wave?: number;\n q?: string;\n /** Hide slugs that alias another capability. The full catalog stays available without this. */\n unique?: boolean;\n limit?: number;\n offset?: number;\n}\n\nexport interface CatalogItem extends CapabilitySummary {\n summary: string | null;\n mode: 'sync' | 'job' | null;\n /** True when more than one hub is called with the same input if an earlier one fails. */\n failover: boolean;\n}\n\nconst SERVED: ReadonlySet<Status> = new Set(['live', 'live-proxy']);\n\nexport class CapabilityRegistry {\n readonly registry: Registry;\n private readonly entries: Map<string, CapabilityEntry>;\n private readonly implementations: Map<string, AnyCapability>;\n\n constructor(registry: Registry, implementations: Map<string, AnyCapability>) {\n this.registry = registry;\n this.entries = new Map(registry.capabilities.map((c) => [c.slug, c]));\n this.implementations = implementations;\n for (const slug of implementations.keys()) {\n if (!this.entries.has(slug)) {\n throw new Error(`capabilities/${slug} is not a workbook capability; propose it in catalog/overrides.yaml first`);\n }\n }\n }\n\n static async load(root: string = findRepoRoot()): Promise<CapabilityRegistry> {\n const registry = readRegistryFile(join(root, 'catalog', 'registry.json'));\n const implementations = await discoverCapabilities(join(root, 'capabilities'));\n const sourcing = new Map(registry.capabilities.map((c) => [c.slug, c.sourcing]));\n for (const entry of readPassthroughFile(join(root, 'catalog', 'proxy-routes.json')).entries) {\n const known = sourcing.get(entry.slug);\n if (known && !implementations.has(entry.slug)) implementations.set(entry.slug, passthroughCapability(entry, known));\n }\n return new CapabilityRegistry(registry, implementations);\n }\n\n get size(): number {\n return this.entries.size;\n }\n\n entry(slug: string): CapabilityEntry | undefined {\n return this.entries.get(slug);\n }\n\n /** The implementation, only when the registry says the capability is served. */\n served(slug: string): AnyCapability | undefined {\n const entry = this.entries.get(slug);\n if (!entry || !SERVED.has(entry.status)) return undefined;\n return this.implementations.get(slug);\n }\n\n servedCapabilities(): AnyCapability[] {\n return [...this.implementations.values()].filter((c) => this.served(c.slug));\n }\n\n item(slug: string): CatalogItem | undefined {\n const entry = this.entries.get(slug);\n if (!entry) return undefined;\n return this.toItem(entry);\n }\n\n private toItem(entry: CapabilityEntry): CatalogItem {\n const { listings: _listings, ...summary } = entry;\n const impl = this.served(entry.slug);\n return { ...summary, summary: impl?.summary ?? null, mode: impl?.mode ?? null, failover: upstreamsOf(impl).some((hub) => hub.failover) };\n }\n\n list(filter: CapabilityFilter = {}): { total: number; unique: number; items: CatalogItem[] } {\n const statuses = filter.status ? new Set([filter.status].flat()) : undefined;\n const sourcings = filter.sourcing ? new Set([filter.sourcing].flat()) : undefined;\n const terms = (filter.q ?? '')\n .toLowerCase()\n .split(/[^a-z0-9]+/)\n .filter(Boolean);\n\n let matched = this.registry.capabilities.filter(\n (c) =>\n (!statuses || statuses.has(c.status)) &&\n (!sourcings || sourcings.has(c.sourcing)) &&\n (filter.wave === undefined || c.wave === filter.wave),\n );\n\n if (terms.length > 0) {\n const scored = matched\n .map((c) => ({ c, score: scoreEntry(c, terms, this.implementations.get(c.slug)?.summary) }))\n .filter((s) => s.score > 0)\n .sort((a, b) => b.score - a.score || b.c.hubCount - a.c.hubCount || a.c.slug.localeCompare(b.c.slug));\n matched = scored.map((s) => s.c);\n }\n\n const unique = matched.filter((c) => !c.aliasOf).length;\n const visible = filter.unique ? matched.filter((c) => !c.aliasOf) : matched;\n const offset = filter.offset ?? 0;\n const page = filter.limit === undefined ? visible.slice(offset) : visible.slice(offset, offset + filter.limit);\n return { total: visible.length, unique: filter.unique ? visible.length : unique, items: page.map((c) => this.toItem(c)) };\n }\n}\n\nfunction scoreEntry(entry: CapabilityEntry, terms: string[], summary?: string): number {\n const slug = entry.slug.toLowerCase();\n const haystack = [entry.exampleApi ?? '', entry.hubCategories.join(' '), summary ?? ''].join(' ').toLowerCase();\n let score = 0;\n for (const term of terms) {\n if (slug === term) score += 10;\n else if (slug.split('-').includes(term)) score += 5;\n else if (slug.includes(term)) score += 3;\n else if (haystack.includes(term)) score += 1;\n else return 0;\n }\n return score;\n}\n"]}
|