@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,270 @@
|
|
|
1
|
+
Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: 'Module' } });
|
|
2
|
+
const require_src_errors = require('../src/errors.cjs');
|
|
3
|
+
const require_src_http_context = require('../src/http_context.cjs');
|
|
4
|
+
const require_src_fedify_middleware = require('../src/fedify_middleware.cjs');
|
|
5
|
+
const require_logging = require('../src/logging.cjs');
|
|
6
|
+
const require_src_builder = require('../src/builder.cjs');
|
|
7
|
+
let _adonisjs_core = require("@adonisjs/core");
|
|
8
|
+
let _logtape_logtape = require("@logtape/logtape");
|
|
9
|
+
|
|
10
|
+
//#region providers/fedify_provider.ts
|
|
11
|
+
/**
|
|
12
|
+
* The `@fedify/adonisjs` service provider; see {@link FedifyProvider}.
|
|
13
|
+
*
|
|
14
|
+
* @module
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* How long a request that arrives before `ready()` waits for the federation.
|
|
18
|
+
*
|
|
19
|
+
* Generous, since it only needs to outlast earlier providers' `ready()` hooks.
|
|
20
|
+
* It turns a `ready()` that never comes (an earlier provider waiting on this
|
|
21
|
+
* server) into an error instead of a request that never completes.
|
|
22
|
+
*/
|
|
23
|
+
const READY_TIMEOUT_MS = 3e4;
|
|
24
|
+
/**
|
|
25
|
+
* Memoises an asynchronous factory, caching successes only.
|
|
26
|
+
*
|
|
27
|
+
* Used for the container bindings instead of `container.singleton()`, which
|
|
28
|
+
* also caches *rejections*: one early, swallowed `make('fedify')` would pin
|
|
29
|
+
* `E_FEDERATION_NOT_READY` for the life of the process. A failed attempt is
|
|
30
|
+
* forgotten instead, so the next caller runs the factory again. `forget()`
|
|
31
|
+
* drops a cached success once the caller knows it is stale.
|
|
32
|
+
*/
|
|
33
|
+
function memoizeSuccess(factory) {
|
|
34
|
+
let cached;
|
|
35
|
+
const memo = () => {
|
|
36
|
+
cached ??= factory().catch((error) => {
|
|
37
|
+
cached = undefined;
|
|
38
|
+
throw error;
|
|
39
|
+
});
|
|
40
|
+
return cached;
|
|
41
|
+
};
|
|
42
|
+
return Object.assign(memo, { forget: () => {
|
|
43
|
+
cached = undefined;
|
|
44
|
+
} });
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The service provider registered from `adonisrc.ts` as
|
|
48
|
+
* `@fedify/adonisjs/fedify_provider`.
|
|
49
|
+
*
|
|
50
|
+
* Binds `fedify.builder`, `fedify.config`, `fedify` and
|
|
51
|
+
* {@link FedifyMiddleware}, installs `ctx.federation`, bridges Fedify's logs
|
|
52
|
+
* into the AdonisJS logger, builds the federation after the preload files
|
|
53
|
+
* have run, and drains the task queue in the web process.
|
|
54
|
+
*/
|
|
55
|
+
var FedifyProvider = class {
|
|
56
|
+
/**
|
|
57
|
+
* Aborts the queue worker on shutdown.
|
|
58
|
+
*/
|
|
59
|
+
#queueController = new AbortController();
|
|
60
|
+
/**
|
|
61
|
+
* The `startQueue()` promise, settled once every listener has stopped.
|
|
62
|
+
* `shutdown()` returns it so AdonisJS waits for the workers. Undefined when
|
|
63
|
+
* this process does not drain the queue.
|
|
64
|
+
*/
|
|
65
|
+
#queue;
|
|
66
|
+
/**
|
|
67
|
+
* Whether this provider configured LogTape and must undo it on shutdown.
|
|
68
|
+
*
|
|
69
|
+
* LogTape's configuration is process-global and its sink closes over *this*
|
|
70
|
+
* application's `Logger`. Left in place, a second application in the same
|
|
71
|
+
* process (the test runner, a dev-server restart) would find LogTape already
|
|
72
|
+
* configured and keep logging into the terminated application's logger.
|
|
73
|
+
*/
|
|
74
|
+
#loggingConfigured = false;
|
|
75
|
+
/**
|
|
76
|
+
* Whether the builder may be sealed into a `Federation` object.
|
|
77
|
+
*
|
|
78
|
+
* Dispatchers are registered in preload files, which AdonisJS imports *after*
|
|
79
|
+
* every provider's `boot()` and `start()`. Building earlier would yield a
|
|
80
|
+
* federation with no routes and 404s as the only symptom, so an early build
|
|
81
|
+
* fails with an explanatory error instead.
|
|
82
|
+
*/
|
|
83
|
+
#buildable = false;
|
|
84
|
+
/**
|
|
85
|
+
* Whether the HTTP server has started accepting connections.
|
|
86
|
+
*
|
|
87
|
+
* AdonisJS listens inside `start()` and runs `ready()` hooks afterwards, so a
|
|
88
|
+
* request (a health probe, `fedify tunnel`) can arrive before this provider
|
|
89
|
+
* is ready. Such a request is the one caller allowed to *wait* for the
|
|
90
|
+
* federation, since `ready()` is on its way; any other caller (a `start()`
|
|
91
|
+
* hook, a preload file, an Ace command) fails fast because nothing would
|
|
92
|
+
* make it ready while they wait.
|
|
93
|
+
*/
|
|
94
|
+
#listening = false;
|
|
95
|
+
/**
|
|
96
|
+
* Settles once `ready()` has built the federation: resolved on success,
|
|
97
|
+
* rejected with the configuration error, or with `E_FEDERATION_NOT_READY`
|
|
98
|
+
* when the application shuts down first or {@link READY_TIMEOUT_MS} passes.
|
|
99
|
+
*/
|
|
100
|
+
#ready = Promise.withResolvers();
|
|
101
|
+
#readyTimer;
|
|
102
|
+
/**
|
|
103
|
+
* The memoised middleware binding, kept here so that a failed `ready()` can
|
|
104
|
+
* forget it.
|
|
105
|
+
*
|
|
106
|
+
* A request that arrives while `ready()` is running builds its middleware
|
|
107
|
+
* from a federation that is buildable at that moment. Should `ready()` fail
|
|
108
|
+
* afterwards, that middleware would otherwise stay cached and keep serving
|
|
109
|
+
* federation traffic in an application whose `make('fedify')` fails fast and
|
|
110
|
+
* whose queue never started.
|
|
111
|
+
*/
|
|
112
|
+
#middleware;
|
|
113
|
+
app;
|
|
114
|
+
/**
|
|
115
|
+
* Longhand rather than a parameter property, which Node's type stripping
|
|
116
|
+
* does not support; the tests run these sources through it.
|
|
117
|
+
*/
|
|
118
|
+
constructor(app) {
|
|
119
|
+
this.app = app;
|
|
120
|
+
this.#ready.promise.catch(() => {});
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Registers the container bindings.
|
|
124
|
+
*/
|
|
125
|
+
register() {
|
|
126
|
+
/**
|
|
127
|
+
* Bound as a value: the builder is a module-level singleton that
|
|
128
|
+
* application code also imports directly, and both routes must reach the
|
|
129
|
+
* same object.
|
|
130
|
+
*/
|
|
131
|
+
this.app.container.bindValue("fedify.builder", require_src_builder.builder);
|
|
132
|
+
this.app.container.bind("fedify.config", memoizeSuccess(async () => {
|
|
133
|
+
const provider = this.app.config.get("fedify");
|
|
134
|
+
const config = await _adonisjs_core.configProvider.resolve(this.app, provider);
|
|
135
|
+
if (!config) {
|
|
136
|
+
throw new require_src_errors.E_INVALID_FEDIFY_CONFIG();
|
|
137
|
+
}
|
|
138
|
+
return config;
|
|
139
|
+
}));
|
|
140
|
+
const buildFederation = memoizeSuccess(async () => {
|
|
141
|
+
const config = await this.app.container.make("fedify.config");
|
|
142
|
+
const federationBuilder = await this.app.container.make("fedify.builder");
|
|
143
|
+
return federationBuilder.build(config);
|
|
144
|
+
});
|
|
145
|
+
this.app.container.bind("fedify", () => {
|
|
146
|
+
if (!this.#buildable) {
|
|
147
|
+
throw new require_src_errors.E_FEDERATION_NOT_READY();
|
|
148
|
+
}
|
|
149
|
+
return buildFederation();
|
|
150
|
+
});
|
|
151
|
+
/**
|
|
152
|
+
* The middleware, built with the federation and configuration in scope, as
|
|
153
|
+
* `@adonisjs/cors` and `@adonisjs/session` do. `server.use()` resolves
|
|
154
|
+
* the class through the container on every request, which lands here.
|
|
155
|
+
*
|
|
156
|
+
* A plain `bind()` plus {@link memoizeSuccess} rather than `singleton()`
|
|
157
|
+
* (see the helper), so every request shares one instance.
|
|
158
|
+
*
|
|
159
|
+
* A request that arrives after the server listens but before `ready()` has
|
|
160
|
+
* built the federation waits for it (see {@link #listening}); any other
|
|
161
|
+
* caller gets `E_FEDERATION_NOT_READY` at once.
|
|
162
|
+
*/
|
|
163
|
+
this.#middleware = memoizeSuccess(async () => {
|
|
164
|
+
if (!this.#buildable) {
|
|
165
|
+
if (!this.#listening) throw new require_src_errors.E_FEDERATION_NOT_READY();
|
|
166
|
+
await this.#ready.promise;
|
|
167
|
+
}
|
|
168
|
+
const federation = await this.app.container.make("fedify");
|
|
169
|
+
const config = await this.app.container.make("fedify.config");
|
|
170
|
+
return new require_src_fedify_middleware.default(federation, config.contextDataFactory, { ignoreRoutePrefixes: config.ignoreRoutePrefixes });
|
|
171
|
+
});
|
|
172
|
+
this.app.container.bind(require_src_fedify_middleware.default, this.#middleware);
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Installs `ctx.federation`, bridges Fedify's logs into the AdonisJS logger,
|
|
176
|
+
* and subscribes to the HTTP server's ready event.
|
|
177
|
+
*/
|
|
178
|
+
async boot() {
|
|
179
|
+
require_src_http_context.installFederationGetter();
|
|
180
|
+
const config = await this.app.container.make("fedify.config");
|
|
181
|
+
if (config.logging !== false) {
|
|
182
|
+
const logger = await this.app.container.make("logger");
|
|
183
|
+
this.#loggingConfigured = await require_logging.configureFedifyLogging({
|
|
184
|
+
logger,
|
|
185
|
+
categories: config.logging === true ? undefined : config.logging.categories
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
if (this.app.container.hasBinding("emitter")) {
|
|
189
|
+
const emitter = await this.app.container.make("emitter");
|
|
190
|
+
emitter.on("http:server_ready", () => this.#serverStarted());
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Runs after the preload files have registered every dispatcher.
|
|
195
|
+
*/
|
|
196
|
+
async ready() {
|
|
197
|
+
this.#buildable = true;
|
|
198
|
+
let federation;
|
|
199
|
+
let worker;
|
|
200
|
+
try {
|
|
201
|
+
federation = await this.app.container.make("fedify");
|
|
202
|
+
const config = await this.app.container.make("fedify.config");
|
|
203
|
+
if (typeof config.queueContextData === "function" && this.app.getEnvironment() === "web") {
|
|
204
|
+
worker = {
|
|
205
|
+
contextData: await config.queueContextData(),
|
|
206
|
+
logger: await this.app.container.make("logger")
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
} catch (error) {
|
|
210
|
+
this.#buildable = false;
|
|
211
|
+
this.#middleware?.forget();
|
|
212
|
+
this.#settle(error);
|
|
213
|
+
throw error;
|
|
214
|
+
}
|
|
215
|
+
this.#settle();
|
|
216
|
+
if (worker === undefined) return;
|
|
217
|
+
if (this.#queueController.signal.aborted) return;
|
|
218
|
+
const { contextData, logger } = worker;
|
|
219
|
+
this.#queue = federation.startQueue(contextData, { signal: this.#queueController.signal }).catch((error) => {
|
|
220
|
+
if (this.#queueController.signal.aborted) return;
|
|
221
|
+
logger.error({ err: error }, "The Fedify task queue stopped unexpectedly");
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Stops the queue worker and waits for it, releases any request still
|
|
226
|
+
* waiting on `ready()`, and takes the LogTape configuration down.
|
|
227
|
+
*
|
|
228
|
+
* Aborting only *asks* the listeners to stop; `startQueue()` settles once
|
|
229
|
+
* they have, possibly one in-flight delivery later. Returning that promise
|
|
230
|
+
* keeps the rest of the shutdown sequence (closing the database, most
|
|
231
|
+
* importantly) behind it.
|
|
232
|
+
*
|
|
233
|
+
* LogTape is reset after the queue stops, so an outgoing delivery can still
|
|
234
|
+
* log, and only if this provider configured it (see
|
|
235
|
+
* {@link #loggingConfigured}); resetting the application's own
|
|
236
|
+
* configuration would silence its logs.
|
|
237
|
+
*
|
|
238
|
+
* The return type stays a promise even with no queue to wait for: AdonisJS
|
|
239
|
+
* warns when a provider hook returns a non-thenable.
|
|
240
|
+
*/
|
|
241
|
+
shutdown() {
|
|
242
|
+
this.#settle(new require_src_errors.E_FEDERATION_NOT_READY());
|
|
243
|
+
this.#queueController.abort();
|
|
244
|
+
const stopped = this.#queue ?? Promise.resolve();
|
|
245
|
+
if (!this.#loggingConfigured) return stopped;
|
|
246
|
+
this.#loggingConfigured = false;
|
|
247
|
+
return stopped.then(() => (0, _logtape_logtape.reset)().catch(() => {}));
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Called when the HTTP server starts listening and requests can arrive.
|
|
251
|
+
*/
|
|
252
|
+
#serverStarted() {
|
|
253
|
+
this.#listening = true;
|
|
254
|
+
if (this.#buildable) return;
|
|
255
|
+
this.#readyTimer = setTimeout(() => this.#settle(new require_src_errors.E_FEDERATION_NOT_READY()), READY_TIMEOUT_MS);
|
|
256
|
+
this.#readyTimer.unref();
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Settles the `ready()` promise once; later calls are no-ops.
|
|
260
|
+
*/
|
|
261
|
+
#settle(error) {
|
|
262
|
+
clearTimeout(this.#readyTimer);
|
|
263
|
+
if (error === undefined) this.#ready.resolve();
|
|
264
|
+
else this.#ready.reject(error);
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
//#endregion
|
|
269
|
+
exports.default = FedifyProvider;
|
|
270
|
+
//# sourceMappingURL=fedify_provider.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fedify_provider.cjs","names":["#ready","builder","configProvider","E_INVALID_FEDIFY_CONFIG","#buildable","E_FEDERATION_NOT_READY","#middleware","#listening","FedifyMiddleware","#loggingConfigured","configureFedifyLogging","#serverStarted","#settle","#queueController","#queue","#readyTimer"],"sources":["../../providers/fedify_provider.ts"],"sourcesContent":["/**\n * The `@fedify/adonisjs` service provider; see {@link FedifyProvider}.\n *\n * @module\n */\nimport { configProvider } from \"@adonisjs/core\";\nimport type { Logger } from \"@adonisjs/core/logger\";\nimport type { ApplicationService, ConfigProvider } from \"@adonisjs/core/types\";\nimport type { Federation } from \"@fedify/fedify\";\nimport { reset as resetLogging } from \"@logtape/logtape\";\n\nimport { builder } from \"../src/builder.ts\";\nimport {\n E_FEDERATION_NOT_READY,\n E_INVALID_FEDIFY_CONFIG,\n} from \"../src/errors.ts\";\nimport FedifyMiddleware from \"../src/fedify_middleware.ts\";\nimport { installFederationGetter } from \"../src/http_context.ts\";\nimport { configureFedifyLogging } from \"../src/logging.ts\";\nimport type { ContextData, ResolvedFedifyConfig } from \"../src/types.ts\";\n\n/**\n * How long a request that arrives before `ready()` waits for the federation.\n *\n * Generous, since it only needs to outlast earlier providers' `ready()` hooks.\n * It turns a `ready()` that never comes (an earlier provider waiting on this\n * server) into an error instead of a request that never completes.\n */\nconst READY_TIMEOUT_MS = 30_000;\n\n/**\n * A memoised factory: the value, plus a way to drop what was cached.\n */\ntype Memoized<T> = (() => Promise<T>) & { forget(): void };\n\n/**\n * Memoises an asynchronous factory, caching successes only.\n *\n * Used for the container bindings instead of `container.singleton()`, which\n * also caches *rejections*: one early, swallowed `make('fedify')` would pin\n * `E_FEDERATION_NOT_READY` for the life of the process. A failed attempt is\n * forgotten instead, so the next caller runs the factory again. `forget()`\n * drops a cached success once the caller knows it is stale.\n */\nfunction memoizeSuccess<T>(factory: () => Promise<T>): Memoized<T> {\n let cached: Promise<T> | undefined;\n\n const memo = (): Promise<T> => {\n cached ??= factory().catch((error: unknown) => {\n cached = undefined;\n throw error;\n });\n\n return cached;\n };\n\n return Object.assign(memo, {\n forget: (): void => {\n cached = undefined;\n },\n });\n}\n\n/**\n * The service provider registered from `adonisrc.ts` as\n * `@fedify/adonisjs/fedify_provider`.\n *\n * Binds `fedify.builder`, `fedify.config`, `fedify` and\n * {@link FedifyMiddleware}, installs `ctx.federation`, bridges Fedify's logs\n * into the AdonisJS logger, builds the federation after the preload files\n * have run, and drains the task queue in the web process.\n */\nexport default class FedifyProvider {\n /**\n * Aborts the queue worker on shutdown.\n */\n #queueController = new AbortController();\n\n /**\n * The `startQueue()` promise, settled once every listener has stopped.\n * `shutdown()` returns it so AdonisJS waits for the workers. Undefined when\n * this process does not drain the queue.\n */\n #queue?: Promise<void>;\n\n /**\n * Whether this provider configured LogTape and must undo it on shutdown.\n *\n * LogTape's configuration is process-global and its sink closes over *this*\n * application's `Logger`. Left in place, a second application in the same\n * process (the test runner, a dev-server restart) would find LogTape already\n * configured and keep logging into the terminated application's logger.\n */\n #loggingConfigured = false;\n\n /**\n * Whether the builder may be sealed into a `Federation` object.\n *\n * Dispatchers are registered in preload files, which AdonisJS imports *after*\n * every provider's `boot()` and `start()`. Building earlier would yield a\n * federation with no routes and 404s as the only symptom, so an early build\n * fails with an explanatory error instead.\n */\n #buildable = false;\n\n /**\n * Whether the HTTP server has started accepting connections.\n *\n * AdonisJS listens inside `start()` and runs `ready()` hooks afterwards, so a\n * request (a health probe, `fedify tunnel`) can arrive before this provider\n * is ready. Such a request is the one caller allowed to *wait* for the\n * federation, since `ready()` is on its way; any other caller (a `start()`\n * hook, a preload file, an Ace command) fails fast because nothing would\n * make it ready while they wait.\n */\n #listening = false;\n\n /**\n * Settles once `ready()` has built the federation: resolved on success,\n * rejected with the configuration error, or with `E_FEDERATION_NOT_READY`\n * when the application shuts down first or {@link READY_TIMEOUT_MS} passes.\n */\n #ready = Promise.withResolvers<void>();\n #readyTimer?: ReturnType<typeof setTimeout>;\n\n /**\n * The memoised middleware binding, kept here so that a failed `ready()` can\n * forget it.\n *\n * A request that arrives while `ready()` is running builds its middleware\n * from a federation that is buildable at that moment. Should `ready()` fail\n * afterwards, that middleware would otherwise stay cached and keep serving\n * federation traffic in an application whose `make('fedify')` fails fast and\n * whose queue never started.\n */\n #middleware?: Memoized<FedifyMiddleware<ContextData>>;\n\n protected app: ApplicationService;\n\n /**\n * Longhand rather than a parameter property, which Node's type stripping\n * does not support; the tests run these sources through it.\n */\n constructor(app: ApplicationService) {\n this.app = app;\n\n // Only requests waiting on `ready()` observe the rejection; without this\n // handler Node would report it as unhandled when nobody was.\n this.#ready.promise.catch(() => {});\n }\n\n /**\n * Registers the container bindings.\n */\n register(): void {\n /**\n * Bound as a value: the builder is a module-level singleton that\n * application code also imports directly, and both routes must reach the\n * same object.\n */\n this.app.container.bindValue(\"fedify.builder\", builder);\n\n this.app.container.bind(\n \"fedify.config\",\n memoizeSuccess(async () => {\n const provider = this.app.config.get<\n ConfigProvider<ResolvedFedifyConfig>\n >(\"fedify\");\n const config = await configProvider.resolve<ResolvedFedifyConfig>(\n this.app,\n provider,\n );\n\n if (!config) {\n throw new E_INVALID_FEDIFY_CONFIG();\n }\n\n return config;\n }),\n );\n\n const buildFederation = memoizeSuccess(async () => {\n const config = await this.app.container.make(\"fedify.config\");\n const federationBuilder = await this.app.container.make(\"fedify.builder\");\n\n return federationBuilder.build(config);\n });\n\n this.app.container.bind(\"fedify\", () => {\n if (!this.#buildable) {\n throw new E_FEDERATION_NOT_READY();\n }\n\n return buildFederation();\n });\n\n /**\n * The middleware, built with the federation and configuration in scope, as\n * `@adonisjs/cors` and `@adonisjs/session` do. `server.use()` resolves\n * the class through the container on every request, which lands here.\n *\n * A plain `bind()` plus {@link memoizeSuccess} rather than `singleton()`\n * (see the helper), so every request shares one instance.\n *\n * A request that arrives after the server listens but before `ready()` has\n * built the federation waits for it (see {@link #listening}); any other\n * caller gets `E_FEDERATION_NOT_READY` at once.\n */\n this.#middleware = memoizeSuccess(async () => {\n if (!this.#buildable) {\n if (!this.#listening) throw new E_FEDERATION_NOT_READY();\n await this.#ready.promise;\n }\n\n const federation = await this.app.container.make(\"fedify\");\n const config = await this.app.container.make(\"fedify.config\");\n\n return new FedifyMiddleware<ContextData>(\n federation,\n config.contextDataFactory,\n { ignoreRoutePrefixes: config.ignoreRoutePrefixes },\n );\n });\n\n this.app.container.bind(FedifyMiddleware, this.#middleware);\n }\n\n /**\n * Installs `ctx.federation`, bridges Fedify's logs into the AdonisJS logger,\n * and subscribes to the HTTP server's ready event.\n */\n async boot(): Promise<void> {\n // `ctx.federation` as a Macroable getter on the `HttpContext` prototype.\n // The middleware also installs it on each context's class (covering\n // another `@adonisjs/core` copy); this covers requests the middleware\n // never saw, which then get `E_FEDIFY_CONTEXT_UNAVAILABLE` rather than\n // `undefined`.\n installFederationGetter();\n\n const config = await this.app.container.make(\"fedify.config\");\n\n if (config.logging !== false) {\n const logger = await this.app.container.make(\"logger\");\n this.#loggingConfigured = await configureFedifyLogging({\n logger,\n categories: config.logging === true\n ? undefined\n : config.logging.categories,\n });\n }\n\n // The core `app_provider` binds the emitter; a bare application in a test\n // may lack it, and then has no HTTP server to hear from either.\n if (this.app.container.hasBinding(\"emitter\")) {\n const emitter = await this.app.container.make(\"emitter\");\n emitter.on(\"http:server_ready\", () => this.#serverStarted());\n }\n }\n\n /**\n * Runs after the preload files have registered every dispatcher.\n */\n async ready(): Promise<void> {\n this.#buildable = true;\n\n // Build eagerly so a broken federation surfaces at startup rather than on\n // the first federation request. Requests waiting in the middleware\n // binding get the same outcome.\n let federation: Federation<ContextData>;\n let worker: { contextData: ContextData; logger: Logger } | undefined;\n try {\n federation = await this.app.container.make(\"fedify\");\n const config = await this.app.container.make(\"fedify.config\");\n\n // Only the web process drains the queue: Ace commands, the REPL and the\n // test runner also reach `ready()`, where a worker would outlive the\n // command and compete for the queue. The context data is built before\n // the waiting requests are released, so a throwing callback fails\n // startup like a broken federation instead of rejecting `ready()` with\n // requests already being served.\n if (\n typeof config.queueContextData === \"function\" &&\n this.app.getEnvironment() === \"web\"\n ) {\n worker = {\n contextData: await config.queueContextData(),\n logger: await this.app.container.make(\"logger\"),\n };\n }\n } catch (error: unknown) {\n // Back to failing fast: a surviving process (an Ace command, the REPL)\n // must not have later `make('fedify')` calls re-run the configuration,\n // nor keep the middleware a request built while this hook was still\n // running (see {@link #middleware}).\n this.#buildable = false;\n this.#middleware?.forget();\n this.#settle(error);\n throw error;\n }\n this.#settle();\n\n if (worker === undefined) return;\n\n // `shutdown()` can have run while this hook was awaiting above: it settles\n // and aborts, but finds no queue promise to wait for. Starting a worker on\n // the aborted signal now would leave one running that nothing awaits.\n if (this.#queueController.signal.aborted) return;\n\n // `startQueue` settles only when the queue stops, so it must not be\n // awaited here. The promise is kept for `shutdown()`, `catch` included,\n // so awaiting it there never rejects.\n const { contextData, logger } = worker;\n this.#queue = federation\n .startQueue(contextData, { signal: this.#queueController.signal })\n .catch((error: unknown) => {\n if (this.#queueController.signal.aborted) return;\n logger.error(\n { err: error },\n \"The Fedify task queue stopped unexpectedly\",\n );\n });\n }\n\n /**\n * Stops the queue worker and waits for it, releases any request still\n * waiting on `ready()`, and takes the LogTape configuration down.\n *\n * Aborting only *asks* the listeners to stop; `startQueue()` settles once\n * they have, possibly one in-flight delivery later. Returning that promise\n * keeps the rest of the shutdown sequence (closing the database, most\n * importantly) behind it.\n *\n * LogTape is reset after the queue stops, so an outgoing delivery can still\n * log, and only if this provider configured it (see\n * {@link #loggingConfigured}); resetting the application's own\n * configuration would silence its logs.\n *\n * The return type stays a promise even with no queue to wait for: AdonisJS\n * warns when a provider hook returns a non-thenable.\n */\n shutdown(): Promise<void> {\n this.#settle(new E_FEDERATION_NOT_READY());\n this.#queueController.abort();\n\n const stopped = this.#queue ?? Promise.resolve();\n if (!this.#loggingConfigured) return stopped;\n\n this.#loggingConfigured = false;\n // A failing reset must not fail the shutdown queued behind it. The queue\n // promise never rejects, so only the reset is guarded.\n return stopped.then(() => resetLogging().catch(() => {}));\n }\n\n /**\n * Called when the HTTP server starts listening and requests can arrive.\n */\n #serverStarted(): void {\n this.#listening = true;\n if (this.#buildable) return;\n\n this.#readyTimer = setTimeout(\n () => this.#settle(new E_FEDERATION_NOT_READY()),\n READY_TIMEOUT_MS,\n );\n // A pending timer must not keep the process alive.\n this.#readyTimer.unref();\n }\n\n /**\n * Settles the `ready()` promise once; later calls are no-ops.\n */\n #settle(error?: unknown): void {\n clearTimeout(this.#readyTimer);\n if (error === undefined) this.#ready.resolve();\n else this.#ready.reject(error);\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { ApplicationService } from "@adonisjs/core/types";
|
|
2
|
+
|
|
3
|
+
//#region providers/fedify_provider.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* The service provider registered from `adonisrc.ts` as
|
|
6
|
+
* `@fedify/adonisjs/fedify_provider`.
|
|
7
|
+
*
|
|
8
|
+
* Binds `fedify.builder`, `fedify.config`, `fedify` and
|
|
9
|
+
* {@link FedifyMiddleware}, installs `ctx.federation`, bridges Fedify's logs
|
|
10
|
+
* into the AdonisJS logger, builds the federation after the preload files
|
|
11
|
+
* have run, and drains the task queue in the web process.
|
|
12
|
+
*/
|
|
13
|
+
declare class FedifyProvider {
|
|
14
|
+
#private;
|
|
15
|
+
protected app: ApplicationService;
|
|
16
|
+
/**
|
|
17
|
+
* Longhand rather than a parameter property, which Node's type stripping
|
|
18
|
+
* does not support; the tests run these sources through it.
|
|
19
|
+
*/
|
|
20
|
+
constructor(app: ApplicationService);
|
|
21
|
+
/**
|
|
22
|
+
* Registers the container bindings.
|
|
23
|
+
*/
|
|
24
|
+
register(): void;
|
|
25
|
+
/**
|
|
26
|
+
* Installs `ctx.federation`, bridges Fedify's logs into the AdonisJS logger,
|
|
27
|
+
* and subscribes to the HTTP server's ready event.
|
|
28
|
+
*/
|
|
29
|
+
boot(): Promise<void>;
|
|
30
|
+
/**
|
|
31
|
+
* Runs after the preload files have registered every dispatcher.
|
|
32
|
+
*/
|
|
33
|
+
ready(): Promise<void>;
|
|
34
|
+
/**
|
|
35
|
+
* Stops the queue worker and waits for it, releases any request still
|
|
36
|
+
* waiting on `ready()`, and takes the LogTape configuration down.
|
|
37
|
+
*
|
|
38
|
+
* Aborting only *asks* the listeners to stop; `startQueue()` settles once
|
|
39
|
+
* they have, possibly one in-flight delivery later. Returning that promise
|
|
40
|
+
* keeps the rest of the shutdown sequence (closing the database, most
|
|
41
|
+
* importantly) behind it.
|
|
42
|
+
*
|
|
43
|
+
* LogTape is reset after the queue stops, so an outgoing delivery can still
|
|
44
|
+
* log, and only if this provider configured it (see
|
|
45
|
+
* {@link #loggingConfigured}); resetting the application's own
|
|
46
|
+
* configuration would silence its logs.
|
|
47
|
+
*
|
|
48
|
+
* The return type stays a promise even with no queue to wait for: AdonisJS
|
|
49
|
+
* warns when a provider hook returns a non-thenable.
|
|
50
|
+
*/
|
|
51
|
+
shutdown(): Promise<void>;
|
|
52
|
+
}
|
|
53
|
+
export = FedifyProvider;
|
|
54
|
+
//# sourceMappingURL=fedify_provider.d.cts.map
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { ApplicationService } from "@adonisjs/core/types";
|
|
2
|
+
|
|
3
|
+
//#region providers/fedify_provider.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* The service provider registered from `adonisrc.ts` as
|
|
6
|
+
* `@fedify/adonisjs/fedify_provider`.
|
|
7
|
+
*
|
|
8
|
+
* Binds `fedify.builder`, `fedify.config`, `fedify` and
|
|
9
|
+
* {@link FedifyMiddleware}, installs `ctx.federation`, bridges Fedify's logs
|
|
10
|
+
* into the AdonisJS logger, builds the federation after the preload files
|
|
11
|
+
* have run, and drains the task queue in the web process.
|
|
12
|
+
*/
|
|
13
|
+
declare class FedifyProvider {
|
|
14
|
+
#private;
|
|
15
|
+
protected app: ApplicationService;
|
|
16
|
+
/**
|
|
17
|
+
* Longhand rather than a parameter property, which Node's type stripping
|
|
18
|
+
* does not support; the tests run these sources through it.
|
|
19
|
+
*/
|
|
20
|
+
constructor(app: ApplicationService);
|
|
21
|
+
/**
|
|
22
|
+
* Registers the container bindings.
|
|
23
|
+
*/
|
|
24
|
+
register(): void;
|
|
25
|
+
/**
|
|
26
|
+
* Installs `ctx.federation`, bridges Fedify's logs into the AdonisJS logger,
|
|
27
|
+
* and subscribes to the HTTP server's ready event.
|
|
28
|
+
*/
|
|
29
|
+
boot(): Promise<void>;
|
|
30
|
+
/**
|
|
31
|
+
* Runs after the preload files have registered every dispatcher.
|
|
32
|
+
*/
|
|
33
|
+
ready(): Promise<void>;
|
|
34
|
+
/**
|
|
35
|
+
* Stops the queue worker and waits for it, releases any request still
|
|
36
|
+
* waiting on `ready()`, and takes the LogTape configuration down.
|
|
37
|
+
*
|
|
38
|
+
* Aborting only *asks* the listeners to stop; `startQueue()` settles once
|
|
39
|
+
* they have, possibly one in-flight delivery later. Returning that promise
|
|
40
|
+
* keeps the rest of the shutdown sequence (closing the database, most
|
|
41
|
+
* importantly) behind it.
|
|
42
|
+
*
|
|
43
|
+
* LogTape is reset after the queue stops, so an outgoing delivery can still
|
|
44
|
+
* log, and only if this provider configured it (see
|
|
45
|
+
* {@link #loggingConfigured}); resetting the application's own
|
|
46
|
+
* configuration would silence its logs.
|
|
47
|
+
*
|
|
48
|
+
* The return type stays a promise even with no queue to wait for: AdonisJS
|
|
49
|
+
* warns when a provider hook returns a non-thenable.
|
|
50
|
+
*/
|
|
51
|
+
shutdown(): Promise<void>;
|
|
52
|
+
}
|
|
53
|
+
//#endregion
|
|
54
|
+
export { FedifyProvider as default };
|
|
55
|
+
//# sourceMappingURL=fedify_provider.d.ts.map
|