@seedcord/http 0.10.2 → 0.11.0-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -10,7 +10,7 @@
10
10
  <a href="https://seedcord.org">Website</a> ·
11
11
  <a href="https://seedcord.org/guide">Guide</a> ·
12
12
  <a href="https://seedcord.org/docs">Reference</a> ·
13
- <a href="https://discord.gg/DzFxY58WXf">Discord</a>
13
+ <a href="https://seedcord.org/chat/">Discord</a>
14
14
  </div>
15
15
 
16
16
  <br />
@@ -23,22 +23,68 @@
23
23
 
24
24
  ## About
25
25
 
26
- `@seedcord/http` runs a seedcord bot on Discord's interactions endpoint. Discord posts each interaction to your URL. The framework verifies the Ed25519 signature before dispatching it.
26
+ `@seedcord/http` is the transport for an http bot. Discord posts each interaction to your bot's URL, and the bot checks the request's Ed25519 signature before it runs your handler. It has no discord.js dependency and keeps no connection open, so it runs on your own server or on Cloudflare Workers.
27
27
 
28
- Discord posts only interactions over this transport. Use [`@seedcord/gateway`](https://www.npmjs.com/package/@seedcord/gateway) when the bot reacts to messages, members, or voice.
28
+ Discord posts only interactions to that URL. Use [`@seedcord/gateway`](https://www.npmjs.com/package/@seedcord/gateway) when your bot reacts to messages, members, voice, or reactions.
29
29
 
30
- Until v1.0.0, minor versions can break.
30
+ seedcord is pre-1.0, so a minor version can break your bot. Each release's changelog says what changed and how to update.
31
31
 
32
32
  ## Installation
33
33
 
34
+ `pnpm create seedcord` sets up a new bot and asks where it runs. To add seedcord to an existing project:
35
+
34
36
  ```sh
35
- pnpm add @seedcord/http
37
+ pnpm add @seedcord/http @discordjs/builders envapt
38
+ pnpm add -D seedcord vite typescript
36
39
  ```
37
40
 
38
- `pnpm create seedcord` scaffolds the whole project. On this transport `pnpm dev` also opens a cloudflared tunnel and registers the URL with Discord.
41
+ `envapt` and `typescript` are peer dependencies. `seedcord` is the CLI that runs the bot in dev and builds it. In dev, it also opens a cloudflared tunnel and sets your app's interactions URL to it.
42
+
43
+ ## Usage
44
+
45
+ `bot.ts` creates the bot and tells it where your handlers are:
46
+
47
+ ```ts
48
+ import { Seedcord } from '@seedcord/http';
49
+ import { Converters, Envapter } from 'envapt';
50
+
51
+ export default new Seedcord({
52
+ bot: {
53
+ interactions: { path: './handlers' },
54
+ commands: {
55
+ path: './commands',
56
+ guilds: Envapter.getAs('TESTING_GUILD_IDS', Converters.array(), [])
57
+ }
58
+ },
59
+ subscribers: { path: null }
60
+ });
61
+ ```
62
+
63
+ Read your environment through [envapt](https://envapt.materwelon.dev), the way `TESTING_GUILD_IDS` is read above. seedcord loads your `.env` files into envapt, and binds a worker's `env` to it on Cloudflare Workers, so the same read works in dev, in a build, and on Workers. envapt also reads your shell's variables, while `process.env` misses everything in your `.env` files.
64
+
65
+ Handlers read raw payloads from `discord-api-types`. Slash commands, components, checks, and plugins work the same way as on the gateway. The [guide](https://seedcord.org/guide) covers them.
66
+
67
+ ## Where it runs
39
68
 
40
- ## Entry points
69
+ ### Its own server
70
+
71
+ On Node or Bun, `Seedcord` listens on `port`, 3000 by default.
72
+
73
+ ### Inside your server
74
+
75
+ Set `port: false`, start the bot, then pass each request to `bot.fetch()`. It takes a web `Request` and returns a `Response`, so a Hono or Next.js route can forward to it:
76
+
77
+ ```ts
78
+ import { Hono } from 'hono';
79
+
80
+ import bot from './bot';
81
+
82
+ await bot.start();
83
+
84
+ const app = new Hono();
85
+ app.post('/interactions', (c) => bot.fetch(c.req.raw));
86
+ ```
41
87
 
42
- The root entry runs on Node through the `Seedcord` class.
88
+ ### Cloudflare Workers
43
89
 
44
- `@seedcord/http/edge` targets Web-standard runtimes through `createSeedcord`. The Node entry re-exports all of it. The edge build for Cloudflare Workers is a work in progress and cannot be used yet.
90
+ The same `Seedcord` import runs on Cloudflare Workers. A `cloudflare.config.ts` beside `seedcord.config.ts` makes `seedcord build` write a worker, which you deploy with `cf deploy --prebuilt`. The [Workers guide](https://seedcord.org/guide/build/workers) covers the setup.
package/dist/index.d.mts CHANGED
@@ -1,91 +1,76 @@
1
- import { S as EngineContext, _ as InjectedEmojiMap, a as CursorSource, b as SlashOptions, c as PageContext, d as ModalHandler, f as ButtonHandler, g as Emojis, h as SlashHandler, i as ArraySource, l as Subscriber, m as UserContextMenuHandler, n as Gated, o as PageSource, p as MessageContextMenuHandler, r as InteractionGateContext, s as Paginator, t as version, u as WebhookLog, v as ModalFields, x as createSeedcord, y as SelectedMentionables } from "./edge.index-DFszgCJp.mjs";
2
- import { _ as Core, a as RoleMenuHandler, b as HttpServerConfig, c as UserMenuHandler, d as RepliableHandler, g as AutocompleteHandler, h as SentMessage, i as MentionableMenuHandler, l as ComponentHandler, m as ReplySender, n as InteractionMiddleware, o as SelectMenuHandler, p as InteractionRef, r as ChannelMenuHandler, s as StringMenuHandler, t as Manifest, u as InteractionHandler, v as HttpConfig, y as HttpEdgeConfig } from "./Manifest-D5MEsTmH.mjs";
3
- import "reflect-metadata";
4
- import { REST } from "@discordjs/rest";
1
+ import { A as RoleMenuHandler, B as AutocompleteHandler, C as Plugin, D as InteractionMiddleware, E as EngineContext, F as InteractionHandler, H as HttpConfig, I as RepliableHandler, L as InteractionRef, M as StringMenuHandler, N as UserMenuHandler, O as ChannelMenuHandler, P as ComponentHandler, R as ReplySender, S as HttpPluginOptions, T as PluginOptions, U as HttpEdgeConfig, V as Core, W as HttpServerConfig, _ as Emojis, a as ArraySource, b as SelectedMentionables, c as Paginator, d as WebhookLog, f as ModalHandler, g as SlashHandler, h as UserContextMenuHandler, i as InteractionGateContext, j as SelectMenuHandler, k as MentionableMenuHandler, l as PageContext, m as MessageContextMenuHandler, n as version, o as CursorSource, p as ButtonHandler, r as Gated, s as PageSource, t as HmrModuleHandler, u as Subscriber, v as InjectedEmojiMap, w as PluginLifecycleSpec, x as SlashOptions, y as ModalFields, z as SentMessage } from "./shared-vsj1uwIH.mjs";
5
2
  import { Bus } from "@seedcord/core";
6
- import { Pluggable } from "@seedcord/core/node";
3
+ import { ServerHost } from "@seedcord/core/node";
4
+ import { HostAugmentTarget, HostListenHost, HostVersion, SeedcordBrand, SeedcordInstance } from "@seedcord/types/internal";
5
+ import { REST } from "@discordjs/rest";
7
6
  import { WinstonConsoleSink, WinstonFileSink } from "@seedcord/logger/node";
8
- import { HostAugmentTarget, HostVersion, SeedcordBrand, SeedcordInstance } from "@seedcord/types/internal";
9
- import { Plugin as Plugin$1, PluginLifecycleSpec, PluginOptions, PluginOptions as PluginOptions$1 } from "@seedcord/core/plugin";
10
- import { IRateLimiter, TypedOmit } from "@seedcord/types";
11
- export * from "@seedcord/logger";
12
- export * from "@seedcord/core";
7
+ import { IRateLimiter } from "@seedcord/types";
13
8
  export * from "@seedcord/errors";
14
- export * from "@seedcord/rate-limiter";
9
+ export * from "@seedcord/core";
10
+ export * from "@seedcord/logger";
15
11
  export * from "@seedcord/event-emitter";
12
+ export * from "@seedcord/rate-limiter";
16
13
  export * from "@seedcord/utils";
17
14
  export * from "@seedcord/types";
18
15
  //#region src/node/Seedcord.d.ts
19
- type RuntimeOfConfig<Cfg extends HttpConfig> = Cfg extends {
20
- runtime: 'edge';
21
- } ? 'edge' : 'server';
22
16
  /**
23
- * The HTTP-interactions bot host, a long-running node server around the engine.
17
+ * The HTTP-interactions bot on node. `start()` loads handlers from `config.bot.interactions.path`
18
+ * and binds a server on `port`. With `port: false`, your own server passes requests to `fetch()`.
24
19
  *
25
- * Discovers handlers from `config.bot.interactions.path`, verifies and dispatches interactions on
26
- * `start()`, and runs coordinated shutdown with an in-flight drain. The edge deploy path calls
27
- * `createSeedcord` from a generated entry.
20
+ * Shutdown waits up to 5s for interactions that are still running.
28
21
  */
29
- export declare class Seedcord<Cfg extends HttpConfig = HttpConfig> extends Pluggable<'http', RuntimeOfConfig<Cfg>> implements SeedcordInstance {
22
+ export declare class Seedcord extends ServerHost<'http'> implements SeedcordInstance {
23
+ #private;
30
24
  /** @internal */
31
25
  readonly [SeedcordBrand] = true;
32
26
  /** @internal */
33
27
  readonly [HostAugmentTarget] = "@seedcord/http";
34
28
  /** @internal */
35
29
  readonly [HostVersion]: string;
30
+ /** @internal */
31
+ [HostListenHost]?: string | undefined;
36
32
  /** Discord REST client built from `bot.restOptions`. `start()` sets the token. */
37
33
  readonly rest: REST;
38
34
  /** @see {@link IRateLimiter} */
39
35
  readonly rateLimiter: IRateLimiter;
40
36
  /** @see {@link Bus} */
41
37
  readonly bus: Bus;
42
- private readonly subscribers;
43
- private readonly interactions?;
44
- private readonly commandRegistry?;
45
- private readonly emojiInjector;
46
- private readonly hmrManager;
47
- private readonly logger;
48
- private token?;
49
- private server?;
50
- private boundPort?;
51
- private fetchedUsername?;
52
- readonly config: HttpConfig;
53
- constructor(config: Cfg);
38
+ readonly config: HttpServerConfig;
39
+ /**
40
+ * @throws A **SeedcordError** when another `Seedcord` exists in this process and has not shut down.
41
+ * @throws A **SeedcordError** when the running Node or Bun is older than the range in `engines`.
42
+ */
43
+ constructor(config: HttpServerConfig);
54
44
  /** The bot's discord username, populated by the ready fetch. */
55
45
  get username(): string | undefined;
56
- /** The bound server port, populated once `start()` is listening. */
46
+ /** The bound server port, set once `start()` is listening. Stays `undefined` with `port: false`. */
57
47
  get port(): number | undefined;
58
48
  /**
59
49
  * Starts the host and runs the startup tasks.
60
50
  */
61
51
  start(): Promise<this>;
62
- protected static reset(host?: object): boolean;
63
- private registerStartupTasks;
64
- private registerHmrAwareModules;
65
- private authenticate;
52
+ /**
53
+ * Answers one request to the interactions endpoint. Mount it in your own server when the
54
+ * config sets `port: false`.
55
+ *
56
+ * Rejects with `CoreFetchBeforeStart` until `start()` is called. A request that arrives while
57
+ * `start()` runs waits for all of startup, the command deploy included. Answers 503 once
58
+ * shutdown begins or `start()` fails.
59
+ *
60
+ * @example
61
+ * ```ts
62
+ * // Hono passes the untouched request through c.req.raw
63
+ * const seedcord = new Seedcord({ ...config, port: false });
64
+ * await seedcord.start();
65
+ *
66
+ * const app = new Hono();
67
+ * app.post('/interactions', (c) => seedcord.fetch(c.req.raw));
68
+ * ```
69
+ */
70
+ fetch(request: Request): Promise<Response>;
66
71
  /** The bot's Discord application id. Throws if you read it before the Configuration phase. */
67
72
  get applicationId(): string;
68
- private listen;
69
- private fetchUsername;
70
- private stopServer;
71
73
  }
72
74
  //#endregion
73
- //#region src/Plugin.d.ts
74
- /** The options an HTTP plugin declares, narrowed to the transports this base serves. */
75
- type HttpPluginOptions = TypedOmit<PluginOptions$1, 'transport'> & {
76
- transport?: 'http';
77
- };
78
- /**
79
- * Base class for an HTTP plugin, binding `this.core` to the HTTP {@link Core}.
80
- *
81
- * A plugin that runs on either transport extends the base from `@seedcord/core/plugin`, whose
82
- * `this.core` is typed to the shared members only.
83
- *
84
- * @typeParam Opts - The transport and runtime the plugin declares.
85
- */
86
- export declare abstract class Plugin<Opts extends HttpPluginOptions = {
87
- transport: 'http';
88
- }> extends Plugin$1<Opts, Core> {}
89
- //#endregion
90
- export { ArraySource, AutocompleteHandler, ButtonHandler, ChannelMenuHandler, ComponentHandler, type Core, CursorSource, Emojis, type EngineContext, Gated, type HttpConfig, type HttpEdgeConfig, type HttpPluginOptions, type HttpServerConfig, type InjectedEmojiMap, type InteractionGateContext, InteractionHandler, InteractionMiddleware, type InteractionRef, type Manifest, MentionableMenuHandler, MessageContextMenuHandler, ModalFields, ModalHandler, type PageContext, type PageSource, Paginator, type PluginLifecycleSpec, type PluginOptions, RepliableHandler, ReplySender, RoleMenuHandler, SelectMenuHandler, type SelectedMentionables, type SentMessage, SlashHandler, type SlashOptions, StringMenuHandler, Subscriber, UserContextMenuHandler, UserMenuHandler, WebhookLog, WinstonConsoleSink, WinstonFileSink, createSeedcord, version };
75
+ export { ArraySource, AutocompleteHandler, ButtonHandler, ChannelMenuHandler, ComponentHandler, type Core, CursorSource, Emojis, type EngineContext, Gated, HmrModuleHandler, type HttpConfig, type HttpEdgeConfig, type HttpPluginOptions, type HttpServerConfig, type InjectedEmojiMap, type InteractionGateContext, InteractionHandler, InteractionMiddleware, type InteractionRef, MentionableMenuHandler, MessageContextMenuHandler, ModalFields, ModalHandler, type PageContext, type PageSource, Paginator, Plugin, type PluginLifecycleSpec, type PluginOptions, RepliableHandler, ReplySender, RoleMenuHandler, SelectMenuHandler, type SelectedMentionables, type SentMessage, SlashHandler, type SlashOptions, StringMenuHandler, Subscriber, UserContextMenuHandler, UserMenuHandler, WebhookLog, WinstonConsoleSink, WinstonFileSink, version };
91
76
  //# sourceMappingURL=index.d.mts.map