@threenine/nuxstr-comments 1.5.0 → 1.5.2

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 CHANGED
@@ -38,38 +38,43 @@ npx nuxi module add @threenine/nuxstr-comments
38
38
  That's it! You can now use Nuxstr Comments in your Nuxt app ✨
39
39
 
40
40
  ### Usage
41
-
42
- 1. Ensure @nuxt/content is enabled and your blog post pages use ContentDoc or render content files.
43
- 2. Add the comments component where you want comments to appear (usually below a ContentDoc):
44
-
45
- ```vue
46
- <template>
47
- <div>
48
- <ContentDoc />
49
- <NuxstrComments />
50
- </div>
51
- </template>
52
- ```
53
-
54
41
  By default, the component tags comments by the current route path (e.g., content:/blog/my-post) and fetches them from configured relays.
55
42
 
56
- 3. Configuration (nuxt.config.ts):
43
+ 1. Configuration (nuxt.config.ts):
57
44
 
58
45
  ```ts
59
46
  export default defineNuxtConfig({
60
47
  modules: [
61
- '@nuxt/content',
62
48
  '@threenine/nuxstr-comments',
63
49
  ],
64
50
  nuxstrComments: {
65
51
  relays: ['wss://relay.damus.io', 'wss://relay.nostr.band'],
66
52
  tagStrategy: 'path',
67
- tagPrefix: 'content:',
53
+ tagPrefix: 'comment:',
68
54
  },
69
55
  })
70
56
  ```
71
57
 
72
- When a user attempts to post, they will be prompted to log in with their Nostr browser extension (NIP-07). Comments are published as kind:1 notes tagged with a `t` tag containing the content tag (e.g., `content:/blog/my-post`). Rendering of comment bodies uses @nuxt/content's ContentRendererMarkdown component so users can write markdown.
58
+
59
+ 2. Add the comments component where you want comments to appear (usually below a ContentDoc):
60
+
61
+ ```vue
62
+ <template>
63
+ <div>
64
+ <NuxstrComments />
65
+ </div>
66
+ </template>
67
+ ```
68
+
69
+ Select your preferred relays from the list of [relays](https://nostrwat.ch/) and configure the tag strategy and tag prefix.
70
+
71
+ When a user attempts to post, they will be prompted to log in with their Nostr browser extension [NIP-07](https://github.com/nostr-protocol/nips/blob/master/07.md).
72
+
73
+ Comments are published as kind: 1111 as defined in [NIP-22](https://github.com/nostr-protocol/nips/blob/master/22.md)
74
+ notes tagged with a `t` tag containing the content tag (e.g., `comment:/blog/my-post`).
75
+
76
+ Replys to comments are enabled, and are also published as kind: 1111, as defined in [NIP-22](https://github.com/nostr-protocol/nips/blob/master/22.md)
77
+
73
78
 
74
79
 
75
80
  ## Contribution
@@ -79,26 +84,26 @@ When a user attempts to post, they will be prompted to log in with their Nostr b
79
84
 
80
85
  ```bash
81
86
  # Install dependencies
82
- npm install
87
+ pnpm install
83
88
 
84
89
  # Generate type stubs
85
- npm run dev:prepare
90
+ pnpm run dev:prepare
86
91
 
87
92
  # Develop with the playground
88
- npm run dev
93
+ pnpm run dev
89
94
 
90
95
  # Build the playground
91
- npm run dev:build
96
+ pnpm run dev:build
92
97
 
93
98
  # Run ESLint
94
- npm run lint
99
+ pnpm run lint
95
100
 
96
101
  # Run Vitest
97
- npm run test
98
- npm run test:watch
102
+ pnpm run test
103
+ pnpm run test:watch
99
104
 
100
105
  # Release new version
101
- npm run release
106
+ pnpm run release
102
107
  ```
103
108
 
104
109
  </details>
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@threenine/nuxstr-comments",
3
3
  "configKey": "nuxstrComments",
4
- "version": "1.5.0",
4
+ "version": "1.5.2",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "3.6.1"
package/dist/module.mjs CHANGED
@@ -55,8 +55,8 @@ const module = defineNuxtModule({
55
55
  filePath: resolver.resolve("./runtime/components/ScaffoldComment.vue")
56
56
  });
57
57
  addComponent({
58
- name: "CommentCommandBar",
59
- filePath: resolver.resolve("./runtime/components/CommentCommandBar.vue")
58
+ name: "ReplyButton",
59
+ filePath: resolver.resolve("./runtime/components/ReplyButton.vue")
60
60
  });
61
61
  addComponent({
62
62
  name: "CommentView",
@@ -1,6 +1,6 @@
1
1
  type __VLS_Props = {
2
2
  content: string;
3
- id?: string;
3
+ id: string;
4
4
  };
5
5
  declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
6
6
  export default _default;
@@ -1,19 +1,23 @@
1
1
  <script setup>
2
+ import { onMounted } from "vue";
2
3
  import { marked } from "marked";
3
4
  const props = defineProps({
4
5
  content: { type: String, required: true },
5
- id: { type: String, required: false }
6
+ id: { type: String, required: true }
7
+ });
8
+ async function renderMarkdown(md) {
9
+ return marked.parse(md);
10
+ }
11
+ onMounted(async () => {
12
+ const targetEl = document.getElementById("comment-content");
13
+ if (!targetEl) return;
14
+ targetEl.innerHTML = await renderMarkdown(props.content);
6
15
  });
7
16
  </script>
8
17
 
9
18
  <template>
10
- <div class="prose prose-sm prose-invert mt-2 mb-2">
11
- <!-- eslint-disable-next-line vue/no-v-html -->
12
- <div
13
- class="mb-4"
14
- v-html="marked.parse(props.content)"
15
- />
16
- <!-- eslint-disable-next-line vue/no-v-html -->
17
- <comment-command-bar :content-id="props.id" />
19
+ <div class="mt-2 mb-2">
20
+ <div id="comment-content" />
21
+ <ReplyButton :content-id="props.id" />
18
22
  </div>
19
23
  </template>
@@ -1,6 +1,6 @@
1
1
  type __VLS_Props = {
2
2
  content: string;
3
- id?: string;
3
+ id: string;
4
4
  };
5
5
  declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
6
6
  export default _default;
@@ -1,7 +1,7 @@
1
1
  <script setup>
2
2
  import { onMounted } from "vue";
3
- import { useNuxstr } from "../composables/useNuxstr";
4
- import { useComments } from "../composables/useComments";
3
+ import useNuxstr from "../composables/useNuxstr";
4
+ import useComments from "../composables/useComments";
5
5
  const props = defineProps({
6
6
  contentId: { type: String, required: false }
7
7
  });
@@ -1,6 +1,6 @@
1
1
  <script setup>
2
2
  import { ref } from "vue";
3
- import { useComments } from "../composables/useComments";
3
+ import useComments from "../composables/useComments";
4
4
  const props = defineProps({
5
5
  contentId: { type: String, required: false }
6
6
  });
@@ -1,5 +1,5 @@
1
1
  <script setup>
2
- import { useReplies } from "../composables/useReplies";
2
+ import useReplies from "../composables/useReplies";
3
3
  import { ref } from "vue";
4
4
  const props = defineProps({
5
5
  rootId: { type: String, required: true }
@@ -23,7 +23,7 @@ async function postReply(comment) {
23
23
  </script>
24
24
 
25
25
  <template>
26
- <div class="text-sm text-muted-foreground mt-16 p-6">
26
+ <div class="text-sm text-muted-foreground mt-4 p-6">
27
27
  <div class="flex gap-2">
28
28
  <div class="flex-1">
29
29
  <UTextarea
@@ -33,13 +33,13 @@ async function postReply(comment) {
33
33
  :rows="4"
34
34
  />
35
35
  </div>
36
- <div class="flex flex-col justify-center items-center p-2">
36
+ <div class="flex flex-col justify-center items-centerp-2">
37
37
  <UButton
38
38
  icon="mingcute:send-line"
39
39
  size="xl"
40
40
  color="primary"
41
41
  variant="solid"
42
- class="mb-4 mr-2"
42
+
43
43
  @click="postReply(content)"
44
44
  />
45
45
  </div>
@@ -1,7 +1,7 @@
1
1
  <script setup>
2
- import { useNuxstr } from "../composables/useNuxstr";
3
- import { useReplies } from "../composables/useReplies";
4
- import { onMounted, ref } from "vue";
2
+ import useNuxstr from "../composables/useNuxstr";
3
+ import useReplies from "../composables/useReplies";
4
+ import { computed, onMounted, ref } from "vue";
5
5
  const props = defineProps({
6
6
  contentId: { type: String, required: true }
7
7
  });
@@ -14,42 +14,44 @@ function toggleReply() {
14
14
  onMounted(() => {
15
15
  subscribeReplies();
16
16
  });
17
+ const showChip = computed(() => replies.value.length > 0);
17
18
  </script>
18
19
 
19
20
  <template>
20
21
  <div>
21
- <div class="flex items-center gap-4 mx-auto mt-4 mb-4">
22
+ <div class="flex items-center gap-4 mx-auto mt-2 mb-1">
22
23
  <u-chip
24
+ :show="showChip"
23
25
  :text="replies.length"
24
26
  size="3xl"
25
27
  inset
26
28
  >
27
29
  <u-button
28
- variant="ghost"
30
+ variant="subtle"
29
31
  icon="mdi:message-reply-text-outline"
30
32
  title="Reply"
31
- square
33
+ color="primary"
32
34
  class="rounded-full hover:bg-gray-900"
33
35
  @click="toggleReply"
34
36
  />
35
37
  </u-chip>
36
38
  </div>
37
- <UCollapsible
38
- class="flex flex-col gap-2 w-48 p-16"
39
+ <u-collapsible
40
+ class="flex flex-col gap-2 w-48 p-4"
39
41
  :open
40
42
  >
41
43
  <template #content>
42
- <div>
44
+ <div class="space-y-4">
43
45
  <reply-view :replies="replies" />
44
46
  </div>
45
47
 
46
48
  <div
47
49
  v-if="isLoggedIn"
48
- class="mt-4"
50
+ class="mt-2"
49
51
  >
50
52
  <post-reply :root-id="props.contentId" />
51
53
  </div>
52
54
  </template>
53
- </UCollapsible>
55
+ </u-collapsible>
54
56
  </div>
55
57
  </template>
@@ -9,14 +9,16 @@ const props = defineProps({
9
9
  <div
10
10
  v-for="reply in props.replies"
11
11
  :key="reply.id"
12
- class="rounded border border-gray-900 p-3 mt-2 mb-2"
12
+ class="rounded-md border p-3 mt-2 mb-2"
13
13
  >
14
14
  <div>
15
15
  <comment-author
16
16
  :profile="reply.profile"
17
17
  :created-at="reply.created_at"
18
18
  />
19
- <p>{{ reply.content }}</p>
19
+ <p class="p-2 text-base">
20
+ {{ reply.content }}
21
+ </p>
20
22
  </div>
21
23
  </div>
22
24
  </div>
@@ -1,23 +1,8 @@
1
- import type { Comment } from '~/src/runtime/types';
2
- export declare function useComments(customContentId?: string): {
3
- loading: import("vue").Ref<boolean, boolean>;
4
- error: import("vue").Ref<string | null, string | null>;
5
- comments: import("vue").Ref<{
6
- id: string;
7
- pubkey: string;
8
- created_at: number;
9
- content: string;
10
- profile?: {
11
- pubkey: string;
12
- display_name?: string | undefined;
13
- about?: string | undefined;
14
- image?: string | undefined;
15
- nip05?: string | undefined;
16
- lud06?: string | undefined;
17
- lud16?: string | undefined;
18
- website?: string | undefined;
19
- } | undefined;
20
- }[], Comment[] | {
1
+ import { type Ref, type UnwrapRef } from 'vue';
2
+ declare function useComments(customContentId?: string): {
3
+ loading: Ref<boolean, boolean>;
4
+ error: Ref<UnwrapRef<string | null>, UnwrapRef<string | null>>;
5
+ comments: import("vue").ComputedRef<{
21
6
  id: string;
22
7
  pubkey: string;
23
8
  created_at: number;
@@ -37,3 +22,4 @@ export declare function useComments(customContentId?: string): {
37
22
  subscribeComments: () => Promise<void>;
38
23
  postComment: (comment: string) => Promise<boolean>;
39
24
  };
25
+ export default useComments;
@@ -1,15 +1,18 @@
1
1
  import { computed, ref } from "vue";
2
- import { useRoute, useRuntimeConfig, useRequestURL } from "#imports";
3
- import { useNuxstr } from "./useNuxstr.js";
2
+ import { useRequestURL, useRoute, useRuntimeConfig } from "#imports";
3
+ import useNuxstr from "./useNuxstr.js";
4
4
  import { NDKEvent, NDKKind } from "@nostr-dev-kit/ndk";
5
- export function useComments(customContentId) {
5
+ function useComments(customContentId) {
6
6
  const { ndk, connect, isLoggedIn, mapComment, pubkey, fetchProfile } = useNuxstr();
7
7
  const route = useRoute();
8
8
  const config = useRuntimeConfig();
9
9
  const opts = config.public?.nuxstrComments || {};
10
10
  const loading = ref(false);
11
11
  const error = ref(null);
12
- const comments = ref([]);
12
+ const commentsData = ref([]);
13
+ const comments = computed(() => {
14
+ return commentsData.value.slice().sort((a, b) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime());
15
+ });
13
16
  const contentId = computed(() => {
14
17
  if (customContentId) return customContentId;
15
18
  return route.path;
@@ -27,12 +30,18 @@ export function useComments(customContentId) {
27
30
  }
28
31
  async function subscribeComments() {
29
32
  await connect();
30
- const filter = { kinds: [NDKKind.GenericReply], ["#t"]: [tagValue()], limit: 100, ["#k"]: ["web"], ["#A"]: [fullUrl(contentId.value)] };
31
- const sub = await ndk.subscribe(filter);
33
+ const filter = {
34
+ kinds: [NDKKind.GenericReply],
35
+ ["#t"]: [tagValue()],
36
+ limit: 100,
37
+ ["#k"]: ["web"],
38
+ ["#A"]: [fullUrl(contentId.value)]
39
+ };
40
+ const sub = ndk.subscribe(filter);
32
41
  sub.on("event", async (event) => {
33
42
  const comment = mapComment(event);
34
43
  comment.profile = await fetchProfile(event.pubkey);
35
- comments.value.push(comment);
44
+ commentsData.value.push(comment);
36
45
  });
37
46
  }
38
47
  async function postComment(comment) {
@@ -58,9 +67,10 @@ export function useComments(customContentId) {
58
67
  // Defined NIP 73
59
68
  ["K", "web"],
60
69
  // Defined NIP 73,
61
- ["p", pubkey.value]
70
+ ["p", pubkey ?? ""]
62
71
  ];
63
72
  return event;
64
73
  }
65
74
  return { loading, error, comments, isLoggedIn, subscribeComments, postComment };
66
75
  }
76
+ export default useComments;
@@ -1,13 +1,14 @@
1
- import NDK, { type NDKEvent } from '@nostr-dev-kit/ndk';
1
+ import NDK, { type NDKEvent, type NDKUserProfile } from '@nostr-dev-kit/ndk';
2
2
  import type { Comment, Profile } from '../types/index.js';
3
- export declare function useNuxstr(): {
3
+ declare function useNuxstr(): {
4
4
  readonly ndk: NDK;
5
5
  connect: () => Promise<NDK>;
6
6
  login: () => Promise<void>;
7
7
  logout: () => void;
8
8
  isLoggedIn: import("vue").ComputedRef<boolean>;
9
- pubkey: import("vue").Ref<string | null | undefined, string | null | undefined>;
10
- mapProfile: (profile: NDKUserProfile) => Profile;
9
+ pubkey: string | undefined;
10
+ mapProfile: (profile: NDKUserProfile | null) => Profile;
11
11
  mapComment: (event: NDKEvent) => Comment;
12
12
  fetchProfile: (pubkey: string) => Promise<Profile | undefined>;
13
13
  };
14
+ export default useNuxstr;
@@ -2,19 +2,21 @@ import { computed, ref } from "vue";
2
2
  import { useRuntimeConfig } from "#imports";
3
3
  import NDK, { NDKNip07Signer } from "@nostr-dev-kit/ndk";
4
4
  import { useToast } from "#ui/composables/useToast";
5
- export function useNuxstr() {
5
+ function useNuxstr() {
6
+ const DEFAULT_PUBKEY = "";
6
7
  const w = globalThis;
7
8
  if (!w.__nuxstr) {
8
9
  w.__nuxstr = {
9
10
  ndk: null,
10
11
  signer: null,
11
- pubkey: ref(null),
12
+ pubkey: ref(DEFAULT_PUBKEY),
12
13
  isConnecting: ref(false),
13
14
  isConnected: ref(false),
14
- userProfile: ref(null)
15
+ userProfile: ref(void 0)
15
16
  };
16
17
  }
17
18
  const state = w.__nuxstr;
19
+ const DEFAULT_TIMESTAMP = 0;
18
20
  const config = useRuntimeConfig();
19
21
  const opts = config.public?.nuxstrComments || {};
20
22
  function initializeNDK() {
@@ -52,13 +54,14 @@ export function useNuxstr() {
52
54
  async function login() {
53
55
  if (await checkExtension()) {
54
56
  const ndk = initializeNDK();
57
+ await connect();
55
58
  const signer = new NDKNip07Signer();
56
59
  ndk.signer = signer;
57
- const user = await signer.user();
60
+ const account = await signer.user();
58
61
  state.signer = signer;
59
- state.pubkey.value = user.pubkey;
60
- await connect();
61
- const profile = await ndk.getUser({ pubkey: user.pubkey });
62
+ state.pubkey.value = account.pubkey;
63
+ const user = ndk.getUser({ pubkey: account.pubkey });
64
+ const profile = await user.fetchProfile();
62
65
  state.userProfile.value = mapProfile(profile);
63
66
  }
64
67
  }
@@ -66,14 +69,15 @@ export function useNuxstr() {
66
69
  return {
67
70
  id: event.id,
68
71
  pubkey: event.pubkey,
69
- created_at: event.created_at || 0,
72
+ created_at: event.created_at || DEFAULT_TIMESTAMP,
70
73
  content: event.content,
71
- profile: null
74
+ profile: void 0
72
75
  };
73
76
  }
74
77
  async function fetchProfile(pubkey) {
75
78
  try {
76
- const user = state.ndk.getUser({ pubkey });
79
+ const ndk = initializeNDK();
80
+ const user = ndk.getUser({ pubkey });
77
81
  const profile = await user.fetchProfile();
78
82
  return mapProfile(profile);
79
83
  } catch (error) {
@@ -82,6 +86,7 @@ export function useNuxstr() {
82
86
  }
83
87
  }
84
88
  function mapProfile(profile) {
89
+ if (profile === null) return {};
85
90
  return {
86
91
  display_name: profile.displayName,
87
92
  about: profile.about,
@@ -94,7 +99,7 @@ export function useNuxstr() {
94
99
  }
95
100
  function logout() {
96
101
  state.signer = null;
97
- state.pubkey.value = null;
102
+ state.pubkey.value = DEFAULT_PUBKEY;
98
103
  }
99
104
  return {
100
105
  get ndk() {
@@ -104,9 +109,10 @@ export function useNuxstr() {
104
109
  login,
105
110
  logout,
106
111
  isLoggedIn,
107
- pubkey: state.pubkey,
112
+ pubkey: state.pubkey.value,
108
113
  mapProfile,
109
114
  mapComment,
110
115
  fetchProfile
111
116
  };
112
117
  }
118
+ export default useNuxstr;
@@ -1,4 +1,4 @@
1
- export declare function useReplies(rootCommentId?: string): {
1
+ declare function useReplies(rootCommentId: string): {
2
2
  subscribeReplies: () => Promise<void>;
3
3
  replies: import("vue").ComputedRef<{
4
4
  id: string;
@@ -17,5 +17,5 @@ export declare function useReplies(rootCommentId?: string): {
17
17
  } | undefined;
18
18
  }[]>;
19
19
  reply: (comment: string) => Promise<boolean>;
20
- count: import("vue").ComputedRef<Promise<string>>;
21
20
  };
21
+ export default useReplies;
@@ -1,7 +1,7 @@
1
1
  import { computed, ref } from "vue";
2
- import { useNuxstr } from "./useNuxstr.js";
2
+ import useNuxstr from "./useNuxstr.js";
3
3
  import { NDKEvent, NDKKind } from "@nostr-dev-kit/ndk";
4
- export function useReplies(rootCommentId) {
4
+ function useReplies(rootCommentId) {
5
5
  const { ndk, connect, mapComment, pubkey, fetchProfile } = useNuxstr();
6
6
  const repliesData = ref([]);
7
7
  const error = ref(null);
@@ -11,7 +11,7 @@ export function useReplies(rootCommentId) {
11
11
  async function subscribeReplies() {
12
12
  await connect();
13
13
  const filter = { kinds: [NDKKind.GenericReply], limit: 100, ["#e"]: [rootCommentId] };
14
- const sub = await ndk.subscribe(filter);
14
+ const sub = ndk.subscribe(filter);
15
15
  sub.on("event", async (event) => {
16
16
  const reply2 = mapComment(event);
17
17
  reply2.profile = await fetchProfile(event.pubkey);
@@ -33,16 +33,10 @@ export function useReplies(rootCommentId) {
33
33
  ["e", `${rootCommentId}`],
34
34
  ["k", `${NDKKind.GenericReply}`],
35
35
  // The parent kind
36
- ["p", pubkey.value]
36
+ ["p", pubkey ?? ""]
37
37
  ];
38
38
  return event;
39
39
  }
40
- const count = computed(async () => {
41
- await connect();
42
- const filter = { kinds: [NDKKind.GenericReply], limit: 100, ["#e"]: [rootCommentId] };
43
- const events = await ndk.fetchEvents(filter);
44
- console.log(events);
45
- return `${Array.from(events).length}`;
46
- });
47
- return { subscribeReplies, replies, reply, count };
40
+ return { subscribeReplies, replies, reply };
48
41
  }
42
+ export default useReplies;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@threenine/nuxstr-comments",
3
- "version": "1.5.0",
3
+ "version": "1.5.2",
4
4
  "description": "Nuxt module to enable Nostr Comments on Nuxt 4 based websites",
5
5
  "repository": "threenine/nuxstr-comments",
6
6
  "license": "MIT",
@@ -24,7 +24,6 @@
24
24
  ],
25
25
  "dependencies": {
26
26
  "@nostr-dev-kit/ndk": "^2.14.33",
27
- "@nuxt/kit": "^4.0.3",
28
27
  "defu": "^6.1.4",
29
28
  "marked": "^16.2.1"
30
29
  },
@@ -32,13 +31,15 @@
32
31
  "@nuxt/devtools": "^2.6.2",
33
32
  "@nuxt/eslint": "1.9.0",
34
33
  "@nuxt/eslint-config": "^1.9.0",
34
+ "@nuxt/kit": "^4.1.2",
35
35
  "@nuxt/module-builder": "^1.0.2",
36
- "@nuxt/schema": "^4.1.1",
36
+ "@nuxt/schema": "^4.1.2",
37
37
  "@nuxt/scripts": "0.11.10",
38
38
  "@nuxt/test-utils": "^3.19.2",
39
39
  "@nuxt/ui": "^3.3.3",
40
40
  "@testing-library/jest-dom": "^6.8.0",
41
41
  "@testing-library/vue": "^8.1.0",
42
+ "@types/node": "latest",
42
43
  "@unhead/vue": "^2.0.14",
43
44
  "changelogen": "^0.6.2",
44
45
  "eslint": "^9.35.0",
@@ -46,8 +47,7 @@
46
47
  "nuxt": "^4.1.2",
47
48
  "typescript": "~5.9.2",
48
49
  "vitest": "^3.2.4",
49
- "vue-tsc": "^3.0.6",
50
- "@types/node": "latest"
50
+ "vue-tsc": "^3.0.6"
51
51
  },
52
52
  "scripts": {
53
53
  "dev": "pnpm run dev:prepare && nuxi dev playground",