From 6420cd640fad76cd68a5e8ef9f416383f86cadde Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Fri, 10 May 2024 09:34:21 -0700 Subject: [PATCH 1/3] fix(server): loosen requirements on statics --- packages/interface/src/lib.ts | 21 +++++++++++---------- packages/server/src/server.js | 28 ++++++++++++++++------------ 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/packages/interface/src/lib.ts b/packages/interface/src/lib.ts index 450b4176..7a56d005 100644 --- a/packages/interface/src/lib.ts +++ b/packages/interface/src/lib.ts @@ -988,7 +988,7 @@ export interface ValidatorOptions { validateAuthorization: (proofs: Authorization) => Await> } -export interface ServerOptions extends ValidatorOptions { +export interface ServerOptions extends ValidatorOptions { /** * Service DID which will be used to verify that received invocation * audience matches it. @@ -996,6 +996,13 @@ export interface ServerOptions extends ValidatorOptions { readonly id: Signer readonly codec: InboundCodec + + /** + * Actual service providing capability handlers. + */ + readonly service: T + + readonly catch?: (err: HandlerExecutionError) => void } /** @@ -1005,13 +1012,9 @@ export interface ServerOptions extends ValidatorOptions { * Used as input to {@link @ucanto/server#create | `Server.create` } when * defining a service implementation. */ -export interface Server extends ServerOptions { - /** - * Actual service providing capability handlers. - */ - readonly service: T - - readonly catch?: (err: HandlerExecutionError) => void +export interface Server extends ServerOptions { + readonly context: InvocationContext + readonly catch: (err: HandlerExecutionError) => void } /** @@ -1025,8 +1028,6 @@ export interface Server extends ServerOptions { export interface ServerView> extends Server, Transport.Channel { - context: InvocationContext - catch: (err: HandlerExecutionError) => void run( invocation: ServiceInvocation ): Await> diff --git a/packages/server/src/server.js b/packages/server/src/server.js index cad98e27..f282c873 100644 --- a/packages/server/src/server.js +++ b/packages/server/src/server.js @@ -14,13 +14,10 @@ export { fail } * Creates a connection to a service. * * @template {Record} Service - * @param {API.Server} options + * @param {API.ServerOptions} options * @returns {API.ServerView} */ -export const create = options => { - const server = new Server(options) - return server -} +export const create = options => new Server(options) /** * @template {Record} S @@ -28,7 +25,7 @@ export const create = options => { */ class Server { /** - * @param {API.Server} options + * @param {API.ServerOptions } options */ constructor({ id, service, codec, principal = Verifier, ...rest }) { const { catch: fail, ...context } = rest @@ -69,7 +66,7 @@ class Server { /** * @template {Record} S * @template {API.Tuple>} I - * @param {API.ServerView} server + * @param {API.Server} server * @param {API.HTTPRequest, Out: API.Tuple }>>} request */ export const handle = async (server, request) => { @@ -94,11 +91,11 @@ export const handle = async (server, request) => { * @template {Record} S * @template {API.Tuple} I * @param {API.AgentMessage<{ In: API.InferInvocations, Out: API.Tuple }>} input - * @param {API.ServerView} server + * @param {API.Server} server * @returns {Promise, In: API.Tuple }>>} */ export const execute = async (input, server) => { - const promises = input.invocations.map($ => invoke($, server)) + const promises = input.invocations.map($ => run($, server)) const receipts = /** @type {API.InferReceipts} */ ( await Promise.all(promises) @@ -108,13 +105,15 @@ export const execute = async (input, server) => { } /** + * Executes a single invocation and returns a receipt. + * * @template {Record} Service * @template {API.Capability} C * @param {API.Invocation} invocation - * @param {API.ServerView} server + * @param {API.Server} server * @returns {Promise} */ -export const invoke = async (invocation, server) => { +export const run = async (invocation, server) => { // Invocation needs to have one single capability if (invocation.capabilities.length !== 1) { return await Receipt.issue({ @@ -170,6 +169,11 @@ export const invoke = async (invocation, server) => { } } +/** + * @deprecated Use `run` instead. + */ +export const invoke = run + /** * @implements {API.HandlerNotFound} */ @@ -276,7 +280,7 @@ class InvocationCapabilityError extends Error { * @returns {null|Record>} */ -const resolve = (service, path) => { +export const resolve = (service, path) => { let target = service for (const key of path) { target = target[key] From d8df3aa1c13fb9cfb75aa666e6306177a0d5e801 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Fri, 10 May 2024 10:32:08 -0700 Subject: [PATCH 2/3] chore: expose more error classes --- packages/server/src/server.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/server/src/server.js b/packages/server/src/server.js index f282c873..fa680f0d 100644 --- a/packages/server/src/server.js +++ b/packages/server/src/server.js @@ -208,7 +208,7 @@ export class HandlerNotFound extends RangeError { } } -class HandlerExecutionError extends Failure { +export class HandlerExecutionError extends Failure { /** * @param {API.Capability} capability * @param {Error} cause @@ -248,7 +248,7 @@ class HandlerExecutionError extends Failure { } } -class InvocationCapabilityError extends Error { +export class InvocationCapabilityError extends Error { /** * @param {any} caps */ From efd74008fd6755e3b1e32590debea6b2fe30f18f Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Fri, 10 May 2024 10:50:20 -0700 Subject: [PATCH 3/3] fix: export collisions --- packages/server/src/error.js | 103 ++++++++++++++++++++++++++++++ packages/server/src/lib.js | 11 +--- packages/server/src/server.js | 115 +++------------------------------- 3 files changed, 112 insertions(+), 117 deletions(-) create mode 100644 packages/server/src/error.js diff --git a/packages/server/src/error.js b/packages/server/src/error.js new file mode 100644 index 00000000..9a278946 --- /dev/null +++ b/packages/server/src/error.js @@ -0,0 +1,103 @@ +import * as API from '@ucanto/interface' +import { Failure } from '@ucanto/core' +export { MalformedCapability } from '@ucanto/validator' + +/** + * @implements {API.HandlerNotFound} + */ +export class HandlerNotFound extends RangeError { + /** + * @param {API.Capability} capability + */ + constructor(capability) { + super() + /** @type {true} */ + this.error = true + this.capability = capability + } + /** @type {'HandlerNotFound'} */ + get name() { + return 'HandlerNotFound' + } + get message() { + return `service does not implement {can: "${this.capability.can}"} handler` + } + toJSON() { + return { + name: this.name, + error: this.error, + capability: { + can: this.capability.can, + with: this.capability.with, + }, + message: this.message, + stack: this.stack, + } + } +} + +export class HandlerExecutionError extends Failure { + /** + * @param {API.Capability} capability + * @param {Error} cause + */ + constructor(capability, cause) { + super() + this.capability = capability + this.cause = cause + /** @type { true } */ + this.error = true + } + + /** @type {'HandlerExecutionError'} */ + get name() { + return 'HandlerExecutionError' + } + get message() { + return `service handler {can: "${this.capability.can}"} error: ${this.cause.message}` + } + toJSON() { + return { + name: this.name, + error: this.error, + capability: { + can: this.capability.can, + with: this.capability.with, + }, + cause: { + ...this.cause, + name: this.cause.name, + message: this.cause.message, + stack: this.cause.stack, + }, + message: this.message, + stack: this.stack, + } + } +} + +export class InvocationCapabilityError extends Error { + /** + * @param {any} caps + */ + constructor(caps) { + super() + /** @type {true} */ + this.error = true + this.caps = caps + } + get name() { + return 'InvocationCapabilityError' + } + get message() { + return `Invocation is required to have a single capability.` + } + toJSON() { + return { + name: this.name, + error: this.error, + message: this.message, + capabilities: this.caps, + } + } +} diff --git a/packages/server/src/lib.js b/packages/server/src/lib.js index 92ac2407..88efb4c8 100644 --- a/packages/server/src/lib.js +++ b/packages/server/src/lib.js @@ -1,15 +1,7 @@ export * from './api.js' export * from '@ucanto/core' export * from './server.js' -export { - Failure, - MalformedCapability, - HandlerNotFound, - Link, - URI, - ok, - error, -} from './server.js' +export { Failure, Link, URI, ok, error } from './server.js' export { invoke, Invocation, @@ -23,3 +15,4 @@ export { access, claim, Schema } from '@ucanto/validator' export * from './handler.js' export * as API from './api.js' +export * as Error from './error.js' diff --git a/packages/server/src/server.js b/packages/server/src/server.js index fa680f0d..2ac1d784 100644 --- a/packages/server/src/server.js +++ b/packages/server/src/server.js @@ -1,13 +1,12 @@ import * as API from '@ucanto/interface' import { Verifier } from '@ucanto/principal' -export { - capability, - URI, - Link, - Failure, - MalformedCapability, -} from '@ucanto/validator' -import { Receipt, Message, Failure, fail } from '@ucanto/core' +export { capability, URI, Link, Failure } from '@ucanto/validator' +import { Receipt, Message, fail } from '@ucanto/core' +import { + HandlerExecutionError, + HandlerNotFound, + InvocationCapabilityError, +} from './error.js' export { ok, error } from './handler.js' export { fail } /** @@ -174,106 +173,6 @@ export const run = async (invocation, server) => { */ export const invoke = run -/** - * @implements {API.HandlerNotFound} - */ -export class HandlerNotFound extends RangeError { - /** - * @param {API.Capability} capability - */ - constructor(capability) { - super() - /** @type {true} */ - this.error = true - this.capability = capability - } - /** @type {'HandlerNotFound'} */ - get name() { - return 'HandlerNotFound' - } - get message() { - return `service does not implement {can: "${this.capability.can}"} handler` - } - toJSON() { - return { - name: this.name, - error: this.error, - capability: { - can: this.capability.can, - with: this.capability.with, - }, - message: this.message, - stack: this.stack, - } - } -} - -export class HandlerExecutionError extends Failure { - /** - * @param {API.Capability} capability - * @param {Error} cause - */ - constructor(capability, cause) { - super() - this.capability = capability - this.cause = cause - /** @type { true } */ - this.error = true - } - - /** @type {'HandlerExecutionError'} */ - get name() { - return 'HandlerExecutionError' - } - get message() { - return `service handler {can: "${this.capability.can}"} error: ${this.cause.message}` - } - toJSON() { - return { - name: this.name, - error: this.error, - capability: { - can: this.capability.can, - with: this.capability.with, - }, - cause: { - ...this.cause, - name: this.cause.name, - message: this.cause.message, - stack: this.cause.stack, - }, - message: this.message, - stack: this.stack, - } - } -} - -export class InvocationCapabilityError extends Error { - /** - * @param {any} caps - */ - constructor(caps) { - super() - /** @type {true} */ - this.error = true - this.caps = caps - } - get name() { - return 'InvocationCapabilityError' - } - get message() { - return `Invocation is required to have a single capability.` - } - toJSON() { - return { - name: this.name, - error: this.error, - message: this.message, - capabilities: this.caps, - } - } -} - /** * @param {Record} service * @param {string[]} path