@omega-edit/client
v2.0.0
Published
OmegaEdit gRPC Client
Readme
TypeScript/Node.js client for Ωedit™ - a library for building editors that can handle massive files with multiple viewports, full undo/redo, and byte-level precision.
Batteries included - this package bundles the native gRPC server via its dependency on
@omega-edit/server. No separate server install is needed.
Install
npm install @omega-edit/client
# or
yarn add @omega-edit/clientQuick Start
import {
startServer,
getClient,
createSession,
insert,
saveSession,
destroySession,
stopServerGraceful,
IOFlags,
} from '@omega-edit/client'
// 1. Start the bundled gRPC server
const pid = await startServer(9000)
// 2. Connect a client
const client = await getClient(9000)
// 3. Create a session (optionally backed by an existing file)
const sessionResp = await createSession()
const sessionId = sessionResp.getSessionId()
// 4. Make edits
await insert(sessionId, 0, new TextEncoder().encode('Hello, Ωedit™!'))
// 5. Save to disk
await saveSession(sessionId, '/tmp/hello.txt', IOFlags.OVERWRITE)
// 6. Clean up
await destroySession(sessionId)
await stopServerGraceful()API Overview
Server Lifecycle
| Function | Description |
| ------------------------------------------------- | ----------------------------------------- |
| startServer(port?, host?, pidFile?, heartbeat?) | Start the bundled native gRPC server |
| startServerUnixSocket(socketPath, ...) | Start using a Unix domain socket |
| stopServerGraceful() | Graceful shutdown, returning status info |
| stopServerImmediate() | Immediate shutdown, returning status info |
| getServerInfo() | Runtime metadata for the native server |
| getServerHeartbeat(sessions) | Heartbeat and process health |
| startServerHeartbeatLoop(options) | Managed liveness heartbeat loop |
startServer(..., heartbeat) and startServerUnixSocket(..., heartbeat) accept the same HeartbeatOptions bag exported from @omega-edit/server, including native logging fields such as logFile, logLevel, and logConfigFile.
Shutdown migration note:
- In the 2.x line,
stopServerGraceful()andstopServerImmediate()return a structured result object withresponseCode,serverProcessId, andstatusinstead of returning only a numeric response code. - In the 2.x line,
@omega-edit/clienttreats allint64-backed public values as JavaScript safe integers. Unsafe inputs are rejected before RPCs are sent, and unsafe outputs are rejected instead of being silently rounded. - In the 2.x line, autogenerated IDs are opaque. New sessions get
sess_<uuidv7>IDs, autogenerated viewports get<sessionId>:vp_<uuidv7>IDs, and applications should treat both as server-issued handles rather than decoding semantics from the string value. - In the 2.x line,
ChangeKindonly exposesUNSPECIFIED,DELETE,INSERT, andOVERWRITE. The oldCHANGE_*andCHANGE_KIND_*aliases were removed. - In the 2.x line, top-level enum exports use one short-name style only. Use names like
IOFlags.OVERWRITE,CountKind.VIEWPORTS,ServerControlKind.GRACEFUL_SHUTDOWN,ServerControlStatus.COMPLETED,SessionEventKind.EDIT, andViewportEventKind.EDIT; the older prefixed aliases were removed.
Server Health API Migration
getServerInfo() and getServerHeartbeat() now expose native-runtime metadata instead of JVM-shaped placeholders.
The client-facing TypeScript API is renamed, while the protobuf layer keeps the
legacy fields for wire compatibility.
Design rule:
- Heartbeat is the only expected polling loop in a healthy OmegaEdit client.
- Use
SubscribeToSessionEventsandSubscribeToViewportEventsto keep UI state, computed file size, and viewport contents current. - If you are polling session or viewport state in steady-state operation, you are working around the design incorrectly and should fix the integration to consume subscriptions instead.
Current getServerInfo() fields:
serverHostnameserverProcessIdserverVersionruntimeKindruntimeNameplatformavailableProcessorscompilerbuildTypecppStandard
Current getServerHeartbeat() fields:
latencysessionCountserverTimestampserverUptimeserverCpuCountserverCpuLoadAverage?serverResidentMemoryBytes?serverVirtualMemoryBytes?serverPeakResidentMemoryBytes?
startServerHeartbeatLoop() builds the sanctioned recurring heartbeat pattern on top of getServerHeartbeat(sessionIds): it starts immediately by default, skips overlapping heartbeats, and routes results and errors through callbacks so integrations do not need to hand-roll their own timer logic.
Migration notes:
getServerHeartbeat()is now strictlygetServerHeartbeat(sessionIds). The old extra hostname / PID / interval-style arguments are removed in 2.x.jvmVersion,jvmVendor, andjvmPathwere removed from the client-facing TypeScript API. UseruntimeKind,runtimeName,platform, andcompilerinstead.serverMaxMemory,serverCommittedMemory, andserverUsedMemorywere removed from the client-facing TypeScript API. They were JVM-heap concepts and are now replaced with process-memory metrics.- Optional heartbeat fields may be
undefinedwhen the host platform cannot report them. Treat missing values as "unavailable", not zero. serverVirtualMemoryBytesis intentionally best-effort and may be omitted on platforms where the available process metric is not semantically comparable.
Client Connection
| Function | Description |
| --------------------------------- | ----------------------------------------------- |
| getClient(port?, host?) | Get or create a gRPC client connection |
| waitForReady(client, deadline?) | Block until the server is accepting connections |
| resetClient() | Tear down the current client connection |
Sessions
| Function | Description |
| ------------------------------------------------------------------------- | ------------------------------ |
| createSession(filePath?, sessionId?, checkpointDir?) | Open an editing session |
| createSessionFromBytes(data, sessionId?, checkpointDir?) | Open a session from bytes |
| destroySession(sessionId) | Close and discard a session |
| saveSession(sessionId, path, flags?, offset?, length?) | Save session content to a file |
| getComputedFileSize(sessionId) | Logical file size after edits |
| getSegment(sessionId, offset, length) | Read a byte range |
| getSessionBytes(sessionId, offset?, length?) | Read session bytes in memory |
| getSessionCount() | Number of active sessions |
| pauseSessionChanges(sessionId) / resumeSessionChanges(sessionId) | Pause/resume change tracking |
| runSessionTransaction(sessionId, work) | Run scoped atomic edit work |
| beginSessionTransaction(sessionId) / endSessionTransaction(sessionId) | Group edits atomically |
Subscription-first guidance:
getComputedFileSize(sessionId)is a direct snapshot RPC, not a steady-state synchronization mechanism.- Long-lived clients should treat
subscribeSessionEvents(...)as the authoritative source for computed file size and other session-derived state. - Long-lived viewport consumers should treat
subscribeViewportEvents(...)as the authoritative source for viewport invalidation and refresh timing. - Prefer
runSessionTransaction(...)over hand-managedbeginSessionTransaction(...)/endSessionTransaction(...)pairs so transaction scope is enforced in one function. - Outside of heartbeat, polling is a design smell. If a consumer needs repeated polling to stay correct, the right fix is to expose or consume the relevant event stream.
Subscriptions
| Function | Description |
| ----------------------------------------------------------------------- | ----------------------------- |
| subscribeSessionEvents({ sessionId, interest?, onEvent, onError? }) | Managed session event stream |
| subscribeViewportEvents({ viewportId, interest?, onEvent, onError? }) | Managed viewport event stream |
These helpers wrap the raw server-stream subscriptions and own the repetitive parts of integration:
- building the subscription request
- attaching
dataanderrorhandlers - ignoring expected cancel/reset shutdown noise
- routing callback failures through
onError - exposing a simple
cancel()handle for teardown
Use them instead of wiring raw gRPC streams by hand in application code.
Editor Integration Helpers
These higher-level helpers sit on top of the session, viewport, and subscription primitives for editor-style applications:
| Helper | Description |
| --------------------------- | ---------------------------------------------------------------------------- |
| EditorHistoryController | Tracks local vs checkpoint-backed undo/redo and save-state semantics |
| EditorSearchController | Owns bounded-vs-large search mode and routes replace-all strategies |
| EditorSessionModel | Tracks computed file size, change count, viewport identity, and sync waiters |
| ScopedEditorSessionHandle | Opens a session, owns viewport lifecycle and subscriptions, and cleans up |
These are especially useful for front-ends such as VS Code extensions that want to stay subscription-first without rebuilding the same session/search/history bookkeeping in each integration.
Editing
| Function | Description |
| ------------------------------------------------------- | --------------------------------- |
| insert(sessionId, offset, data) | Insert bytes at an offset |
| del(sessionId, offset, length) | Delete a byte range |
| overwrite(sessionId, offset, data) | Overwrite bytes at an offset |
| replace(sessionId, offset, removeLen, replacement) | Remove + insert in one operation |
| undo(sessionId) / redo(sessionId) | Unlimited undo/redo |
| clear(sessionId) | Undo all changes |
| getLastChange(sessionId) | Details of the most recent change |
| getChangeCount(sessionId) / getUndoCount(sessionId) | Change and undo stack depth |
Viewports
| Function | Description |
| ----------------------------------------------------------------------- | -------------------------------------------- |
| createViewport(viewportId?, sessionId, offset, capacity, isFloating?) | Create a window into the data |
| modifyViewport(viewportId, offset, capacity, isFloating?) | Move or resize a viewport |
| destroyViewport(viewportId) | Remove a viewport |
| getViewportData(viewportId) | Read the current viewport content |
| viewportHasChanges(viewportId) | Check if content has changed since last read |
| getViewportCount(sessionId) | Number of active viewports |
Search & Profile
searchSession(...)- forward and reverse byte-pattern searchreplaceSession(...)- search-and-replace across the sessionprofileSession(...)- byte-frequency profiling and line-ending detection
Logging
import { createSimpleFileLogger, setLogger } from '@omega-edit/client'
setLogger(createSimpleFileLogger('/tmp/omega-edit.log', 'debug'))Package Format
Distributed as both ESM and CommonJS with full TypeScript source maps and declaration files. Internally, the package now uses protobuf-ts for native ESM-friendly generated bindings instead of the old jspb runtime bridge.
| Output | Path | Format |
| ----------- | ----------------- | --------------------------------- |
| ESM | dist/esm/ | ES2020 module syntax (ES6 target) |
| CJS | dist/cjs/ | CommonJS |
| Types | dist/esm/*.d.ts | TypeScript declarations |
| Source Maps | dist/**/*.map | Embedded TypeScript sources |
Environment Variables
| Variable | Default | Description |
| ----------------------------- | ----------- | --------------------- |
| OMEGA_EDIT_SERVER_HOST | 127.0.0.1 | Server bind address |
| OMEGA_EDIT_SERVER_PORT | 9000 | Server port |
| OMEGA_EDIT_SERVER_LOG_CONFIG | - | Native server logback-style XML config |
| OMEGA_EDIT_SERVER_LOG_FILE | - | Native server log file |
| OMEGA_EDIT_SERVER_LOG_LEVEL | - | Native server log level |
| OMEGA_EDIT_CLIENT_LOG_LEVEL | - | Client-side log level |
Examples
See the examples/typescript/ directory for runnable TypeScript examples covering editing, search/replace, viewports, data profiling, and record/replay.
Development
See DEVELOPMENT.md for build, test, and contribution instructions.
Documentation
Full documentation is published at https://ctc-oss.github.io/omega-edit/.
Versioning
Ωedit™ follows Semantic Versioning.
License
Apache 2.0 - see LICENSE.txt.
