@went.tf/discord-bot-framework 2.1.0 → 2.2.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
@@ -162,19 +162,31 @@ autocomplete?, modal? }`) no longer describe their own wire shape at all.
162
162
  below):
163
163
 
164
164
  ```json
165
- [
166
- { "type": "CHAT_INPUT", "name": "ping", "description": "Replies with pong" },
167
- {
168
- "type": "CHAT_INPUT",
169
- "name": "search",
170
- "description": "Search for something",
171
- "options": [
172
- { "type": "STRING", "name": "query", "description": "Query string", "required": true }
173
- ]
174
- }
175
- ]
165
+ {
166
+ "$schema": "./commands.schema.json",
167
+ "commands": [
168
+ { "type": "CHAT_INPUT", "name": "ping", "description": "Replies with pong" },
169
+ {
170
+ "type": "CHAT_INPUT",
171
+ "name": "search",
172
+ "description": "Search for something",
173
+ "options": [
174
+ { "type": "STRING", "name": "query", "description": "Query string", "required": true }
175
+ ]
176
+ }
177
+ ]
178
+ }
176
179
  ```
177
180
 
181
+ The `{ "$schema", "commands" }` wrapper is what makes `commands.json`
182
+ editable with real autocomplete/validation in VS Code, JetBrains, or any
183
+ other JSON-Schema-aware editor: `$schema` is only ever valid on a JSON
184
+ *object*, and a bare array can never carry one. A **bare array** (just
185
+ the `commands` value on its own, no wrapper) is still fully supported —
186
+ nothing about the wrapper is required — but you lose the inline `$schema`
187
+ editor hookup if you use it. `buildApplicationCommandsBody` accepts
188
+ either shape directly.
189
+
178
190
  `type` (and `contexts`/`integration_types`/`channel_types`) accept either
179
191
  Discord's raw numeric value or the UPPER_SNAKE_CASE string alias shown
180
192
  above — nobody should have to remember that `3` means `STRING`. Both
@@ -189,10 +201,14 @@ autocomplete?, modal? }`) no longer describe their own wire shape at all.
189
201
 
190
202
  ```ts
191
203
  import { Ajv } from 'ajv';
192
- import { parseCommandsFile, registerFrameworkSchemas } from '@went.tf/discord-bot-framework/commands/schema';
193
- import myCommandsSchema from './commands.schema.json' with { type: 'json' };
204
+ import { parseCommandsFile, registerFrameworkSchemas, resolveCommandsSchemaRefs } from '@went.tf/discord-bot-framework/commands/schema';
205
+ import myCommandsSchemaRaw from './commands.schema.json' with { type: 'json' };
194
206
  import commandsData from './commands.json' with { type: 'json' };
195
207
 
208
+ // Rewrites your schema's relative-path $refs (into node_modules) to each
209
+ // fragment's real ajv-resolvable identity - see "JSON Schema fragments" below.
210
+ const myCommandsSchema = resolveCommandsSchemaRefs(myCommandsSchemaRaw);
211
+
196
212
  const ajv = new Ajv({ allErrors: true, allowUnionTypes: true });
197
213
  registerFrameworkSchemas(ajv);
198
214
  const validate = ajv.compile(myCommandsSchema);
@@ -243,37 +259,80 @@ This package ships only the **generic, reusable JSON Schema building
243
259
  blocks** mirroring `discord-api-types`' command/option shapes — it does not
244
260
  dictate one rigid schema for your whole `commands.json` file. Compose your
245
261
  own schema on top via `$ref`/`allOf`, e.g. to narrow `name` to an enum of
246
- your bot's actual command names:
262
+ your bot's actual command names. Since a bot's own `commands.schema.json` is
263
+ authored as plain JSON (so external tools can consume it too, not just this
264
+ package's TS composition helpers), supporting **both** the bare-array and
265
+ the `{ $schema, commands }` root shapes over the same narrowed entry uses a
266
+ local `$defs` entry referenced from both branches:
247
267
 
248
268
  ```json
249
269
  {
250
- "$id": "https://schema.your-bot.example/commands-file.json",
251
- "type": "array",
252
- "items": {
253
- "oneOf": [
254
- {
255
- "allOf": [
256
- { "$ref": "https://schema.went.tf/discord-bot-framework/chat-input-command.json" },
257
- { "properties": { "name": { "enum": ["ping", "search"] } } }
258
- ]
270
+ "$defs": {
271
+ "entry": {
272
+ "oneOf": [
273
+ {
274
+ "allOf": [
275
+ { "$ref": "../node_modules/@went.tf/discord-bot-framework/build/commands/schema/chat-input-command.json" },
276
+ { "properties": { "name": { "enum": ["ping", "search"] } } }
277
+ ]
278
+ },
279
+ { "$ref": "../node_modules/@went.tf/discord-bot-framework/build/commands/schema/context-menu-command.json" }
280
+ ]
281
+ }
282
+ },
283
+ "oneOf": [
284
+ { "type": "array", "items": { "$ref": "#/$defs/entry" } },
285
+ {
286
+ "type": "object",
287
+ "properties": {
288
+ "$schema": { "type": "string" },
289
+ "commands": { "type": "array", "items": { "$ref": "#/$defs/entry" } }
259
290
  },
260
- { "$ref": "https://schema.went.tf/discord-bot-framework/context-menu-command.json" }
261
- ]
262
- }
291
+ "required": ["commands"],
292
+ "additionalProperties": false
293
+ }
294
+ ]
263
295
  }
264
296
  ```
265
297
 
266
- Call `registerFrameworkSchemas(ajv)` before compiling your own schema so its
267
- `$ref`s resolve. The fragments this package ships (all under
298
+ `$ref` uses a **real relative filesystem path** into `node_modules` (the
299
+ exact `../` count doesn't have to be exactly right — see below) rather than
300
+ an opaque URL — this is what makes an editor (VS Code, JetBrains, ...)
301
+ actually able to follow it and offer real autocomplete/validation while you
302
+ hand-edit `commands.schema.json`, since it points at a real file: this
303
+ package ships its raw fragment `.json` mirrors precisely so a path like this
304
+ resolves to something real on disk, no network access involved.
305
+
306
+ **ajv** can't follow that same relative path directly — it resolves relative
307
+ `$ref`s via real RFC3986 URI resolution against the referencing schema's own
308
+ `$id`, and `node_modules` is virtualized under a symlinked store by pnpm (and
309
+ similar tools), so there is no `$id` value that makes ajv's own resolution
310
+ land on the right file for every install. `resolveCommandsSchemaRefs()`
311
+ sidesteps this: it rewrites any `$ref` matching a shipped fragment's
312
+ filename to that fragment's canonical `$id` (whatever relative-path prefix
313
+ you used), which `registerFrameworkSchemas(ajv)` has already registered.
314
+ Your own local refs (e.g. `#/$defs/...`) are left untouched:
315
+
316
+ ```ts
317
+ import myCommandsSchemaRaw from './commands.schema.json' with { type: 'json' };
318
+ const myCommandsSchema = resolveCommandsSchemaRefs(myCommandsSchemaRaw);
319
+ registerFrameworkSchemas(ajv); // before ajv.compile(myCommandsSchema)
320
+ ```
321
+
322
+ If you don't need the name-narrowing (or any other bot-specific constraint),
323
+ skip authoring your own schema entirely and validate directly against this
324
+ package's own `commandsFileSchema` — it already accepts both root shapes.
325
+
326
+ The fragments this package ships (all under
268
327
  `@went.tf/discord-bot-framework/commands/schema`, and as real standalone
269
328
  `.json` files under `build/commands/schema/` for non-TS tooling):
270
- `commands-file`, `chat-input-command`, `context-menu-command`,
271
- `application-command-option` (and its `application-command-leaf-option`/
272
- `application-command-subcommand`/`application-command-subcommand-group`
273
- building blocks), `application-command-option-choice`,
274
- `default-member-permissions`, `option-name`, `context-menu-name`,
275
- `application-command-type`, `interaction-context-type`,
276
- `application-integration-type`, `channel-type`.
329
+ `commands-file`, `command-file-entry`, `chat-input-command`,
330
+ `context-menu-command`, `application-command-option` (and its
331
+ `application-command-leaf-option`/`application-command-subcommand`/
332
+ `application-command-subcommand-group` building blocks),
333
+ `application-command-option-choice`, `default-member-permissions`,
334
+ `option-name`, `context-menu-name`, `application-command-type`,
335
+ `interaction-context-type`, `application-integration-type`, `channel-type`.
277
336
 
278
337
  `./commands/schema` also exports `enum-maps.ts`'s `APPLICATION_COMMAND_TYPE_MAP`,
279
338
  `APPLICATION_COMMAND_OPTION_TYPE_MAP`, `INTERACTION_CONTEXT_TYPE_MAP`,