feat: CLI JSON-RPC wrapper + Node.js integration (Issue #19)#24
feat: CLI JSON-RPC wrapper + Node.js integration (Issue #19)#24mmonterroca wants to merge 13 commits intomasterfrom
Conversation
Add 7 new JSON-RPC methods to complete the CLI layer: - document.addContent: append content array to existing documents - document.addPageBreak: add page break to existing documents - paragraph.add: add single paragraph with full formatting - paragraph.list: list all paragraphs with text/style/index - table.add: add table with rows/cells/borders - table.list: list all tables with dimensions - section.add: add section with page layout and headers/footers Includes 17 new tests (49 total) and updated CLI_GUIDE.md. Closes section A of #19.
- TypeScript package @mmonterroca/docxgo with CJS + ESM support - DocxgoExec: synchronous one-shot client (execFileSync) - DocxgoRPC: async persistent client (child_process.spawn) - DocumentBuilder: fluent API for creating and modifying documents - Binary resolver with platform-specific package support - Complete type definitions for all 15 RPC methods - 17 tests across 4 suites (resolveBinary, DocxgoExec, DocxgoRPC, DocumentBuilder) - ESM build script (scripts/build-esm.js) - Comprehensive README with API reference and examples Implements Section B of issue #19
…h methods - system.ping, system.version, system.capabilities for health/feature discovery - system.batch for multi-request single roundtrip execution - template.inspect to discover placeholders with location details - template.render to merge template data with strict mode + warnings - document.applyPatch for atomic multi-operation patching (6 op types) - version --json flag with capabilities map - Enriched RPCError with data field (category, retryable, index) - Node.js wrapper: new types, builder methods, DocxgoError.data - Go tests: 73 pass (+24 new) - Node.js tests: 30 pass (+13 new)
… npm README - CLI_GUIDE.md: added 7 new methods to TOC, enriched error format docs, full reference for system.ping/version/capabilities/batch, template.inspect/render, document.applyPatch, TEMPLATE_ERROR code - npm/README.md: added template workflow example, patch operations example, system/template/patch builder method tables, enriched DocxgoError.data examples, updated RPC methods table (15 → 22 methods)
- release.yml: builds Go binaries for 5 platforms (darwin-arm64/x64,
linux-x64/arm64, win32-x64) with CGO_ENABLED=0, creates GH Release
with archives + checksums
- npm-publish.yml: triggered on GH Release, publishes platform-specific
@mmonterroca/docxgo-{platform} packages then main package with
matching optionalDependencies versions
- ci.yml: fixed branch names (main→master), added npm-test job that
builds Go binary and runs Node.js tests
- npm/platforms/: scaffold for platform binary packages
- Added id-token: write permission for OIDC - Added --provenance flag to npm publish commands - Added max-parallel: 1 to platform matrix (npm rate limits)
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly expands the utility of the docxgo library by introducing a robust command-line interface (CLI) with JSON-RPC capabilities and a dedicated Node.js wrapper. This allows developers to programmatically create, manipulate, and manage Word documents from both Go and Node.js environments, enabling powerful automation and integration scenarios. The changes include new RPC methods for document content manipulation, templating, and batch processing, alongside comprehensive testing and CI/CD pipelines for multi-platform binary distribution. Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Ignored Files
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
There was a problem hiding this comment.
Pull request overview
Adds a JSON-RPC CLI surface to cmd/docxgo and introduces a Node.js/TypeScript wrapper package under npm/, alongside CI workflows for multi-platform releases and npm publishing to support Issue #19’s integration plan.
Changes:
- Expanded the Go CLI JSON-RPC protocol (new system/template/batch/patch + mutation methods) and updated CLI docs.
- Added an npm package (
@mmonterroca/docxgo) with RPC/exec clients, a fluentDocumentBuilder, protocol types, and Node tests. - Added GitHub Actions workflows for building release binaries and publishing platform-specific npm binary packages + main wrapper package.
Reviewed changes
Copilot reviewed 22 out of 28 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
npm/tsconfig.json |
TypeScript build configuration for the Node wrapper. |
npm/src/types.ts |
Shared JSON-RPC protocol and document/content type definitions. |
npm/src/rpc.ts |
Persistent NDJSON RPC client implementation + DocxgoError. |
npm/src/index.ts |
Public entrypoint exports for the npm package. |
npm/src/exec.ts |
Synchronous one-shot execution client. |
npm/src/builder.ts |
Fluent builder API wrapping the RPC client. |
npm/src/binary.ts |
Cross-platform binary resolution logic (optionalDependencies/local/PATH). |
npm/src/__tests__/docxgo.test.ts |
Node integration tests across exec/RPC/builder/template/patch/batch. |
npm/scripts/build-esm.js |
Generates an ESM wrapper (dist/index.mjs) over CJS output. |
npm/platforms/win32-x64/.gitkeep |
Placeholder for Windows platform package directory. |
npm/platforms/linux-x64/.gitkeep |
Placeholder for Linux x64 platform package directory. |
npm/platforms/linux-arm64/.gitkeep |
Placeholder for Linux arm64 platform package directory. |
npm/platforms/darwin-x64/.gitkeep |
Placeholder for macOS x64 platform package directory. |
npm/platforms/darwin-arm64/.gitkeep |
Placeholder for macOS arm64 platform package directory. |
npm/platforms/README.md |
Documentation for how platform binary npm packages are produced/published. |
npm/platforms/.gitignore |
Ignores generated binaries and platform package manifests. |
npm/package.json |
Defines the npm package, scripts, exports, and optionalDependencies. |
npm/package-lock.json |
Lockfile for npm development dependencies and tooling. |
npm/README.md |
Node wrapper documentation, examples, and API reference. |
npm/.gitignore |
Ignores npm build artifacts (dist/, bin/, tarballs). |
docs/CLI_GUIDE.md |
Protocol/method reference updates (system/template/batch/applyPatch/etc.). |
cmd/docxgo/protocol.go |
Adds ProtocolVersion and enriches RPC errors with data. |
cmd/docxgo/main.go |
Adds version --json output with capabilities and runtime info. |
cmd/docxgo/handlers_test.go |
Adds extensive handler/unit/integration tests for new RPC methods. |
cmd/docxgo/handlers.go |
Implements new RPC handlers (system., mutations, template., applyPatch). |
.github/workflows/release.yml |
Builds multi-platform Go binaries on tag pushes and creates GH Releases. |
.github/workflows/npm-publish.yml |
Publishes platform binary packages then the main npm wrapper package. |
.github/workflows/ci.yml |
Updates CI branch filters and adds a Node.js test job. |
Files not reviewed (1)
- npm/package-lock.json: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
| applied := 0 | ||
| for i, raw := range params.Operations { | ||
| var pOp patchOperation | ||
| if err := json.Unmarshal(raw, &pOp); err != nil { | ||
| return errorResponseWithData(req.ID, errors.ErrCodeValidation, | ||
| fmt.Sprintf("invalid operation at index %d: %v", i, err), op, | ||
| map[string]interface{}{"index": i}) | ||
| } |
docs/CLI_GUIDE.md
Outdated
|
|
||
| ### document.applyPatch | ||
|
|
||
| Applies a sequence of patch operations to an existing document atomically. If any operation fails, subsequent operations are not applied and the error includes the failing index. |
npm/src/builder.ts
Outdated
| * Operations are applied atomically — if any fails, subsequent ones | ||
| * are not applied and the error includes the failing index. | ||
| * | ||
| * @param operations Array of patch operations. | ||
| * @returns Result with count of applied operations. |
npm/package.json
Outdated
| ], | ||
| "scripts": { | ||
| "build": "tsc && node scripts/build-esm.js", | ||
| "test": "node --test dist/__tests__/*.test.js", |
| return new Promise<T>((resolve, reject) => { | ||
| const id = ++this.seq; | ||
| const timeoutMs = timeout ?? this.defaultTimeout; | ||
|
|
||
| const timer = timeoutMs > 0 | ||
| ? setTimeout(() => { | ||
| this.pending.delete(id); | ||
| reject(new Error(`RPC call ${method} timed out after ${timeoutMs}ms`)); | ||
| }, timeoutMs) | ||
| : undefined; | ||
|
|
||
| this.pending.set(id, { | ||
| resolve: resolve as (value: unknown) => void, | ||
| reject, | ||
| timer, | ||
| }); | ||
|
|
||
| const req: RPCRequest = { id, method, params }; | ||
| this.proc.stdin!.write(JSON.stringify(req) + '\n'); | ||
| }); |
.github/workflows/npm-publish.yml
Outdated
| "name": "@mmonterroca/docxgo-${{ matrix.suffix }}", | ||
| "version": "${{ steps.version.outputs.version }}", | ||
| "description": "docxgo binary for ${{ matrix.suffix }}", | ||
| "os": ["${{ matrix.goos }}"], |
cmd/docxgo/main.go
Outdated
| "arch": runtime.GOARCH, | ||
| "features": capabilitiesMap(), | ||
| } | ||
| out, _ := json.MarshalIndent(info, "", " ") |
| func (s *server) handleTemplateInspect(req *Request) Response { | ||
| const op = "template.inspect" | ||
|
|
||
| var params templateInspectParams | ||
| raw := req.Params | ||
| if len(raw) == 0 || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) { | ||
| raw = []byte("{}") | ||
| } | ||
| if err := json.Unmarshal(raw, ¶ms); err != nil { | ||
| return errorResponse(req.ID, errors.ErrCodeValidation, "invalid params: "+err.Error(), op) | ||
| } | ||
|
|
||
| doc, ok := s.getDoc(params.DocumentID) | ||
| if !ok { | ||
| return errorResponse(req.ID, errors.ErrCodeNotFound, | ||
| fmt.Sprintf("document %q not found", params.DocumentID), op) | ||
| } | ||
|
|
||
| placeholders := template.FindPlaceholders(doc) | ||
|
|
- errcheck: add explicit _ = for unchecked returns in consolidate.go (12) and merge.go (1) - errcheck: wrap deferred os.Remove in anonymous func in handlers.go - staticcheck: convert if/else chain to switch on Orientation in handlers.go - unparam: change runRPC() from int to void since it always returned 0 - unused: remove unused cleanup function and stale imports in example 15
There was a problem hiding this comment.
Code Review
This pull request is a major feature addition, introducing a JSON-RPC wrapper for the docxgo library and a comprehensive Node.js integration package. The changes include new CLI commands, numerous RPC handlers in Go for document manipulation, templating, and batch operations, along with extensive test coverage. The Node.js package provides a well-designed, typed, and fluent API for interacting with the Go binary. The documentation has also been updated thoroughly. The overall quality of the implementation is very high. I have one suggestion to improve the API design of the document.applyPatch method for better clarity and to prevent potential misuse.
| case "setMetadata": | ||
| var mp setMetadataParams | ||
| if err := json.Unmarshal(raw, &mp); err != nil { | ||
| return errorResponseWithData(req.ID, errors.ErrCodeValidation, | ||
| fmt.Sprintf("invalid setMetadata at index %d: %v", i, err), op, | ||
| map[string]interface{}{"index": i}) | ||
| } | ||
| meta := &domain.Metadata{ | ||
| Title: mp.Title, | ||
| Subject: mp.Subject, | ||
| Creator: mp.Creator, | ||
| Description: mp.Description, | ||
| Keywords: mp.Keywords, | ||
| Created: mp.Created, | ||
| Modified: mp.Modified, | ||
| } | ||
| if err := doc.SetMetadata(meta); err != nil { | ||
| return errorResponseWithData(req.ID, errors.ErrCodeValidation, | ||
| fmt.Sprintf("failed to set metadata at index %d: %v", i, err), op, | ||
| map[string]interface{}{"index": i}) | ||
| } |
There was a problem hiding this comment.
In the handleApplyPatch function, the setMetadata operation reuses the setMetadataParams struct. This struct includes a DocumentID field, which is redundant and potentially confusing within a patch operation, as the document ID is already provided at the applyPatch level. The DocumentID from the patch operation is parsed but ignored, which could mislead API users.
To improve API clarity and prevent misuse, I suggest defining a specific struct for the setMetadata patch operation that omits the DocumentID, similar to how the setBackgroundColor operation is handled using a local anonymous struct. This would make the API contract more explicit.
case "setMetadata":
var mp struct {
Title string `json:"title,omitempty"`
Subject string `json:"subject,omitempty"`
Creator string `json:"creator,omitempty"`
Description string `json:"description,omitempty"`
Keywords []string `json:"keywords,omitempty"`
Created string `json:"created,omitempty"`
Modified string `json:"modified,omitempty"`
}
if err := json.Unmarshal(raw, &mp); err != nil {
return errorResponseWithData(req.ID, errors.ErrCodeValidation,
fmt.Sprintf("invalid setMetadata at index %d: %v", i, err), op,
map[string]interface{}{"index": i})
}
meta := &domain.Metadata{
Title: mp.Title,
Subject: mp.Subject,
Creator: mp.Creator,
Description: mp.Description,
Keywords: mp.Keywords,
Created: mp.Created,
Modified: mp.Modified,
}
if err := doc.SetMetadata(meta); err != nil {
return errorResponseWithData(req.ID, errors.ErrCodeValidation,
fmt.Sprintf("failed to set metadata at index %d: %v", i, err), op,
map[string]interface{}{"index": i})
}- Fix atomicity claim: docs and docstrings now describe sequential fail-fast behavior with no rollback instead of atomic guarantees - Fix template.inspect: now honors openDelimiter/closeDelimiter params via FindPlaceholdersCustom + exported BuildPattern - Fix setMetadata in applyPatch: use anonymous struct without redundant DocumentID - Fix main.go: handle json.MarshalIndent error instead of discarding it - Fix rpc.ts: wrap stdin.write in try/catch to prevent pending map leaks - Fix npm-publish.yml: map Go 'windows' to npm 'win32' for os field - Fix npm test script: run source tests via tsx instead of compiled dist
The reader extracted CoreProperties bytes from the zip but never populated the document's Metadata struct during reconstruction. This meant that metadata (title, creator, subject, etc.) set before saving was lost when the document was re-opened. - Add parseCoreProperties() in reconstruct.go with namespace-aware XML struct (Go's encoding/xml resolves prefixes to URIs during decode) - Call doc.SetMetadata() during ReconstructDocument after preserveOriginalParts - Add TestMetadataRoundTrip test verifying write → reopen → read cycle
There was a problem hiding this comment.
Pull request overview
This PR implements Issue #19 by adding a JSON-RPC CLI layer to docxgo, publishing a TypeScript/Node.js wrapper (npm/), and wiring up CI workflows for multi-platform binary releases and npm publishing.
Changes:
- Added/expanded Go CLI JSON-RPC protocol, handlers, and tests (including new namespaces like
system.*, mutation methods, template methods, and batch requests). - Introduced a TypeScript Node.js client package with exec + persistent RPC + fluent builder APIs, plus protocol typings and tests.
- Added CI/CD workflows for Go binary releases, npm publishing (including platform packages), and Node test execution.
Reviewed changes
Copilot reviewed 28 out of 34 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| pkg/template/merge.go | Exposes pattern builder and adjusts run text-setting during template merge. |
| pkg/template/consolidate.go | Updates run consolidation to use error-returning run setters. |
| npm/tsconfig.json | Adds TypeScript compiler configuration for the Node wrapper package. |
| npm/src/types.ts | Defines the JSON-RPC protocol + document/content/template/patch TypeScript types. |
| npm/src/rpc.ts | Implements persistent NDJSON JSON-RPC client and error type for Node. |
| npm/src/index.ts | Exports Node wrapper public API (clients, builder, binary resolver, types). |
| npm/src/exec.ts | Implements one-shot synchronous exec-based JSON-RPC client. |
| npm/src/builder.ts | Adds fluent builder API atop the persistent RPC client. |
| npm/src/binary.ts | Implements cross-platform binary resolution via optionalDependencies/env/PATH. |
| npm/src/tests/docxgo.test.ts | Adds Node integration tests covering exec/rpc/builder/template/batch/patch flows. |
| npm/scripts/build-esm.js | Generates an ESM wrapper entrypoint from CJS build output. |
| npm/platforms/win32-x64/.gitkeep | Placeholder for platform package directory. |
| npm/platforms/linux-x64/.gitkeep | Placeholder for platform package directory. |
| npm/platforms/linux-arm64/.gitkeep | Placeholder for platform package directory. |
| npm/platforms/darwin-x64/.gitkeep | Placeholder for platform package directory. |
| npm/platforms/darwin-arm64/.gitkeep | Placeholder for platform package directory. |
| npm/platforms/README.md | Documents how platform-specific npm binary packages work. |
| npm/platforms/.gitignore | Ignores platform-package build artifacts (binaries, generated package.json). |
| npm/package.json | Defines @mmonterroca/docxgo package metadata, exports, scripts, optionalDependencies. |
| npm/package-lock.json | Locks Node dev dependency tree for the npm package. |
| npm/README.md | Adds Node wrapper documentation, examples, and API reference. |
| npm/.gitignore | Ignores npm build/test artifacts (node_modules, dist, bin, tarballs). |
| internal/reader/reconstruct.go | Hydrates document metadata from core properties to preserve round-trip metadata. |
| examples/15_external_template/main.go | Removes output cleanup logic from the external template example. |
| docx_read_test.go | Adds a Go test validating metadata round-trip behavior. |
| docs/CLI_GUIDE.md | Expands CLI protocol documentation for new RPC methods and enriched errors. |
| cmd/docxgo/rpc.go | Adjusts RPC runner signature/exit behavior for persistent mode. |
| cmd/docxgo/protocol.go | Adds protocol version constant and enriched error data support. |
| cmd/docxgo/main.go | Extends CLI version command to support --json + capabilities output. |
| cmd/docxgo/handlers_test.go | Adds extensive handler tests for mutation/template/batch/applyPatch/system namespaces. |
| cmd/docxgo/handlers.go | Implements new RPC handlers for system, mutation, template, and patch operations. |
| .github/workflows/release.yml | Adds multi-platform Go binary build + GitHub Release publishing workflow. |
| .github/workflows/npm-publish.yml | Adds release-triggered npm publish workflow for platform packages + main package. |
| .github/workflows/ci.yml | Updates branch filters and adds a Node.js test job building the Go CLI binary. |
Files not reviewed (1)
- npm/package-lock.json: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
| stdout = execFileSync(this.binPath, ['exec', '--request', requestJSON], { | ||
| timeout: timeout ?? this.defaultTimeout, | ||
| encoding: 'utf-8', | ||
| stdio: ['pipe', 'pipe', 'pipe'], | ||
| }); |
| ## Development | ||
|
|
||
| ```bash | ||
| # Install dependencies | ||
| cd npm && npm install | ||
|
|
||
| # Run tests (TypeScript, no build needed) | ||
| npm run test:src | ||
|
|
||
| # Build (CJS + ESM) | ||
| npm run build | ||
|
|
||
| # Type-check | ||
| npm run lint |
| // Validate first | ||
| validationErrors := template.ValidateTemplate(doc, template.MergeData(params.Data), opts) | ||
| var warnings []map[string]interface{} | ||
| for _, ve := range validationErrors { | ||
| sev := "error" | ||
| if ve.Severity == template.SeverityWarning { | ||
| sev = "warning" | ||
| } | ||
| warnings = append(warnings, map[string]interface{}{ | ||
| "severity": sev, | ||
| "key": ve.Key, | ||
| "message": ve.Message, | ||
| }) |
| if newText != text { | ||
| run.SetText(newText) | ||
| _ = run.SetText(newText) | ||
| // Clear fields on this run if it has any. | ||
| if len(run.Fields()) > 0 { | ||
| run.ClearFields() |
| _ = r.SetText(m.text) | ||
| _ = r.SetFont(m.src.Font()) | ||
| _ = r.SetColor(m.src.Color()) | ||
| _ = r.SetSize(m.src.Size()) | ||
| _ = r.SetBold(m.src.Bold()) | ||
| _ = r.SetItalic(m.src.Italic()) | ||
| _ = r.SetUnderline(m.src.Underline()) | ||
| _ = r.SetStrike(m.src.Strike()) | ||
| _ = r.SetHighlight(m.src.Highlight()) |
internal/reader/reconstruct.go
Outdated
| // Hydrate metadata from docProps/core.xml so it survives round-trip. | ||
| if parsed.Package != nil && len(parsed.Package.CoreProperties) > 0 { | ||
| if meta, err := parseCoreProperties(parsed.Package.CoreProperties); err == nil && meta != nil { | ||
| _ = doc.SetMetadata(meta) |
internal/reader/reconstruct.go
Outdated
| meta.Keywords = strings.Split(cp.Keywords, ",") | ||
| for i := range meta.Keywords { | ||
| meta.Keywords[i] = strings.TrimSpace(meta.Keywords[i]) |
The paragraphItem struct only had a 'runs' array, so passing
{"text": "Hello"} was silently ignored — no error but no text
rendered in the document either.
Add a 'text' shortcut field that auto-creates a single run when
no explicit runs are provided. This makes the simple case ergonomic
while preserving full runs array support for formatted text.
- 01_basic: headings, formatted text, multi-run paragraphs
- 02_tables_and_sections: tables, page breaks, landscape sections
- 03_read_modify: open → inspect → add content → save workflow
- 04_template: mail merge with {{placeholders}}, inspect, render
- 05_patch_and_system: applyPatch, batch, metadata, system info
All 5 examples validated: 8 .docx files generated, all valid
Microsoft Word 2007+ format. Run with: bash npm/examples/run_all.sh
- exec.ts: set maxBuffer to 50MB to prevent crash on large documents - README.md: fix test:src reference to npm test - handlers.go: downgrade validation severity to warning/info in non-strict template.render (avoid mixing error severity with ok:true responses) - merge.go: propagate run.SetText error instead of ignoring it - reconstruct.go: propagate SetMetadata error, filter empty keywords
- Add text shortcut to AppendParagraphOp type - Add errors array to ValidateResult type - Change TemplateWarning severity to warning|info (matches handler) - Use optional chaining for info.metadata in examples
Summary
CLI JSON-RPC wrapper + Node.js integration for docxgo, implementing all sections of Issue #19.
Enables document creation and manipulation from Node.js (and any language) via a JSON-RPC binary that communicates over stdin/stdout — zero config, zero ports, zero auth.
What's Included
CLI Binary (
cmd/docxgo/)exec(single request) andrpc(persistent session)system.{ping,version,capabilities,batch},document.{create,open,save,validate,inspect,setMetadata,setBackgroundColor,addContent,addPageBreak,applyPatch,close},paragraph.{add,list},table.{add,list},section.add,template.{inspect,render}Node.js Wrapper (
npm/)@mmonterroca/docxgo— TypeScript CJS + ESM packageDocxgoExec(one-shot),DocxgoRPC(low-level persistent),DocumentBuilder(high-level fluent)CI/CD (
.github/workflows/)release.yml: Multi-platform Go binary builds (darwin/linux/windows × arm64/x64) + GitHub Releasenpm-publish.yml: Platform-specific npm packages with OIDC provenanceci.yml: Updated with npm-test jobDocumentation
docs/CLI_GUIDE.md: Full reference for all 22 methods with examplesnpm/README.md: Installation, quick start, API referenceReader Fix
docProps/core.xmlduring document open, so metadata (title, creator, subject, etc.) survives save → reopen cyclesCommits (9)
5d2bfc9ae75fb484cb54e91de4cd79f33a460225ca778d76055c28d9eab4589Review Feedback Addressed
All 9 code review comments resolved:
document.applyPatchdocstemplate.inspectto honor custom delimiterssetMetadatain applyPatch to use anonymous struct (no redundant DocumentID)rpc.tsstdin.write error handling (try/catch + pending cleanup)npm-publish.ymlOS mapping (windows→win32)main.gojson.MarshalIndent error handlingTest Results
-race)tsc --noEmitcleanCloses #19