npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@execonline-inc/resource

v6.21.0

Published

Models JSON api resources

Downloads

15

Readme

resource

The resource package provides types and functions for dealing with HATEOAS REST resources.

Usage

Create a module dedicated to resources and export as necessary.

import {
  Link as LinkR,
  payload as payloadR,
  Resource as ResourceR,
  resource as resourceR,
  ResourceWithErrors as ResourceWithErrorsR,
  ResourceWithMetadata as ResourceWithMetadataR,
} from '@execonline-inc/resource';
import { Result } from 'resulty';

const rels = ['prev', 'next', 'self'] as const;

export type Rel = typeof rels[number];

export const toRel = (value: string): Result<string, Rel> =>
  toResult(
    `Expected to find an HTTP rel string. Instead I found ${value}`,
    find(rel => rel === value, rels)
  );

export type Link = LinkR<Rel>;
export type Resource<T> = ResourceR<T, Rel>;
export type ResourceWithErrors<T> = ResourceWithErrorsR<T, Rel>;
export type ResourceWithMetadata<T, M> = ResourceWithMetadataR<T, M, Rel>;

export const resource: <T>(links: ReadonlyArray<Link>, payload: T) => Resource<T> = resourceR;
export const payload: <T, R extends Resource<T>>(r: R) => T = payloadR;

Types

ResourceCollection

import { Empty, None, Results } from '@execonline-inc/resource';

type ResourceCollection<T, Rel extends string> = None | Empty | Results<T, Rel>;

None

interface None {
  kind: 'none';
}

Empty

interface Empty {
  kind: 'empty';
}

Results

import { Resource } from '@execonline-inc/resource';

interface Results<T, Rel extends string> {
  kind: 'results';
  results: Resource<T[], Rel>;
}

Link

import { Method } from 'ajaxian';

interface Link<Rel extends string> {
  rel: Rel;
  href: string;
  method: Method;
  type: string;
}

ServerError

interface ServerError {
  type: string;
  param: string;
  message: string;
  code: string;
  source: string;
}

Linkable

import { Link } from '@execonline-inc/resource';

interface Linkable<Rel extends string> {
  links: ReadonlyArray<Link<Rel>>;
}

PossiblyLinkable

import { Linkable } from '@execonline-inc/resource';

interface PossiblyLinkable<Rel extends string> {
  whenLinks: Maybe<Linkable<Rel>>;
}

Payloaded

interface Payloaded<T> {
  payload: T;
}

Resource

import { Linkable, Payloaded } from '@execonline-inc/resource';

interface Resource<T, Rel extends string> extends Payloaded<T>, Linkable<Rel> {}

ResourceWithErrors

import { Resource, ServerError } from '@execonline-inc/resource';

interface ResourceWithErrors<T, Rel extends string> extends Resource<T, Rel> {
  errors: ServerError[];
}

IdentifiablePayload

interface IdentifiablePayload {
  id: number;
}

ResourceWithMetadata

import { Resource } from '@execonline-inc/resource';

interface ResourceWithMetadata<T, M, Rel extends string> extends Resource<T, Rel> {
  metadata: M;
}

ValidationError

interface ValidationError {
  kind: 'validation-error';
  on: string;
  param: string;
  error: string;
  detail: string;
}

ValidationErrors

import { ValidationError } from '@execonline-inc/resource';

type ValidationErrors = ValidationError[];

PaginationMetadata

interface PaginationMetadata {
  resultsCount: number;
  pageCount: number;
  perPage: number;
  currentPage: number;
}

Functions

none

Creates a None object.

import { none, ResourceCollection } from '@execonline-inc/resource';

const result: ResourceCollection<unknown, 'self'> = none();

empty

Creates an Empty object.

import { empty, ResourceCollection } from '@execonline-inc/resource';

const result: ResourceCollection<unknown, 'self'> = empty();

results

Creates a Results object.

import { Link, results, Resource, ResourceCollection } from '@execonline-inc/resource';

interface ExamplePayload {
  kind: 'example-payload';
}
const payload = { kind: 'example-payload' };
const links: ReadonlyArray<Link<'self'>> = [
  {
    rel: 'self',
    href: 'https://example.com/',
    method: 'get',
    type: 'application/json',
  },
];
const resource: Resource<ExamplePayload[], 'self'> = { payload: [payload], links };
const result: ResourceCollection<ExamplePayload, 'self'> = results(resource);

resources

This function returns the appropriate ResourceCollection type depending on the presence of the given resource and its payload.

import { Link, resources, Resource, ResourceCollection } from '@execonline-inc/resource';
import { just } from 'maybeasy';

interface ExamplePayload {
  kind: 'example-payload';
}
const payload = { kind: 'example-payload' };
const links: ReadonlyArray<Link<'self'>> = [
  {
    rel: 'self',
    href: 'https://example.com/',
    method: 'get',
    type: 'application/json',
  },
];
const resource: Resource<ExamplePayload[], 'self'> = { payload: [payload], links };
const result: ResourceCollection<ExamplePayload, 'self'> = resources(just(resource));

linksDecoder

This function returns a decoder for decoding HATEOAS links with valid rel values. It takes a function that checks for valid rel values.

import { find } from '@execonline-inc/collections';
import { toResult } from '@execonline-inc/maybe-adapter';
import { Link, linksDecoder } from '@execonline-inc/resource';
import { Result } from 'resulty';

const validRels = ['self'] as const;
type Rel = typeof validRels[number];
const toRel = (v: string): Result<string, Rel> =>
  toResult(
    `Invalid rel found: ${v}`,
    find(rel => rel === v, validRels)
  );
const linksDecoder: Decoder<ReadonlyArray<Link<Rel>>> = linksDecoder<Rel>(toRel);

errorDecoder

This decoder is for a particular error object structure.

import { errorDecoder, ServerError } from '@execonline-inc/resource';

const obj = {
  type: 'some type',
  param: 'some param',
  code: '123',
  source: 'some source',
  message: 'some message',
};
const decoder: Decoder<ServerError> = errorDecoder;
const result: Result<string, ServerError> = decoder.decodeAny(obj);

resourceDecoder

This function with a curried and non-curried form. The returned decoder is for a HATEOAS resource structure.

import { find } from '@execonline-inc/collections';
import { resourceDecoder, Resource } from '@execonline-inc/resource';
import { toResult } from '@execonline-inc/maybe-adapter';
import { Result } from 'resulty';

type Rel = 'self';
interface ExamplePayload {}
const obj = { payload: {}, links: [] };

const toRel = (v: string): Result<string, Rel> =>
  toResult(
    `Invalid rel found: ${v}`,
    find(rel => rel === v, validRels)
  );

const payloadDecoder: Decoder<ExamplePayload> = succeed({});
const decoder: Decoder<Resource<ExamplePayload, Rel>> = resourceDecoder<ExamplePayload, Rel>(toRel)(
  payloadDecoder
);
const result: Result<string, Resource<ExamplePayload, Rel>> = decoder.decodeAny(obj);

resourceWithMetadataDecoder

This curried function returns a decoder for a resource with an additional metadata key.

import { find } from '@execonline-inc/collections';
import { resourceWithMetadataDecoder, ResourceWithMetadata } from '@execonline-inc/resource';
import { toResult } from '@execonline-inc/maybe-adapter';
import Decoder, { succeed } from 'jsonous';
import { Result } from 'resulty';

type Rel = 'self';
interface ExamplePayload {}
interface ExampleMetadata {}

const obj = {
  payload: {},
  links: [],
  metadata: {},
};
const toRel = (v: string): Result<string, Rel> =>
  toResult(
    `Invalid rel found: ${v}`,
    find(rel => rel === v, validRels)
  );

const payloadDecoder: Decoder<ExamplePayload> = succeed({});
const metadataDecoder: Decoder<ExampleMetadata> = succeed({});
const decoder: Decoder<ResourceWithMetadata<
  ExamplePayload,
  ExampleMetadata,
  Rel
>> = resourceWithMetadataDecoder<ExamplePayload, ExampleMetadata, Rel>(toRel)(
  payloadDecoder,
  metadataDecoder
);
const result: Result<
  string,
  ResourceWithMetadata<ExamplePayload, ExampleMetadata, Rel>
> = decoder.decodeAny(obj);

resourceWithErrorsDecoder

This curried function returns a decoder for a resource with an additional errors key.

import { find } from '@execonline-inc/collections';
import { resourceWithErrorsDecoder, ResourceWithErrors } from '@execonline-inc/resource';
import { toResult } from '@execonline-inc/maybe-adapter';
import Decoder, { succeed } from 'jsonous';
import { Result } from 'resulty';

type Rel = 'self';
interface ExamplePayload {}

const obj = {
  payload: {},
  links: [],
  errors: [],
};
const toRel = (v: string): Result<string, Rel> =>
  toResult(
    `Invalid rel found: ${v}`,
    find(rel => rel === v, validRels)
  );

const payloadDecoder: Decoder<ExamplePayload> = succeed({});
const decoder: Decoder<ResourceWithErrors<ExamplePayload, Rel>> = resourceWithErrorsDecoder<
  ExamplePayload,
  Rel
>(toRel)(payloadDecoder, metadataDecoder);
const result: Result<string, ResourceWithErrors<ExamplePayload, Rel>> = decoder.decodeAny(obj);

paginationMetadataDecoder

This decoder decodes a specific pagination object structure.

import { paginationMetadataDecoder, PaginationMetadata } from '@execonline-inc/resource';
import { Result } from 'resulty';

const obj = { results_count: 1, page_count: 1, per_page: 1, current_page: 1 };
const result: Result<string, PaginationMetadata> = paginationMetadataDecoder.decodeAny(obj);

validationErrorDecoder

This decoder decodes a specific validation error object structure.

import { validationErrorDecoder, ValidationError } from '@execonline-inc/resource';
import { Result } from 'resulty';

const obj = {
  kind: 'validation-error',
  on: 'that',
  param: 'that',
  error: 'error',
  detail: 'detail',
};
const result: Result<string, ValidationError> = validationErrorDecoder.decodeAny(obj);

validationErrorsDecoder

This decoder is for decoding an array with validationErrorDecoder.

selfUrl

This function finds the link in a resource with a rel of self.

import { Resource, selfUrl } from '@execonline-inc/resource';
import { Maybe } from 'maybeasy';

type Rel = 'self';
interface ExamplePayload {}

const resource: Resource<ExamplePaylod, Rel> = {
  payload: {},
  links: [
    {
      rel: 'self',
      href: 'https://example.com/',
      method: 'get',
      type: 'application/json',
    },
  ],
};
const result: Maybe<Link<Rel>> = selfUrl<ExamplePayload, Rel>(resource);

iconUrl

This function finds the link in a resource with a rel of icon.

import { iconUrl, Resource } from '@execonline-inc/resource';
import { Maybe } from 'maybeasy';

type Rel = 'icon';
interface ExamplePayload {}

const resource: Resource<ExamplePaylod, Rel> = {
  payload: {},
  links: [
    {
      rel: 'icon',
      href: 'https://example.com/',
      method: 'get',
      type: 'application/json',
    },
  ],
};
const result: Maybe<Link<Rel>> = iconUrl<ExamplePayload, Rel>(resource);

isNotSelf

This curried function determines if the given URL is not the same as the link in the resource with a rel of self.

import { isNotSelf, Resource } from '@execonline-inc/resource';

type Rel = 'self';
interface ExamplePayload {}

const resource: Resource<ExamplePaylod, Rel> = {
  payload: {},
  links: [
    {
      rel: 'self',
      href: 'https://example.com/',
      method: 'get',
      type: 'application/json',
    },
  ],
};
const result: boolean = isNotSelf('https://not.example.com/')(resource);

resource

This function constructs a resource object given separated links and a payload.

import { Link, resource, Resource } from '@execonline-inc/resource';

type Rel = 'self';
interface ExamplePayload {}

const links: ReadonlyArray<Link<Rel>> = [];
const payload: ExamplePayload = {};
const result: Resource<ExamplePayload, Rel> = resource<Rel, ExamplePayload>(links, payload);

payload

This function retrieves the payload from a given resource.

import { payload, Resource } from '@execonline-inc/resource';

type Rel = 'self';
interface ExamplePayload {}

const resource: Resource<ExamplePayload, Rel> = { payload: {}, links: [] };
const result: ExamplePayload = payload(resource);

links

This function retrieves the links from a given resource.

import { links, Link, Resource } from '@execonline-inc/resource';

type Rel = 'self';
interface ExamplePayload {}

const resource: Resource<ExamplePayload, Rel> = { payload: {}, links: [] };
const result: ReadonlyArray<Link<Rel>> = links(resource);