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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@interactive-os/json-document

v1.0.1

Published

Headless JSON editing primitives guarded by Zod schemas.

Downloads

314

Readme

json-document

Zod schema로 보호되는 JSON state를 읽고, 바꾸고, 선택하고, 복사하고, 붙여넣고, 되돌리기 위한 headless document layer입니다.

json-document는 UI component library, CRUD framework, app state manager가 아닙니다. Form, CMS block, kanban board, outliner, settings editor처럼 서로 다른 제품이 반복해서 구현하는 document editing core를 제공합니다.

schema -> document -> pointer/query -> can* -> change -> result
  • 공식 사이트: https://developer-1px.github.io/json-document/
  • GitHub Wiki: https://github.com/developer-1px/json-document/wiki

왜 json-document인가

JSON을 단순 data blob으로만 다루면 값 하나를 바꾸는 일은 쉽습니다. 하지만 실제 제품에서는 곧 더 많은 질문이 생깁니다.

  • 이 변경이 schema를 통과하는가?
  • 실행 전에 버튼이나 command palette에서 가능 여부를 알 수 있는가?
  • 실패하면 어떤 code, pointer, violations로 설명할 수 있는가?
  • 선택된 항목을 삭제, 이동, 복제할 때 source는 어떻게 정해지는가?
  • 여러 항목을 copy/cut/paste하면 array insertion target에서 어떻게 들어가는가?
  • undo/redo는 value뿐 아니라 selection도 함께 복원하는가?
  • React 밖에서도 같은 document behavior를 쓸 수 있는가?

json-document는 이 질문들을 UI 밖의 stable core로 옮깁니다. 앱은 rendering, focus, keyboard, drag/drop, network sync를 계속 소유하고, json-document는 schema-safe JSON document semantics를 소유합니다.

설치

npm install @interactive-os/json-document zod

zod는 peer dependency입니다. React 앱에서만 @interactive-os/json-document/react를 import합니다.

60초 시작

import { z } from "zod";
import { createJSONDocument } from "@interactive-os/json-document";

const Card = z.object({
  id: z.string(),
  title: z.string().min(1),
  status: z.enum(["todo", "doing", "done"]),
});

const doc = createJSONDocument(Card, {
  id: "c1",
  title: "Write docs",
  status: "todo",
}, {
  history: 100,
  selection: true,
});

const canChange = doc.canReplace("/status", "doing");

if (canChange.ok) {
  doc.replace("/status", "doing");
}

모든 실행 API는 성공 또는 실패 result를 반환합니다. UI는 같은 can* result를 사용해 button disabled, command availability, validation message를 만들 수 있습니다.

const result = doc.canReplace("/title", "");

if (!result.ok) {
  result.code;
  result.reason;
  result.violations;
}

작업별 진입점

| 하고 싶은 일 | 공개 API | | --- | --- | | headless document 만들기 | createJSONDocument(schema, initial, options?) | | React에서 같은 표면 쓰기 | useJSONDocument(schema, initial, options?) | | 현재 값 읽기 | doc.value, doc.lastPatch | | 한 위치 읽기 | doc.at(pointer) | | 하위 항목 나열 | doc.entries(pointer) | | 여러 위치 찾기 | doc.find(jsonPath), doc.query(jsonPath) | | 값 삽입, 교체, 삭제, 이동 | doc.insert(...), doc.replace(...), doc.delete(...), doc.move(...) | | 실행 전 확인 | doc.canPatch, doc.canFind, doc.canInsert, doc.canReplace, doc.canDelete, doc.canMove, doc.canDuplicate, doc.canCopy, doc.canCut, doc.canPaste, doc.canUndo, doc.canRedo | | sibling 복제 | doc.duplicate(pointer?, options) | | 선택 상태 저장 | doc.selection | | copy, cut, paste | doc.copy(...), doc.cut(...), doc.paste(...) | | 외부 payload 삽입 | doc.insert(target, value, options?) | | undo, redo | doc.undo(), doc.redo(), doc.history | | 위치별 schema 확인 | doc.schema.at, doc.schema.kind, doc.schema.describe, doc.schema.accepts |

1.0 public contract

1.x에서 고정되는 것은 export 이름만이 아닙니다. 사용자 코드가 의존하는 result shape, error code, signature/call shape, selection/history semantics도 public contract입니다.

  • public import는 @interactive-os/json-document@interactive-os/json-document/react입니다.
  • 실패 분기는 reason 문구가 아니라 stable code와 구조로 합니다.
  • 기본값은 strict: false입니다. 실패는 throw가 아니라 result로 돌아옵니다.
  • doc.undo()doc.redo()는 top-level command이며 JSONCapabilityResult를 반환합니다.
  • createJSONDocumentuseJSONDocument는 trusted/untrusted initial overload를 유지합니다.
  • doc.insert(value), doc.insert(target, value, options), doc.move(source, target), doc.move(target), doc.paste(target, options) call shape는 public API입니다.
  • commit(..., { selectionAfter })는 patch와 final selection을 같은 history entry에 기록합니다.

상세 API와 method reference는 GitHub Wiki에 있습니다.

  • Core API Reference: https://github.com/developer-1px/json-document/wiki/Core-API-Reference
  • JSONDocument Method Reference: https://github.com/developer-1px/json-document/wiki/JSONDocument-Method-Reference
  • 1.0 Semantic Contract: https://github.com/developer-1px/json-document/wiki/1.0-Semantic-Contract

핵심 규칙

  • Patch path와 source는 JSON Pointer입니다.
  • JSONPath는 값을 찾는 언어이며 직접 변경하지 않습니다.
  • doc.at(pointer)는 raw value가 아니라 ReadResult를 반환합니다.
  • can*는 boolean이 아니라 이유 있는 capability result입니다.
  • doc.duplicate, doc.cut, doc.paste는 성공하면 즉시 적용됩니다.
  • 성공 결과의 applied patch는 이미 적용된 record이므로 다시 commit하지 않습니다.
  • Pointer 배열을 copy/cut하면 clipboard payload도 배열입니다.
  • Multi-source clipboard payload를 array insertion target에 paste하면 기본적으로 item별 sibling insert가 됩니다.
  • Tree semantics는 app-owned입니다. json-document는 JSON을 검증하고 mutate합니다.

Selection, clipboard, history

Selection은 DOM focus가 아니라 JSON-safe document state입니다.

doc.selection?.selectRanges([
  "/lists/0/cards/0",
  "/lists/0/cards/1",
]);

doc.copy(doc.selection?.selectedPointers ?? []);
doc.paste("/lists/1/cards/-");

Clipboard는 browser clipboard가 아니라 document instance 안의 headless JSON payload buffer입니다. Browser system clipboard는 @interactive-os/json-document-clipboard-web extension에서 조립합니다.

History는 사용자 의도 단위로 묶을 수 있습니다.

doc.history.transaction({ label: "bulk status change" }, () => {
  doc.replace("/status", "doing");
  doc.replace("/title", "In progress");
});

doc.undo();
doc.redo();

구조 편집 후 다음 selection을 command가 정확히 알고 있으면 selectionAfter를 명시합니다.

doc.commit([
  { op: "replace", path: "/status", value: "done" },
], {
  label: "complete card",
  selectionAfter: "/status",
});

Placement targets

삽입/이동 위치를 이미 알고 있으면 /items/-/lists/1/cards/- 같은 exact Pointer를 그대로 넘깁니다.

doc.insert("/items/-", item);
doc.move("/items/0", "/items/2");

Array container 안에 append할 때는 { into }, array item 기준으로 배치할 때는 { before }, { after }를 사용합니다. 기존 값을 바꾸는 작업은 replace이고, document clipboard paste만 { replace: pointer }를 추가로 지원합니다.

doc.insert({ into: "/items" }, item);
doc.move("/items/0", { after: "/items/2" });
doc.paste({ replace: "/items/0" });

삽입 위치를 직접 지정하려면 placement object를 만들지 말고 pointer 문자열을 그대로 넘깁니다.

React — useJSONDocument

import { z } from "zod";
import { useJSONDocument } from "@interactive-os/json-document/react";

const Schema = z.object({
  title: z.string(),
  tasks: z.array(z.object({ id: z.string(), done: z.boolean() })),
});

export function App() {
  const doc = useJSONDocument(Schema, { title: "", tasks: [] }, { history: 50 });

  return (
    <>
      <input
        value={doc.value.title}
        onChange={(event) =>
          doc.patch({ op: "replace", path: "/title", value: event.target.value })
        }
      />
      <button
        onClick={() =>
          doc.insert("/tasks/-", { id: "task-1", done: false })
        }
      >
        insert task
      </button>
      <button onClick={() => doc.undo()} disabled={!doc.canUndo().ok}>
        undo
      </button>
    </>
  );
}

Root package는 React-free입니다. React hook은 별도 entrypoint에만 있습니다.

Official extensions

공식 extension은 core에 plugin 등록하지 않고 public JSONDocument surface를 함수로 조립합니다.

import { createCollection } from "@interactive-os/json-document-collection";
import { createOutline } from "@interactive-os/json-document-outline";
import { createSchemaForm } from "@interactive-os/json-document-schema-form";
import { createFormDraft } from "@interactive-os/json-document-form-draft";
import { createProtectedRanges } from "@interactive-os/json-document-protected-ranges";
import { createSnippets } from "@interactive-os/json-document-snippets";
import { createDirtyState } from "@interactive-os/json-document-dirty-state";
import { createBulkEdit } from "@interactive-os/json-document-bulk-edit";
import { createPatchLog } from "@interactive-os/json-document-patch-log";
import { createDocumentPersistence } from "@interactive-os/json-document-persist-web";
import { createIdResolver } from "@interactive-os/json-document-id-resolver";
import { createPatchPreview } from "@interactive-os/json-document-patch-preview";
import { createSearchReplace } from "@interactive-os/json-document-search-replace";
import { createGrouping } from "@interactive-os/json-document-grouping";
import { createProposedChanges } from "@interactive-os/json-document-proposed-changes";
import { createComments } from "@interactive-os/json-document-comments";
import { createWebClipboard } from "@interactive-os/json-document-clipboard-web";

공식 package는 현재 packages/*에 있는 extension만 뜻합니다. labs/extensions/*는 후보이며 public API로 약속하지 않습니다.

Extension guide: https://github.com/developer-1px/json-document/wiki/Labs-and-Extensions

순수 core

Root helper는 React-free이며 외부 JSON 경계에서 유용합니다.

import * as z from "zod";
import { applyPatch } from "@interactive-os/json-document";

const Schema = z.object({ title: z.string(), tags: z.array(z.string()) });
const initial = { title: "draft", tags: [] };

const r = applyPatch(Schema, initial, [
  { op: "add", path: "/tags/-", value: "docs" },
  { op: "replace", path: "/title", value: "final" },
]);

직렬화

State, operation, selection snapshot, patch record는 JSON입니다.

import * as z from "zod";

const Schema = z.object({ title: z.string() });
const state = { title: "draft" };

const json = JSON.stringify(state);
const restored = Schema.parse(JSON.parse(json));
const safe = Schema.safeParse(JSON.parse(json));

Operation은 application/json-patch+json으로 보낼 수 있습니다.

const operations = [{ op: "replace", path: "/title", value: "final" }];
const body = JSON.stringify(operations);

body satisfies string;

문서

  • Overview: https://developer-1px.github.io/json-document/docs
  • Tutorial: https://developer-1px.github.io/json-document/docs/tutorial
  • API reference: https://developer-1px.github.io/json-document/docs/api
  • Recipes: https://developer-1px.github.io/json-document/docs/recipes
  • Wiki: https://github.com/developer-1px/json-document/wiki