From a200b554de85e9aa95f22aaf263f0950c01b1bdb Mon Sep 17 00:00:00 2001
From: tiny-craft <137850705+tiny-craft@users.noreply.github.com>
Date: Tue, 4 Jul 2023 22:56:57 +0800
Subject: [PATCH] feat: add key and prefix reload refactor: separate update db
tree node logic from open database
---
backend/services/connection_service.go | 11 +-
frontend/package-lock.json | 36 +-
frontend/package.json | 12 +-
frontend/package.json.md5 | 2 +-
.../content_value/ContentToolbar.vue | 2 +-
.../components/dialogs/ConnectionDialog.vue | 2 +-
.../components/dialogs/PreferencesDialog.vue | 2 +-
.../src/components/sidebar/BrowserTree.vue | 32 +-
frontend/src/langs/en.json | 1 -
frontend/src/langs/zh-cn.json | 1 -
frontend/src/stores/connections.js | 207 +++--
frontend/src/stores/database.js | 857 ------------------
go.mod | 14 +-
go.sum | 28 +-
14 files changed, 209 insertions(+), 998 deletions(-)
delete mode 100644 frontend/src/stores/database.js
diff --git a/backend/services/connection_service.go b/backend/services/connection_service.go
index 9fe56cd..9a0144b 100644
--- a/backend/services/connection_service.go
+++ b/backend/services/connection_service.go
@@ -307,18 +307,27 @@ func (c *connectionService) parseDBItemInfo(info string) map[string]int {
// OpenDatabase open select database, and list all keys
// @param path contain connection name and db name
func (c *connectionService) OpenDatabase(connName string, db int) (resp types.JSResp) {
+ return c.ScanKeys(connName, db, "*")
+}
+
+// ScanKeys scan all keys below prefix
+func (c *connectionService) ScanKeys(connName string, db int, prefix string) (resp types.JSResp) {
rdb, ctx, err := c.getRedisClient(connName, db)
if err != nil {
resp.Msg = err.Error()
return
}
+ if !strings.HasSuffix(prefix, "*") {
+ prefix += ":*"
+ }
+
//var keys []string
keys := map[string]keyItem{}
var cursor uint64
for {
var loadedKey []string
- loadedKey, cursor, err = rdb.Scan(ctx, cursor, "*", 10000).Result()
+ loadedKey, cursor, err = rdb.Scan(ctx, cursor, prefix, 10000).Result()
if err != nil {
resp.Msg = err.Error()
return
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index c62cbe3..9029b20 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -10,9 +10,9 @@
"dependencies": {
"highlight.js": "^11.8.0",
"lodash": "^4.17.21",
- "pinia": "^2.1.3",
- "sass": "^1.62.1",
- "vue": "^3.2.37",
+ "pinia": "^2.1.4",
+ "sass": "^1.63.6",
+ "vue": "^3.3.4",
"vue-i18n": "^9.2.2"
},
"devDependencies": {
@@ -20,9 +20,9 @@
"naive-ui": "^2.34.4",
"prettier": "^2.8.8",
"unplugin-auto-import": "^0.16.4",
- "unplugin-icons": "^0.16.1",
- "unplugin-vue-components": "^0.25.0",
- "vite": "^4.3.0"
+ "unplugin-icons": "^0.16.3",
+ "unplugin-vue-components": "^0.25.1",
+ "vite": "^4.3.9"
}
},
"node_modules/@antfu/install-pkg": {
@@ -1503,9 +1503,9 @@
}
},
"node_modules/pinia": {
- "version": "2.1.3",
- "resolved": "https://registry.npmmirror.com/pinia/-/pinia-2.1.3.tgz",
- "integrity": "sha512-XNA/z/ye4P5rU1pieVmh0g/hSuDO98/a5UC8oSP0DNdvt6YtetJNHTrXwpwsQuflkGT34qKxAEcp7lSxXNjf/A==",
+ "version": "2.1.4",
+ "resolved": "https://registry.npmmirror.com/pinia/-/pinia-2.1.4.tgz",
+ "integrity": "sha512-vYlnDu+Y/FXxv1ABo1vhjC+IbqvzUdiUC3sfDRrRyY2CQSrqqaa+iiHmqtARFxJVqWQMCJfXx1PBvFs9aJVLXQ==",
"dependencies": {
"@vue/devtools-api": "^6.5.0",
"vue-demi": ">=0.14.5"
@@ -1655,9 +1655,9 @@
}
},
"node_modules/sass": {
- "version": "1.63.2",
- "resolved": "https://registry.npmmirror.com/sass/-/sass-1.63.2.tgz",
- "integrity": "sha512-u56TU0AIFqMtauKl/OJ1AeFsXqRHkgO7nCWmHaDwfxDo9GUMSqBA4NEh6GMuh1CYVM7zuROYtZrHzPc2ixK+ww==",
+ "version": "1.63.6",
+ "resolved": "https://registry.npmmirror.com/sass/-/sass-1.63.6.tgz",
+ "integrity": "sha512-MJuxGMHzaOW7ipp+1KdELtqKbfAWbH7OLIdoSMnVe3EXPMTmxTmlaZDCTsgIpPCs3w99lLo9/zDKkOrJuT5byw==",
"dependencies": {
"chokidar": ">=3.0.0 <4.0.0",
"immutable": "^4.0.0",
@@ -3113,9 +3113,9 @@
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="
},
"pinia": {
- "version": "2.1.3",
- "resolved": "https://registry.npmmirror.com/pinia/-/pinia-2.1.3.tgz",
- "integrity": "sha512-XNA/z/ye4P5rU1pieVmh0g/hSuDO98/a5UC8oSP0DNdvt6YtetJNHTrXwpwsQuflkGT34qKxAEcp7lSxXNjf/A==",
+ "version": "2.1.4",
+ "resolved": "https://registry.npmmirror.com/pinia/-/pinia-2.1.4.tgz",
+ "integrity": "sha512-vYlnDu+Y/FXxv1ABo1vhjC+IbqvzUdiUC3sfDRrRyY2CQSrqqaa+iiHmqtARFxJVqWQMCJfXx1PBvFs9aJVLXQ==",
"requires": {
"@vue/devtools-api": "^6.5.0",
"vue-demi": ">=0.14.5"
@@ -3212,9 +3212,9 @@
}
},
"sass": {
- "version": "1.63.2",
- "resolved": "https://registry.npmmirror.com/sass/-/sass-1.63.2.tgz",
- "integrity": "sha512-u56TU0AIFqMtauKl/OJ1AeFsXqRHkgO7nCWmHaDwfxDo9GUMSqBA4NEh6GMuh1CYVM7zuROYtZrHzPc2ixK+ww==",
+ "version": "1.63.6",
+ "resolved": "https://registry.npmmirror.com/sass/-/sass-1.63.6.tgz",
+ "integrity": "sha512-MJuxGMHzaOW7ipp+1KdELtqKbfAWbH7OLIdoSMnVe3EXPMTmxTmlaZDCTsgIpPCs3w99lLo9/zDKkOrJuT5byw==",
"requires": {
"chokidar": ">=3.0.0 <4.0.0",
"immutable": "^4.0.0",
diff --git a/frontend/package.json b/frontend/package.json
index acf4120..c66ac07 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -11,9 +11,9 @@
"dependencies": {
"highlight.js": "^11.8.0",
"lodash": "^4.17.21",
- "pinia": "^2.1.3",
- "sass": "^1.62.1",
- "vue": "^3.2.37",
+ "pinia": "^2.1.4",
+ "sass": "^1.63.6",
+ "vue": "^3.3.4",
"vue-i18n": "^9.2.2"
},
"devDependencies": {
@@ -21,8 +21,8 @@
"naive-ui": "^2.34.4",
"prettier": "^2.8.8",
"unplugin-auto-import": "^0.16.4",
- "unplugin-icons": "^0.16.1",
- "unplugin-vue-components": "^0.25.0",
- "vite": "^4.3.0"
+ "unplugin-icons": "^0.16.3",
+ "unplugin-vue-components": "^0.25.1",
+ "vite": "^4.3.9"
}
}
diff --git a/frontend/package.json.md5 b/frontend/package.json.md5
index eb075a8..3645e52 100755
--- a/frontend/package.json.md5
+++ b/frontend/package.json.md5
@@ -1 +1 @@
-da66eb9d13a7ace25f7f75d36c2510f9
\ No newline at end of file
+e8efe46ff15777b1af82225bac5f4626
\ No newline at end of file
diff --git a/frontend/src/components/content_value/ContentToolbar.vue b/frontend/src/components/content_value/ContentToolbar.vue
index fc41e54..4ee13e9 100644
--- a/frontend/src/components/content_value/ContentToolbar.vue
+++ b/frontend/src/components/content_value/ContentToolbar.vue
@@ -56,7 +56,7 @@ const onDeleteKey = () => {
-
+
diff --git a/frontend/src/components/dialogs/ConnectionDialog.vue b/frontend/src/components/dialogs/ConnectionDialog.vue
index bf801f4..1051d2c 100644
--- a/frontend/src/components/dialogs/ConnectionDialog.vue
+++ b/frontend/src/components/dialogs/ConnectionDialog.vue
@@ -152,7 +152,7 @@ const onClose = () => {
preset="dialog"
transform-origin="center"
>
-
+
{
preset="dialog"
transform-origin="center"
>
-
+
-import { h, nextTick, onMounted, reactive, ref, watch } from 'vue'
+import { h, nextTick, onMounted, reactive, ref } from 'vue'
import { ConnectionType } from '../../consts/connection_type.js'
import { NIcon, useDialog, useMessage } from 'naive-ui'
import Key from '../icons/Key.vue'
import ToggleDb from '../icons/ToggleDb.vue'
-import { get, indexOf, size } from 'lodash'
+import { indexOf, isEmpty } from 'lodash'
import { useI18n } from 'vue-i18n'
import Refresh from '../icons/Refresh.vue'
import CopyLink from '../icons/CopyLink.vue'
@@ -158,7 +158,20 @@ const onUpdateExpanded = (value, option, meta) => {
}
}
-const onUpdateSelectedKeys = (keys, option) => {
+const onUpdateSelectedKeys = (keys, options) => {
+ if (!isEmpty(options)) {
+ // prevent load duplicate key
+ for (const node of options) {
+ if (node.type === ConnectionType.RedisValue) {
+ const { key, name, db, redisKey } = node
+ if (indexOf(selectedKeys.value, key) === -1) {
+ connectionStore.loadKeyValue(name, db, redisKey)
+ }
+ break
+ }
+ }
+ }
+
selectedKeys.value = keys
}
@@ -210,13 +223,6 @@ const renderSuffix = ({ option }) => {
const nodeProps = ({ option }) => {
return {
- onClick() {
- const { key, name, db, type, redisKey } = option
- if (option.type === ConnectionType.RedisValue) {
- connectionStore.loadKeyValue(name, db, redisKey)
- }
- // console.log('[click]:' + JSON.stringify(option))
- },
onDblclick: async () => {
if (loading.value) {
console.warn('TODO: alert to ignore double click when loading')
@@ -279,6 +285,12 @@ const handleSelectContextMenu = (key) => {
case 'key_newkey':
dialogStore.openNewKeyDialog(redisKey, name, db)
break
+ case 'key_reload':
+ connectionStore.scanKeys(name, db, redisKey)
+ break
+ case 'value_reload':
+ connectionStore.loadKeyValue(name, db, redisKey)
+ break
case 'key_remove':
case 'value_remove':
confirmDialog.warning(i18n.t('remove_tip', { name: redisKey }), () => {
diff --git a/frontend/src/langs/en.json b/frontend/src/langs/en.json
index 2dad811..cfddbf5 100644
--- a/frontend/src/langs/en.json
+++ b/frontend/src/langs/en.json
@@ -11,7 +11,6 @@
"disconnect_all": "Disconnect all connections",
"filter": "Filter",
"sort_conn": "Resort Connections",
- "reload_key": "Reload Current Key",
"close_confirm": "Confirm close this tab and connection",
"opening_connection": "Opening Connection...",
"remove_tip": "{type} \"{name}\" will be deleted",
diff --git a/frontend/src/langs/zh-cn.json b/frontend/src/langs/zh-cn.json
index 81e4e53..4d18b29 100644
--- a/frontend/src/langs/zh-cn.json
+++ b/frontend/src/langs/zh-cn.json
@@ -11,7 +11,6 @@
"disconnect_all": "断开所有连接",
"filter": "筛选",
"sort_conn": "调整连接顺序",
- "reload_key": "重新载入此键内容",
"close_confirm": "是否关闭当前连接",
"opening_connection": "正在打开连接...",
"remove_tip": "{type} \"{name}\" 将会被删除",
diff --git a/frontend/src/stores/connections.js b/frontend/src/stores/connections.js
index 807b574..f6ac0dd 100644
--- a/frontend/src/stores/connections.js
+++ b/frontend/src/stores/connections.js
@@ -1,5 +1,5 @@
import { defineStore } from 'pinia'
-import { get, isEmpty, last, map, remove, size, sortedIndexBy, split, uniq } from 'lodash'
+import { findIndex, get, isEmpty, last, map, remove, size, sortedIndexBy, split, uniq } from 'lodash'
import {
AddHashField,
AddListItem,
@@ -18,6 +18,7 @@ import {
RenameKey,
SaveConnection,
SaveSortedConnection,
+ ScanKeys,
SetHashValue,
SetKeyTTL,
SetKeyValue,
@@ -389,78 +390,8 @@ const useConnectionStore = defineStore('connections', {
return
}
- // insert child to children list by order
- const sortedInsertChild = (childrenList, item) => {
- const insertIdx = sortedIndexBy(childrenList, item, 'key')
- childrenList.splice(insertIdx, 0, item)
- // childrenList.push(item)
- }
- // update all node item's children num
- const updateChildrenNum = (node) => {
- let count = 0
- const totalChildren = size(node.children)
- if (totalChildren > 0) {
- for (const elem of node.children) {
- updateChildrenNum(elem)
- count += elem.keys
- }
- } else {
- count += 1
- }
- node.keys = count
- // node.children = sortBy(node.children, 'label')
- }
-
- const keyStruct = []
- const mark = {}
- for (const key in keys) {
- const keyPart = split(key, separator)
- // const prefixLen = size(keyPart) - 1
- const len = size(keyPart)
- let handlePath = ''
- let ks = keyStruct
- for (let i = 0; i < len; i++) {
- handlePath += keyPart[i]
- if (i !== len - 1) {
- // layer
- const treeKey = `${handlePath}@${ConnectionType.RedisKey}`
- if (!mark.hasOwnProperty(treeKey)) {
- mark[treeKey] = {
- key: `${connName}/db${db}/${treeKey}`,
- label: keyPart[i],
- name: connName,
- db,
- keys: 0,
- redisKey: handlePath,
- type: ConnectionType.RedisKey,
- children: [],
- }
- sortedInsertChild(ks, mark[treeKey])
- }
- ks = mark[treeKey].children
- handlePath += separator
- } else {
- // key
- const treeKey = `${handlePath}@${ConnectionType.RedisValue}`
- mark[treeKey] = {
- key: `${connName}/db${db}/${treeKey}`,
- label: keyPart[i],
- name: connName,
- db,
- keys: 0,
- redisKey: handlePath,
- type: ConnectionType.RedisValue,
- }
- sortedInsertChild(ks, mark[treeKey])
- }
- }
- }
-
// append db node to current connection's children
- const dbs = this.databases[connName]
- dbs[db].children = keyStruct
- dbs[db].opened = true
- updateChildrenNum(dbs[db])
+ this._updateNodeChildren(connName, db, keys)
},
/**
@@ -491,7 +422,127 @@ const useConnectionStore = defineStore('connections', {
},
/**
- *
+ * scan keys with prefix
+ * @param {string} connName
+ * @param {number} db
+ * @param {string} prefix
+ * @returns {Promise}
+ */
+ async scanKeys(connName, db, prefix) {
+ const { data, success, msg } = await ScanKeys(connName, db, prefix)
+ if (!success) {
+ throw new Error(msg)
+ }
+ // remove current keys below prefix
+ const prefixPart = split(prefix, separator)
+ const dbs = this.databases[connName]
+ let node = dbs[db]
+ for (const key of prefixPart) {
+ const idx = findIndex(node.children, { label: key })
+ if (idx === -1) {
+ node = null
+ break
+ }
+ node = node.children[idx]
+ }
+ if (node != null) {
+ node.children = []
+ }
+
+ const { keys = [] } = data
+ this._updateNodeChildren(connName, db, keys)
+ },
+
+ /**
+ * remove keys in db
+ * @param {string} connName
+ * @param {number} db
+ * @param {Object.[]} keys
+ * @private
+ */
+ _updateNodeChildren(connName, db, keys) {
+ // find match key node in node list
+ const findNodeByKey = (nodes, key) => {
+ const idx = findIndex(nodes, { key })
+ return idx !== -1 ? nodes[idx] : null
+ }
+ // insert child to children list by order
+ const sortedInsertChild = (childrenList, item) => {
+ const insertIdx = sortedIndexBy(childrenList, item, 'key')
+ childrenList.splice(insertIdx, 0, item)
+ // childrenList.push(item)
+ }
+ // update all node item's children num
+ const updateChildrenNum = (node) => {
+ let count = 0
+ const totalChildren = size(node.children)
+ if (totalChildren > 0) {
+ for (const elem of node.children) {
+ updateChildrenNum(elem)
+ count += elem.keys
+ }
+ } else {
+ count += 1
+ }
+ node.keys = count
+ // node.children = sortBy(node.children, 'label')
+ }
+
+ const dbs = this.databases[connName]
+ if (dbs[db].children == null) {
+ dbs[db].children = []
+ }
+ const keyStruct = dbs[db].children
+ for (const key in keys) {
+ const keyPart = split(key, separator)
+ // const prefixLen = size(keyPart) - 1
+ const len = size(keyPart)
+ let handlePath = ''
+ let ks = keyStruct
+ for (let i = 0; i < len; i++) {
+ handlePath += keyPart[i]
+ if (i !== len - 1) {
+ // layer
+ const curKey = `${connName}/db${db}/${handlePath}@${ConnectionType.RedisKey}`
+ let selectedNode = findNodeByKey(ks, curKey)
+ if (selectedNode == null) {
+ selectedNode = {
+ key: curKey,
+ label: keyPart[i],
+ name: connName,
+ db,
+ keys: 0,
+ redisKey: handlePath,
+ type: ConnectionType.RedisKey,
+ children: [],
+ }
+ sortedInsertChild(ks, selectedNode)
+ }
+ ks = selectedNode.children
+ handlePath += separator
+ } else {
+ // key
+ const curKey = `${connName}/db${db}/${handlePath}@${ConnectionType.RedisValue}`
+ const selectedNode = {
+ key: curKey,
+ label: keyPart[i],
+ name: connName,
+ db,
+ keys: 0,
+ redisKey: handlePath,
+ type: ConnectionType.RedisValue,
+ }
+ sortedInsertChild(ks, selectedNode)
+ }
+ }
+ }
+
+ dbs[db].opened = true
+ updateChildrenNum(dbs[db])
+ },
+
+ /**
+ * add key to db
* @param {string} connName
* @param {number} db
* @param {string} key
@@ -521,9 +572,8 @@ const useConnectionStore = defineStore('connections', {
for (let j = 0; j < len + 1; j++) {
const treeKey = get(nodeList[j], 'key')
const isLast = j >= len - 1
- const currentKey = `${connName}/db${db}/${redisKey}@${
- isLastKeyPart ? ConnectionType.RedisValue : ConnectionType.RedisKey
- }`
+ const keyType = isLastKeyPart ? ConnectionType.RedisValue : ConnectionType.RedisKey
+ const currentKey = `${connName}/db${db}/${redisKey}@${keyType}`
if (treeKey > currentKey || isLast) {
// out of search range, add new item
if (isLastKeyPart) {
@@ -981,9 +1031,8 @@ const useConnectionStore = defineStore('connections', {
const isLastKeyPart = i === keyLen - 1
for (let j = 0; j < len; j++) {
const treeKey = get(nodeList[j], 'key')
- const currentKey = `${connName}/db${db}/${redisKey}@${
- isLastKeyPart ? ConnectionType.RedisValue : ConnectionType.RedisKey
- }`
+ const keyType = isLastKeyPart ? ConnectionType.RedisValue : ConnectionType.RedisKey
+ const currentKey = `${connName}/db${db}/${redisKey}@${keyType}`
if (treeKey > currentKey) {
// out of search range, target not exists
forceBreak = true
diff --git a/frontend/src/stores/database.js b/frontend/src/stores/database.js
deleted file mode 100644
index 75549d8..0000000
--- a/frontend/src/stores/database.js
+++ /dev/null
@@ -1,857 +0,0 @@
-import { get, isEmpty, last, remove, size, sortedIndexBy, split } from 'lodash'
-import { defineStore } from 'pinia'
-import {
- AddHashField,
- AddListItem,
- AddZSetValue,
- CloseConnection,
- GetKeyValue,
- OpenConnection,
- OpenDatabase,
- RemoveKey,
- RenameKey,
- SetHashValue,
- SetKeyTTL,
- SetKeyValue,
- SetListItem,
- SetSetItem,
- UpdateSetItem,
- UpdateZSetValue,
-} from '../../wailsjs/go/services/connectionService.js'
-import { ConnectionType } from '../consts/connection_type.js'
-import useTabStore from './tab.js'
-import useConnectionStore from './connections.js'
-
-const separator = ':'
-
-const useDatabaseStore = defineStore('database', {
- /**
- * @typedef {Object} DatabaseItem
- * @property {string} key
- * @property {string} label
- * @property {string} name - server name, type != ConnectionType.Group only
- * @property {number} type
- * @property {number} [db] - database index, type == ConnectionType.RedisDB only
- * @property {number} keys
- * @property {boolean} [connected] - redis server is connected, type == ConnectionType.Server only
- * @property {boolean} [opened] - redis db is opened, type == ConnectionType.RedisDB only
- * @property {boolean} [expanded] - current node is expanded
- */
-
- /**
- *
- * @returns {{connections: DatabaseItem[]}}
- */
- state: () => ({
- connections: [], // all connections list
- databases: {}, // all database group by opened connections
- }),
- getters: {},
- actions: {
- /**
- * Open connection
- * @param {string} connName
- * @returns {Promise}
- */
- async openConnection(connName) {
- const { data, success, msg } = await OpenConnection(connName)
- if (!success) {
- throw new Error(msg)
- }
- // append to db node to current connection
- const connStore = useConnectionStore()
- const connNode = connStore.getConnection(connName)
- if (connNode == null) {
- throw new Error('no such connection')
- }
- const { db } = data
- if (isEmpty(db)) {
- throw new Error('no db loaded')
- }
- const children = []
- for (let i = 0; i < db.length; i++) {
- children.push({
- key: `${connName}/${db[i].name}`,
- label: db[i].name,
- name: connName,
- keys: db[i].keys,
- db: i,
- type: ConnectionType.RedisDB,
- // isLeaf: false,
- })
- }
- connNode.children = children
- connNode.connected = true
- },
-
- /**
- * Close connection
- * @param {string} connName
- * @returns {Promise}
- */
- async closeConnection(connName) {
- const { success, msg } = await CloseConnection(connName)
- if (!success) {
- // throw new Error(msg)
- return false
- }
-
- // successful close connection, remove all children
- const connNode = this.getConnection(connName)
- if (connNode == null) {
- // throw new Error('no such connection')
- return false
- }
- connNode.children = undefined
- connNode.isLeaf = undefined
- connNode.connected = false
- connNode.expanded = false
- return true
- },
-
- /**
- * Get Connection by path name
- * @param {string} connName
- * @returns
- */
- getConnection(connName) {
- const conn = this.connections
- for (let i = 0; i < conn.length; i++) {
- if (conn[i].type === ConnectionType.Server && conn[i].key === connName) {
- return conn[i]
- } else if (conn[i].type === ConnectionType.Group) {
- const children = conn[i].children
- for (let j = 0; j < children.length; j++) {
- if (children[j].type === ConnectionType.Server && conn[i].key === connName) {
- return children[j]
- }
- }
- }
- }
- return null
- },
-
- /**
- * Open database and load all keys
- * @param connName
- * @param db
- * @returns {Promise}
- */
- async openDatabase(connName, db) {
- const { data, success, msg } = await OpenDatabase(connName, db)
- if (!success) {
- throw new Error(msg)
- }
- const { keys = [] } = data
- if (isEmpty(keys)) {
- const connNode = this.getConnection(connName)
- const { children = [] } = connNode
- children[db].children = []
- children[db].opened = true
- return
- }
-
- // insert child to children list by order
- const sortedInsertChild = (childrenList, item) => {
- const insertIdx = sortedIndexBy(childrenList, item, 'key')
- childrenList.splice(insertIdx, 0, item)
- // childrenList.push(item)
- }
- // update all node item's children num
- const updateChildrenNum = (node) => {
- let count = 0
- const totalChildren = size(node.children)
- if (totalChildren > 0) {
- for (const elem of node.children) {
- updateChildrenNum(elem)
- count += elem.keys
- }
- } else {
- count += 1
- }
- node.keys = count
- // node.children = sortBy(node.children, 'label')
- }
-
- const keyStruct = []
- const mark = {}
- for (const key in keys) {
- const keyPart = split(key, separator)
- // const prefixLen = size(keyPart) - 1
- const len = size(keyPart)
- let handlePath = ''
- let ks = keyStruct
- for (let i = 0; i < len; i++) {
- handlePath += keyPart[i]
- if (i !== len - 1) {
- // layer
- const treeKey = `${handlePath}@${ConnectionType.RedisKey}`
- if (!mark.hasOwnProperty(treeKey)) {
- mark[treeKey] = {
- key: `${connName}/db${db}/${treeKey}`,
- label: keyPart[i],
- name: connName,
- db,
- keys: 0,
- redisKey: handlePath,
- type: ConnectionType.RedisKey,
- children: [],
- }
- sortedInsertChild(ks, mark[treeKey])
- }
- ks = mark[treeKey].children
- handlePath += separator
- } else {
- // key
- const treeKey = `${handlePath}@${ConnectionType.RedisValue}`
- mark[treeKey] = {
- key: `${connName}/db${db}/${treeKey}`,
- label: keyPart[i],
- name: connName,
- db,
- keys: 0,
- redisKey: handlePath,
- type: ConnectionType.RedisValue,
- }
- sortedInsertChild(ks, mark[treeKey])
- }
- }
- }
-
- // append db node to current connection's children
- const connNode = this.getConnection(connName)
- const { children = [] } = connNode
- children[db].children = keyStruct
- children[db].opened = true
- updateChildrenNum(children[db])
- },
-
- /**
- * select node
- * @param key
- * @param name
- * @param db
- * @param type
- * @param redisKey
- */
- select({ key, name, db, type, redisKey }) {
- if (type === ConnectionType.RedisValue) {
- console.log(`[click]key:${key} db: ${db} redis key: ${redisKey}`)
-
- // async get value for key
- this.loadKeyValue(name, db, redisKey).then(() => {})
- }
- },
-
- /**
- * load redis key
- * @param server
- * @param db
- * @param key
- */
- async loadKeyValue(server, db, key) {
- try {
- const { data, success, msg } = await GetKeyValue(server, db, key)
- if (success) {
- const { type, ttl, value } = data
- const tab = useTabStore()
- tab.upsertTab({
- server,
- db,
- type,
- ttl,
- key,
- value,
- })
- } else {
- console.warn('TODO: handle get key fail')
- }
- } finally {
- }
- },
-
- /**
- *
- * @param {string} connName
- * @param {number} db
- * @param {string} key
- * @private
- */
- _addKey(connName, db, key) {
- const connNode = this.getConnection(connName)
- const { children: dbs = [] } = connNode
- const dbDetail = get(dbs, db, {})
-
- if (dbDetail == null) {
- return
- }
-
- const descendantChain = [dbDetail]
-
- const keyPart = split(key, separator)
- let redisKey = ''
- const keyLen = size(keyPart)
- let added = false
- for (let i = 0; i < keyLen; i++) {
- redisKey += keyPart[i]
-
- const node = last(descendantChain)
- const nodeList = get(node, 'children', [])
- const len = size(nodeList)
- const isLastKeyPart = i === keyLen - 1
- for (let j = 0; j < len + 1; j++) {
- const treeKey = get(nodeList[j], 'key')
- const isLast = j >= len - 1
- const currentKey = `${connName}/db${db}/${redisKey}@${
- isLastKeyPart ? ConnectionType.RedisValue : ConnectionType.RedisKey
- }`
- if (treeKey > currentKey || isLast) {
- // out of search range, add new item
- if (isLastKeyPart) {
- // key not exists, add new one
- const item = {
- key: currentKey,
- label: keyPart[i],
- name: connName,
- db,
- keys: 1,
- redisKey,
- type: ConnectionType.RedisValue,
- }
- if (isLast) {
- nodeList.push(item)
- } else {
- nodeList.splice(j, 0, item)
- }
- added = true
- } else {
- // layer not exists, add new one
- const item = {
- key: currentKey,
- label: keyPart[i],
- name: connName,
- db,
- keys: 0,
- redisKey,
- type: ConnectionType.RedisKey,
- children: [],
- }
- if (isLast) {
- nodeList.push(item)
- descendantChain.push(last(nodeList))
- } else {
- nodeList.splice(j, 0, item)
- descendantChain.push(nodeList[j])
- }
- redisKey += separator
- added = true
- }
- break
- } else if (treeKey === currentKey) {
- if (isLastKeyPart) {
- // same key exists, do nothing
- console.log('TODO: same key exist, do nothing now, should replace value later')
- } else {
- // same group exists, find into it's children
- descendantChain.push(nodeList[j])
- redisKey += separator
- }
- break
- }
- }
- }
-
- // update ancestor node's info
- if (added) {
- const desLen = size(descendantChain)
- for (let i = 0; i < desLen; i++) {
- const children = get(descendantChain[i], 'children', [])
- let keys = 0
- for (const child of children) {
- if (child.type === ConnectionType.RedisKey) {
- keys += get(child, 'keys', 1)
- } else if (child.type === ConnectionType.RedisValue) {
- keys += get(child, 'keys', 0)
- }
- }
- descendantChain[i].keys = keys
- }
- }
- },
-
- /**
- * set redis key
- * @param {string} connName
- * @param {number} db
- * @param {string} key
- * @param {number} keyType
- * @param {any} value
- * @param {number} ttl
- * @returns {Promise<{[msg]: string, success: boolean}>}
- */
- async setKey(connName, db, key, keyType, value, ttl) {
- try {
- const { data, success, msg } = await SetKeyValue(connName, db, key, keyType, value, ttl)
- if (success) {
- // update tree view data
- this._addKey(connName, db, key)
- return { success }
- } else {
- return { success, msg }
- }
- } catch (e) {
- return { success: false, msg: e.message }
- }
- },
-
- /**
- * update hash field
- * when field is set, newField is null, delete field
- * when field is null, newField is set, add new field
- * when both field and newField are set, and field === newField, update field
- * when both field and newField are set, and field !== newField, delete field and add newField
- * @param {string} connName
- * @param {number} db
- * @param {string} key
- * @param {string} field
- * @param {string} newField
- * @param {string} value
- * @returns {Promise<{[msg]: string, success: boolean, [updated]: {}}>}
- */
- async setHash(connName, db, key, field, newField, value) {
- try {
- const { data, success, msg } = await SetHashValue(connName, db, key, field, newField || '', value || '')
- if (success) {
- const { updated = {} } = data
- return { success, updated }
- } else {
- return { success, msg }
- }
- } catch (e) {
- return { success: false, msg: e.message }
- }
- },
-
- /**
- * insert or update hash field item
- * @param {string} connName
- * @param {number} db
- * @param {string} key
- * @param {number }action 0:ignore duplicated fields 1:overwrite duplicated fields
- * @param {string[]} fieldItems field1, value1, filed2, value2...
- * @returns {Promise<{[msg]: string, success: boolean, [updated]: {}}>}
- */
- async addHashField(connName, db, key, action, fieldItems) {
- try {
- const { data, success, msg } = await AddHashField(connName, db, key, action, fieldItems)
- if (success) {
- const { updated = {} } = data
- return { success, updated }
- } else {
- return { success: false, msg }
- }
- } catch (e) {
- return { success: false, msg: e.message }
- }
- },
-
- /**
- * remove hash field
- * @param {string} connName
- * @param {number} db
- * @param {string} key
- * @param {string} field
- * @returns {Promise<{[msg]: {}, success: boolean, [removed]: string[]}>}
- */
- async removeHashField(connName, db, key, field) {
- try {
- const { data, success, msg } = await SetHashValue(connName, db, key, field, '', '')
- if (success) {
- const { removed = [] } = data
- return { success, removed }
- } else {
- return { success, msg }
- }
- } catch (e) {
- return { success: false, msg: e.message }
- }
- },
-
- /**
- * insert list item
- * @param {string} connName
- * @param {number} db
- * @param {string} key
- * @param {int} action 0: push to head, 1: push to tail
- * @param {string[]}values
- * @returns {Promise<*|{msg, success: boolean}>}
- */
- async addListItem(connName, db, key, action, values) {
- try {
- return AddListItem(connName, db, key, action, values)
- } catch (e) {
- return { success: false, msg: e.message }
- }
- },
-
- /**
- * prepend item to head of list
- * @param connName
- * @param db
- * @param key
- * @param values
- * @returns {Promise<[msg]: string, success: boolean, [item]: []>}
- */
- async prependListItem(connName, db, key, values) {
- try {
- const { data, success, msg } = await AddListItem(connName, db, key, 0, values)
- if (success) {
- const { left = [] } = data
- return { success, item: left }
- } else {
- return { success: false, msg }
- }
- } catch (e) {
- return { success: false, msg: e.message }
- }
- },
-
- /**
- * append item to tail of list
- * @param connName
- * @param db
- * @param key
- * @param values
- * @returns {Promise<[msg]: string, success: boolean, [item]: any[]>}
- */
- async appendListItem(connName, db, key, values) {
- try {
- const { data, success, msg } = await AddListItem(connName, db, key, 1, values)
- if (success) {
- const { right = [] } = data
- return { success, item: right }
- } else {
- return { success: false, msg }
- }
- } catch (e) {
- return { success: false, msg: e.message }
- }
- },
-
- /**
- * update value of list item by index
- * @param {string} connName
- * @param {number} db
- * @param {string} key
- * @param {number} index
- * @param {string} value
- * @returns {Promise<{[msg]: string, success: boolean, [updated]: {}}>}
- */
- async updateListItem(connName, db, key, index, value) {
- try {
- const { data, success, msg } = await SetListItem(connName, db, key, index, value)
- if (success) {
- const { updated = {} } = data
- return { success, updated }
- } else {
- return { success, msg }
- }
- } catch (e) {
- return { success: false, msg: e.message }
- }
- },
-
- /**
- * remove list item
- * @param {string} connName
- * @param {number} db
- * @param {string} key
- * @param {number} index
- * @returns {Promise<{[msg]: string, success: boolean, [removed]: string[]}>}
- */
- async removeListItem(connName, db, key, index) {
- try {
- const { data, success, msg } = await SetListItem(connName, db, key, index, '')
- if (success) {
- const { removed = [] } = data
- return { success, removed }
- } else {
- return { success, msg }
- }
- } catch (e) {
- return { success: false, msg: e.message }
- }
- },
-
- /**
- * add item to set
- * @param {string} connName
- * @param {number} db
- * @param {string} key
- * @param {string} value
- * @returns {Promise<{[msg]: string, success: boolean}>}
- */
- async addSetItem(connName, db, key, value) {
- try {
- const { success, msg } = await SetSetItem(connName, db, key, false, [value])
- if (success) {
- return { success }
- } else {
- return { success, msg }
- }
- } catch (e) {
- return { success: false, msg: e.message }
- }
- },
-
- /**
- * update value of set item
- * @param {string} connName
- * @param {number} db
- * @param {string} key
- * @param {string} value
- * @param {string} newValue
- * @returns {Promise<{[msg]: string, success: boolean}>}
- */
- async updateSetItem(connName, db, key, value, newValue) {
- try {
- const { success, msg } = await UpdateSetItem(connName, db, key, value, newValue)
- if (success) {
- return { success: true }
- } else {
- return { success, msg }
- }
- } catch (e) {
- return { success: false, msg: e.message }
- }
- },
-
- /**
- * remove item from set
- * @param connName
- * @param db
- * @param key
- * @param value
- * @returns {Promise<{[msg]: string, success: boolean}>}
- */
- async removeSetItem(connName, db, key, value) {
- try {
- const { success, msg } = await SetSetItem(connName, db, key, true, [value])
- if (success) {
- return { success }
- } else {
- return { success, msg }
- }
- } catch (e) {
- return { success: false, msg: e.message }
- }
- },
-
- /**
- * add item to sorted set
- * @param {string} connName
- * @param {number} db
- * @param {string} key
- * @param {number} action
- * @param {Object.} vs value: score
- * @returns {Promise<{[msg]: string, success: boolean}>}
- */
- async addZSetItem(connName, db, key, action, vs) {
- try {
- const { success, msg } = await AddZSetValue(connName, db, key, action, vs)
- if (success) {
- return { success }
- } else {
- return { success, msg }
- }
- } catch (e) {
- return { success: false, msg: e.message }
- }
- },
-
- /**
- * update item of sorted set
- * @param {string} connName
- * @param {number} db
- * @param {string} key
- * @param {string} value
- * @param {string} newValue
- * @param {number} score
- * @returns {Promise<{[msg]: string, success: boolean, [updated]: {}, [removed]: []}>}
- */
- async updateZSetItem(connName, db, key, value, newValue, score) {
- try {
- const { data, success, msg } = await UpdateZSetValue(connName, db, key, value, newValue, score)
- if (success) {
- const { updated, removed } = data
- return { success, updated, removed }
- } else {
- return { success, msg }
- }
- } catch (e) {
- return { success: false, msg: e.message }
- }
- },
-
- /**
- * remove item from sorted set
- * @param {string} connName
- * @param {number} db
- * @param key
- * @param {string} value
- * @returns {Promise<{[msg]: string, success: boolean, [removed]: []}>}
- */
- async removeZSetItem(connName, db, key, value) {
- try {
- const { data, success, msg } = await UpdateZSetValue(connName, db, key, value, '', 0)
- if (success) {
- const { removed } = data
- return { success, removed }
- } else {
- return { success, msg }
- }
- } catch (e) {
- return { success: false, msg: e.message }
- }
- },
-
- /**
- * reset key's ttl
- * @param {string} connName
- * @param {number} db
- * @param {string} key
- * @param {number} ttl
- * @returns {Promise}
- */
- async setTTL(connName, db, key, ttl) {
- try {
- const { success, msg } = await SetKeyTTL(connName, db, key, ttl)
- return success === true
- } catch (e) {
- return false
- }
- },
-
- /**
- *
- * @param {string} connName
- * @param {number} db
- * @param {string} key
- * @private
- */
- _removeKey(connName, db, key) {
- const connNode = this.getConnection(connName)
- const { children: dbs = [] } = connNode
- const dbDetail = get(dbs, db, {})
-
- if (dbDetail == null) {
- return
- }
-
- const descendantChain = [dbDetail]
- const keyPart = split(key, separator)
- let redisKey = ''
- const keyLen = size(keyPart)
- let deleted = false
- let forceBreak = false
- for (let i = 0; i < keyLen && !forceBreak; i++) {
- redisKey += keyPart[i]
-
- const node = last(descendantChain)
- const nodeList = get(node, 'children', [])
- const len = size(nodeList)
- const isLastKeyPart = i === keyLen - 1
- for (let j = 0; j < len; j++) {
- const treeKey = get(nodeList[j], 'key')
- const currentKey = `${connName}/db${db}/${redisKey}@${
- isLastKeyPart ? ConnectionType.RedisValue : ConnectionType.RedisKey
- }`
- if (treeKey > currentKey) {
- // out of search range, target not exists
- forceBreak = true
- break
- } else if (treeKey === currentKey) {
- if (isLastKeyPart) {
- // find target
- nodeList.splice(j, 1)
- node.keys -= 1
- deleted = true
- forceBreak = true
- } else {
- // find into it's children
- descendantChain.push(nodeList[j])
- redisKey += separator
- }
- break
- }
- }
-
- if (forceBreak) {
- break
- }
- }
- // console.log(JSON.stringify(descendantChain))
-
- // update ancestor node's info
- if (deleted) {
- const desLen = size(descendantChain)
- for (let i = desLen - 1; i > 0; i--) {
- const children = get(descendantChain[i], 'children', [])
- const parent = descendantChain[i - 1]
- if (isEmpty(children)) {
- const parentChildren = get(parent, 'children', [])
- const k = get(descendantChain[i], 'key')
- remove(parentChildren, (item) => item.key === k)
- }
- parent.keys -= 1
- }
- }
- },
-
- /**
- * remove redis key
- * @param {string} connName
- * @param {number} db
- * @param {string} key
- * @returns {Promise}
- */
- async removeKey(connName, db, key) {
- try {
- const { data, success, msg } = await RemoveKey(connName, db, key)
- if (success) {
- // update tree view data
- this._removeKey(connName, db, key)
-
- // set tab content empty
- const tab = useTabStore()
- tab.emptyTab(connName)
- return true
- }
- } finally {
- }
- return false
- },
-
- /**
- * rename key
- * @param {string} connName
- * @param {number} db
- * @param {string} key
- * @param {string} newKey
- * @returns {Promise<{[msg]: string, success: boolean}>}
- */
- async renameKey(connName, db, key, newKey) {
- const { success = false, msg } = await RenameKey(connName, db, key, newKey)
- if (success) {
- // delete old key and add new key struct
- this._removeKey(connName, db, key)
- this._addKey(connName, db, newKey)
- return { success: true }
- } else {
- return { success: false, msg }
- }
- },
- },
-})
-
-export default useDatabaseStore
diff --git a/go.mod b/go.mod
index 8ef2ca9..9dace25 100644
--- a/go.mod
+++ b/go.mod
@@ -3,7 +3,7 @@ module tinyrdm
go 1.20
require (
- github.com/bytedance/sonic v1.9.1
+ github.com/bytedance/sonic v1.9.2
github.com/google/go-cmp v0.5.9
github.com/redis/go-redis/v9 v9.0.5
github.com/vrischmann/userdir v0.0.0-20151206171402-20f291cebd68
@@ -22,7 +22,7 @@ require (
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
github.com/labstack/echo/v4 v4.10.2 // indirect
github.com/labstack/gommon v0.4.0 // indirect
- github.com/leaanthony/go-ansi-parser v1.6.0 // indirect
+ github.com/leaanthony/go-ansi-parser v1.6.1 // indirect
github.com/leaanthony/gosod v1.0.3 // indirect
github.com/leaanthony/slicer v1.6.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
@@ -38,11 +38,11 @@ require (
github.com/valyala/fasttemplate v1.2.2 // indirect
github.com/wailsapp/mimetype v1.4.1 // indirect
golang.org/x/arch v0.3.0 // indirect
- golang.org/x/crypto v0.9.0 // indirect
- golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect
- golang.org/x/net v0.10.0 // indirect
- golang.org/x/sys v0.8.0 // indirect
- golang.org/x/text v0.9.0 // indirect
+ golang.org/x/crypto v0.10.0 // indirect
+ golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df // indirect
+ golang.org/x/net v0.11.0 // indirect
+ golang.org/x/sys v0.9.0 // indirect
+ golang.org/x/text v0.10.0 // indirect
)
// replace github.com/wailsapp/wails/v2 v2.5.1 => /Users/lykin/go/pkg/mod
diff --git a/go.sum b/go.sum
index 1214053..f23eb44 100644
--- a/go.sum
+++ b/go.sum
@@ -3,8 +3,8 @@ github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3IS
github.com/bsm/ginkgo/v2 v2.7.0 h1:ItPMPH90RbmZJt5GtkcNvIRuGEdwlBItdNVoyzaNQao=
github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y=
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
-github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
-github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
+github.com/bytedance/sonic v1.9.2 h1:GDaNjuWSGu09guE9Oql0MSTNhNCLlWwO8y/xM5BzcbM=
+github.com/bytedance/sonic v1.9.2/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
@@ -32,8 +32,8 @@ github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8
github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
github.com/leaanthony/debme v1.2.1 h1:9Tgwf+kjcrbMQ4WnPcEIUcQuIZYqdWftzZkBr+i/oOc=
github.com/leaanthony/debme v1.2.1/go.mod h1:3V+sCm5tYAgQymvSOfYQ5Xx2JCr+OXiD9Jkw3otUjiA=
-github.com/leaanthony/go-ansi-parser v1.6.0 h1:T8TuMhFB6TUMIUm0oRrSbgJudTFw9csT3ZK09w0t4Pg=
-github.com/leaanthony/go-ansi-parser v1.6.0/go.mod h1:+vva/2y4alzVmmIEpk9QDhA7vLC5zKDTRwfZGOp3IWU=
+github.com/leaanthony/go-ansi-parser v1.6.1 h1:xd8bzARK3dErqkPFtoF9F3/HgN8UQk0ed1YDKpEz01A=
+github.com/leaanthony/go-ansi-parser v1.6.1/go.mod h1:+vva/2y4alzVmmIEpk9QDhA7vLC5zKDTRwfZGOp3IWU=
github.com/leaanthony/gosod v1.0.3 h1:Fnt+/B6NjQOVuCWOKYRREZnjGyvg+mEhd1nkkA04aTQ=
github.com/leaanthony/gosod v1.0.3/go.mod h1:BJ2J+oHsQIyIQpnLPjnqFGTMnOZXDbvWtRCSG7jGxs4=
github.com/leaanthony/slicer v1.5.0/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY=
@@ -88,13 +88,13 @@ github.com/wailsapp/wails/v2 v2.5.1/go.mod h1:jbOZbcr/zm79PxXxAjP8UoVlDd9wLW3uDs
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
-golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
-golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
-golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
-golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
+golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM=
+golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
+golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME=
+golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
-golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
+golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU=
+golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -106,12 +106,12 @@ golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
-golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
+golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
-golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
+golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58=
+golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=