Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
34 changes: 8 additions & 26 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ jobs:
- name: Install dependencies
if: steps.scope.outputs.package_any == 'true'
run: npm ci --no-audit --no-fund
- name: Verify package and official extensions
- name: Verify v2 package
if: steps.scope.outputs.package_full == 'true'
run: npm run typecheck && npm test && npm run build && npm run smoke:package && npm run extensions:verify && npm run docs:evaluate
run: npm run archive:check && npm run verify -w @interactive-os/json-document && npm run standard:check && npm run docs:evaluate
- name: Verify package documentation
if: steps.scope.outputs.package_full != 'true' && (steps.scope.outputs.package_docs == 'true' || steps.scope.outputs.package_smoke == 'true')
run: |
Expand All @@ -48,33 +48,18 @@ jobs:
run: npm pack -w @interactive-os/json-document --dry-run

lab-extensions:
name: Lab extensions
name: Archive isolation
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: actions/setup-node@v6
with:
node-version: "22"
- name: Detect changed scope
id: scope
env:
CI_BASE_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || github.event.before }}
CI_HEAD_SHA: ${{ github.sha }}
run: node scripts/ci-scope.mjs
- name: Install dependencies
if: steps.scope.outputs.lab_extensions == 'true'
run: npm ci --no-audit --no-fund
- name: Verify changed lab extension dist imports
if: steps.scope.outputs.lab_extensions == 'true'
env:
LAB_EXTENSIONS_BASE: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || github.event.before }}
LAB_EXTENSIONS_HEAD: ${{ github.sha }}
run: npm run labs:extensions:verify:changed
- name: Verify archived 1.x isolation
run: npm run archive:check

playground-site:
name: Playgrounds and site
name: Site
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
Expand All @@ -90,14 +75,11 @@ jobs:
CI_HEAD_SHA: ${{ github.sha }}
run: node scripts/ci-scope.mjs
- name: Install dependencies
if: steps.scope.outputs.site == 'true' || steps.scope.outputs.playground == 'true' || steps.scope.outputs.browser == 'true'
if: steps.scope.outputs.site == 'true' || steps.scope.outputs.browser == 'true'
run: npm ci --no-audit --no-fund
- name: Verify playgrounds and site
if: steps.scope.outputs.playground == 'true'
run: npm run playground:typecheck && npm run playground:test && npm run playground:build
- name: Verify site
if: steps.scope.outputs.site == 'true'
run: npm run site:verify:pages
run: npm run typecheck -w @interactive-os/json-document-site && npm test -w @interactive-os/json-document-site && npm run site:verify:pages
- name: Verify browser behavior
if: steps.scope.outputs.browser == 'true'
run: npm run browser:test
10 changes: 4 additions & 6 deletions .github/workflows/lab-extensions.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Lab Extension Catalog
name: Archived 1.x Isolation

on:
workflow_dispatch:
Expand All @@ -10,14 +10,12 @@ permissions:

jobs:
lab-extensions:
name: Full lab extension verification
name: Archive isolation
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: "22"
- name: Install dependencies
run: npm ci --no-audit --no-fund
- name: Verify all lab extension dist imports
run: npm run build && npm run labs:extensions:verify
- name: Verify archived code is outside the v2 release graph
run: npm run archive:check
80 changes: 2 additions & 78 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ on:
push:
tags:
- "json-document-v*-rc.*"
- "json-document-extensions-v*-rc.*"

permissions:
contents: read
Expand Down Expand Up @@ -50,84 +49,9 @@ jobs:
throw new Error("publishConfig.tag must remain next for prereleases");
}
'
- name: Verify extension prerelease identity
if: startsWith(github.ref_name, 'json-document-extensions-v')
env:
RELEASE_TAG: ${{ github.ref_name }}
run: |
node --input-type=module --eval '
import { readFileSync } from "node:fs";
const packagePaths = [
"packages/grouping/package.json",
"packages/patch-preview/package.json",
"packages/search-replace/package.json",
];
const expectedNames = [
"@interactive-os/json-document-grouping",
"@interactive-os/json-document-patch-preview",
"@interactive-os/json-document-search-replace",
];
const packages = packagePaths.map((path) =>
JSON.parse(readFileSync(path, "utf8"))
);
const versions = new Set(packages.map((pkg) => pkg.version));
if (versions.size !== 1) {
throw new Error("extension prerelease versions must match");
}
const [version] = versions;
const expectedTag = `json-document-extensions-v${version}`;
if (process.env.RELEASE_TAG !== expectedTag) {
throw new Error(`release tag ${process.env.RELEASE_TAG} does not match ${expectedTag}`);
}
if (!/^\d+\.\d+\.\d+-rc\.\d+$/.test(version)) {
throw new Error(`extension version is not an rc prerelease: ${version}`);
}
for (const [index, pkg] of packages.entries()) {
if (pkg.name !== expectedNames[index]) {
throw new Error(`${packagePaths[index]} has unexpected name ${pkg.name}`);
}
if (pkg.publishConfig?.tag !== "next") {
throw new Error(`${pkg.name} publishConfig.tag must remain next`);
}
if (pkg.publishConfig?.access !== "public" || pkg.publishConfig?.provenance !== true) {
throw new Error(`${pkg.name} must publish publicly with provenance`);
}
if (pkg.peerDependencies?.["@interactive-os/json-document"] !== "^1.0.1 || ^1.1.0-rc.0") {
throw new Error(`${pkg.name} does not accept the core integration RC`);
}
}
'
- name: Install dependencies
run: npm ci --no-audit --no-fund
- name: Build core for extension verification
if: startsWith(github.ref_name, 'json-document-extensions-v')
run: npm run build -w @interactive-os/json-document
- name: Verify extension prereleases
if: startsWith(github.ref_name, 'json-document-extensions-v')
run: |
version=$(node -p 'require("./packages/grouping/package.json").version')
for package in \
@interactive-os/json-document-grouping \
@interactive-os/json-document-patch-preview \
@interactive-os/json-document-search-replace
do
if npm view "$package@$version" version >/dev/null 2>&1; then
echo "::error::$package@$version is already published"
exit 1
fi
npm run verify -w "$package"
npm pack -w "$package" --dry-run --cache "${RUNNER_TEMP}/npm-cache"
done
- name: Verify core prerelease
run: npm run release:check
- name: Publish core prerelease
if: startsWith(github.ref_name, 'json-document-v')
run: npm publish -w @interactive-os/json-document --tag next --provenance
- name: Publish extension prereleases
if: startsWith(github.ref_name, 'json-document-extensions-v')
run: |
for package in \
@interactive-os/json-document-grouping \
@interactive-os/json-document-patch-preview \
@interactive-os/json-document-search-replace
do
npm publish -w "$package" --tag next --provenance
done
45 changes: 12 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ v2 root는 JSON, JSON Pointer, JSONPath, JSON Patch만 전제로 하며 Zod, Rea
selection, clipboard, history를 필수 계약에 넣지 않습니다.

```txt
Pure Protocol
|-> Document Projection -> host adapter
`-> Candidate Editing Session -> React / rich host adapter
Pure Protocol -> Document Projection -> host adapter
```

공식 사이트: https://developer-1px.github.io/json-document/
Expand All @@ -21,42 +19,25 @@ Pure Protocol
| 프로젝트 이해 | [docs/public/overview.md](docs/public/overview.md) |
| 빠른 사용 예제 | [docs/public/quickstart.md](docs/public/quickstart.md) |
| 공개 API | [docs/public/api.md](docs/public/api.md) |
| 공식 extension 사용법 | [docs/public/extensions.md](docs/public/extensions.md) |
| 제품별 feature 지도 | [docs/public/recipes.md](docs/public/recipes.md) |
| 문서 구조 | [docs/README.md](docs/README.md) |
| 변경 기록 | [docs/changelog.md](docs/changelog.md) |
| v2 Projection 표준 | [docs/standard/v2-projection-profile.md](docs/standard/v2-projection-profile.md) |
| v2 공개 표면 manifest | [docs/standard/v2-public-surface.json](docs/standard/v2-public-surface.json) |
| Candidate Session의 1.x 기준선 | [docs/standard/conformance-profile.md](docs/standard/conformance-profile.md) |
| 1.x 기록 | [archive/v1/docs](archive/v1/docs) |

## 코드 지도

| 위치 | 역할 |
| --- | --- |
| [packages/json-document](packages/json-document) | v2 Kernel과 optional Candidate Session |
| [packages/collection](packages/collection) | ordered JSON array item 이동/복제/삭제 |
| [packages/clipboard-web](packages/clipboard-web) | browser clipboard bridge |
| [packages/contenteditable-web](packages/contenteditable-web) | `@interactive-os/json-document-contenteditable-web` DOM contenteditable text-surface adapter |
| [packages/contenteditable-react](packages/contenteditable-react) | `@interactive-os/json-document-contenteditable-react` React wrapper for contenteditable timing |
| [packages/schema-form](packages/schema-form) | schema-backed field descriptor |
| [packages/form-draft](packages/form-draft) | valid JSON commit 전 temporary invalid form input |
| [packages/protected-ranges](packages/protected-ranges) | protected JSON Pointer range edit guard |
| [packages/snippets](packages/snippets) | reusable JSON payload snippet insertion |
| [packages/dirty-state](packages/dirty-state) | clean baseline 대비 dirty state |
| [packages/bulk-edit](packages/bulk-edit) | JSONPath replace-all/delete-all |
| [packages/patch-log](packages/patch-log) | applied patch stream 기록/replay |
| [packages/persist-web](packages/persist-web) | browser storage-like persistence |
| [packages/id-resolver](packages/id-resolver) | stable id를 현재 JSON Pointer로 해석 |
| [packages/patch-preview](packages/patch-preview) | JSON Patch 적용 전 schema-safe dry-run |
| [packages/search-replace](packages/search-replace) | JSON string field 검색/치환 |
| [packages/grouping](packages/grouping) | sibling JSON item structural group/ungroup |
| [packages/proposed-changes](packages/proposed-changes) | JSON Patch 제안 accept/reject review model |
| [packages/comments](packages/comments) | JSON Pointer anchor 기반 review comment |
| [packages/outline](packages/outline) | document outline projection |
| [apps/site](apps/site) | public docs site와 workbench |
| [apps/outliner](apps/outliner) | outliner demo app |
| [apps/mobile-cms](apps/mobile-cms) | mobile CMS demo app |
| [labs/extensions](labs/extensions) | 아직 공식 package가 아닌 extension 실험 |
| [packages/json-document](packages/json-document) | 배포되는 v2 Kernel |
| [apps/site](apps/site) | v2 Core 공개 문서 사이트 |
| [archive/v1](archive/v1) | 배포·workspace·검증에서 분리된 1.x 기록 |

이 저장소가 배포하는 package는 `@interactive-os/json-document` 하나입니다.
Selection, history, clipboard, persistence와 DOM lifecycle은 host adapter가
여섯-member `JSONDocument` 위에서 조합합니다. DOM과 Input Events 정규화가
필요한 제품은 별도 수명 주기를 가진 companion `@interactive-os/editable`을
검토할 수 있으며, companion은 이 저장소의 배포물이나 v2 Core API가 아닙니다.

## 경계

Expand All @@ -68,12 +49,10 @@ v2 Kernel이 제공하는 최소 계약:
- ordered atomic JSON Patch commit
- canonical applied change publication

`@interactive-os/json-document/session`은 Zod 기반 schema 검증, selection,
clipboard, history와 고수준 편집 동사를 제공하는 optional Candidate 표면입니다.

편집 툴이 계속 소유하는 것:

- rendering, DOM focus, keyboard, drag/drop UI
- selection, clipboard, history, framework lifecycle
- grid selection, TSV clipboard, formula engine
- product command 이름, layout, route, remote protocol

Expand Down
9 changes: 4 additions & 5 deletions apps/site/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,11 @@
"typecheck": "tsc -p tsconfig.json --noEmit"
},
"dependencies": {
"@interactive-os/json-document-mobile-cms": "*",
"@interactive-os/json-document-outliner": "*",
"react": "^19.2.5",
"react-dom": "^19.2.5",
"react-markdown": "^10.1.0",
"rehype-slug": "^6.0.0",
"remark-gfm": "^4.0.1",
"zod": "^4.0.0",
"@interactive-os/json-document": "*"
"remark-gfm": "^4.0.1"
},
"devDependencies": {
"@testing-library/react": "^16.3.2",
Expand All @@ -33,5 +29,8 @@
"typescript": "^5.0.0",
"vite": "^6.0.0",
"vitest": "^4.1.7"
},
"optionalDependencies": {
"@esbuild/linux-x64": "0.25.12"
}
}
13 changes: 1 addition & 12 deletions apps/site/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ type SiteRoute = {
label: string;
title: string;
description: string;
group: "Start" | "Demos";
group: "Start";
};
type Route = SiteRoute & { Component: ComponentType };

Expand All @@ -16,21 +16,11 @@ const SITE_URL = (import.meta.env.VITE_SITE_URL ?? "https://developer-1px.github
const Docs = lazy(() => import("./routes/Docs").then((module) => ({ default: module.Docs })));
const DocsTutorial = lazy(() => import("./routes/Docs").then((module) => ({ default: module.DocsTutorial })));
const DocsApiReference = lazy(() => import("./routes/Docs").then((module) => ({ default: module.DocsApiReference })));
const DocsExtensions = lazy(() => import("./routes/Docs").then((module) => ({ default: module.DocsExtensions })));
const DocsRecipes = lazy(() => import("./routes/Docs").then((module) => ({ default: module.DocsRecipes })));
const Playground = lazy(() => import("./routes/Playground").then((module) => ({ default: module.Playground })));
const Outliner = lazy(() => import("@interactive-os/json-document-outliner").then((module) => ({ default: module.Outliner })));
const MobileCms = lazy(() => import("@interactive-os/json-document-mobile-cms").then((module) => ({ default: module.App })));
const routeComponents: Record<string, ComponentType> = {
"/": Home,
"/docs": Docs,
"/docs/tutorial": DocsTutorial,
"/docs/api": DocsApiReference,
"/docs/extensions": DocsExtensions,
"/docs/recipes": DocsRecipes,
"/playground": Playground,
"/playground/outliner": Outliner,
"/playground/mobile-cms": MobileCms,
};
const ROUTES: Route[] = (siteRoutes as SiteRoute[]).map((route) => ({
...route,
Expand Down Expand Up @@ -148,7 +138,6 @@ export function App() {
const Page = route.Component;
const groupedRoutes = {
Start: ROUTES.filter((item) => item.group === "Start"),
Demos: ROUTES.filter((item) => item.group === "Demos"),
};

useEffect(() => {
Expand Down
Loading