authula 0.7.16 → 0.8.0-beta.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 +196 -40
- package/dist/{index.js → core-Csdt31Tx.js} +6 -17
- package/dist/core-Csdt31Tx.js.map +1 -0
- package/dist/core.d.ts +2 -0
- package/dist/core.js +4 -0
- package/dist/{custom-fetch-CXJ4-i7I.js → custom-fetch-DJxn7nVP.js} +26 -10
- package/dist/custom-fetch-DJxn7nVP.js.map +1 -0
- package/dist/framework-client-CsGjKPXl.d.ts +8 -0
- package/dist/framework-client-D-yGjqAx.js +50 -0
- package/dist/framework-client-D-yGjqAx.js.map +1 -0
- package/dist/{index-xeVPFZn7.d.ts → index-Cuok0NWW.d.ts} +2 -448
- package/dist/models.d.ts +2 -0
- package/dist/models.js +11 -0
- package/dist/models.js.map +1 -0
- package/dist/plugins.d.ts +513 -0
- package/dist/plugins.js +2472 -0
- package/dist/plugins.js.map +1 -0
- package/dist/react.d.ts +3728 -0
- package/dist/{plugins/index.js → react.js} +975 -1256
- package/dist/react.js.map +1 -0
- package/dist/{rolldown-runtime-w6R9maHv.js → rolldown-runtime-CNh9ExCG.js} +1 -2
- package/dist/sdk-Cu28BXFh.d.ts +102 -0
- package/dist/solid.d.ts +3955 -0
- package/dist/solid.js +5461 -0
- package/dist/solid.js.map +1 -0
- package/dist/svelte.d.ts +3368 -0
- package/dist/svelte.js +5500 -0
- package/dist/svelte.js.map +1 -0
- package/dist/vue.d.ts +3289 -0
- package/dist/vue.js +5644 -0
- package/dist/vue.js.map +1 -0
- package/dist/zod.d.ts +3862 -0
- package/dist/zod.js +1480 -0
- package/dist/zod.js.map +1 -0
- package/package.json +61 -9
- package/dist/custom-fetch-CXJ4-i7I.js.map +0 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.js.map +0 -1
- package/dist/plugins/index.d.ts +0 -4908
- package/dist/plugins/index.js.map +0 -1
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
5
|
<p align="center">
|
|
6
|
-
This Node.js SDK provides seamless integration with an Authula server for
|
|
6
|
+
This Node.js SDK provides seamless integration with an Authula server for client-side and server-side applications. Import only the pieces you need from a framework subpath.
|
|
7
7
|
</p>
|
|
8
8
|
|
|
9
9
|
<div align="center">
|
|
@@ -22,70 +22,218 @@ This Node.js SDK provides seamless integration with an Authula server for both c
|
|
|
22
22
|
|
|
23
23
|
## Features
|
|
24
24
|
|
|
25
|
-
- **Framework
|
|
25
|
+
- **Framework subpaths**: `authula/core`, `authula/models`, `authula/plugins`, `authula/zod`, `authula/react`, `authula/vue`, `authula/solid`, `authula/svelte`
|
|
26
26
|
- **Full TypeScript Support**: Complete TypeScript definitions with strict typing
|
|
27
27
|
- **CSRF Protection**: Automatic CSRF token handling for mutating requests
|
|
28
28
|
- **Multiple Auth Methods**: Email/password, OAuth2, JWT-based authentication
|
|
29
29
|
- **Plugin Architecture**: Extensible plugin system for custom authentication flows
|
|
30
30
|
- **Automatic Token Refresh**: Built-in bearer token refresh mechanism
|
|
31
|
-
- **Built-in
|
|
31
|
+
- **Built-in Zod and TanStack Query support**: Zod schemas from `authula/zod` and per-framework TanStack Query hooks
|
|
32
32
|
|
|
33
33
|
## Installation
|
|
34
34
|
|
|
35
|
+
Install `authula` plus the TanStack Query adapter for your framework:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# React
|
|
39
|
+
pnpm add authula @tanstack/react-query
|
|
40
|
+
|
|
41
|
+
# Vue
|
|
42
|
+
pnpm add authula @tanstack/vue-query
|
|
43
|
+
|
|
44
|
+
# Solid
|
|
45
|
+
pnpm add authula @tanstack/solid-query
|
|
46
|
+
|
|
47
|
+
# Svelte
|
|
48
|
+
pnpm add authula @tanstack/svelte-query
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
The framework runtime (`react`, `vue`, `solid-js`, `svelte`) is already present in
|
|
52
|
+
your app and is declared as an optional peer, not installed by `authula`.
|
|
53
|
+
|
|
54
|
+
Core-only/server users need no query adapter and no framework runtime:
|
|
55
|
+
|
|
35
56
|
```bash
|
|
36
|
-
npm install authula
|
|
37
|
-
# or
|
|
38
|
-
yarn add authula
|
|
39
|
-
# or
|
|
40
57
|
pnpm add authula
|
|
41
58
|
```
|
|
42
59
|
|
|
60
|
+
## Imports
|
|
61
|
+
|
|
62
|
+
| Import | Contents |
|
|
63
|
+
| ----------------- | ---------------------------------------------------------------------- |
|
|
64
|
+
| `authula/core` | `AuthulaClient`, core `createClient` (direct methods only), `ApiError` |
|
|
65
|
+
| `authula/models` | Generated TypeScript models and enum values |
|
|
66
|
+
| `authula/zod` | Generated Zod schemas |
|
|
67
|
+
| `authula/plugins` | Plugin classes |
|
|
68
|
+
| `authula/react` | React `createClient`, hooks/types, core re-export |
|
|
69
|
+
| `authula/vue` | Vue `createClient`, hooks/types, core re-export |
|
|
70
|
+
| `authula/solid` | Solid `createClient`, hooks/types, core re-export |
|
|
71
|
+
| `authula/svelte` | Svelte `createClient`, hooks/types, core re-export |
|
|
72
|
+
|
|
73
|
+
There is no `authula` root import. Always use a subpath.
|
|
74
|
+
|
|
43
75
|
## Quick Start
|
|
44
76
|
|
|
45
|
-
|
|
77
|
+
Import `createClient` from your framework subpath. The client it returns has
|
|
78
|
+
both direct methods and that framework's query hooks attached to each plugin:
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
const client = createClient({ url, plugins: [new CorePlugin()] });
|
|
46
82
|
|
|
47
|
-
|
|
83
|
+
await client.core.getMe(); // direct request method
|
|
84
|
+
client.core.useGetMe(); // React/Vue/Solid hook
|
|
85
|
+
client.core.createGetMe(); // Svelte hook
|
|
86
|
+
```
|
|
48
87
|
|
|
49
|
-
|
|
88
|
+
Hooks are bound to the client instance that created them, so their URL, cookies,
|
|
89
|
+
and CSRF/bearer hooks always come from that client. They still use the TanStack
|
|
90
|
+
Query client from your app's `QueryClientProvider` (or equivalent).
|
|
50
91
|
|
|
51
|
-
|
|
52
|
-
|
|
92
|
+
### React
|
|
93
|
+
|
|
94
|
+
```tsx
|
|
95
|
+
import { createClient } from "authula/react";
|
|
53
96
|
import { CorePlugin, EmailPasswordPlugin } from "authula/plugins";
|
|
54
97
|
|
|
55
|
-
const
|
|
56
|
-
// Your Authula server URL
|
|
98
|
+
const client = createClient({
|
|
57
99
|
url: "http://localhost:8080/api/auth",
|
|
58
100
|
plugins: [new CorePlugin(), new EmailPasswordPlugin()],
|
|
59
101
|
});
|
|
60
102
|
|
|
61
|
-
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
103
|
+
function Profile() {
|
|
104
|
+
const { data, isLoading, isError } = client.core.useGetMe();
|
|
105
|
+
if (isLoading) return <p>Loading…</p>;
|
|
106
|
+
if (isError) return <p>Not signed in</p>;
|
|
107
|
+
return <p>{data?.user?.email}</p>;
|
|
108
|
+
}
|
|
66
109
|
```
|
|
67
110
|
|
|
68
|
-
|
|
111
|
+
### Vue
|
|
69
112
|
|
|
70
|
-
|
|
113
|
+
```vue
|
|
114
|
+
<script setup lang="ts">
|
|
115
|
+
import { createClient } from "authula/vue";
|
|
116
|
+
import { CorePlugin, EmailPasswordPlugin } from "authula/plugins";
|
|
71
117
|
|
|
72
|
-
|
|
73
|
-
|
|
118
|
+
const client = createClient({
|
|
119
|
+
url: "http://localhost:8080/api/auth",
|
|
120
|
+
plugins: [new CorePlugin(), new EmailPasswordPlugin()],
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
const { data, isLoading, isError } = client.core.useGetMe();
|
|
124
|
+
</script>
|
|
125
|
+
|
|
126
|
+
<template>
|
|
127
|
+
<p v-if="isLoading">Loading…</p>
|
|
128
|
+
<p v-else-if="isError">Not signed in</p>
|
|
129
|
+
<p v-else>{{ data?.user?.email }}</p>
|
|
130
|
+
</template>
|
|
131
|
+
```
|
|
74
132
|
|
|
75
|
-
|
|
133
|
+
### Solid
|
|
134
|
+
|
|
135
|
+
```tsx
|
|
136
|
+
import { createClient } from "authula/solid";
|
|
76
137
|
import { CorePlugin, EmailPasswordPlugin } from "authula/plugins";
|
|
77
138
|
|
|
78
|
-
const
|
|
79
|
-
// Your Authula server URL
|
|
139
|
+
const client = createClient({
|
|
80
140
|
url: "http://localhost:8080/api/auth",
|
|
81
141
|
plugins: [new CorePlugin(), new EmailPasswordPlugin()],
|
|
82
|
-
// For frameworks other than Next.js, you may need to implement this cookies object manually by implementing the interface.
|
|
83
|
-
cookies: cookies,
|
|
84
142
|
});
|
|
85
143
|
|
|
86
|
-
|
|
144
|
+
function Profile() {
|
|
145
|
+
const query = client.core.useGetMe();
|
|
146
|
+
if (query.isLoading) return <p>Loading…</p>;
|
|
147
|
+
if (query.isError) return <p>Not signed in</p>;
|
|
148
|
+
return <p>{query.data?.user?.email}</p>;
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Svelte
|
|
153
|
+
|
|
154
|
+
```svelte
|
|
155
|
+
<script lang="ts">
|
|
156
|
+
import { createClient } from "authula/svelte";
|
|
157
|
+
import { CorePlugin, EmailPasswordPlugin } from "authula/plugins";
|
|
87
158
|
|
|
88
|
-
const
|
|
159
|
+
const client = createClient({
|
|
160
|
+
url: "http://localhost:8080/api/auth",
|
|
161
|
+
plugins: [new CorePlugin(), new EmailPasswordPlugin()],
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
const query = client.core.createGetMe();
|
|
165
|
+
</script>
|
|
166
|
+
|
|
167
|
+
{#if $query.isLoading}
|
|
168
|
+
<p>Loading…</p>
|
|
169
|
+
{:else if $query.isError}
|
|
170
|
+
<p>Not signed in</p>
|
|
171
|
+
{:else}
|
|
172
|
+
<p>{$query.data?.user?.email}</p>
|
|
173
|
+
{/if}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Raw hooks are still exported from each framework subpath for convenience:
|
|
177
|
+
|
|
178
|
+
```ts
|
|
179
|
+
import { createClient, useGetMe } from "authula/react";
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
The raw hook resolves the most recently created client as its default. Prefer
|
|
183
|
+
`client.core.useGetMe()` when you need a specific client instance, such as SSR.
|
|
184
|
+
|
|
185
|
+
### Direct methods and models
|
|
186
|
+
|
|
187
|
+
For server-side or core-only usage, import `createClient` from `authula/core`.
|
|
188
|
+
It provides direct methods without query hooks:
|
|
189
|
+
|
|
190
|
+
```ts
|
|
191
|
+
import { createClient } from "authula/core";
|
|
192
|
+
import { CorePlugin } from "authula/plugins";
|
|
193
|
+
import type { User } from "authula/models";
|
|
194
|
+
|
|
195
|
+
const client = createClient({
|
|
196
|
+
url: "http://localhost:8080/api/auth",
|
|
197
|
+
plugins: [new CorePlugin()],
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
const me = await client.core.getMe();
|
|
201
|
+
|
|
202
|
+
const user: User = me.data.user;
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Zod schemas
|
|
206
|
+
|
|
207
|
+
```ts
|
|
208
|
+
import * as schemas from "authula/zod";
|
|
209
|
+
|
|
210
|
+
const result = schemas.SignInRequest.safeParse(input);
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## Server-side rendering
|
|
214
|
+
|
|
215
|
+
- Client-bound hooks (`client.core.useGetMe()`) use the cookies and URL of the
|
|
216
|
+
client instance they came from, so create one client per request when using
|
|
217
|
+
hooks during SSR.
|
|
218
|
+
- Use direct client methods (`await client.core.getMe()`) when possible.
|
|
219
|
+
- Raw hook imports (`import { useGetMe } from "authula/react"`) use the most
|
|
220
|
+
recently created client globally, so avoid them for request-scoped SSR.
|
|
221
|
+
- Per-call overrides are supported and take precedence over the client config:
|
|
222
|
+
`client.core.useGetMe({ request: { baseUrl, cookies } })`.
|
|
223
|
+
|
|
224
|
+
```ts
|
|
225
|
+
import { cookies } from "next/headers";
|
|
226
|
+
|
|
227
|
+
import { createClient } from "authula/react";
|
|
228
|
+
import { CorePlugin } from "authula/plugins";
|
|
229
|
+
|
|
230
|
+
const client = createClient({
|
|
231
|
+
url: "http://localhost:8080/api/auth",
|
|
232
|
+
plugins: [new CorePlugin()],
|
|
233
|
+
cookies,
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
const me = await client.core.getMe();
|
|
89
237
|
```
|
|
90
238
|
|
|
91
239
|
## Available Plugins
|
|
@@ -109,8 +257,6 @@ The following plugins are available in this SDK:
|
|
|
109
257
|
|
|
110
258
|
### Fetch Options
|
|
111
259
|
|
|
112
|
-
Configure fetch behavior with timeout and other options:
|
|
113
|
-
|
|
114
260
|
```typescript
|
|
115
261
|
const authulaClient = createClient({
|
|
116
262
|
url: "http://localhost:8080/auth",
|
|
@@ -126,33 +272,43 @@ const authulaClient = createClient({
|
|
|
126
272
|
|
|
127
273
|
### Custom Hooks
|
|
128
274
|
|
|
129
|
-
Add custom before/after fetch hooks for advanced customization:
|
|
130
|
-
|
|
131
275
|
```typescript
|
|
132
276
|
const authulaClient = createClient({
|
|
133
277
|
url: "http://localhost:8080/auth",
|
|
134
278
|
plugins: [new CorePlugin()],
|
|
135
279
|
});
|
|
136
280
|
|
|
137
|
-
// Register a before fetch hook
|
|
138
281
|
authulaClient.registerBeforeFetch(async (ctx) => {
|
|
139
282
|
console.log(`Making request to: ${ctx.url}`);
|
|
140
|
-
// Modify context if needed
|
|
141
283
|
ctx.init.headers = {
|
|
142
284
|
...ctx.init.headers,
|
|
143
285
|
"X-Custom-Header": "value",
|
|
144
286
|
};
|
|
145
287
|
});
|
|
146
288
|
|
|
147
|
-
// Register an after fetch hook
|
|
148
289
|
authulaClient.registerAfterFetch(async (ctx, response) => {
|
|
149
290
|
console.log(`Received response: ${response.status}`);
|
|
150
|
-
if (response.status >= 400) {
|
|
151
|
-
// Handle error
|
|
152
|
-
}
|
|
153
291
|
});
|
|
154
292
|
```
|
|
155
293
|
|
|
294
|
+
## Migration from 0.7.x to 0.8.0
|
|
295
|
+
|
|
296
|
+
| Before | After |
|
|
297
|
+
| ---------------------------------------- | ------------------------------------------------------------------ |
|
|
298
|
+
| `import { createClient } from "authula"` | `import { createClient } from "authula/react"` (or your framework) |
|
|
299
|
+
| `import type { User } from "authula"` | `import type { User } from "authula/models"` |
|
|
300
|
+
| `client.core.useGetMe()` | unchanged when `createClient` comes from a framework subpath |
|
|
301
|
+
| `authula/plugins` | unchanged |
|
|
302
|
+
| zod schemas inaccessible | `import * as schemas from "authula/zod"` |
|
|
303
|
+
|
|
304
|
+
For core-only or server code, `authula/core` also exports `createClient`, but its
|
|
305
|
+
clients expose direct methods only (no query hooks).
|
|
306
|
+
|
|
307
|
+
`@tanstack/react-query` was previously a direct dependency and is now an optional
|
|
308
|
+
peer, so existing React users must add it explicitly. The same applies to the
|
|
309
|
+
other TanStack adapters and to the framework runtimes (`react`, `vue`,
|
|
310
|
+
`solid-js`, `svelte`), which are optional peers supplied by your app.
|
|
311
|
+
|
|
156
312
|
## Contributing
|
|
157
313
|
|
|
158
314
|
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
|
|
@@ -1,17 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { r as setDefaultClient } from "./custom-fetch-DJxn7nVP.js";
|
|
2
2
|
import { parseCookie } from "cookie";
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
const OrganizationInvitationStatus = {
|
|
6
|
-
pending: "pending",
|
|
7
|
-
accepted: "accepted",
|
|
8
|
-
rejected: "rejected",
|
|
9
|
-
revoked: "revoked",
|
|
10
|
-
expired: "expired"
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
//#endregion
|
|
14
|
-
//#region src/client.ts
|
|
4
|
+
// src/core/client.ts
|
|
15
5
|
var AuthulaClient = class {
|
|
16
6
|
config;
|
|
17
7
|
plugins;
|
|
@@ -22,6 +12,7 @@ var AuthulaClient = class {
|
|
|
22
12
|
const { plugins: _, ...rest } = options;
|
|
23
13
|
this.config = rest;
|
|
24
14
|
for (const plugin of this.plugins) this[plugin.id] = plugin.init(this);
|
|
15
|
+
setDefaultClient(this);
|
|
25
16
|
}
|
|
26
17
|
registerBeforeFetch(hook) {
|
|
27
18
|
this.beforeFetchHooks.push(hook);
|
|
@@ -55,12 +46,10 @@ var AuthulaClient = class {
|
|
|
55
46
|
}
|
|
56
47
|
};
|
|
57
48
|
|
|
58
|
-
|
|
59
|
-
//#region src/sdk.ts
|
|
49
|
+
// src/core/sdk.ts
|
|
60
50
|
function createClient(options) {
|
|
61
51
|
return new AuthulaClient(options);
|
|
62
52
|
}
|
|
63
53
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
//# sourceMappingURL=index.js.map
|
|
54
|
+
export { AuthulaClient as n, createClient as t };
|
|
55
|
+
//# sourceMappingURL=core-Csdt31Tx.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core-Csdt31Tx.js","names":[],"sources":["../src/core/client.ts","../src/core/sdk.ts"],"sourcesContent":["import { parseCookie } from \"cookie\";\n\nimport { setDefaultClient } from \"./runtime\";\nimport type {\n FetchContext,\n AuthulaClientConfig,\n AuthulaClientOptions,\n Plugin,\n BeforeFetchHook,\n AfterFetchHook,\n} from \"./types\";\n\nexport class AuthulaClient {\n public readonly config: AuthulaClientConfig;\n private readonly plugins: Array<Plugin>;\n private readonly beforeFetchHooks: BeforeFetchHook[] = [];\n private readonly afterFetchHooks: AfterFetchHook[] = [];\n\n constructor(options: AuthulaClientOptions) {\n this.plugins = options.plugins || [];\n\n const { plugins: _, ...rest } = options;\n this.config = rest;\n\n for (const plugin of this.plugins) {\n (this as any)[plugin.id] = plugin.init(this);\n }\n\n setDefaultClient(this);\n }\n\n public registerBeforeFetch(hook: BeforeFetchHook): void {\n this.beforeFetchHooks.push(hook);\n }\n\n public registerAfterFetch(hook: AfterFetchHook): void {\n this.afterFetchHooks.push(hook);\n }\n\n public async runBeforeFetch(ctx: FetchContext): Promise<void> {\n for (const hook of this.beforeFetchHooks) {\n await hook(ctx);\n }\n }\n\n public async runAfterFetch(ctx: FetchContext, res: Response) {\n for (const hook of this.afterFetchHooks) {\n const result = await hook(ctx, res);\n if (result === \"retry\") {\n return \"retry\";\n }\n }\n }\n\n public getPlugin<T extends Plugin>(id: string): T | undefined {\n return this.plugins.find((plugin) => plugin.id === id) as T | undefined;\n }\n\n public async getCookie(name: string): Promise<string | undefined> {\n const all = await this.getAllCookies();\n return all ? all[name] : undefined;\n }\n\n public async getAllCookies(): Promise<Record<string, string | undefined> | undefined> {\n if (this.config.cookies) {\n try {\n const store = await this.config.cookies();\n const all = store.getAll();\n const map: Record<string, string> = {};\n for (const c of all) {\n map[c.name] = c.value;\n }\n return map;\n } catch {\n return undefined;\n }\n }\n\n if (typeof document !== \"undefined\") {\n return parseCookie(document.cookie);\n }\n\n return undefined;\n }\n}\n","import { AuthulaClient } from \"./client\";\nimport type { AuthulaClientOptions, Plugin } from \"./types\";\n\n// Helper to extract plugin methods\nexport type InferPluginMethods<P> = P extends { init: (client: any) => infer M } ? M : never;\n\nexport type ClientWithPlugins<T extends readonly Plugin[]> = AuthulaClient & {\n [P in T[number] as P[\"id\"]]: InferPluginMethods<P>;\n};\n\nexport function createClient<const T extends readonly Plugin[]>(\n options: Omit<AuthulaClientOptions, \"plugins\"> & { plugins: T },\n): ClientWithPlugins<T> {\n return new AuthulaClient(options as any) as ClientWithPlugins<T>;\n}\n"],"mappings":";;;;AAYA,IAAa,gBAAb,MAA2B;CACzB,AAAgB;CAChB,AAAiB;CACjB,AAAiB,mBAAsC,CAAC;CACxD,AAAiB,kBAAoC,CAAC;CAEtD,YAAY,SAA+B;EACzC,KAAK,UAAU,QAAQ,WAAW,CAAC;EAEnC,MAAM,EAAE,SAAS,GAAG,GAAG,SAAS;EAChC,KAAK,SAAS;EAEd,KAAK,MAAM,UAAU,KAAK,SACxB,AAAC,KAAa,OAAO,MAAM,OAAO,KAAK,IAAI;EAG7C,iBAAiB,IAAI;CACvB;CAEA,AAAO,oBAAoB,MAA6B;EACtD,KAAK,iBAAiB,KAAK,IAAI;CACjC;CAEA,AAAO,mBAAmB,MAA4B;EACpD,KAAK,gBAAgB,KAAK,IAAI;CAChC;CAEA,MAAa,eAAe,KAAkC;EAC5D,KAAK,MAAM,QAAQ,KAAK,kBACtB,MAAM,KAAK,GAAG;CAElB;CAEA,MAAa,cAAc,KAAmB,KAAe;EAC3D,KAAK,MAAM,QAAQ,KAAK,iBAEtB,IAAI,MADiB,KAAK,KAAK,GAAG,MACnB,SACb,OAAO;CAGb;CAEA,AAAO,UAA4B,IAA2B;EAC5D,OAAO,KAAK,QAAQ,MAAM,WAAW,OAAO,OAAO,EAAE;CACvD;CAEA,MAAa,UAAU,MAA2C;EAChE,MAAM,MAAM,MAAM,KAAK,cAAc;EACrC,OAAO,MAAM,IAAI,QAAQ;CAC3B;CAEA,MAAa,gBAAyE;EACpF,IAAI,KAAK,OAAO,SACd,IAAI;GAEF,MAAM,OAAM,MADQ,KAAK,OAAO,QAAQ,EACvB,CAAC,OAAO;GACzB,MAAM,MAA8B,CAAC;GACrC,KAAK,MAAM,KAAK,KACd,IAAI,EAAE,QAAQ,EAAE;GAElB,OAAO;EACT,QAAQ;GACN;EACF;EAGF,IAAI,OAAO,aAAa,aACtB,OAAO,YAAY,SAAS,MAAM;CAItC;AACF;;;AC1EA,SAAgB,aACd,SACsB;CACtB,OAAO,IAAI,cAAc,OAAc;AACzC"}
|
package/dist/core.d.ts
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { c as CookieAttributes, d as Plugin, f as AuthulaClient, h as FetchContext, i as ApiError, l as CookieStore, m as BeforeFetchHook, n as InferPluginMethods, o as AuthulaClientConfig, p as AfterFetchHook, r as createClient, s as AuthulaClientOptions, t as ClientWithPlugins, u as FetchOptions } from "./sdk-Cu28BXFh.js";
|
|
2
|
+
export { AfterFetchHook, ApiError, AuthulaClient, AuthulaClientConfig, AuthulaClientOptions, BeforeFetchHook, type ClientWithPlugins, CookieAttributes, CookieStore, FetchContext, FetchOptions, type InferPluginMethods, Plugin, createClient };
|
package/dist/core.js
ADDED
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
import { parseSetCookie } from "cookie";
|
|
2
2
|
import { toCamelCaseKeys, toSnakeCaseKeys } from "es-toolkit";
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
// src/core/runtime.ts
|
|
5
|
+
const DEFAULT_CLIENT = Symbol.for("authula.default-client");
|
|
6
|
+
const globalStore = globalThis;
|
|
7
|
+
const store = globalStore[DEFAULT_CLIENT] ??= {};
|
|
8
|
+
function setDefaultClient(client) {
|
|
9
|
+
store.client = client;
|
|
10
|
+
}
|
|
11
|
+
function getDefaultClient() {
|
|
12
|
+
return store.client;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// src/core/mutators/custom-fetch.ts
|
|
5
16
|
var ApiError = class extends Error {
|
|
6
17
|
status;
|
|
7
18
|
data;
|
|
@@ -80,12 +91,18 @@ function convertQueryKeysToSnakeCase(url) {
|
|
|
80
91
|
return `${base}?${snakeParams.toString()}`;
|
|
81
92
|
}
|
|
82
93
|
async function customFetch(url, options) {
|
|
83
|
-
const
|
|
94
|
+
const client = options?.baseUrl === void 0 ? getDefaultClient() : void 0;
|
|
95
|
+
const baseUrl = options?.baseUrl ?? client?.config.url.replace(/\/+$/, "") ?? "";
|
|
96
|
+
const cookies = options?.cookies ?? client?.config.cookies;
|
|
97
|
+
const onBeforeFetch = options?.onBeforeFetch ?? (client ? client.runBeforeFetch.bind(client) : void 0);
|
|
98
|
+
const onAfterFetch = options?.onAfterFetch ?? (client ? client.runAfterFetch.bind(client) : void 0);
|
|
99
|
+
if (!baseUrl && !/^https?:\/\//.test(url)) throw new Error("No Authula client configured. Call createClient() before using Authula hooks, or pass a baseUrl request option.");
|
|
100
|
+
const resolvedUrl = convertQueryKeysToSnakeCase(prependBase(url, baseUrl));
|
|
84
101
|
const headers = new Headers(options?.headers || {});
|
|
85
102
|
let cookieStore = null;
|
|
86
103
|
let resolvedCookies;
|
|
87
|
-
if (
|
|
88
|
-
cookieStore = await
|
|
104
|
+
if (cookies) try {
|
|
105
|
+
cookieStore = await cookies();
|
|
89
106
|
const all = cookieStore.getAll();
|
|
90
107
|
if (all.length > 0 && !headers.has("cookie")) headers.set("cookie", all.map((c) => `${c.name}=${c.value}`).join("; "));
|
|
91
108
|
resolvedCookies = {};
|
|
@@ -105,10 +122,10 @@ async function customFetch(url, options) {
|
|
|
105
122
|
meta: {},
|
|
106
123
|
cookies: resolvedCookies
|
|
107
124
|
};
|
|
108
|
-
if (
|
|
125
|
+
if (onBeforeFetch) await onBeforeFetch(ctx);
|
|
109
126
|
let res = await fetch(ctx.url, ctx.init);
|
|
110
|
-
if (
|
|
111
|
-
if (await
|
|
127
|
+
if (onAfterFetch) {
|
|
128
|
+
if (await onAfterFetch(ctx, res) === "retry" && !ctx.meta.retry) {
|
|
112
129
|
ctx.meta.retry = true;
|
|
113
130
|
res = await fetch(ctx.url, ctx.init);
|
|
114
131
|
}
|
|
@@ -125,6 +142,5 @@ async function customFetch(url, options) {
|
|
|
125
142
|
return toCamelCaseKeys(data);
|
|
126
143
|
}
|
|
127
144
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
//# sourceMappingURL=custom-fetch-CXJ4-i7I.js.map
|
|
145
|
+
export { customFetch as n, setDefaultClient as r, ApiError as t };
|
|
146
|
+
//# sourceMappingURL=custom-fetch-DJxn7nVP.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"custom-fetch-DJxn7nVP.js","names":[],"sources":["../src/core/runtime.ts","../src/core/mutators/custom-fetch.ts"],"sourcesContent":["import type { AuthulaClient } from \"./client\";\n\nconst DEFAULT_CLIENT = Symbol.for(\"authula.default-client\");\n\ntype RuntimeStore = {\n client?: AuthulaClient;\n};\n\nconst globalStore = globalThis as unknown as Record<symbol, RuntimeStore | undefined>;\nconst store = (globalStore[DEFAULT_CLIENT] ??= {});\n\nexport function setDefaultClient(client: AuthulaClient | undefined): void {\n store.client = client;\n}\n\nexport function getDefaultClient(): AuthulaClient | undefined {\n return store.client;\n}\n","import { parseSetCookie } from \"cookie\";\nimport { toCamelCaseKeys, toSnakeCaseKeys } from \"es-toolkit\";\n\nimport type { FetchContext, CookieStore } from \"../types\";\nimport { getDefaultClient } from \"../runtime\";\n\nexport class ApiError extends Error {\n status: number;\n data: any;\n headers: Headers;\n\n constructor(message: string, status: number, data: any, headers: Headers) {\n super(message || `Request failed with status ${status}`);\n this.name = \"ApiError\";\n this.status = status;\n this.data = data;\n this.headers = headers;\n }\n}\n\nfunction prependBase(url: string, base: string): string {\n if (!base) {\n return url;\n }\n if (/^https?:\\/\\//.test(url)) {\n return url.replace(/^https?:\\/\\/[^/]+/, base);\n }\n return `${base}${url.startsWith(\"/\") ? url : `/${url}`}`;\n}\n\nfunction convertRequestBodyToSnakeCase(\n body: BodyInit | null | undefined,\n): BodyInit | null | undefined {\n if (typeof body !== \"string\") {\n return body;\n }\n try {\n const parsed = JSON.parse(body);\n return parsed !== null && typeof parsed === \"object\"\n ? JSON.stringify(toSnakeCaseKeys(parsed))\n : body;\n } catch {\n return body;\n }\n}\n\nasync function parseJsonBody(res: Response): Promise<any> {\n if ([204, 205, 304].includes(res.status)) {\n return null;\n }\n const text = await res.text();\n return text ? JSON.parse(text) : null;\n}\n\nconst KNOWN_COOKIE_ATTRS = new Set([\n \"path\",\n \"domain\",\n \"expires\",\n \"maxAge\",\n \"httpOnly\",\n \"secure\",\n \"sameSite\",\n]);\n\nfunction parseSetCookieToStore(raw: string, store: CookieStore): void {\n let parsed: Record<string, any>;\n try {\n parsed = parseSetCookie(raw) as Record<string, any>;\n } catch {\n return;\n }\n if (!parsed) {\n return;\n }\n\n const { name, value, ...attrs } = parsed;\n const normalized: Record<string, any> = {};\n for (const [k, v] of Object.entries(attrs)) {\n const lower = k.toLowerCase();\n if (KNOWN_COOKIE_ATTRS.has(k)) {\n normalized[k] = v;\n } else if (lower === \"maxage\") normalized[\"maxAge\"] = v;\n else if (lower === \"httponly\") normalized[\"httpOnly\"] = v;\n else if (lower === \"samesite\") normalized[\"sameSite\"] = v;\n }\n\n try {\n store.set(name, value, normalized);\n } catch {\n // store may be immutable during SSR render\n }\n}\n\nexport function convertQueryKeysToSnakeCase(url: string): string {\n const qIndex = url.indexOf(\"?\");\n if (qIndex === -1) {\n return url;\n }\n\n const base = url.slice(0, qIndex);\n const searchParams = new URLSearchParams(url.slice(qIndex + 1));\n const snakeParams = new URLSearchParams();\n\n for (const [key, value] of searchParams.entries()) {\n const snakeKey = key.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`);\n snakeParams.append(snakeKey, value);\n }\n\n return `${base}?${snakeParams.toString()}`;\n}\n\nexport async function customFetch<T>(\n url: string,\n options?: RequestInit & {\n method?: string;\n baseUrl?: string;\n cookies?: () => CookieStore | Promise<CookieStore>;\n onBeforeFetch?: (ctx: FetchContext) => Promise<void>;\n onAfterFetch?: (ctx: FetchContext, res: Response) => Promise<\"retry\" | void>;\n },\n): Promise<T> {\n const client = options?.baseUrl === undefined ? getDefaultClient() : undefined;\n const baseUrl = options?.baseUrl ?? client?.config.url.replace(/\\/+$/, \"\") ?? \"\";\n const cookies = options?.cookies ?? client?.config.cookies;\n const onBeforeFetch =\n options?.onBeforeFetch ?? (client ? client.runBeforeFetch.bind(client) : undefined);\n const onAfterFetch =\n options?.onAfterFetch ?? (client ? client.runAfterFetch.bind(client) : undefined);\n\n if (!baseUrl && !/^https?:\\/\\//.test(url)) {\n throw new Error(\n \"No Authula client configured. Call createClient() before using Authula hooks, or pass a baseUrl request option.\",\n );\n }\n\n const resolvedUrl = convertQueryKeysToSnakeCase(prependBase(url, baseUrl));\n\n const headers = new Headers(options?.headers || {});\n\n let cookieStore: CookieStore | null = null;\n let resolvedCookies: Record<string, string> | undefined;\n if (cookies) {\n try {\n cookieStore = await cookies();\n const all = cookieStore.getAll();\n if (all.length > 0 && !headers.has(\"cookie\")) {\n headers.set(\"cookie\", all.map((c) => `${c.name}=${c.value}`).join(\"; \"));\n }\n resolvedCookies = {};\n for (const c of all) {\n resolvedCookies[c.name] = c.value;\n }\n } catch {\n cookieStore = null;\n }\n }\n\n const ctx: FetchContext = {\n url: resolvedUrl,\n init: {\n method: options?.method ?? \"GET\",\n headers,\n body: convertRequestBodyToSnakeCase(options?.body),\n credentials: \"include\",\n signal: options?.signal ?? null,\n },\n meta: {},\n cookies: resolvedCookies,\n };\n\n if (onBeforeFetch) {\n await onBeforeFetch(ctx);\n }\n\n let res = await fetch(ctx.url, ctx.init);\n\n if (onAfterFetch) {\n const action = await onAfterFetch(ctx, res);\n if (action === \"retry\" && !ctx.meta.retry) {\n ctx.meta.retry = true;\n res = await fetch(ctx.url, ctx.init);\n }\n }\n\n if (cookieStore) {\n const values: string[] =\n typeof res.headers.getSetCookie === \"function\"\n ? res.headers.getSetCookie()\n : (res.headers.get(\"set-cookie\")?.split(\", \") ?? []);\n for (const raw of values) {\n parseSetCookieToStore(raw, cookieStore);\n }\n }\n\n const data = await parseJsonBody(res);\n\n if (!res.ok) {\n const message =\n typeof data === \"object\" && data !== null && \"message\" in data ? String(data.message) : \"\";\n throw new ApiError(message, res.status, data, res.headers);\n }\n\n return toCamelCaseKeys(data) as T;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { d as Plugin, f as AuthulaClient, n as InferPluginMethods } from "./sdk-Cu28BXFh.js";
|
|
2
|
+
type HookPrefix = "use" | "create";
|
|
3
|
+
type HookName<Prefix extends HookPrefix, Method extends PropertyKey> = Method extends string ? `${Prefix}${Capitalize<Method>}` : never;
|
|
4
|
+
type PluginHooks<P extends Plugin, G extends object, Prefix extends HookPrefix> = { [K in keyof G as K extends HookName<Prefix, keyof InferPluginMethods<P>> ? K : never]: G[K]; };
|
|
5
|
+
type WithFrameworkApi<P extends Plugin, G extends object, Prefix extends HookPrefix> = [InferPluginMethods<P>] extends [void] ? InferPluginMethods<P> : InferPluginMethods<P> & PluginHooks<P, G, Prefix>;
|
|
6
|
+
type ClientWithPluginHooks<T extends readonly Plugin[], G extends object, Prefix extends HookPrefix> = AuthulaClient & { [P in T[number] as P["id"]]: WithFrameworkApi<P, G, Prefix>; };
|
|
7
|
+
export { ClientWithPluginHooks as t };
|
|
8
|
+
//# sourceMappingURL=framework-client-CsGjKPXl.d.ts.map
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// src/core/framework-client.ts
|
|
2
|
+
function hookName(prefix, method) {
|
|
3
|
+
return `${prefix}${method.charAt(0).toUpperCase()}${method.slice(1)}`;
|
|
4
|
+
}
|
|
5
|
+
function createRequestContext(client) {
|
|
6
|
+
return {
|
|
7
|
+
baseUrl: client.config.url.replace(/\/+$/, ""),
|
|
8
|
+
cookies: client.config.cookies,
|
|
9
|
+
onBeforeFetch: (ctx) => client.runBeforeFetch(ctx),
|
|
10
|
+
onAfterFetch: (ctx, res) => client.runAfterFetch(ctx, res)
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function wrapHook(hook, client) {
|
|
14
|
+
const requestContext = createRequestContext(client);
|
|
15
|
+
return (...args) => {
|
|
16
|
+
let optionsIdx = Math.max(0, hook.length - 2);
|
|
17
|
+
if (args[optionsIdx] != null && typeof args[optionsIdx] !== "object") optionsIdx = Math.max(0, hook.length - 1);
|
|
18
|
+
while (args.length <= optionsIdx) args.push(void 0);
|
|
19
|
+
const existing = args[optionsIdx];
|
|
20
|
+
const existingOptions = typeof existing === "object" && existing !== null ? existing : {};
|
|
21
|
+
const existingRequest = existingOptions.request;
|
|
22
|
+
args[optionsIdx] = {
|
|
23
|
+
...existingOptions,
|
|
24
|
+
request: {
|
|
25
|
+
...requestContext,
|
|
26
|
+
...existingRequest
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
return hook(...args);
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function attachFrameworkHooks(client, plugins, generated, prefix) {
|
|
33
|
+
const generatedHooks = generated;
|
|
34
|
+
const pluginApis = client;
|
|
35
|
+
for (const plugin of plugins) {
|
|
36
|
+
const api = pluginApis[plugin.id];
|
|
37
|
+
if (!api || typeof api !== "object") continue;
|
|
38
|
+
const pluginApi = api;
|
|
39
|
+
for (const [method, value] of Object.entries(pluginApi)) {
|
|
40
|
+
if (typeof value !== "function") continue;
|
|
41
|
+
const name = hookName(prefix, method);
|
|
42
|
+
const hook = generatedHooks[name];
|
|
43
|
+
if (typeof hook === "function") pluginApi[name] = wrapHook(hook, client);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return client;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export { attachFrameworkHooks as t };
|
|
50
|
+
//# sourceMappingURL=framework-client-D-yGjqAx.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"framework-client-D-yGjqAx.js","names":[],"sources":["../src/core/framework-client.ts"],"sourcesContent":["import type { AuthulaClient } from \"./client\";\nimport type { InferPluginMethods } from \"./sdk\";\nimport type { Plugin } from \"./types\";\n\nexport type HookPrefix = \"use\" | \"create\";\n\ntype HookName<Prefix extends HookPrefix, Method extends PropertyKey> = Method extends string\n ? `${Prefix}${Capitalize<Method>}`\n : never;\n\ntype PluginHooks<P extends Plugin, G extends object, Prefix extends HookPrefix> = {\n [K in keyof G as K extends HookName<Prefix, keyof InferPluginMethods<P>> ? K : never]: G[K];\n};\n\ntype WithFrameworkApi<P extends Plugin, G extends object, Prefix extends HookPrefix> = [\n InferPluginMethods<P>,\n] extends [void]\n ? InferPluginMethods<P>\n : InferPluginMethods<P> & PluginHooks<P, G, Prefix>;\n\nexport type ClientWithPluginHooks<\n T extends readonly Plugin[],\n G extends object,\n Prefix extends HookPrefix,\n> = AuthulaClient & {\n [P in T[number] as P[\"id\"]]: WithFrameworkApi<P, G, Prefix>;\n};\n\ntype AnyFunction = (...args: any[]) => any;\n\nfunction hookName(prefix: HookPrefix, method: string): string {\n return `${prefix}${method.charAt(0).toUpperCase()}${method.slice(1)}`;\n}\n\nfunction createRequestContext(client: AuthulaClient) {\n return {\n baseUrl: client.config.url.replace(/\\/+$/, \"\"),\n cookies: client.config.cookies,\n onBeforeFetch: (ctx: any) => client.runBeforeFetch(ctx),\n onAfterFetch: (ctx: any, res: Response) => client.runAfterFetch(ctx, res),\n };\n}\n\nexport function wrapHook(hook: AnyFunction, client: AuthulaClient): AnyFunction {\n const requestContext = createRequestContext(client);\n\n return (...args: any[]) => {\n let optionsIdx = Math.max(0, hook.length - 2);\n if (args[optionsIdx] != null && typeof args[optionsIdx] !== \"object\") {\n optionsIdx = Math.max(0, hook.length - 1);\n }\n while (args.length <= optionsIdx) {\n args.push(undefined);\n }\n\n const existing = args[optionsIdx];\n const existingOptions = typeof existing === \"object\" && existing !== null ? existing : {};\n const existingRequest = (existingOptions as { request?: object }).request;\n\n args[optionsIdx] = {\n ...existingOptions,\n // Explicit per-call values win over the client defaults, so SSR callers\n // can override cookies/baseUrl for a single request.\n request: { ...requestContext, ...existingRequest },\n };\n\n return hook(...args);\n };\n}\n\nexport function attachFrameworkHooks<\n T extends readonly Plugin[],\n G extends object,\n Prefix extends HookPrefix,\n>(\n client: AuthulaClient,\n plugins: T,\n generated: G,\n prefix: Prefix,\n): ClientWithPluginHooks<T, G, Prefix> {\n const generatedHooks = generated as Record<string, unknown>;\n const pluginApis = client as unknown as Record<string, unknown>;\n\n for (const plugin of plugins) {\n const api = pluginApis[plugin.id];\n if (!api || typeof api !== \"object\") {\n continue;\n }\n\n const pluginApi = api as Record<string, unknown>;\n for (const [method, value] of Object.entries(pluginApi)) {\n if (typeof value !== \"function\") {\n continue;\n }\n\n const name = hookName(prefix, method);\n const hook = generatedHooks[name];\n if (typeof hook === \"function\") {\n pluginApi[name] = wrapHook(hook as AnyFunction, client);\n }\n }\n }\n\n return client as ClientWithPluginHooks<T, G, Prefix>;\n}\n"],"mappings":";AA8BA,SAAS,SAAS,QAAoB,QAAwB;CAC5D,OAAO,GAAG,SAAS,OAAO,OAAO,CAAC,CAAC,CAAC,YAAY,IAAI,OAAO,MAAM,CAAC;AACpE;AAEA,SAAS,qBAAqB,QAAuB;CACnD,OAAO;EACL,SAAS,OAAO,OAAO,IAAI,QAAQ,QAAQ,EAAE;EAC7C,SAAS,OAAO,OAAO;EACvB,gBAAgB,QAAa,OAAO,eAAe,GAAG;EACtD,eAAe,KAAU,QAAkB,OAAO,cAAc,KAAK,GAAG;CAC1E;AACF;AAEA,SAAgB,SAAS,MAAmB,QAAoC;CAC9E,MAAM,iBAAiB,qBAAqB,MAAM;CAElD,QAAQ,GAAG,SAAgB;EACzB,IAAI,aAAa,KAAK,IAAI,GAAG,KAAK,SAAS,CAAC;EAC5C,IAAI,KAAK,eAAe,QAAQ,OAAO,KAAK,gBAAgB,UAC1D,aAAa,KAAK,IAAI,GAAG,KAAK,SAAS,CAAC;EAE1C,OAAO,KAAK,UAAU,YACpB,KAAK,KAAK,MAAS;EAGrB,MAAM,WAAW,KAAK;EACtB,MAAM,kBAAkB,OAAO,aAAa,YAAY,aAAa,OAAO,WAAW,CAAC;EACxF,MAAM,kBAAmB,gBAAyC;EAElE,KAAK,cAAc;GACjB,GAAG;GAGH,SAAS;IAAE,GAAG;IAAgB,GAAG;GAAgB;EACnD;EAEA,OAAO,KAAK,GAAG,IAAI;CACrB;AACF;AAEA,SAAgB,qBAKd,QACA,SACA,WACA,QACqC;CACrC,MAAM,iBAAiB;CACvB,MAAM,aAAa;CAEnB,KAAK,MAAM,UAAU,SAAS;EAC5B,MAAM,MAAM,WAAW,OAAO;EAC9B,IAAI,CAAC,OAAO,OAAO,QAAQ,UACzB;EAGF,MAAM,YAAY;EAClB,KAAK,MAAM,CAAC,QAAQ,UAAU,OAAO,QAAQ,SAAS,GAAG;GACvD,IAAI,OAAO,UAAU,YACnB;GAGF,MAAM,OAAO,SAAS,QAAQ,MAAM;GACpC,MAAM,OAAO,eAAe;GAC5B,IAAI,OAAO,SAAS,YAClB,UAAU,QAAQ,SAAS,MAAqB,MAAM;EAE1D;CACF;CAEA,OAAO;AACT"}
|