diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 00000000..d5d82b12 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,54 @@ +name: Publish json-document prerelease + +on: + push: + tags: + - "json-document-v*-rc.*" + +permissions: + contents: read + id-token: write + +concurrency: + group: npm-json-document-${{ github.ref }} + cancel-in-progress: false + +jobs: + publish: + environment: npm-publish + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + with: + fetch-depth: 0 + - name: Require a tagged main commit + run: | + git fetch origin main + git merge-base --is-ancestor "${GITHUB_SHA}^{commit}" origin/main + - uses: actions/setup-node@v6 + with: + node-version: "24" + registry-url: "https://registry.npmjs.org" + package-manager-cache: false + - name: Verify prerelease identity + env: + RELEASE_TAG: ${{ github.ref_name }} + run: | + node --input-type=module --eval ' + import { readFileSync } from "node:fs"; + const pkg = JSON.parse(readFileSync("packages/json-document/package.json", "utf8")); + const expectedTag = `json-document-v${pkg.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(pkg.version)) { + throw new Error(`package version is not an rc prerelease: ${pkg.version}`); + } + if (pkg.publishConfig?.tag !== "next") { + throw new Error("publishConfig.tag must remain next for prereleases"); + } + ' + - name: Install dependencies + run: npm ci --no-audit --no-fund + - name: Publish prerelease + run: npm publish -w @interactive-os/json-document --tag next --provenance diff --git a/apps/site/src/generated/repo-catalog.ts b/apps/site/src/generated/repo-catalog.ts index f5c7e6ae..32b5f26a 100644 --- a/apps/site/src/generated/repo-catalog.ts +++ b/apps/site/src/generated/repo-catalog.ts @@ -385,7 +385,7 @@ export const repoCatalog = { "status": "core", "private": false, "publishable": true, - "version": "1.1.0", + "version": "1.1.0-rc.0", "description": "Headless JSON editing primitives guarded by Zod schemas.", "license": "MIT", "summary": "Zod schema로 보호되는 JSON state를 읽고, 바꾸고, 선택하고, 복사하고,\n붙여넣고, 되돌리기 위한 headless document layer입니다.", diff --git a/docs/changelog.md b/docs/changelog.md index 8bed59f4..b5162498 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -6,10 +6,16 @@ All notable changes to this project are documented here. No unreleased changes. -## 1.1.0 - 2026-07-05 +## 1.1.0-rc.0 - 2026-07-11 -This release adds the next additive public API surface for the core -`@interactive-os/json-document` document facade while keeping 1.x compatibility. +This prerelease lets sibling editors such as `editable` and `canvas` exercise +the next `@interactive-os/json-document` document facade through an explicit +npm opt-in. It is published under the `next` dist-tag while `latest` remains on +`1.0.1`. + +The unified mutation success result described below changes the runtime shape +published in `1.0.1`. Treat it as an integration experiment, not a stable 1.x +compatibility promise. Its final shape must be decided before a stable release. ### Added @@ -49,6 +55,20 @@ This release adds the next additive public API surface for the core - Fixed schema introspection traversal through wrapper schemas. - Fixed `readAt` so non-canonical array indices are rejected. - Fixed paste-into capability errors for absent targets. +- Replanned prepared insert, paste, cut, and duplicate changes when schema + validation or a custom rekey callback changed the document revision before + publication. +- Preserved atomic guard-test and mutation semantics for overlapping replace + batches and guarded proposed-change acceptance. + +### Performance + +- Added a copy-on-write sequential replace path for eligible overlapping + parent/child replacements on plain structural schemas. +- Reused that path for history inverse capture and eligible guarded replace + batches, avoiding repeated whole-document cloning while preserving operation + order. Unsupported operations and schemas keep the canonical full-validation + path. ## 1.0.1 - 2026-06-19 diff --git a/docs/generated/repo-catalog.json b/docs/generated/repo-catalog.json index 95bd2449..110c01fb 100644 --- a/docs/generated/repo-catalog.json +++ b/docs/generated/repo-catalog.json @@ -384,7 +384,7 @@ "status": "core", "private": false, "publishable": true, - "version": "1.1.0", + "version": "1.1.0-rc.0", "description": "Headless JSON editing primitives guarded by Zod schemas.", "license": "MIT", "summary": "Zod schema로 보호되는 JSON state를 읽고, 바꾸고, 선택하고, 복사하고,\n붙여넣고, 되돌리기 위한 headless document layer입니다.", diff --git a/docs/public/api.md b/docs/public/api.md index dace159e..d7adc482 100644 --- a/docs/public/api.md +++ b/docs/public/api.md @@ -269,15 +269,19 @@ doc.paste({ after: "/lists/0/cards/0" }); Pointer 배열을 copy/cut하면 clipboard payload도 배열입니다. 여러 source를 담은 clipboard buffer는 array 삽입 target에 기본으로 펼쳐집니다. -성공 result shape는 method family별로 다릅니다. +성공 result shape는 method family별로 다릅니다. 아래 `EditOk` shape는 +`1.1.0-rc.0` 통합 후보이며 npm `latest`의 `1.0.1` shape와 다릅니다. RC +소비자는 exact version으로 opt in하고 stable 승격 전에 최종 계약을 다시 +확인해야 합니다. | Method | 성공 result | | --- | --- | -| `insert`, `replace`, `delete`, `move`, `patch`, `commit` | `{ ok: true }`; 실제 patch는 `doc.lastPatch`, subscriber, history에 기록 | +| `insert`, `replace`, `delete`, `move` | `{ ok: true, value, applied, target }` (`EditOk`) | +| `patch`, `commit` | `{ ok: true }`; 실제 patch는 `doc.lastPatch`, subscriber, history에 기록 | | `copy` | `{ ok: true, payload, source, sources }` | -| `cut` | `{ ok: true, value, applied, payload, source, sources }` | -| `paste` | `{ ok: true, value, applied }` | -| `duplicate` | `{ ok: true, value, applied, duplicatedTo }` | +| `cut` | `EditOk` + `{ payload, source, sources }` (`target: null`) | +| `paste` | `EditOk` | +| `duplicate` | `EditOk` + `{ duplicatedTo }` (`target`과 같은 값) | `applied`는 이미 commit된 JSON Patch입니다. 다시 `commit`하지 않습니다. 실패 result는 `reason` 문구가 아니라 stable `code`로 분기합니다. diff --git a/docs/standard/result-contract.md b/docs/standard/result-contract.md index 3035bb87..e532297d 100644 --- a/docs/standard/result-contract.md +++ b/docs/standard/result-contract.md @@ -1,6 +1,6 @@ # Result and Error Code Contract -상태: 1.0 semantic freeze 기준. +상태: 1.0 stable baseline과 1.1.0-rc.0 통합 후보를 함께 기록. 이 문서는 public API가 반환하는 성공, 실패, error code, diagnostic shape를 고정한다. 문서의 목적은 앱이 실패를 문자열로 추측하지 않고, 안정적인 코드와 @@ -14,6 +14,10 @@ 동일하게 유지해야 한다. 새 error code 추가는 minor일 수 있지만, 기존 code 제거, 의미 변경, 성공/실패 discriminant 변경은 breaking change다. +`1.1.0-rc.0` 후보라고 표시한 shape는 sibling editor의 통합 압력을 받기 위한 +opt-in prerelease다. npm `latest`의 1.0 stable 계약을 대체하지 않으며, stable +승격 전에 호환성 또는 다음 major 계약으로 다시 판정해야 한다. + ## 공통 Result Shape 공개 Result family는 `ok` discriminant를 가져야 한다. @@ -143,6 +147,10 @@ Clipboard와 structural command result는 `ok` discriminant를 공유하지만 A Mutation verb의 성공은 통일 `EditOk` shape를 따른다. "duplicate는 새 객체(위치)를 반환한다"는 정본을 전 verb로 일반화한 것이다. +> `1.1.0-rc.0` 후보: 이 shape는 `1.0.1`의 성공 result에 필수 field를 +> 추가한다. prerelease 소비자는 exact version으로 opt in해야 하며 stable +> 1.x 호환 shape로 간주하면 안 된다. + ```ts type JSONDocumentEditOk = { ok: true; @@ -224,6 +232,7 @@ throw 여부가 아니라 result contract의 `code`로 실패를 분류해야 - `violations[].path`를 JSON Pointer가 아닌 형식으로 변경. - `schema-slot`과 `document-result` violation path 기준 변경. - 성공 mutation result에서 `applied` 의미를 실제 commit patch가 아닌 것으로 변경. +- 기존 성공 result의 필수 field 또는 exact key shape 변경. 다음 변경은 호환 가능한 확장일 수 있다. diff --git a/package-lock.json b/package-lock.json index a82f32d7..a0af3e31 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6557,7 +6557,7 @@ }, "packages/json-document": { "name": "@interactive-os/json-document", - "version": "1.1.0", + "version": "1.1.0-rc.0", "license": "MIT", "devDependencies": { "@types/node": "^25.9.0", diff --git a/package.json b/package.json index 7aae3f1a..2af8d6e3 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "standard:check": "node scripts/evaluate-standardization.mjs && npm test -w @interactive-os/json-document -- standard-conformance", "verify": "npm run typecheck && npm test && npm run build && npm run smoke:package && npm run build && npm run extensions:verify && npm run labs:extensions:verify && npm run docs:evaluate && npm run playground:typecheck && npm run playground:test && npm run playground:build && npm run site:evaluate && npm run site:verify:pages && npm run browser:test", "pack:library": "npm pack -w @interactive-os/json-document --cache ./.npm-cache", - "publish": "npm publish -w @interactive-os/json-document", + "publish": "npm publish -w @interactive-os/json-document --tag next", "playground:dev": "npm run dev -w @interactive-os/json-document-site", "playground:build": "node scripts/run-workspace-scripts.mjs apps build --if-present", "playground:typecheck": "node scripts/run-workspace-scripts.mjs apps typecheck", diff --git a/packages/json-document/README.md b/packages/json-document/README.md index 73f1a325..eda84ae5 100644 --- a/packages/json-document/README.md +++ b/packages/json-document/README.md @@ -14,6 +14,16 @@ schema -> document -> pointer/query -> can* -> change -> result - 공식 사이트: https://developer-1px.github.io/json-document/ - GitHub Wiki: https://github.com/developer-1px/json-document/wiki +> `1.1.0-rc.0`은 `editable`, `canvas` 같은 sibling editor에서 통합 압력을 +> 확인하기 위한 prerelease입니다. npm `latest`는 계속 `1.0.1`을 가리킵니다. +> 이 RC는 document mutation 성공을 `{ ok, value, applied, target }` 후보로 +> 통일하지만 low-level `patch`/`commit`은 `{ ok: true }`를 유지합니다. 이 shape는 +> stable 승격 전에 바뀔 수 있으므로 아래처럼 exact version으로 opt in합니다. +> +> ```sh +> npm install @interactive-os/json-document@1.1.0-rc.0 zod +> ``` + ## 왜 json-document인가 JSON을 단순 data blob으로만 다루면 값 하나를 바꾸는 일은 쉽습니다. 하지만 실제 diff --git a/packages/json-document/package.json b/packages/json-document/package.json index c7596798..71de1e4f 100644 --- a/packages/json-document/package.json +++ b/packages/json-document/package.json @@ -1,6 +1,6 @@ { "name": "@interactive-os/json-document", - "version": "1.1.0", + "version": "1.1.0-rc.0", "description": "Headless JSON editing primitives guarded by Zod schemas.", "type": "module", "license": "MIT", @@ -18,7 +18,8 @@ }, "publishConfig": { "access": "public", - "provenance": true + "provenance": true, + "tag": "next" }, "keywords": [ "zod",