text: const curry = require("just-curry-it");
  
  
  const nil = "";
  
  const compile = (pointer) => {
    if (pointer.length > 0 && pointer[0] !== "/") {
      throw Error("Invalid JSON Pointer");
    }
  
    return pointer.split("/").slice(1).map(unescape);
  };
  
  const get = (pointer, value = undefined) => {
    const ptr = compile(pointer);
  
    const fn = (value) => ptr.reduce(([value, pointer], segment) => {
      return [applySegment(value, segment, pointer), append(segment, pointer)];
    }, [value, ""])[0];
  
    return value === undefined ? fn : fn(value);
  };
  
  const set = (pointer, subject = undefined, value = undefined) => {
    const ptr = compile(pointer);
    const fn = curry((subject, value) => _set(ptr, subject, value, nil));
    return subject === undefined ? fn : fn(subject, value);
  };
  
  const _set = (pointer, subject, value, cursor) => {
    if (pointer.length === 0) {
      return value;
    } else if (pointer.length > 1) {
      const segment = pointer.shift();
      return { ...subject, [segment]: _set(pointer, applySegment(subject, segment, cursor), value, append(segment, cursor)) };
    } else if (Array.isArray(subject)) {
      const clonedSubject = [...subject];
      const segment = computeSegment(subject, pointer[0]);
      clonedSubject[segment] = value;
      return clonedSubject;
    } else if (typeof subject === "object" && subject !== null) {
      return { ...subject, [pointer[0]]: value };
    } else {
      return applySegment(subject, pointer[0], cursor);
    }
  };
  
  const assign = (pointer, subject = undefined, value = undefined) => {
    const ptr = compile(pointer);
    const fn = curry((subject, value) => _assign(ptr, subject, value, nil));
    return subject === undefined ? fn : fn(subject, value);
  };
  
  const _assign = (pointer, subject, value, cursor) => {
    if (pointer.length === 0) {
      return;
    } else if (pointer.length === 1 && !isScalar(subject)) {
      const segment = computeSegment(subject, pointer[0]);
      subject[segment] = value;
    } else {
      const segment = pointer.shift();
      _assign(pointer, applySegment(subject, segment, cursor), value, append(segment, cursor));
    }
  };
  
  const unset = (pointer, subject = undefined) => {
    const ptr = compile(pointer);
    const fn = (subject) => _unset(ptr, subject, nil);
    return subject === undefined ? fn : fn(subject);
  };
  
  const _unset = (pointer, subject, cursor) => {
    if (pointer.length == 0) {
      return undefined;
    } else if (pointer.length > 1) {
      const segment = pointer.shift();
      const value = applySegment(subject, segment, cursor);
      return { ...subject, [segment]: _unset(pointer, value, append(segment, cursor)) };
    } else if (Array.isArray(subject)) {
      return subject.filter((_, ndx) => ndx != pointer[0]);
    } else if (typeof subject === "object" && subject !== null) {
      // eslint-disable-next-line no-unused-vars
      const { [pointer[0]]: _, ...result } = subject;
      return result;
    } else {
      return applySegment(subject, pointer[0], cursor);
    }
  };
  
  const remove = (pointer, subject = undefined) => {
    const ptr = compile(pointer);
    const fn = (subject) => _remove(ptr, subject, nil);
    return subject === undefined ? fn : fn(subject);
  };
  
  const _remove = (pointer, subject, cursor) => {
    if (pointer.length === 0) {
      return;
    } else if (pointer.length > 1) {
      const segment = pointer.shift();
      const value = applySegment(subject, segment, cursor);
      _remove(pointer, value, append(segment, cursor));
    } else if (Array.isArray(subject)) {
      subject.splice(pointer[0], 1);
    } else if (typeof subject === "object" && subject !== null) {
      delete subject[pointer[0]];
    } else {
      applySegment(subject, pointer[0], cursor);
    }
  };
  
  const append = curry((segment, pointer) => pointer + "/" + escape(segment));
  
  const escape = (segment) => segment.toString().replace(/~/g, "~0").replace(/\//g, "~1");
  const unescape = (segment) => segment.toString().replace(/~1/g, "/").replace(/~0/g, "~");
  const computeSegment = (value, segment) => Array.isArray(value) && segment === "-" ? value.length : segment;
  
  const applySegment = (value, segment, cursor = "") => {
    if (value === undefined) {
      throw TypeError(`Value at '${cursor}' is undefined and does not have property '${segment}'`);
    } else if (value === null) {
      throw TypeError(`Value at '${cursor}' is null and does not have property '${segment}'`);
    } else if (isScalar(value)) {
      throw TypeError(`Value at '${cursor}' is a ${typeof value} and does not have property '${segment}'`);
    } else {
      const computedSegment = computeSegment(value, segment);
      return value[computedSegment];
    }
  };
  
  const isScalar = (value) => value === null || typeof value !== "object";
  
  module.exports = { nil, append, get, set, assign, unset, remove };
  