@ignex/nova 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -1
- package/docs/ai/TREE.md +69 -9
- package/docs/architecture.md +75 -27
- package/docs/events.md +83 -1
- package/docs/generic-bindings.md +10 -0
- package/docs/wire-format.md +65 -18
- package/package.json +2 -1
- package/prebuilds/linux-x64/libignex_ffi.so +0 -0
- package/public/generate.ts +97 -3
- package/public/server.ts +10 -0
- package/rust/src/generated/backend.rs +503 -0
- package/rust/src/transcode/generated.rs +376 -17
- package/src/bridge/nats/inbound.ts +46 -0
- package/src/bridge/nats/index.ts +131 -0
- package/src/bridge/nats/real-transport.ts +133 -0
- package/src/bridge/nats/types.ts +80 -0
- package/src/codegen/constants.ts +14 -4
- package/src/codegen/direct-gen.ts +20 -6
- package/src/codegen/registry-gen.ts +10 -6
- package/src/codegen/rust-glue-gen.ts +10 -3
- package/src/codegen/schema-model.ts +28 -3
- package/src/codegen/ts-ser-gen.ts +12 -3
- package/src/core/auth.ts +65 -4
- package/src/core/client-rpc.ts +75 -0
- package/src/core/client-state.ts +42 -0
- package/src/core/client-wire.ts +142 -8
- package/src/core/client.ts +72 -3
- package/src/core/groups.ts +5 -0
- package/src/core/metrics.ts +38 -21
- package/src/core/outbound.ts +50 -6
- package/src/core/rate-limit.ts +69 -0
- package/src/core/replay.ts +41 -1
- package/src/core/resume.ts +181 -0
- package/src/core/rooms.ts +10 -3
- package/src/core/routing.ts +128 -5
- package/src/core/server/client-info.ts +37 -0
- package/src/core/server/http-routes.ts +59 -0
- package/src/core/{server.ts → server/index.ts} +112 -120
- package/src/core/server/metrics-view.ts +53 -0
- package/src/core/server/socket-lifecycle.ts +57 -0
- package/src/core/state.ts +73 -1
- package/src/core/topic-log.ts +86 -0
- package/src/events/clients.ts +18 -0
- package/src/events/cluster/dedupe.ts +43 -0
- package/src/events/cluster/envelope.ts +149 -0
- package/src/events/cluster/index.ts +50 -0
- package/src/events/cluster/keys.ts +33 -0
- package/src/events/cluster/kinds.ts +32 -0
- package/src/events/cluster/presence-table.ts +99 -0
- package/src/events/cluster/presence.ts +53 -0
- package/src/events/cluster/redis-client.ts +50 -0
- package/src/events/cluster/store-memory.ts +67 -0
- package/src/events/cluster/store-redis.ts +44 -0
- package/src/events/cluster/subjects.ts +30 -0
- package/src/events/cluster/sync.ts +476 -0
- package/src/events/cluster/transport-nats.ts +24 -0
- package/src/events/cluster/transport-redis.ts +120 -0
- package/src/events/cluster-rpc.ts +196 -0
- package/src/events/delivery.ts +83 -0
- package/src/events/emit.ts +57 -11
- package/src/events/hub/context-factory.ts +79 -0
- package/src/events/hub/dispatch.ts +86 -0
- package/src/events/hub/index.ts +536 -0
- package/src/events/hub/internal.ts +31 -0
- package/src/events/hub/metrics-snapshot.ts +84 -0
- package/src/events/hub/resolve-cluster.ts +49 -0
- package/src/events/queue.ts +36 -9
- package/src/events/registry.ts +90 -54
- package/src/events/schedule.ts +73 -0
- package/src/events/trace.ts +283 -0
- package/src/events/types/client.ts +68 -0
- package/src/events/types/cluster.ts +40 -0
- package/src/events/types/context.ts +50 -0
- package/src/events/types/emit-target.ts +29 -0
- package/src/events/types/groups.ts +35 -0
- package/src/events/types/hub.ts +124 -0
- package/src/events/types/index.ts +30 -0
- package/src/events/types/metrics.ts +52 -0
- package/src/events/types/options.ts +62 -0
- package/src/generated/direct-ser.ts +146 -59
- package/src/generated/fbs/backend.fbs +23 -0
- package/src/generated/registry.ts +92 -33
- package/src/generated/rust/backend_generated.rs +503 -0
- package/src/generated/ts/backend.ts +4 -0
- package/src/generated/ts/resume.ts +74 -0
- package/src/generated/ts/resumed.ts +88 -0
- package/src/generated/ts/rpc-call.ts +112 -0
- package/src/generated/ts/rpc-result.ts +126 -0
- package/src/generated/ts/snapshot-request.ts +19 -5
- package/src/generated/ts-ser.ts +109 -16
- package/src/generated/wire-registry.json +7 -3
- package/src/schema/index.ts +45 -1
- package/src/transport/transport.ts +117 -77
- package/src/bridge/nats.ts +0 -309
- package/src/events/cluster.ts +0 -732
- package/src/events/hub.ts +0 -481
- package/src/events/types.ts +0 -378
- package/src/transport/stats.ts +0 -48
package/public/generate.ts
CHANGED
|
@@ -95,6 +95,32 @@ export interface GenerateOptions {
|
|
|
95
95
|
wireVersion?: number;
|
|
96
96
|
/** overwrite `outDir` if it exists, default true. */
|
|
97
97
|
force?: boolean;
|
|
98
|
+
/**
|
|
99
|
+
* Emit a typed FRONTEND client (`<outDir>/client.gen.ts`): the assembled
|
|
100
|
+
* app bindings + a `createRealtimeClient(url, options?)` factory typed
|
|
101
|
+
* against YOUR events (the realtime analog of the generated OpenAPI SDK).
|
|
102
|
+
*
|
|
103
|
+
* ```ts
|
|
104
|
+
* // generated client.gen.ts
|
|
105
|
+
* import { createRealtimeClient } from "./generated/client.gen";
|
|
106
|
+
* const client = createRealtimeClient("ws://host/ws", { reconnect: true });
|
|
107
|
+
* client.on("chat.message", (m) => /* m: ChatMessagePayload *\/);
|
|
108
|
+
* client.send("chat.send", { orderId, body, clientTs });
|
|
109
|
+
* ```
|
|
110
|
+
*
|
|
111
|
+
* `schemaImport` is required: the emitted client assembles bindings from
|
|
112
|
+
* YOUR TypeBox registry (`makeBindings(schema)`), so it must point at the
|
|
113
|
+
* same `{ schemas, events, controlEvents }` object passed here (relative
|
|
114
|
+
* to the emitted file's location in `outDir`).
|
|
115
|
+
*/
|
|
116
|
+
client?: {
|
|
117
|
+
/** specifier for the generated wire stack (`makeBindings`), default "./index". */
|
|
118
|
+
wireImport?: string;
|
|
119
|
+
/** specifier for YOUR TypeBox registry, relative to the emitted file. Required. */
|
|
120
|
+
schemaImport: string;
|
|
121
|
+
/** emitted filename inside `outDir`, default "client.gen.ts". */
|
|
122
|
+
outFile?: string;
|
|
123
|
+
};
|
|
98
124
|
}
|
|
99
125
|
|
|
100
126
|
export interface GeneratedBindings {
|
|
@@ -159,12 +185,62 @@ codegen-units = 1
|
|
|
159
185
|
return { lib, ffi, cargo };
|
|
160
186
|
}
|
|
161
187
|
|
|
188
|
+
/**
|
|
189
|
+
* Emit the typed FRONTEND client module (see `GenerateOptions.client`).
|
|
190
|
+
* The client assembles the app bindings from YOUR schema and exposes a
|
|
191
|
+
* typed `createRealtimeClient` factory — `on`/`send` are statically checked
|
|
192
|
+
* against the app's event names, so no `as never` casts are needed on the FE.
|
|
193
|
+
*/
|
|
194
|
+
function emitClientModel(
|
|
195
|
+
model: Model,
|
|
196
|
+
wireImport: string,
|
|
197
|
+
schemaImport: string,
|
|
198
|
+
libraryImport: string,
|
|
199
|
+
): string {
|
|
200
|
+
const eventNames = model.events
|
|
201
|
+
.filter((e) => !e.control)
|
|
202
|
+
.map((e) => `"${e.name}"`)
|
|
203
|
+
.join(" | ");
|
|
204
|
+
const lines: string[] = [];
|
|
205
|
+
lines.push("// @generated by ignex-nova generate — DO NOT EDIT");
|
|
206
|
+
lines.push("// Typed frontend realtime client (the realtime analog of the generated OpenAPI SDK).");
|
|
207
|
+
lines.push(`import { createClient } from ${JSON.stringify(libraryImport.replace(/\/bindings$/, "/client"))};`);
|
|
208
|
+
lines.push(`import type { IgnClient, IgnClientOptions } from ${JSON.stringify(libraryImport.replace(/\/bindings$/, "/client"))};`);
|
|
209
|
+
lines.push(`import { makeBindings } from ${JSON.stringify(wireImport)};`);
|
|
210
|
+
lines.push(`import * as schema from ${JSON.stringify(schemaImport)};`);
|
|
211
|
+
lines.push("");
|
|
212
|
+
lines.push("/** The app's assembled runtime bindings (typed against your events). */");
|
|
213
|
+
lines.push("export const bindings = makeBindings(schema);");
|
|
214
|
+
lines.push("export type RealtimeBindings = typeof bindings;");
|
|
215
|
+
lines.push("export type RealtimeClient = IgnClient<RealtimeBindings>;");
|
|
216
|
+
lines.push("");
|
|
217
|
+
lines.push("/** Event names your client can send/observe (derived from the schema). */");
|
|
218
|
+
lines.push(`export type RealtimeEventName = ${eventNames || "never"};`);
|
|
219
|
+
lines.push("");
|
|
220
|
+
lines.push("/**");
|
|
221
|
+
lines.push(" * Create a typed realtime client. `on`/`send` are checked against");
|
|
222
|
+
lines.push(" * RealtimeEventName — payload types flow from the generated registry.");
|
|
223
|
+
lines.push(" */");
|
|
224
|
+
lines.push("export const createRealtimeClient = (");
|
|
225
|
+
lines.push(" url: string,");
|
|
226
|
+
lines.push(' options?: Omit<IgnClientOptions<RealtimeBindings>, "bindings">,');
|
|
227
|
+
lines.push("): RealtimeClient => createClient(url, { ...options, bindings });");
|
|
228
|
+
lines.push("");
|
|
229
|
+
return lines.join("\n");
|
|
230
|
+
}
|
|
231
|
+
|
|
162
232
|
export function generateBindings(
|
|
163
233
|
schema: SchemaRegistry,
|
|
164
234
|
options: GenerateOptions = {},
|
|
165
235
|
): GeneratedBindings {
|
|
166
236
|
const wireVersion = options.wireVersion ?? WIRE_VERSION;
|
|
167
|
-
|
|
237
|
+
// Default: the FFI-free entry points. The package barrel ("@ignex/nova")
|
|
238
|
+
// pulls in bun:ffi at import time, which breaks vitest/node/browser module
|
|
239
|
+
// runners consuming generated bindings. Custom libraryImport values (e.g. a
|
|
240
|
+
// relative path to the repo in tests) keep the legacy single-import behavior.
|
|
241
|
+
const isDefaultLib = options.libraryImport === undefined || options.libraryImport === "@ignex/nova";
|
|
242
|
+
const libraryImport = options.libraryImport ?? "@ignex/nova/bindings";
|
|
243
|
+
const helperImport = isDefaultLib ? "@ignex/nova/internal" : libraryImport;
|
|
168
244
|
|
|
169
245
|
// The standard transport control protocol is always present; users may add
|
|
170
246
|
// custom control events but never override the standard ones.
|
|
@@ -246,8 +322,16 @@ export function generateBindings(
|
|
|
246
322
|
}
|
|
247
323
|
|
|
248
324
|
// Generated TS stack (user mode: self-contained local types + library imports)
|
|
249
|
-
|
|
250
|
-
|
|
325
|
+
// registry + direct-ser need the runtime HELPERS (@ignex/nova/internal in the
|
|
326
|
+
// default case); index.ts needs assembleBindings (@ignex/nova/bindings).
|
|
327
|
+
files["registry.ts"] = emitRegistry(model, fingerprint, {
|
|
328
|
+
schemaImport: null,
|
|
329
|
+
libraryImport: helperImport,
|
|
330
|
+
});
|
|
331
|
+
files["direct-ser.ts"] = emitDirectSer(model, {
|
|
332
|
+
schemaImport: null,
|
|
333
|
+
libraryImport: helperImport,
|
|
334
|
+
});
|
|
251
335
|
files["ts-ser.ts"] = emitTsSer(model, { schemaImport: null });
|
|
252
336
|
|
|
253
337
|
const subjectPrefix = options.subjectPrefix ?? "ignex";
|
|
@@ -261,6 +345,16 @@ export function generateBindings(
|
|
|
261
345
|
fingerprint,
|
|
262
346
|
);
|
|
263
347
|
|
|
348
|
+
if (options.client) {
|
|
349
|
+
const clientFile = options.client.outFile ?? "client.gen.ts";
|
|
350
|
+
files[clientFile] = emitClientModel(
|
|
351
|
+
model,
|
|
352
|
+
options.client.wireImport ?? "./index",
|
|
353
|
+
options.client.schemaImport,
|
|
354
|
+
libraryImport,
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
|
|
264
358
|
files["README.md"] = emitReadme();
|
|
265
359
|
|
|
266
360
|
const write = (): string[] => {
|
package/public/server.ts
CHANGED
|
@@ -30,6 +30,8 @@ export { createNatsBridge } from "../src/bridge/nats";
|
|
|
30
30
|
export type { Int64GuardMode } from "../src/core/int64-guard";
|
|
31
31
|
export type { MetricsSnapshot } from "../src/core/metrics";
|
|
32
32
|
export { type ClientInfo, createServer, type IgnServer } from "../src/core/server";
|
|
33
|
+
// Durable topic log (replay seam) — `createServer({ topicLog })` + adapters.
|
|
34
|
+
export { createMemoryTopicLog, type LoggedFrame, type TopicLog } from "../src/core/topic-log";
|
|
33
35
|
export type {
|
|
34
36
|
AuthResult,
|
|
35
37
|
BackpressurePolicy,
|
|
@@ -40,6 +42,7 @@ export type {
|
|
|
40
42
|
} from "../src/core/state";
|
|
41
43
|
// Events layer — re-exported so `createServer({ events })` is fully typed
|
|
42
44
|
// without a separate import (the runtime API is `@ignex/nova/events`).
|
|
45
|
+
export type { DeliveryPolicy } from "../src/events/delivery";
|
|
43
46
|
export type {
|
|
44
47
|
ClientData,
|
|
45
48
|
ClientGroup,
|
|
@@ -59,3 +62,10 @@ export type {
|
|
|
59
62
|
ServerEventHandler,
|
|
60
63
|
UserGroup,
|
|
61
64
|
} from "../src/events/types";
|
|
65
|
+
// Event trace (debugger visibility) — `server.getEventTrace()` surface types.
|
|
66
|
+
export type {
|
|
67
|
+
EventTraceOptions,
|
|
68
|
+
EventTraceRow,
|
|
69
|
+
EventTraceStats,
|
|
70
|
+
TraceDirection,
|
|
71
|
+
} from "../src/events/trace";
|