@rivetkit/rivetkit-native 2.2.1-pr.4600.4f36c20

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/index.d.ts ADDED
@@ -0,0 +1,93 @@
1
+ /* tslint:disable */
2
+ /* eslint-disable */
3
+
4
+ /* auto-generated by NAPI-RS */
5
+
6
+ export interface JsBindParam {
7
+ kind: string
8
+ intValue?: number
9
+ floatValue?: number
10
+ textValue?: string
11
+ blobValue?: Buffer
12
+ }
13
+ export interface ExecuteResult {
14
+ changes: number
15
+ }
16
+ export interface QueryResult {
17
+ columns: Array<string>
18
+ rows: Array<Array<any>>
19
+ }
20
+ /** Open a native SQLite database backed by the envoy's KV channel. */
21
+ export declare function openDatabaseFromEnvoy(jsHandle: JsEnvoyHandle, actorId: string): Promise<JsNativeDatabase>
22
+ /** Configuration for starting the native envoy client. */
23
+ export interface JsEnvoyConfig {
24
+ endpoint: string
25
+ token: string
26
+ namespace: string
27
+ poolName: string
28
+ version: number
29
+ metadata?: any
30
+ notGlobal: boolean
31
+ /**
32
+ * Log level for the Rust tracing subscriber (e.g. "trace", "debug", "info", "warn", "error").
33
+ * Falls back to RIVET_LOG_LEVEL, then LOG_LEVEL, then RUST_LOG env vars. Defaults to "warn".
34
+ */
35
+ logLevel?: string
36
+ }
37
+ /** Options for KV list operations. */
38
+ export interface JsKvListOptions {
39
+ reverse?: boolean
40
+ limit?: number
41
+ }
42
+ /** A key-value entry returned from KV list operations. */
43
+ export interface JsKvEntry {
44
+ key: Buffer
45
+ value: Buffer
46
+ }
47
+ /** A single hibernating request entry. */
48
+ export interface HibernatingRequestEntry {
49
+ gatewayId: Buffer
50
+ requestId: Buffer
51
+ }
52
+ /**
53
+ * Start the native envoy client synchronously.
54
+ *
55
+ * Returns a handle immediately. The caller must call `await handle.started()`
56
+ * to wait for the connection to be ready.
57
+ */
58
+ export declare function startEnvoySyncJs(config: JsEnvoyConfig, eventCallback: (event: any) => void): JsEnvoyHandle
59
+ /** Start the native envoy client asynchronously. */
60
+ export declare function startEnvoyJs(config: JsEnvoyConfig, eventCallback: (event: any) => void): JsEnvoyHandle
61
+ /** Native SQLite database handle exposed to JavaScript. */
62
+ export declare class JsNativeDatabase {
63
+ run(sql: string, params?: Array<JsBindParam> | undefined | null): Promise<ExecuteResult>
64
+ query(sql: string, params?: Array<JsBindParam> | undefined | null): Promise<QueryResult>
65
+ exec(sql: string): Promise<QueryResult>
66
+ close(): Promise<void>
67
+ }
68
+ /** Native envoy handle exposed to JavaScript via N-API. */
69
+ export declare class JsEnvoyHandle {
70
+ started(): Promise<void>
71
+ shutdown(immediate: boolean): void
72
+ get envoyKey(): string
73
+ sleepActor(actorId: string, generation?: number | undefined | null): void
74
+ stopActor(actorId: string, generation?: number | undefined | null, error?: string | undefined | null): void
75
+ destroyActor(actorId: string, generation?: number | undefined | null): void
76
+ setAlarm(actorId: string, alarmTs?: number | undefined | null, generation?: number | undefined | null): void
77
+ kvGet(actorId: string, keys: Array<Buffer>): Promise<Array<Buffer | undefined | null>>
78
+ kvPut(actorId: string, entries: Array<JsKvEntry>): Promise<void>
79
+ kvDelete(actorId: string, keys: Array<Buffer>): Promise<void>
80
+ kvDeleteRange(actorId: string, start: Buffer, end: Buffer): Promise<void>
81
+ kvListAll(actorId: string, options?: JsKvListOptions | undefined | null): Promise<Array<JsKvEntry>>
82
+ kvListRange(actorId: string, start: Buffer, end: Buffer, exclusive?: boolean | undefined | null, options?: JsKvListOptions | undefined | null): Promise<Array<JsKvEntry>>
83
+ kvListPrefix(actorId: string, prefix: Buffer, options?: JsKvListOptions | undefined | null): Promise<Array<JsKvEntry>>
84
+ kvDrop(actorId: string): Promise<void>
85
+ restoreHibernatingRequests(actorId: string, requests: Array<HibernatingRequestEntry>): void
86
+ sendHibernatableWebSocketMessageAck(gatewayId: Buffer, requestId: Buffer, clientMessageIndex: number): void
87
+ /** Send a message on an open WebSocket connection identified by messageIdHex. */
88
+ sendWsMessage(gatewayId: Buffer, requestId: Buffer, data: Buffer, binary: boolean): Promise<void>
89
+ /** Close an open WebSocket connection. */
90
+ closeWebsocket(gatewayId: Buffer, requestId: Buffer, code?: number | undefined | null, reason?: string | undefined | null): Promise<void>
91
+ startServerless(payload: Buffer): Promise<void>
92
+ respondCallback(responseId: string, data: any): Promise<void>
93
+ }
package/index.js ADDED
@@ -0,0 +1,319 @@
1
+ /* tslint:disable */
2
+ /* eslint-disable */
3
+ /* prettier-ignore */
4
+
5
+ /* auto-generated by NAPI-RS */
6
+
7
+ const { existsSync, readFileSync } = require('fs')
8
+ const { join } = require('path')
9
+
10
+ const { platform, arch } = process
11
+
12
+ let nativeBinding = null
13
+ let localFileExisted = false
14
+ let loadError = null
15
+
16
+ function isMusl() {
17
+ // For Node 10
18
+ if (!process.report || typeof process.report.getReport !== 'function') {
19
+ try {
20
+ const lddPath = require('child_process').execSync('which ldd').toString().trim()
21
+ return readFileSync(lddPath, 'utf8').includes('musl')
22
+ } catch (e) {
23
+ return true
24
+ }
25
+ } else {
26
+ const { glibcVersionRuntime } = process.report.getReport().header
27
+ return !glibcVersionRuntime
28
+ }
29
+ }
30
+
31
+ switch (platform) {
32
+ case 'android':
33
+ switch (arch) {
34
+ case 'arm64':
35
+ localFileExisted = existsSync(join(__dirname, 'rivetkit-native.android-arm64.node'))
36
+ try {
37
+ if (localFileExisted) {
38
+ nativeBinding = require('./rivetkit-native.android-arm64.node')
39
+ } else {
40
+ nativeBinding = require('@rivetkit/rivetkit-native-android-arm64')
41
+ }
42
+ } catch (e) {
43
+ loadError = e
44
+ }
45
+ break
46
+ case 'arm':
47
+ localFileExisted = existsSync(join(__dirname, 'rivetkit-native.android-arm-eabi.node'))
48
+ try {
49
+ if (localFileExisted) {
50
+ nativeBinding = require('./rivetkit-native.android-arm-eabi.node')
51
+ } else {
52
+ nativeBinding = require('@rivetkit/rivetkit-native-android-arm-eabi')
53
+ }
54
+ } catch (e) {
55
+ loadError = e
56
+ }
57
+ break
58
+ default:
59
+ throw new Error(`Unsupported architecture on Android ${arch}`)
60
+ }
61
+ break
62
+ case 'win32':
63
+ switch (arch) {
64
+ case 'x64':
65
+ localFileExisted = existsSync(
66
+ join(__dirname, 'rivetkit-native.win32-x64-msvc.node')
67
+ )
68
+ try {
69
+ if (localFileExisted) {
70
+ nativeBinding = require('./rivetkit-native.win32-x64-msvc.node')
71
+ } else {
72
+ nativeBinding = require('@rivetkit/rivetkit-native-win32-x64-msvc')
73
+ }
74
+ } catch (e) {
75
+ loadError = e
76
+ }
77
+ break
78
+ case 'ia32':
79
+ localFileExisted = existsSync(
80
+ join(__dirname, 'rivetkit-native.win32-ia32-msvc.node')
81
+ )
82
+ try {
83
+ if (localFileExisted) {
84
+ nativeBinding = require('./rivetkit-native.win32-ia32-msvc.node')
85
+ } else {
86
+ nativeBinding = require('@rivetkit/rivetkit-native-win32-ia32-msvc')
87
+ }
88
+ } catch (e) {
89
+ loadError = e
90
+ }
91
+ break
92
+ case 'arm64':
93
+ localFileExisted = existsSync(
94
+ join(__dirname, 'rivetkit-native.win32-arm64-msvc.node')
95
+ )
96
+ try {
97
+ if (localFileExisted) {
98
+ nativeBinding = require('./rivetkit-native.win32-arm64-msvc.node')
99
+ } else {
100
+ nativeBinding = require('@rivetkit/rivetkit-native-win32-arm64-msvc')
101
+ }
102
+ } catch (e) {
103
+ loadError = e
104
+ }
105
+ break
106
+ default:
107
+ throw new Error(`Unsupported architecture on Windows: ${arch}`)
108
+ }
109
+ break
110
+ case 'darwin':
111
+ localFileExisted = existsSync(join(__dirname, 'rivetkit-native.darwin-universal.node'))
112
+ try {
113
+ if (localFileExisted) {
114
+ nativeBinding = require('./rivetkit-native.darwin-universal.node')
115
+ } else {
116
+ nativeBinding = require('@rivetkit/rivetkit-native-darwin-universal')
117
+ }
118
+ break
119
+ } catch {}
120
+ switch (arch) {
121
+ case 'x64':
122
+ localFileExisted = existsSync(join(__dirname, 'rivetkit-native.darwin-x64.node'))
123
+ try {
124
+ if (localFileExisted) {
125
+ nativeBinding = require('./rivetkit-native.darwin-x64.node')
126
+ } else {
127
+ nativeBinding = require('@rivetkit/rivetkit-native-darwin-x64')
128
+ }
129
+ } catch (e) {
130
+ loadError = e
131
+ }
132
+ break
133
+ case 'arm64':
134
+ localFileExisted = existsSync(
135
+ join(__dirname, 'rivetkit-native.darwin-arm64.node')
136
+ )
137
+ try {
138
+ if (localFileExisted) {
139
+ nativeBinding = require('./rivetkit-native.darwin-arm64.node')
140
+ } else {
141
+ nativeBinding = require('@rivetkit/rivetkit-native-darwin-arm64')
142
+ }
143
+ } catch (e) {
144
+ loadError = e
145
+ }
146
+ break
147
+ default:
148
+ throw new Error(`Unsupported architecture on macOS: ${arch}`)
149
+ }
150
+ break
151
+ case 'freebsd':
152
+ if (arch !== 'x64') {
153
+ throw new Error(`Unsupported architecture on FreeBSD: ${arch}`)
154
+ }
155
+ localFileExisted = existsSync(join(__dirname, 'rivetkit-native.freebsd-x64.node'))
156
+ try {
157
+ if (localFileExisted) {
158
+ nativeBinding = require('./rivetkit-native.freebsd-x64.node')
159
+ } else {
160
+ nativeBinding = require('@rivetkit/rivetkit-native-freebsd-x64')
161
+ }
162
+ } catch (e) {
163
+ loadError = e
164
+ }
165
+ break
166
+ case 'linux':
167
+ switch (arch) {
168
+ case 'x64':
169
+ if (isMusl()) {
170
+ localFileExisted = existsSync(
171
+ join(__dirname, 'rivetkit-native.linux-x64-musl.node')
172
+ )
173
+ try {
174
+ if (localFileExisted) {
175
+ nativeBinding = require('./rivetkit-native.linux-x64-musl.node')
176
+ } else {
177
+ nativeBinding = require('@rivetkit/rivetkit-native-linux-x64-musl')
178
+ }
179
+ } catch (e) {
180
+ loadError = e
181
+ }
182
+ } else {
183
+ localFileExisted = existsSync(
184
+ join(__dirname, 'rivetkit-native.linux-x64-gnu.node')
185
+ )
186
+ try {
187
+ if (localFileExisted) {
188
+ nativeBinding = require('./rivetkit-native.linux-x64-gnu.node')
189
+ } else {
190
+ nativeBinding = require('@rivetkit/rivetkit-native-linux-x64-gnu')
191
+ }
192
+ } catch (e) {
193
+ loadError = e
194
+ }
195
+ }
196
+ break
197
+ case 'arm64':
198
+ if (isMusl()) {
199
+ localFileExisted = existsSync(
200
+ join(__dirname, 'rivetkit-native.linux-arm64-musl.node')
201
+ )
202
+ try {
203
+ if (localFileExisted) {
204
+ nativeBinding = require('./rivetkit-native.linux-arm64-musl.node')
205
+ } else {
206
+ nativeBinding = require('@rivetkit/rivetkit-native-linux-arm64-musl')
207
+ }
208
+ } catch (e) {
209
+ loadError = e
210
+ }
211
+ } else {
212
+ localFileExisted = existsSync(
213
+ join(__dirname, 'rivetkit-native.linux-arm64-gnu.node')
214
+ )
215
+ try {
216
+ if (localFileExisted) {
217
+ nativeBinding = require('./rivetkit-native.linux-arm64-gnu.node')
218
+ } else {
219
+ nativeBinding = require('@rivetkit/rivetkit-native-linux-arm64-gnu')
220
+ }
221
+ } catch (e) {
222
+ loadError = e
223
+ }
224
+ }
225
+ break
226
+ case 'arm':
227
+ if (isMusl()) {
228
+ localFileExisted = existsSync(
229
+ join(__dirname, 'rivetkit-native.linux-arm-musleabihf.node')
230
+ )
231
+ try {
232
+ if (localFileExisted) {
233
+ nativeBinding = require('./rivetkit-native.linux-arm-musleabihf.node')
234
+ } else {
235
+ nativeBinding = require('@rivetkit/rivetkit-native-linux-arm-musleabihf')
236
+ }
237
+ } catch (e) {
238
+ loadError = e
239
+ }
240
+ } else {
241
+ localFileExisted = existsSync(
242
+ join(__dirname, 'rivetkit-native.linux-arm-gnueabihf.node')
243
+ )
244
+ try {
245
+ if (localFileExisted) {
246
+ nativeBinding = require('./rivetkit-native.linux-arm-gnueabihf.node')
247
+ } else {
248
+ nativeBinding = require('@rivetkit/rivetkit-native-linux-arm-gnueabihf')
249
+ }
250
+ } catch (e) {
251
+ loadError = e
252
+ }
253
+ }
254
+ break
255
+ case 'riscv64':
256
+ if (isMusl()) {
257
+ localFileExisted = existsSync(
258
+ join(__dirname, 'rivetkit-native.linux-riscv64-musl.node')
259
+ )
260
+ try {
261
+ if (localFileExisted) {
262
+ nativeBinding = require('./rivetkit-native.linux-riscv64-musl.node')
263
+ } else {
264
+ nativeBinding = require('@rivetkit/rivetkit-native-linux-riscv64-musl')
265
+ }
266
+ } catch (e) {
267
+ loadError = e
268
+ }
269
+ } else {
270
+ localFileExisted = existsSync(
271
+ join(__dirname, 'rivetkit-native.linux-riscv64-gnu.node')
272
+ )
273
+ try {
274
+ if (localFileExisted) {
275
+ nativeBinding = require('./rivetkit-native.linux-riscv64-gnu.node')
276
+ } else {
277
+ nativeBinding = require('@rivetkit/rivetkit-native-linux-riscv64-gnu')
278
+ }
279
+ } catch (e) {
280
+ loadError = e
281
+ }
282
+ }
283
+ break
284
+ case 's390x':
285
+ localFileExisted = existsSync(
286
+ join(__dirname, 'rivetkit-native.linux-s390x-gnu.node')
287
+ )
288
+ try {
289
+ if (localFileExisted) {
290
+ nativeBinding = require('./rivetkit-native.linux-s390x-gnu.node')
291
+ } else {
292
+ nativeBinding = require('@rivetkit/rivetkit-native-linux-s390x-gnu')
293
+ }
294
+ } catch (e) {
295
+ loadError = e
296
+ }
297
+ break
298
+ default:
299
+ throw new Error(`Unsupported architecture on Linux: ${arch}`)
300
+ }
301
+ break
302
+ default:
303
+ throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`)
304
+ }
305
+
306
+ if (!nativeBinding) {
307
+ if (loadError) {
308
+ throw loadError
309
+ }
310
+ throw new Error(`Failed to load native binding`)
311
+ }
312
+
313
+ const { JsNativeDatabase, openDatabaseFromEnvoy, JsEnvoyHandle, startEnvoySyncJs, startEnvoyJs } = nativeBinding
314
+
315
+ module.exports.JsNativeDatabase = JsNativeDatabase
316
+ module.exports.openDatabaseFromEnvoy = openDatabaseFromEnvoy
317
+ module.exports.JsEnvoyHandle = JsEnvoyHandle
318
+ module.exports.startEnvoySyncJs = startEnvoySyncJs
319
+ module.exports.startEnvoyJs = startEnvoyJs
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "@rivetkit/rivetkit-native",
3
+ "version": "2.2.1-pr.4600.4f36c20",
4
+ "description": "Native N-API addon for RivetKit providing envoy client and SQLite access",
5
+ "license": "Apache-2.0",
6
+ "main": "index.js",
7
+ "types": "index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./index.d.ts",
11
+ "default": "./index.js"
12
+ },
13
+ "./wrapper": {
14
+ "types": "./wrapper.d.ts",
15
+ "default": "./wrapper.js"
16
+ }
17
+ },
18
+ "engines": {
19
+ "node": ">= 20.0.0"
20
+ },
21
+ "napi": {
22
+ "name": "rivetkit-native",
23
+ "triples": {
24
+ "defaults": false,
25
+ "additional": [
26
+ "x86_64-unknown-linux-gnu",
27
+ "aarch64-unknown-linux-gnu",
28
+ "x86_64-unknown-linux-musl",
29
+ "aarch64-unknown-linux-musl",
30
+ "x86_64-apple-darwin",
31
+ "aarch64-apple-darwin",
32
+ "x86_64-pc-windows-msvc"
33
+ ]
34
+ }
35
+ },
36
+ "files": [
37
+ "index.js",
38
+ "index.d.ts",
39
+ "wrapper.js",
40
+ "wrapper.d.ts",
41
+ "package.json",
42
+ "scripts/build.mjs"
43
+ ],
44
+ "scripts": {
45
+ "build": "node scripts/build.mjs",
46
+ "build:release": "node scripts/build.mjs --release",
47
+ "build:force": "node scripts/build.mjs --force",
48
+ "build:force:release": "node scripts/build.mjs --force --release"
49
+ },
50
+ "dependencies": {
51
+ "@napi-rs/cli": "^2.18.4",
52
+ "@rivetkit/engine-envoy-protocol": "2.2.1-pr.4600.4f36c20"
53
+ },
54
+ "optionalDependencies": {
55
+ "@rivetkit/rivetkit-native-darwin-arm64": "2.2.1-pr.4600.4f36c20",
56
+ "@rivetkit/rivetkit-native-darwin-x64": "2.2.1-pr.4600.4f36c20",
57
+ "@rivetkit/rivetkit-native-linux-arm64-gnu": "2.2.1-pr.4600.4f36c20",
58
+ "@rivetkit/rivetkit-native-linux-arm64-musl": "2.2.1-pr.4600.4f36c20",
59
+ "@rivetkit/rivetkit-native-linux-x64-gnu": "2.2.1-pr.4600.4f36c20",
60
+ "@rivetkit/rivetkit-native-linux-x64-musl": "2.2.1-pr.4600.4f36c20",
61
+ "@rivetkit/rivetkit-native-win32-x64-msvc": "2.2.1-pr.4600.4f36c20"
62
+ }
63
+ }
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Smart build wrapper for rivetkit-native.
4
+ *
5
+ * Skips the napi build if a prebuilt .node file already exists next to
6
+ * this package (either a root-level `rivetkit-native.*.node` or one inside
7
+ * a `npm/<platform>/` directory). This lets CI skip a redundant napi build
8
+ * when the cross-compiled artifacts have already been downloaded from the
9
+ * platform build jobs.
10
+ *
11
+ * Pass `--force` to always run the napi build.
12
+ */
13
+ import { execSync } from "node:child_process";
14
+ import { existsSync, readdirSync, statSync } from "node:fs";
15
+ import { dirname, join } from "node:path";
16
+ import { fileURLToPath } from "node:url";
17
+
18
+ const __dirname = dirname(fileURLToPath(import.meta.url));
19
+ const packageDir = join(__dirname, "..");
20
+
21
+ const args = process.argv.slice(2);
22
+ const force = args.includes("--force");
23
+ const releaseArg = args.find((a) => a === "--release");
24
+ const extraFlags = releaseArg ? ["--release"] : [];
25
+
26
+ function hasPrebuiltArtifact() {
27
+ // Check for root-level .node files.
28
+ const rootFiles = readdirSync(packageDir);
29
+ if (rootFiles.some((f) => f.endsWith(".node"))) {
30
+ return true;
31
+ }
32
+ // Check for any npm/<platform>/*.node files.
33
+ const npmDir = join(packageDir, "npm");
34
+ if (existsSync(npmDir) && statSync(npmDir).isDirectory()) {
35
+ for (const entry of readdirSync(npmDir)) {
36
+ const platDir = join(npmDir, entry);
37
+ if (!statSync(platDir).isDirectory()) continue;
38
+ const files = readdirSync(platDir);
39
+ if (files.some((f) => f.endsWith(".node"))) {
40
+ return true;
41
+ }
42
+ }
43
+ }
44
+ return false;
45
+ }
46
+
47
+ if (!force && hasPrebuiltArtifact()) {
48
+ console.log(
49
+ "[rivetkit-native/build] prebuilt .node artifact found — skipping napi build",
50
+ );
51
+ console.log("[rivetkit-native/build] use --force to rebuild from source");
52
+ process.exit(0);
53
+ }
54
+
55
+ const cmd = ["napi", "build", "--platform", ...extraFlags].join(" ");
56
+ console.log(`[rivetkit-native/build] running: ${cmd}`);
57
+ execSync(cmd, { stdio: "inherit", cwd: packageDir });
package/wrapper.d.ts ADDED
@@ -0,0 +1,144 @@
1
+ import type { JsNativeDatabase, JsKvEntry, JsKvListOptions } from "./index";
2
+
3
+ export type { JsNativeDatabase, JsKvEntry, JsKvListOptions };
4
+
5
+ // Re-export protocol types from the envoy protocol package
6
+ export * as protocol from "@rivetkit/engine-envoy-protocol";
7
+
8
+ export interface HibernatingWebSocketMetadata {
9
+ gatewayId: ArrayBuffer;
10
+ requestId: ArrayBuffer;
11
+ envoyMessageIndex: number;
12
+ rivetMessageIndex: number;
13
+ path: string;
14
+ headers: Record<string, string>;
15
+ }
16
+
17
+ export interface KvListOptions {
18
+ reverse?: boolean;
19
+ limit?: number;
20
+ }
21
+
22
+ /** Matches the TS EnvoyHandle interface from @rivetkit/engine-envoy-client */
23
+ export interface EnvoyHandle {
24
+ shutdown(immediate: boolean): void;
25
+ getProtocolMetadata(): any | undefined;
26
+ getEnvoyKey(): string;
27
+ started(): Promise<void>;
28
+ getActor(actorId: string, generation?: number): any | undefined;
29
+ sleepActor(actorId: string, generation?: number): void;
30
+ stopActor(actorId: string, generation?: number, error?: string): void;
31
+ destroyActor(actorId: string, generation?: number): void;
32
+ setAlarm(actorId: string, alarmTs: number | null, generation?: number): void;
33
+ kvGet(actorId: string, keys: Uint8Array[]): Promise<(Uint8Array | null)[]>;
34
+ kvListAll(actorId: string, options?: KvListOptions): Promise<[Uint8Array, Uint8Array][]>;
35
+ kvListRange(
36
+ actorId: string,
37
+ start: Uint8Array,
38
+ end: Uint8Array,
39
+ exclusive?: boolean,
40
+ options?: KvListOptions,
41
+ ): Promise<[Uint8Array, Uint8Array][]>;
42
+ kvListPrefix(
43
+ actorId: string,
44
+ prefix: Uint8Array,
45
+ options?: KvListOptions,
46
+ ): Promise<[Uint8Array, Uint8Array][]>;
47
+ kvPut(actorId: string, entries: [Uint8Array, Uint8Array][]): Promise<void>;
48
+ kvDelete(actorId: string, keys: Uint8Array[]): Promise<void>;
49
+ kvDeleteRange(actorId: string, start: Uint8Array, end: Uint8Array): Promise<void>;
50
+ kvDrop(actorId: string): Promise<void>;
51
+ restoreHibernatingRequests(
52
+ actorId: string,
53
+ metaEntries: HibernatingWebSocketMetadata[],
54
+ ): void;
55
+ sendHibernatableWebSocketMessageAck(
56
+ gatewayId: ArrayBuffer,
57
+ requestId: ArrayBuffer,
58
+ clientMessageIndex: number,
59
+ ): void;
60
+ startServerlessActor(payload: ArrayBuffer): Promise<void>;
61
+ }
62
+
63
+ /** Matches the TS EnvoyConfig interface from @rivetkit/engine-envoy-client */
64
+ export interface EnvoyConfig {
65
+ logger?: any;
66
+ version: number;
67
+ endpoint: string;
68
+ token?: string;
69
+ namespace: string;
70
+ poolName: string;
71
+ prepopulateActorNames: Record<string, { metadata: Record<string, any> }>;
72
+ metadata?: Record<string, any>;
73
+ notGlobal?: boolean;
74
+ debugLatencyMs?: number;
75
+ serverlessStartPayload?: ArrayBuffer;
76
+ fetch: (
77
+ envoyHandle: EnvoyHandle,
78
+ actorId: string,
79
+ gatewayId: ArrayBuffer,
80
+ requestId: ArrayBuffer,
81
+ request: Request,
82
+ ) => Promise<Response>;
83
+ websocket: (
84
+ envoyHandle: EnvoyHandle,
85
+ actorId: string,
86
+ ws: any,
87
+ gatewayId: ArrayBuffer,
88
+ requestId: ArrayBuffer,
89
+ request: Request,
90
+ path: string,
91
+ headers: Record<string, string>,
92
+ isHibernatable: boolean,
93
+ isRestoringHibernatable: boolean,
94
+ ) => Promise<void>;
95
+ hibernatableWebSocket: {
96
+ canHibernate: (
97
+ actorId: string,
98
+ gatewayId: ArrayBuffer,
99
+ requestId: ArrayBuffer,
100
+ request: Request,
101
+ ) => boolean;
102
+ };
103
+ onActorStart: (
104
+ envoyHandle: EnvoyHandle,
105
+ actorId: string,
106
+ generation: number,
107
+ config: import("@rivetkit/engine-envoy-protocol").ActorConfig,
108
+ preloadedKv: import("@rivetkit/engine-envoy-protocol").PreloadedKv | null,
109
+ ) => Promise<void>;
110
+ onActorStop: (
111
+ envoyHandle: EnvoyHandle,
112
+ actorId: string,
113
+ generation: number,
114
+ reason: import("@rivetkit/engine-envoy-protocol").StopActorReason,
115
+ ) => Promise<void>;
116
+ onShutdown: () => void;
117
+ }
118
+
119
+ /** Start the native envoy synchronously. Returns a handle immediately. */
120
+ export declare function startEnvoySync(config: EnvoyConfig): EnvoyHandle;
121
+
122
+ /** Start the native envoy and wait for it to be ready. */
123
+ export declare function startEnvoy(config: EnvoyConfig): Promise<EnvoyHandle>;
124
+
125
+ /** Open a native database backed by envoy KV for the specified actor. */
126
+ export declare function openDatabaseFromEnvoy(
127
+ handle: EnvoyHandle,
128
+ actorId: string,
129
+ ): Promise<JsNativeDatabase>;
130
+
131
+ export interface NativeRawDatabase {
132
+ execute: <TRow extends Record<string, unknown> = Record<string, unknown>>(
133
+ query: string,
134
+ ...args: unknown[]
135
+ ) => Promise<TRow[]>;
136
+ close: () => Promise<void>;
137
+ }
138
+
139
+ export declare function openRawDatabaseFromEnvoy(
140
+ handle: EnvoyHandle,
141
+ actorId: string,
142
+ ): Promise<NativeRawDatabase>;
143
+
144
+ export declare const utils: {};
package/wrapper.js ADDED
@@ -0,0 +1,422 @@
1
+ /**
2
+ * Thin JS wrapper that adapts native callback envelopes to the
3
+ * EnvoyConfig callback shape used by the TypeScript envoy client.
4
+ *
5
+ * The native addon sends JSON envelopes with a "kind" field.
6
+ * This wrapper routes them to the appropriate EnvoyConfig callbacks.
7
+ */
8
+
9
+ const native = require("./index");
10
+
11
+ // Re-export protocol for consumers that need protocol types at runtime
12
+ let _protocol;
13
+ try {
14
+ _protocol = require("@rivetkit/engine-envoy-protocol");
15
+ } catch {
16
+ _protocol = {};
17
+ }
18
+ module.exports.protocol = _protocol;
19
+ module.exports.utils = {};
20
+
21
+ /**
22
+ * Create a wrapped EnvoyHandle that matches the TS EnvoyHandle interface.
23
+ */
24
+ function wrapHandle(jsHandle) {
25
+ const handle = {
26
+ started: () => jsHandle.started(),
27
+ shutdown: (immediate) => jsHandle.shutdown(immediate ?? false),
28
+ getProtocolMetadata: () => undefined,
29
+ getEnvoyKey: () => jsHandle.envoyKey,
30
+ getActor: (_actorId, _generation) => undefined,
31
+ sleepActor: (actorId, generation) =>
32
+ jsHandle.sleepActor(actorId, generation ?? null),
33
+ stopActor: (actorId, generation, error) =>
34
+ jsHandle.stopActor(actorId, generation ?? null, error ?? null),
35
+ destroyActor: (actorId, generation) =>
36
+ jsHandle.destroyActor(actorId, generation ?? null),
37
+ setAlarm: (actorId, alarmTs, generation) =>
38
+ jsHandle.setAlarm(actorId, alarmTs ?? null, generation ?? null),
39
+ kvGet: async (actorId, keys) => {
40
+ const bufKeys = keys.map((k) => Buffer.from(k));
41
+ const result = await jsHandle.kvGet(actorId, bufKeys);
42
+ return result.map((v) => (v ? new Uint8Array(v) : null));
43
+ },
44
+ kvPut: async (actorId, entries) => {
45
+ const jsEntries = entries.map(([k, v]) => ({
46
+ key: Buffer.from(k),
47
+ value: Buffer.from(v),
48
+ }));
49
+ return jsHandle.kvPut(actorId, jsEntries);
50
+ },
51
+ kvDelete: async (actorId, keys) => {
52
+ const bufKeys = keys.map((k) => Buffer.from(k));
53
+ return jsHandle.kvDelete(actorId, bufKeys);
54
+ },
55
+ kvDeleteRange: async (actorId, start, end) => {
56
+ return jsHandle.kvDeleteRange(
57
+ actorId,
58
+ Buffer.from(start),
59
+ Buffer.from(end),
60
+ );
61
+ },
62
+ kvListAll: async (actorId, options) => {
63
+ const result = await jsHandle.kvListAll(actorId, options || null);
64
+ return result.map((e) => [new Uint8Array(e.key), new Uint8Array(e.value)]);
65
+ },
66
+ kvListRange: async (actorId, start, end, exclusive, options) => {
67
+ const result = await jsHandle.kvListRange(
68
+ actorId,
69
+ Buffer.from(start),
70
+ Buffer.from(end),
71
+ exclusive,
72
+ options || null,
73
+ );
74
+ return result.map((e) => [new Uint8Array(e.key), new Uint8Array(e.value)]);
75
+ },
76
+ kvListPrefix: async (actorId, prefix, options) => {
77
+ const result = await jsHandle.kvListPrefix(
78
+ actorId,
79
+ Buffer.from(prefix),
80
+ options || null,
81
+ );
82
+ return result.map((e) => [new Uint8Array(e.key), new Uint8Array(e.value)]);
83
+ },
84
+ kvDrop: (actorId) => jsHandle.kvDrop(actorId),
85
+ restoreHibernatingRequests: (actorId, metaEntries) => {
86
+ const requests = (metaEntries || []).map((e) => ({
87
+ gatewayId: Buffer.from(e.gatewayId),
88
+ requestId: Buffer.from(e.requestId),
89
+ }));
90
+ jsHandle.restoreHibernatingRequests(actorId, requests);
91
+ },
92
+ sendHibernatableWebSocketMessageAck: (
93
+ gatewayId,
94
+ requestId,
95
+ clientMessageIndex,
96
+ ) =>
97
+ jsHandle.sendHibernatableWebSocketMessageAck(
98
+ Buffer.from(gatewayId),
99
+ Buffer.from(requestId),
100
+ clientMessageIndex,
101
+ ),
102
+ startServerlessActor: (payload) => {
103
+ jsHandle.startServerless(Buffer.from(payload));
104
+ },
105
+ // Internal: expose raw handle for openDatabaseFromEnvoy
106
+ _raw: jsHandle,
107
+ };
108
+ return handle;
109
+ }
110
+
111
+ /**
112
+ * Start the native envoy synchronously with EnvoyConfig callbacks.
113
+ * Returns a wrapped handle matching the TS EnvoyHandle interface.
114
+ */
115
+ function startEnvoySync(config) {
116
+ const wrappedHandle = { current: null };
117
+
118
+ const jsHandle = native.startEnvoySyncJs(
119
+ {
120
+ endpoint: config.endpoint,
121
+ token: config.token || "",
122
+ namespace: config.namespace,
123
+ poolName: config.poolName,
124
+ version: config.version,
125
+ metadata: config.metadata || null,
126
+ notGlobal: config.notGlobal ?? false,
127
+ },
128
+ (event) => {
129
+ handleEvent(event, config, wrappedHandle);
130
+ },
131
+ );
132
+
133
+ const handle = wrapHandle(jsHandle);
134
+ wrappedHandle.current = handle;
135
+ return handle;
136
+ }
137
+
138
+ /**
139
+ * Start the native envoy and wait for it to be ready.
140
+ */
141
+ async function startEnvoy(config) {
142
+ const handle = startEnvoySync(config);
143
+ await handle.started();
144
+ return handle;
145
+ }
146
+
147
+ /**
148
+ * Open a native database backed by envoy KV.
149
+ */
150
+ async function openDatabaseFromEnvoy(handle, actorId) {
151
+ const rawHandle = handle._raw || handle;
152
+ return native.openDatabaseFromEnvoy(rawHandle, actorId);
153
+ }
154
+
155
+ /**
156
+ * Route callback envelopes from the native addon to EnvoyConfig callbacks.
157
+ */
158
+ function handleEvent(event, config, wrappedHandle) {
159
+ const handle = wrappedHandle.current;
160
+
161
+ switch (event.kind) {
162
+ case "actor_start": {
163
+ const input = event.input ? Buffer.from(event.input, "base64") : undefined;
164
+ const actorConfig = {
165
+ name: event.name,
166
+ key: event.key || undefined,
167
+ createTs: event.createTs,
168
+ input,
169
+ };
170
+ Promise.resolve(
171
+ config.onActorStart(
172
+ handle,
173
+ event.actorId,
174
+ event.generation,
175
+ actorConfig,
176
+ null, // preloadedKv
177
+ ),
178
+ ).then(
179
+ () => {
180
+ if (handle._raw) {
181
+ handle._raw.respondCallback(event.responseId, {});
182
+ }
183
+ },
184
+ (err) => {
185
+ console.error("onActorStart error:", err);
186
+ if (handle._raw) {
187
+ handle._raw.respondCallback(event.responseId, {
188
+ error: String(err),
189
+ });
190
+ }
191
+ },
192
+ );
193
+ break;
194
+ }
195
+ case "actor_stop": {
196
+ Promise.resolve(
197
+ config.onActorStop(
198
+ handle,
199
+ event.actorId,
200
+ event.generation,
201
+ event.reason || "stopped",
202
+ ),
203
+ ).then(
204
+ () => {
205
+ if (handle._raw) {
206
+ handle._raw.respondCallback(event.responseId, {});
207
+ }
208
+ },
209
+ (err) => {
210
+ console.error("onActorStop error:", err);
211
+ if (handle._raw) {
212
+ handle._raw.respondCallback(event.responseId, {
213
+ error: String(err),
214
+ });
215
+ }
216
+ },
217
+ );
218
+ break;
219
+ }
220
+ case "http_request": {
221
+ const body = event.body ? Buffer.from(event.body, "base64") : undefined;
222
+ const messageId = Buffer.from(event.messageId);
223
+ const gatewayId = messageId.subarray(0, 4);
224
+ const requestId = messageId.subarray(4, 8);
225
+
226
+ // Build a Request object matching the TS envoy-client interface
227
+ const headers = new Headers(event.headers || {});
228
+ const url = `http://actor${event.path}`;
229
+ const request = new Request(url, {
230
+ method: event.method,
231
+ headers,
232
+ body: body || undefined,
233
+ });
234
+
235
+ Promise.resolve(
236
+ config.fetch(handle, event.actorId, gatewayId, requestId, request),
237
+ ).then(
238
+ async (response) => {
239
+ if (handle._raw && response) {
240
+ const respHeaders = {};
241
+ if (response.headers) {
242
+ response.headers.forEach((value, key) => {
243
+ respHeaders[key] = value;
244
+ });
245
+ }
246
+ const respBody = response.body
247
+ ? Buffer.from(await response.arrayBuffer()).toString("base64")
248
+ : undefined;
249
+ handle._raw.respondCallback(event.responseId, {
250
+ status: response.status || 200,
251
+ headers: respHeaders,
252
+ body: respBody,
253
+ });
254
+ }
255
+ },
256
+ (err) => {
257
+ console.error("fetch callback error:", err);
258
+ if (handle._raw) {
259
+ handle._raw.respondCallback(event.responseId, {
260
+ status: 500,
261
+ headers: { "content-type": "text/plain" },
262
+ body: Buffer.from(String(err)).toString("base64"),
263
+ });
264
+ }
265
+ },
266
+ );
267
+ break;
268
+ }
269
+ case "websocket_open": {
270
+ if (config.websocket) {
271
+ const messageId = Buffer.from(event.messageId);
272
+ const gatewayId = messageId.subarray(0, 4);
273
+ const requestId = messageId.subarray(4, 8);
274
+ const wsIdHex = gatewayId.toString("hex") + requestId.toString("hex");
275
+
276
+ const headers = new Headers(event.headers || {});
277
+ headers.set("Upgrade", "websocket");
278
+ headers.set("Connection", "Upgrade");
279
+ const url = `http://actor${event.path}`;
280
+ const request = new Request(url, {
281
+ method: "GET",
282
+ headers,
283
+ });
284
+
285
+ // Create a WebSocket-like object backed by EventTarget.
286
+ // The EngineActorDriver calls addEventListener on this.
287
+ // Events are dispatched when native websocket_message/close events arrive.
288
+ const target = new EventTarget();
289
+ const OPEN = 1;
290
+ const CLOSED = 3;
291
+ const ws = Object.create(target, {
292
+ readyState: { value: OPEN, writable: true },
293
+ OPEN: { value: OPEN },
294
+ CLOSED: { value: CLOSED },
295
+ send: {
296
+ value: (data) => {
297
+ if (handle._raw) {
298
+ const isBinary =
299
+ data instanceof ArrayBuffer || ArrayBuffer.isView(data);
300
+ const bytes = isBinary
301
+ ? Buffer.from(data instanceof ArrayBuffer ? data : data.buffer, data instanceof ArrayBuffer ? 0 : data.byteOffset, data instanceof ArrayBuffer ? data.byteLength : data.byteLength)
302
+ : Buffer.from(String(data));
303
+ handle._raw.sendWsMessage(gatewayId, requestId, bytes, isBinary);
304
+ }
305
+ }
306
+ },
307
+ close: {
308
+ value: (code, reason) => {
309
+ ws.readyState = CLOSED;
310
+ if (handle._raw) {
311
+ handle._raw.closeWebsocket(
312
+ gatewayId,
313
+ requestId,
314
+ code != null ? code : undefined,
315
+ reason != null ? String(reason) : undefined,
316
+ );
317
+ }
318
+ }
319
+ },
320
+ addEventListener: { value: target.addEventListener.bind(target) },
321
+ removeEventListener: { value: target.removeEventListener.bind(target) },
322
+ dispatchEvent: { value: target.dispatchEvent.bind(target) },
323
+ });
324
+
325
+ // Store the ws object so websocket_message/close events can dispatch to it
326
+ if (!handle._wsMap) handle._wsMap = new Map();
327
+ handle._wsMap.set(wsIdHex, ws);
328
+
329
+ const canHibernate = config.hibernatableWebSocket
330
+ ? config.hibernatableWebSocket.canHibernate(
331
+ event.actorId,
332
+ gatewayId,
333
+ requestId,
334
+ request,
335
+ )
336
+ : false;
337
+
338
+ console.log("[wrapper] websocket_open actorId:", event.actorId?.slice(0, 12), "path:", event.path);
339
+ Promise.resolve(
340
+ config.websocket(
341
+ handle,
342
+ event.actorId,
343
+ ws,
344
+ gatewayId,
345
+ requestId,
346
+ request,
347
+ event.path,
348
+ event.headers || {},
349
+ canHibernate,
350
+ false,
351
+ ),
352
+ ).then(() => {
353
+ console.log("[wrapper] websocket callback resolved, dispatching open event");
354
+ ws.dispatchEvent(new Event("open"));
355
+ console.log("[wrapper] open event dispatched");
356
+ }).catch((err) => {
357
+ console.error("[wrapper] websocket callback error:", err);
358
+ });
359
+ }
360
+ break;
361
+ }
362
+ case "websocket_message": {
363
+ if (handle._wsMap && event.messageId) {
364
+ const messageId = Buffer.from(event.messageId);
365
+ const gatewayId = messageId.subarray(0, 4);
366
+ const requestId = messageId.subarray(4, 8);
367
+ const wsIdHex = gatewayId.toString("hex") + requestId.toString("hex");
368
+
369
+ const ws = handle._wsMap.get(wsIdHex);
370
+
371
+ if (ws) {
372
+ const data = event.data
373
+ ? (event.binary
374
+ ? Buffer.from(event.data, "base64")
375
+ : Buffer.from(event.data, "base64").toString())
376
+ : "";
377
+ const msgEvent = new MessageEvent("message", { data });
378
+ msgEvent.rivetGatewayId = messageId.subarray(0, 4);
379
+ msgEvent.rivetRequestId = messageId.subarray(4, 8);
380
+ msgEvent.rivetMessageIndex = messageId.readUint16LE(8);
381
+ ws.dispatchEvent(msgEvent);
382
+ }
383
+ }
384
+ break;
385
+ }
386
+ case "websocket_close": {
387
+ if (handle._wsMap && event.messageId) {
388
+ const messageId = Buffer.from(event.messageId);
389
+ const gatewayId = messageId.subarray(0, 4);
390
+ const requestId = messageId.subarray(4, 8);
391
+ const wsIdHex = gatewayId.toString("hex") + requestId.toString("hex");
392
+
393
+ const ws = handle._wsMap.get(wsIdHex);
394
+ if (ws) {
395
+ ws.readyState = 3;
396
+ ws.dispatchEvent(new CloseEvent("close", {
397
+ code: event.code || 1000,
398
+ reason: event.reason || "",
399
+ }));
400
+ handle._wsMap.delete(wsIdHex);
401
+ }
402
+ }
403
+ break;
404
+ }
405
+ case "hibernation_restore":
406
+ case "alarm":
407
+ case "wake":
408
+ break;
409
+ case "shutdown": {
410
+ if (config.onShutdown) {
411
+ config.onShutdown();
412
+ }
413
+ break;
414
+ }
415
+ default:
416
+ console.warn("unknown native event kind:", event.kind);
417
+ }
418
+ }
419
+
420
+ module.exports.startEnvoy = startEnvoy;
421
+ module.exports.startEnvoySync = startEnvoySync;
422
+ module.exports.openDatabaseFromEnvoy = openDatabaseFromEnvoy;