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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 39 additions & 10 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
name: Publish json-document prerelease
name: Publish v2 prerelease package

on:
push:
tags:
- "json-document-v*-rc.*"
- "json-document-collaboration-v*-rc.*"
- "json-document-contenteditable-collaboration-v*-rc.*"

permissions:
contents: read
id-token: write

concurrency:
group: npm-json-document-${{ github.ref }}
group: npm-v2-package-${{ github.ref }}
cancel-in-progress: false

jobs:
Expand All @@ -30,15 +32,39 @@ jobs:
node-version: "24"
registry-url: "https://registry.npmjs.org"
package-manager-cache: false
- name: Verify core prerelease identity
if: startsWith(github.ref_name, 'json-document-v')
- name: Resolve prerelease package
id: release
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}`;
import { appendFileSync, readFileSync } from "node:fs";

const releases = [
{
prefix: "json-document-contenteditable-collaboration-v",
packageFile: "packages/contenteditable-collaboration/package.json",
workspace: "@interactive-os/json-document-contenteditable-collaboration",
},
{
prefix: "json-document-collaboration-v",
packageFile: "packages/json-document-collaboration/package.json",
workspace: "@interactive-os/json-document-collaboration",
},
{
prefix: "json-document-v",
packageFile: "packages/json-document/package.json",
workspace: "@interactive-os/json-document",
},
];

const release = releases.find(({ prefix }) => process.env.RELEASE_TAG.startsWith(prefix));
if (release === undefined) {
throw new Error(`unsupported release tag ${process.env.RELEASE_TAG}`);
}

const pkg = JSON.parse(readFileSync(release.packageFile, "utf8"));
const expectedTag = `${release.prefix}${pkg.version}`;
if (process.env.RELEASE_TAG !== expectedTag) {
throw new Error(`release tag ${process.env.RELEASE_TAG} does not match ${expectedTag}`);
}
Expand All @@ -48,10 +74,13 @@ jobs:
if (pkg.publishConfig?.tag !== "next") {
throw new Error("publishConfig.tag must remain next for prereleases");
}
appendFileSync(process.env.GITHUB_OUTPUT, `workspace=${release.workspace}\n`);
'
- name: Install dependencies
run: npm ci --no-audit --no-fund
- name: Verify core prerelease
- name: Verify v2 prerelease graph
run: npm run release:check
- name: Publish core prerelease
run: npm publish -w @interactive-os/json-document --tag next --provenance
- name: Publish prerelease package
env:
RELEASE_WORKSPACE: ${{ steps.release.outputs.workspace }}
run: npm publish -w "$RELEASE_WORKSPACE" --tag next --access public --provenance
6 changes: 6 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ All notable changes to the active v2 packages are documented here. The complete

## Unreleased

- Added independent prerelease tag publishing for the Core and both optional
collaboration packages, and kept the live-site archive gate aware of the two
supported v2 companions.
- Enabled the two root-value cases that the active RFC 6902 implementation
already conforms to and documented the two vendored duplicate-member cases
that JSON module parsing cannot represent.
- Added the independently versioned, transport-free
`@interactive-os/json-document-collaboration` provider. Local and
collaborative runtimes expose the same six-member `JSONDocument` Projection;
Expand Down
4 changes: 2 additions & 2 deletions docs/generated/repo-catalog.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
"status": "companion",
"private": false,
"publishable": true,
"version": "0.1.0",
"version": "0.1.0-rc.0",
"description": "Transport-free causal collaboration provider for @interactive-os/json-document.",
"license": "MIT",
"summary": "Transport-free causal collaboration provider for the six-member\n`@interactive-os/json-document` Projection contract.",
Expand Down Expand Up @@ -119,7 +119,7 @@
"status": "companion",
"private": false,
"publishable": true,
"version": "0.1.0",
"version": "0.1.0-rc.0",
"description": "IME-safe contenteditable publication lease for json-document collaboration text.",
"license": "MIT",
"summary": "IME-safe DOM publication lease for\n`@interactive-os/json-document-collaboration/text`.",
Expand Down
5 changes: 4 additions & 1 deletion docs/standard/v2-projection-profile.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,10 @@ conformance corpus의 public-root binding을 서로 분리한다.
| `packages/json-document/tests/public/v2-jsonpath-standard-conformance.test.ts` | public root Projection의 전체 RFC 9535 CTS binding |

suite가 export하는 structural type은 test harness 내부 계약이며 package public
export가 아니다.
export가 아니다. Vendored RFC 6902 corpus 112건 중 110건을 실행한다. 남은 2건은
원본 JSON에 중복 `op` member가 있지만 JSON module parsing 단계에서 마지막
member만 남아 public operation object로는 그 입력을 표현할 수 없어, 각 fixture에
명시적인 `disabledReason`을 기록한다.

21개 요구사항의 현재 증거 상태는 runtime 13개, static 6개, deferred 2개다.
schema-free `applyPatch`는 구현됐다. transform identity의 two-provider 검증,
Expand Down
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions packages/contenteditable-collaboration/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@interactive-os/json-document-contenteditable-collaboration",
"version": "0.1.0",
"version": "0.1.0-rc.0",
"description": "IME-safe contenteditable publication lease for json-document collaboration text.",
"type": "module",
"license": "MIT",
Expand Down Expand Up @@ -47,7 +47,7 @@
},
"peerDependencies": {
"@interactive-os/json-document": "^2.0.0-rc.0",
"@interactive-os/json-document-collaboration": "^0.1.0"
"@interactive-os/json-document-collaboration": "^0.1.0-rc.0"
},
"devDependencies": {
"@interactive-os/json-document": "*",
Expand Down
2 changes: 1 addition & 1 deletion packages/json-document-collaboration/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@interactive-os/json-document-collaboration",
"version": "0.1.0",
"version": "0.1.0-rc.0",
"description": "Transport-free causal collaboration provider for @interactive-os/json-document.",
"type": "module",
"license": "MIT",
Expand Down
12 changes: 9 additions & 3 deletions packages/json-document/tests/conformance/v2/rfc6902-suite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ interface RFC6902Case {
readonly expected?: unknown;
readonly error?: string;
readonly disabled?: boolean;
readonly disabledReason?: string;
}

export interface RFC6902Harness {
Expand All @@ -35,7 +36,7 @@ export function runRFC6902Conformance(harness: RFC6902Harness): void {
for (const [index, testCase] of allCases.entries()) {
const label = `[${index}] ${testCase.comment ?? "(no comment)"}`;
if (testCase.disabled) {
test.skip(label, () => undefined);
test.skip(`${label} — ${testCase.disabledReason ?? "no reason recorded"}`, () => undefined);
continue;
}

Expand All @@ -58,8 +59,13 @@ export function runRFC6902Conformance(harness: RFC6902Harness): void {

test("vendored suite size is intentional", () => {
expect(allCases).toHaveLength(112);
expect(allCases.length - disabled).toBe(108);
expect(disabled).toBe(4);
expect(allCases.length - disabled).toBe(110);
expect(disabled).toBe(2);
expect(
allCases
.filter((testCase) => testCase.disabled)
.every((testCase) => testCase.disabledReason !== undefined),
).toBe(true);
});
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,8 @@
{ "op": "add", "path": "/baz", "value": "qux", "op": "remove" }
],
"error": "operation has two 'op' members",
"disabled": true
"disabled": true,
"disabledReason": "JSON parsing collapses the duplicate op member before the operation reaches the public API"
},

{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,7 @@
{ "comment": "Toplevel scalar values OK?",
"doc": "foo",
"patch": [{"op": "replace", "path": "", "value": "bar"}],
"expected": "bar",
"disabled": true },
"expected": "bar" },

{ "comment": "replace object document with array document?",
"doc": {},
Expand Down Expand Up @@ -264,7 +263,7 @@
{ "comment": "Whole document",
"doc": { "foo": 1 },
"patch": [{"op": "test", "path": "", "value": {"foo": 1}}],
"disabled": true },
"expected": { "foo": 1 } },

{ "comment": "Empty-string element",
"doc": { "": 1 },
Expand Down Expand Up @@ -443,7 +442,8 @@
"patch": [ { "op": "add", "path": "/baz", "value": "qux",
"op": "move", "from":"/foo" } ],
"error": "patch has two 'op' members",
"disabled": true },
"disabled": true,
"disabledReason": "JSON parsing collapses the duplicate op member before the operation reaches the public API" },

{ "comment": "unrecognized op should fail",
"doc": {"foo": 1},
Expand Down
2 changes: 1 addition & 1 deletion scripts/evaluate-archive-isolation.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ if (
] !== "^2.0.0-rc.0"
|| contenteditableCollaborationPackage.peerDependencies?.[
"@interactive-os/json-document-collaboration"
] !== "^0.1.0"
] !== "^0.1.0-rc.0"
) {
failures.push("the contenteditable collaboration companion surface is invalid");
}
Expand Down
7 changes: 6 additions & 1 deletion scripts/evaluate-live-site.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ const siteUrl = (process.env.SITE_URL ?? "https://developer-1px.github.io/json-d
const attempts = Number(process.env.SITE_LIVE_ATTEMPTS ?? "18");
const delayMs = Number(process.env.SITE_LIVE_DELAY_MS ?? "10000");
const routes = JSON.parse(readFileSync(new URL("../apps/site/src/site-routes.json", import.meta.url), "utf8"));
const v2CompanionPackages = new Set([
"@interactive-os/json-document-collaboration",
"@interactive-os/json-document-contenteditable-collaboration",
]);
validateSiteRoutes(routes, fail);

function fail(message) {
Expand Down Expand Up @@ -74,8 +78,9 @@ async function checkOnce() {
) {
fail("live llms.txt is missing the v2 Core contract.");
}
const packageReferences = llms.match(/@interactive-os\/json-document-[a-z0-9-]+/g) ?? [];
if (
/@interactive-os\/json-document-(?:[a-z0-9-]+)/.test(llms)
packageReferences.some((packageName) => !v2CompanionPackages.has(packageName))
|| /@interactive-os\/json-document\/(?:session|react)\b/.test(llms)
|| /\blabs\/extensions\b/.test(llms)
) {
Expand Down