All files / sample-services/src/vat-lib contact-endpoint.ts

0% Statements 0/11
0% Branches 0/4
0% Functions 0/4
0% Lines 0/11

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105                                                                                                                                                                                                                 
import { makeDefaultExo } from '@metamask/kernel-utils/exo';
import type {
  ContactPoint,
  ContactResponse,
  RemotableSpec,
  ServiceContactInfo,
  ServiceDescription,
  ServicePoint,
} from '@metamask/service-discovery-types';
 
/**
 * Build a ContactPoint exo for a service with the Public access model.
 *
 * The returned exo reports its ServiceDescription on demand from
 * `getServiceDescription()`, computing the description lazily so the
 * contact URL (set post-issuance via the `getContactUrl` closure) is
 * always current. It validates the matcher's registration callback via
 * `confirmServiceRegistration(token)` by comparing against `expectedToken`,
 * and returns `service` directly from `initiateContact()` (Public model).
 *
 * Registration tokens are single-use in this phase; once consumed,
 * subsequent confirmation attempts throw.
 *
 * @param options - Construction options.
 * @param options.name - Exo name, used in alleged type tags and the
 * top-level spec property.
 * @param options.service - The service exo to vend on `initiateContact()`.
 * @param options.description - Natural-language description of the
 * service as a whole.
 * @param options.remotableSpec - Pre-computed remotable API spec.
 * @param options.getContactUrl - Closure returning this endpoint's URL.
 * Typically captures a `let` in the vat root that is assigned after
 * `ocapURLIssuerService.issue(...)` resolves.
 * @param options.expectedToken - The registration token the matcher must
 * present to validate registration.
 * @param options.providerTag - The provider-local identifier for the
 * service. Must be unique among services hosted by this provider and
 * must persist across restarts of the same logical service; the matcher
 * uses (peerId, providerTag) as the dedup key when re-registrations
 * arrive.
 * @returns A ContactPoint exo.
 */
export function makeContactEndpoint(options: {
  name: string;
  service: ServicePoint;
  description: string;
  remotableSpec: RemotableSpec;
  getContactUrl: () => string;
  expectedToken: string;
  providerTag: string;
}): ContactPoint {
  const {
    name,
    service,
    description,
    remotableSpec,
    getContactUrl,
    expectedToken,
    providerTag,
  } = options;
  let consumed = false;
 
  return makeDefaultExo(`${name}ContactEndpoint`, {
    async getServiceDescription(): Promise<ServiceDescription> {
      const contact: ServiceContactInfo[] = [
        { contactType: 'public', contactUrl: getContactUrl() },
      ];
      return harden({
        apiSpec: harden({
          properties: harden({
            service: harden({
              description: `The ${name} service.`,
              type: harden({
                kind: 'remotable' as const,
                spec: remotableSpec,
              }),
            }),
          }),
        }),
        description,
        contact: harden(contact),
        providerTag,
      });
    },
 
    async confirmServiceRegistration(registrationToken: string): Promise<void> {
      if (consumed) {
        throw new Error(
          `Registration token for ${name} has already been confirmed.`,
        );
      }
      if (registrationToken !== expectedToken) {
        throw new Error(
          `Registration token mismatch for ${name}: matcher presented an unrecognized token.`,
        );
      }
      consumed = true;
    },
 
    async initiateContact(): Promise<ContactResponse> {
      return harden({ kind: 'public', service });
    },
  }) as unknown as ContactPoint;
}