/* * Copyright (c) 2022 EdgerOS Team. * All rights reserved. * * Detailed license information can be found in the LICENSE file. * * Author : 薛强 * Date : 2024-11-20 16:17:18 * LastEditors : 薛强 * LastEditTime : 2024-11-26 18:26:46 */ import type { CallOptions } from './options' export interface IResponseStream { on: ((event: 'data', fn: (item: T) => void) => this) & ((event: 'end', fn: () => void) => this) & ((event: 'status', fn: (status: any) => void) => this) & ((event: 'error', fn: (err: Error) => void) => this) } export interface IRequestStream { write: (item: T) => void end: () => void cancel: () => void } export interface IDuplexStream extends IRequestStream, IResponseStream {} export interface IKVClient { /** * Range gets the keys in the range from the key-value store. */ range: (req: IRangeRequest, options?: CallOptions) => Promise /** * Put puts the given key into the key-value store. * A put request increments the revision of the key-value store * and generates one event in the event history. */ put: (req: IPutRequest, options?: CallOptions) => Promise /** * DeleteRange deletes the given range from the key-value store. * A delete request increments the revision of the key-value store * and generates a delete event in the event history for every deleted key. */ deleteRange: (req: IDeleteRangeRequest, options?: CallOptions) => Promise /** * Txn processes multiple requests in a single transaction. * A txn request increments the revision of the key-value store * and generates events with the same revision for every completed request. * It is not allowed to modify the same key several times within one txn. */ txn: (req: ITxnRequest, options?: CallOptions) => Promise /** * Compact compacts the event history in the etcd key-value store. The key-value * store should be periodically compacted or the event history will continue to grow * indefinitely. */ // compact(req: ICompactionRequest, options?: CallOptions): Promise } export interface IWatchClient { /** * Watch watches for events happening or that have happened. Both input and output * are streams; the input stream is for creating and canceling watchers and the output * stream sends events. One watch RPC can watch on multiple key ranges, streaming events * for several watches at once. The entire event history can be watched starting from the * last compaction revision. */ watch: (options?: CallOptions) => Promise> } export interface ILeaseClient { /** * LeaseGrant creates a lease which expires if the server does not receive a keepAlive * within a given time to live period. All keys attached to the lease will be expired and * deleted if the lease expires. Each expired key generates a delete event in the event history. */ leaseGrant: (req: ILeaseGrantRequest, options?: CallOptions) => Promise /** * LeaseRevoke revokes a lease. All keys attached to the lease will expire and be deleted. */ leaseRevoke: (req: ILeaseRevokeRequest, options?: CallOptions) => Promise /** * LeaseKeepAlive keeps the lease alive by streaming keep alive requests from the client * to the server and streaming keep alive responses from the server to the client. */ leaseKeepAlive: (options?: CallOptions) => Promise> /** * LeaseTimeToLive retrieves lease information. */ leaseTimeToLive: (req: ILeaseTimeToLiveRequest, options?: CallOptions) => Promise /** * LeaseLeases lists all existing leases. */ leaseLeases: (options?: CallOptions) => Promise } export interface IResponseHeader { /** * cluster_id is the ID of the cluster which sent the response. */ cluster_id: string /** * member_id is the ID of the member which sent the response. */ member_id: string /** * revision is the key-value store revision when the request was applied. * For watch progress responses, the header.revision indicates progress. All future events * recieved in this stream are guaranteed to have a higher revision number than the * header.revision number. */ revision: string /** * raft_term is the raft term when the request was applied. */ raft_term: string } export enum SortOrder { /** * default, no sorting */ None = 0, /** * lowest target value first */ Ascend = 1, /** * highest target value first */ Descend = 2, } export enum SortTarget { Key = 0, Version = 1, Create = 2, Mod = 3, Value = 4, } export interface IRangeRequest { /** * key is the first key for the range. If range_end is not given, the request only looks up key. */ key: Buffer /** * range_end is the upper bound on the requested range [key, range_end). * If range_end is '\0', the range is all keys >= key. * If range_end is key plus one (e.g., "aa"+1 == "ab", "a\xff"+1 == "b"), * then the range request gets all keys prefixed with key. * If both key and range_end are '\0', then the range request returns all keys. */ range_end?: Buffer /** * limit is a limit on the number of keys returned for the request. When limit is set to 0, * it is treated as no limit. */ limit?: string | number /** * revision is the point-in-time of the key-value store to use for the range. * If revision is less or equal to zero, the range is over the newest key-value store. * If the revision has been compacted, ErrCompacted is returned as a response. */ revision?: string | number /** * sort_order is the order for returned sorted results. */ sort_order?: SortOrder | keyof typeof SortOrder /** * sort_target is the key-value field to use for sorting. */ sort_target?: SortTarget | keyof typeof SortTarget /** * serializable sets the range request to use serializable member-local reads. * Range requests are linearizable by default; linearizable requests have higher * latency and lower throughput than serializable requests but reflect the current * consensus of the cluster. For better performance, in exchange for possible stale reads, * a serializable range request is served locally without needing to reach consensus * with other nodes in the cluster. */ serializable?: boolean /** * keys_only when set returns only the keys and not the values. */ keys_only?: boolean /** * count_only when set returns only the count of the keys in the range. */ count_only?: boolean /** * min_mod_revision is the lower bound for returned key mod revisions; all keys with * lesser mod revisions will be filtered away. */ min_mod_revision?: string | number /** * max_mod_revision is the upper bound for returned key mod revisions; all keys with * greater mod revisions will be filtered away. */ max_mod_revision?: string | number /** * min_create_revision is the lower bound for returned key create revisions; all keys with * lesser create revisions will be filtered away. */ min_create_revision?: string | number /** * max_create_revision is the upper bound for returned key create revisions; all keys with * greater create revisions will be filtered away. */ max_create_revision?: string | number } export interface IRangeResponse { header: IResponseHeader /** * kvs is the list of key-value pairs matched by the range request. * kvs is empty when count is requested. */ kvs: IKeyValue[] /** * more indicates if there are more keys to return in the requested range. */ more: boolean /** * count is set to the number of keys within the range when requested. */ count: string } export interface IPutRequest { /** * key is the key, in bytes, to put into the key-value store. */ key: Buffer /** * value is the value, in bytes, to associate with the key in the key-value store. */ value?: Buffer /** * lease is the lease ID to associate with the key in the key-value store. A lease * value of 0 indicates no lease. */ lease?: string | number /** * If prev_kv is set, etcd gets the previous key-value pair before changing it. * The previous key-value pair will be returned in the put response. */ prev_kv?: boolean /** * If ignore_value is set, etcd updates the key using its current value. * Returns an error if the key does not exist. */ ignore_value?: boolean /** * If ignore_lease is set, etcd updates the key using its current lease. * Returns an error if the key does not exist. */ ignore_lease?: boolean } export interface IPutResponse { header: IResponseHeader /** * if prev_kv is set in the request, the previous key-value pair will be returned. */ prev_kv: IKeyValue } export interface IDeleteRangeRequest { /** * key is the first key to delete in the range. */ key: Buffer /** * range_end is the key following the last key to delete for the range [key, range_end). * If range_end is not given, the range is defined to contain only the key argument. * If range_end is one bit larger than the given key, then the range is all the keys * with the prefix (the given key). * If range_end is '\0', the range is all keys greater than or equal to the key argument. */ range_end?: Buffer /** * If prev_kv is set, etcd gets the previous key-value pairs before deleting it. * The previous key-value pairs will be returned in the delete response. */ prev_kv?: boolean } export interface IDeleteRangeResponse { header: IResponseHeader | undefined /** * deleted is the number of keys deleted by the delete range request. */ deleted: string /** * if prev_kv is set in the request, the previous key-value pairs will be returned. */ prev_kvs: IKeyValue[] } export interface IRequestOp { request_range?: IRangeRequest request_put?: IPutRequest request_delete_range?: IDeleteRangeRequest request_txn?: ITxnRequest } export interface IResponseOp { response_range: IRangeResponse response_put: IPutResponse response_delete_range: IDeleteRangeResponse response_txn: ITxnResponse } export enum CompareResult { Equal = 0, Greater = 1, Less = 2, NotEqual = 3, } export enum CompareTarget { Version = 0, Create = 1, Mod = 2, Value = 3, Lease = 4, } export interface ICompare { /** * result is logical comparison operation for this comparison. */ result?: CompareResult | keyof typeof CompareResult /** * target is the key-value field to inspect for the comparison. */ target?: CompareTarget | keyof typeof CompareTarget /** * key is the subject key for the comparison operation. */ key?: Buffer /** * version is the version of the given key */ version?: string | number /** * create_revision is the creation revision of the given key */ create_revision?: string | number /** * mod_revision is the last modified revision of the given key. */ mod_revision?: string | number /** * value is the value of the given key, in bytes. */ value?: Buffer /** * lease is the lease id of the given key. */ lease?: string | number /** * range_end compares the given target to all keys in the range [key, range_end). * See RangeRequest for more details on key ranges. */ range_end?: Buffer } export interface ITxnRequest { /** * compare is a list of predicates representing a conjunction of terms. * If the comparisons succeed, then the success requests will be processed in order, * and the response will contain their respective responses in order. * If the comparisons fail, then the failure requests will be processed in order, * and the response will contain their respective responses in order. */ compare?: ICompare[] /** * success is a list of requests which will be applied when compare evaluates to true. */ success?: IRequestOp[] /** * failure is a list of requests which will be applied when compare evaluates to false. */ failure?: IRequestOp[] } export interface ITxnResponse { header: IResponseHeader /** * succeeded is set to true if the compare evaluated to true or false otherwise. */ succeeded: boolean /** * responses is a list of responses corresponding to the results from applying * success if succeeded is true or failure if succeeded is false. */ responses: IResponseOp[] } export interface ICompactionRequest { /** * revision is the key-value store revision for the compaction operation. */ revision?: string | number /** * physical is set so the RPC will wait until the compaction is physically * applied to the local database such that compacted entries are totally * removed from the backend database. */ physical?: boolean } export interface ICompactionResponse { header: IResponseHeader } // eslint-disable-next-line @typescript-eslint/no-empty-object-type -- todo export interface IHashRequest {} export interface IHashKVRequest { /** * revision is the key-value store revision for the hash operation. */ revision?: string | number } export interface IHashKVResponse { header: IResponseHeader /** * hash is the hash value computed from the responding member's MVCC keys up to a given revision. */ hash: string /** * compact_revision is the compacted revision of key-value store when hash begins. */ compact_revision: string } export interface IHashResponse { header: IResponseHeader /** * hash is the hash value computed from the responding member's KV's backend. */ hash: string } // eslint-disable-next-line @typescript-eslint/no-empty-object-type -- todo export interface ISnapshotRequest {} export interface ISnapshotResponse { /** * header has the current key-value store information. The first header in the snapshot * stream indicates the point in time of the snapshot. */ header: IResponseHeader /** * remaining_bytes is the number of blob bytes to be sent after this message */ remaining_bytes: string /** * blob contains the next chunk of the snapshot in the snapshot stream. */ blob: Buffer } export interface IWatchRequest { create_request?: IWatchCreateRequest cancel_request?: IWatchCancelRequest progress_request?: IWatchProgressRequest } export enum FilterType { /** * filter out put event. */ Noput = 0, /** * filter out delete event. */ Nodelete = 1, } export interface IWatchCreateRequest { /** * key is the key to register for watching. */ key?: Buffer /** * range_end is the end of the range [key, range_end) to watch. If range_end is not given, * only the key argument is watched. If range_end is equal to '\0', all keys greater than * or equal to the key argument are watched. * If the range_end is one bit larger than the given key, * then all keys with the prefix (the given key) will be watched. */ range_end?: Buffer /** * start_revision is an optional revision to watch from (inclusive). No start_revision is "now". */ start_revision?: string | number /** * progress_notify is set so that the etcd server will periodically send a WatchResponse with * no events to the new watcher if there are no recent events. It is useful when clients * wish to recover a disconnected watcher starting from a recent known revision. * The etcd server may decide how often it will send notifications based on current load. */ progress_notify?: boolean /** * filters filter the events at server side before it sends back to the watcher. */ filters?: Array /** * If prev_kv is set, created watcher gets the previous KV before the event happens. * If the previous KV is already compacted, nothing will be returned. */ prev_kv?: boolean /** * If watch_id is provided and non-zero, it will be assigned to this watcher. * Since creating a watcher in etcd is not a synchronous operation, * this can be used ensure that ordering is correct when creating multiple * watchers on the same stream. Creating a watcher with an ID already in * use on the stream will cause an error to be returned. */ watch_id?: string | number /** * fragment enables splitting large revisions into multiple watch responses. */ fragment?: boolean } export interface IWatchCancelRequest { /** * watch_id is the watcher id to cancel so that no more events are transmitted. */ watch_id?: string | number } // eslint-disable-next-line @typescript-eslint/no-empty-object-type -- todo export interface IWatchProgressRequest {} export interface IWatchResponse { header: IResponseHeader /** * watch_id is the ID of the watcher that corresponds to the response. */ watch_id: string /** * created is set to true if the response is for a create watch request. * The client should record the watch_id and expect to receive events for * the created watcher from the same stream. * All events sent to the created watcher will attach with the same watch_id. */ created: boolean /** * canceled is set to true if the response is for a cancel watch request. * No further events will be sent to the canceled watcher. */ canceled: boolean /** * compact_revision is set to the minimum index if a watcher tries to watch * at a compacted index. * * This happens when creating a watcher at a compacted revision or the watcher cannot * catch up with the progress of the key-value store. * * The client should treat the watcher as canceled and should not try to create any * watcher with the same start_revision again. */ compact_revision: string /** * cancel_reason indicates the reason for canceling the watcher. */ cancel_reason: string /** * framgment is true if large watch response was split over multiple responses. */ fragment: boolean events: IEvent[] } export interface ILeaseGrantRequest { /** * TTL is the advisory time-to-live in seconds. Expired lease will return -1. */ TTL?: string | number /** * ID is the requested ID for the lease. If ID is set to 0, the lessor chooses an ID. */ ID?: string | number } export interface ILeaseGrantResponse { header: IResponseHeader /** * ID is the lease ID for the granted lease. */ ID: string /** * TTL is the server chosen lease time-to-live in seconds. */ TTL: string error: string } export interface ILeaseRevokeRequest { /** * ID is the lease ID to revoke. When the ID is revoked, all associated keys will be deleted. */ ID?: string | number } export interface ILeaseRevokeResponse { header: IResponseHeader } export interface ILeaseCheckpoint { /** * ID is the lease ID to checkpoint. */ ID?: string | number /** * Remaining_TTL is the remaining time until expiry of the lease. */ remaining_TTL?: string | number } export interface ILeaseCheckpointRequest { checkpoints?: ILeaseCheckpoint[] } export interface ILeaseCheckpointResponse { header?: IResponseHeader } export interface ILeaseKeepAliveRequest { /** * ID is the lease ID for the lease to keep alive. */ ID?: string | number } export interface ILeaseKeepAliveResponse { header: IResponseHeader /** * ID is the lease ID from the keep alive request. */ ID: string /** * TTL is the new time-to-live for the lease. */ TTL: string } export interface ILeaseTimeToLiveRequest { /** * ID is the lease ID for the lease. */ ID?: string | number /** * keys is true to query all the keys attached to this lease. */ keys?: boolean } export interface ILeaseTimeToLiveResponse { header: IResponseHeader /** * ID is the lease ID from the keep alive request. */ ID: string /** * TTL is the remaining TTL in seconds for the lease; the lease will expire in under TTL+1 seconds. */ TTL: string /** * GrantedTTL is the initial granted time in seconds upon lease creation/renewal. */ grantedTTL: string /** * Keys is the list of keys attached to this lease. */ keys: Buffer[] } // eslint-disable-next-line @typescript-eslint/no-empty-object-type -- todo export interface ILeaseLeasesRequest {} export interface ILeaseStatus { ID: string } export interface ILeaseLeasesResponse { header: IResponseHeader leases: ILeaseStatus[] } export interface IKeyValue { /** * key is the first key for the range. If range_end is not given, the request only looks up key. */ key: Buffer create_revision: string mod_revision: string /** * version is the version of the given key */ version: string /** * If ignore_value is set, etcd updates the key using its current value. * Returns an error if the key does not exist. */ value: Buffer /** * If ignore_lease is set, etcd updates the key using its current lease. * Returns an error if the key does not exist. */ lease: string } export enum EventType { Put = 0, Delete = 1, } export interface IEvent { type: keyof typeof EventType /** * if prev_kv is set in the request, the previous key-value pair will be returned. */ kv: IKeyValue /** * If prev_kv is set, etcd gets the previous key-value pairs before deleting it. * The previous key-value pairs will be returned in the delete response. */ prev_kv?: IKeyValue } export interface IServices { KV: IKVClient Watch: IWatchClient Lease: ILeaseClient } export type CallContext = | { service: 'KV', method: 'range', isStream: false, params: IRangeRequest } | { service: 'KV', method: 'put', isStream: false, params: IPutRequest } | { service: 'KV', method: 'deleteRange', isStream: false, params: IDeleteRangeRequest } | { service: 'KV', method: 'txn', isStream: false, params: ITxnRequest } // | { service: 'KV'; method: 'compact'; isStream: false; params: ICompactionRequest } | { service: 'Watch', method: 'watch', isStream: true } | { service: 'Lease', method: 'leaseGrant', isStream: false, params: ILeaseGrantRequest } | { service: 'Lease', method: 'leaseRevoke', isStream: false, params: ILeaseRevokeRequest } | { service: 'Lease', method: 'leaseKeepAlive', isStream: true } | { service: 'Lease', method: 'leaseTimeToLive', isStream: false, params: ILeaseTimeToLiveRequest } | { service: 'Lease', method: 'leaseLeases', isStream: false } export type CallMethod = CallContext['method'] export type CallService = CallContext['service']