@shurizma/discord-message-contract 0.4.0 → 0.6.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
@@ -1,169 +1,218 @@
1
- # @shurizma/discord-message-contract
2
-
3
- The template engine shared by the [shuri.gg](https://shuri.gg) Discord Message Builder and the bots
4
- that implement its compatibility contract.
5
-
6
- The builder lets you compose a Discord [Components V2](https://discord.com/developers/docs/components/reference)
7
- message in the browser and hand it to a bot to send. Templates let that message carry placeholders
8
- (`{{guild.name}}`) that the bot fills in at send time, so a stored message can re-render itself
9
- against fresh data.
10
-
11
- That only works if the website's live preview and the bot's actual send agree on the grammar, down
12
- to the character. This package is that agreement, extracted so there is exactly one implementation
13
- instead of one per participant.
14
-
15
- ## Install
16
-
17
- ```
18
- pnpm add @shurizma/discord-message-contract
19
- ```
20
-
21
- Zero runtime dependencies. Works in Node and in a browser bundle.
22
-
23
- ## A working implementation
24
-
25
- If you are here because you want to build a bot the message builder can talk to, start from
26
- [discord-message-bot](https://git.shuri.gg/ShuriZma/discord-message-bot) instead of from this
27
- package. It is a complete, runnable reference implementation of the whole contract, multi-server out
28
- of the box, and it uses everything documented below. Clone it and run it, or read it and write your
29
- own.
30
-
31
- This package is the part both sides have to agree on. The bot is what that agreement is for.
32
-
33
- ## API
34
-
35
- ### `renderTemplate(value, data, placeholders)`
36
-
37
- Walks any JSON-shaped value and substitutes every template tag found in its strings. Arrays and
38
- objects recurse; non-strings pass through untouched.
39
-
40
- ```ts
41
- import { renderTemplate } from '@shurizma/discord-message-contract';
42
-
43
- const placeholders = [
44
- { name: 'guild.name', description: 'Server name', kind: 'scalar' },
45
- { name: 'roles', description: 'Server roles', kind: 'list', itemAlias: 'role' },
46
- ];
47
-
48
- const data = {
49
- guild: { name: 'Skadi' },
50
- roles: [{ name: 'Admin' }, { name: 'Mod' }],
51
- };
52
-
53
- renderTemplate(
54
- { components: [{ type: 10, content: '# {{guild.name}}\n{{#foreach:roles:, }}{{role.name}}{{/foreach}}' }] },
55
- data,
56
- placeholders,
57
- );
58
- // { components: [{ type: 10, content: '# Skadi\nAdmin, Mod' }] }
59
- ```
60
-
61
- ### `applyConditionalButtons(components, rules, data)`
62
-
63
- Removes buttons whose declared condition does not hold, and drops any action row left empty. A
64
- button matches a rule when its `custom_id` starts with the rule's `customIdPrefix`. Buttons matching
65
- no rule are always kept.
66
-
67
- Link buttons with an empty `url` are always removed, because Discord rejects them outright.
68
-
69
- ```ts
70
- import { applyConditionalButtons } from '@shurizma/discord-message-contract';
71
-
72
- const rules = [{ customIdPrefix: 'boost:', condition: 'guild.boostCount', negate: true }];
73
-
74
- applyConditionalButtons(components, rules, { guild: { boostCount: 0 } });
75
- // the boost: button survives, because boostCount is falsy and the rule is negated
76
- ```
77
-
78
- A bot must apply these rules on **every** render that reaches Discord, not only on scheduled
79
- refreshes. If it does not, the website's preview and the message people actually see will disagree.
80
-
81
- ### `resolveNowTimestamps(value)`
82
-
83
- Replaces `<t:now>` and `<t:now:R>` with a real Unix timestamp, recursing the same way
84
- `renderTemplate` does. Call it last, immediately before handing the payload to Discord, and keep the
85
- literal `<t:now>` in storage so it re-resolves on every future send or edit rather than freezing to
86
- whenever the message was composed.
87
-
88
- Supported format suffixes are Discord's own: `t`, `T`, `d`, `D`, `f`, `F`, `R`. Anything else is
89
- left alone.
90
-
91
- ### Order of operations
92
-
93
- ```ts
94
- const rendered = renderTemplate(stored.payload, data, definition.placeholders);
95
- const filtered = { ...rendered, components: applyConditionalButtons(rendered.components, definition.conditionalButtons, data) };
96
- const payload = resolveNowTimestamps(filtered);
97
- ```
98
-
99
- Substitute, then filter, then resolve timestamps. Filtering before substitution would test
100
- conditions against unrendered placeholder text; resolving timestamps early would freeze them.
101
-
102
- ## Grammar
103
-
104
- | Tag | Meaning |
105
- | --- | --- |
106
- | `{{path}}` | Insert a value. Dotted paths walk objects. A missing or null value renders as the empty string. |
107
- | `{{#if:path}}` | Truthy test. An empty array is falsy; every other value follows JavaScript truthiness. |
108
- | `{{#if:empty:path}}` | True when the value is `null`, `undefined`, `0` or `""`. |
109
- | `{{#if:isset:path}}` | True when the value is neither `null` nor `undefined`. `0` and `""` count as set. |
110
- | `{{#if:eq:path:value}}` | String comparison against a literal. `{{#if:eq:count:0}}` matches the number `0`. |
111
- | `{{else}}` | Optional alternate branch. |
112
- | `{{/if}}` | Closes the nearest open `if`. |
113
- | `{{#foreach:list}}` | Iterate a list placeholder. |
114
- | `{{#foreach:list:separator}}` | Same, joining iterations with the literal separator. `{{#foreach:roles:, }}` gives `Admin, Mod`. |
115
- | `{{/foreach}}` | Closes the nearest open `foreach`. |
116
-
117
- Inside a `foreach` body, the current item is bound to the list placeholder's `itemAlias`, so a
118
- placeholder declared as `{ name: 'roles', kind: 'list', itemAlias: 'role' }` exposes `{{role.name}}`.
119
- The enclosing scope stays visible, so `{{guild.name}}` still resolves inside the loop.
120
-
121
- `if` blocks nest. `foreach` blocks do not: a `foreach` inside another `foreach` is emitted as
122
- literal text rather than iterated. Iterating a list with no registered `itemAlias` throws.
123
-
124
- Unrecognised tags pass through as literal text.
125
-
126
- ### Known malformed-input behaviour
127
-
128
- Malformed blocks fail quietly rather than raising:
129
-
130
- - An unclosed `{{#if:x}}` renders its body when the condition holds.
131
- - Text after an unmatched `{{/if}}` is discarded. `a{{/if}}b` renders as `a`.
132
-
133
- These are frozen as-is because three live implementations already agreed on them. Treat them as
134
- behaviour to preserve, not as intent.
135
-
136
- ## Conforming in another language
137
-
138
- `fixtures/grammar.json` is the language-agnostic definition of the grammar. Passing every fixture is
139
- what conformance means. Each entry is:
140
-
141
- ```json
142
- {
143
- "name": "foreach-separator",
144
- "template": "{{#foreach:roles:, }}{{role.name}}{{/foreach}}",
145
- "placeholders": [{ "name": "roles", "description": "roles", "kind": "list", "itemAlias": "role" }],
146
- "data": { "roles": [{ "name": "Admin" }, { "name": "Mod" }] },
147
- "expected": "Admin, Mod"
148
- }
149
- ```
150
-
151
- `template` is any JSON value, not only a string, since substitution recurses through objects and
152
- arrays. An entry carrying `throws` instead of `expected` must raise an error containing that text.
153
-
154
- The fixtures were generated by running a shared corpus through the two implementations that existed
155
- before this package (the shuri.gg frontend and the dallas bot) and recording only the cases where
156
- both agreed, so they describe behaviour that was already in production rather than a fresh
157
- specification.
158
-
159
- ## See also
160
-
161
- - [discord-message-bot](https://git.shuri.gg/ShuriZma/discord-message-bot), a complete reference bot
162
- built on this package.
163
- - [The bot compatibility contract](https://shuri.gg/tools/discord/bots/compatibility), the HTTP
164
- endpoints a bot implements.
165
- - [The message builder](https://shuri.gg/tools/discord/message-builder) itself.
166
-
167
- ## License
168
-
169
- MIT
1
+ # @shurizma/discord-message-contract
2
+
3
+ The template engine shared by the [shuri.gg](https://shuri.gg) Discord Message Builder and the bots
4
+ that implement its compatibility contract.
5
+
6
+ The builder lets you compose a Discord [Components V2](https://discord.com/developers/docs/components/reference)
7
+ message in the browser and hand it to a bot to send. Templates let that message carry placeholders
8
+ (`{{guild.name}}`) that the bot fills in at send time, so a stored message can re-render itself
9
+ against fresh data.
10
+
11
+ That only works if the website's live preview and the bot's actual send agree on the grammar, down
12
+ to the character. This package is that agreement, extracted so there is exactly one implementation
13
+ instead of one per participant.
14
+
15
+ ## Install
16
+
17
+ ```
18
+ pnpm add @shurizma/discord-message-contract
19
+ ```
20
+
21
+ Zero runtime dependencies. Works in Node and in a browser bundle.
22
+
23
+ ## A working implementation
24
+
25
+ If you are here because you want to build a bot the message builder can talk to, start from
26
+ [discord-message-bot](https://git.shuri.gg/ShuriZma/discord-message-bot) instead of from this
27
+ package. It is a complete, runnable reference implementation of the whole contract, multi-server out
28
+ of the box, and it uses everything documented below. Clone it and run it, or read it and write your
29
+ own.
30
+
31
+ This package is the part both sides have to agree on. The bot is what that agreement is for.
32
+
33
+ ## API
34
+
35
+ ### `renderTemplate(value, data, placeholders)`
36
+
37
+ Walks any JSON-shaped value and substitutes every template tag found in its strings. Arrays and
38
+ objects recurse; non-strings pass through untouched.
39
+
40
+ ```ts
41
+ import { renderTemplate } from '@shurizma/discord-message-contract';
42
+
43
+ const placeholders = [
44
+ { name: 'guild.name', description: 'Server name', kind: 'scalar' },
45
+ { name: 'roles', description: 'Server roles', kind: 'list', itemAlias: 'role' },
46
+ ];
47
+
48
+ const data = {
49
+ guild: { name: 'Skadi' },
50
+ roles: [{ name: 'Admin' }, { name: 'Mod' }],
51
+ };
52
+
53
+ renderTemplate(
54
+ { components: [{ type: 10, content: '# {{guild.name}}\n{{#foreach:roles:, }}{{role.name}}{{/foreach}}' }] },
55
+ data,
56
+ placeholders,
57
+ );
58
+ // { components: [{ type: 10, content: '# Skadi\nAdmin, Mod' }] }
59
+ ```
60
+
61
+ ### `applyConditionalButtons(components, rules, data)`
62
+
63
+ Removes buttons whose declared condition does not hold, and drops any action row left empty. A
64
+ button matches a rule when its `custom_id` starts with the rule's `customIdPrefix`. Buttons matching
65
+ no rule are always kept.
66
+
67
+ Link buttons with an empty `url` are always removed, because Discord rejects them outright.
68
+
69
+ ```ts
70
+ import { applyConditionalButtons } from '@shurizma/discord-message-contract';
71
+
72
+ const rules = [{ customIdPrefix: 'boost:', condition: 'guild.boostCount', negate: true }];
73
+
74
+ applyConditionalButtons(components, rules, { guild: { boostCount: 0 } });
75
+ // the boost: button survives, because boostCount is falsy and the rule is negated
76
+ ```
77
+
78
+ A bot must apply these rules on **every** render that reaches Discord, not only on scheduled
79
+ refreshes. If it does not, the website's preview and the message people actually see will disagree.
80
+
81
+ ### `resolveNowTimestamps(value)`
82
+
83
+ Replaces `<t:now>` and `<t:now:R>` with a real Unix timestamp, recursing the same way
84
+ `renderTemplate` does. Call it last, immediately before handing the payload to Discord, and keep the
85
+ literal `<t:now>` in storage so it re-resolves on every future send or edit rather than freezing to
86
+ whenever the message was composed.
87
+
88
+ Supported format suffixes are Discord's own: `t`, `T`, `d`, `D`, `f`, `F`, `R`. Anything else is
89
+ left alone.
90
+
91
+ ### `validateTemplate(value, placeholders)`
92
+
93
+ Returns diagnostics for a template without rendering it, so an editor can show problems before
94
+ anyone saves. Every check below is silent at render time, which is why this exists.
95
+
96
+ ```ts
97
+ import { validateTemplate } from '@shurizma/discord-message-contract';
98
+
99
+ validateTemplate({ components: [{ type: 10, content: 'a{{/if}}b' }] }, placeholders);
100
+ // [{
101
+ // severity: 'error',
102
+ // message: '{{/if}} has no matching {{#if}}, and everything after it is discarded',
103
+ // path: 'components.0.content',
104
+ // }]
105
+ ```
106
+
107
+ | Reported | Severity | Why it matters |
108
+ | --- | --- | --- |
109
+ | `{{/if}}` or `{{/foreach}}` with no opener | error | Everything after it is silently dropped |
110
+ | `{{#if}}` or `{{#foreach}}` never closed | error | The block renders anyway, which is rarely intended |
111
+ | `{{else}}` outside an if, or twice in one if | error | Only the first is used |
112
+ | `foreach` over a list with no registered `itemAlias` | error | Rendering throws |
113
+ | `{{@index}}` outside a `foreach` | error | Renders empty |
114
+ | A placeholder you never declared | warning | Renders as empty text |
115
+
116
+ `path` locates the offending string inside the payload, so `components.0.components.1.content`
117
+ points at exactly the text field to fix.
118
+
119
+ ### Order of operations
120
+
121
+ ```ts
122
+ const rendered = renderTemplate(stored.payload, data, definition.placeholders);
123
+ const filtered = { ...rendered, components: applyConditionalButtons(rendered.components, definition.conditionalButtons, data) };
124
+ const payload = resolveNowTimestamps(filtered);
125
+ ```
126
+
127
+ Substitute, then filter, then resolve timestamps. Filtering before substitution would test
128
+ conditions against unrendered placeholder text; resolving timestamps early would freeze them.
129
+
130
+ ## Grammar
131
+
132
+ | Tag | Meaning |
133
+ | --- | --- |
134
+ | `{{path}}` | Insert a value. Dotted paths walk objects. A missing or null value renders as the empty string. |
135
+ | `{{#if:path}}` | Truthy test. An empty array is falsy; every other value follows JavaScript truthiness. |
136
+ | `{{#if:empty:path}}` | True when the value is `null`, `undefined`, `0` or `""`. |
137
+ | `{{#if:isset:path}}` | True when the value is neither `null` nor `undefined`. `0` and `""` count as set. |
138
+ | `{{#if:eq:path:value}}` | String comparison against a literal. `{{#if:eq:count:0}}` matches the number `0`. |
139
+ | `{{else}}` | Optional alternate branch. |
140
+ | `{{/if}}` | Closes the nearest open `if`. |
141
+ | `{{#foreach:list}}` | Iterate a list placeholder. |
142
+ | `{{#foreach:list:separator}}` | Same, joining iterations with the literal separator. `{{#foreach:roles:, }}` gives `Admin, Mod`. |
143
+ | `{{/foreach}}` | Closes the nearest open `foreach`. |
144
+ | `{{#if:ne:path:value}}` | String inequality, the counterpart to `eq`. |
145
+ | `{{@index}}` / `{{@index0}}` | Position in the current `foreach`, 1-based and 0-based. An inner loop shadows an outer one. |
146
+
147
+ Inside a `foreach` body, the current item is bound to the list placeholder's `itemAlias`, so a
148
+ placeholder declared as `{ name: 'roles', kind: 'list', itemAlias: 'role' }` exposes `{{role.name}}`.
149
+ The enclosing scope stays visible, so `{{guild.name}}` still resolves inside the loop.
150
+
151
+ Both `if` and `foreach` blocks nest. To iterate a list that lives inside the current item, declare
152
+ it as a list placeholder named after the loop alias and give it its own `itemAlias`:
153
+
154
+ ```ts
155
+ placeholders: [
156
+ { name: 'teams', kind: 'list', itemAlias: 'team', ... },
157
+ { name: 'team.members', kind: 'list', itemAlias: 'member', ... },
158
+ ]
159
+ ```
160
+
161
+ ```
162
+ {{#foreach:teams:; }}{{team.name}}: {{#foreach:team.members:, }}{{member.n}}{{/foreach}}{{/foreach}}
163
+ -> "Red: a, b; Blue: c"
164
+ ```
165
+
166
+ The outer alias stays visible inside the inner loop. Iterating a list with no registered
167
+ `itemAlias` throws, which `validateTemplate` catches first.
168
+
169
+ Dotted paths reach into anything the data holds, including array length: `{{roles.length}}` and
170
+ `{{team.members.length}}` both work, declared or not. Declaring placeholders is what makes them
171
+ appear in an editor's toolbar; it is not required for them to resolve.
172
+
173
+ Unrecognised tags pass through as literal text.
174
+
175
+ ### Known malformed-input behaviour
176
+
177
+ Malformed blocks fail quietly rather than raising:
178
+
179
+ - An unclosed `{{#if:x}}` renders its body when the condition holds.
180
+ - Text after an unmatched `{{/if}}` is discarded. `a{{/if}}b` renders as `a`.
181
+
182
+ These are frozen as-is because three live implementations already agreed on them. Treat them as
183
+ behaviour to preserve, not as intent.
184
+
185
+ ## Conforming in another language
186
+
187
+ `fixtures/grammar.json` is the language-agnostic definition of the grammar. Passing every fixture is
188
+ what conformance means. Each entry is:
189
+
190
+ ```json
191
+ {
192
+ "name": "foreach-separator",
193
+ "template": "{{#foreach:roles:, }}{{role.name}}{{/foreach}}",
194
+ "placeholders": [{ "name": "roles", "description": "roles", "kind": "list", "itemAlias": "role" }],
195
+ "data": { "roles": [{ "name": "Admin" }, { "name": "Mod" }] },
196
+ "expected": "Admin, Mod"
197
+ }
198
+ ```
199
+
200
+ `template` is any JSON value, not only a string, since substitution recurses through objects and
201
+ arrays. An entry carrying `throws` instead of `expected` must raise an error containing that text.
202
+
203
+ The fixtures were generated by running a shared corpus through the two implementations that existed
204
+ before this package (the shuri.gg frontend and the dallas bot) and recording only the cases where
205
+ both agreed, so they describe behaviour that was already in production rather than a fresh
206
+ specification.
207
+
208
+ ## See also
209
+
210
+ - [discord-message-bot](https://git.shuri.gg/ShuriZma/discord-message-bot), a complete reference bot
211
+ built on this package.
212
+ - [The bot compatibility contract](https://shuri.gg/tools/discord/bots/compatibility), the HTTP
213
+ endpoints a bot implements.
214
+ - [The message builder](https://shuri.gg/tools/discord/message-builder) itself.
215
+
216
+ ## License
217
+
218
+ MIT
package/dist/grammar.d.ts CHANGED
@@ -1,3 +1,28 @@
1
1
  import type { TemplatePlaceholder } from './types.js';
2
+ type IfMode = 'plain' | 'empty' | 'isset' | 'eq' | 'ne';
3
+ export type Token = {
4
+ kind: 'text';
5
+ value: string;
6
+ } | {
7
+ kind: 'if';
8
+ mode: IfMode;
9
+ path: string;
10
+ value?: string;
11
+ } | {
12
+ kind: 'else';
13
+ } | {
14
+ kind: 'endif';
15
+ } | {
16
+ kind: 'foreach';
17
+ name: string;
18
+ separator?: string;
19
+ } | {
20
+ kind: 'endforeach';
21
+ } | {
22
+ kind: 'scalar';
23
+ path: string;
24
+ };
25
+ export declare function tokenize(template: string): Token[];
2
26
  export declare function renderTemplate(value: unknown, data: Record<string, unknown>, placeholders: TemplatePlaceholder[]): unknown;
27
+ export {};
3
28
  //# sourceMappingURL=grammar.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"grammar.d.ts","sourceRoot":"","sources":["../src/grammar.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAkLtD,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,EAAE,mBAAmB,EAAE,GAAG,OAAO,CAe1H"}
1
+ {"version":3,"file":"grammar.d.ts","sourceRoot":"","sources":["../src/grammar.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEtD,KAAK,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,IAAI,GAAG,IAAI,CAAC;AAExD,MAAM,MAAM,KAAK,GACX;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC/B;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAC1D;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,GACjB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GACrD;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,GACtB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAWvC,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,KAAK,EAAE,CAiClD;AA2HD,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,EAAE,mBAAmB,EAAE,GAAG,OAAO,CAe1H"}
package/dist/grammar.js CHANGED
@@ -1,5 +1,5 @@
1
1
  const TAG_PATTERN = /\{\{(?:#if:(?:(?<ifMode>empty|isset):(?<ifPath>[\w.]+)|(?<cmpOp>eq|ne):(?<cmpPath>[\w.]+):(?<cmpValue>[^}]*)|(?<plainPath>[\w.]+))|(?<elseTag>else)|(?<endifTag>\/if)|#foreach:(?<foreachName>[\w.]+)(?::(?<foreachSep>[^}]*))?|(?<endforeachTag>\/foreach)|(?<scalarPath>@?[\w.]+))\}\}/g;
2
- function tokenize(template) {
2
+ export function tokenize(template) {
3
3
  const tokens = [];
4
4
  let lastIndex = 0;
5
5
  let match;
@@ -1 +1 @@
1
- {"version":3,"file":"grammar.js","sourceRoot":"","sources":["../src/grammar.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"grammar.js","sourceRoot":"","sources":["../src/grammar.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  export { applyConditionalButtons } from './conditional-buttons.js';
2
2
  export { renderTemplate } from './grammar.js';
3
3
  export { resolveNowTimestamps } from './timestamps.js';
4
+ export { validateTemplate } from './validate.js';
5
+ export type { TemplateDiagnostic, TemplateDiagnosticSeverity } from './validate.js';
4
6
  export type { MessageStore, NewSavedMessage, SavedMessage, SavedMessagePatch } from './store.js';
5
7
  export type { ChannelSummary, ConditionalButtonRule, ContractCapabilities, ContractError, DiscordMessagePayload, GuildSummary, RoleSummary, TemplateDefinition, TemplatePlaceholder, TemplatePlaceholderKind, TemplatePreviewParam, TemplatePreviewSpec, UserSummary, } from './types.js';
6
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AACvD,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACjG,YAAY,EACR,cAAc,EACd,qBAAqB,EACrB,oBAAoB,EACpB,aAAa,EACb,qBAAqB,EACrB,YAAY,EACZ,WAAW,EACX,kBAAkB,EAClB,mBAAmB,EACnB,uBAAuB,EACvB,oBAAoB,EACpB,mBAAmB,EACnB,WAAW,GACd,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,YAAY,EAAE,kBAAkB,EAAE,0BAA0B,EAAE,MAAM,eAAe,CAAC;AACpF,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACjG,YAAY,EACR,cAAc,EACd,qBAAqB,EACrB,oBAAoB,EACpB,aAAa,EACb,qBAAqB,EACrB,YAAY,EACZ,WAAW,EACX,kBAAkB,EAClB,mBAAmB,EACnB,uBAAuB,EACvB,oBAAoB,EACpB,mBAAmB,EACnB,WAAW,GACd,MAAM,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export { applyConditionalButtons } from './conditional-buttons.js';
2
2
  export { renderTemplate } from './grammar.js';
3
3
  export { resolveNowTimestamps } from './timestamps.js';
4
+ export { validateTemplate } from './validate.js';
4
5
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC"}
package/dist/types.d.ts CHANGED
@@ -44,6 +44,7 @@ export interface ContractCapabilities {
44
44
  files: boolean;
45
45
  users: boolean;
46
46
  guildScopedMessages: boolean;
47
+ testSend: boolean;
47
48
  };
48
49
  }
49
50
  export interface GuildSummary {
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,uBAAuB,GAAG,QAAQ,GAAG,MAAM,CAAC;AAExD,MAAM,WAAW,mBAAmB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,uBAAuB,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,qBAAqB;IAClC,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,oBAAoB,EAAE,CAAC;CAClC;AAED,MAAM,WAAW,kBAAkB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,OAAO,CAAC;IACtB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,kBAAkB,EAAE,qBAAqB,EAAE,CAAC;IAC5C,YAAY,EAAE,mBAAmB,EAAE,CAAC;IACpC,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B,cAAc,CAAC,EAAE,qBAAqB,CAAC;CAC1C;AAED,MAAM,WAAW,aAAa;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACjC,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE;QACN,SAAS,EAAE,OAAO,CAAC;QACnB,eAAe,EAAE,OAAO,CAAC;QACzB,KAAK,EAAE,OAAO,CAAC;QACf,KAAK,EAAE,OAAO,CAAC;QACf,mBAAmB,EAAE,OAAO,CAAC;KAChC,CAAC;CACL;AAED,MAAM,WAAW,YAAY;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,qBAAqB;IAClC,UAAU,EAAE,OAAO,EAAE,CAAC;CACzB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,uBAAuB,GAAG,QAAQ,GAAG,MAAM,CAAC;AAExD,MAAM,WAAW,mBAAmB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,uBAAuB,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,qBAAqB;IAClC,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,oBAAoB,EAAE,CAAC;CAClC;AAED,MAAM,WAAW,kBAAkB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,OAAO,CAAC;IACtB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,kBAAkB,EAAE,qBAAqB,EAAE,CAAC;IAC5C,YAAY,EAAE,mBAAmB,EAAE,CAAC;IACpC,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B,cAAc,CAAC,EAAE,qBAAqB,CAAC;CAC1C;AAED,MAAM,WAAW,aAAa;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACjC,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE;QACN,SAAS,EAAE,OAAO,CAAC;QACnB,eAAe,EAAE,OAAO,CAAC;QACzB,KAAK,EAAE,OAAO,CAAC;QACf,KAAK,EAAE,OAAO,CAAC;QACf,mBAAmB,EAAE,OAAO,CAAC;QAC7B,QAAQ,EAAE,OAAO,CAAC;KACrB,CAAC;CACL;AAED,MAAM,WAAW,YAAY;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,qBAAqB;IAClC,UAAU,EAAE,OAAO,EAAE,CAAC;CACzB"}
@@ -0,0 +1,9 @@
1
+ import type { TemplatePlaceholder } from './types.js';
2
+ export type TemplateDiagnosticSeverity = 'error' | 'warning';
3
+ export interface TemplateDiagnostic {
4
+ severity: TemplateDiagnosticSeverity;
5
+ message: string;
6
+ path: string;
7
+ }
8
+ export declare function validateTemplate(value: unknown, placeholders: TemplatePlaceholder[], path?: string): TemplateDiagnostic[];
9
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEtD,MAAM,MAAM,0BAA0B,GAAG,OAAO,GAAG,SAAS,CAAC;AAE7D,MAAM,WAAW,kBAAkB;IAC/B,QAAQ,EAAE,0BAA0B,CAAC;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CAChB;AA0ID,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,EAAE,IAAI,SAAK,GAAG,kBAAkB,EAAE,CAarH"}
@@ -0,0 +1,139 @@
1
+ import { tokenize } from './grammar.js';
2
+ function aliasesByListName(placeholders) {
3
+ const map = new Map();
4
+ for (const placeholder of placeholders) {
5
+ if (placeholder.kind === 'list' && placeholder.itemAlias) {
6
+ map.set(placeholder.name, placeholder.itemAlias);
7
+ }
8
+ }
9
+ return map;
10
+ }
11
+ function declaredRoots(placeholders) {
12
+ const roots = new Set();
13
+ for (const placeholder of placeholders) {
14
+ roots.add(placeholder.name.split('.')[0]);
15
+ if (placeholder.kind === 'list' && placeholder.itemAlias) {
16
+ roots.add(placeholder.itemAlias);
17
+ }
18
+ }
19
+ return roots;
20
+ }
21
+ function checkPath(path, roots, openBlocks, where, diagnostics) {
22
+ const head = path.split('.')[0];
23
+ if (head === '@index' || head === '@index0') {
24
+ if (!openBlocks.some((block) => block.kind === 'foreach')) {
25
+ diagnostics.push({
26
+ severity: 'error',
27
+ message: `{{${path}}} only works inside a {{#foreach}} block`,
28
+ path: where,
29
+ });
30
+ }
31
+ return;
32
+ }
33
+ if (roots.has(head))
34
+ return;
35
+ diagnostics.push({
36
+ severity: 'warning',
37
+ message: `{{${path}}} is not a declared placeholder, so it renders as empty text`,
38
+ path: where,
39
+ });
40
+ }
41
+ function validateString(template, placeholders, where) {
42
+ const diagnostics = [];
43
+ const aliases = aliasesByListName(placeholders);
44
+ const roots = declaredRoots(placeholders);
45
+ const open = [];
46
+ const activeRoots = () => {
47
+ const set = new Set(roots);
48
+ for (const block of open) {
49
+ if (block.alias)
50
+ set.add(block.alias);
51
+ }
52
+ return set;
53
+ };
54
+ for (const token of tokenize(template)) {
55
+ if (token.kind === 'scalar') {
56
+ checkPath(token.path, activeRoots(), open, where, diagnostics);
57
+ }
58
+ else if (token.kind === 'if') {
59
+ checkPath(token.path, activeRoots(), open, where, diagnostics);
60
+ open.push({ kind: 'if', name: token.path, sawElse: false });
61
+ }
62
+ else if (token.kind === 'else') {
63
+ const current = open[open.length - 1];
64
+ if (!current || current.kind !== 'if') {
65
+ diagnostics.push({ severity: 'error', message: '{{else}} has no matching {{#if}}', path: where });
66
+ }
67
+ else if (current.sawElse) {
68
+ diagnostics.push({
69
+ severity: 'error',
70
+ message: `{{#if:${current.name}}} has more than one {{else}}; only the first is used`,
71
+ path: where,
72
+ });
73
+ }
74
+ else {
75
+ current.sawElse = true;
76
+ }
77
+ }
78
+ else if (token.kind === 'endif') {
79
+ const current = open[open.length - 1];
80
+ if (!current || current.kind !== 'if') {
81
+ diagnostics.push({
82
+ severity: 'error',
83
+ message: '{{/if}} has no matching {{#if}}, and everything after it is discarded',
84
+ path: where,
85
+ });
86
+ }
87
+ else {
88
+ open.pop();
89
+ }
90
+ }
91
+ else if (token.kind === 'foreach') {
92
+ checkPath(token.name, activeRoots(), open, where, diagnostics);
93
+ const alias = aliases.get(token.name);
94
+ if (!alias) {
95
+ diagnostics.push({
96
+ severity: 'error',
97
+ message: `{{#foreach:${token.name}}} needs a list placeholder named "${token.name}" with an itemAlias, or rendering throws`,
98
+ path: where,
99
+ });
100
+ }
101
+ open.push({ kind: 'foreach', name: token.name, sawElse: false, alias });
102
+ }
103
+ else if (token.kind === 'endforeach') {
104
+ const current = open[open.length - 1];
105
+ if (!current || current.kind !== 'foreach') {
106
+ diagnostics.push({
107
+ severity: 'error',
108
+ message: '{{/foreach}} has no matching {{#foreach}}, and everything after it is discarded',
109
+ path: where,
110
+ });
111
+ }
112
+ else {
113
+ open.pop();
114
+ }
115
+ }
116
+ }
117
+ for (const block of open) {
118
+ const opener = block.kind === 'if' ? `{{#if:${block.name}}}` : `{{#foreach:${block.name}}}`;
119
+ diagnostics.push({
120
+ severity: 'error',
121
+ message: `${opener} is never closed with {{/${block.kind}}}`,
122
+ path: where,
123
+ });
124
+ }
125
+ return diagnostics;
126
+ }
127
+ export function validateTemplate(value, placeholders, path = '') {
128
+ if (typeof value === 'string') {
129
+ return validateString(value, placeholders, path);
130
+ }
131
+ if (Array.isArray(value)) {
132
+ return value.flatMap((entry, index) => validateTemplate(entry, placeholders, path ? `${path}.${index}` : String(index)));
133
+ }
134
+ if (value !== null && typeof value === 'object') {
135
+ return Object.entries(value).flatMap(([key, entry]) => validateTemplate(entry, placeholders, path ? `${path}.${key}` : key));
136
+ }
137
+ return [];
138
+ }
139
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shurizma/discord-message-contract",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "Shared template grammar and types for the shuri.gg Discord message builder bot contract",
5
5
  "license": "MIT",
6
6
  "type": "module",