kami-redis 2.2.0 → 3.0.0
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/README.md +30 -7
- package/dist/index.d.ts +1 -2364
- package/dist/index.js +14 -1
- package/package.json +3 -1
package/dist/index.js
CHANGED
|
@@ -1,2 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
2
|
+
import{Effect as m,Schema as V}from"effect";import Le from"meow";import _ from"picocolors";import{Effect as C,Schema as $}from"effect";import{createClient as ee}from"redis";var c=class extends $.TaggedError()("RedisError",{operation:$.String,cause:$.Defect()}){},te=C.fn("connectRedis")(function*(e){let t=yield*C.try({try:()=>ee({url:e}),catch:n=>new c({operation:"create client",cause:n})});return yield*C.tryPromise({try:()=>t.connect(),catch:n=>new c({operation:"connect",cause:n})}),t}),re=C.fn("quitRedis")(function*(e){e.isOpen&&(yield*C.tryPromise({try:()=>e.quit(),catch:t=>new c({operation:"quit",cause:t})}))});function D(e,t){return C.acquireUseRelease(te(e),t,n=>re(n).pipe(C.catch(()=>C.void)))}function Y(e){try{let t=new URL(e);return(t.protocol==="redis:"||t.protocol==="rediss:")&&!!t.hostname&&(!t.port||!Number.isNaN(Number(t.port)))}catch{return!1}}import{Effect as b,Schema as oe}from"effect";import se from"readline";import N from"picocolors";function ne(e){if(e<0)return null;if(e<60)return`${e} sec`;let t=Math.floor(e/60);if(t<60)return`${t} min`;let n=Math.floor(t/60);return n<24?`${n} hr`:`${Math.floor(n/24)} day`}function K(e){return ne(e)}var T=class extends oe.TaggedError()("PromptCancelled",{}){},ie=500,L=20,ce=100,W=!1;function ae(){!W&&process.stdin.isTTY&&(se.emitKeypressEvents(process.stdin),W=!0)}var le=b.fn("scanKeys")(function*(e,t,n){return yield*b.tryPromise({async try(){let r=[],o=!1;for await(let s of e.scanIterator({MATCH:t,COUNT:ce})){for(let i of s)if(r.push(i),r.length>=n){o=!0;break}if(o)break}return r.sort((s,i)=>s.localeCompare(i)),{hitLimit:o,keys:r}},catch:r=>new c({operation:"scan keys",cause:r})})}),de=b.fn("loadKeyRows")(function*(e,t){return yield*b.tryPromise({async try(){return Promise.all(t.map(async n=>({key:n,ttlLabel:K(await e.ttl(n).catch(()=>-1))})))},catch:n=>new c({operation:"load key expiry timers",cause:n})})});function k(e,t,n){console.clear(),console.log(`0_0 ${n}: ${N.dim("(Use \u2191/\u2193, Enter/\u2192 to open)")}`);let r=Math.max(0,Math.min(t-Math.floor(L/2),e.length-L)),o=Math.min(e.length,r+L),s=Math.max(...e.slice(r,o).map(i=>i.key.length));for(let i=r;i<o;i++){let l=e[i],u=i===t?N.cyan(">"):" ",y=l.key.padEnd(s),p=i===t?N.cyan(y):y,f=l.ttlLabel?N.dim(` ${l.ttlLabel}`):"";console.log(`${u} ${p}${f}`)}}function pe(e,t,n){let r=n?e.findIndex(P=>P.key===n):-1,o=r>=0?r:0;if(!process.stdin.isTTY)return Promise.resolve(e[o].key);let{promise:s,reject:i,resolve:l}=Promise.withResolvers(),u=!1,y;ae();let p=()=>{if(!u){u=!0,y&&process.stdin.removeListener("keypress",y);try{process.stdin.setRawMode(!1)}catch{}process.stdin.pause()}},f=()=>{let P=e[o].key;p(),console.clear(),l(P)},v=()=>{p(),i(new T)};return y=(P,R)=>{if(!(!R||u)){if(R.ctrl&&R.name==="c"){v();return}if(R.name==="up"){o=(o-1+e.length)%e.length,k(e,o,t);return}if(R.name==="down"){o=(o+1)%e.length,k(e,o,t);return}(R.name==="return"||R.name==="enter"||R.name==="right")&&f()}},process.stdin.setRawMode(!0),process.stdin.resume(),process.stdin.on("keypress",y),k(e,o,t),s}var q=b.fn("promptForKey")(function*(e,t={}){let n=t.pattern?.trim()||"*",r=t.limit??ie,{hitLimit:o,keys:s}=yield*le(e,n,r);if(s.length===0)return null;let i=yield*de(e,s),l=n==="*"?"keys":`matching ${n}`,y=`Select a key (${`${s.length}${o?"+":""}`} ${l})`,p=t.defaultKey&&s.includes(t.defaultKey)?t.defaultKey:void 0;return yield*b.tryPromise({try:()=>pe(i,y,p),catch:f=>f instanceof T?f:new T})});import Ee from"boxen";import{highlight as Te}from"cli-highlight";import{Effect as a}from"effect";import xe from"readline";import h from"picocolors";import{Effect as E}from"effect";import{Buffer as fe}from"buffer";import{spawn as me}from"child_process";var M=globalThis.process?.platform||"",O=/^win/i.test(M),I=/^linux/i.test(M),F=/^darwin/i.test(M),j=I&&(!!process.env.WSL_DISTRO_NAME||/microsoft/i.test(process.env.OS??""));var ue=800;function ye(){if(O)return[{command:"powershell.exe",args:["-NoProfile","-Command","Set-Clipboard -Value ([Console]::In.ReadToEnd())"],method:"Windows PowerShell Set-Clipboard"},{command:"pwsh.exe",args:["-NoProfile","-Command","Set-Clipboard -Value ([Console]::In.ReadToEnd())"],method:"PowerShell Set-Clipboard"},{command:"clip.exe",args:[],method:"Windows clip.exe"}];if(F)return[{command:"pbcopy",args:[],method:"macOS pbcopy"}];if(I){let e=[];return j&&e.push({command:"clip.exe",args:[],method:"WSL clip.exe"}),process.env.WAYLAND_DISPLAY&&e.push({command:"wl-copy",args:[],method:"Wayland wl-copy"}),process.env.DISPLAY&&e.push({command:"xclip",args:["-selection","clipboard"],method:"X11 xclip"},{command:"xsel",args:["--clipboard","--input"],method:"X11 xsel"}),e.push({command:"termux-clipboard-set",args:[],method:"Termux clipboard"}),e}return[{command:"pbcopy",args:[],method:"pbcopy"},{command:"wl-copy",args:[],method:"wl-copy"},{command:"xclip",args:["-selection","clipboard"],method:"xclip"},{command:"xsel",args:["--clipboard","--input"],method:"xsel"}]}function ge(e,t){return E.callback(n=>{let r=me(t.command,t.args,{detached:!O,stdio:["pipe","ignore","ignore"],windowsHide:!0}),o=!1,s,i=l=>{o||(o=!0,clearTimeout(s),n(E.succeed(l)))};return s=setTimeout(()=>{r.stdin.destroy(),r.unref(),i({ok:!0,method:t.method})},ue),r.once("error",l=>{i({ok:!1,message:l.message})}),r.once("close",l=>{if(l===0){i({ok:!0,method:t.method});return}i({ok:!1,message:`${t.command} exited with code ${l}`})}),r.stdin.end(e),E.sync(()=>{o||(o=!0,clearTimeout(s),r.stdin.destroy(),r.kill())})})}var he=E.fnUntraced(function*(e){return yield*E.sync(()=>{if(!process.stdout.isTTY)return{ok:!1,message:"stdout is not a TTY"};let t=fe.from(e).toString("base64");return process.stdout.write(`\x1B]52;c;${t}\x07`),{ok:!0,method:"terminal OSC 52"}})}),z=E.fn("copyToClipboard")(function*(e){let t=[];for(let r of ye()){let o=yield*ge(e,r);if(o.ok)return o;t.push(`${r.method}: ${o.message}`)}let n=yield*he(e);return n.ok?n:(t.push(`OSC 52: ${n.message}`),{ok:!1,message:`No supported clipboard command worked (${t.join("; ")})`})});import{Effect as x}from"effect";var J=x.fnUntraced(function*(e){return typeof e=="object"&&e!==null?yield*x.sync(()=>JSON.stringify(e,null,2)):typeof e=="string"?yield*x.try({try:()=>JSON.stringify(JSON.parse(e),null,2),catch:()=>e}).pipe(x.catch(t=>x.succeed(t))):String(e)});import{Effect as g}from"effect";var d=100,A=1e4,U=100;function w(e,t,n){return`\u2026 showing first ${e} of ${t} ${n}`}function Re(e){if(e===null)return null;try{return JSON.parse(e)}catch{return e}}async function Ce(e,t){let n=await e.hLen(t),r={};for await(let o of e.hScanIterator(t,{COUNT:U}))for(let s of o)if(r[s.field]=s.value,Object.keys(r).length>=d)return r["\u2026"]=w(d,n,"fields"),r;return r}async function we(e,t){let n=await e.sCard(t),r=[];for await(let o of e.sScanIterator(t,{COUNT:U}))for(let s of o)if(r.push(s),r.length>=d)return r.push(w(d,n,"members")),r;return r}async function be(e,t){let n=await e.zCard(t),r=[];for await(let o of e.zScanIterator(t,{COUNT:U}))for(let s of o)if(r.push(s),r.length>=d)return r.push(w(d,n,"members")),r;return r}var H=g.fn("getValue")(function*(e,t,n){switch(n){case"string":return yield*g.tryPromise({async try(){let[r,o]=await Promise.all([e.strLen(t),e.getRange(t,0,A-1)]),s=Re(o);return typeof s=="string"&&r>A?`${s}
|
|
3
|
+
|
|
4
|
+
${w(A,r,"characters")}`:s},catch:r=>new c({operation:"get string",cause:r})});case"hash":return yield*g.tryPromise({try:()=>Ce(e,t),catch:r=>new c({operation:"get hash",cause:r})});case"list":return yield*g.tryPromise({async try(){let r=await e.lLen(t),o=await e.lRange(t,0,d-1);return r>d&&o.push(w(d,r,"items")),o},catch:r=>new c({operation:"get list",cause:r})});case"set":return yield*g.tryPromise({try:()=>we(e,t),catch:r=>new c({operation:"get set",cause:r})});case"zset":return yield*g.tryPromise({try:()=>be(e,t),catch:r=>new c({operation:"get zset",cause:r})});case"stream":return yield*g.tryPromise({async try(){let[r,o]=await Promise.all([e.xLen(t),e.xRange(t,"-","+",{COUNT:d})]);return r>d?{entries:o,note:w(d,r,"entries")}:o},catch:r=>new c({operation:"get stream",cause:r})});case"ReJSON-RL":case"json":return yield*g.tryPromise({try:()=>e.json.get(t),catch:r=>new c({operation:"get json",cause:r})});case"TSDB-TYPE":case"timeseries":return yield*g.tryPromise({async try(){let[r,o]=await Promise.all([e.ts.info(t),e.ts.range(t,"-","+",{COUNT:d})]);return r.totalSamples>d?{samples:o,note:w(d,r.totalSamples,"samples")}:o},catch:r=>new c({operation:"get timeseries",cause:r})});case"vectorset":return yield*g.tryPromise({async try(){let[r,o]=await Promise.all([e.vCard(t),e.vRandMember(t,d)]);return r>d?{members:o,note:w(d,r,"members")}:o},catch:r=>new c({operation:"get vector set",cause:r})});default:return`Unsupported Redis type: ${n}`}});var X=!1;function Se(){!X&&process.stdin.isTTY&&(xe.emitKeypressEvents(process.stdin),X=!0)}var Pe=a.fn("getKeyType")(function*(e,t){return yield*a.tryPromise({try:()=>e.type(t),catch:n=>new c({operation:"get key type",cause:n})})}),Ke=a.fn("getKeyTtl")(function*(e,t){return yield*a.tryPromise({try:()=>e.ttl(t),catch:n=>new c({operation:"get key ttl",cause:n})})}),Ne=a.fn("deleteKey")(function*(e,t){yield*a.tryPromise({try:()=>e.del(t),catch:n=>new c({operation:"delete key",cause:n})})}),ve=a.fnUntraced(function*(e,t,n){yield*a.sync(()=>{console.clear(),console.log(Ee(Te(t,{language:"json",ignoreIllegals:!0}),{borderColor:"cyan",padding:1,title:e,borderStyle:"round"}));let r=K(n);r&&console.log(h.yellow(r)),console.log(h.dim("d: delete | c: copy | \u2190: back | ctrl+c: quit"))})});function $e(e,t,n){return a.callback(r=>{let o=!1,s=!1,i=!1,l,u=()=>{if(!o){if(o=!0,l&&process.stdin.removeListener("keypress",l),process.stdin.isTTY)try{process.stdin.setRawMode(!1)}catch{}process.stdin.pause()}};return l=(y,p)=>{if(!(!p||o)){if(p.ctrl&&p.name==="c"){u(),r(a.sync(()=>console.clear()).pipe(a.as("quit")));return}if(s){let f=p.sequence?.toLowerCase()??p.name;if(f!=="y"){(f==="n"||p.name==="escape")&&(s=!1,console.log(h.dim("Delete cancelled")));return}s=!1,u(),r(Ne(e,t).pipe(a.tap(()=>a.sync(()=>{console.clear(),console.log(h.green(`Deleted ${t}`))})),a.catch(v=>a.sync(()=>{console.clear(),console.error(h.red("Delete failed:"),v)})),a.as("deleted")));return}if(p.name==="d"){s=!0,console.log(h.yellow(`Delete ${t}? Press y to confirm, n or Esc to cancel.`));return}if(p.name==="c"){if(i)return;i=!0,a.runPromise(z(n)).then(f=>{if(i=!1,!f.ok){console.log(h.yellow(`Clipboard unavailable: ${f.message}`));return}console.log(h.green(`Copied value to clipboard via ${f.method}`))}).catch(f=>{i=!1,console.error(h.red("Copy failed:"),f)});return}p.name==="left"&&(u(),r(a.sync(()=>console.clear()).pipe(a.as("back"))))}},process.stdin.isTTY&&process.stdin.setRawMode(!0),process.stdin.resume(),process.stdin.on("keypress",l),a.sync(u)})}var B=a.fn("viewKey")(function*(e,t){let n=yield*Pe(e,t),r=yield*Ke(e,t),o=yield*H(e,t,n),s=yield*J(o);return yield*ve(t,s,r),yield*a.sync(Se),yield*$e(e,t,s)});var S=class extends V.TaggedError()("CliValidationError",{help:V.String,message:V.String}){},ke=m.fn("parseCli")(function*(){let e=yield*m.sync(()=>Le(`
|
|
5
|
+
Usage
|
|
6
|
+
$ kami-redis <url>
|
|
7
|
+
|
|
8
|
+
Options
|
|
9
|
+
--pattern, -p Redis key pattern to browse (default: *)
|
|
10
|
+
--limit, -l Maximum keys to load into the selector (default: 500)
|
|
11
|
+
|
|
12
|
+
Examples
|
|
13
|
+
$ kami-redis redis://localhost:6379
|
|
14
|
+
$ kami-redis redis://localhost:6379 --pattern "user:*"
|
|
15
|
+
`,{importMeta:import.meta,flags:{limit:{type:"number",shortFlag:"l",default:500},pattern:{type:"string",shortFlag:"p",default:"*"}}})),[t]=e.input,n=e.flags.pattern.trim()||"*",r=e.flags.limit;return t?Y(t)?!Number.isInteger(r)||r<1?yield*new S({help:e.help,message:"Key limit must be a positive integer"}):{keyLimit:r,pattern:n,url:t}:yield*new S({help:e.help,message:"Invalid Redis URL"}):yield*new S({help:e.help,message:""})}),Me=m.fn("browseKeys")(function*(e,t){yield*m.sync(()=>console.clear());let n;for(;;){let r=yield*q(e,{defaultKey:n,pattern:t.pattern,limit:t.keyLimit});if(!r){yield*m.sync(()=>console.log(t.pattern==="*"?"No keys in Redis":`No keys match pattern ${t.pattern}`));return}if(n=r,(yield*B(e,r))==="quit")return}}),G=m.sync(()=>{if(process.stdin.isTTY)try{process.stdin.setRawMode(!1)}catch{}try{process.stdin.pause()}catch{}}),Oe=ke().pipe(m.flatMap(e=>D(e.url,t=>Me(t,e))),m.catchTags({CliValidationError:e=>m.sync(()=>{console.log(e.help),e.message&&console.log(_.red(e.message)),process.exitCode=1}),PromptCancelled:()=>G.pipe(m.tap(()=>m.sync(()=>console.clear())))}),m.catch(e=>G.pipe(m.tap(()=>m.sync(()=>{console.error(_.red("Error:"),e),process.exitCode=1}))))),Z=new AbortController,Q=!1;process.once("SIGINT",()=>{Q=!0,Z.abort()});m.runPromise(Oe,{signal:Z.signal}).catch(e=>{if(Q){process.exitCode=0;return}console.error(_.red("Error:"),e),process.exitCode=1});
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kami-redis",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "3.0.0",
|
|
5
5
|
"description": "Browse, inspect, copy, and delete Redis keys from your CLI",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"main": "dist/index.js",
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"scripts": {
|
|
19
19
|
"dev": "bun run --watch src/index.ts",
|
|
20
20
|
"build": "tsup",
|
|
21
|
+
"seed": "bun run scripts/seed-redis.ts",
|
|
21
22
|
"lint": "eslint",
|
|
22
23
|
"lint:fix": "eslint --fix",
|
|
23
24
|
"release": "bun run build && changeset publish",
|
|
@@ -29,6 +30,7 @@
|
|
|
29
30
|
"dependencies": {
|
|
30
31
|
"boxen": "^8.0.1",
|
|
31
32
|
"cli-highlight": "^2.1.11",
|
|
33
|
+
"effect": "^4.0.0-rc.118",
|
|
32
34
|
"inquirer": "^12.9.6",
|
|
33
35
|
"meow": "^14.0.0",
|
|
34
36
|
"picocolors": "^1.1.1",
|