@fedify/adonisjs 2.4.0-dev.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/LICENSE +20 -0
- package/README.md +420 -0
- package/dist/configure.cjs +108 -0
- package/dist/configure.cjs.map +1 -0
- package/dist/configure.d.cts +14 -0
- package/dist/configure.d.ts +14 -0
- package/dist/configure.js +107 -0
- package/dist/configure.js.map +1 -0
- package/dist/index.cjs +19 -0
- package/dist/index.d.cts +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +8 -0
- package/dist/package.cjs +121 -0
- package/dist/package.cjs.map +1 -0
- package/dist/package.js +115 -0
- package/dist/package.js.map +1 -0
- package/dist/providers/fedify_provider.cjs +270 -0
- package/dist/providers/fedify_provider.cjs.map +1 -0
- package/dist/providers/fedify_provider.d.cts +54 -0
- package/dist/providers/fedify_provider.d.ts +55 -0
- package/dist/providers/fedify_provider.js +269 -0
- package/dist/providers/fedify_provider.js.map +1 -0
- package/dist/services/builder.cjs +9 -0
- package/dist/services/builder.cjs.map +1 -0
- package/dist/services/builder.d.cts +7 -0
- package/dist/services/builder.d.ts +8 -0
- package/dist/services/builder.js +8 -0
- package/dist/services/builder.js.map +1 -0
- package/dist/src/builder.cjs +55 -0
- package/dist/src/builder.cjs.map +1 -0
- package/dist/src/builder.d.cts +14 -0
- package/dist/src/builder.d.ts +14 -0
- package/dist/src/builder.js +54 -0
- package/dist/src/builder.js.map +1 -0
- package/dist/src/define_config.cjs +114 -0
- package/dist/src/define_config.cjs.map +1 -0
- package/dist/src/define_config.d.cts +30 -0
- package/dist/src/define_config.d.ts +30 -0
- package/dist/src/define_config.js +113 -0
- package/dist/src/define_config.js.map +1 -0
- package/dist/src/errors.cjs +77 -0
- package/dist/src/errors.cjs.map +1 -0
- package/dist/src/errors.d.cts +56 -0
- package/dist/src/errors.d.ts +56 -0
- package/dist/src/errors.js +73 -0
- package/dist/src/errors.js.map +1 -0
- package/dist/src/fedify_middleware.cjs +507 -0
- package/dist/src/fedify_middleware.cjs.map +1 -0
- package/dist/src/fedify_middleware.d.cts +85 -0
- package/dist/src/fedify_middleware.d.ts +85 -0
- package/dist/src/fedify_middleware.js +505 -0
- package/dist/src/fedify_middleware.js.map +1 -0
- package/dist/src/http_context.cjs +79 -0
- package/dist/src/http_context.cjs.map +1 -0
- package/dist/src/http_context.d.cts +31 -0
- package/dist/src/http_context.d.ts +31 -0
- package/dist/src/http_context.js +76 -0
- package/dist/src/http_context.js.map +1 -0
- package/dist/src/logging.cjs +106 -0
- package/dist/src/logging.cjs.map +1 -0
- package/dist/src/logging.d.cts +45 -0
- package/dist/src/logging.d.ts +45 -0
- package/dist/src/logging.js +106 -0
- package/dist/src/logging.js.map +1 -0
- package/dist/src/types.cjs +1 -0
- package/dist/src/types.d.cts +269 -0
- package/dist/src/types.d.ts +269 -0
- package/dist/src/types.js +1 -0
- package/dist/src/unusable_authority.cjs +79 -0
- package/dist/src/unusable_authority.cjs.map +1 -0
- package/dist/src/unusable_authority.js +76 -0
- package/dist/src/unusable_authority.js.map +1 -0
- package/dist/stubs/config/fedify.stub +105 -0
- package/dist/stubs/federation/main.stub +64 -0
- package/dist/stubs/main.cjs +16 -0
- package/dist/stubs/main.cjs.map +1 -0
- package/dist/stubs/main.d.cts +11 -0
- package/dist/stubs/main.d.ts +11 -0
- package/dist/stubs/main.js +15 -0
- package/dist/stubs/main.js.map +1 -0
- package/dist/stubs/start/federation.stub +20 -0
- package/package.json +124 -0
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import { HttpContext } from "@adonisjs/core/http";
|
|
2
|
+
import { Federation, FederationBuilder, FederationOptions, FederationOrigin, KvStore, RequestContext } from "@fedify/fedify";
|
|
3
|
+
|
|
4
|
+
//#region src/types.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* The "register pattern" interface used to configure the context data type of
|
|
7
|
+
* an application, in the same spirit as AdonisJS's own `ContainerBindings` or
|
|
8
|
+
* `EventsList` interfaces.
|
|
9
|
+
*
|
|
10
|
+
* Fedify threads an application-defined value — `TContextData` — through every
|
|
11
|
+
* dispatcher, collection callback and inbox listener. Because a package cannot
|
|
12
|
+
* know that type in advance, an application declares it by augmenting this
|
|
13
|
+
* interface once, usually in `config/fedify.ts`:
|
|
14
|
+
*
|
|
15
|
+
* ```ts
|
|
16
|
+
* declare module '@fedify/adonisjs/types' {
|
|
17
|
+
* interface FedifyTypes {
|
|
18
|
+
* contextData: { user: User | null }
|
|
19
|
+
* }
|
|
20
|
+
* }
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* After that augmentation, {@link ContextData} resolves to the declared type
|
|
24
|
+
* everywhere in the application, including inside `ctx.federation`.
|
|
25
|
+
*/
|
|
26
|
+
interface FedifyTypes {}
|
|
27
|
+
/**
|
|
28
|
+
* The context data type of the current application.
|
|
29
|
+
*
|
|
30
|
+
* Resolves to whatever {@link FedifyTypes.contextData} was augmented to, and
|
|
31
|
+
* falls back to `HttpContext | null` when the application has not augmented
|
|
32
|
+
* anything.
|
|
33
|
+
*
|
|
34
|
+
* The `| null` half of the default is deliberate and important. Fedify invokes
|
|
35
|
+
* dispatchers and inbox listeners from two very different places:
|
|
36
|
+
*
|
|
37
|
+
* - During an HTTP request, where an `HttpContext` exists, and
|
|
38
|
+
* - From a background queue worker or an Ace command, where one does *not*.
|
|
39
|
+
*
|
|
40
|
+
* Defaulting to a non-nullable `HttpContext` would let application code write
|
|
41
|
+
* `ctx.data.auth.user` in an inbox listener and only discover at 3 a.m. in
|
|
42
|
+
* production — once a real server delivers an activity through the queue — that
|
|
43
|
+
* there was never an HTTP context to read. The nullable default forces that
|
|
44
|
+
* case to be handled at compile time.
|
|
45
|
+
*/
|
|
46
|
+
type ContextData = FedifyTypes extends {
|
|
47
|
+
contextData: infer TContextData;
|
|
48
|
+
} ? TContextData : HttpContext | null;
|
|
49
|
+
/**
|
|
50
|
+
* A callback that produces the Fedify context data for a single HTTP request.
|
|
51
|
+
*
|
|
52
|
+
* This mirrors the `ContextDataFactory` type exported by the other Fedify
|
|
53
|
+
* integration packages (`@fedify/express`, `@fedify/hono`, `@fedify/koa`, …).
|
|
54
|
+
* Like Hono's and Koa's variants — and unlike Express's, which only receives
|
|
55
|
+
* the request — it is handed the whole framework context.
|
|
56
|
+
*
|
|
57
|
+
* That context is a *pre-routing* one, though: the factory runs in the server
|
|
58
|
+
* middleware stack, before route matching and every router middleware, so only
|
|
59
|
+
* `ctx.request`, `ctx.response`, `ctx.logger` and `ctx.containerResolver` are
|
|
60
|
+
* set. `ctx.auth`, `ctx.session`, `ctx.bouncer`, `ctx.params` and `ctx.route`
|
|
61
|
+
* are still `undefined` (`ctx.params` is `{}`), and dereferencing one fails
|
|
62
|
+
* every request the middleware does not bypass.
|
|
63
|
+
*
|
|
64
|
+
* The ordering is forced rather than chosen: Fedify has to decide whether it
|
|
65
|
+
* serves a request before AdonisJS routes it, and `Federation.fetch()` takes
|
|
66
|
+
* the context data by value rather than as a callback. Carry request-level
|
|
67
|
+
* state here, and look anything session-bound up inside the dispatcher that
|
|
68
|
+
* needs it.
|
|
69
|
+
*
|
|
70
|
+
* It may be synchronous or asynchronous, and it is invoked at most once per
|
|
71
|
+
* request.
|
|
72
|
+
*/
|
|
73
|
+
type ContextDataFactory<TContextData> = (ctx: HttpContext) => TContextData | Promise<TContextData>;
|
|
74
|
+
/**
|
|
75
|
+
* Options for bridging Fedify's LogTape logs into the AdonisJS logger.
|
|
76
|
+
*/
|
|
77
|
+
interface FedifyLoggingConfig {
|
|
78
|
+
/**
|
|
79
|
+
* The LogTape categories to route into the AdonisJS logger.
|
|
80
|
+
*
|
|
81
|
+
* Defaults to Fedify's own category plus LogTape's meta category, which is
|
|
82
|
+
* where LogTape reports its own configuration problems.
|
|
83
|
+
*
|
|
84
|
+
* @default `[['logtape', 'meta'], ['fedify']]`
|
|
85
|
+
*/
|
|
86
|
+
categories?: (string | string[])[];
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Everything {@link FedifyConfig} accepts except `contextDataFactory`, whose
|
|
90
|
+
* optionality is decided separately by {@link ContextDataFactoryOption}.
|
|
91
|
+
*
|
|
92
|
+
* It is Fedify's own {@link FederationOptions} — so every Fedify option is
|
|
93
|
+
* available and documented in Fedify's own reference — plus a handful of
|
|
94
|
+
* AdonisJS-specific keys, and with two differences:
|
|
95
|
+
*
|
|
96
|
+
* - `origin` is **required** (Fedify treats it as optional and derives the
|
|
97
|
+
* origin from the incoming request, which produces wrong actor URIs the
|
|
98
|
+
* moment the app sits behind a proxy or a tunnel), and
|
|
99
|
+
* - `kv` is **optional** (it falls back to an in-memory store in development).
|
|
100
|
+
*/
|
|
101
|
+
interface FedifyConfigBase extends Omit<FederationOptions<ContextData>, "kv" | "origin"> {
|
|
102
|
+
/**
|
|
103
|
+
* The canonical public origin of this server, for example
|
|
104
|
+
* `https://social.example.com`.
|
|
105
|
+
*
|
|
106
|
+
* Fedify mints actor URIs, activity IDs and collection URLs from this value,
|
|
107
|
+
* so it must be the address remote servers can reach — not the address the
|
|
108
|
+
* Node process happens to be listening on. Read it from an environment
|
|
109
|
+
* variable (the `configure` hook adds `PUBLIC_URL` for exactly this purpose).
|
|
110
|
+
*
|
|
111
|
+
* Pass a {@link FederationOrigin} object when the WebFinger handle domain
|
|
112
|
+
* differs from the web origin, for example handles `@me@example.com` served
|
|
113
|
+
* from `https://social.example.com`.
|
|
114
|
+
*/
|
|
115
|
+
origin: string | FederationOrigin;
|
|
116
|
+
/**
|
|
117
|
+
* The key–value store Fedify uses for caching remote documents, tracking
|
|
118
|
+
* inbox idempotency, and holding outbox state.
|
|
119
|
+
*
|
|
120
|
+
* Defaults to an in-memory store, which is fine for development and tests but
|
|
121
|
+
* loses all state on restart and cannot be shared between processes. A
|
|
122
|
+
* warning is logged if the default is used in production. Use a persistent
|
|
123
|
+
* implementation such as `@fedify/sqlite`, `@fedify/postgres`,
|
|
124
|
+
* `@fedify/redis`, or `@fedify/mysql` in production.
|
|
125
|
+
*/
|
|
126
|
+
kv?: KvStore;
|
|
127
|
+
/**
|
|
128
|
+
* URL path prefixes that Fedify should never see.
|
|
129
|
+
*
|
|
130
|
+
* Requests whose path starts with one of these prefixes skip the Fedify
|
|
131
|
+
* middleware entirely and go straight to the AdonisJS router. This is a pure
|
|
132
|
+
* performance escape hatch for hot paths that can never be federation
|
|
133
|
+
* endpoints — Vite's dev-server routes are the usual example.
|
|
134
|
+
*
|
|
135
|
+
* Accessing `ctx.federation` inside a request that matched one of these
|
|
136
|
+
* prefixes throws, because no Fedify context was created for it.
|
|
137
|
+
*
|
|
138
|
+
* @default `[]`
|
|
139
|
+
*/
|
|
140
|
+
ignoreRoutePrefixes?: string[];
|
|
141
|
+
/**
|
|
142
|
+
* Whether to pipe Fedify's LogTape logs into the AdonisJS logger.
|
|
143
|
+
*
|
|
144
|
+
* Fedify logs a great deal of useful diagnostic detail (signature
|
|
145
|
+
* verification, delivery attempts, WebFinger lookups) through LogTape.
|
|
146
|
+
* Without this bridge, none of it appears in the application's log output.
|
|
147
|
+
*
|
|
148
|
+
* Set to `false` if the application configures LogTape itself.
|
|
149
|
+
*
|
|
150
|
+
* @default `true`
|
|
151
|
+
*/
|
|
152
|
+
logging?: boolean | FedifyLoggingConfig;
|
|
153
|
+
/**
|
|
154
|
+
* Who drains Fedify's task queue, and with what context data.
|
|
155
|
+
*
|
|
156
|
+
* Required whenever {@link FederationOptions.queue} is set, because there is
|
|
157
|
+
* no safe default. Letting Fedify start the queue lazily — which is what it
|
|
158
|
+
* does out of the box — captures the context data of whichever HTTP request
|
|
159
|
+
* happened to enqueue the first task, and every background job for the rest
|
|
160
|
+
* of the process then runs with that one stale request's `HttpContext`.
|
|
161
|
+
*
|
|
162
|
+
* Pass a callback to have the **web process** drain the queue: the service
|
|
163
|
+
* provider starts the worker in the `ready` lifecycle hook and stops it on
|
|
164
|
+
* shutdown. There is no `HttpContext` behind a queued task, hence the
|
|
165
|
+
* callback takes no arguments.
|
|
166
|
+
*
|
|
167
|
+
* Pass `false` when a **separate worker process** owns the queue. Nothing is
|
|
168
|
+
* started in-process, and it is then that worker's job to call
|
|
169
|
+
* `federation.startQueue()`.
|
|
170
|
+
*
|
|
171
|
+
* Without a {@link FederationOptions.queue}, a callback is accepted with a
|
|
172
|
+
* warning — a task registered through `defineTask()` may bring its own
|
|
173
|
+
* queue, which this configuration cannot see — while `false` is accepted
|
|
174
|
+
* silently: it stays true when there is no queue at all, so a configuration
|
|
175
|
+
* that enables one in production only does not fail to boot in development.
|
|
176
|
+
*/
|
|
177
|
+
queueContextData?: (() => ContextData | Promise<ContextData>) | false;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* The `contextDataFactory` half of {@link FedifyConfig}, whose optionality
|
|
181
|
+
* depends on {@link ContextData}.
|
|
182
|
+
*
|
|
183
|
+
* The runtime default is `(ctx) => ctx`, which is only a correct
|
|
184
|
+
* {@link ContextData} while the application has not augmented
|
|
185
|
+
* {@link FedifyTypes} — after an augmentation an `HttpContext` is precisely
|
|
186
|
+
* what a dispatcher must *not* receive. Making the key required in that case
|
|
187
|
+
* turns "every dispatcher silently gets the wrong value" into a compile error
|
|
188
|
+
* on `config/fedify.ts`, which is the only place that can fix it.
|
|
189
|
+
*
|
|
190
|
+
* The tuple wrappers keep the conditional from distributing over a union
|
|
191
|
+
* {@link ContextData}, so an augmentation such as `User | null` is compared as
|
|
192
|
+
* a whole.
|
|
193
|
+
*/
|
|
194
|
+
type ContextDataFactoryOption = [HttpContext] extends [ContextData] ? {
|
|
195
|
+
/**
|
|
196
|
+
* Produces the {@link ContextData} for each incoming HTTP request.
|
|
197
|
+
*
|
|
198
|
+
* Defaults to `(ctx) => ctx`, i.e. the `HttpContext` itself, which matches
|
|
199
|
+
* the default of {@link ContextData}.
|
|
200
|
+
*/
|
|
201
|
+
contextDataFactory?: ContextDataFactory<ContextData>;
|
|
202
|
+
} : {
|
|
203
|
+
/**
|
|
204
|
+
* Produces the {@link ContextData} for each incoming HTTP request.
|
|
205
|
+
*
|
|
206
|
+
* Required here because the application augmented {@link FedifyTypes}:
|
|
207
|
+
* the built-in default hands dispatchers the `HttpContext`, which is no
|
|
208
|
+
* longer the declared context data type.
|
|
209
|
+
*/
|
|
210
|
+
contextDataFactory: ContextDataFactory<ContextData>;
|
|
211
|
+
};
|
|
212
|
+
/**
|
|
213
|
+
* The shape of the object passed to {@link defineConfig} in `config/fedify.ts`.
|
|
214
|
+
*
|
|
215
|
+
* {@link FedifyConfigBase} plus {@link ContextDataFactoryOption}, which makes
|
|
216
|
+
* `contextDataFactory` required exactly when {@link FedifyTypes} has been
|
|
217
|
+
* augmented.
|
|
218
|
+
*/
|
|
219
|
+
type FedifyConfig = FedifyConfigBase & ContextDataFactoryOption;
|
|
220
|
+
/**
|
|
221
|
+
* The fully-resolved configuration held by the `fedify.config` container
|
|
222
|
+
* binding: {@link FedifyConfig} with every optional AdonisJS-specific key
|
|
223
|
+
* filled in.
|
|
224
|
+
*/
|
|
225
|
+
interface ResolvedFedifyConfig extends FederationOptions<ContextData> {
|
|
226
|
+
contextDataFactory: ContextDataFactory<ContextData>;
|
|
227
|
+
ignoreRoutePrefixes: string[];
|
|
228
|
+
logging: boolean | FedifyLoggingConfig;
|
|
229
|
+
queueContextData?: (() => ContextData | Promise<ContextData>) | false;
|
|
230
|
+
}
|
|
231
|
+
declare module "@adonisjs/core/types" {
|
|
232
|
+
interface ContainerBindings {
|
|
233
|
+
/**
|
|
234
|
+
* The built {@link Federation} instance. Resolvable only once the
|
|
235
|
+
* application is ready — see the service provider for why.
|
|
236
|
+
*/
|
|
237
|
+
"fedify": Federation<ContextData>;
|
|
238
|
+
/**
|
|
239
|
+
* The {@link FederationBuilder} that dispatchers and inbox listeners are
|
|
240
|
+
* registered on, before the `Federation` instance is built.
|
|
241
|
+
*/
|
|
242
|
+
"fedify.builder": FederationBuilder<ContextData>;
|
|
243
|
+
/**
|
|
244
|
+
* The resolved contents of `config/fedify.ts`.
|
|
245
|
+
*/
|
|
246
|
+
"fedify.config": ResolvedFedifyConfig;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
declare module "@adonisjs/core/http" {
|
|
250
|
+
interface HttpContext {
|
|
251
|
+
/**
|
|
252
|
+
* The Fedify {@link RequestContext} for the current request.
|
|
253
|
+
*
|
|
254
|
+
* Use it to mint canonical URIs (`ctx.federation.getActorUri('me')`), look
|
|
255
|
+
* remote objects up (`ctx.federation.lookupObject('@user@example.com')`),
|
|
256
|
+
* or send activities (`ctx.federation.sendActivity(...)`) from ordinary
|
|
257
|
+
* AdonisJS controllers.
|
|
258
|
+
*
|
|
259
|
+
* The context is created lazily on first access, so requests that never
|
|
260
|
+
* touch it pay nothing. Accessing it throws if the request path matched
|
|
261
|
+
* {@link FedifyConfig.ignoreRoutePrefixes}, or if the Fedify server
|
|
262
|
+
* middleware is not registered in `start/kernel.ts`.
|
|
263
|
+
*/
|
|
264
|
+
federation: RequestContext<ContextData>;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
//#endregion
|
|
268
|
+
export { ContextData, ContextDataFactory, ContextDataFactoryOption, FedifyConfig, FedifyConfigBase, FedifyLoggingConfig, FedifyTypes, ResolvedFedifyConfig };
|
|
269
|
+
//# sourceMappingURL=types.d.cts.map
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import { HttpContext } from "@adonisjs/core/http";
|
|
2
|
+
import { Federation, FederationBuilder, FederationOptions, FederationOrigin, KvStore, RequestContext } from "@fedify/fedify";
|
|
3
|
+
|
|
4
|
+
//#region src/types.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* The "register pattern" interface used to configure the context data type of
|
|
7
|
+
* an application, in the same spirit as AdonisJS's own `ContainerBindings` or
|
|
8
|
+
* `EventsList` interfaces.
|
|
9
|
+
*
|
|
10
|
+
* Fedify threads an application-defined value — `TContextData` — through every
|
|
11
|
+
* dispatcher, collection callback and inbox listener. Because a package cannot
|
|
12
|
+
* know that type in advance, an application declares it by augmenting this
|
|
13
|
+
* interface once, usually in `config/fedify.ts`:
|
|
14
|
+
*
|
|
15
|
+
* ```ts
|
|
16
|
+
* declare module '@fedify/adonisjs/types' {
|
|
17
|
+
* interface FedifyTypes {
|
|
18
|
+
* contextData: { user: User | null }
|
|
19
|
+
* }
|
|
20
|
+
* }
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* After that augmentation, {@link ContextData} resolves to the declared type
|
|
24
|
+
* everywhere in the application, including inside `ctx.federation`.
|
|
25
|
+
*/
|
|
26
|
+
interface FedifyTypes {}
|
|
27
|
+
/**
|
|
28
|
+
* The context data type of the current application.
|
|
29
|
+
*
|
|
30
|
+
* Resolves to whatever {@link FedifyTypes.contextData} was augmented to, and
|
|
31
|
+
* falls back to `HttpContext | null` when the application has not augmented
|
|
32
|
+
* anything.
|
|
33
|
+
*
|
|
34
|
+
* The `| null` half of the default is deliberate and important. Fedify invokes
|
|
35
|
+
* dispatchers and inbox listeners from two very different places:
|
|
36
|
+
*
|
|
37
|
+
* - During an HTTP request, where an `HttpContext` exists, and
|
|
38
|
+
* - From a background queue worker or an Ace command, where one does *not*.
|
|
39
|
+
*
|
|
40
|
+
* Defaulting to a non-nullable `HttpContext` would let application code write
|
|
41
|
+
* `ctx.data.auth.user` in an inbox listener and only discover at 3 a.m. in
|
|
42
|
+
* production — once a real server delivers an activity through the queue — that
|
|
43
|
+
* there was never an HTTP context to read. The nullable default forces that
|
|
44
|
+
* case to be handled at compile time.
|
|
45
|
+
*/
|
|
46
|
+
type ContextData = FedifyTypes extends {
|
|
47
|
+
contextData: infer TContextData;
|
|
48
|
+
} ? TContextData : HttpContext | null;
|
|
49
|
+
/**
|
|
50
|
+
* A callback that produces the Fedify context data for a single HTTP request.
|
|
51
|
+
*
|
|
52
|
+
* This mirrors the `ContextDataFactory` type exported by the other Fedify
|
|
53
|
+
* integration packages (`@fedify/express`, `@fedify/hono`, `@fedify/koa`, …).
|
|
54
|
+
* Like Hono's and Koa's variants — and unlike Express's, which only receives
|
|
55
|
+
* the request — it is handed the whole framework context.
|
|
56
|
+
*
|
|
57
|
+
* That context is a *pre-routing* one, though: the factory runs in the server
|
|
58
|
+
* middleware stack, before route matching and every router middleware, so only
|
|
59
|
+
* `ctx.request`, `ctx.response`, `ctx.logger` and `ctx.containerResolver` are
|
|
60
|
+
* set. `ctx.auth`, `ctx.session`, `ctx.bouncer`, `ctx.params` and `ctx.route`
|
|
61
|
+
* are still `undefined` (`ctx.params` is `{}`), and dereferencing one fails
|
|
62
|
+
* every request the middleware does not bypass.
|
|
63
|
+
*
|
|
64
|
+
* The ordering is forced rather than chosen: Fedify has to decide whether it
|
|
65
|
+
* serves a request before AdonisJS routes it, and `Federation.fetch()` takes
|
|
66
|
+
* the context data by value rather than as a callback. Carry request-level
|
|
67
|
+
* state here, and look anything session-bound up inside the dispatcher that
|
|
68
|
+
* needs it.
|
|
69
|
+
*
|
|
70
|
+
* It may be synchronous or asynchronous, and it is invoked at most once per
|
|
71
|
+
* request.
|
|
72
|
+
*/
|
|
73
|
+
type ContextDataFactory<TContextData> = (ctx: HttpContext) => TContextData | Promise<TContextData>;
|
|
74
|
+
/**
|
|
75
|
+
* Options for bridging Fedify's LogTape logs into the AdonisJS logger.
|
|
76
|
+
*/
|
|
77
|
+
interface FedifyLoggingConfig {
|
|
78
|
+
/**
|
|
79
|
+
* The LogTape categories to route into the AdonisJS logger.
|
|
80
|
+
*
|
|
81
|
+
* Defaults to Fedify's own category plus LogTape's meta category, which is
|
|
82
|
+
* where LogTape reports its own configuration problems.
|
|
83
|
+
*
|
|
84
|
+
* @default `[['logtape', 'meta'], ['fedify']]`
|
|
85
|
+
*/
|
|
86
|
+
categories?: (string | string[])[];
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Everything {@link FedifyConfig} accepts except `contextDataFactory`, whose
|
|
90
|
+
* optionality is decided separately by {@link ContextDataFactoryOption}.
|
|
91
|
+
*
|
|
92
|
+
* It is Fedify's own {@link FederationOptions} — so every Fedify option is
|
|
93
|
+
* available and documented in Fedify's own reference — plus a handful of
|
|
94
|
+
* AdonisJS-specific keys, and with two differences:
|
|
95
|
+
*
|
|
96
|
+
* - `origin` is **required** (Fedify treats it as optional and derives the
|
|
97
|
+
* origin from the incoming request, which produces wrong actor URIs the
|
|
98
|
+
* moment the app sits behind a proxy or a tunnel), and
|
|
99
|
+
* - `kv` is **optional** (it falls back to an in-memory store in development).
|
|
100
|
+
*/
|
|
101
|
+
interface FedifyConfigBase extends Omit<FederationOptions<ContextData>, "kv" | "origin"> {
|
|
102
|
+
/**
|
|
103
|
+
* The canonical public origin of this server, for example
|
|
104
|
+
* `https://social.example.com`.
|
|
105
|
+
*
|
|
106
|
+
* Fedify mints actor URIs, activity IDs and collection URLs from this value,
|
|
107
|
+
* so it must be the address remote servers can reach — not the address the
|
|
108
|
+
* Node process happens to be listening on. Read it from an environment
|
|
109
|
+
* variable (the `configure` hook adds `PUBLIC_URL` for exactly this purpose).
|
|
110
|
+
*
|
|
111
|
+
* Pass a {@link FederationOrigin} object when the WebFinger handle domain
|
|
112
|
+
* differs from the web origin, for example handles `@me@example.com` served
|
|
113
|
+
* from `https://social.example.com`.
|
|
114
|
+
*/
|
|
115
|
+
origin: string | FederationOrigin;
|
|
116
|
+
/**
|
|
117
|
+
* The key–value store Fedify uses for caching remote documents, tracking
|
|
118
|
+
* inbox idempotency, and holding outbox state.
|
|
119
|
+
*
|
|
120
|
+
* Defaults to an in-memory store, which is fine for development and tests but
|
|
121
|
+
* loses all state on restart and cannot be shared between processes. A
|
|
122
|
+
* warning is logged if the default is used in production. Use a persistent
|
|
123
|
+
* implementation such as `@fedify/sqlite`, `@fedify/postgres`,
|
|
124
|
+
* `@fedify/redis`, or `@fedify/mysql` in production.
|
|
125
|
+
*/
|
|
126
|
+
kv?: KvStore;
|
|
127
|
+
/**
|
|
128
|
+
* URL path prefixes that Fedify should never see.
|
|
129
|
+
*
|
|
130
|
+
* Requests whose path starts with one of these prefixes skip the Fedify
|
|
131
|
+
* middleware entirely and go straight to the AdonisJS router. This is a pure
|
|
132
|
+
* performance escape hatch for hot paths that can never be federation
|
|
133
|
+
* endpoints — Vite's dev-server routes are the usual example.
|
|
134
|
+
*
|
|
135
|
+
* Accessing `ctx.federation` inside a request that matched one of these
|
|
136
|
+
* prefixes throws, because no Fedify context was created for it.
|
|
137
|
+
*
|
|
138
|
+
* @default `[]`
|
|
139
|
+
*/
|
|
140
|
+
ignoreRoutePrefixes?: string[];
|
|
141
|
+
/**
|
|
142
|
+
* Whether to pipe Fedify's LogTape logs into the AdonisJS logger.
|
|
143
|
+
*
|
|
144
|
+
* Fedify logs a great deal of useful diagnostic detail (signature
|
|
145
|
+
* verification, delivery attempts, WebFinger lookups) through LogTape.
|
|
146
|
+
* Without this bridge, none of it appears in the application's log output.
|
|
147
|
+
*
|
|
148
|
+
* Set to `false` if the application configures LogTape itself.
|
|
149
|
+
*
|
|
150
|
+
* @default `true`
|
|
151
|
+
*/
|
|
152
|
+
logging?: boolean | FedifyLoggingConfig;
|
|
153
|
+
/**
|
|
154
|
+
* Who drains Fedify's task queue, and with what context data.
|
|
155
|
+
*
|
|
156
|
+
* Required whenever {@link FederationOptions.queue} is set, because there is
|
|
157
|
+
* no safe default. Letting Fedify start the queue lazily — which is what it
|
|
158
|
+
* does out of the box — captures the context data of whichever HTTP request
|
|
159
|
+
* happened to enqueue the first task, and every background job for the rest
|
|
160
|
+
* of the process then runs with that one stale request's `HttpContext`.
|
|
161
|
+
*
|
|
162
|
+
* Pass a callback to have the **web process** drain the queue: the service
|
|
163
|
+
* provider starts the worker in the `ready` lifecycle hook and stops it on
|
|
164
|
+
* shutdown. There is no `HttpContext` behind a queued task, hence the
|
|
165
|
+
* callback takes no arguments.
|
|
166
|
+
*
|
|
167
|
+
* Pass `false` when a **separate worker process** owns the queue. Nothing is
|
|
168
|
+
* started in-process, and it is then that worker's job to call
|
|
169
|
+
* `federation.startQueue()`.
|
|
170
|
+
*
|
|
171
|
+
* Without a {@link FederationOptions.queue}, a callback is accepted with a
|
|
172
|
+
* warning — a task registered through `defineTask()` may bring its own
|
|
173
|
+
* queue, which this configuration cannot see — while `false` is accepted
|
|
174
|
+
* silently: it stays true when there is no queue at all, so a configuration
|
|
175
|
+
* that enables one in production only does not fail to boot in development.
|
|
176
|
+
*/
|
|
177
|
+
queueContextData?: (() => ContextData | Promise<ContextData>) | false;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* The `contextDataFactory` half of {@link FedifyConfig}, whose optionality
|
|
181
|
+
* depends on {@link ContextData}.
|
|
182
|
+
*
|
|
183
|
+
* The runtime default is `(ctx) => ctx`, which is only a correct
|
|
184
|
+
* {@link ContextData} while the application has not augmented
|
|
185
|
+
* {@link FedifyTypes} — after an augmentation an `HttpContext` is precisely
|
|
186
|
+
* what a dispatcher must *not* receive. Making the key required in that case
|
|
187
|
+
* turns "every dispatcher silently gets the wrong value" into a compile error
|
|
188
|
+
* on `config/fedify.ts`, which is the only place that can fix it.
|
|
189
|
+
*
|
|
190
|
+
* The tuple wrappers keep the conditional from distributing over a union
|
|
191
|
+
* {@link ContextData}, so an augmentation such as `User | null` is compared as
|
|
192
|
+
* a whole.
|
|
193
|
+
*/
|
|
194
|
+
type ContextDataFactoryOption = [HttpContext] extends [ContextData] ? {
|
|
195
|
+
/**
|
|
196
|
+
* Produces the {@link ContextData} for each incoming HTTP request.
|
|
197
|
+
*
|
|
198
|
+
* Defaults to `(ctx) => ctx`, i.e. the `HttpContext` itself, which matches
|
|
199
|
+
* the default of {@link ContextData}.
|
|
200
|
+
*/
|
|
201
|
+
contextDataFactory?: ContextDataFactory<ContextData>;
|
|
202
|
+
} : {
|
|
203
|
+
/**
|
|
204
|
+
* Produces the {@link ContextData} for each incoming HTTP request.
|
|
205
|
+
*
|
|
206
|
+
* Required here because the application augmented {@link FedifyTypes}:
|
|
207
|
+
* the built-in default hands dispatchers the `HttpContext`, which is no
|
|
208
|
+
* longer the declared context data type.
|
|
209
|
+
*/
|
|
210
|
+
contextDataFactory: ContextDataFactory<ContextData>;
|
|
211
|
+
};
|
|
212
|
+
/**
|
|
213
|
+
* The shape of the object passed to {@link defineConfig} in `config/fedify.ts`.
|
|
214
|
+
*
|
|
215
|
+
* {@link FedifyConfigBase} plus {@link ContextDataFactoryOption}, which makes
|
|
216
|
+
* `contextDataFactory` required exactly when {@link FedifyTypes} has been
|
|
217
|
+
* augmented.
|
|
218
|
+
*/
|
|
219
|
+
type FedifyConfig = FedifyConfigBase & ContextDataFactoryOption;
|
|
220
|
+
/**
|
|
221
|
+
* The fully-resolved configuration held by the `fedify.config` container
|
|
222
|
+
* binding: {@link FedifyConfig} with every optional AdonisJS-specific key
|
|
223
|
+
* filled in.
|
|
224
|
+
*/
|
|
225
|
+
interface ResolvedFedifyConfig extends FederationOptions<ContextData> {
|
|
226
|
+
contextDataFactory: ContextDataFactory<ContextData>;
|
|
227
|
+
ignoreRoutePrefixes: string[];
|
|
228
|
+
logging: boolean | FedifyLoggingConfig;
|
|
229
|
+
queueContextData?: (() => ContextData | Promise<ContextData>) | false;
|
|
230
|
+
}
|
|
231
|
+
declare module "@adonisjs/core/types" {
|
|
232
|
+
interface ContainerBindings {
|
|
233
|
+
/**
|
|
234
|
+
* The built {@link Federation} instance. Resolvable only once the
|
|
235
|
+
* application is ready — see the service provider for why.
|
|
236
|
+
*/
|
|
237
|
+
"fedify": Federation<ContextData>;
|
|
238
|
+
/**
|
|
239
|
+
* The {@link FederationBuilder} that dispatchers and inbox listeners are
|
|
240
|
+
* registered on, before the `Federation` instance is built.
|
|
241
|
+
*/
|
|
242
|
+
"fedify.builder": FederationBuilder<ContextData>;
|
|
243
|
+
/**
|
|
244
|
+
* The resolved contents of `config/fedify.ts`.
|
|
245
|
+
*/
|
|
246
|
+
"fedify.config": ResolvedFedifyConfig;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
declare module "@adonisjs/core/http" {
|
|
250
|
+
interface HttpContext {
|
|
251
|
+
/**
|
|
252
|
+
* The Fedify {@link RequestContext} for the current request.
|
|
253
|
+
*
|
|
254
|
+
* Use it to mint canonical URIs (`ctx.federation.getActorUri('me')`), look
|
|
255
|
+
* remote objects up (`ctx.federation.lookupObject('@user@example.com')`),
|
|
256
|
+
* or send activities (`ctx.federation.sendActivity(...)`) from ordinary
|
|
257
|
+
* AdonisJS controllers.
|
|
258
|
+
*
|
|
259
|
+
* The context is created lazily on first access, so requests that never
|
|
260
|
+
* touch it pay nothing. Accessing it throws if the request path matched
|
|
261
|
+
* {@link FedifyConfig.ignoreRoutePrefixes}, or if the Fedify server
|
|
262
|
+
* middleware is not registered in `start/kernel.ts`.
|
|
263
|
+
*/
|
|
264
|
+
federation: RequestContext<ContextData>;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
//#endregion
|
|
268
|
+
export { ContextData, ContextDataFactory, ContextDataFactoryOption, FedifyConfig, FedifyConfigBase, FedifyLoggingConfig, FedifyTypes, ResolvedFedifyConfig };
|
|
269
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
|
|
2
|
+
//#region src/unusable_authority.ts
|
|
3
|
+
/**
|
|
4
|
+
* The host used when the request carries no usable authority.
|
|
5
|
+
*
|
|
6
|
+
* `FederationOptions.origin` covers the URLs Fedify *mints* — actor URIs,
|
|
7
|
+
* activity IDs, collection URLs — so the placeholder never reaches a peer
|
|
8
|
+
* through them, and only the path has to survive for routing. It does not
|
|
9
|
+
* cover verification: RFC 9421's `@authority`, `@target-uri` and `@scheme`
|
|
10
|
+
* are derived from this URL, so a request signed over `@authority` fails to
|
|
11
|
+
* verify while the placeholder stands in. That is the cost of answering
|
|
12
|
+
* instead of failing, and why the fallback is logged.
|
|
13
|
+
*/
|
|
14
|
+
const PLACEHOLDER_HOST = "localhost";
|
|
15
|
+
/**
|
|
16
|
+
* Unusable authorities already warned about in the current window. Any
|
|
17
|
+
* client can trigger the warning through `Host`, so each distinct value is
|
|
18
|
+
* reported once and the set is capped, leaving neither it nor the log
|
|
19
|
+
* unbounded. The key is the whole value, since two hostile values can share
|
|
20
|
+
* a 128-character prefix.
|
|
21
|
+
*/
|
|
22
|
+
const warnedAuthorities = new Set();
|
|
23
|
+
/**
|
|
24
|
+
* How many distinct values one window reports.
|
|
25
|
+
*/
|
|
26
|
+
const WARNED_AUTHORITIES_LIMIT = 16;
|
|
27
|
+
/**
|
|
28
|
+
* How long the cap holds before the budget starts over.
|
|
29
|
+
*
|
|
30
|
+
* Without a window the cap is spent for the life of the process, and the
|
|
31
|
+
* values that spend it are the client's to choose: sixteen junk `Host`
|
|
32
|
+
* headers would silence the warning for good, so a proxy misconfigured an
|
|
33
|
+
* hour later—every request falling back to {@link PLACEHOLDER_HOST}, every
|
|
34
|
+
* signature over `@authority` failing to verify—would never be reported.
|
|
35
|
+
* Starting over bounds the log at {@link WARNED_AUTHORITIES_LIMIT} lines per
|
|
36
|
+
* window instead of over all time, which is what the log has to be protected
|
|
37
|
+
* from, and leaves a standing misconfiguration visible.
|
|
38
|
+
*/
|
|
39
|
+
const WARNED_AUTHORITIES_WINDOW_MS = 60 * 60 * 1e3;
|
|
40
|
+
/**
|
|
41
|
+
* When the current window began; `0` so the first warning opens one. It
|
|
42
|
+
* advances lazily, on the first warning after the window has elapsed, rather
|
|
43
|
+
* than from a timer that would hold the event loop open on a quiet process.
|
|
44
|
+
*/
|
|
45
|
+
let warnedAuthoritiesWindowStart = 0;
|
|
46
|
+
/**
|
|
47
|
+
* Whether the cap has already been reported in this window. Reaching it
|
|
48
|
+
* silences every later distinct value, which should not happen invisibly.
|
|
49
|
+
*/
|
|
50
|
+
let warnedAuthoritiesCapReported = false;
|
|
51
|
+
/**
|
|
52
|
+
* Reports an authority the request URL could not be built from, once per
|
|
53
|
+
* distinct value and window.
|
|
54
|
+
*/
|
|
55
|
+
function warnUnusableAuthority(logger, host) {
|
|
56
|
+
const now = Date.now();
|
|
57
|
+
if (now < warnedAuthoritiesWindowStart || now - warnedAuthoritiesWindowStart >= 36e5) {
|
|
58
|
+
warnedAuthoritiesWindowStart = now;
|
|
59
|
+
warnedAuthorities.clear();
|
|
60
|
+
warnedAuthoritiesCapReported = false;
|
|
61
|
+
}
|
|
62
|
+
if (warnedAuthorities.has(host)) return;
|
|
63
|
+
if (warnedAuthorities.size >= 16) {
|
|
64
|
+
if (warnedAuthoritiesCapReported) return;
|
|
65
|
+
warnedAuthoritiesCapReported = true;
|
|
66
|
+
logger.warn("Reached %d distinct unusable request authorities within %d minutes; " + "further distinct values are not reported until that window ends", 16, WARNED_AUTHORITIES_WINDOW_MS / 6e4);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
warnedAuthorities.add(host);
|
|
70
|
+
const points = Array.from(host);
|
|
71
|
+
const shown = points.length > 128 ? `${points.slice(0, 128).join("")}…` : host;
|
|
72
|
+
logger.warn("Unusable request authority \"%s\"; the URL Fedify sees falls back to " + "\"%s\", so signatures covering the authority will not verify. Behind a " + "proxy this is a misbehaving X-Forwarded-Host; otherwise a client sent " + "it directly. Each distinct value is reported once per %d minutes, up " + "to %d values in that window", shown, PLACEHOLDER_HOST, WARNED_AUTHORITIES_WINDOW_MS / 6e4, 16);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
//#endregion
|
|
76
|
+
exports.PLACEHOLDER_HOST = PLACEHOLDER_HOST;
|
|
77
|
+
exports.WARNED_AUTHORITIES_WINDOW_MS = WARNED_AUTHORITIES_WINDOW_MS;
|
|
78
|
+
exports.warnUnusableAuthority = warnUnusableAuthority;
|
|
79
|
+
//# sourceMappingURL=unusable_authority.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unusable_authority.cjs","names":[],"sources":["../../src/unusable_authority.ts"],"sourcesContent":["/**\n * The warning for a request whose authority cannot be used, and the budget\n * that keeps it from flooding the log.\n *\n * Internal to the package: this module is reachable through none of the\n * published subpaths. The two constants are exported for the tests, which\n * have to spend exactly this budget.\n *\n * @module\n */\nimport type { HttpContext } from \"@adonisjs/core/http\";\n\n/**\n * The host used when the request carries no usable authority.\n *\n * `FederationOptions.origin` covers the URLs Fedify *mints* — actor URIs,\n * activity IDs, collection URLs — so the placeholder never reaches a peer\n * through them, and only the path has to survive for routing. It does not\n * cover verification: RFC 9421's `@authority`, `@target-uri` and `@scheme`\n * are derived from this URL, so a request signed over `@authority` fails to\n * verify while the placeholder stands in. That is the cost of answering\n * instead of failing, and why the fallback is logged.\n */\nexport const PLACEHOLDER_HOST: string = \"localhost\";\n\n/**\n * Unusable authorities already warned about in the current window. Any\n * client can trigger the warning through `Host`, so each distinct value is\n * reported once and the set is capped, leaving neither it nor the log\n * unbounded. The key is the whole value, since two hostile values can share\n * a 128-character prefix.\n */\nconst warnedAuthorities = new Set<string>();\n\n/**\n * How many distinct values one window reports.\n */\nexport const WARNED_AUTHORITIES_LIMIT: number = 16;\n\n/**\n * How long the cap holds before the budget starts over.\n *\n * Without a window the cap is spent for the life of the process, and the\n * values that spend it are the client's to choose: sixteen junk `Host`\n * headers would silence the warning for good, so a proxy misconfigured an\n * hour later—every request falling back to {@link PLACEHOLDER_HOST}, every\n * signature over `@authority` failing to verify—would never be reported.\n * Starting over bounds the log at {@link WARNED_AUTHORITIES_LIMIT} lines per\n * window instead of over all time, which is what the log has to be protected\n * from, and leaves a standing misconfiguration visible.\n */\nexport const WARNED_AUTHORITIES_WINDOW_MS: number = 60 * 60 * 1000;\n\n/**\n * When the current window began; `0` so the first warning opens one. It\n * advances lazily, on the first warning after the window has elapsed, rather\n * than from a timer that would hold the event loop open on a quiet process.\n */\nlet warnedAuthoritiesWindowStart = 0;\n\n/**\n * Whether the cap has already been reported in this window. Reaching it\n * silences every later distinct value, which should not happen invisibly.\n */\nlet warnedAuthoritiesCapReported = false;\n\n/**\n * Reports an authority the request URL could not be built from, once per\n * distinct value and window.\n */\nexport function warnUnusableAuthority(\n logger: HttpContext[\"logger\"],\n host: string,\n): void {\n const now = Date.now();\n // `<` as well as the elapsed check: a backwards clock step (an NTP\n // correction) would otherwise freeze the window until real time caught up\n // with a start that lies in the future.\n if (\n now < warnedAuthoritiesWindowStart ||\n now - warnedAuthoritiesWindowStart >= WARNED_AUTHORITIES_WINDOW_MS\n ) {\n warnedAuthoritiesWindowStart = now;\n warnedAuthorities.clear();\n warnedAuthoritiesCapReported = false;\n }\n\n if (warnedAuthorities.has(host)) return;\n if (warnedAuthorities.size >= WARNED_AUTHORITIES_LIMIT) {\n if (warnedAuthoritiesCapReported) return;\n warnedAuthoritiesCapReported = true;\n logger.warn(\n \"Reached %d distinct unusable request authorities within %d minutes; \" +\n \"further distinct values are not reported until that window ends\",\n WARNED_AUTHORITIES_LIMIT,\n WARNED_AUTHORITIES_WINDOW_MS / 60_000,\n );\n return;\n }\n warnedAuthorities.add(host);\n // By code point, so an emoji straddling the boundary cannot leave a lone\n // surrogate in the key or the log line.\n const points = Array.from(host);\n const shown = points.length > 128\n ? `${points.slice(0, 128).join(\"\")}…`\n : host;\n logger.warn(\n 'Unusable request authority \"%s\"; the URL Fedify sees falls back to ' +\n '\"%s\", so signatures covering the authority will not verify. Behind a ' +\n \"proxy this is a misbehaving X-Forwarded-Host; otherwise a client sent \" +\n \"it directly. Each distinct value is reported once per %d minutes, up \" +\n \"to %d values in that window\",\n shown,\n PLACEHOLDER_HOST,\n WARNED_AUTHORITIES_WINDOW_MS / 60_000,\n WARNED_AUTHORITIES_LIMIT,\n );\n}\n"],"mappings":";;;;;;;;;;;;;AAuBA,MAAa,mBAA2B;;;;;;;;AASxC,MAAM,oBAAoB,IAAI,IAAY;;;;AAK1C,MAAa,2BAAmC;;;;;;;;;;;;;AAchD,MAAa,+BAAuC,KAAK,KAAK;;;;;;AAO9D,IAAI,+BAA+B;;;;;AAMnC,IAAI,+BAA+B;;;;;AAMnC,SAAgB,sBACd,QACA,MACM;CACN,MAAM,MAAM,KAAK,IAAI;CAIrB,IACE,MAAM,gCACN,MAAM,sCACN;EACA,+BAA+B;EAC/B,kBAAkB,MAAM;EACxB,+BAA+B;CACjC;CAEA,IAAI,kBAAkB,IAAI,IAAI,GAAG;CACjC,IAAI,kBAAkB,YAAkC;EACtD,IAAI,8BAA8B;EAClC,+BAA+B;EAC/B,OAAO,KACL,yEACE,uEAEF,+BAA+B,GACjC;EACA;CACF;CACA,kBAAkB,IAAI,IAAI;CAG1B,MAAM,SAAS,MAAM,KAAK,IAAI;CAC9B,MAAM,QAAQ,OAAO,SAAS,MAC1B,GAAG,OAAO,MAAM,GAAG,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,KACjC;CACJ,OAAO,KACL,0EACE,4EACA,2EACA,0EACA,+BACF,OACA,kBACA,+BAA+B,OAEjC;AACF"}
|