UNPKG

@deno/kv

Version:

A Deno KV client library optimized for Node.js.

159 lines (158 loc) 6.49 kB
"use strict"; // Copyright 2023 the Deno authors. All rights reserved. MIT license. Object.defineProperty(exports, "__esModule", { value: true }); exports.isNapiInterface = exports.NAPI_FUNCTIONS = exports.makeNapiBasedService = void 0; const check_js_1 = require("./check.js"); const kv_connect_api_js_1 = require("./kv_connect_api.js"); const kv_key_js_1 = require("./kv_key.js"); const Watch_js_1 = require("./proto/messages/com/deno/kv/datapath/Watch.js"); const WatchOutput_js_1 = require("./proto/messages/com/deno/kv/datapath/WatchOutput.js"); const proto_based_js_1 = require("./proto_based.js"); const unraw_watch_stream_js_1 = require("./unraw_watch_stream.js"); /** * Return a KVService that creates KV instances backed by a native Node NAPI interface. */ function makeNapiBasedService(opts) { return { openKv: (v) => Promise.resolve(NapiBasedKv.of(v, opts)), }; } exports.makeNapiBasedService = makeNapiBasedService; exports.NAPI_FUNCTIONS = [ "open", "close", "snapshotRead", "atomicWrite", "dequeueNextMessage", "finishMessage", "startWatch", "dequeueNextWatchMessage", "endWatch", ]; function isNapiInterface(obj) { return (0, check_js_1.isRecord)(obj) && exports.NAPI_FUNCTIONS.every((v) => typeof obj[v] === "function"); } exports.isNapiInterface = isNapiInterface; // // deno-lint-ignore no-explicit-any const DEFAULT_NAPI_INTERFACE = require('./_napi_index.cjs'); class NapiBasedKv extends proto_based_js_1.ProtoBasedKv { constructor(debug, napi, dbId, decodeV8, encodeV8) { super(debug, decodeV8, encodeV8); Object.defineProperty(this, "napi", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "dbId", { enumerable: true, configurable: true, writable: true, value: void 0 }); this.napi = napi; this.dbId = dbId; } static of(url, opts) { (0, check_js_1.checkOptionalString)("url", url); (0, check_js_1.checkRecord)("opts", opts); (0, check_js_1.checkOptionalBoolean)("opts.debug", opts.debug); (0, check_js_1.checkOptionalObject)("opts.napi", opts.napi); (0, check_js_1.checkOptionalFunction)("opts.decodeV8", opts.decodeV8); (0, check_js_1.checkOptionalFunction)("opts.encodeV8", opts.encodeV8); (0, check_js_1.checkOptionalBoolean)("opts.inMemory", opts.inMemory); const { debug = false, napi = DEFAULT_NAPI_INTERFACE, decodeV8, encodeV8, inMemory, } = opts; if (typeof url !== "string" || /^https?:\/\//i.test(url)) { throw new Error(`Invalid path: ${url}`); } if (napi === undefined) { throw new Error(`No default napi interface, provide one via the 'napi' option.`); } const dbId = napi.open(url, inMemory, debug); return new NapiBasedKv(debug, napi, dbId, decodeV8, encodeV8); } async listenQueue_(handler) { const { napi, dbId, decodeV8, debug } = this; while (true) { if (debug) console.log(`listenQueue_: before dequeueNextMessage`); const result = await napi.dequeueNextMessage(dbId, debug); if (result === undefined) return; const { bytes, messageId } = result; const value = decodeV8(bytes); if (debug) console.log(`listenQueue_: after value ${value}`); try { await Promise.resolve(handler(value)); await napi.finishMessage(dbId, messageId, true, debug); } catch (e) { if (debug) console.log(`listenQueue_: handler failed ${e.stack || e}`); await napi.finishMessage(dbId, messageId, false, debug); } } } close_() { const { napi, dbId, debug } = this; napi.close(dbId, debug); } async snapshotRead(req, _consistency) { const { napi, dbId, debug } = this; const res = await napi.snapshotRead(dbId, (0, kv_connect_api_js_1.encodeSnapshotRead)(req), debug); return (0, kv_connect_api_js_1.decodeSnapshotReadOutput)(res); } async atomicWrite(req) { const { napi, dbId, debug } = this; const res = await napi.atomicWrite(dbId, (0, kv_connect_api_js_1.encodeAtomicWrite)(req), debug); return (0, kv_connect_api_js_1.decodeAtomicWriteOutput)(res); } watch_(keys, raw) { const { napi, dbId, debug, decodeV8 } = this; const { startWatch, dequeueNextWatchMessage, endWatch } = napi; if (startWatch === undefined || dequeueNextWatchMessage === undefined || endWatch === undefined) { throw new Error("watch: not implemented"); } const watch = { keys: keys.map((v) => ({ key: (0, kv_key_js_1.packKey)(v) })), }; let watchId; let ended = false; const endWatchIfNecessary = () => { if (watchId !== undefined && !ended) endWatch(dbId, watchId, debug); ended = true; }; const cache = new proto_based_js_1.WatchCache(decodeV8, keys); const rawStream = new ReadableStream({ async pull(controller) { if (watchId === undefined) { watchId = await startWatch(dbId, (0, Watch_js_1.encodeBinary)(watch), debug); } const watchOutputBytes = await dequeueNextWatchMessage(dbId, watchId, debug); if (watchOutputBytes === undefined) { endWatchIfNecessary(); controller.close(); return; } const watchOutput = (0, WatchOutput_js_1.decodeBinary)(watchOutputBytes); const { status, keys: outputKeys } = watchOutput; if (status !== "SR_SUCCESS") { throw new Error(`Unexpected status: ${status}`); } const entries = cache.processOutputKeys(outputKeys); controller.enqueue(entries); }, cancel() { endWatchIfNecessary(); }, }); return raw ? rawStream : (0, unraw_watch_stream_js_1.makeUnrawWatchStream)(rawStream, endWatchIfNecessary); } }