nuxt-atproto 0.0.5 → 0.1.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 +53 -249
- package/dist/module.d.mts +31 -2
- package/dist/module.json +3 -3
- package/dist/module.mjs +65 -21
- package/dist/runtime/app/composables/useAgent.d.ts +7 -0
- package/dist/runtime/app/composables/useAgent.js +59 -0
- package/dist/runtime/app/composables/useAtproto.d.ts +12 -0
- package/dist/runtime/app/composables/useAtproto.js +21 -0
- package/dist/runtime/app/composables/useAtprotoAgent.d.ts +10 -0
- package/dist/runtime/app/composables/useAtprotoAgent.js +8 -0
- package/dist/runtime/app/composables/useAtprotoAuth.d.ts +12 -0
- package/dist/runtime/app/composables/useAtprotoAuth.js +70 -0
- package/dist/runtime/app/composables/useAtprotoSession.d.ts +12 -0
- package/dist/runtime/app/composables/useAtprotoSession.js +18 -0
- package/dist/runtime/app/plugins/atproto.client.d.ts +7 -0
- package/dist/runtime/app/plugins/atproto.client.js +68 -0
- package/dist/runtime/app/utils/agentCache.d.ts +8 -0
- package/dist/runtime/app/utils/agentCache.js +15 -0
- package/dist/runtime/app/utils/sessionLifecycle.d.ts +22 -0
- package/dist/runtime/app/utils/sessionLifecycle.js +29 -0
- package/dist/runtime/app/utils/useAtprotoRuntimeConfig.d.ts +2 -0
- package/dist/runtime/app/utils/useAtprotoRuntimeConfig.js +4 -0
- package/package.json +15 -15
- package/dist/runtime/composables/useAgent.d.ts +0 -8
- package/dist/runtime/composables/useAgent.js +0 -23
- package/dist/runtime/composables/useAtproto.d.ts +0 -9
- package/dist/runtime/composables/useAtproto.js +0 -65
- package/dist/runtime/plugin.d.ts +0 -12
- package/dist/runtime/plugin.js +0 -61
- package/dist/runtime/utils/utilActor.d.ts +0 -2
- package/dist/runtime/utils/utilActor.js +0 -16
package/README.md
CHANGED
|
@@ -5,288 +5,92 @@
|
|
|
5
5
|
[![License][license-src]][license-href]
|
|
6
6
|
[![Nuxt][nuxt-src]][nuxt-href]
|
|
7
7
|
|
|
8
|
-
>
|
|
9
|
-
Nuxt.js app.
|
|
8
|
+
> OAuth login and session management for [Bluesky](https://bsky.app) and [AT Protocol](https://atproto.com) in [Nuxt](https://nuxt.com).
|
|
10
9
|
|
|
11
|
-
##
|
|
10
|
+
## Documentation
|
|
12
11
|
|
|
13
|
-
|
|
12
|
+
**[Documentation](https://dxlliv.github.io/nuxt-atproto/)** — getting started, guides, and API reference.
|
|
14
13
|
|
|
15
|
-
|
|
16
|
-
providing public and authenticated agents for seamless interaction with AT Protocol services.
|
|
14
|
+
Run the docs site locally:
|
|
17
15
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
|
16
|
+
```bash
|
|
17
|
+
cd docs && pnpm install && pnpm dev
|
|
18
|
+
```
|
|
27
19
|
|
|
28
|
-
|
|
20
|
+
Open **http://127.0.0.1:3456/nuxt-atproto/** (use `127.0.0.1`, not `localhost`, for the OAuth demo).
|
|
29
21
|
|
|
30
|
-
|
|
31
|
-
npm install nuxt-atproto
|
|
32
|
-
```
|
|
22
|
+
## Features
|
|
33
23
|
|
|
34
|
-
|
|
24
|
+
- AT Protocol OAuth in the browser (`@atproto/oauth-client-browser`)
|
|
25
|
+
- Composables for session, auth, and cached `@atproto/api` agents
|
|
26
|
+
- Lifecycle hooks: `atproto:sessionCreated`, `atproto:sessionRestored`, `atproto:sessionDeleted`
|
|
27
|
+
- Typed `$atproto` plugin (`client`, `session`, `status`)
|
|
28
|
+
- Optional generation of `public/client-metadata.json` from module config
|
|
35
29
|
|
|
36
|
-
|
|
30
|
+
## Quick start
|
|
37
31
|
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
modules: ['nuxt-atproto']
|
|
41
|
-
})
|
|
32
|
+
```bash
|
|
33
|
+
pnpm add nuxt-atproto
|
|
42
34
|
```
|
|
43
35
|
|
|
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
36
|
```ts
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
// configuration for the local client_metadata.json
|
|
61
|
-
local: {
|
|
62
|
-
client_id: 'https://nuxt-atproto.pages.dev/client-metadata.json',
|
|
63
|
-
client_name: 'nuxt-atproto',
|
|
64
|
-
client_uri: 'https://nuxt-atproto.pages.dev',
|
|
65
|
-
logo_uri: 'https://nuxt-atproto.pages.dev/logo.png',
|
|
66
|
-
tos_uri: 'https://nuxt-atproto.pages.dev',
|
|
67
|
-
policy_uri: 'https://nuxt-atproto.pages.dev',
|
|
68
|
-
redirect_uris: ['https://nuxt-atproto.pages.dev'],
|
|
69
|
-
scope: "atproto transition:generic",
|
|
70
|
-
grant_types: ["authorization_code", "refresh_token"],
|
|
71
|
-
response_types: ["code"],
|
|
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
|
-
},
|
|
37
|
+
// nuxt.config.ts
|
|
38
|
+
export default defineNuxtConfig({
|
|
39
|
+
modules: ['nuxt-atproto'],
|
|
40
|
+
atproto: {
|
|
41
|
+
oauth: {
|
|
42
|
+
clientMetadata: {
|
|
43
|
+
local: {
|
|
44
|
+
client_id: 'https://your-app.example/client-metadata.json',
|
|
45
|
+
client_name: 'My App',
|
|
46
|
+
client_uri: 'https://your-app.example',
|
|
47
|
+
redirect_uris: ['https://your-app.example'],
|
|
48
|
+
// ... see docs for full metadata
|
|
83
49
|
},
|
|
84
|
-
|
|
85
|
-
}
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
},
|
|
86
53
|
})
|
|
87
54
|
```
|
|
88
55
|
|
|
89
|
-
|
|
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
|
|
56
|
+
```vue
|
|
100
57
|
<script setup lang="ts">
|
|
101
|
-
const
|
|
58
|
+
const { isLogged, session } = useAtprotoSession()
|
|
59
|
+
const { signIn, signInWithHandle, signOut } = useAtprotoAuth()
|
|
102
60
|
</script>
|
|
103
61
|
|
|
104
62
|
<template>
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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>
|
|
63
|
+
<ClientOnly>
|
|
64
|
+
<button v-if="!isLogged" type="button" @click="signIn()">Sign in</button>
|
|
65
|
+
<template v-else>
|
|
66
|
+
<p>{{ session?.sub }}</p>
|
|
67
|
+
<button type="button" @click="signOut()">Sign out</button>
|
|
124
68
|
</template>
|
|
69
|
+
</ClientOnly>
|
|
125
70
|
</template>
|
|
126
71
|
```
|
|
127
72
|
|
|
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
|
-
### ➡️ isLogged()
|
|
234
|
-
|
|
235
|
-
Indicates whether the user is currently authenticated.
|
|
236
|
-
|
|
237
|
-
```ts
|
|
238
|
-
const atproto = useAtproto()
|
|
239
|
-
|
|
240
|
-
if (!atproto.isLogged()) {
|
|
241
|
-
return console.log('User is not logged in')
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
console.log('User is authenticated')
|
|
245
|
-
```
|
|
246
|
-
|
|
247
|
-
**Returns**: Returns a boolean indicating whether the user is authenticated.
|
|
248
|
-
|
|
249
|
-
<br />
|
|
250
|
-
|
|
251
|
-
### ➡️ getSession()
|
|
252
|
-
|
|
253
|
-
Retrieves the current session from the Nuxt application context.
|
|
254
|
-
|
|
255
73
|
```ts
|
|
256
|
-
const
|
|
257
|
-
|
|
258
|
-
if (atproto.isLogged()) {
|
|
259
|
-
const session = atproto.getSession()
|
|
260
|
-
|
|
261
|
-
console.log('User is logged in', session.sub)
|
|
262
|
-
}
|
|
74
|
+
const agent = useAtprotoAgent('authenticated')
|
|
75
|
+
await agent.getTimeline()
|
|
263
76
|
```
|
|
264
77
|
|
|
265
|
-
**
|
|
266
|
-
|
|
267
|
-
<br />
|
|
268
|
-
|
|
269
|
-
## 🧩 useAgent(service?: string, fetch?: any)
|
|
270
|
-
|
|
271
|
-
A composable provided by `nuxt-atproto` that offers methods for user authentication and session management, including authenticating, signing out and session restore.
|
|
272
|
-
|
|
273
|
-
```html
|
|
274
|
-
|
|
275
|
-
<script setup lang="ts">
|
|
276
|
-
const agent = useAgent('public')
|
|
277
|
-
</script>
|
|
278
|
-
```
|
|
78
|
+
> **Client-only OAuth.** Wrap login UI in `<ClientOnly>` or disable SSR on auth routes.
|
|
79
|
+
> See the [introduction](https://dxlliv.github.io/nuxt-atproto/getting-started/introduction) in the docs.
|
|
279
80
|
|
|
280
|
-
|
|
81
|
+
## Composables
|
|
281
82
|
|
|
282
|
-
|
|
283
|
-
|
|
83
|
+
| Composable | Purpose |
|
|
84
|
+
|------------|---------|
|
|
85
|
+
| `useAtprotoSession()` | `session`, `isLogged`, `status` |
|
|
86
|
+
| `useAtprotoAuth()` | `signIn`, `signInWithHandle`, `signOut`, `restore` |
|
|
87
|
+
| `useAtprotoAgent(scope)` | Cached `Agent` / `AtpAgent` (`authenticated`, `public`, or custom URL) |
|
|
284
88
|
|
|
285
|
-
|
|
89
|
+
`useAtproto()` and `useAgent()` are deprecated — see [Migration](https://dxlliv.github.io/nuxt-atproto/advanced/migration).
|
|
286
90
|
|
|
287
91
|
## License
|
|
288
92
|
|
|
289
|
-
|
|
93
|
+
Released under the [MIT License](./LICENSE).
|
|
290
94
|
|
|
291
95
|
<!-- Badges -->
|
|
292
96
|
[npm-version-src]: https://img.shields.io/npm/v/nuxt-atproto/latest.svg?style=flat&colorA=020420&colorB=00DC82
|
package/dist/module.d.mts
CHANGED
|
@@ -1,7 +1,21 @@
|
|
|
1
1
|
import * as _nuxt_schema from '@nuxt/schema';
|
|
2
2
|
import { HookResult } from '@nuxt/schema';
|
|
3
|
+
import { BrowserOAuthClient, OAuthSession } from '@atproto/oauth-client-browser';
|
|
4
|
+
import { Ref } from 'vue';
|
|
5
|
+
|
|
6
|
+
type AtprotoSessionStatus = 'initializing' | 'ready' | 'anonymous' | 'authenticated'
|
|
7
|
+
|
|
8
|
+
interface AtprotoContext {
|
|
9
|
+
client: BrowserOAuthClient
|
|
10
|
+
session: Ref<OAuthSession | undefined>
|
|
11
|
+
status: Ref<AtprotoSessionStatus>
|
|
12
|
+
}
|
|
3
13
|
|
|
4
14
|
declare module '#app' {
|
|
15
|
+
interface NuxtApp {
|
|
16
|
+
$atproto: AtprotoContext
|
|
17
|
+
}
|
|
18
|
+
|
|
5
19
|
interface RuntimeNuxtHooks {
|
|
6
20
|
'atproto:sessionCreated': (did: string) => HookResult
|
|
7
21
|
'atproto:sessionRestored': (did: string) => HookResult
|
|
@@ -9,7 +23,17 @@ declare module '#app' {
|
|
|
9
23
|
}
|
|
10
24
|
}
|
|
11
25
|
|
|
26
|
+
declare module 'vue' {
|
|
27
|
+
interface ComponentCustomProperties {
|
|
28
|
+
$atproto: AtprotoContext
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
12
32
|
declare module 'nuxt/schema' {
|
|
33
|
+
/**
|
|
34
|
+
* Copied to `runtimeConfig.public.atproto` at build time.
|
|
35
|
+
* OAuth client metadata and endpoints are exposed to the client bundle by design.
|
|
36
|
+
*/
|
|
13
37
|
interface PublicRuntimeConfig {
|
|
14
38
|
atproto: AtprotoNuxtOptions
|
|
15
39
|
}
|
|
@@ -21,6 +45,11 @@ interface AtprotoNuxtOptions {
|
|
|
21
45
|
public: string
|
|
22
46
|
}
|
|
23
47
|
oauth: {
|
|
48
|
+
/**
|
|
49
|
+
* When true, writes `public/client-metadata.json` from `oauth.clientMetadata.local` during module setup.
|
|
50
|
+
* @default false
|
|
51
|
+
*/
|
|
52
|
+
writeClientMetadata?: boolean
|
|
24
53
|
clientMetadata: {
|
|
25
54
|
remote?: string
|
|
26
55
|
local?: {
|
|
@@ -32,8 +61,8 @@ interface AtprotoNuxtOptions {
|
|
|
32
61
|
policy_uri: string
|
|
33
62
|
redirect_uris: string[]
|
|
34
63
|
scope: string
|
|
35
|
-
grant_types: Array<
|
|
36
|
-
response_types: Array<
|
|
64
|
+
grant_types: Array<string>
|
|
65
|
+
response_types: Array<string>
|
|
37
66
|
token_endpoint_auth_method: string
|
|
38
67
|
application_type: string
|
|
39
68
|
dpop_bound_access_tokens: boolean
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { defineNuxtModule, createResolver, addImportsDir, addPlugin } from '@nuxt/kit';
|
|
3
3
|
|
|
4
|
-
const module = defineNuxtModule({
|
|
4
|
+
const module$1 = defineNuxtModule({
|
|
5
5
|
meta: {
|
|
6
6
|
name: "nuxt-atproto",
|
|
7
7
|
configKey: "atproto"
|
|
@@ -12,6 +12,7 @@ const module = defineNuxtModule({
|
|
|
12
12
|
public: "https://public.api.bsky.app"
|
|
13
13
|
},
|
|
14
14
|
oauth: {
|
|
15
|
+
writeClientMetadata: false,
|
|
15
16
|
clientMetadata: {
|
|
16
17
|
// load the client_metadata.json asynchronously from the URL
|
|
17
18
|
remote: "",
|
|
@@ -46,28 +47,71 @@ const module = defineNuxtModule({
|
|
|
46
47
|
const publicDir = resolve(_nuxt.options.rootDir, "public");
|
|
47
48
|
_nuxt.options.runtimeConfig.public.atproto = _options;
|
|
48
49
|
_nuxt.options.build.transpile = _nuxt.options.build.transpile || [];
|
|
49
|
-
_nuxt.options.build.transpile.push(
|
|
50
|
-
_nuxt.
|
|
51
|
-
|
|
52
|
-
config.optimizeDeps =
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
50
|
+
_nuxt.options.build.transpile.push(/@atproto\//);
|
|
51
|
+
_nuxt.hook("vite:extendConfig", (viteConfig) => {
|
|
52
|
+
const config = viteConfig;
|
|
53
|
+
config.optimizeDeps = {
|
|
54
|
+
...config.optimizeDeps,
|
|
55
|
+
include: [
|
|
56
|
+
...config.optimizeDeps?.include ?? [],
|
|
57
|
+
"@atproto/oauth-client-browser",
|
|
58
|
+
"@atproto/api",
|
|
59
|
+
"core-js"
|
|
60
|
+
],
|
|
61
|
+
esbuildOptions: {
|
|
62
|
+
...config.optimizeDeps?.esbuildOptions,
|
|
63
|
+
define: {
|
|
64
|
+
...config.optimizeDeps?.esbuildOptions?.define,
|
|
65
|
+
global: "globalThis"
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
const existingOutput = config.build?.rollupOptions?.output;
|
|
70
|
+
const outputOptions = typeof existingOutput === "object" && !Array.isArray(existingOutput) ? existingOutput : {};
|
|
71
|
+
config.build = {
|
|
72
|
+
...config.build,
|
|
73
|
+
commonjsOptions: {
|
|
74
|
+
...config.build?.commonjsOptions,
|
|
75
|
+
transformMixedEsModules: true,
|
|
76
|
+
include: [
|
|
77
|
+
...Array.isArray(config.build?.commonjsOptions?.include) ? config.build.commonjsOptions.include : [],
|
|
78
|
+
/node_modules\/core-js/,
|
|
79
|
+
/node_modules\/@atproto\//
|
|
80
|
+
]
|
|
81
|
+
},
|
|
82
|
+
rollupOptions: {
|
|
83
|
+
...config.build?.rollupOptions,
|
|
84
|
+
output: {
|
|
85
|
+
...outputOptions,
|
|
86
|
+
// core-js (via @atproto/oauth-client) can leave bare `exports` refs in the client chunk
|
|
87
|
+
banner: 'var exports=typeof exports!=="undefined"?exports:{};var module=typeof module!=="undefined"?module:{exports};'
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
config.define = {
|
|
92
|
+
...config.define,
|
|
93
|
+
"process.env": config.define?.["process.env"] ?? {},
|
|
94
|
+
"global": "globalThis"
|
|
95
|
+
};
|
|
56
96
|
});
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
97
|
+
addImportsDir(resolve("./runtime/app/composables"));
|
|
98
|
+
addImportsDir(resolve("./runtime/app/utils"));
|
|
99
|
+
addPlugin({
|
|
100
|
+
src: resolve("./runtime/app/plugins/atproto.client"),
|
|
101
|
+
mode: "client"
|
|
102
|
+
});
|
|
103
|
+
if (_options.oauth.writeClientMetadata) {
|
|
104
|
+
try {
|
|
105
|
+
mkdirSync(publicDir, { recursive: true });
|
|
106
|
+
writeFileSync(
|
|
107
|
+
`${publicDir}/client-metadata.json`,
|
|
108
|
+
JSON.stringify(_options.oauth.clientMetadata.local, null, 2)
|
|
109
|
+
);
|
|
110
|
+
} catch (error) {
|
|
111
|
+
console.warn("[nuxt-atproto] Failed to write public/client-metadata.json:", error);
|
|
112
|
+
}
|
|
62
113
|
}
|
|
63
|
-
writeFileSync(
|
|
64
|
-
publicDir + "/client-metadata.json",
|
|
65
|
-
JSON.stringify(_options.oauth.clientMetadata.local, null, 2)
|
|
66
|
-
);
|
|
67
|
-
addImportsDir(resolve("./runtime/composables"));
|
|
68
|
-
addImportsDir(resolve("./runtime/utils"));
|
|
69
|
-
addPlugin(resolve("./runtime/plugin"));
|
|
70
114
|
}
|
|
71
115
|
});
|
|
72
116
|
|
|
73
|
-
export { module as default };
|
|
117
|
+
export { module$1 as default };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Agent } from '@atproto/api';
|
|
2
|
+
/**
|
|
3
|
+
* Returns a cached AT Protocol agent for the given scope.
|
|
4
|
+
*
|
|
5
|
+
* @deprecated Use `useAtprotoAgent` instead (`private` → `authenticated`).
|
|
6
|
+
*/
|
|
7
|
+
export declare function useAgent(service: 'private' | 'public' | (string & {}), fetch?: typeof globalThis.fetch): Agent;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { useNuxtApp, useState } from "nuxt/app";
|
|
2
|
+
import { useAtprotoRuntimeConfig } from "../utils/useAtprotoRuntimeConfig.js";
|
|
3
|
+
import { Agent, AtpAgent } from "@atproto/api";
|
|
4
|
+
import {
|
|
5
|
+
customAgentStateKey,
|
|
6
|
+
invalidateAtprotoPrivateAgent,
|
|
7
|
+
PRIVATE_AGENT_SESSION_KEY,
|
|
8
|
+
PRIVATE_AGENT_STATE_KEY,
|
|
9
|
+
publicAgentStateKey
|
|
10
|
+
} from "../utils/agentCache.js";
|
|
11
|
+
export function useAgent(service, fetch) {
|
|
12
|
+
const atprotoConfig = useAtprotoRuntimeConfig();
|
|
13
|
+
const { $atproto } = useNuxtApp();
|
|
14
|
+
switch (service) {
|
|
15
|
+
case "private": {
|
|
16
|
+
if (!$atproto.session.value) {
|
|
17
|
+
invalidateAtprotoPrivateAgent();
|
|
18
|
+
throw new Error("Not authenticated");
|
|
19
|
+
}
|
|
20
|
+
const sessionSub = $atproto.session.value.sub;
|
|
21
|
+
const cachedAgent = useState(PRIVATE_AGENT_STATE_KEY, () => null);
|
|
22
|
+
const cachedSessionSub = useState(PRIVATE_AGENT_SESSION_KEY, () => null);
|
|
23
|
+
if (cachedAgent.value && cachedSessionSub.value === sessionSub) {
|
|
24
|
+
return cachedAgent.value;
|
|
25
|
+
}
|
|
26
|
+
const agent = new Agent($atproto.session.value);
|
|
27
|
+
cachedAgent.value = agent;
|
|
28
|
+
cachedSessionSub.value = sessionSub;
|
|
29
|
+
return agent;
|
|
30
|
+
}
|
|
31
|
+
case "public": {
|
|
32
|
+
const endpoint = atprotoConfig.serviceEndpoint.public;
|
|
33
|
+
const cacheKey = publicAgentStateKey(endpoint);
|
|
34
|
+
const cachedAgent = useState(cacheKey, () => null);
|
|
35
|
+
if (cachedAgent.value) {
|
|
36
|
+
return cachedAgent.value;
|
|
37
|
+
}
|
|
38
|
+
const agent = new AtpAgent({
|
|
39
|
+
service: endpoint,
|
|
40
|
+
fetch
|
|
41
|
+
});
|
|
42
|
+
cachedAgent.value = agent;
|
|
43
|
+
return agent;
|
|
44
|
+
}
|
|
45
|
+
default: {
|
|
46
|
+
const cacheKey = customAgentStateKey(service);
|
|
47
|
+
const cachedAgent = useState(cacheKey, () => null);
|
|
48
|
+
if (cachedAgent.value) {
|
|
49
|
+
return cachedAgent.value;
|
|
50
|
+
}
|
|
51
|
+
const agent = new AtpAgent({
|
|
52
|
+
service,
|
|
53
|
+
fetch
|
|
54
|
+
});
|
|
55
|
+
cachedAgent.value = agent;
|
|
56
|
+
return agent;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { OAuthSession } from '@atproto/oauth-client-browser';
|
|
2
|
+
/**
|
|
3
|
+
* @deprecated Use `useAtprotoSession` and `useAtprotoAuth` instead.
|
|
4
|
+
*/
|
|
5
|
+
export declare function useAtproto(): {
|
|
6
|
+
signInWithHandle: (handle?: string, options?: Parameters<(handle: string, options?: import("../../../types/index.js").AtprotoSignInOptions) => Promise<void>>[1]) => Promise<void>;
|
|
7
|
+
isLogged: () => boolean;
|
|
8
|
+
getSession: () => OAuthSession | undefined;
|
|
9
|
+
signIn: (serviceEndpoint?: string, options?: import("../../../types/index.js").AtprotoSignInOptions) => Promise<void>;
|
|
10
|
+
signOut: () => Promise<void>;
|
|
11
|
+
restore: (did: string) => Promise<OAuthSession>;
|
|
12
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { useAtprotoAuth } from "./useAtprotoAuth.js";
|
|
2
|
+
import { useAtprotoSession } from "./useAtprotoSession.js";
|
|
3
|
+
export function useAtproto() {
|
|
4
|
+
const { session, isLogged } = useAtprotoSession();
|
|
5
|
+
const auth = useAtprotoAuth();
|
|
6
|
+
return {
|
|
7
|
+
...auth,
|
|
8
|
+
signInWithHandle: async (handle, options) => {
|
|
9
|
+
if (!handle) {
|
|
10
|
+
const handlePrompt = window.prompt("Type your handle");
|
|
11
|
+
if (!handlePrompt) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
return auth.signInWithHandle(handlePrompt, options);
|
|
15
|
+
}
|
|
16
|
+
return auth.signInWithHandle(handle, options);
|
|
17
|
+
},
|
|
18
|
+
isLogged: () => isLogged.value,
|
|
19
|
+
getSession: () => session.value
|
|
20
|
+
};
|
|
21
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Agent, AtpAgent } from '@atproto/api';
|
|
2
|
+
export type AtprotoAgentScope = 'authenticated' | 'public' | (string & {});
|
|
3
|
+
/**
|
|
4
|
+
* Returns a cached AT Protocol agent for the given scope (client-only).
|
|
5
|
+
*
|
|
6
|
+
* - `authenticated` — OAuth session agent (formerly `private`)
|
|
7
|
+
* - `public` — unauthenticated public API agent
|
|
8
|
+
* - custom string — `AtpAgent` for a specific PDS/service URL
|
|
9
|
+
*/
|
|
10
|
+
export declare function useAtprotoAgent(scope: AtprotoAgentScope, fetch?: typeof globalThis.fetch): Agent | AtpAgent;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { useAgent } from "./useAgent.js";
|
|
2
|
+
export function useAtprotoAgent(scope, fetch) {
|
|
3
|
+
if (import.meta.server) {
|
|
4
|
+
throw new Error("useAtprotoAgent is only available on the client");
|
|
5
|
+
}
|
|
6
|
+
const service = scope === "authenticated" ? "private" : scope;
|
|
7
|
+
return useAgent(service, fetch);
|
|
8
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { OAuthSession } from '@atproto/oauth-client-browser';
|
|
2
|
+
import type { AtprotoSignInOptions } from '../../../types/index.js';
|
|
3
|
+
export interface UseAtprotoAuthReturn {
|
|
4
|
+
signIn: (serviceEndpoint?: string, options?: AtprotoSignInOptions) => Promise<void>;
|
|
5
|
+
signInWithHandle: (handle: string, options?: AtprotoSignInOptions) => Promise<void>;
|
|
6
|
+
signOut: () => Promise<void>;
|
|
7
|
+
restore: (did: string) => Promise<OAuthSession>;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* ATProto sign-in, sign-out, and session restore (client-only).
|
|
11
|
+
*/
|
|
12
|
+
export declare function useAtprotoAuth(): UseAtprotoAuthReturn;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { useNuxtApp } from "nuxt/app";
|
|
2
|
+
import { applyAtprotoSession } from "../utils/sessionLifecycle.js";
|
|
3
|
+
import { useAtprotoRuntimeConfig } from "../utils/useAtprotoRuntimeConfig.js";
|
|
4
|
+
const serverAuthReturn = {
|
|
5
|
+
signIn: async () => {
|
|
6
|
+
},
|
|
7
|
+
signInWithHandle: async () => {
|
|
8
|
+
},
|
|
9
|
+
signOut: async () => {
|
|
10
|
+
},
|
|
11
|
+
restore: async () => {
|
|
12
|
+
throw new Error("useAtprotoAuth is only available on the client");
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
export function useAtprotoAuth() {
|
|
16
|
+
if (import.meta.server) {
|
|
17
|
+
return serverAuthReturn;
|
|
18
|
+
}
|
|
19
|
+
const atprotoConfig = useAtprotoRuntimeConfig();
|
|
20
|
+
async function signIn(serviceEndpoint = atprotoConfig.serviceEndpoint.private, options = atprotoConfig.oauth.signInOptions) {
|
|
21
|
+
const { $atproto } = useNuxtApp();
|
|
22
|
+
try {
|
|
23
|
+
await $atproto.client.signInRedirect(
|
|
24
|
+
serviceEndpoint,
|
|
25
|
+
{
|
|
26
|
+
...options,
|
|
27
|
+
signal: new AbortController().signal
|
|
28
|
+
}
|
|
29
|
+
);
|
|
30
|
+
} catch (error) {
|
|
31
|
+
console.error("Error during sign-in:", error);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
async function signInWithHandle(handle, options = atprotoConfig.oauth.signInOptions) {
|
|
35
|
+
const { $atproto } = useNuxtApp();
|
|
36
|
+
try {
|
|
37
|
+
const url = await $atproto.client.authorize(handle, options);
|
|
38
|
+
window.location.href = url.href;
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.error(`Error during sign-in for handle ${handle}:`, error);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
async function restore(did) {
|
|
44
|
+
const nuxtApp = useNuxtApp();
|
|
45
|
+
const session = await nuxtApp.$atproto.client.restore(did);
|
|
46
|
+
applyAtprotoSession(nuxtApp, session, "account-switch");
|
|
47
|
+
return session;
|
|
48
|
+
}
|
|
49
|
+
async function signOut() {
|
|
50
|
+
const nuxtApp = useNuxtApp();
|
|
51
|
+
const session = nuxtApp.$atproto.session.value;
|
|
52
|
+
if (session) {
|
|
53
|
+
try {
|
|
54
|
+
await nuxtApp.$atproto.client.revoke(session.did);
|
|
55
|
+
} catch (error) {
|
|
56
|
+
console.error("Error revoking ATProto session:", error);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
applyAtprotoSession(nuxtApp, void 0, "sign-out");
|
|
60
|
+
if (atprotoConfig.debug) {
|
|
61
|
+
console.log("User signed out");
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
signIn,
|
|
66
|
+
signInWithHandle,
|
|
67
|
+
signOut,
|
|
68
|
+
restore
|
|
69
|
+
};
|
|
70
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { OAuthSession } from '@atproto/oauth-client-browser';
|
|
2
|
+
import type { AtprotoSessionStatus } from '../../../types/index.js';
|
|
3
|
+
import { type ComputedRef, type Ref } from 'vue';
|
|
4
|
+
export interface UseAtprotoSessionReturn {
|
|
5
|
+
session: Ref<OAuthSession | undefined>;
|
|
6
|
+
isLogged: ComputedRef<boolean>;
|
|
7
|
+
status: Ref<AtprotoSessionStatus>;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Reactive ATProto OAuth session state (client-only).
|
|
11
|
+
*/
|
|
12
|
+
export declare function useAtprotoSession(): UseAtprotoSessionReturn;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { useNuxtApp } from "nuxt/app";
|
|
2
|
+
import { computed, shallowRef } from "vue";
|
|
3
|
+
const serverSessionReturn = {
|
|
4
|
+
session: shallowRef(void 0),
|
|
5
|
+
isLogged: computed(() => false),
|
|
6
|
+
status: shallowRef("anonymous")
|
|
7
|
+
};
|
|
8
|
+
export function useAtprotoSession() {
|
|
9
|
+
if (import.meta.server) {
|
|
10
|
+
return serverSessionReturn;
|
|
11
|
+
}
|
|
12
|
+
const { $atproto } = useNuxtApp();
|
|
13
|
+
return {
|
|
14
|
+
session: $atproto.session,
|
|
15
|
+
isLogged: computed(() => !!$atproto.session.value),
|
|
16
|
+
status: $atproto.status
|
|
17
|
+
};
|
|
18
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { BrowserOAuthClient } from "@atproto/oauth-client-browser";
|
|
2
|
+
import { defineNuxtPlugin } from "nuxt/app";
|
|
3
|
+
import { ref } from "vue";
|
|
4
|
+
import { useAtprotoRuntimeConfig } from "../utils/useAtprotoRuntimeConfig.js";
|
|
5
|
+
import { applyAtprotoSession } from "../utils/sessionLifecycle.js";
|
|
6
|
+
export default defineNuxtPlugin({
|
|
7
|
+
name: "atproto",
|
|
8
|
+
async setup(nuxtApp) {
|
|
9
|
+
const atprotoConfig = useAtprotoRuntimeConfig();
|
|
10
|
+
const status = ref("initializing");
|
|
11
|
+
const atprotoOAuthSession = ref();
|
|
12
|
+
const clientOptions = {
|
|
13
|
+
handleResolver: atprotoConfig.serviceEndpoint.private,
|
|
14
|
+
onDelete: (sub, cause) => {
|
|
15
|
+
if (atprotoConfig.debug) {
|
|
16
|
+
console.warn(`Session deleted for ${sub}. Cause:`, cause);
|
|
17
|
+
}
|
|
18
|
+
nuxtApp.hooks.callHook("atproto:sessionDeleted", sub);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
let atprotoOAuthClient;
|
|
22
|
+
if (atprotoConfig.oauth.clientMetadata.remote) {
|
|
23
|
+
atprotoOAuthClient = await BrowserOAuthClient.load({
|
|
24
|
+
...clientOptions,
|
|
25
|
+
clientId: atprotoConfig.oauth.clientMetadata.remote
|
|
26
|
+
});
|
|
27
|
+
} else {
|
|
28
|
+
atprotoOAuthClient = new BrowserOAuthClient({
|
|
29
|
+
...clientOptions,
|
|
30
|
+
...import.meta.env.DEV || !atprotoConfig.oauth.clientMetadata.local ? {} : { clientMetadata: atprotoConfig.oauth.clientMetadata.local }
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
const atproto = {
|
|
34
|
+
client: atprotoOAuthClient,
|
|
35
|
+
session: atprotoOAuthSession,
|
|
36
|
+
status
|
|
37
|
+
};
|
|
38
|
+
try {
|
|
39
|
+
const result = await atprotoOAuthClient.init();
|
|
40
|
+
if (result) {
|
|
41
|
+
const { session, state } = result;
|
|
42
|
+
if (atprotoConfig.debug) {
|
|
43
|
+
if (state != null) {
|
|
44
|
+
console.log(`${session.sub} was successfully authenticated (state: ${state})`);
|
|
45
|
+
} else {
|
|
46
|
+
console.log(`${session.sub} was restored (last active session)`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
applyAtprotoSession(
|
|
50
|
+
nuxtApp,
|
|
51
|
+
session,
|
|
52
|
+
state != null ? "oauth-return" : "cold-restore",
|
|
53
|
+
{ deferHooks: true, atproto }
|
|
54
|
+
);
|
|
55
|
+
} else {
|
|
56
|
+
status.value = "anonymous";
|
|
57
|
+
}
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.error("Error initializing ATProto client or restoring session:", error);
|
|
60
|
+
status.value = "anonymous";
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
provide: {
|
|
64
|
+
atproto
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const PRIVATE_AGENT_STATE_KEY = "atproto:agent:private";
|
|
2
|
+
export declare const PRIVATE_AGENT_SESSION_KEY = "atproto:agent:private-session-sub";
|
|
3
|
+
export declare function publicAgentStateKey(serviceEndpoint: string): string;
|
|
4
|
+
export declare function customAgentStateKey(service: string): string;
|
|
5
|
+
/**
|
|
6
|
+
* Clears the cached authenticated agent. Call after sign-out or before restore.
|
|
7
|
+
*/
|
|
8
|
+
export declare function invalidateAtprotoPrivateAgent(): void;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { useState } from "nuxt/app";
|
|
2
|
+
export const PRIVATE_AGENT_STATE_KEY = "atproto:agent:private";
|
|
3
|
+
export const PRIVATE_AGENT_SESSION_KEY = "atproto:agent:private-session-sub";
|
|
4
|
+
export function publicAgentStateKey(serviceEndpoint) {
|
|
5
|
+
return `atproto:agent:public:${serviceEndpoint}`;
|
|
6
|
+
}
|
|
7
|
+
export function customAgentStateKey(service) {
|
|
8
|
+
return `atproto:agent:${service}`;
|
|
9
|
+
}
|
|
10
|
+
export function invalidateAtprotoPrivateAgent() {
|
|
11
|
+
const cachedAgent = useState(PRIVATE_AGENT_STATE_KEY, () => null);
|
|
12
|
+
const cachedSessionSub = useState(PRIVATE_AGENT_SESSION_KEY, () => null);
|
|
13
|
+
cachedAgent.value = null;
|
|
14
|
+
cachedSessionSub.value = null;
|
|
15
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { OAuthSession } from '@atproto/oauth-client-browser';
|
|
2
|
+
import type { NuxtApp } from 'nuxt/app';
|
|
3
|
+
import type { AtprotoContext } from '../../../types/index.js';
|
|
4
|
+
export interface AtprotoSessionLifecycleHooks {
|
|
5
|
+
hook: (name: string, callback: () => void) => void;
|
|
6
|
+
callHook: (name: string, did: string) => void | Promise<void>;
|
|
7
|
+
}
|
|
8
|
+
export type AtprotoSessionTransition = 'oauth-return' | 'cold-restore' | 'account-switch' | 'sign-out';
|
|
9
|
+
export interface ApplyAtprotoSessionOptions {
|
|
10
|
+
/** When true, lifecycle hooks run after `app:mounted` (plugin init). */
|
|
11
|
+
deferHooks?: boolean;
|
|
12
|
+
/** Pass during plugin setup before `provide` returns. */
|
|
13
|
+
atproto?: AtprotoContext;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Updates `$atproto` session state, agent cache, status, and lifecycle hooks.
|
|
17
|
+
*/
|
|
18
|
+
export declare function applyAtprotoSession(nuxtApp: NuxtApp, session: OAuthSession | undefined, transition: AtprotoSessionTransition, options?: ApplyAtprotoSessionOptions): void;
|
|
19
|
+
/**
|
|
20
|
+
* @deprecated Use `applyAtprotoSession` with `deferHooks: true` instead.
|
|
21
|
+
*/
|
|
22
|
+
export declare function scheduleAtprotoSessionHooks(hooks: AtprotoSessionLifecycleHooks, sessionSub: string, state: string | null | undefined): void;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { invalidateAtprotoPrivateAgent } from "./agentCache.js";
|
|
2
|
+
export function applyAtprotoSession(nuxtApp, session, transition, options = {}) {
|
|
3
|
+
const atproto = options.atproto ?? nuxtApp.$atproto;
|
|
4
|
+
const { deferHooks = false } = options;
|
|
5
|
+
if (session) {
|
|
6
|
+
invalidateAtprotoPrivateAgent();
|
|
7
|
+
atproto.session.value = session;
|
|
8
|
+
atproto.status.value = "authenticated";
|
|
9
|
+
const hookName = transition === "oauth-return" ? "atproto:sessionCreated" : "atproto:sessionRestored";
|
|
10
|
+
emitSessionLifecycleHook(nuxtApp.hooks, hookName, session.sub, deferHooks);
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
invalidateAtprotoPrivateAgent();
|
|
14
|
+
atproto.session.value = void 0;
|
|
15
|
+
atproto.status.value = "anonymous";
|
|
16
|
+
}
|
|
17
|
+
function emitSessionLifecycleHook(hooks, hookName, did, defer) {
|
|
18
|
+
if (defer) {
|
|
19
|
+
hooks.hook("app:mounted", () => {
|
|
20
|
+
hooks.callHook(hookName, did);
|
|
21
|
+
});
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
hooks.callHook(hookName, did);
|
|
25
|
+
}
|
|
26
|
+
export function scheduleAtprotoSessionHooks(hooks, sessionSub, state) {
|
|
27
|
+
const hookName = state != null ? "atproto:sessionCreated" : "atproto:sessionRestored";
|
|
28
|
+
emitSessionLifecycleHook(hooks, hookName, sessionSub, true);
|
|
29
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nuxt-atproto",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"description": "Interact with AT Protocol and Bluesky in your Nuxt.js application",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"nuxt",
|
|
@@ -47,23 +47,23 @@
|
|
|
47
47
|
"test:types": "vue-tsc --noEmit && cd playground && vue-tsc --noEmit"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@nuxt/kit": "^
|
|
51
|
-
"@atproto/api": "^0.
|
|
52
|
-
"@atproto/oauth-client-browser": "^0.
|
|
50
|
+
"@nuxt/kit": "^4.4.6",
|
|
51
|
+
"@atproto/api": "^0.20.6",
|
|
52
|
+
"@atproto/oauth-client-browser": "^0.4.1"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
|
-
"@nuxt/devtools": "^2.4
|
|
56
|
-
"@nuxt/eslint-config": "^1.
|
|
57
|
-
"@nuxt/module-builder": "^1.0.
|
|
58
|
-
"@nuxt/schema": "^
|
|
59
|
-
"@nuxt/test-utils": "^3.18.0",
|
|
55
|
+
"@nuxt/devtools": "^3.2.4",
|
|
56
|
+
"@nuxt/eslint-config": "^1.15.2",
|
|
57
|
+
"@nuxt/module-builder": "^1.0.2",
|
|
58
|
+
"@nuxt/schema": "^4.4.6",
|
|
60
59
|
"@types/node": "latest",
|
|
61
|
-
"changelogen": "^0.6.
|
|
62
|
-
"eslint": "^
|
|
63
|
-
"nuxt": "^
|
|
64
|
-
"typescript": "~
|
|
65
|
-
"vitest": "^
|
|
66
|
-
"vue
|
|
60
|
+
"changelogen": "^0.6.2",
|
|
61
|
+
"eslint": "^10.4.0",
|
|
62
|
+
"nuxt": "^4.4.6",
|
|
63
|
+
"typescript": "~6.0.3",
|
|
64
|
+
"vitest": "^4.1.7",
|
|
65
|
+
"vue": "^3.5.35",
|
|
66
|
+
"vue-tsc": "^3.3.2"
|
|
67
67
|
},
|
|
68
68
|
"unbuild": {
|
|
69
69
|
"failOnWarn": false
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { useNuxtApp, useRuntimeConfig } from "#app";
|
|
2
|
-
import { Agent, AtpAgent } from "@atproto/api";
|
|
3
|
-
export function useAgent(service, fetch) {
|
|
4
|
-
const runtimeConfig = useRuntimeConfig();
|
|
5
|
-
const { $atproto } = useNuxtApp();
|
|
6
|
-
switch (service) {
|
|
7
|
-
case "private":
|
|
8
|
-
if (!$atproto.session.value) {
|
|
9
|
-
throw new Error("Not authenticated");
|
|
10
|
-
}
|
|
11
|
-
return new Agent($atproto.session.value);
|
|
12
|
-
case "public":
|
|
13
|
-
return new AtpAgent({
|
|
14
|
-
service: runtimeConfig.public.atproto.serviceEndpoint.public,
|
|
15
|
-
fetch
|
|
16
|
-
});
|
|
17
|
-
default:
|
|
18
|
-
return new AtpAgent({
|
|
19
|
-
service,
|
|
20
|
-
fetch
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { OAuthSession } from '@atproto/oauth-client';
|
|
2
|
-
export declare function useAtproto(service?: string, fetch?: any): {
|
|
3
|
-
signIn: (serviceEndpoint?: string, options?: any) => Promise<void>;
|
|
4
|
-
signInWithHandle: (handle?: string, options?: any) => Promise<void>;
|
|
5
|
-
signOut: () => Promise<void>;
|
|
6
|
-
restore: (did: string) => Promise<OAuthSession>;
|
|
7
|
-
isLogged: () => boolean;
|
|
8
|
-
getSession: () => any;
|
|
9
|
-
};
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import { useRuntimeConfig, useNuxtApp } from "nuxt/app";
|
|
2
|
-
export function useAtproto(service, fetch) {
|
|
3
|
-
const runtimeConfig = useRuntimeConfig();
|
|
4
|
-
async function signIn(serviceEndpoint = runtimeConfig.public.atproto.serviceEndpoint.private, options = runtimeConfig.public.atproto.oauth.signInOptions) {
|
|
5
|
-
const { $atproto } = useNuxtApp();
|
|
6
|
-
try {
|
|
7
|
-
await $atproto.client.signInRedirect(
|
|
8
|
-
serviceEndpoint,
|
|
9
|
-
{
|
|
10
|
-
...options,
|
|
11
|
-
signal: new AbortController().signal
|
|
12
|
-
}
|
|
13
|
-
);
|
|
14
|
-
} catch (error) {
|
|
15
|
-
console.error(`Error during sign-in:`, error);
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
async function signInWithHandle(handle, options = runtimeConfig.public.atproto.oauth.signInOptions) {
|
|
19
|
-
const { $atproto } = useNuxtApp();
|
|
20
|
-
try {
|
|
21
|
-
let handlePrompt = null;
|
|
22
|
-
if (!handle) {
|
|
23
|
-
handlePrompt = window.prompt("Type your handle");
|
|
24
|
-
if (!handlePrompt) {
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
handle = handlePrompt;
|
|
28
|
-
}
|
|
29
|
-
const url = await $atproto.client.authorize(handle, options);
|
|
30
|
-
window.location.href = url.href;
|
|
31
|
-
} catch (error) {
|
|
32
|
-
console.error(`Error during sign-in for handle ${handle}:`, error);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
async function restore(did) {
|
|
36
|
-
const { $atproto } = useNuxtApp();
|
|
37
|
-
const session = await $atproto.client.restore(did);
|
|
38
|
-
$atproto.session.value = session;
|
|
39
|
-
return session;
|
|
40
|
-
}
|
|
41
|
-
async function signOut() {
|
|
42
|
-
const { $atproto } = useNuxtApp();
|
|
43
|
-
$atproto.client.revoke($atproto.session.value.did);
|
|
44
|
-
$atproto.session.value = void 0;
|
|
45
|
-
if (runtimeConfig.public.atproto.debug) {
|
|
46
|
-
console.log("User signed out");
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
function isLogged() {
|
|
50
|
-
const { $atproto } = useNuxtApp();
|
|
51
|
-
return !!$atproto.session.value;
|
|
52
|
-
}
|
|
53
|
-
function getSession() {
|
|
54
|
-
const { $atproto } = useNuxtApp();
|
|
55
|
-
return $atproto.session.value;
|
|
56
|
-
}
|
|
57
|
-
return {
|
|
58
|
-
signIn,
|
|
59
|
-
signInWithHandle,
|
|
60
|
-
signOut,
|
|
61
|
-
restore,
|
|
62
|
-
isLogged,
|
|
63
|
-
getSession
|
|
64
|
-
};
|
|
65
|
-
}
|
package/dist/runtime/plugin.d.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
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;
|
package/dist/runtime/plugin.js
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
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
|
-
});
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { useAgent } from "../composables/useAgent.js";
|
|
2
|
-
export async function resolveActorDid(handle) {
|
|
3
|
-
const agent = useAgent("public");
|
|
4
|
-
const { did } = await agent.com.atproto.identity.resolveHandle({
|
|
5
|
-
handle
|
|
6
|
-
}).then((result) => result.data);
|
|
7
|
-
return did;
|
|
8
|
-
}
|
|
9
|
-
export async function resolveActorServiceEndpoint(did) {
|
|
10
|
-
const response = await fetch(`https://plc.directory/${did}`);
|
|
11
|
-
if (!response.ok) {
|
|
12
|
-
throw new Error("Failed to fetch profile service endpoint");
|
|
13
|
-
}
|
|
14
|
-
const data = await response.json();
|
|
15
|
-
return data.service[0].serviceEndpoint;
|
|
16
|
-
}
|