@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.
Files changed (82) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +420 -0
  3. package/dist/configure.cjs +108 -0
  4. package/dist/configure.cjs.map +1 -0
  5. package/dist/configure.d.cts +14 -0
  6. package/dist/configure.d.ts +14 -0
  7. package/dist/configure.js +107 -0
  8. package/dist/configure.js.map +1 -0
  9. package/dist/index.cjs +19 -0
  10. package/dist/index.d.cts +8 -0
  11. package/dist/index.d.ts +8 -0
  12. package/dist/index.js +8 -0
  13. package/dist/package.cjs +121 -0
  14. package/dist/package.cjs.map +1 -0
  15. package/dist/package.js +115 -0
  16. package/dist/package.js.map +1 -0
  17. package/dist/providers/fedify_provider.cjs +270 -0
  18. package/dist/providers/fedify_provider.cjs.map +1 -0
  19. package/dist/providers/fedify_provider.d.cts +54 -0
  20. package/dist/providers/fedify_provider.d.ts +55 -0
  21. package/dist/providers/fedify_provider.js +269 -0
  22. package/dist/providers/fedify_provider.js.map +1 -0
  23. package/dist/services/builder.cjs +9 -0
  24. package/dist/services/builder.cjs.map +1 -0
  25. package/dist/services/builder.d.cts +7 -0
  26. package/dist/services/builder.d.ts +8 -0
  27. package/dist/services/builder.js +8 -0
  28. package/dist/services/builder.js.map +1 -0
  29. package/dist/src/builder.cjs +55 -0
  30. package/dist/src/builder.cjs.map +1 -0
  31. package/dist/src/builder.d.cts +14 -0
  32. package/dist/src/builder.d.ts +14 -0
  33. package/dist/src/builder.js +54 -0
  34. package/dist/src/builder.js.map +1 -0
  35. package/dist/src/define_config.cjs +114 -0
  36. package/dist/src/define_config.cjs.map +1 -0
  37. package/dist/src/define_config.d.cts +30 -0
  38. package/dist/src/define_config.d.ts +30 -0
  39. package/dist/src/define_config.js +113 -0
  40. package/dist/src/define_config.js.map +1 -0
  41. package/dist/src/errors.cjs +77 -0
  42. package/dist/src/errors.cjs.map +1 -0
  43. package/dist/src/errors.d.cts +56 -0
  44. package/dist/src/errors.d.ts +56 -0
  45. package/dist/src/errors.js +73 -0
  46. package/dist/src/errors.js.map +1 -0
  47. package/dist/src/fedify_middleware.cjs +507 -0
  48. package/dist/src/fedify_middleware.cjs.map +1 -0
  49. package/dist/src/fedify_middleware.d.cts +85 -0
  50. package/dist/src/fedify_middleware.d.ts +85 -0
  51. package/dist/src/fedify_middleware.js +505 -0
  52. package/dist/src/fedify_middleware.js.map +1 -0
  53. package/dist/src/http_context.cjs +79 -0
  54. package/dist/src/http_context.cjs.map +1 -0
  55. package/dist/src/http_context.d.cts +31 -0
  56. package/dist/src/http_context.d.ts +31 -0
  57. package/dist/src/http_context.js +76 -0
  58. package/dist/src/http_context.js.map +1 -0
  59. package/dist/src/logging.cjs +106 -0
  60. package/dist/src/logging.cjs.map +1 -0
  61. package/dist/src/logging.d.cts +45 -0
  62. package/dist/src/logging.d.ts +45 -0
  63. package/dist/src/logging.js +106 -0
  64. package/dist/src/logging.js.map +1 -0
  65. package/dist/src/types.cjs +1 -0
  66. package/dist/src/types.d.cts +269 -0
  67. package/dist/src/types.d.ts +269 -0
  68. package/dist/src/types.js +1 -0
  69. package/dist/src/unusable_authority.cjs +79 -0
  70. package/dist/src/unusable_authority.cjs.map +1 -0
  71. package/dist/src/unusable_authority.js +76 -0
  72. package/dist/src/unusable_authority.js.map +1 -0
  73. package/dist/stubs/config/fedify.stub +105 -0
  74. package/dist/stubs/federation/main.stub +64 -0
  75. package/dist/stubs/main.cjs +16 -0
  76. package/dist/stubs/main.cjs.map +1 -0
  77. package/dist/stubs/main.d.cts +11 -0
  78. package/dist/stubs/main.d.ts +11 -0
  79. package/dist/stubs/main.js +15 -0
  80. package/dist/stubs/main.js.map +1 -0
  81. package/dist/stubs/start/federation.stub +20 -0
  82. 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"}