@upstash/react-redis-browser 0.1.2-canary-6 → 0.1.2-canary-8

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/dist/index.css CHANGED
@@ -971,6 +971,10 @@
971
971
  .ups-db .p-6 {
972
972
  padding: 1.5rem;
973
973
  }
974
+ .ups-db .px-0 {
975
+ padding-left: 0px;
976
+ padding-right: 0px;
977
+ }
974
978
  .ups-db .px-2 {
975
979
  padding-left: 0.5rem;
976
980
  padding-right: 0.5rem;
package/dist/index.js CHANGED
@@ -222,6 +222,20 @@ var _iconsreact = require('@tabler/icons-react');
222
222
 
223
223
  var _reactresizablepanels = require('react-resizable-panels');
224
224
 
225
+ // src/components/ui/toaster.tsx
226
+ var _reactportal = require('@radix-ui/react-portal');
227
+
228
+ // src/lib/portal-root.ts
229
+ var root;
230
+ if (typeof document !== "undefined") {
231
+ const id = "react-redis-browser-portal-root";
232
+ root = _nullishCoalesce(document.querySelector(`#${id}`), () => ( document.createElement("div")));
233
+ root.classList.add("ups-db");
234
+ root.id = "react-redis-browser-portal-root";
235
+ document.body.append(root);
236
+ }
237
+ var portalRoot = root;
238
+
225
239
  // src/components/ui/toast.tsx
226
240
 
227
241
  var _reacticons = require('@radix-ui/react-icons');
@@ -2874,7 +2888,7 @@ ToastDescription.displayName = ToastPrimitives.Description.displayName;
2874
2888
 
2875
2889
  function Toaster() {
2876
2890
  const { toasts } = useToast();
2877
- return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, ToastProvider, { children: [
2891
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _reactportal.Portal, { container: portalRoot, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, ToastProvider, { children: [
2878
2892
  toasts.map(({ id, title, description, action, ...props }) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, Toast, { ...props, children: [
2879
2893
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "grid gap-1", children: [
2880
2894
  title && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, ToastTitle, { children: title }),
@@ -2884,7 +2898,7 @@ function Toaster() {
2884
2898
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, ToastClose, {})
2885
2899
  ] }, id)),
2886
2900
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, ToastViewport, {})
2887
- ] });
2901
+ ] }) });
2888
2902
  }
2889
2903
 
2890
2904
  // src/components/databrowser/hooks/use-keys.tsx
@@ -3037,7 +3051,7 @@ var KeysProvider = ({ children }) => {
3037
3051
  queryKey: [FETCH_KEYS_QUERY_KEY, search],
3038
3052
  initialPageParam: 0,
3039
3053
  queryFn: async ({ pageParam: page }) => {
3040
- if (pageRef.current > page) resetCache();
3054
+ if (pageRef.current >= page) resetCache();
3041
3055
  pageRef.current = page;
3042
3056
  return await fetchKeys();
3043
3057
  },
@@ -3174,41 +3188,42 @@ var useAddKey = () => {
3174
3188
  const { redis } = useDatabrowser();
3175
3189
  const mutation = _reactquery.useMutation.call(void 0, {
3176
3190
  mutationFn: async ({ key, type }) => {
3191
+ if (await redis.exists(key)) throw new Error(`Key "${key}" already exists`);
3177
3192
  switch (type) {
3178
3193
  case "set": {
3179
- redis.sadd(key, "value");
3194
+ await redis.sadd(key, "value");
3180
3195
  break;
3181
3196
  }
3182
3197
  case "zset": {
3183
- redis.zadd(key, {
3198
+ await redis.zadd(key, {
3184
3199
  member: "value",
3185
3200
  score: 0
3186
3201
  });
3187
3202
  break;
3188
3203
  }
3189
3204
  case "hash": {
3190
- redis.hset(key, {
3205
+ await redis.hset(key, {
3191
3206
  field: "field",
3192
3207
  value: "value"
3193
3208
  });
3194
3209
  break;
3195
3210
  }
3196
3211
  case "list": {
3197
- redis.lpush(key, "value");
3212
+ await redis.lpush(key, "value");
3198
3213
  break;
3199
3214
  }
3200
3215
  case "stream": {
3201
- redis.xadd(key, "*", {
3216
+ await redis.xadd(key, "*", {
3202
3217
  foo: "bar"
3203
3218
  });
3204
3219
  break;
3205
3220
  }
3206
3221
  case "string": {
3207
- redis.set(key, "value");
3222
+ await redis.set(key, "value");
3208
3223
  break;
3209
3224
  }
3210
3225
  case "json": {
3211
- redis.json.set(key, "$", {
3226
+ await redis.json.set(key, "$", {
3212
3227
  foo: "bar"
3213
3228
  });
3214
3229
  break;
@@ -3577,19 +3592,6 @@ var _reactcontextmenu = require('@radix-ui/react-context-menu'); var ContextMenu
3577
3592
 
3578
3593
 
3579
3594
 
3580
- // src/lib/portal-root.ts
3581
- var root;
3582
- if (typeof document !== "undefined") {
3583
- const id = "react-redis-browser-portal-root";
3584
- root = _nullishCoalesce(document.querySelector(`#${id}`), () => ( document.createElement("div")));
3585
- root.classList.add("ups-db");
3586
- root.id = "react-redis-browser-portal-root";
3587
- document.body.append(root);
3588
- }
3589
- var portalRoot = root;
3590
-
3591
- // src/components/ui/context-menu.tsx
3592
-
3593
3595
  var ContextMenu = ContextMenuPrimitive.Root;
3594
3596
  var ContextMenuTrigger = ContextMenuPrimitive.Trigger;
3595
3597
  var ContextMenuSubTrigger = React4.forwardRef(({ className, inset, children, ...props }, ref) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
@@ -4270,7 +4272,7 @@ var Spinner = ({
4270
4272
  strokeWidth: "2",
4271
4273
  strokeLinecap: "round",
4272
4274
  strokeLinejoin: "round",
4273
- className: "ml-2 h-4 w-4 animate-spin",
4275
+ className: cn("h-4 w-4 animate-spin", isLoadingText ? "ml-2" : ""),
4274
4276
  children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M21 12a9 9 0 1 1-6.219-8.56" })
4275
4277
  }
4276
4278
  )
@@ -4567,7 +4569,14 @@ function KeyActions({ dataKey, content }) {
4567
4569
  children: "Copy content"
4568
4570
  }
4569
4571
  ),
4570
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, DeleteAlertDialog, { deletionType: "key", onDeleteConfirm: async () => await deleteKey(dataKey), children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, DropdownMenuItem, { onSelect: (e) => e.preventDefault(), children: "Delete key" }) })
4572
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
4573
+ DeleteAlertDialog,
4574
+ {
4575
+ deletionType: "key",
4576
+ onDeleteConfirm: async () => await deleteKey(dataKey),
4577
+ children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, DropdownMenuItem, { onSelect: (e) => e.preventDefault(), children: "Delete key" })
4578
+ }
4579
+ )
4571
4580
  ] })
4572
4581
  ] });
4573
4582
  }
@@ -4615,7 +4624,7 @@ var TooltipContent = React11.forwardRef(({ className, sideOffset = 4, ...props }
4615
4624
  ref,
4616
4625
  sideOffset,
4617
4626
  className: cn(
4618
- "animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 overflow-hidden rounded-md bg-zinc-900 px-3 py-1.5 text-xs text-zinc-50 dark:bg-zinc-50 dark:text-zinc-900",
4627
+ "z-50 overflow-hidden rounded-md bg-zinc-900 px-3 py-1.5 text-xs text-zinc-50 animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 dark:bg-zinc-50 dark:text-zinc-900",
4619
4628
  className
4620
4629
  ),
4621
4630
  ...props
@@ -4701,7 +4710,8 @@ var CustomEditor = ({
4701
4710
  value,
4702
4711
  onChange,
4703
4712
  height,
4704
- showCopyButton
4713
+ showCopyButton,
4714
+ readOnly
4705
4715
  }) => {
4706
4716
  const monaco = _react2.useMonaco.call(void 0, );
4707
4717
  const editorRef = _react.useRef.call(void 0, );
@@ -4732,6 +4742,7 @@ var CustomEditor = ({
4732
4742
  },
4733
4743
  defaultLanguage: language,
4734
4744
  options: {
4745
+ readOnly,
4735
4746
  wordWrap: "on",
4736
4747
  overviewRulerBorder: false,
4737
4748
  overviewRulerLanes: 0,
@@ -4777,7 +4788,8 @@ var useField = ({
4777
4788
  name,
4778
4789
  form,
4779
4790
  height,
4780
- showCopyButton
4791
+ showCopyButton,
4792
+ readOnly
4781
4793
  }) => {
4782
4794
  const { field, fieldState } = _reacthookform.useController.call(void 0, {
4783
4795
  name,
@@ -4815,7 +4827,8 @@ var useField = ({
4815
4827
  value: field.value,
4816
4828
  onChange: field.onChange,
4817
4829
  height,
4818
- showCopyButton
4830
+ showCopyButton,
4831
+ readOnly
4819
4832
  }
4820
4833
  ) })
4821
4834
  };
@@ -4866,8 +4879,24 @@ var ListEditForm = ({
4866
4879
  });
4867
4880
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _reacthookform.FormProvider, { ...form, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "form", { onSubmit, className: "flex flex-col gap-2", children: [
4868
4881
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex grow flex-col gap-2", children: [
4869
- type !== "list" && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, FormItem, { name: "key", height: type === "set" ? 250 : 100, label: keyLabel }),
4870
- type === "zset" ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, NumberFormItem, { name: "value", label: valueLabel }) : type !== "set" && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, FormItem, { name: "value", height: type === "list" ? 250 : 100, label: valueLabel })
4882
+ type !== "list" && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
4883
+ FormItem,
4884
+ {
4885
+ readOnly: type === "stream",
4886
+ name: "key",
4887
+ height: type === "set" ? 250 : 100,
4888
+ label: keyLabel
4889
+ }
4890
+ ),
4891
+ type === "zset" ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, NumberFormItem, { name: "value", label: valueLabel }) : type !== "set" && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
4892
+ FormItem,
4893
+ {
4894
+ readOnly: type === "stream",
4895
+ name: "value",
4896
+ height: type === "list" ? 250 : 100,
4897
+ label: valueLabel
4898
+ }
4899
+ )
4871
4900
  ] }),
4872
4901
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex justify-end gap-2", children: [
4873
4902
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
@@ -4920,14 +4949,16 @@ var NumberFormItem = ({ name, label }) => {
4920
4949
  var FormItem = ({
4921
4950
  name,
4922
4951
  label,
4923
- height
4952
+ height,
4953
+ readOnly
4924
4954
  }) => {
4925
4955
  const form = _reacthookform.useFormContext.call(void 0, );
4926
4956
  const { editor, selector } = useField({
4927
4957
  name,
4928
4958
  form,
4929
4959
  height,
4930
- showCopyButton: true
4960
+ showCopyButton: true,
4961
+ readOnly
4931
4962
  });
4932
4963
  return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex flex-col gap-1", children: [
4933
4964
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex items-center gap-1 text-xs", children: [
@@ -5090,10 +5121,14 @@ var EditorDisplayForm = ({
5090
5121
 
5091
5122
  var DataDisplay = () => {
5092
5123
  const { selectedKey } = useDatabrowserStore();
5124
+ const { query } = useKeys();
5093
5125
  const type = useKeyType(selectedKey);
5094
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "h-full rounded-xl border p-1 bg-white", children: !selectedKey ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", {}) : !type ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "text-gray-500", children: "Loading..." }) }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _jsxruntime.Fragment, { children: type === "string" || type === "json" ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, EditorDisplay, { dataKey: selectedKey, type }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, ListDisplay, { dataKey: selectedKey, type }) }) });
5126
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "h-full rounded-xl border bg-white p-1", children: !selectedKey ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", {}) : !type ? query.isLoading ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "text-gray-500", children: "Loading..." }) }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", {}) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _jsxruntime.Fragment, { children: type === "string" || type === "json" ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, EditorDisplay, { dataKey: selectedKey, type }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, ListDisplay, { dataKey: selectedKey, type }) }) });
5095
5127
  };
5096
5128
 
5129
+ // src/components/databrowser/components/sidebar/index.tsx
5130
+
5131
+
5097
5132
  // src/components/databrowser/components/add-key-modal.tsx
5098
5133
 
5099
5134
  var _reactdialog = require('@radix-ui/react-dialog'); var DialogPrimitive = _interopRequireWildcard(_reactdialog);
@@ -5214,23 +5249,16 @@ function AddKeyModal() {
5214
5249
  }
5215
5250
  });
5216
5251
  const onSubmit = handleSubmit(async ({ key, type }) => {
5217
- try {
5218
- await addKey({ key, type });
5219
- setSelectedKey(key);
5220
- setOpen(false);
5221
- setTimeout(() => {
5222
- _optionalChain([window, 'access', _32 => _32.document, 'access', _33 => _33.querySelector, 'call', _34 => _34(`[data-key="${key}"]`), 'optionalAccess', _35 => _35.scrollIntoView, 'call', _36 => _36({
5223
- behavior: "smooth",
5224
- block: "start",
5225
- inline: "nearest"
5226
- })]);
5227
- }, 100);
5228
- } catch (error) {
5229
- toast({
5230
- description: error instanceof Error ? error.message : "An error occurred",
5231
- variant: "destructive"
5232
- });
5233
- }
5252
+ await addKey({ key, type });
5253
+ setSelectedKey(key);
5254
+ setOpen(false);
5255
+ setTimeout(() => {
5256
+ _optionalChain([window, 'access', _32 => _32.document, 'access', _33 => _33.querySelector, 'call', _34 => _34(`[data-key="${key}"]`), 'optionalAccess', _35 => _35.scrollIntoView, 'call', _36 => _36({
5257
+ behavior: "smooth",
5258
+ block: "start",
5259
+ inline: "nearest"
5260
+ })]);
5261
+ }, 100);
5234
5262
  });
5235
5263
  return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
5236
5264
  Dialog,
@@ -5472,12 +5500,15 @@ function DataTypeSelector() {
5472
5500
  // src/components/databrowser/components/sidebar/index.tsx
5473
5501
 
5474
5502
  function Sidebar() {
5475
- const { keys, query } = useKeys();
5476
- return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex h-full flex-col gap-2 rounded-xl border p-1 bg-white", children: [
5503
+ const { keys, query, refetch } = useKeys();
5504
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex h-full flex-col gap-2 rounded-xl border bg-white p-1", children: [
5477
5505
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "rounded-lg bg-zinc-100 px-3 py-2", children: [
5478
5506
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex h-10 items-center justify-between pl-1", children: [
5479
5507
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, DisplayDbSize, {}),
5480
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "flex gap-1", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, AddKeyModal, {}) })
5508
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex gap-1", children: [
5509
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Button, { className: "h-7 w-7 px-0", onClick: refetch, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Spinner, { isLoading: query.isFetching, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _iconsreact.IconRefresh, { size: 16 }) }) }),
5510
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, AddKeyModal, {})
5511
+ ] })
5481
5512
  ] }),
5482
5513
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex h-10 items-center", children: [
5483
5514
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, DataTypeSelector, {}),
@@ -5492,6 +5523,9 @@ function Sidebar() {
5492
5523
 
5493
5524
  var RedisBrowser = ({ token, url }) => {
5494
5525
  const credentials = _react.useMemo.call(void 0, () => ({ token, url }), [token, url]);
5526
+ _react.useEffect.call(void 0, () => {
5527
+ queryClient.resetQueries();
5528
+ }, [credentials.url]);
5495
5529
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _reactquery.QueryClientProvider, { client: queryClient, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _reacttooltip.TooltipProvider, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, DatabrowserProvider, { redisCredentials: credentials, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, KeysProvider, { children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "ups-db", style: { height: "100%" }, children: [
5496
5530
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
5497
5531
  _reactresizablepanels.PanelGroup,
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/components/databrowser/index.tsx
2
- import { useMemo as useMemo6 } from "react";
2
+ import { useEffect as useEffect9, useMemo as useMemo6 } from "react";
3
3
 
4
4
  // src/store.tsx
5
5
  import { createContext, useContext, useMemo, useState as useState2 } from "react";
@@ -222,6 +222,20 @@ import { IconDotsVertical as IconDotsVertical2 } from "@tabler/icons-react";
222
222
  import { QueryClientProvider } from "@tanstack/react-query";
223
223
  import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels";
224
224
 
225
+ // src/components/ui/toaster.tsx
226
+ import { Portal } from "@radix-ui/react-portal";
227
+
228
+ // src/lib/portal-root.ts
229
+ var root;
230
+ if (typeof document !== "undefined") {
231
+ const id = "react-redis-browser-portal-root";
232
+ root = document.querySelector(`#${id}`) ?? document.createElement("div");
233
+ root.classList.add("ups-db");
234
+ root.id = "react-redis-browser-portal-root";
235
+ document.body.append(root);
236
+ }
237
+ var portalRoot = root;
238
+
225
239
  // src/components/ui/toast.tsx
226
240
  import * as React2 from "react";
227
241
  import { Cross2Icon } from "@radix-ui/react-icons";
@@ -2874,7 +2888,7 @@ ToastDescription.displayName = ToastPrimitives.Description.displayName;
2874
2888
  import { jsx as jsx3, jsxs } from "react/jsx-runtime";
2875
2889
  function Toaster() {
2876
2890
  const { toasts } = useToast();
2877
- return /* @__PURE__ */ jsxs(ToastProvider, { children: [
2891
+ return /* @__PURE__ */ jsx3(Portal, { container: portalRoot, children: /* @__PURE__ */ jsxs(ToastProvider, { children: [
2878
2892
  toasts.map(({ id, title, description, action, ...props }) => /* @__PURE__ */ jsxs(Toast, { ...props, children: [
2879
2893
  /* @__PURE__ */ jsxs("div", { className: "grid gap-1", children: [
2880
2894
  title && /* @__PURE__ */ jsx3(ToastTitle, { children: title }),
@@ -2884,7 +2898,7 @@ function Toaster() {
2884
2898
  /* @__PURE__ */ jsx3(ToastClose, {})
2885
2899
  ] }, id)),
2886
2900
  /* @__PURE__ */ jsx3(ToastViewport, {})
2887
- ] });
2901
+ ] }) });
2888
2902
  }
2889
2903
 
2890
2904
  // src/components/databrowser/hooks/use-keys.tsx
@@ -3037,7 +3051,7 @@ var KeysProvider = ({ children }) => {
3037
3051
  queryKey: [FETCH_KEYS_QUERY_KEY, search],
3038
3052
  initialPageParam: 0,
3039
3053
  queryFn: async ({ pageParam: page }) => {
3040
- if (pageRef.current > page) resetCache();
3054
+ if (pageRef.current >= page) resetCache();
3041
3055
  pageRef.current = page;
3042
3056
  return await fetchKeys();
3043
3057
  },
@@ -3174,41 +3188,42 @@ var useAddKey = () => {
3174
3188
  const { redis } = useDatabrowser();
3175
3189
  const mutation = useMutation({
3176
3190
  mutationFn: async ({ key, type }) => {
3191
+ if (await redis.exists(key)) throw new Error(`Key "${key}" already exists`);
3177
3192
  switch (type) {
3178
3193
  case "set": {
3179
- redis.sadd(key, "value");
3194
+ await redis.sadd(key, "value");
3180
3195
  break;
3181
3196
  }
3182
3197
  case "zset": {
3183
- redis.zadd(key, {
3198
+ await redis.zadd(key, {
3184
3199
  member: "value",
3185
3200
  score: 0
3186
3201
  });
3187
3202
  break;
3188
3203
  }
3189
3204
  case "hash": {
3190
- redis.hset(key, {
3205
+ await redis.hset(key, {
3191
3206
  field: "field",
3192
3207
  value: "value"
3193
3208
  });
3194
3209
  break;
3195
3210
  }
3196
3211
  case "list": {
3197
- redis.lpush(key, "value");
3212
+ await redis.lpush(key, "value");
3198
3213
  break;
3199
3214
  }
3200
3215
  case "stream": {
3201
- redis.xadd(key, "*", {
3216
+ await redis.xadd(key, "*", {
3202
3217
  foo: "bar"
3203
3218
  });
3204
3219
  break;
3205
3220
  }
3206
3221
  case "string": {
3207
- redis.set(key, "value");
3222
+ await redis.set(key, "value");
3208
3223
  break;
3209
3224
  }
3210
3225
  case "json": {
3211
- redis.json.set(key, "$", {
3226
+ await redis.json.set(key, "$", {
3212
3227
  foo: "bar"
3213
3228
  });
3214
3229
  break;
@@ -3576,19 +3591,6 @@ import { ContextMenuSeparator as ContextMenuSeparator2 } from "@radix-ui/react-c
3576
3591
  import * as React4 from "react";
3577
3592
  import * as ContextMenuPrimitive from "@radix-ui/react-context-menu";
3578
3593
  import { CheckIcon, ChevronRightIcon, DotFilledIcon } from "@radix-ui/react-icons";
3579
-
3580
- // src/lib/portal-root.ts
3581
- var root;
3582
- if (typeof document !== "undefined") {
3583
- const id = "react-redis-browser-portal-root";
3584
- root = document.querySelector(`#${id}`) ?? document.createElement("div");
3585
- root.classList.add("ups-db");
3586
- root.id = "react-redis-browser-portal-root";
3587
- document.body.append(root);
3588
- }
3589
- var portalRoot = root;
3590
-
3591
- // src/components/ui/context-menu.tsx
3592
3594
  import { jsx as jsx8, jsxs as jsxs3 } from "react/jsx-runtime";
3593
3595
  var ContextMenu = ContextMenuPrimitive.Root;
3594
3596
  var ContextMenuTrigger = ContextMenuPrimitive.Trigger;
@@ -4270,7 +4272,7 @@ var Spinner = ({
4270
4272
  strokeWidth: "2",
4271
4273
  strokeLinecap: "round",
4272
4274
  strokeLinejoin: "round",
4273
- className: "ml-2 h-4 w-4 animate-spin",
4275
+ className: cn("h-4 w-4 animate-spin", isLoadingText ? "ml-2" : ""),
4274
4276
  children: /* @__PURE__ */ jsx18("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" })
4275
4277
  }
4276
4278
  )
@@ -4567,7 +4569,14 @@ function KeyActions({ dataKey, content }) {
4567
4569
  children: "Copy content"
4568
4570
  }
4569
4571
  ),
4570
- /* @__PURE__ */ jsx22(DeleteAlertDialog, { deletionType: "key", onDeleteConfirm: async () => await deleteKey(dataKey), children: /* @__PURE__ */ jsx22(DropdownMenuItem, { onSelect: (e) => e.preventDefault(), children: "Delete key" }) })
4572
+ /* @__PURE__ */ jsx22(
4573
+ DeleteAlertDialog,
4574
+ {
4575
+ deletionType: "key",
4576
+ onDeleteConfirm: async () => await deleteKey(dataKey),
4577
+ children: /* @__PURE__ */ jsx22(DropdownMenuItem, { onSelect: (e) => e.preventDefault(), children: "Delete key" })
4578
+ }
4579
+ )
4571
4580
  ] })
4572
4581
  ] });
4573
4582
  }
@@ -4615,7 +4624,7 @@ var TooltipContent = React11.forwardRef(({ className, sideOffset = 4, ...props }
4615
4624
  ref,
4616
4625
  sideOffset,
4617
4626
  className: cn(
4618
- "animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 overflow-hidden rounded-md bg-zinc-900 px-3 py-1.5 text-xs text-zinc-50 dark:bg-zinc-50 dark:text-zinc-900",
4627
+ "z-50 overflow-hidden rounded-md bg-zinc-900 px-3 py-1.5 text-xs text-zinc-50 animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 dark:bg-zinc-50 dark:text-zinc-900",
4619
4628
  className
4620
4629
  ),
4621
4630
  ...props
@@ -4701,7 +4710,8 @@ var CustomEditor = ({
4701
4710
  value,
4702
4711
  onChange,
4703
4712
  height,
4704
- showCopyButton
4713
+ showCopyButton,
4714
+ readOnly
4705
4715
  }) => {
4706
4716
  const monaco = useMonaco();
4707
4717
  const editorRef = useRef3();
@@ -4732,6 +4742,7 @@ var CustomEditor = ({
4732
4742
  },
4733
4743
  defaultLanguage: language,
4734
4744
  options: {
4745
+ readOnly,
4735
4746
  wordWrap: "on",
4736
4747
  overviewRulerBorder: false,
4737
4748
  overviewRulerLanes: 0,
@@ -4777,7 +4788,8 @@ var useField = ({
4777
4788
  name,
4778
4789
  form,
4779
4790
  height,
4780
- showCopyButton
4791
+ showCopyButton,
4792
+ readOnly
4781
4793
  }) => {
4782
4794
  const { field, fieldState } = useController({
4783
4795
  name,
@@ -4815,7 +4827,8 @@ var useField = ({
4815
4827
  value: field.value,
4816
4828
  onChange: field.onChange,
4817
4829
  height,
4818
- showCopyButton
4830
+ showCopyButton,
4831
+ readOnly
4819
4832
  }
4820
4833
  ) })
4821
4834
  };
@@ -4866,8 +4879,24 @@ var ListEditForm = ({
4866
4879
  });
4867
4880
  return /* @__PURE__ */ jsx29(FormProvider, { ...form, children: /* @__PURE__ */ jsxs19("form", { onSubmit, className: "flex flex-col gap-2", children: [
4868
4881
  /* @__PURE__ */ jsxs19("div", { className: "flex grow flex-col gap-2", children: [
4869
- type !== "list" && /* @__PURE__ */ jsx29(FormItem, { name: "key", height: type === "set" ? 250 : 100, label: keyLabel }),
4870
- type === "zset" ? /* @__PURE__ */ jsx29(NumberFormItem, { name: "value", label: valueLabel }) : type !== "set" && /* @__PURE__ */ jsx29(FormItem, { name: "value", height: type === "list" ? 250 : 100, label: valueLabel })
4882
+ type !== "list" && /* @__PURE__ */ jsx29(
4883
+ FormItem,
4884
+ {
4885
+ readOnly: type === "stream",
4886
+ name: "key",
4887
+ height: type === "set" ? 250 : 100,
4888
+ label: keyLabel
4889
+ }
4890
+ ),
4891
+ type === "zset" ? /* @__PURE__ */ jsx29(NumberFormItem, { name: "value", label: valueLabel }) : type !== "set" && /* @__PURE__ */ jsx29(
4892
+ FormItem,
4893
+ {
4894
+ readOnly: type === "stream",
4895
+ name: "value",
4896
+ height: type === "list" ? 250 : 100,
4897
+ label: valueLabel
4898
+ }
4899
+ )
4871
4900
  ] }),
4872
4901
  /* @__PURE__ */ jsxs19("div", { className: "flex justify-end gap-2", children: [
4873
4902
  /* @__PURE__ */ jsx29(
@@ -4920,14 +4949,16 @@ var NumberFormItem = ({ name, label }) => {
4920
4949
  var FormItem = ({
4921
4950
  name,
4922
4951
  label,
4923
- height
4952
+ height,
4953
+ readOnly
4924
4954
  }) => {
4925
4955
  const form = useFormContext();
4926
4956
  const { editor, selector } = useField({
4927
4957
  name,
4928
4958
  form,
4929
4959
  height,
4930
- showCopyButton: true
4960
+ showCopyButton: true,
4961
+ readOnly
4931
4962
  });
4932
4963
  return /* @__PURE__ */ jsxs19("div", { className: "flex flex-col gap-1", children: [
4933
4964
  /* @__PURE__ */ jsxs19("div", { className: "flex items-center gap-1 text-xs", children: [
@@ -5090,10 +5121,14 @@ var EditorDisplayForm = ({
5090
5121
  import { Fragment as Fragment7, jsx as jsx32 } from "react/jsx-runtime";
5091
5122
  var DataDisplay = () => {
5092
5123
  const { selectedKey } = useDatabrowserStore();
5124
+ const { query } = useKeys();
5093
5125
  const type = useKeyType(selectedKey);
5094
- return /* @__PURE__ */ jsx32("div", { className: "h-full rounded-xl border p-1 bg-white", children: !selectedKey ? /* @__PURE__ */ jsx32("div", {}) : !type ? /* @__PURE__ */ jsx32("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsx32("span", { className: "text-gray-500", children: "Loading..." }) }) : /* @__PURE__ */ jsx32(Fragment7, { children: type === "string" || type === "json" ? /* @__PURE__ */ jsx32(EditorDisplay, { dataKey: selectedKey, type }) : /* @__PURE__ */ jsx32(ListDisplay, { dataKey: selectedKey, type }) }) });
5126
+ return /* @__PURE__ */ jsx32("div", { className: "h-full rounded-xl border bg-white p-1", children: !selectedKey ? /* @__PURE__ */ jsx32("div", {}) : !type ? query.isLoading ? /* @__PURE__ */ jsx32("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsx32("span", { className: "text-gray-500", children: "Loading..." }) }) : /* @__PURE__ */ jsx32("div", {}) : /* @__PURE__ */ jsx32(Fragment7, { children: type === "string" || type === "json" ? /* @__PURE__ */ jsx32(EditorDisplay, { dataKey: selectedKey, type }) : /* @__PURE__ */ jsx32(ListDisplay, { dataKey: selectedKey, type }) }) });
5095
5127
  };
5096
5128
 
5129
+ // src/components/databrowser/components/sidebar/index.tsx
5130
+ import { IconRefresh } from "@tabler/icons-react";
5131
+
5097
5132
  // src/components/databrowser/components/add-key-modal.tsx
5098
5133
  import { useState as useState8 } from "react";
5099
5134
  import { DialogDescription as DialogDescription2 } from "@radix-ui/react-dialog";
@@ -5214,23 +5249,16 @@ function AddKeyModal() {
5214
5249
  }
5215
5250
  });
5216
5251
  const onSubmit = handleSubmit(async ({ key, type }) => {
5217
- try {
5218
- await addKey({ key, type });
5219
- setSelectedKey(key);
5220
- setOpen(false);
5221
- setTimeout(() => {
5222
- window.document.querySelector(`[data-key="${key}"]`)?.scrollIntoView({
5223
- behavior: "smooth",
5224
- block: "start",
5225
- inline: "nearest"
5226
- });
5227
- }, 100);
5228
- } catch (error) {
5229
- toast({
5230
- description: error instanceof Error ? error.message : "An error occurred",
5231
- variant: "destructive"
5252
+ await addKey({ key, type });
5253
+ setSelectedKey(key);
5254
+ setOpen(false);
5255
+ setTimeout(() => {
5256
+ window.document.querySelector(`[data-key="${key}"]`)?.scrollIntoView({
5257
+ behavior: "smooth",
5258
+ block: "start",
5259
+ inline: "nearest"
5232
5260
  });
5233
- }
5261
+ }, 100);
5234
5262
  });
5235
5263
  return /* @__PURE__ */ jsxs23(
5236
5264
  Dialog,
@@ -5472,12 +5500,15 @@ function DataTypeSelector() {
5472
5500
  // src/components/databrowser/components/sidebar/index.tsx
5473
5501
  import { jsx as jsx41, jsxs as jsxs30 } from "react/jsx-runtime";
5474
5502
  function Sidebar() {
5475
- const { keys, query } = useKeys();
5476
- return /* @__PURE__ */ jsxs30("div", { className: "flex h-full flex-col gap-2 rounded-xl border p-1 bg-white", children: [
5503
+ const { keys, query, refetch } = useKeys();
5504
+ return /* @__PURE__ */ jsxs30("div", { className: "flex h-full flex-col gap-2 rounded-xl border bg-white p-1", children: [
5477
5505
  /* @__PURE__ */ jsxs30("div", { className: "rounded-lg bg-zinc-100 px-3 py-2", children: [
5478
5506
  /* @__PURE__ */ jsxs30("div", { className: "flex h-10 items-center justify-between pl-1", children: [
5479
5507
  /* @__PURE__ */ jsx41(DisplayDbSize, {}),
5480
- /* @__PURE__ */ jsx41("div", { className: "flex gap-1", children: /* @__PURE__ */ jsx41(AddKeyModal, {}) })
5508
+ /* @__PURE__ */ jsxs30("div", { className: "flex gap-1", children: [
5509
+ /* @__PURE__ */ jsx41(Button, { className: "h-7 w-7 px-0", onClick: refetch, children: /* @__PURE__ */ jsx41(Spinner, { isLoading: query.isFetching, children: /* @__PURE__ */ jsx41(IconRefresh, { size: 16 }) }) }),
5510
+ /* @__PURE__ */ jsx41(AddKeyModal, {})
5511
+ ] })
5481
5512
  ] }),
5482
5513
  /* @__PURE__ */ jsxs30("div", { className: "flex h-10 items-center", children: [
5483
5514
  /* @__PURE__ */ jsx41(DataTypeSelector, {}),
@@ -5492,6 +5523,9 @@ function Sidebar() {
5492
5523
  import { jsx as jsx42, jsxs as jsxs31 } from "react/jsx-runtime";
5493
5524
  var RedisBrowser = ({ token, url }) => {
5494
5525
  const credentials = useMemo6(() => ({ token, url }), [token, url]);
5526
+ useEffect9(() => {
5527
+ queryClient.resetQueries();
5528
+ }, [credentials.url]);
5495
5529
  return /* @__PURE__ */ jsx42(QueryClientProvider, { client: queryClient, children: /* @__PURE__ */ jsx42(TooltipProvider, { children: /* @__PURE__ */ jsx42(DatabrowserProvider, { redisCredentials: credentials, children: /* @__PURE__ */ jsx42(KeysProvider, { children: /* @__PURE__ */ jsxs31("div", { className: "ups-db", style: { height: "100%" }, children: [
5496
5530
  /* @__PURE__ */ jsxs31(
5497
5531
  PanelGroup,
package/package.json CHANGED
@@ -1 +1 @@
1
- { "name": "@upstash/react-redis-browser", "version": "v0.1.2-canary-6", "main": "./dist/index.js", "types": "./dist/index.d.ts", "license": "MIT", "private": false, "publishConfig": { "access": "public" }, "bugs": { "url": "https://github.com/upstash/react-redis-browser/issues" }, "homepage": "https://github.com/upstash/react-redis-browser", "files": [ "./dist/**" ], "scripts": { "build": "tsup", "dev": "vite", "lint": "tsc && eslint", "fmt": "prettier --write ." }, "lint-staged": { "**/*.{js,ts,tsx}": [ "prettier --write", "eslint --fix" ] }, "dependencies": { "@ianvs/prettier-plugin-sort-imports": "^4.4.0", "@monaco-editor/react": "^4.6.0", "@radix-ui/react-alert-dialog": "^1.0.5", "@radix-ui/react-context-menu": "^2.2.2", "@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-dropdown-menu": "^2.1.2", "@radix-ui/react-icons": "1.3.0", "@radix-ui/react-popover": "^1.0.7", "@radix-ui/react-scroll-area": "^1.0.3", "@radix-ui/react-select": "^2.0.0", "@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-toast": "^1.1.5", "@radix-ui/react-tooltip": "^1.0.7", "@tabler/icons-react": "^3.19.0", "@tanstack/react-query": "^5.32.0", "@types/bytes": "^3.1.4", "@upstash/redis": "^1.31.6", "bytes": "^3.1.2", "react-hook-form": "^7.53.0", "react-resizable-panels": "^2.1.4", "zustand": "5.0.0" }, "devDependencies": { "postcss-prefix-selector": "^2.1.0", "@types/node": "^22.8.4", "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", "@typescript-eslint/eslint-plugin": "8.4.0", "@typescript-eslint/parser": "8.4.0", "@vitejs/plugin-react": "^4.1.0", "autoprefixer": "^10.4.14", "class-variance-authority": "^0.7.0", "clsx": "^2.0.0", "eslint": "9.10.0", "eslint-plugin-unicorn": "55.0.0", "postcss": "^8.4.31", "prettier": "^3.0.3", "prettier-plugin-tailwindcss": "^0.5.5", "react": "^18.3.1", "react-dom": "^18.3.1", "tailwind-merge": "^2.5.4", "tailwindcss": "^3.4.14", "tailwindcss-animate": "^1.0.7", "tsup": "^8.3.5", "typescript": "^5.0.4", "vite": "^5.4.10", "vite-tsconfig-paths": "^5.0.1" }, "peerDependencies": { "react": "^18.2.0 || ^19", "react-dom": "^18.2.0 || ^19" } }
1
+ { "name": "@upstash/react-redis-browser", "version": "v0.1.2-canary-8", "main": "./dist/index.js", "types": "./dist/index.d.ts", "license": "MIT", "private": false, "publishConfig": { "access": "public" }, "bugs": { "url": "https://github.com/upstash/react-redis-browser/issues" }, "homepage": "https://github.com/upstash/react-redis-browser", "files": [ "./dist/**" ], "scripts": { "build": "tsup", "dev": "vite", "lint": "tsc && eslint", "fmt": "prettier --write ./src" }, "lint-staged": { "**/*.{js,ts,tsx}": [ "prettier --write", "eslint --fix" ] }, "dependencies": { "@ianvs/prettier-plugin-sort-imports": "^4.4.0", "@monaco-editor/react": "^4.6.0", "@radix-ui/react-alert-dialog": "^1.0.5", "@radix-ui/react-context-menu": "^2.2.2", "@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-dropdown-menu": "^2.1.2", "@radix-ui/react-icons": "1.3.0", "@radix-ui/react-popover": "^1.0.7", "@radix-ui/react-portal": "^1.1.2", "@radix-ui/react-scroll-area": "^1.0.3", "@radix-ui/react-select": "^2.0.0", "@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-toast": "^1.1.5", "@radix-ui/react-tooltip": "^1.0.7", "@tabler/icons-react": "^3.19.0", "@tanstack/react-query": "^5.32.0", "@types/bytes": "^3.1.4", "@upstash/redis": "^1.34.3", "bytes": "^3.1.2", "react-hook-form": "^7.53.0", "react-resizable-panels": "^2.1.4", "zustand": "5.0.0" }, "devDependencies": { "postcss-prefix-selector": "^2.1.0", "@types/node": "^22.8.4", "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", "@typescript-eslint/eslint-plugin": "8.4.0", "@typescript-eslint/parser": "8.4.0", "@vitejs/plugin-react": "^4.1.0", "autoprefixer": "^10.4.14", "class-variance-authority": "^0.7.0", "clsx": "^2.0.0", "eslint": "9.10.0", "eslint-plugin-unicorn": "55.0.0", "postcss": "^8.4.31", "prettier": "^3.0.3", "prettier-plugin-tailwindcss": "^0.5.5", "react": "^18.3.1", "react-dom": "^18.3.1", "tailwind-merge": "^2.5.4", "tailwindcss": "^3.4.14", "tailwindcss-animate": "^1.0.7", "tsup": "^8.3.5", "typescript": "^5.0.4", "vite": "^5.4.10", "vite-tsconfig-paths": "^5.0.1" }, "peerDependencies": { "react": "^18.2.0 || ^19", "react-dom": "^18.2.0 || ^19" } }