nuxt-atproto 0.0.1

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/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 dxlliv
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
19
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
20
+ IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,271 @@
1
+ # nuxt-atproto
2
+
3
+ [![npm version][npm-version-src]][npm-version-href]
4
+ [![npm downloads][npm-downloads-src]][npm-downloads-href]
5
+ [![License][license-src]][license-href]
6
+ [![Nuxt][nuxt-src]][nuxt-href]
7
+
8
+ > Easily integrate [Bluesky](https://bsky.app) login and [AT Protocol](https://atproto.com/) authentication into your
9
+ Nuxt.js app.
10
+
11
+ ## Overview
12
+
13
+ `nuxt-atproto` is a Nuxt.js module that simplifies the OAuth authentication via AT Protocol.
14
+
15
+ It handles the login and the session management using the `@atproto/oauth-client` library,
16
+ providing public and authenticated agents for seamless interaction with AT Protocol services.
17
+
18
+ ## Features
19
+
20
+ - SSR-friendly login via Bluesky and AT Protocol with automatic service endpoint resolution.
21
+ - Allow logins without specifying the handle, enabling account selection from PDS interface.
22
+ - Simple access to sign-in, sign-out, public and private agents from `useAtproto` composable.
23
+ - Access to the underlying client and session with `$atproto` provided by the plugin.
24
+ - Dynamically generates `client-metadata.json` when the Nuxt.js app starts.
25
+
26
+ ## Installation
27
+
28
+ Install the module via npm:
29
+
30
+ ```sh
31
+ npm install nuxt-atproto
32
+ ```
33
+
34
+ ## Configuration
35
+
36
+ Register the module in your `nuxt.config.js`:
37
+
38
+ ```ts
39
+ export default defineNuxtConfig({
40
+ modules: ['nuxt-atproto']
41
+ })
42
+ ```
43
+
44
+ You can configure `nuxt-atproto` in your `nuxt.config.ts` file under the `atproto` key.
45
+ The following options are available with their default values:
46
+
47
+ ```ts
48
+ defineNuxtConfig({
49
+ modules: ['nuxt-atproto'],
50
+ atproto: {
51
+ serviceEndpoint: {
52
+ private: 'https://bsky.social',
53
+ public: 'https://public.api.bsky.app'
54
+ },
55
+ oauth: {
56
+ clientMetadata: {
57
+ // url of your remote client_metadata.json, leave the field empty
58
+ // to let `nuxt-atproto` generate a local /public/client_metadata.json
59
+ remote: '',
60
+ // configuration for the local client_metadata.json
61
+ local: {
62
+ client_id: '',
63
+ client_name: '',
64
+ client_uri: '',
65
+ logo_uri: '',
66
+ tos_uri: '',
67
+ policy_uri: '',
68
+ redirect_uris: [''],
69
+ scope: 'atproto',
70
+ grant_types: [],
71
+ response_types: [],
72
+ token_endpoint_auth_method: 'none',
73
+ application_type: 'web',
74
+ dpop_bound_access_tokens: true
75
+ }
76
+ },
77
+ signInOptions: {
78
+ state: '',
79
+ prompt: 'login',
80
+ scope: 'atproto',
81
+ ui_locales: 'en',
82
+ },
83
+ },
84
+ debug: true,
85
+ }
86
+ })
87
+ ```
88
+
89
+ You must configure the `atproto.oauth.clientMetadata` with in your `nuxt.config.ts`,
90
+ especially `client_id` and `redirect_uris`, for the authentication flow to work correctly.
91
+
92
+ If you don't provide a remote URL in the module options, when Nuxt.js starts it will create a `client-metadata.json` in your `public` folder.
93
+ Using a local `client-metadata.json` generally offers a faster user experience compared to fetching it from a remote URL.
94
+
95
+ ## Usage
96
+
97
+ #### Basic example
98
+
99
+ ```html
100
+ <script setup lang="ts">
101
+ const atproto = useAtproto()
102
+ </script>
103
+
104
+ <template>
105
+ <div>
106
+ <Button @click="atproto.signIn()">
107
+ Sign-in with ATProto
108
+ </Button><br />
109
+ <Button @click="atproto.signInWithHandle('dxlliv.bsky.social')">
110
+ Sign-in with ATProto using dxlliv.bsky.social
111
+ </Button><br />
112
+ <Button @click="atproto.signInWithHandle()">
113
+ Sign-in with ATProto using your handle (prompt)
114
+ </Button>
115
+ <Button @click="atproto.restore('did:plc:2pkidgvfnbxx7sq3shporxij')">
116
+ Restore dxlliv.bsky.social session
117
+ </Button>
118
+ </div>
119
+ <template v-if="atproto.agent.account">
120
+ <div>logged with: {{atproto.agent.account.assertDid}}</div>
121
+ <Button @click="atproto.signOut()">
122
+ Sign-out
123
+ </Button>
124
+ </template>
125
+ </template>
126
+ ```
127
+
128
+ <br />
129
+
130
+ ## 🧩 useAtproto(service?: string, fetch?: any)
131
+
132
+ A composable provided by `nuxt-atproto` that offers methods for user authentication and session management, including authenticating, signing out and session restore.
133
+
134
+ ```html
135
+
136
+ <script setup lang="ts">
137
+ const atproto = useAtproto()
138
+ </script>
139
+ ```
140
+
141
+ **Parameters:**
142
+
143
+ - `service` (optional): Override the service endpoint of the public agent.
144
+ - `fetch` (optional): A custom fetch implementation.
145
+
146
+ <br />
147
+
148
+ ### ➡️ signIn(serviceEndpoint?: string, options?: AtprotoSignInOptions)
149
+
150
+ Initiates the standard ATProto sign-in flow redirecting the user for authentication.
151
+
152
+ ```html
153
+
154
+ <Button @click="atproto.signIn()">
155
+ Sign-in with ATProto
156
+ </Button>
157
+ ```
158
+
159
+ **Parameters:**
160
+
161
+ - `serviceEndpoint`: *(optional)* The specific ATProto service endpoint to use for sign-in.
162
+ - `options`: *(optional)* Additional options to configure the sign-in process.
163
+
164
+ ```ts
165
+ // options
166
+ {
167
+ state: "",
168
+ prompt: "login",
169
+ scope: "atproto",
170
+ ui_locales: "en"
171
+ }
172
+ ```
173
+
174
+ **Returns:** A Promise that might not directly resolve due to the redirection to the ATProto service.
175
+
176
+ <br />
177
+
178
+ ### ➡️ signInWithHandle(handle?: string, options?: AtprotoSignInOptions)
179
+
180
+ Initiates the ATProto sign-in flow using the user's AT Protocol handle.
181
+ The user will be prompted to enter their handle on the ATProto service if you omit the handle.
182
+
183
+ ```html
184
+
185
+ <Button @click="atproto.signInWithHandle()">
186
+ Sign-in with ATProto
187
+ </Button>
188
+ ```
189
+
190
+ **Parameters:**
191
+
192
+ - `handle`: *(optional)* The AT Protocol handle of the user.
193
+ - `options`: *(optional)* Additional options to configure the sign-in process.
194
+
195
+ **Returns:** A Promise that might not directly resolve due to the redirection to the ATProto service.
196
+
197
+ <br />
198
+
199
+ ### ➡️ restore(did: string)
200
+
201
+ Restore the user account associated with the provided Decentralized Identifier (DID).
202
+
203
+ ```html
204
+
205
+ <Button @click="atproto.restore('did:plc:2pkidgvfnbxx7sq3shporxij')">
206
+ Restore dxlliv.bsky.social
207
+ </Button>
208
+ ```
209
+
210
+ **Parameters:**
211
+
212
+ - `handle`: *(optional)* The Decentralized Identifier (DID) of the account to be restored.
213
+
214
+ **Returns:** A Promise that resolves when the restoration process is complete.
215
+
216
+ <br />
217
+
218
+ ### ➡️ signOut()
219
+
220
+ Logs out the currently authenticated user and clears the stored session data.
221
+
222
+ ```html
223
+
224
+ <Button @click="atproto.signOut()">
225
+ Sign-out
226
+ </Button>
227
+ ```
228
+
229
+ **Returns:** A Promise that resolves when the restoration process is complete.
230
+
231
+ <br />
232
+
233
+ ### 🔢 agent
234
+
235
+ Provides an object containing both a public `AtpAgent` for general access and an authenticated `Agent` to perform actions that require a logged-in user, such as posting, following, and liking.
236
+
237
+ ```ts
238
+ const atproto = useAtproto()
239
+
240
+ const profile = await atproto.agent.public.getProfile({actor: 'owdproject.org'})
241
+ console.log(profile.data)
242
+
243
+ const likedPost = await atproto.agent.account.likePost({cid, uri})
244
+ console.log(likedPost.data)
245
+ ```
246
+
247
+ **Parameters:**
248
+
249
+ - `service` (optional): The base URL of the ATProto service.
250
+ - `fetch` (optional): A custom fetch implementation.
251
+
252
+ **Returns:** An object with both public AtpAgent or authenticated account Agent.
253
+
254
+ <br />
255
+
256
+ ## License
257
+
258
+ This package is released under the MIT license.
259
+
260
+ <!-- Badges -->
261
+ [npm-version-src]: https://img.shields.io/npm/v/nuxt-atproto/latest.svg?style=flat&colorA=020420&colorB=00DC82
262
+ [npm-version-href]: https://npmjs.com/package/nuxt-atproto
263
+
264
+ [npm-downloads-src]: https://img.shields.io/npm/dm/nuxt-atproto.svg?style=flat&colorA=020420&colorB=00DC82
265
+ [npm-downloads-href]: https://npm.chart.dev/nuxt-atproto
266
+
267
+ [license-src]: https://img.shields.io/npm/l/nuxt-atproto.svg?style=flat&colorA=020420&colorB=00DC82
268
+ [license-href]: https://npmjs.com/package/nuxt-atproto
269
+
270
+ [nuxt-src]: https://img.shields.io/badge/Nuxt-020420?logo=nuxt.js
271
+ [nuxt-href]: https://nuxt.com
@@ -0,0 +1,56 @@
1
+ import * as _nuxt_schema from '@nuxt/schema';
2
+ import { HookResult } from '@nuxt/schema';
3
+
4
+ declare module '#app' {
5
+ interface RuntimeNuxtHooks {
6
+ 'atproto:sessionCreated': (did: string) => HookResult
7
+ 'atproto:sessionRestored': (did: string) => HookResult
8
+ 'atproto:sessionDeleted': (did: string) => HookResult
9
+ }
10
+ }
11
+
12
+ declare module 'nuxt/schema' {
13
+ interface PublicRuntimeConfig {
14
+ atproto: AtprotoNuxtOptions
15
+ }
16
+ }
17
+
18
+ interface AtprotoNuxtOptions {
19
+ serviceEndpoint: {
20
+ private: string
21
+ public: string
22
+ }
23
+ oauth: {
24
+ clientMetadata: {
25
+ remote?: string
26
+ local?: {
27
+ client_id: string
28
+ client_name: string
29
+ client_uri: string
30
+ logo_uri: string
31
+ tos_uri: string
32
+ policy_uri: string
33
+ redirect_uris: [string, ...string[]]
34
+ scope: string
35
+ grant_types: Array<any>
36
+ response_types: Array<any>
37
+ token_endpoint_auth_method: string
38
+ application_type: string
39
+ dpop_bound_access_tokens: boolean
40
+ }
41
+ }
42
+ signInOptions: AtprotoSignInOptions
43
+ }
44
+ debug: boolean
45
+ }
46
+
47
+ interface AtprotoSignInOptions {
48
+ state: string
49
+ prompt: 'login' | 'consent'
50
+ scope: string
51
+ ui_locales: string
52
+ }
53
+
54
+ declare const _default: _nuxt_schema.NuxtModule<AtprotoNuxtOptions, AtprotoNuxtOptions, false>;
55
+
56
+ export { _default as default };
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "nuxt-atproto",
3
+ "configKey": "atproto",
4
+ "version": "0.0.1",
5
+ "builder": {
6
+ "@nuxt/module-builder": "1.0.1",
7
+ "unbuild": "3.5.0"
8
+ }
9
+ }
@@ -0,0 +1,76 @@
1
+ import { mkdirSync, writeFileSync, rmSync } from 'node:fs';
2
+ import { defineNuxtModule, createResolver, addImportsDir, addPlugin } from '@nuxt/kit';
3
+
4
+ const module = defineNuxtModule({
5
+ meta: {
6
+ name: "nuxt-atproto",
7
+ configKey: "atproto"
8
+ },
9
+ defaults: {
10
+ serviceEndpoint: {
11
+ private: "https://bsky.social",
12
+ public: "https://public.api.bsky.app"
13
+ },
14
+ oauth: {
15
+ clientMetadata: {
16
+ // load the client_metadata.json asynchronously from the URL
17
+ remote: "",
18
+ // or use the local client_metadata.json generated by `nuxt-atproto`
19
+ local: {
20
+ client_id: "",
21
+ client_name: "",
22
+ client_uri: "",
23
+ logo_uri: "",
24
+ tos_uri: "",
25
+ policy_uri: "",
26
+ redirect_uris: [""],
27
+ scope: "atproto",
28
+ grant_types: [],
29
+ response_types: [],
30
+ token_endpoint_auth_method: "none",
31
+ application_type: "web",
32
+ dpop_bound_access_tokens: true
33
+ }
34
+ },
35
+ signInOptions: {
36
+ state: "",
37
+ prompt: "login",
38
+ scope: "atproto",
39
+ ui_locales: "en"
40
+ }
41
+ },
42
+ debug: true
43
+ },
44
+ setup(_options, _nuxt) {
45
+ const { resolve } = createResolver(import.meta.url);
46
+ const publicDir = resolve(_nuxt.options.rootDir, "public");
47
+ _nuxt.options.runtimeConfig.public.atproto = _options;
48
+ _nuxt.options.build.transpile = _nuxt.options.build.transpile || [];
49
+ _nuxt.options.build.transpile.push("@atproto/oauth-client-browser");
50
+ _nuxt.options.build.transpile.push("@atproto/api");
51
+ _nuxt.hook("vite:extendConfig", (config) => {
52
+ config.optimizeDeps = config.optimizeDeps || {};
53
+ config.optimizeDeps.include = config.optimizeDeps.include || [];
54
+ config.optimizeDeps.include.push("@atproto/oauth-client-browser");
55
+ config.optimizeDeps.include.push("@atproto/api");
56
+ });
57
+ if (!_options.oauth.clientMetadata.remote) {
58
+ try {
59
+ mkdirSync(publicDir, { recursive: true });
60
+ } catch (error) {
61
+ console.error("Failed creating /public/client-metadata.json", error);
62
+ return;
63
+ }
64
+ writeFileSync(
65
+ publicDir + "/client-metadata.json",
66
+ JSON.stringify(_options.oauth.clientMetadata.local, null, 2)
67
+ );
68
+ } else {
69
+ rmSync(publicDir + "/client-metadata.json");
70
+ }
71
+ addImportsDir(resolve("./runtime/composables"));
72
+ addPlugin(resolve("./runtime/plugin.client"));
73
+ }
74
+ });
75
+
76
+ export { module as default };
File without changes
@@ -0,0 +1,74 @@
1
+ import { Agent, AtpAgent } from "@atproto/api";
2
+ import { useRuntimeConfig, useNuxtApp } from "nuxt/app";
3
+ import { reactive } from "vue";
4
+ export function useAtproto(service, fetch) {
5
+ const runtimeConfig = useRuntimeConfig();
6
+ async function signIn(serviceEndpoint = runtimeConfig.public.atproto.serviceEndpoint.private, options = runtimeConfig.public.atproto.oauth.signInOptions) {
7
+ const { $atproto } = useNuxtApp();
8
+ try {
9
+ await $atproto.client.signInRedirect(
10
+ serviceEndpoint,
11
+ {
12
+ ...options,
13
+ signal: new AbortController().signal
14
+ }
15
+ );
16
+ } catch (error) {
17
+ console.error(`Error during sign-in:`, error);
18
+ }
19
+ }
20
+ async function signInWithHandle(handle, options = runtimeConfig.public.atproto.oauth.signInOptions) {
21
+ const { $atproto } = useNuxtApp();
22
+ try {
23
+ let handlePrompt = null;
24
+ if (!handle) {
25
+ handlePrompt = window.prompt("Type your handle");
26
+ if (!handlePrompt) {
27
+ return;
28
+ }
29
+ handle = handlePrompt;
30
+ }
31
+ const url = await $atproto.client.authorize(handle, options);
32
+ window.location.href = url.href;
33
+ } catch (error) {
34
+ console.error(`Error during sign-in for handle ${handle}:`, error);
35
+ }
36
+ }
37
+ async function restore(did) {
38
+ const { $atproto } = useNuxtApp();
39
+ const session = await $atproto.client.restore(did);
40
+ $atproto.session.value = session;
41
+ return session;
42
+ }
43
+ async function signOut() {
44
+ const { $atproto } = useNuxtApp();
45
+ $atproto.client.revoke($atproto.session.value.did);
46
+ $atproto.session.value = void 0;
47
+ if (runtimeConfig.public.atproto.debug) {
48
+ console.log("User signed out");
49
+ }
50
+ }
51
+ function useAgent(service2, fetch2) {
52
+ const { $atproto } = useNuxtApp();
53
+ const runtimeConfig2 = useRuntimeConfig();
54
+ if (!service2) {
55
+ service2 = runtimeConfig2.public.atproto.serviceEndpoint.public;
56
+ }
57
+ let accountAgent;
58
+ if ($atproto && $atproto.session.value) {
59
+ accountAgent = new Agent($atproto.session.value);
60
+ }
61
+ return reactive({
62
+ public: new AtpAgent({ service: service2, fetch: fetch2 }),
63
+ account: accountAgent
64
+ });
65
+ }
66
+ const agent = useAgent(service, fetch);
67
+ return {
68
+ signIn,
69
+ signInWithHandle,
70
+ signOut,
71
+ restore,
72
+ agent
73
+ };
74
+ }
@@ -0,0 +1,12 @@
1
+ declare const _default: import("nuxt/app").Plugin<{
2
+ atproto: {
3
+ client: any;
4
+ session: import("vue").Ref<any, any>;
5
+ };
6
+ }> & import("nuxt/app").ObjectPlugin<{
7
+ atproto: {
8
+ client: any;
9
+ session: import("vue").Ref<any, any>;
10
+ };
11
+ }>;
12
+ export default _default;
@@ -0,0 +1,61 @@
1
+ import { BrowserOAuthClient } from "@atproto/oauth-client-browser";
2
+ import { useRuntimeConfig, defineNuxtPlugin } from "nuxt/app";
3
+ import { ref } from "vue";
4
+ export default defineNuxtPlugin({
5
+ name: "atproto",
6
+ async setup(_nuxtApp) {
7
+ const runtimeConfig = useRuntimeConfig();
8
+ let atprotoOAuthClient;
9
+ const atprotoOAuthSession = ref();
10
+ if (runtimeConfig.public.atproto.oauth.clientMetadata.remote) {
11
+ atprotoOAuthClient = await BrowserOAuthClient.load({
12
+ handleResolver: runtimeConfig.public.atproto.serviceEndpoint.private,
13
+ clientId: runtimeConfig.public.atproto.oauth.clientMetadata.remote
14
+ // todo implement custom fetch
15
+ });
16
+ } else {
17
+ atprotoOAuthClient = new BrowserOAuthClient({
18
+ handleResolver: runtimeConfig.public.atproto.serviceEndpoint.private,
19
+ // @ts-ignore
20
+ clientMetadata: import.meta.env.MODE === "development" ? void 0 : runtimeConfig.public.atproto.oauth.clientMetadata.local
21
+ // todo implement custom fetch
22
+ });
23
+ }
24
+ atprotoOAuthClient.addEventListener("deleted", (event) => {
25
+ const { sub, cause } = event.detail;
26
+ if (runtimeConfig.public.atproto.debug) {
27
+ console.warn(`Session deleted for ${sub}. Cause:`, cause);
28
+ }
29
+ _nuxtApp.hooks.callHook("atproto:sessionDeleted", sub);
30
+ });
31
+ await atprotoOAuthClient.init().then(async (result) => {
32
+ if (result) {
33
+ const { session, state } = result;
34
+ if (runtimeConfig.public.atproto.debug) {
35
+ if (state != null) {
36
+ console.log(`${session.sub} was successfully authenticated (state: ${state})`);
37
+ _nuxtApp.hook("app:mounted", async () => {
38
+ _nuxtApp.hooks.callHook("atproto:sessionCreated", session.sub);
39
+ });
40
+ } else {
41
+ console.log(`${session.sub} was restored (last active session)`);
42
+ _nuxtApp.hook("app:mounted", async () => {
43
+ _nuxtApp.hooks.callHook("atproto:sessionRestored", session.sub);
44
+ });
45
+ }
46
+ }
47
+ atprotoOAuthSession.value = session;
48
+ }
49
+ }).catch((error) => {
50
+ console.error("Error initializing ATProto client or restoring session:", error);
51
+ });
52
+ return {
53
+ provide: {
54
+ atproto: {
55
+ client: atprotoOAuthClient,
56
+ session: atprotoOAuthSession
57
+ }
58
+ }
59
+ };
60
+ }
61
+ });
@@ -0,0 +1,7 @@
1
+ import type { NuxtModule } from '@nuxt/schema'
2
+
3
+ import type { default as Module } from './module.mjs'
4
+
5
+ export type ModuleOptions = typeof Module extends NuxtModule<infer O> ? Partial<O> : Record<string, any>
6
+
7
+ export { default } from './module.mjs'
package/package.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "nuxt-atproto",
3
+ "version": "0.0.1",
4
+ "description": "Interact with AT Protocol and Bluesky in your Nuxt.js application",
5
+ "keywords": [
6
+ "nuxt",
7
+ "bluesky",
8
+ "atprotocol",
9
+ "oauth",
10
+ "atproto",
11
+ "authentication"
12
+ ],
13
+ "license": "MIT",
14
+ "repository": "nuxt-atproto",
15
+ "homepage": "https://github.com/dxlliv/nuxt-atproto",
16
+ "author": {
17
+ "name": "dxlliv",
18
+ "url": "https://github.com/dxlliv"
19
+ },
20
+ "type": "module",
21
+ "exports": {
22
+ ".": {
23
+ "types": "./dist/types.d.mts",
24
+ "import": "./dist/module.mjs"
25
+ }
26
+ },
27
+ "main": "./dist/module.mjs",
28
+ "typesVersions": {
29
+ "*": {
30
+ ".": [
31
+ "./dist/types.d.mts"
32
+ ]
33
+ }
34
+ },
35
+ "files": [
36
+ "dist"
37
+ ],
38
+ "scripts": {
39
+ "prepack": "nuxt-module-build build",
40
+ "dev": "nuxi dev playground",
41
+ "dev:build": "nuxi build playground",
42
+ "dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare playground",
43
+ "release": "npm run lint && npm run test && npm run prepack && changelogen --release && npm publish && git push --follow-tags",
44
+ "lint": "eslint .",
45
+ "test": "vitest run",
46
+ "test:watch": "vitest watch",
47
+ "test:types": "vue-tsc --noEmit && cd playground && vue-tsc --noEmit"
48
+ },
49
+ "dependencies": {
50
+ "@nuxt/kit": "^3.17.2",
51
+ "@atproto/api": "^0.15.3",
52
+ "@atproto/oauth-client-browser": "^0.3.11"
53
+ },
54
+ "devDependencies": {
55
+ "@nuxt/devtools": "^2.4.0",
56
+ "@nuxt/eslint-config": "^1.3.0",
57
+ "@nuxt/module-builder": "^1.0.1",
58
+ "@nuxt/schema": "^3.17.2",
59
+ "@nuxt/test-utils": "^3.18.0",
60
+ "@types/node": "latest",
61
+ "changelogen": "^0.6.1",
62
+ "eslint": "^9.26.0",
63
+ "nuxt": "^3.17.2",
64
+ "typescript": "~5.8.3",
65
+ "vitest": "^3.1.2",
66
+ "vue-tsc": "^2.2.10"
67
+ },
68
+ "unbuild": {
69
+ "failOnWarn": false
70
+ }
71
+ }