diff --git a/apps/website/content/docs/chat/api/parse-tree-store.mdx b/apps/website/content/docs/chat/api/parse-tree-store.mdx index 419c282a1..a631724f3 100644 --- a/apps/website/content/docs/chat/api/parse-tree-store.mdx +++ b/apps/website/content/docs/chat/api/parse-tree-store.mdx @@ -1,6 +1,6 @@ # createParseTreeStore() -Factory function that creates a `ParseTreeStore` — a bridge between the `@ngaf/partial-json` parser and Angular's signal-based `Spec` rendering. It materializes the parse tree into a `Spec` signal with structural sharing on each push. +Factory function that creates a `ParseTreeStore` — a bridge between the `@cacheplane/partial-json` parser and Angular's signal-based `Spec` rendering. It materializes the parse tree into a `Spec` signal with structural sharing on each push. **Import:** @@ -17,7 +17,7 @@ function createParseTreeStore(parser: PartialJsonParser): ParseTreeStore | Parameter | Type | Description | |-----------|------|-------------| -| `parser` | `PartialJsonParser` | A parser instance from `createPartialJsonParser()` in `@ngaf/partial-json` | +| `parser` | `PartialJsonParser` | A parser instance from `createPartialJsonParser()` in `@cacheplane/partial-json` | **Returns:** `ParseTreeStore` — must be called within an Angular injection context. @@ -50,7 +50,7 @@ interface ElementAccumulationState { ## Usage ```typescript -import { createPartialJsonParser } from '@ngaf/partial-json'; +import { createPartialJsonParser } from '@cacheplane/partial-json'; import { createParseTreeStore } from '@ngaf/chat'; const parser = createPartialJsonParser(); diff --git a/apps/website/content/docs/chat/getting-started/installation.mdx b/apps/website/content/docs/chat/getting-started/installation.mdx index 6221c3839..5aece0734 100644 --- a/apps/website/content/docs/chat/getting-started/installation.mdx +++ b/apps/website/content/docs/chat/getting-started/installation.mdx @@ -32,11 +32,12 @@ npm install @ngaf/chat marked | `@angular/forms` | `^20.0.0 \|\| ^21.0.0` | Yes | | `@ngaf/render` | `^0.0.1` | Yes | | `@ngaf/a2ui` | `^0.0.1` | Yes | -| `@ngaf/partial-json` | `^0.0.1` | Yes | | `@json-render/core` | `^0.16.0` | Yes | | `@langchain/core` | `^1.1.33` | Yes | | `marked` | `^15.0.0 \|\| ^16.0.0` | Yes | +`@cacheplane/partial-json` is installed by `@ngaf/chat` for streaming JSON parsing. + `marked` parses AI message content into HTML (headings, code blocks, tables, lists). It is a required peer; the library ships a defensive plain-text fallback for resilience, but the rendered output is unusable without `marked` installed. diff --git a/apps/website/content/docs/chat/guides/generative-ui.mdx b/apps/website/content/docs/chat/guides/generative-ui.mdx index 3fe8936de..f1443e010 100644 --- a/apps/website/content/docs/chat/guides/generative-ui.mdx +++ b/apps/website/content/docs/chat/guides/generative-ui.mdx @@ -16,7 +16,7 @@ AI message content (token by token) → JSON specs via RenderSpecComponent + your view registry ``` -The JSON path uses `@ngaf/partial-json` to parse incomplete JSON character-by-character, producing a live `Spec` signal with structural sharing — unchanged elements keep the same object reference so Angular skips re-rendering them. +The JSON path uses `@cacheplane/partial-json` to parse incomplete JSON character-by-character, producing a live `Spec` signal with structural sharing — unchanged elements keep the same object reference so Angular skips re-rendering them. ## Setup diff --git a/apps/website/content/docs/chat/guides/streaming.mdx b/apps/website/content/docs/chat/guides/streaming.mdx index c0ee2336d..4693b10c8 100644 --- a/apps/website/content/docs/chat/guides/streaming.mdx +++ b/apps/website/content/docs/chat/guides/streaming.mdx @@ -8,7 +8,7 @@ Each AI message is processed by a `ContentClassifier` that examines the content | Trigger | Content Type | What Happens | |---------|-------------|--------------| -| First non-whitespace is `{` | `json-render` | Parsed as a JSON spec via `@ngaf/partial-json` | +| First non-whitespace is `{` | `json-render` | Parsed as a JSON spec via `@cacheplane/partial-json` | | Any other text | `markdown` | Rendered as markdown prose | @@ -74,7 +74,7 @@ type ContentType = 'undetermined' | 'markdown' | 'json-render' | 'a2ui' | 'mixed For lower-level control over JSON-to-Spec materialization: ```typescript -import { createPartialJsonParser } from '@ngaf/partial-json'; +import { createPartialJsonParser } from '@cacheplane/partial-json'; import { createParseTreeStore } from '@ngaf/chat'; const parser = createPartialJsonParser(); diff --git a/apps/website/src/app/llms.txt/route.ts b/apps/website/src/app/llms.txt/route.ts index a204fa58f..82f6f0fd6 100644 --- a/apps/website/src/app/llms.txt/route.ts +++ b/apps/website/src/app/llms.txt/route.ts @@ -26,7 +26,6 @@ function buildLlmsTxt(): string { '- @ngaf/ag-ui — adapter for any AG-UI-compatible backend (CrewAI, Mastra, Microsoft AF, AG2, Pydantic AI, AWS Strands, CopilotKit runtime)', '- @ngaf/render — generative UI runtime (Vercel json-render + Google A2UI)', '- @ngaf/a2ui — A2UI catalog components', - '- @ngaf/partial-json — streaming JSON parser', '- @ngaf/licensing — license verification client', '', '## Install', diff --git a/cockpit/render/shared/streaming-simulator.ts b/cockpit/render/shared/streaming-simulator.ts index 1452ef0fc..45ed77908 100644 --- a/cockpit/render/shared/streaming-simulator.ts +++ b/cockpit/render/shared/streaming-simulator.ts @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT import { signal, computed } from '@angular/core'; -import { createPartialJsonParser, materialize } from '@ngaf/partial-json'; -import type { PartialJsonParser, ParseEvent } from '@ngaf/partial-json'; +import { createPartialJsonParser, materialize } from '@cacheplane/partial-json'; +import type { PartialJsonParser, ParseEvent } from '@cacheplane/partial-json'; import type { Spec } from '@json-render/core'; export class StreamingSimulator { diff --git a/docs/RELEASE.md b/docs/RELEASE.md index 32be579de..f4a2d981a 100644 --- a/docs/RELEASE.md +++ b/docs/RELEASE.md @@ -1,6 +1,6 @@ # Release Process -The seven publishable libraries (`@ngaf/chat`, `@ngaf/langgraph`, `@ngaf/ag-ui`, `@ngaf/render`, `@ngaf/a2ui`, `@ngaf/partial-json`, `@ngaf/licensing`) ship together at a synchronized version via Nx Release. During the `0.0.x` exploratory phase, only patch bumps are used. +The seven publishable libraries (`@ngaf/chat`, `@ngaf/langgraph`, `@ngaf/ag-ui`, `@ngaf/render`, `@ngaf/a2ui`, `@ngaf/licensing`, `@ngaf/telemetry`) ship together at a synchronized version via Nx Release. During the `0.0.x` exploratory phase, only patch bumps are used. ## One-shot release (recommended; second release onward) @@ -51,7 +51,7 @@ The very first publish ships the version currently on disk (`0.0.1`) — no vers ```bash # 1. Build everything -npx nx run-many -t build --projects=chat,langgraph,ag-ui,render,a2ui,partial-json,licensing +npx nx run-many -t build --projects=chat,langgraph,ag-ui,render,a2ui,licensing,telemetry # 2. Generate the initial CHANGELOG, commit, and tag v0.0.1 npx nx release changelog 0.0.1 --first-release diff --git a/libs/partial-json/package.json b/libs/partial-json/package.json deleted file mode 100644 index 79a4e3468..000000000 --- a/libs/partial-json/package.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "@ngaf/partial-json", - "version": "0.0.29", - "deprecated": "Replaced by @cacheplane/partial-json. No further versions will be published from this package.", - "license": "MIT", - "repository": { - "type": "git", - "url": "https://github.com/cacheplane/angular-agent-framework.git", - "directory": "libs/partial-json" - }, - "homepage": "https://github.com/cacheplane/angular-agent-framework#readme", - "bugs": { - "url": "https://github.com/cacheplane/angular-agent-framework/issues" - }, - "sideEffects": false -} diff --git a/libs/partial-json/project.json b/libs/partial-json/project.json deleted file mode 100644 index 841eb2710..000000000 --- a/libs/partial-json/project.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "name": "partial-json", - "$schema": "../../node_modules/nx/schemas/project-schema.json", - "sourceRoot": "libs/partial-json/src", - "projectType": "library", - "tags": [ - "scope:shared", - "type:lib" - ], - "targets": { - "build": { - "executor": "@nx/js:tsc", - "outputs": [ - "{workspaceRoot}/dist/libs/partial-json" - ], - "options": { - "outputPath": "dist/libs/partial-json", - "main": "libs/partial-json/src/index.ts", - "tsConfig": "libs/partial-json/tsconfig.lib.json" - } - }, - "lint": { - "executor": "@nx/eslint:lint" - }, - "test": { - "executor": "@nx/vitest:test", - "options": { - "configFile": "libs/partial-json/vite.config.mts" - } - }, - "nx-release-publish": { - "options": { - "packageRoot": "dist/{projectRoot}" - } - } - } -} diff --git a/libs/partial-json/src/index.ts b/libs/partial-json/src/index.ts deleted file mode 100644 index 119d2fa75..000000000 --- a/libs/partial-json/src/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-License-Identifier: MIT -export type { - JsonNodeType, JsonNodeStatus, JsonNodeBase, - JsonObjectNode, JsonArrayNode, JsonStringNode, - JsonNumberNode, JsonBooleanNode, JsonNullNode, - JsonNode, ParseEvent, PartialJsonParser, -} from './lib/types'; -export { createPartialJsonParser } from './lib/parser'; -export { materialize } from './lib/materialize'; diff --git a/libs/partial-json/src/lib/materialize.spec.ts b/libs/partial-json/src/lib/materialize.spec.ts deleted file mode 100644 index e0bb4e718..000000000 --- a/libs/partial-json/src/lib/materialize.spec.ts +++ /dev/null @@ -1,144 +0,0 @@ -// SPDX-License-Identifier: MIT -import { describe, it, expect } from 'vitest'; -import { createPartialJsonParser } from './parser'; -import { materialize } from './materialize'; -import type { JsonArrayNode, JsonObjectNode, JsonStringNode } from './types'; - -describe('materialize', () => { - describe('basic materialization', () => { - it('should materialize a string node', () => { - const parser = createPartialJsonParser(); - parser.push('"hello"'); - expect(materialize(parser.root!)).toBe('hello'); - }); - - it('should materialize a number node inside an array', () => { - const parser = createPartialJsonParser(); - parser.push('[42]'); - const result = materialize(parser.root!) as unknown[]; - expect(result).toEqual([42]); - }); - - it('should materialize a boolean true', () => { - const parser = createPartialJsonParser(); - parser.push('[true]'); - const result = materialize(parser.root!) as unknown[]; - expect(result).toEqual([true]); - }); - - it('should materialize a boolean false', () => { - const parser = createPartialJsonParser(); - parser.push('[false]'); - const result = materialize(parser.root!) as unknown[]; - expect(result).toEqual([false]); - }); - - it('should materialize null', () => { - const parser = createPartialJsonParser(); - parser.push('[null]'); - const result = materialize(parser.root!) as unknown[]; - expect(result).toEqual([null]); - }); - - it('should materialize a simple object', () => { - const parser = createPartialJsonParser(); - parser.push('{"a": 1, "b": "two"}'); - const result = materialize(parser.root!) as Record; - expect(result).toEqual({ a: 1, b: 'two' }); - }); - - it('should materialize an array', () => { - const parser = createPartialJsonParser(); - parser.push('[1, 2, 3]'); - const result = materialize(parser.root!) as unknown[]; - expect(result).toEqual([1, 2, 3]); - }); - - it('should materialize nested structures', () => { - const parser = createPartialJsonParser(); - parser.push('{"items": [{"name": "a"}, {"name": "b"}]}'); - const result = materialize(parser.root!) as Record; - expect(result).toEqual({ items: [{ name: 'a' }, { name: 'b' }] }); - }); - - it('should materialize a partial streaming string', () => { - const parser = createPartialJsonParser(); - parser.push('{"msg": "hel'); - const result = materialize(parser.root!) as Record; - expect(result).toEqual({ msg: 'hel' }); - }); - - it('should materialize a partial streaming number with best-effort', () => { - const parser = createPartialJsonParser(); - parser.push('[12'); - // Number is still streaming (not terminated), so best-effort parse - const result = materialize(parser.root!) as unknown[]; - expect(result).toEqual([12]); - }); - }); - - describe('structural sharing', () => { - it('should return the same reference for unchanged subtrees', () => { - const parser = createPartialJsonParser(); - parser.push('{"a": {"x": 1}, "b": "hel'); - - const result1 = materialize(parser.root!) as Record; - const aRef1 = result1['a']; - - // Stream more into b - parser.push('lo"'); - const result2 = materialize(parser.root!) as Record; - const aRef2 = result2['a']; - - // a subtree didn't change, should be same reference - expect(aRef2).toBe(aRef1); - // b did change - expect(result2['b']).toBe('hello'); - }); - - it('should preserve sibling references when one property changes', () => { - const parser = createPartialJsonParser(); - parser.push('{"x": [1, 2], "y": [3, 4], "z": "stream'); - - const result1 = materialize(parser.root!) as Record; - const xRef1 = result1['x']; - const yRef1 = result1['y']; - - // Only z changes - parser.push('ing"'); - const result2 = materialize(parser.root!) as Record; - - expect(result2['x']).toBe(xRef1); - expect(result2['y']).toBe(yRef1); - expect(result2['z']).toBe('streaming'); - }); - - it('should return the same reference when nothing changed between calls', () => { - const parser = createPartialJsonParser(); - parser.push('{"a": 1, "b": 2}'); - - const result1 = materialize(parser.root!); - const result2 = materialize(parser.root!); - - expect(result2).toBe(result1); - }); - - it('should detect changes in nested arrays', () => { - const parser = createPartialJsonParser(); - parser.push('{"items": [1'); - - const result1 = materialize(parser.root!) as Record; - const items1 = result1['items'] as unknown[]; - expect(items1).toEqual([1]); - - // Add another item - parser.push(', 2]'); - const result2 = materialize(parser.root!) as Record; - const items2 = result2['items'] as unknown[]; - expect(items2).toEqual([1, 2]); - - // items array changed (new child), so different reference - expect(items2).not.toBe(items1); - }); - }); -}); diff --git a/libs/partial-json/src/lib/materialize.ts b/libs/partial-json/src/lib/materialize.ts deleted file mode 100644 index d42d73798..000000000 --- a/libs/partial-json/src/lib/materialize.ts +++ /dev/null @@ -1,97 +0,0 @@ -// SPDX-License-Identifier: MIT -import type { JsonNode, JsonObjectNode, JsonArrayNode } from './types'; - -/** - * Cache entry storing the last materialized value and a version fingerprint - * used to detect whether a subtree has changed. - */ -interface CacheEntry { - version: string; - value: unknown; -} - -const cache = new WeakMap(); - -/** - * Compute a lightweight version fingerprint for a node. - * The fingerprint captures enough state to detect any change in the - * subtree rooted at this node. - */ -function computeVersion(node: JsonNode): string { - switch (node.type) { - case 'string': - return `s:${node.status}:${node.value}`; - case 'number': - return `n:${node.status}:${node.raw}`; - case 'boolean': - return `b:${node.value}`; - case 'null': - return 'null'; - case 'object': { - const obj = node as JsonObjectNode; - const parts: string[] = [`o:${obj.status}:${obj.children.size}`]; - for (const [key, child] of obj.children) { - parts.push(`${key}=${computeVersion(child)}`); - } - return parts.join('|'); - } - case 'array': { - const arr = node as JsonArrayNode; - const parts: string[] = [`a:${arr.status}:${arr.children.length}`]; - for (const child of arr.children) { - parts.push(computeVersion(child)); - } - return parts.join('|'); - } - } -} - -/** - * Convert a parse-tree node to a plain JS value. - * - * Structural sharing: unchanged subtrees return the exact same object - * reference across consecutive calls, enabling cheap `===` checks - * in downstream consumers (e.g. Angular signals, React memos). - */ -export function materialize(node: JsonNode): unknown { - const version = computeVersion(node); - const cached = cache.get(node); - if (cached && cached.version === version) { - return cached.value; - } - - let value: unknown; - - switch (node.type) { - case 'string': - value = node.value; - break; - case 'number': - // Complete numbers have a parsed value; streaming ones get best-effort - value = node.value !== null ? node.value : Number(node.raw); - break; - case 'boolean': - value = node.value; - break; - case 'null': - value = null; - break; - case 'object': { - const obj = node as JsonObjectNode; - const result: Record = {}; - for (const [key, child] of obj.children) { - result[key] = materialize(child); - } - value = result; - break; - } - case 'array': { - const arr = node as JsonArrayNode; - value = arr.children.map((child) => materialize(child)); - break; - } - } - - cache.set(node, { version, value }); - return value; -} diff --git a/libs/partial-json/src/lib/parser.spec.ts b/libs/partial-json/src/lib/parser.spec.ts deleted file mode 100644 index 7fb0afe02..000000000 --- a/libs/partial-json/src/lib/parser.spec.ts +++ /dev/null @@ -1,404 +0,0 @@ -// SPDX-License-Identifier: MIT -import { describe, it, expect } from 'vitest'; -import { createPartialJsonParser } from './parser'; -import type { - JsonStringNode, - JsonNumberNode, - JsonBooleanNode, - JsonNullNode, - JsonObjectNode, - JsonArrayNode, - ParseEvent, -} from './types'; - -describe('createPartialJsonParser', () => { - describe('strings', () => { - it('should parse a complete string', () => { - const parser = createPartialJsonParser(); - const events = parser.push('"hello"'); - const root = parser.root as JsonStringNode; - expect(root).not.toBeNull(); - expect(root.type).toBe('string'); - expect(root.value).toBe('hello'); - expect(root.status).toBe('complete'); - }); - - it('should stream a string character-by-character', () => { - const parser = createPartialJsonParser(); - parser.push('"'); - parser.push('h'); - parser.push('e'); - parser.push('l'); - parser.push('l'); - parser.push('o'); - parser.push('"'); - const root = parser.root as JsonStringNode; - expect(root.type).toBe('string'); - expect(root.value).toBe('hello'); - expect(root.status).toBe('complete'); - }); - - it('should emit value-updated events with delta for strings', () => { - const parser = createPartialJsonParser(); - parser.push('"'); - const events1 = parser.push('he'); - const valueUpdates = events1.filter((e) => e.type === 'value-updated'); - expect(valueUpdates.length).toBeGreaterThan(0); - for (const ev of valueUpdates) { - expect(ev.delta).toBeDefined(); - } - parser.push('llo"'); - const root = parser.root as JsonStringNode; - expect(root.value).toBe('hello'); - }); - - it('should handle escaped characters (\\n, \\", \\\\)', () => { - const parser = createPartialJsonParser(); - parser.push('"line1\\nline2"'); - const root = parser.root as JsonStringNode; - expect(root.value).toBe('line1\nline2'); - }); - - it('should handle escaped double quote', () => { - const parser = createPartialJsonParser(); - parser.push('"say \\"hi\\""'); - const root = parser.root as JsonStringNode; - expect(root.value).toBe('say "hi"'); - }); - - it('should handle escaped backslash', () => { - const parser = createPartialJsonParser(); - parser.push('"a\\\\b"'); - const root = parser.root as JsonStringNode; - expect(root.value).toBe('a\\b'); - }); - - it('should handle unicode escapes (\\u0041 = A)', () => { - const parser = createPartialJsonParser(); - parser.push('"\\u0041"'); - const root = parser.root as JsonStringNode; - expect(root.value).toBe('A'); - }); - }); - - describe('numbers', () => { - it('should parse a complete integer in an array', () => { - const parser = createPartialJsonParser(); - parser.push('[42]'); - const root = parser.root as JsonArrayNode; - const num = root.children[0] as JsonNumberNode; - expect(num.type).toBe('number'); - expect(num.value).toBe(42); - expect(num.status).toBe('complete'); - }); - - it('should complete a number when followed by }', () => { - const parser = createPartialJsonParser(); - parser.push('{"a":123}'); - const root = parser.root as JsonObjectNode; - const num = root.children.get('a') as JsonNumberNode; - expect(num.type).toBe('number'); - expect(num.value).toBe(123); - expect(num.status).toBe('complete'); - }); - - it('should handle negative and decimal numbers', () => { - const parser = createPartialJsonParser(); - parser.push('[-3.14]'); - const root = parser.root as JsonArrayNode; - const num = root.children[0] as JsonNumberNode; - expect(num.value).toBe(-3.14); - expect(num.status).toBe('complete'); - }); - - it('should stream numbers at end of input', () => { - const parser = createPartialJsonParser(); - parser.push('[12'); - const root = parser.root as JsonArrayNode; - const num = root.children[0] as JsonNumberNode; - expect(num.type).toBe('number'); - expect(num.raw).toBe('12'); - expect(num.status).toBe('streaming'); - }); - }); - - describe('booleans and null', () => { - it('should parse true', () => { - const parser = createPartialJsonParser(); - parser.push('[true]'); - const root = parser.root as JsonArrayNode; - const node = root.children[0] as JsonBooleanNode; - expect(node.type).toBe('boolean'); - expect(node.value).toBe(true); - expect(node.status).toBe('complete'); - }); - - it('should parse false', () => { - const parser = createPartialJsonParser(); - parser.push('[false]'); - const root = parser.root as JsonArrayNode; - const node = root.children[0] as JsonBooleanNode; - expect(node.type).toBe('boolean'); - expect(node.value).toBe(false); - expect(node.status).toBe('complete'); - }); - - it('should parse null', () => { - const parser = createPartialJsonParser(); - parser.push('[null]'); - const root = parser.root as JsonArrayNode; - const node = root.children[0] as JsonNullNode; - expect(node.type).toBe('null'); - expect(node.status).toBe('complete'); - }); - - it('should handle partial keywords gracefully', () => { - const parser = createPartialJsonParser(); - parser.push('[tru'); - const root = parser.root as JsonArrayNode; - // Partial keyword should create a pending node - expect(root.children.length).toBe(1); - expect(root.children[0].status).toBe('pending'); - }); - }); - - describe('objects', () => { - it('should parse a simple object', () => { - const parser = createPartialJsonParser(); - parser.push('{"a":"b"}'); - const root = parser.root as JsonObjectNode; - expect(root.type).toBe('object'); - expect(root.status).toBe('complete'); - const child = root.children.get('a') as JsonStringNode; - expect(child.value).toBe('b'); - expect(child.status).toBe('complete'); - }); - - it('should stream property values', () => { - const parser = createPartialJsonParser(); - parser.push('{"name":"Al'); - const root = parser.root as JsonObjectNode; - expect(root.type).toBe('object'); - expect(root.status).toBe('streaming'); - const child = root.children.get('name') as JsonStringNode; - expect(child.type).toBe('string'); - expect(child.value).toBe('Al'); - expect(child.status).toBe('streaming'); - }); - - it('should handle multiple properties', () => { - const parser = createPartialJsonParser(); - parser.push('{"a":"1","b":"2"}'); - const root = parser.root as JsonObjectNode; - expect(root.children.size).toBe(2); - expect((root.children.get('a') as JsonStringNode).value).toBe('1'); - expect((root.children.get('b') as JsonStringNode).value).toBe('2'); - }); - - it('should handle nested objects', () => { - const parser = createPartialJsonParser(); - parser.push('{"outer":{"inner":"value"}}'); - const root = parser.root as JsonObjectNode; - const outer = root.children.get('outer') as JsonObjectNode; - expect(outer.type).toBe('object'); - const inner = outer.children.get('inner') as JsonStringNode; - expect(inner.value).toBe('value'); - }); - }); - - describe('arrays', () => { - it('should parse a simple array of numbers', () => { - const parser = createPartialJsonParser(); - parser.push('[1,2,3]'); - const root = parser.root as JsonArrayNode; - expect(root.type).toBe('array'); - expect(root.status).toBe('complete'); - expect(root.children.length).toBe(3); - expect((root.children[0] as JsonNumberNode).value).toBe(1); - expect((root.children[1] as JsonNumberNode).value).toBe(2); - expect((root.children[2] as JsonNumberNode).value).toBe(3); - }); - - it('should parse an array of strings', () => { - const parser = createPartialJsonParser(); - parser.push('["a","b","c"]'); - const root = parser.root as JsonArrayNode; - expect(root.children.length).toBe(3); - expect((root.children[0] as JsonStringNode).value).toBe('a'); - expect((root.children[1] as JsonStringNode).value).toBe('b'); - expect((root.children[2] as JsonStringNode).value).toBe('c'); - }); - - it('should parse nested arrays', () => { - const parser = createPartialJsonParser(); - parser.push('[[1,2],[3]]'); - const root = parser.root as JsonArrayNode; - expect(root.children.length).toBe(2); - const first = root.children[0] as JsonArrayNode; - expect(first.type).toBe('array'); - expect(first.children.length).toBe(2); - }); - }); - - describe('streaming complex structures', () => { - it('should build a spec-like structure token-by-token', () => { - const parser = createPartialJsonParser(); - const json = '{"type":"div","props":{"class":"main"},"children":[{"type":"span"}]}'; - // Feed one character at a time - for (const ch of json) { - parser.push(ch); - } - const root = parser.root as JsonObjectNode; - expect(root.type).toBe('object'); - expect(root.status).toBe('complete'); - const typeNode = root.children.get('type') as JsonStringNode; - expect(typeNode.value).toBe('div'); - const propsNode = root.children.get('props') as JsonObjectNode; - expect(propsNode.children.get('class')).toBeDefined(); - const childrenNode = root.children.get('children') as JsonArrayNode; - expect(childrenNode.children.length).toBe(1); - }); - - it('should maintain stable node identities across pushes', () => { - const parser = createPartialJsonParser(); - parser.push('{"name":"'); - const root1 = parser.root as JsonObjectNode; - const nameNode1 = root1.children.get('name') as JsonStringNode; - const id1 = nameNode1.id; - - parser.push('Al'); - const nameNode2 = (parser.root as JsonObjectNode).children.get( - 'name' - ) as JsonStringNode; - expect(nameNode2.id).toBe(id1); - expect(nameNode2.value).toBe('Al'); - - parser.push('ice"'); - const nameNode3 = (parser.root as JsonObjectNode).children.get( - 'name' - ) as JsonStringNode; - expect(nameNode3.id).toBe(id1); - expect(nameNode3.value).toBe('Alice'); - }); - }); - - describe('getByPath', () => { - it('should return root for empty path', () => { - const parser = createPartialJsonParser(); - parser.push('{"a":1}'); - expect(parser.getByPath('')).toBe(parser.root); - }); - - it('should look up object properties', () => { - const parser = createPartialJsonParser(); - parser.push('{"a":{"b":"c"}}'); - const node = parser.getByPath('/a/b') as JsonStringNode; - expect(node).not.toBeNull(); - expect(node.type).toBe('string'); - expect(node.value).toBe('c'); - }); - - it('should look up array indices', () => { - const parser = createPartialJsonParser(); - parser.push('{"items":["x","y","z"]}'); - const node = parser.getByPath('/items/1') as JsonStringNode; - expect(node).not.toBeNull(); - expect(node.value).toBe('y'); - }); - - it('should return null for non-existent paths', () => { - const parser = createPartialJsonParser(); - parser.push('{"a":1}'); - expect(parser.getByPath('/b')).toBeNull(); - expect(parser.getByPath('/a/b')).toBeNull(); - }); - }); - - describe('error recovery', () => { - it('handles empty input', () => { - const parser = createPartialJsonParser(); - const events = parser.push(''); - expect(events).toEqual([]); - expect(parser.root).toBeNull(); - }); - - it('handles input with only whitespace', () => { - const parser = createPartialJsonParser(); - const events = parser.push(' \n\t'); - expect(events).toEqual([]); - expect(parser.root).toBeNull(); - }); - - it('leaves root as null for invalid characters in EXPECT_VALUE state', () => { - const parser = createPartialJsonParser(); - // 'x' does not match any case in EXPECT_VALUE, so it falls through the switch. - // The parser never creates a root node for unrecognized characters. - const events = parser.push('xxx{"a":1}'); - // Because 'x' is not whitespace and not a recognized token start, - // the parser stays in EXPECT_VALUE but does nothing — root remains null. - // Only when '{' is encountered does parsing begin, but by then 'xxx' has already - // been consumed with no effect. - // Actually, 'x' falls through the switch with no match, so processing continues - // to the next char. '{' will be reached and parsed normally. - expect(parser.root).not.toBeNull(); - const root = parser.root as JsonObjectNode; - expect(root.type).toBe('object'); - expect((root.children.get('a') as JsonNumberNode).value).toBe(1); - }); - - it('handles trailing text after valid JSON', () => { - const parser = createPartialJsonParser(); - parser.push('{"a":1}some trailing text'); - const root = parser.root as JsonObjectNode; - expect(root.type).toBe('object'); - expect(root.status).toBe('complete'); - expect((root.children.get('a') as JsonNumberNode).value).toBe(1); - }); - - it('handles very long strings without crashing', () => { - const parser = createPartialJsonParser(); - const longStr = 'a'.repeat(100000); - parser.push('"' + longStr + '"'); - const root = parser.root as JsonStringNode; - expect(root.type).toBe('string'); - expect(root.value.length).toBe(100000); - expect(root.status).toBe('complete'); - }); - - it('handles deeply nested objects without stack overflow', () => { - const parser = createPartialJsonParser(); - const depth = 100; - const open = '{"a":'.repeat(depth); - const close = '}'.repeat(depth); - parser.push(open + '"leaf"' + close); - let current = parser.root as JsonObjectNode; - for (let i = 0; i < depth - 1; i++) { - expect(current.type).toBe('object'); - current = current.children.get('a') as JsonObjectNode; - } - // The innermost value is a string - const leaf = current.children.get('a') as JsonStringNode; - expect(leaf.type).toBe('string'); - expect(leaf.value).toBe('leaf'); - }); - }); - - describe('whitespace', () => { - it('should handle whitespace between tokens', () => { - const parser = createPartialJsonParser(); - parser.push('{ "a" : "b" , "c" : "d" }'); - const root = parser.root as JsonObjectNode; - expect(root.type).toBe('object'); - expect(root.status).toBe('complete'); - expect((root.children.get('a') as JsonStringNode).value).toBe('b'); - expect((root.children.get('c') as JsonStringNode).value).toBe('d'); - }); - - it('should handle leading whitespace', () => { - const parser = createPartialJsonParser(); - parser.push(' "hello"'); - const root = parser.root as JsonStringNode; - expect(root.value).toBe('hello'); - }); - }); -}); diff --git a/libs/partial-json/src/lib/parser.ts b/libs/partial-json/src/lib/parser.ts deleted file mode 100644 index 1f1c99f33..000000000 --- a/libs/partial-json/src/lib/parser.ts +++ /dev/null @@ -1,472 +0,0 @@ -// SPDX-License-Identifier: MIT -import type { - JsonNode, - JsonObjectNode, - JsonArrayNode, - JsonStringNode, - JsonNumberNode, - JsonBooleanNode, - JsonNullNode, - ParseEvent, - PartialJsonParser, -} from './types'; - -type State = - | 'EXPECT_VALUE' - | 'IN_STRING' - | 'IN_STRING_ESCAPE' - | 'IN_STRING_UNICODE' - | 'IN_NUMBER' - | 'IN_KEYWORD' - | 'EXPECT_KEY' - | 'IN_KEY_STRING' - | 'IN_KEY_STRING_ESCAPE' - | 'IN_KEY_STRING_UNICODE' - | 'EXPECT_COLON' - | 'AFTER_VALUE'; - -const ESCAPE_MAP: Record = { - '"': '"', - '\\': '\\', - '/': '/', - b: '\b', - f: '\f', - n: '\n', - r: '\r', - t: '\t', -}; - -const KEYWORDS: Record = { - true: { type: 'boolean', value: true }, - false: { type: 'boolean', value: false }, - null: { type: 'null', value: null }, -}; - -export function createPartialJsonParser(): PartialJsonParser { - let nextId = 0; - let root: JsonNode | null = null; - let state: State = 'EXPECT_VALUE'; - let currentNode: JsonNode | null = null; - - // For string values - let stringNode: JsonStringNode | null = null; - - // For unicode escapes - let unicodeBuffer = ''; - let unicodeCount = 0; - - // For key strings in objects - let keyBuffer = ''; - let keyUnicodeBuffer = ''; - let keyUnicodeCount = 0; - - // For keywords (true, false, null) - let keywordBuffer = ''; - let keywordNode: JsonNode | null = null; - - // Stack of container nodes for nested structures - const containerStack: (JsonObjectNode | JsonArrayNode)[] = []; - - function makeId(): number { - return nextId++; - } - - function createStringNode(parent: JsonNode | null, key: string | number | null): JsonStringNode { - return { - id: makeId(), - type: 'string', - status: 'streaming', - parent, - key, - value: '', - }; - } - - function createNumberNode(parent: JsonNode | null, key: string | number | null, firstChar: string): JsonNumberNode { - return { - id: makeId(), - type: 'number', - status: 'streaming', - parent, - key, - raw: firstChar, - value: null, - }; - } - - function createObjectNode(parent: JsonNode | null, key: string | number | null): JsonObjectNode { - return { - id: makeId(), - type: 'object', - status: 'streaming', - parent, - key, - children: new Map(), - pendingKey: null, - }; - } - - function createArrayNode(parent: JsonNode | null, key: string | number | null): JsonArrayNode { - return { - id: makeId(), - type: 'array', - status: 'streaming', - parent, - key, - children: [], - }; - } - - function currentContainer(): JsonObjectNode | JsonArrayNode | null { - return containerStack.length > 0 ? containerStack[containerStack.length - 1] : null; - } - - function getKeyForNewChild(): string | number | null { - const container = currentContainer(); - if (!container) return null; - if (container.type === 'object') { - return container.pendingKey; - } - return container.children.length; - } - - function attachChild(node: JsonNode): void { - const container = currentContainer(); - if (!container) { - root = node; - return; - } - if (container.type === 'object') { - const objContainer = container; - const key = objContainer.pendingKey!; - node.key = key; - node.parent = objContainer; - objContainer.children.set(key, node); - objContainer.pendingKey = null; - } else { - node.key = container.children.length; - node.parent = container; - container.children.push(node); - } - } - - function completeNumber(numNode: JsonNumberNode, events: ParseEvent[]): void { - numNode.value = Number(numNode.raw); - numNode.status = 'complete'; - events.push({ type: 'node-completed', node: numNode }); - } - - function completeKeyword(events: ParseEvent[]): void { - const kw = keywordBuffer; - const kwDef = KEYWORDS[kw]; - if (!kwDef) return; - - const node = keywordNode!; - if (kwDef.type === 'boolean') { - (node as JsonBooleanNode).value = kwDef.value as boolean; - } - node.status = 'complete'; - events.push({ type: 'node-completed', node }); - keywordBuffer = ''; - keywordNode = null; - } - - function push(chunk: string): ParseEvent[] { - const events: ParseEvent[] = []; - - for (let i = 0; i < chunk.length; i++) { - const ch = chunk[i]; - - switch (state) { - case 'EXPECT_VALUE': { - if (ch === ' ' || ch === '\t' || ch === '\n' || ch === '\r') continue; - if (ch === '"') { - const key = getKeyForNewChild(); - const node = createStringNode(currentContainer(), key); - attachChild(node); - if (!root) root = node; - stringNode = node; - events.push({ type: 'node-created', node }); - state = 'IN_STRING'; - } else if (ch === '{') { - const key = getKeyForNewChild(); - const node = createObjectNode(currentContainer(), key); - attachChild(node); - if (!root) root = node; - events.push({ type: 'node-created', node }); - containerStack.push(node); - state = 'EXPECT_KEY'; - } else if (ch === '[') { - const key = getKeyForNewChild(); - const node = createArrayNode(currentContainer(), key); - attachChild(node); - if (!root) root = node; - events.push({ type: 'node-created', node }); - containerStack.push(node); - state = 'EXPECT_VALUE'; - } else if (ch === ']') { - // Empty array close - const container = currentContainer(); - if (container && container.type === 'array') { - container.status = 'complete'; - containerStack.pop(); - events.push({ type: 'node-completed', node: container }); - state = containerStack.length > 0 ? 'AFTER_VALUE' : 'AFTER_VALUE'; - } - } else if (ch === '-' || (ch >= '0' && ch <= '9')) { - const key = getKeyForNewChild(); - const node = createNumberNode(currentContainer(), key, ch); - attachChild(node); - if (!root) root = node; - events.push({ type: 'node-created', node }); - currentNode = node; - state = 'IN_NUMBER'; - } else if (ch === 't' || ch === 'f' || ch === 'n') { - const key = getKeyForNewChild(); - let node: JsonNode; - if (ch === 'n') { - node = { - id: makeId(), - type: 'null', - status: 'pending', - parent: currentContainer(), - key, - } as JsonNullNode; - } else { - node = { - id: makeId(), - type: 'boolean', - status: 'pending', - parent: currentContainer(), - key, - value: ch === 't', - } as JsonBooleanNode; - } - attachChild(node); - if (!root) root = node; - events.push({ type: 'node-created', node }); - keywordBuffer = ch; - keywordNode = node; - state = 'IN_KEYWORD'; - } - break; - } - - case 'IN_STRING': { - if (ch === '\\') { - state = 'IN_STRING_ESCAPE'; - } else if (ch === '"') { - stringNode!.status = 'complete'; - events.push({ type: 'node-completed', node: stringNode! }); - stringNode = null; - state = containerStack.length > 0 ? 'AFTER_VALUE' : 'AFTER_VALUE'; - } else { - stringNode!.value += ch; - events.push({ type: 'value-updated', node: stringNode!, delta: ch }); - } - break; - } - - case 'IN_STRING_ESCAPE': { - if (ch === 'u') { - unicodeBuffer = ''; - unicodeCount = 0; - state = 'IN_STRING_UNICODE'; - } else { - const mapped = ESCAPE_MAP[ch] ?? ch; - stringNode!.value += mapped; - events.push({ type: 'value-updated', node: stringNode!, delta: mapped }); - state = 'IN_STRING'; - } - break; - } - - case 'IN_STRING_UNICODE': { - unicodeBuffer += ch; - unicodeCount++; - if (unicodeCount === 4) { - const codePoint = parseInt(unicodeBuffer, 16); - const char = String.fromCharCode(codePoint); - stringNode!.value += char; - events.push({ type: 'value-updated', node: stringNode!, delta: char }); - unicodeBuffer = ''; - unicodeCount = 0; - state = 'IN_STRING'; - } - break; - } - - case 'IN_NUMBER': { - const numNode = currentNode as JsonNumberNode; - if ((ch >= '0' && ch <= '9') || ch === '.' || ch === 'e' || ch === 'E' || ch === '+' || ch === '-') { - numNode.raw += ch; - } else { - // Number terminated by this character - completeNumber(numNode, events); - currentNode = null; - // Re-process this character - if (ch === ',' || ch === ']' || ch === '}') { - state = 'AFTER_VALUE'; - i--; // reprocess - } else { - state = 'AFTER_VALUE'; - i--; // reprocess - } - } - break; - } - - case 'IN_KEYWORD': { - keywordBuffer += ch; - const possibleKeywords = Object.keys(KEYWORDS).filter((k) => k.startsWith(keywordBuffer)); - if (possibleKeywords.length === 0) { - // Not a valid keyword continuation - treat as terminator - state = 'AFTER_VALUE'; - i--; // reprocess - } else { - const exact = KEYWORDS[keywordBuffer]; - if (exact) { - completeKeyword(events); - state = containerStack.length > 0 ? 'AFTER_VALUE' : 'AFTER_VALUE'; - } - // Otherwise still accumulating - } - break; - } - - case 'EXPECT_KEY': { - if (ch === ' ' || ch === '\t' || ch === '\n' || ch === '\r') continue; - if (ch === '"') { - keyBuffer = ''; - state = 'IN_KEY_STRING'; - } else if (ch === '}') { - // Empty object - const container = currentContainer(); - if (container && container.type === 'object') { - container.status = 'complete'; - containerStack.pop(); - events.push({ type: 'node-completed', node: container }); - state = containerStack.length > 0 ? 'AFTER_VALUE' : 'AFTER_VALUE'; - } - } - break; - } - - case 'IN_KEY_STRING': { - if (ch === '\\') { - state = 'IN_KEY_STRING_ESCAPE'; - } else if (ch === '"') { - const container = currentContainer() as JsonObjectNode; - container.pendingKey = keyBuffer; - state = 'EXPECT_COLON'; - } else { - keyBuffer += ch; - } - break; - } - - case 'IN_KEY_STRING_ESCAPE': { - if (ch === 'u') { - keyUnicodeBuffer = ''; - keyUnicodeCount = 0; - state = 'IN_KEY_STRING_UNICODE'; - } else { - const mapped = ESCAPE_MAP[ch] ?? ch; - keyBuffer += mapped; - state = 'IN_KEY_STRING'; - } - break; - } - - case 'IN_KEY_STRING_UNICODE': { - keyUnicodeBuffer += ch; - keyUnicodeCount++; - if (keyUnicodeCount === 4) { - const codePoint = parseInt(keyUnicodeBuffer, 16); - keyBuffer += String.fromCharCode(codePoint); - keyUnicodeBuffer = ''; - keyUnicodeCount = 0; - state = 'IN_KEY_STRING'; - } - break; - } - - case 'EXPECT_COLON': { - if (ch === ' ' || ch === '\t' || ch === '\n' || ch === '\r') continue; - if (ch === ':') { - state = 'EXPECT_VALUE'; - } - break; - } - - case 'AFTER_VALUE': { - if (ch === ' ' || ch === '\t' || ch === '\n' || ch === '\r') continue; - if (ch === ',') { - const container = currentContainer(); - if (container) { - if (container.type === 'object') { - state = 'EXPECT_KEY'; - } else { - state = 'EXPECT_VALUE'; - } - } - } else if (ch === '}') { - const container = currentContainer(); - if (container && container.type === 'object') { - container.status = 'complete'; - containerStack.pop(); - events.push({ type: 'node-completed', node: container }); - state = containerStack.length > 0 ? 'AFTER_VALUE' : 'AFTER_VALUE'; - } - } else if (ch === ']') { - const container = currentContainer(); - if (container && container.type === 'array') { - container.status = 'complete'; - containerStack.pop(); - events.push({ type: 'node-completed', node: container }); - state = containerStack.length > 0 ? 'AFTER_VALUE' : 'AFTER_VALUE'; - } - } - break; - } - } - } - - return events; - } - - function getByPath(path: string): JsonNode | null { - if (!root) return null; - if (path === '' || path === '/') return root; - - // Handle paths that don't start with / - const normalizedPath = path.startsWith('/') ? path : '/' + path; - const segments = normalizedPath.split('/').slice(1); // remove leading empty string - - let current: JsonNode = root; - for (const segment of segments) { - if (current.type === 'object') { - const child = (current as JsonObjectNode).children.get(segment); - if (!child) return null; - current = child; - } else if (current.type === 'array') { - const index = parseInt(segment, 10); - if (isNaN(index)) return null; - const child = (current as JsonArrayNode).children[index]; - if (!child) return null; - current = child; - } else { - return null; - } - } - return current; - } - - return { - push, - get root() { - return root; - }, - getByPath, - }; -} diff --git a/libs/partial-json/src/lib/types.ts b/libs/partial-json/src/lib/types.ts deleted file mode 100644 index 8269d7794..000000000 --- a/libs/partial-json/src/lib/types.ts +++ /dev/null @@ -1,82 +0,0 @@ -// SPDX-License-Identifier: MIT - -/** Kinds of JSON values a node can represent. */ -export type JsonNodeType = 'object' | 'array' | 'string' | 'number' | 'boolean' | 'null'; - -/** Parsing state of a node. */ -export type JsonNodeStatus = 'pending' | 'streaming' | 'complete'; - -/** Base shape shared by all nodes. */ -export interface JsonNodeBase { - /** Stable identity — assigned on creation, never changes. */ - readonly id: number; - /** What kind of JSON value this node represents. */ - readonly type: JsonNodeType; - /** Parsing state. */ - status: JsonNodeStatus; - /** Parent node (null for root). */ - parent: JsonNode | null; - /** Key in parent — string for object properties, number for array indices. */ - key: string | number | null; -} - -export interface JsonObjectNode extends JsonNodeBase { - readonly type: 'object'; - children: Map; - /** Key currently being built (between quote open and colon). */ - pendingKey: string | null; -} - -export interface JsonArrayNode extends JsonNodeBase { - readonly type: 'array'; - children: JsonNode[]; -} - -export interface JsonStringNode extends JsonNodeBase { - readonly type: 'string'; - /** Grows character-by-character as tokens arrive. */ - value: string; -} - -export interface JsonNumberNode extends JsonNodeBase { - readonly type: 'number'; - /** Raw characters accumulated so far. */ - raw: string; - /** Parsed value — set when node completes. */ - value: number | null; -} - -export interface JsonBooleanNode extends JsonNodeBase { - readonly type: 'boolean'; - value: boolean; -} - -export interface JsonNullNode extends JsonNodeBase { - readonly type: 'null'; -} - -export type JsonNode = - | JsonObjectNode - | JsonArrayNode - | JsonStringNode - | JsonNumberNode - | JsonBooleanNode - | JsonNullNode; - -/** Events emitted by the parser as the tree changes. */ -export interface ParseEvent { - type: 'node-created' | 'value-updated' | 'node-completed'; - node: JsonNode; - /** For value-updated on strings: the characters appended this push. */ - delta?: string; -} - -/** Push-based streaming JSON parser. */ -export interface PartialJsonParser { - /** Feed characters. Returns events for what changed. */ - push(chunk: string): ParseEvent[]; - /** Root node of the parse tree. */ - readonly root: JsonNode | null; - /** Look up a node by JSON Pointer path (e.g., "/elements/el-1/props"). */ - getByPath(path: string): JsonNode | null; -} diff --git a/libs/partial-json/tsconfig.json b/libs/partial-json/tsconfig.json deleted file mode 100644 index 1645f314f..000000000 --- a/libs/partial-json/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "files": [], - "references": [ - { "path": "./tsconfig.lib.json" } - ] -} diff --git a/libs/partial-json/tsconfig.lib.json b/libs/partial-json/tsconfig.lib.json deleted file mode 100644 index 6ad422f39..000000000 --- a/libs/partial-json/tsconfig.lib.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "outDir": "../../dist/out-tsc", - "declaration": true, - "emitDeclarationOnly": false - }, - "include": [ - "src/**/*.ts" - ], - "exclude": [ - "src/**/*.spec.ts" - ] -} diff --git a/libs/partial-json/vite.config.mts b/libs/partial-json/vite.config.mts deleted file mode 100644 index 971c722be..000000000 --- a/libs/partial-json/vite.config.mts +++ /dev/null @@ -1,12 +0,0 @@ -import { defineConfig } from 'vite'; -import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin'; - -export default defineConfig({ - plugins: [nxViteTsPaths()], - test: { - environment: 'node', - globals: true, - include: ['src/**/*.spec.ts'], - passWithNoTests: true, - }, -}); diff --git a/libs/render/README.md b/libs/render/README.md index e3954e5cf..40d8d803a 100644 --- a/libs/render/README.md +++ b/libs/render/README.md @@ -16,7 +16,7 @@ npm install @ngaf/render - **Two protocols supported** — Vercel `json-render` and Google A2UI v1 - **Per-component fallback API** — when a spec node has no registered component, you control what renders (and surface it to your observability layer) - **Readiness gate** — holds renders until the surface is real, so users never see mystery partial UI -- **Streaming partial renders** — works with `@ngaf/partial-json` to render progressive JSON as it streams +- **Streaming partial renders** — works with `@cacheplane/partial-json` to render progressive JSON as it streams ## Documentation diff --git a/nx.json b/nx.json index 89d3a428e..c3ae6fe34 100644 --- a/nx.json +++ b/nx.json @@ -51,7 +51,6 @@ "ag-ui", "render", "a2ui", - "partial-json", "licensing", "telemetry" ], @@ -59,7 +58,7 @@ } }, "version": { - "preVersionCommand": "npx nx run-many -t build --projects=chat,langgraph,ag-ui,render,a2ui,partial-json,licensing,telemetry", + "preVersionCommand": "npx nx run-many -t build --projects=chat,langgraph,ag-ui,render,a2ui,licensing,telemetry", "updateDependents": "auto", "preserveLocalDependencyProtocols": true }, diff --git a/package-lock.json b/package-lock.json index 73dad3e68..5d76e8436 100644 --- a/package-lock.json +++ b/package-lock.json @@ -244,7 +244,7 @@ }, "libs/design-tokens": { "name": "@ngaf/design-tokens", - "version": "0.0.33", + "version": "0.0.34", "license": "MIT" }, "libs/example-layouts": { @@ -278,12 +278,6 @@ "@noble/ed25519": "^2.2.3" } }, - "libs/partial-json": { - "name": "@ngaf/partial-json", - "version": "0.0.29", - "deprecated": "Replaced by @cacheplane/partial-json. No further versions will be published from this package.", - "license": "MIT" - }, "libs/render": { "name": "@ngaf/render", "version": "0.0.29", @@ -12021,10 +12015,6 @@ "resolved": "apps/minting-service", "link": true }, - "node_modules/@ngaf/partial-json": { - "resolved": "libs/partial-json", - "link": true - }, "node_modules/@ngaf/render": { "resolved": "libs/render", "link": true diff --git a/tsconfig.base.json b/tsconfig.base.json index 8a4bc04c7..79183d553 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -33,7 +33,6 @@ "@ngaf/langgraph": ["libs/langgraph/src/public-api.ts"], "@ngaf/licensing": ["libs/licensing/src/index.ts"], "@ngaf/licensing/testing": ["libs/licensing/src/testing.ts"], - "@ngaf/partial-json": ["libs/partial-json/src/index.ts"], "@ngaf/render": ["libs/render/src/public-api.ts"] }, "skipLibCheck": true,