@rivetkit/rivetkit-native 0.0.0-main.31c444e
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 +92 -0
- package/index.js +319 -0
- package/package.json +63 -0
- package/scripts/build.mjs +66 -0
- package/wrapper.d.ts +144 -0
- package/wrapper.js +577 -0
package/index.d.ts
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
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
|
+
/**
|
|
31
|
+
* Log level for the Rust tracing subscriber (e.g. "trace", "debug", "info", "warn", "error").
|
|
32
|
+
* Falls back to RIVET_LOG_LEVEL, then LOG_LEVEL, then RUST_LOG env vars. Defaults to "warn".
|
|
33
|
+
*/
|
|
34
|
+
logLevel?: string
|
|
35
|
+
}
|
|
36
|
+
/** Options for KV list operations. */
|
|
37
|
+
export interface JsKvListOptions {
|
|
38
|
+
reverse?: boolean
|
|
39
|
+
limit?: number
|
|
40
|
+
}
|
|
41
|
+
/** A key-value entry returned from KV list operations. */
|
|
42
|
+
export interface JsKvEntry {
|
|
43
|
+
key: Buffer
|
|
44
|
+
value: Buffer
|
|
45
|
+
}
|
|
46
|
+
/** A single hibernating request entry. */
|
|
47
|
+
export interface HibernatingRequestEntry {
|
|
48
|
+
gatewayId: Buffer
|
|
49
|
+
requestId: Buffer
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Start the native envoy client synchronously.
|
|
53
|
+
*
|
|
54
|
+
* Returns a handle immediately. The caller must call `await handle.started()`
|
|
55
|
+
* to wait for the connection to be ready.
|
|
56
|
+
*/
|
|
57
|
+
export declare function startEnvoySyncJs(config: JsEnvoyConfig, eventCallback: (event: any) => void): JsEnvoyHandle
|
|
58
|
+
/** Start the native envoy client asynchronously. */
|
|
59
|
+
export declare function startEnvoyJs(config: JsEnvoyConfig, eventCallback: (event: any) => void): JsEnvoyHandle
|
|
60
|
+
/** Native SQLite database handle exposed to JavaScript. */
|
|
61
|
+
export declare class JsNativeDatabase {
|
|
62
|
+
run(sql: string, params?: Array<JsBindParam> | undefined | null): Promise<ExecuteResult>
|
|
63
|
+
query(sql: string, params?: Array<JsBindParam> | undefined | null): Promise<QueryResult>
|
|
64
|
+
exec(sql: string): Promise<QueryResult>
|
|
65
|
+
close(): Promise<void>
|
|
66
|
+
}
|
|
67
|
+
/** Native envoy handle exposed to JavaScript via N-API. */
|
|
68
|
+
export declare class JsEnvoyHandle {
|
|
69
|
+
started(): Promise<void>
|
|
70
|
+
shutdown(immediate: boolean): void
|
|
71
|
+
get envoyKey(): string
|
|
72
|
+
sleepActor(actorId: string, generation?: number | undefined | null): void
|
|
73
|
+
stopActor(actorId: string, generation?: number | undefined | null, error?: string | undefined | null): void
|
|
74
|
+
destroyActor(actorId: string, generation?: number | undefined | null): void
|
|
75
|
+
setAlarm(actorId: string, alarmTs?: number | undefined | null, generation?: number | undefined | null): void
|
|
76
|
+
kvGet(actorId: string, keys: Array<Buffer>): Promise<Array<Buffer | undefined | null>>
|
|
77
|
+
kvPut(actorId: string, entries: Array<JsKvEntry>): Promise<void>
|
|
78
|
+
kvDelete(actorId: string, keys: Array<Buffer>): Promise<void>
|
|
79
|
+
kvDeleteRange(actorId: string, start: Buffer, end: Buffer): Promise<void>
|
|
80
|
+
kvListAll(actorId: string, options?: JsKvListOptions | undefined | null): Promise<Array<JsKvEntry>>
|
|
81
|
+
kvListRange(actorId: string, start: Buffer, end: Buffer, exclusive?: boolean | undefined | null, options?: JsKvListOptions | undefined | null): Promise<Array<JsKvEntry>>
|
|
82
|
+
kvListPrefix(actorId: string, prefix: Buffer, options?: JsKvListOptions | undefined | null): Promise<Array<JsKvEntry>>
|
|
83
|
+
kvDrop(actorId: string): Promise<void>
|
|
84
|
+
restoreHibernatingRequests(actorId: string, requests: Array<HibernatingRequestEntry>): void
|
|
85
|
+
sendHibernatableWebSocketMessageAck(gatewayId: Buffer, requestId: Buffer, clientMessageIndex: number): void
|
|
86
|
+
/** Send a message on an open WebSocket connection identified by messageIdHex. */
|
|
87
|
+
sendWsMessage(gatewayId: Buffer, requestId: Buffer, data: Buffer, binary: boolean): Promise<void>
|
|
88
|
+
/** Close an open WebSocket connection. */
|
|
89
|
+
closeWebsocket(gatewayId: Buffer, requestId: Buffer, code?: number | undefined | null, reason?: string | undefined | null): Promise<void>
|
|
90
|
+
startServerless(payload: Buffer): Promise<void>
|
|
91
|
+
respondCallback(responseId: string, data: any): Promise<void>
|
|
92
|
+
}
|
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": "0.0.0-main.31c444e",
|
|
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": "0.0.0-main.31c444e"
|
|
53
|
+
},
|
|
54
|
+
"optionalDependencies": {
|
|
55
|
+
"@rivetkit/rivetkit-native-darwin-arm64": "0.0.0-main.31c444e",
|
|
56
|
+
"@rivetkit/rivetkit-native-darwin-x64": "0.0.0-main.31c444e",
|
|
57
|
+
"@rivetkit/rivetkit-native-linux-arm64-gnu": "0.0.0-main.31c444e",
|
|
58
|
+
"@rivetkit/rivetkit-native-linux-arm64-musl": "0.0.0-main.31c444e",
|
|
59
|
+
"@rivetkit/rivetkit-native-linux-x64-gnu": "0.0.0-main.31c444e",
|
|
60
|
+
"@rivetkit/rivetkit-native-linux-x64-musl": "0.0.0-main.31c444e",
|
|
61
|
+
"@rivetkit/rivetkit-native-win32-x64-msvc": "0.0.0-main.31c444e"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
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
|
+
// Explicit skip for environments that don't need the native binary (e.g.
|
|
27
|
+
// Docker engine-frontend build which only consumes TypeScript types).
|
|
28
|
+
if (!force && process.env.SKIP_NAPI_BUILD === "1") {
|
|
29
|
+
console.log(
|
|
30
|
+
"[rivetkit-native/build] SKIP_NAPI_BUILD=1 — skipping napi build",
|
|
31
|
+
);
|
|
32
|
+
process.exit(0);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function hasPrebuiltArtifact() {
|
|
36
|
+
// Check for root-level .node files.
|
|
37
|
+
const rootFiles = readdirSync(packageDir);
|
|
38
|
+
if (rootFiles.some((f) => f.endsWith(".node"))) {
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
// Check for any npm/<platform>/*.node files.
|
|
42
|
+
const npmDir = join(packageDir, "npm");
|
|
43
|
+
if (existsSync(npmDir) && statSync(npmDir).isDirectory()) {
|
|
44
|
+
for (const entry of readdirSync(npmDir)) {
|
|
45
|
+
const platDir = join(npmDir, entry);
|
|
46
|
+
if (!statSync(platDir).isDirectory()) continue;
|
|
47
|
+
const files = readdirSync(platDir);
|
|
48
|
+
if (files.some((f) => f.endsWith(".node"))) {
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (!force && hasPrebuiltArtifact()) {
|
|
57
|
+
console.log(
|
|
58
|
+
"[rivetkit-native/build] prebuilt .node artifact found — skipping napi build",
|
|
59
|
+
);
|
|
60
|
+
console.log("[rivetkit-native/build] use --force to rebuild from source");
|
|
61
|
+
process.exit(0);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const cmd = ["napi", "build", "--platform", ...extraFlags].join(" ");
|
|
65
|
+
console.log(`[rivetkit-native/build] running: ${cmd}`);
|
|
66
|
+
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,577 @@
|
|
|
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
|
+
// CloseEvent was added to Node.js in v22. Polyfill for older versions.
|
|
12
|
+
if (typeof CloseEvent === "undefined") {
|
|
13
|
+
global.CloseEvent = class CloseEvent extends Event {
|
|
14
|
+
constructor(type, init = {}) {
|
|
15
|
+
super(type);
|
|
16
|
+
this.code = init.code ?? 0;
|
|
17
|
+
this.reason = init.reason ?? "";
|
|
18
|
+
this.wasClean = init.wasClean ?? false;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Re-export protocol for consumers that need protocol types at runtime
|
|
24
|
+
let _protocol;
|
|
25
|
+
try {
|
|
26
|
+
_protocol = require("@rivetkit/engine-envoy-protocol");
|
|
27
|
+
} catch {
|
|
28
|
+
_protocol = {};
|
|
29
|
+
}
|
|
30
|
+
module.exports.protocol = _protocol;
|
|
31
|
+
module.exports.utils = {};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Create a wrapped EnvoyHandle that matches the TS EnvoyHandle interface.
|
|
35
|
+
*/
|
|
36
|
+
function wrapHandle(jsHandle) {
|
|
37
|
+
const handle = {
|
|
38
|
+
started: () => jsHandle.started(),
|
|
39
|
+
shutdown: (immediate) => jsHandle.shutdown(immediate ?? false),
|
|
40
|
+
getProtocolMetadata: () => undefined,
|
|
41
|
+
getEnvoyKey: () => jsHandle.envoyKey,
|
|
42
|
+
getActor: (_actorId, _generation) => undefined,
|
|
43
|
+
sleepActor: (actorId, generation) =>
|
|
44
|
+
jsHandle.sleepActor(actorId, generation ?? null),
|
|
45
|
+
stopActor: (actorId, generation, error) =>
|
|
46
|
+
jsHandle.stopActor(actorId, generation ?? null, error ?? null),
|
|
47
|
+
destroyActor: (actorId, generation) =>
|
|
48
|
+
jsHandle.destroyActor(actorId, generation ?? null),
|
|
49
|
+
setAlarm: (actorId, alarmTs, generation) =>
|
|
50
|
+
jsHandle.setAlarm(actorId, alarmTs ?? null, generation ?? null),
|
|
51
|
+
kvGet: async (actorId, keys) => {
|
|
52
|
+
const bufKeys = keys.map((k) => Buffer.from(k));
|
|
53
|
+
const result = await jsHandle.kvGet(actorId, bufKeys);
|
|
54
|
+
return result.map((v) => (v ? new Uint8Array(v) : null));
|
|
55
|
+
},
|
|
56
|
+
kvPut: async (actorId, entries) => {
|
|
57
|
+
const jsEntries = entries.map(([k, v]) => ({
|
|
58
|
+
key: Buffer.from(k),
|
|
59
|
+
value: Buffer.from(v),
|
|
60
|
+
}));
|
|
61
|
+
return jsHandle.kvPut(actorId, jsEntries);
|
|
62
|
+
},
|
|
63
|
+
kvDelete: async (actorId, keys) => {
|
|
64
|
+
const bufKeys = keys.map((k) => Buffer.from(k));
|
|
65
|
+
return jsHandle.kvDelete(actorId, bufKeys);
|
|
66
|
+
},
|
|
67
|
+
kvDeleteRange: async (actorId, start, end) => {
|
|
68
|
+
return jsHandle.kvDeleteRange(
|
|
69
|
+
actorId,
|
|
70
|
+
Buffer.from(start),
|
|
71
|
+
Buffer.from(end),
|
|
72
|
+
);
|
|
73
|
+
},
|
|
74
|
+
kvListAll: async (actorId, options) => {
|
|
75
|
+
const result = await jsHandle.kvListAll(actorId, options || null);
|
|
76
|
+
return result.map((e) => [new Uint8Array(e.key), new Uint8Array(e.value)]);
|
|
77
|
+
},
|
|
78
|
+
kvListRange: async (actorId, start, end, exclusive, options) => {
|
|
79
|
+
const result = await jsHandle.kvListRange(
|
|
80
|
+
actorId,
|
|
81
|
+
Buffer.from(start),
|
|
82
|
+
Buffer.from(end),
|
|
83
|
+
exclusive,
|
|
84
|
+
options || null,
|
|
85
|
+
);
|
|
86
|
+
return result.map((e) => [new Uint8Array(e.key), new Uint8Array(e.value)]);
|
|
87
|
+
},
|
|
88
|
+
kvListPrefix: async (actorId, prefix, options) => {
|
|
89
|
+
const result = await jsHandle.kvListPrefix(
|
|
90
|
+
actorId,
|
|
91
|
+
Buffer.from(prefix),
|
|
92
|
+
options || null,
|
|
93
|
+
);
|
|
94
|
+
return result.map((e) => [new Uint8Array(e.key), new Uint8Array(e.value)]);
|
|
95
|
+
},
|
|
96
|
+
kvDrop: (actorId) => jsHandle.kvDrop(actorId),
|
|
97
|
+
restoreHibernatingRequests: (actorId, metaEntries) => {
|
|
98
|
+
const requests = (metaEntries || []).map((e) => ({
|
|
99
|
+
gatewayId: Buffer.from(e.gatewayId),
|
|
100
|
+
requestId: Buffer.from(e.requestId),
|
|
101
|
+
}));
|
|
102
|
+
jsHandle.restoreHibernatingRequests(actorId, requests);
|
|
103
|
+
},
|
|
104
|
+
sendHibernatableWebSocketMessageAck: (
|
|
105
|
+
gatewayId,
|
|
106
|
+
requestId,
|
|
107
|
+
clientMessageIndex,
|
|
108
|
+
) =>
|
|
109
|
+
jsHandle.sendHibernatableWebSocketMessageAck(
|
|
110
|
+
Buffer.from(gatewayId),
|
|
111
|
+
Buffer.from(requestId),
|
|
112
|
+
clientMessageIndex,
|
|
113
|
+
),
|
|
114
|
+
startServerlessActor: async (payload) =>
|
|
115
|
+
await jsHandle.startServerless(Buffer.from(payload)),
|
|
116
|
+
// Internal: expose raw handle for openDatabaseFromEnvoy
|
|
117
|
+
_raw: jsHandle,
|
|
118
|
+
};
|
|
119
|
+
return handle;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Start the native envoy synchronously with EnvoyConfig callbacks.
|
|
124
|
+
* Returns a wrapped handle matching the TS EnvoyHandle interface.
|
|
125
|
+
*/
|
|
126
|
+
function startEnvoySync(config) {
|
|
127
|
+
const wrappedHandle = { current: null };
|
|
128
|
+
|
|
129
|
+
const jsHandle = native.startEnvoySyncJs(
|
|
130
|
+
{
|
|
131
|
+
endpoint: config.endpoint,
|
|
132
|
+
token: config.token || "",
|
|
133
|
+
namespace: config.namespace,
|
|
134
|
+
poolName: config.poolName,
|
|
135
|
+
version: config.version,
|
|
136
|
+
metadata: config.metadata || null,
|
|
137
|
+
notGlobal: config.notGlobal,
|
|
138
|
+
},
|
|
139
|
+
(event) => {
|
|
140
|
+
handleEvent(event, config, wrappedHandle);
|
|
141
|
+
},
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
const handle = wrapHandle(jsHandle);
|
|
145
|
+
wrappedHandle.current = handle;
|
|
146
|
+
return handle;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Start the native envoy and wait for it to be ready.
|
|
151
|
+
*/
|
|
152
|
+
async function startEnvoy(config) {
|
|
153
|
+
const handle = startEnvoySync(config);
|
|
154
|
+
await handle.started();
|
|
155
|
+
return handle;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Open a native database backed by envoy KV.
|
|
160
|
+
*/
|
|
161
|
+
async function openDatabaseFromEnvoy(handle, actorId) {
|
|
162
|
+
const rawHandle = handle._raw || handle;
|
|
163
|
+
return native.openDatabaseFromEnvoy(rawHandle, actorId);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function isPlainObject(value) {
|
|
167
|
+
return (
|
|
168
|
+
!!value &&
|
|
169
|
+
typeof value === "object" &&
|
|
170
|
+
!Array.isArray(value) &&
|
|
171
|
+
Object.getPrototypeOf(value) === Object.prototype
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function toNativeBinding(value) {
|
|
176
|
+
if (value === null || value === undefined) {
|
|
177
|
+
return { kind: "null" };
|
|
178
|
+
}
|
|
179
|
+
if (typeof value === "bigint") {
|
|
180
|
+
return { kind: "int", intValue: Number(value) };
|
|
181
|
+
}
|
|
182
|
+
if (typeof value === "number") {
|
|
183
|
+
return Number.isInteger(value)
|
|
184
|
+
? { kind: "int", intValue: value }
|
|
185
|
+
: { kind: "float", floatValue: value };
|
|
186
|
+
}
|
|
187
|
+
if (typeof value === "string") {
|
|
188
|
+
return { kind: "text", textValue: value };
|
|
189
|
+
}
|
|
190
|
+
if (value instanceof ArrayBuffer) {
|
|
191
|
+
return { kind: "blob", blobValue: Buffer.from(value) };
|
|
192
|
+
}
|
|
193
|
+
if (ArrayBuffer.isView(value)) {
|
|
194
|
+
return {
|
|
195
|
+
kind: "blob",
|
|
196
|
+
blobValue: Buffer.from(value.buffer, value.byteOffset, value.byteLength),
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
throw new Error(`unsupported sqlite binding type: ${typeof value}`);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function extractNamedSqliteParameters(sql) {
|
|
204
|
+
return [...sql.matchAll(/([:@$][A-Za-z_][A-Za-z0-9_]*)/g)].map(
|
|
205
|
+
(match) => match[1],
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function getNamedSqliteBinding(bindings, name) {
|
|
210
|
+
if (name in bindings) {
|
|
211
|
+
return bindings[name];
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const bareName = name.slice(1);
|
|
215
|
+
if (bareName in bindings) {
|
|
216
|
+
return bindings[bareName];
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
for (const prefix of [":", "@", "$"]) {
|
|
220
|
+
const candidate = `${prefix}${bareName}`;
|
|
221
|
+
if (candidate in bindings) {
|
|
222
|
+
return bindings[candidate];
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return undefined;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function normalizeBindings(sql, args) {
|
|
230
|
+
if (!args || args.length === 0) {
|
|
231
|
+
return [];
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (
|
|
235
|
+
args.length === 1 &&
|
|
236
|
+
isPlainObject(args[0]) &&
|
|
237
|
+
!(args[0] instanceof Uint8Array)
|
|
238
|
+
) {
|
|
239
|
+
const names = extractNamedSqliteParameters(sql);
|
|
240
|
+
if (names.length === 0) {
|
|
241
|
+
throw new Error(
|
|
242
|
+
"native sqlite object bindings require named placeholders in the SQL statement",
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
return names.map((name) => {
|
|
246
|
+
const value = getNamedSqliteBinding(args[0], name);
|
|
247
|
+
if (value === undefined) {
|
|
248
|
+
throw new Error(`missing bind parameter: ${name}`);
|
|
249
|
+
}
|
|
250
|
+
return toNativeBinding(value);
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return args.map(toNativeBinding);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function mapRows(rows, columns) {
|
|
258
|
+
return rows.map((row) => {
|
|
259
|
+
const rowObject = {};
|
|
260
|
+
for (let i = 0; i < columns.length; i++) {
|
|
261
|
+
rowObject[columns[i]] = row[i];
|
|
262
|
+
}
|
|
263
|
+
return rowObject;
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
async function openRawDatabaseFromEnvoy(handle, actorId) {
|
|
268
|
+
const nativeDb = await openDatabaseFromEnvoy(handle, actorId);
|
|
269
|
+
let closed = false;
|
|
270
|
+
|
|
271
|
+
const ensureOpen = () => {
|
|
272
|
+
if (closed) {
|
|
273
|
+
throw new Error("database is closed");
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
return {
|
|
278
|
+
execute: async (query, ...args) => {
|
|
279
|
+
ensureOpen();
|
|
280
|
+
|
|
281
|
+
if (args.length > 0) {
|
|
282
|
+
const bindings = normalizeBindings(query, args);
|
|
283
|
+
const token = query.trimStart().slice(0, 16).toUpperCase();
|
|
284
|
+
const returnsRows =
|
|
285
|
+
token.startsWith("SELECT") ||
|
|
286
|
+
token.startsWith("PRAGMA") ||
|
|
287
|
+
token.startsWith("WITH") ||
|
|
288
|
+
/\bRETURNING\b/i.test(query);
|
|
289
|
+
|
|
290
|
+
if (returnsRows) {
|
|
291
|
+
const result = await nativeDb.query(query, bindings);
|
|
292
|
+
return mapRows(result.rows, result.columns);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
await nativeDb.run(query, bindings);
|
|
296
|
+
return [];
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const result = await nativeDb.exec(query);
|
|
300
|
+
return mapRows(result.rows, result.columns);
|
|
301
|
+
},
|
|
302
|
+
close: async () => {
|
|
303
|
+
if (closed) {
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
closed = true;
|
|
307
|
+
await nativeDb.close();
|
|
308
|
+
},
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Route callback envelopes from the native addon to EnvoyConfig callbacks.
|
|
314
|
+
*/
|
|
315
|
+
function handleEvent(event, config, wrappedHandle) {
|
|
316
|
+
const handle = wrappedHandle.current;
|
|
317
|
+
|
|
318
|
+
switch (event.kind) {
|
|
319
|
+
case "actor_start": {
|
|
320
|
+
const input = event.input ? Buffer.from(event.input, "base64") : undefined;
|
|
321
|
+
const actorConfig = {
|
|
322
|
+
name: event.name,
|
|
323
|
+
key: event.key || undefined,
|
|
324
|
+
createTs: event.createTs,
|
|
325
|
+
input,
|
|
326
|
+
};
|
|
327
|
+
Promise.resolve(
|
|
328
|
+
config.onActorStart(
|
|
329
|
+
handle,
|
|
330
|
+
event.actorId,
|
|
331
|
+
event.generation,
|
|
332
|
+
actorConfig,
|
|
333
|
+
null, // preloadedKv
|
|
334
|
+
),
|
|
335
|
+
).then(
|
|
336
|
+
async () => {
|
|
337
|
+
if (handle._raw) {
|
|
338
|
+
await handle._raw.respondCallback(event.responseId, {});
|
|
339
|
+
}
|
|
340
|
+
},
|
|
341
|
+
async (err) => {
|
|
342
|
+
console.error("onActorStart error:", err);
|
|
343
|
+
if (handle._raw) {
|
|
344
|
+
await handle._raw.respondCallback(event.responseId, {
|
|
345
|
+
error: String(err),
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
},
|
|
349
|
+
);
|
|
350
|
+
break;
|
|
351
|
+
}
|
|
352
|
+
case "actor_stop": {
|
|
353
|
+
Promise.resolve(
|
|
354
|
+
config.onActorStop(
|
|
355
|
+
handle,
|
|
356
|
+
event.actorId,
|
|
357
|
+
event.generation,
|
|
358
|
+
event.reason || "stopped",
|
|
359
|
+
),
|
|
360
|
+
).then(
|
|
361
|
+
async () => {
|
|
362
|
+
if (handle._raw) {
|
|
363
|
+
await handle._raw.respondCallback(event.responseId, {});
|
|
364
|
+
}
|
|
365
|
+
},
|
|
366
|
+
async (err) => {
|
|
367
|
+
console.error("onActorStop error:", err);
|
|
368
|
+
if (handle._raw) {
|
|
369
|
+
await handle._raw.respondCallback(event.responseId, {
|
|
370
|
+
error: String(err),
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
},
|
|
374
|
+
);
|
|
375
|
+
break;
|
|
376
|
+
}
|
|
377
|
+
case "http_request": {
|
|
378
|
+
const body = event.body ? Buffer.from(event.body, "base64") : undefined;
|
|
379
|
+
const messageId = Buffer.from(event.messageId);
|
|
380
|
+
const gatewayId = messageId.subarray(0, 4);
|
|
381
|
+
const requestId = messageId.subarray(4, 8);
|
|
382
|
+
|
|
383
|
+
// Build a Request object matching the TS envoy-client interface
|
|
384
|
+
const headers = new Headers(event.headers || {});
|
|
385
|
+
const url = `http://actor${event.path}`;
|
|
386
|
+
const request = new Request(url, {
|
|
387
|
+
method: event.method,
|
|
388
|
+
headers,
|
|
389
|
+
body: body || undefined,
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
Promise.resolve(
|
|
393
|
+
config.fetch(handle, event.actorId, gatewayId, requestId, request),
|
|
394
|
+
).then(
|
|
395
|
+
async (response) => {
|
|
396
|
+
if (handle._raw && response) {
|
|
397
|
+
const respHeaders = {};
|
|
398
|
+
if (response.headers) {
|
|
399
|
+
response.headers.forEach((value, key) => {
|
|
400
|
+
respHeaders[key] = value;
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
const respBody = response.body
|
|
404
|
+
? Buffer.from(await response.arrayBuffer()).toString("base64")
|
|
405
|
+
: undefined;
|
|
406
|
+
await handle._raw.respondCallback(event.responseId, {
|
|
407
|
+
status: response.status || 200,
|
|
408
|
+
headers: respHeaders,
|
|
409
|
+
body: respBody,
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
},
|
|
413
|
+
async (err) => {
|
|
414
|
+
console.error("fetch callback error:", err);
|
|
415
|
+
if (handle._raw) {
|
|
416
|
+
await handle._raw.respondCallback(event.responseId, {
|
|
417
|
+
status: 500,
|
|
418
|
+
headers: { "content-type": "text/plain" },
|
|
419
|
+
body: Buffer.from(String(err)).toString("base64"),
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
},
|
|
423
|
+
);
|
|
424
|
+
break;
|
|
425
|
+
}
|
|
426
|
+
case "websocket_open": {
|
|
427
|
+
if (config.websocket) {
|
|
428
|
+
const messageId = Buffer.from(event.messageId);
|
|
429
|
+
const gatewayId = messageId.subarray(0, 4);
|
|
430
|
+
const requestId = messageId.subarray(4, 8);
|
|
431
|
+
const wsIdHex = gatewayId.toString("hex") + requestId.toString("hex");
|
|
432
|
+
|
|
433
|
+
const headers = new Headers(event.headers || {});
|
|
434
|
+
headers.set("Upgrade", "websocket");
|
|
435
|
+
headers.set("Connection", "Upgrade");
|
|
436
|
+
const url = `http://actor${event.path}`;
|
|
437
|
+
const request = new Request(url, {
|
|
438
|
+
method: "GET",
|
|
439
|
+
headers,
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
// Create a WebSocket-like object backed by EventTarget.
|
|
443
|
+
// The EngineActorDriver calls addEventListener on this.
|
|
444
|
+
// Events are dispatched when native websocket_message/close events arrive.
|
|
445
|
+
const target = new EventTarget();
|
|
446
|
+
const OPEN = 1;
|
|
447
|
+
const CLOSED = 3;
|
|
448
|
+
const ws = Object.create(target, {
|
|
449
|
+
readyState: { value: OPEN, writable: true },
|
|
450
|
+
OPEN: { value: OPEN },
|
|
451
|
+
CLOSED: { value: CLOSED },
|
|
452
|
+
send: {
|
|
453
|
+
value: (data) => {
|
|
454
|
+
if (handle._raw) {
|
|
455
|
+
const isBinary =
|
|
456
|
+
data instanceof ArrayBuffer || ArrayBuffer.isView(data);
|
|
457
|
+
const bytes = isBinary
|
|
458
|
+
? Buffer.from(data instanceof ArrayBuffer ? data : data.buffer, data instanceof ArrayBuffer ? 0 : data.byteOffset, data instanceof ArrayBuffer ? data.byteLength : data.byteLength)
|
|
459
|
+
: Buffer.from(String(data));
|
|
460
|
+
handle._raw.sendWsMessage(gatewayId, requestId, bytes, isBinary);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
},
|
|
464
|
+
close: {
|
|
465
|
+
value: (code, reason) => {
|
|
466
|
+
ws.readyState = CLOSED;
|
|
467
|
+
if (handle._raw) {
|
|
468
|
+
handle._raw.closeWebsocket(
|
|
469
|
+
gatewayId,
|
|
470
|
+
requestId,
|
|
471
|
+
code != null ? code : undefined,
|
|
472
|
+
reason != null ? String(reason) : undefined,
|
|
473
|
+
);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
},
|
|
477
|
+
addEventListener: { value: target.addEventListener.bind(target) },
|
|
478
|
+
removeEventListener: { value: target.removeEventListener.bind(target) },
|
|
479
|
+
dispatchEvent: { value: target.dispatchEvent.bind(target) },
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
// Store the ws object so websocket_message/close events can dispatch to it
|
|
483
|
+
if (!handle._wsMap) handle._wsMap = new Map();
|
|
484
|
+
handle._wsMap.set(wsIdHex, ws);
|
|
485
|
+
|
|
486
|
+
const canHibernate = config.hibernatableWebSocket
|
|
487
|
+
? config.hibernatableWebSocket.canHibernate(
|
|
488
|
+
event.actorId,
|
|
489
|
+
gatewayId,
|
|
490
|
+
requestId,
|
|
491
|
+
request,
|
|
492
|
+
)
|
|
493
|
+
: false;
|
|
494
|
+
|
|
495
|
+
Promise.resolve(
|
|
496
|
+
config.websocket(
|
|
497
|
+
handle,
|
|
498
|
+
event.actorId,
|
|
499
|
+
ws,
|
|
500
|
+
gatewayId,
|
|
501
|
+
requestId,
|
|
502
|
+
request,
|
|
503
|
+
event.path,
|
|
504
|
+
event.headers || {},
|
|
505
|
+
canHibernate,
|
|
506
|
+
false,
|
|
507
|
+
),
|
|
508
|
+
).then(() => {
|
|
509
|
+
ws.dispatchEvent(new Event("open"));
|
|
510
|
+
}).catch((err) => {
|
|
511
|
+
console.error("[wrapper] websocket callback error:", err);
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
break;
|
|
515
|
+
}
|
|
516
|
+
case "websocket_message": {
|
|
517
|
+
if (handle._wsMap && event.messageId) {
|
|
518
|
+
const messageId = Buffer.from(event.messageId);
|
|
519
|
+
const gatewayId = messageId.subarray(0, 4);
|
|
520
|
+
const requestId = messageId.subarray(4, 8);
|
|
521
|
+
const wsIdHex = gatewayId.toString("hex") + requestId.toString("hex");
|
|
522
|
+
|
|
523
|
+
const ws = handle._wsMap.get(wsIdHex);
|
|
524
|
+
|
|
525
|
+
if (ws) {
|
|
526
|
+
const data = event.data
|
|
527
|
+
? (event.binary
|
|
528
|
+
? Buffer.from(event.data, "base64")
|
|
529
|
+
: Buffer.from(event.data, "base64").toString())
|
|
530
|
+
: "";
|
|
531
|
+
const msgEvent = new MessageEvent("message", { data });
|
|
532
|
+
msgEvent.rivetGatewayId = messageId.subarray(0, 4);
|
|
533
|
+
msgEvent.rivetRequestId = messageId.subarray(4, 8);
|
|
534
|
+
msgEvent.rivetMessageIndex = messageId.readUint16LE(8);
|
|
535
|
+
ws.dispatchEvent(msgEvent);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
break;
|
|
539
|
+
}
|
|
540
|
+
case "websocket_close": {
|
|
541
|
+
if (handle._wsMap && event.messageId) {
|
|
542
|
+
const messageId = Buffer.from(event.messageId);
|
|
543
|
+
const gatewayId = messageId.subarray(0, 4);
|
|
544
|
+
const requestId = messageId.subarray(4, 8);
|
|
545
|
+
const wsIdHex = gatewayId.toString("hex") + requestId.toString("hex");
|
|
546
|
+
|
|
547
|
+
const ws = handle._wsMap.get(wsIdHex);
|
|
548
|
+
if (ws) {
|
|
549
|
+
ws.readyState = 3;
|
|
550
|
+
ws.dispatchEvent(new CloseEvent("close", {
|
|
551
|
+
code: event.code || 1000,
|
|
552
|
+
reason: event.reason || "",
|
|
553
|
+
}));
|
|
554
|
+
handle._wsMap.delete(wsIdHex);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
break;
|
|
558
|
+
}
|
|
559
|
+
case "hibernation_restore":
|
|
560
|
+
case "alarm":
|
|
561
|
+
case "wake":
|
|
562
|
+
break;
|
|
563
|
+
case "shutdown": {
|
|
564
|
+
if (config.onShutdown) {
|
|
565
|
+
config.onShutdown();
|
|
566
|
+
}
|
|
567
|
+
break;
|
|
568
|
+
}
|
|
569
|
+
default:
|
|
570
|
+
console.warn("unknown native event kind:", event.kind);
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
module.exports.startEnvoy = startEnvoy;
|
|
575
|
+
module.exports.startEnvoySync = startEnvoySync;
|
|
576
|
+
module.exports.openDatabaseFromEnvoy = openDatabaseFromEnvoy;
|
|
577
|
+
module.exports.openRawDatabaseFromEnvoy = openRawDatabaseFromEnvoy;
|