-
Notifications
You must be signed in to change notification settings - Fork 0
Low Level Function Reference
이 페이지는 JSONDocument<T> instance 없이도 사용할 수 있는 function 중심 API를 설명한다.
대부분의 제품 코드는 먼저 JSONDocument<T> command를 사용한다. Low-level function은 adapter, extension package, server-side transform, import pipeline, test, custom editor primitive를 만들 때 사용한다.
function createJSONDocument<S extends z.ZodType>(
schema: S,
initial: z.output<S>,
options: JSONDocumentOptions & { trustedInitial: true },
): JSONDocument<z.output<S>>;
function createJSONDocument<S extends z.ZodType>(
schema: S,
initial: z.input<S>,
options?: JSONDocumentOptions & { trustedInitial?: false | undefined },
): JSONDocument<z.output<S>>;Headless document instance를 만든다.
기본 호출은 initial value를 schema로 parse한다. trustedInitial: true는 호출자가 이미 schema 검증 경계를 소유할 때만 사용한다.
const doc = createJSONDocument(Card, input, {
history: 100,
selection: true,
});function useJSONDocument<S extends z.ZodType>(
schema: S,
initial: z.output<S>,
options: JSONDocumentOptions & { trustedInitial: true },
): JSONDocument<z.output<S>>;
function useJSONDocument<S extends z.ZodType>(
schema: S,
initial: z.input<S>,
options?: JSONDocumentOptions & { trustedInitial?: false | undefined },
): JSONDocument<z.output<S>>;React lifecycle 안에서 같은 JSONDocument<T> surface를 제공한다.
Import 경로는 root가 아니라 React entrypoint다.
import { useJSONDocument } from "@interactive-os/json-document/react";Patch helper는 document instance 없이 schema와 state만으로 patch 결과를 계산한다.
interface ApplyResult<S extends z.ZodTypeAny> {
state: z.output<S>;
result: JSONResult;
applied: ReadonlyArray<JSONPatchOperation>;
}function applyOperation<S extends z.ZodTypeAny>(
schema: S,
state: z.output<S>,
op: JSONPatchOperation,
): ApplyResult<S>;Operation 하나를 적용한다.
검증 순서:
- 기존 state가 JSON-serializable인지 확인
- operation shape 확인
- raw operation 적용
- schema 검증
실패하면 기존 state와 빈 applied를 반환한다.
function applyPatch<S extends z.ZodTypeAny>(
schema: S,
state: z.output<S>,
ops: ReadonlyArray<JSONPatchOperation>,
): ApplyResult<S>;Operation 배열을 하나의 patch로 적용한다.
전체 patch가 성공해야 state가 바뀐다. 중간 operation이 실패하거나 마지막 schema 검증이 실패하면 기존 state를 반환한다.
const result = applyPatch(CardList, state, [
{ op: "replace", path: "/items/0/title", value: "Ready" },
]);
if (result.result.ok) {
result.state;
result.applied;
}function applyPatchToTrustedState<S extends z.ZodTypeAny>(
schema: S,
state: z.output<S>,
ops: ReadonlyArray<JSONPatchOperation>,
): ApplyResult<S>;이미 z.output<S>로 신뢰한 state에 patch를 적용한다.
applyPatch와 마찬가지로 마지막 schema 검증은 수행한다. 차이는 호출자가 state의 JSON serializability와 검증 경계를 더 강하게 소유한다는 의미다.
Pointer는 core의 공통 주소 형식이다. Root pointer는 빈 문자열 ""이다.
function parsePointer(pointer: Pointer): string[];JSON Pointer 문자열을 segment 배열로 변환한다.
잘못된 pointer면 PointerSyntaxError를 throw한다. 사용자 입력처럼 실패가 정상 흐름인 경우 tryParsePointer를 쓴다.
parsePointer("/items/0/title"); // ["items", "0", "title"]function tryParsePointer(pointer: Pointer): string[] | null;Pointer를 parse하고, 실패하면 null을 반환한다.
function buildPointer(
segments: ReadonlyArray<string | number>,
options?: { uriFragment?: boolean },
): Pointer;Segment 배열을 pointer 문자열로 만든다. ~와 / escape를 자동 처리한다.
buildPointer(["items", 0, "a/b"]); // "/items/0/a~1b"
buildPointer([], { uriFragment: true }); // "#"function escapeSegment(segment: string): string;
function unescapeSegment(segment: string): string;Pointer segment 단위 escape/unescape를 수행한다.
직접 pointer 문자열을 이어 붙이기보다 buildPointer나 appendSegment를 우선 사용한다.
function parentPointer(pointer: Pointer): Pointer | null;Parent pointer를 반환한다.
parentPointer("/items/0/title"); // "/items/0"
parentPointer("/items"); // ""
parentPointer(""); // nullfunction lastSegment(pointer: Pointer): string | null;마지막 segment를 unescape된 문자열로 반환한다.
function lastSegmentIndex(pointer: Pointer): number | null;마지막 segment가 array index면 number를 반환하고, record key나 root면 null을 반환한다.
function appendSegment(pointer: Pointer, segment: string | number): Pointer;Pointer 끝에 segment를 추가한다.
appendSegment("/items", 0); // "/items/0"function withLastSegment(pointer: Pointer, segment: string | number): Pointer | null;마지막 segment를 교체한다. Root pointer면 null이다.
withLastSegment("/items/0", 1); // "/items/1"class PointerSyntaxError extends Error {}parsePointer가 잘못된 pointer에서 throw하는 error class다.
function trackPointer(
pointer: Pointer,
applied: ReadonlyArray<JSONPatchOperation>,
): Pointer | null;Applied patch 이후 기존 pointer가 어디를 가리키는지 계산한다.
삭제된 target이면 null을 반환한다.
const next = trackPointer("/items/0", doc.lastPatch);사용 맥락:
- selection tracking
- annotation/comment anchor tracking
- external id mapping
- review marker tracking
주의: applied는 실제 적용된 normalized operation이어야 한다. Hand-built operation에 /-가 남아 있으면 추적 결과가 null이 될 수 있다.
function resolveSiblingRange(
source: Pointer | ReadonlyArray<Pointer>,
options?: ResolveSiblingRangeOptions,
): SiblingRangeResult;같은 parent array 아래의 item pointer들을 parent와 index run으로 정규화한다.
const range = resolveSiblingRange(["/items/2", "/items/0", "/items/1"], {
requireContiguous: true,
});
if (range.ok) {
range.parent; // "/items"
range.locations.map((location) => location.index); // [0, 1, 2]
}Options:
interface ResolveSiblingRangeOptions {
dedupe?: boolean;
pruneDescendants?: boolean;
requireContiguous?: boolean;
}성공 result:
{
ok: true;
parent: Pointer;
locations: ReadonlyArray<SiblingLocation>;
contiguous: boolean;
}실패 code:
empty_selectioninvalid_pointernot_array_itemmixed_parentnon_contiguous
이 function은 document state를 읽지 않는다. Parent가 실제 array인지 확인해야 하는 경우 호출자가 doc.at(parent)나 schema API로 별도 검증한다.
- 제품 UI command는 먼저
JSONDocument<T>method를 사용한다. - Adapter나 extension이 patch 계획만 필요하면
applyPatch계열을 사용한다. - Path 문자열을 직접 다루는 코드는 pointer helper를 사용한다.
- Document 외부의 anchor를 patch 이후 따라가야 하면
trackPointer를 사용한다. - 같은 parent array의 선택 item을 정규화해야 하면
resolveSiblingRange를 사용한다.