Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .changeset/modules-config-key.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
"@wolfstar/plugin-api": minor
"@wolfstar/plugin-broker": minor
"@wolfstar/plugin-cache": minor
"@wolfstar/plugin-gateway": minor
"@wolfstar/plugin-i18next": minor
"@wolfstar/plugin-logger": minor
"@wolfstar/plugin-scheduled-tasks": minor
"@wolfstar/plugin-sharder": minor
"@wolfstar/plugin-subcommands-advanced": minor
---

Every module sets `meta.configKey`, so an application writes a module's options under its own top-level key of `stars.config` (`api`, `broker`, `cache`, `gateway`, `i18n`, `logger`, `scheduledTasks`, `sharder`, `subcommandsAdvanced`) instead of the `[specifier, options]` tuple of `modules`. The tuple still works and is merged over the key. The key is typed in `defineConfig` once the module is imported in `stars.config.ts`; `cache`, `gateway`, `sharder` and `subcommandsAdvanced` take no options yet and only reserve their name.

The `@wolfstar/kit` peer range moves from `^0.1.0` to `^0.2.0`, the first version that reads `configKey`, and `@wolfstar/schema` (`^0.9.0`) is a new optional peer that carries the `StarsConfig` type the keys extend. A project on `@wolfstar/kit` 0.1 has to update it.
3 changes: 2 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ pnpm changeset # add a changeset (`pnpm changeset --e
- CI runs on GitHub-hosted runners (`ubuntu-24.04-arm` for `ci.yml` and `pkg-pr-new.yml`, `ubuntu-latest` for `release.yml`) — not Blacksmith, despite some now-superseded PR history.
- Adding a new package: add a matching `packages:<name>` entry to **both** `.github/labels.yml` (label sync) and `.github/labeler.yml` (path-based auto-labeling on PRs) — these can drift independently (e.g. `plugin-subcommands-advanced` currently has no `labeler.yml` path mapping, and its `labels.yml` entry is misspelled `packages:plugins-subcommands-advanced`, so it's never auto-applied) — add its name to the `scopes` list of `semantic-pull-requests.yml`, and append its `tsconfig.consumption.json` (if any) to the root `typecheck` script.
- `tests/http-framework-peer-range.test.ts` (part of `pnpm test`, so the CI `unit` job) fails when any non-private package's `peerDependencies["@wolfstar/http-framework"]` excludes the framework version the workspace installs (its devDependency, kept on the latest release by Renovate), when a package declaring that peer doesn't also devDepend on it, or when the packages' peer ranges accept different majors (a package whose range starts at a later major, like `plugin-scheduled-tasks`' `^6.1.0`, only has to match from that major up). When Renovate bumps the framework to a new major, widen **every** plugin's peer range (`^x || ^<new>.0.0`) in the same PR **and** add a patch changeset per package — a peer-range change on `main` isn't published without one (that is how `plugin-api`/`plugin-logger`/`plugin-subcommands-advanced` shipped `^3`-only peers to npm after the v5 bump, #121).
- Activation has two paths. `plugin-scheduled-tasks` is module-only (its `./register` is a no-op). The older plugins ship `./module` (default export: `defineModule` from `@wolfstar/kit`, optional peer) and, for those with runtime hooks, `./plugin` (default export: a `definePlugin` factory); `./register` stays the legacy class + `Client.plugins.register*Hook` path for framework v3/v5/v6. Both call the same functions in `src/hooks.ts`, so change behaviour there. The `stars` CLI never imports `<pkg>/register` itself; it only preloads the sources registered through `ctx.addPlugin`, so a user's own `import "<pkg>/register"` (or passing the `./plugin` factory to `plugins` while also importing `/register`) still double-installs the hooks. `tests/module-entries.test.ts` checks that every `src/module.ts` (except `plugin-scheduled-tasks`, excluded on purpose) is exported, built and has kit as an optional peer.
- Activation has two paths. `plugin-scheduled-tasks` is module-only (its `./register` is a no-op). The older plugins ship `./module` (default export: `defineModule` from `@wolfstar/kit`, optional peer) and, for those with runtime hooks, `./plugin` (default export: a `definePlugin` factory); `./register` stays the legacy class + `Client.plugins.register*Hook` path for framework v3/v5/v6. Both call the same functions in `src/hooks.ts`, so change behaviour there. The `stars` CLI never imports `<pkg>/register` itself; it only preloads the sources registered through `ctx.addPlugin`, so a user's own `import "<pkg>/register"` (or passing the `./plugin` factory to `plugins` while also importing `/register`) still double-installs the hooks. `tests/module-entries.test.ts` checks that every `src/module.ts` (except `plugin-scheduled-tasks`, excluded on purpose) is exported, built and has kit and schema as optional peers.
- Module config keys: every `defineModule` sets `meta.configKey`, so a project writes the options under that top-level key of `stars.config` instead of the `[specifier, options]` tuple (the tuple is merged over the key and wins). Needs `@wolfstar/kit` `^0.2.0` (the peer and dev range of every package with a `src/module.ts`), and each `module.ts` augments `StarsConfig` from `@wolfstar/schema` (an optional peer and devDependency) to type the key. Keys: `plugin-api` `api`, `plugin-broker` `broker`, `plugin-cache` `cache`, `plugin-gateway` `gateway`, `plugin-i18next` `i18n`, `plugin-logger` `logger`, `plugin-scheduled-tasks` `scheduledTasks`, `plugin-sharder` `sharder`, `plugin-subcommands-advanced` `subcommandsAdvanced`; `cache`, `gateway`, `sharder` and `subcommandsAdvanced` take no options yet and only reserve the name. A key must be unique and not a built-in key (`BUILT_IN_CONFIG_KEYS` of `@wolfstar/schema`); renaming one is a breaking change. `tests/module-config-keys.test.ts` runs every module through kit's `setupModules`. What sits under a key is written into the build, so it must be JSON.

## Notes for agents

Expand Down
13 changes: 8 additions & 5 deletions knip.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@
]
},
"packages/*": {
"project": ["src/**/*.ts"]
"project": ["src/**/*.ts"],
"ignoreDependencies": ["@wolfstar/schema"]
},
"packages/plugin-logger": {
"entry": ["src/assertions.ts"],
"project": ["src/**/*.ts"]
"project": ["src/**/*.ts"],
"ignoreDependencies": ["@wolfstar/schema"]
},
"packages/plugin-broker": {
"entry": ["src/index.ts", "tests/types/*.ts"],
Expand All @@ -25,7 +27,8 @@
},
"packages/plugin-cache": {
"entry": ["src/index.ts", "tests/types/*.ts"],
"project": ["src/**/*.ts", "tests/types/*.ts"]
"project": ["src/**/*.ts", "tests/types/*.ts"],
"ignoreDependencies": ["@wolfstar/schema"]
},
"packages/plugin-gateway": {
"project": ["src/**/*.ts"],
Expand All @@ -34,11 +37,11 @@
"packages/plugin-scheduled-tasks": {
"entry": ["src/index.ts", "tests/types/*.ts"],
"project": ["src/**/*.ts", "tests/types/*.ts"],
"ignoreDependencies": ["@sapphire/pieces"]
"ignoreDependencies": ["@sapphire/pieces", "@wolfstar/schema"]
},
"packages/plugin-i18next": {
"project": ["src/**/*.ts"],
"ignoreDependencies": ["@wolfstar/i18next-type-generator"]
"ignoreDependencies": ["@wolfstar/i18next-type-generator", "@wolfstar/schema"]
}
}
}
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
"@types/semver": "^7.8.0",
"@types/ws": "^8.18.2",
"@vitest/coverage-v8": "^5.0.3",
"@wolfstar/kit": "^0.2.0",
"@wolfstar/schema": "^0.9.0",
"cz-conventional-changelog": "^3.3.0",
"golar": "^0.1.10",
"husky": "9.1.7",
Expand Down
12 changes: 9 additions & 3 deletions packages/plugin-api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,22 @@ Requires Node.js `>=20.18.1`.

### Stars module

On framework 6.1 and later, list the module in `modules` in `stars.config` (needs the optional
`@wolfstar/kit` peer):
On framework 6.1 and later, list the module in `modules` in `stars.config` and write its options
under the `api` key (needs the optional `@wolfstar/kit` peer, 0.2 or later):

```ts
// stars.config.ts
import type {} from "@wolfstar/plugin-api/module"; // types the `api` key in `defineConfig`

export default defineConfig({
modules: [["@wolfstar/plugin-api/module", { prefix: "v1", listenOptions: { port: 4000 } }]],
modules: ["@wolfstar/plugin-api/module"],
api: { prefix: "v1", listenOptions: { port: 4000 } },
});
```

The `[specifier, options]` tuple still works: `modules: [["@wolfstar/plugin-api/module", { prefix: "v1" }]]`.
Its options are merged over the `api` key, so they win.

The options are written into the built entry, so they must be JSON-serialisable. Anything that is
not (for example `server` options holding functions or buffers) goes through `ClientOptions.api`,
which is merged over the module options.
Expand Down
9 changes: 7 additions & 2 deletions packages/plugin-api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,15 +73,20 @@
"devDependencies": {
"@redstardev/unplugin-version-injector": "^0.0.2",
"@wolfstar/http-framework": "^6.1.0",
"@wolfstar/kit": "^0.1.0"
"@wolfstar/kit": "^0.2.0",
"@wolfstar/schema": "^0.9.0"
},
"peerDependencies": {
"@wolfstar/http-framework": "^3.6.0 || ^5.0.0 || ^6.0.0",
"@wolfstar/kit": "^0.1.0"
"@wolfstar/kit": "^0.2.0",
"@wolfstar/schema": "^0.9.0"
},
"peerDependenciesMeta": {
"@wolfstar/kit": {
"optional": true
},
"@wolfstar/schema": {
"optional": true
}
},
"engines": {
Expand Down
14 changes: 12 additions & 2 deletions packages/plugin-api/src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ import type { ServerOptions } from "./lib/structures/http/Server";

/**
* The Stars module: listing `@wolfstar/plugin-api/module` in `modules` in `stars.config` registers the API plugin with
* the options written there.
* the options written under its `api` key.
*
* @remarks
* The `[specifier, options]` tuple of `modules` still works: its options are merged over the `api` key, so they win.
*
* The options are written into the built entry, so they have to be JSON-serialisable. Pass anything that is not
* (such as the `server` options holding functions or buffers) through `ClientOptions.api`, which is merged over the
* module options.
Expand All @@ -14,16 +16,24 @@ import type { ServerOptions } from "./lib/structures/http/Server";
* ```ts
* // stars.config.ts
* export default defineConfig({
* modules: [['@wolfstar/plugin-api/module', { prefix: '/api' }]],
* modules: ['@wolfstar/plugin-api/module'],
* api: { prefix: '/api' },
* });
* ```
*/
export default defineModule<ServerOptions>({
meta: {
name: "@wolfstar/plugin-api",
configKey: "api",
compatibility: { framework: ">=6.1.0" },
},
setup(options, ctx) {
ctx.addPlugin({ from: "@wolfstar/plugin-api/plugin", options });
},
});

declare module "@wolfstar/schema" {
interface StarsConfig {
api?: ServerOptions;
}
}
13 changes: 10 additions & 3 deletions packages/plugin-broker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,16 +51,23 @@ await broker.publish("messageCreate", payload);

#### Stars module

On framework 6.1 and later, list the module in `modules` in `stars.config` (needs the optional
`@wolfstar/kit` peer):
On framework 6.1 and later, list the module in `modules` in `stars.config` and write its options
under the `broker` key (needs the optional `@wolfstar/kit` peer, 0.2 or later):

```ts
// stars.config.ts
import type {} from "@wolfstar/plugin-broker/module"; // types the `broker` key in `defineConfig`

export default defineConfig({
modules: [["@wolfstar/plugin-broker/module", { stream: "wolfstar:events", group: "workers" }]],
modules: ["@wolfstar/plugin-broker/module"],
broker: { stream: "wolfstar:events", group: "workers" },
});
```

The `[specifier, options]` tuple still works:
`modules: [["@wolfstar/plugin-broker/module", { stream: "wolfstar:events" }]]`. Its options are merged
over the `broker` key, so they win.

The options are written into the built entry, so they must be JSON-serialisable. `redis` is a client
instance and `consumer` is per-process (for example `worker-${process.pid}`), so neither can live in
`stars.config`: supply both through `ClientOptions.broker`, which is shallow-merged over the module
Expand Down
9 changes: 7 additions & 2 deletions packages/plugin-broker/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,17 +74,22 @@
"devDependencies": {
"@sapphire/pieces": "^4.4.1",
"@wolfstar/http-framework": "^6.1.0",
"@wolfstar/kit": "^0.1.0",
"@wolfstar/kit": "^0.2.0",
"@wolfstar/plugin-gateway": "workspace:^",
"@wolfstar/schema": "^0.9.0",
"ioredis": "^6.0.0"
},
"peerDependencies": {
"@wolfstar/http-framework": "^3.4.0 || ^5.0.0 || ^6.0.0",
"@wolfstar/kit": "^0.1.0"
"@wolfstar/kit": "^0.2.0",
"@wolfstar/schema": "^0.9.0"
},
"peerDependenciesMeta": {
"@wolfstar/kit": {
"optional": true
},
"@wolfstar/schema": {
"optional": true
}
},
"engines": {
Expand Down
15 changes: 13 additions & 2 deletions packages/plugin-broker/src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ import type { BrokerConsumerOptions } from "./BrokerConsumer.js";

/**
* The Stars module: listing `@wolfstar/plugin-broker/module` in `modules` in `stars.config` registers the broker
* plugin with the options written there.
* plugin with the options written under its `broker` key.
*
* @remarks
* The `[specifier, options]` tuple of `modules` still works: its options are merged over the `broker` key, so they
* win.
*
* The options are written into the built entry, so they have to be JSON-serialisable. `redis` is a client instance
* and `consumer` is per-process, so neither can be written in `stars.config`: supply both through
* `ClientOptions.broker`, which is shallow-merged over the module options (the module options are the base values
Expand All @@ -17,16 +20,24 @@ import type { BrokerConsumerOptions } from "./BrokerConsumer.js";
* ```ts
* // stars.config.ts
* export default defineConfig({
* modules: [['@wolfstar/plugin-broker/module', { stream: 'wolfstar:events', group: 'workers' }]],
* modules: ['@wolfstar/plugin-broker/module'],
* broker: { stream: 'wolfstar:events', group: 'workers' },
* });
* ```
*/
export default defineModule<Partial<BrokerConsumerOptions>>({
meta: {
name: "@wolfstar/plugin-broker",
configKey: "broker",
compatibility: { framework: ">=6.1.0" },
},
setup(options, ctx) {
ctx.addPlugin({ from: "@wolfstar/plugin-broker/plugin", options });
},
});

declare module "@wolfstar/schema" {
interface StarsConfig {
broker?: Partial<BrokerConsumerOptions>;
}
}
3 changes: 2 additions & 1 deletion packages/plugin-cache/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,8 @@ export default defineConfig({
```

The module registers no plugin and takes no options: the cache itself is still constructed by
`@wolfstar/plugin-gateway`.
`@wolfstar/plugin-gateway`. It owns the `cache` key of `stars.config` (reserved for future options), so no
other module can claim it.

## Credits

Expand Down
9 changes: 7 additions & 2 deletions packages/plugin-cache/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,18 +69,23 @@
"discord-api-types": "^0.38.33"
},
"devDependencies": {
"@wolfstar/kit": "^0.1.0",
"@wolfstar/kit": "^0.2.0",
"@wolfstar/schema": "^0.9.0",
"ioredis": "^6.0.0",
"msgpackr": "^2.1.0"
},
"peerDependencies": {
"@wolfstar/kit": "^0.1.0",
"@wolfstar/kit": "^0.2.0",
"@wolfstar/schema": "^0.9.0",
"msgpackr": "^2.0.0"
},
"peerDependenciesMeta": {
"@wolfstar/kit": {
"optional": true
},
"@wolfstar/schema": {
"optional": true
},
"msgpackr": {
"optional": true
}
Expand Down
18 changes: 16 additions & 2 deletions packages/plugin-cache/src/module.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,26 @@
import { defineModule } from "@wolfstar/kit";

/**
* The options of the module: none yet. The `cache` key of `stars.config` is reserved for them.
*/
export type CacheModuleOptions = Record<string, never>;

/**
* The Stars module: listing `@wolfstar/plugin-cache/module` in `modules` in `stars.config` adds the package to the
* auto imports. The cache itself is constructed by `@wolfstar/plugin-gateway`.
*
* @remarks
* The module owns the `cache` key of `stars.config`, which takes no options for now.
*/
export default defineModule({
meta: { name: "@wolfstar/plugin-cache" },
export default defineModule<CacheModuleOptions>({
meta: { name: "@wolfstar/plugin-cache", configKey: "cache" },
setup(_options, ctx) {
ctx.addImports("@wolfstar/plugin-cache");
},
});

declare module "@wolfstar/schema" {
interface StarsConfig {
cache?: CacheModuleOptions;
}
}
2 changes: 1 addition & 1 deletion packages/plugin-gateway/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1318,7 +1318,7 @@ export default defineConfig({
});
```

The module registers no plugin and takes no options: you still construct the `GatewayClient` yourself.
The module registers no plugin and takes no options: you still construct the `GatewayClient` yourself. It owns the `gateway` key of `stars.config` (reserved for future options), so no other module can claim it.

## Credits

Expand Down
9 changes: 7 additions & 2 deletions packages/plugin-gateway/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,20 @@
},
"devDependencies": {
"@wolfstar/http-framework": "^6.1.0",
"@wolfstar/kit": "^0.1.0"
"@wolfstar/kit": "^0.2.0",
"@wolfstar/schema": "^0.9.0"
},
"peerDependencies": {
"@wolfstar/http-framework": "^3.4.0 || ^5.0.0 || ^6.0.0",
"@wolfstar/kit": "^0.1.0"
"@wolfstar/kit": "^0.2.0",
"@wolfstar/schema": "^0.9.0"
},
"peerDependenciesMeta": {
"@wolfstar/kit": {
"optional": true
},
"@wolfstar/schema": {
"optional": true
}
},
"engines": {
Expand Down
22 changes: 20 additions & 2 deletions packages/plugin-gateway/src/module.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,30 @@
import { defineModule } from "@wolfstar/kit";

/**
* The options of the module: none yet. The `gateway` key of `stars.config` is reserved for them.
*/
export type GatewayModuleOptions = Record<string, never>;

/**
* The Stars module: listing `@wolfstar/plugin-gateway/module` in `modules` in `stars.config` adds the package to the
* auto imports. The `GatewayClient` is still constructed by you.
*
* @remarks
* The module owns the `gateway` key of `stars.config`, which takes no options for now.
*/
export default defineModule({
meta: { name: "@wolfstar/plugin-gateway", compatibility: { framework: ">=6.1.0" } },
export default defineModule<GatewayModuleOptions>({
meta: {
name: "@wolfstar/plugin-gateway",
configKey: "gateway",
compatibility: { framework: ">=6.1.0" },
},
setup(_options, ctx) {
ctx.addImports("@wolfstar/plugin-gateway");
},
});

declare module "@wolfstar/schema" {
interface StarsConfig {
gateway?: GatewayModuleOptions;
}
}
Loading
Loading