@rhost/testkit 0.1.1 → 1.3.2

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.
Files changed (110) hide show
  1. package/README.md +393 -5
  2. package/ROADMAP.md +241 -0
  3. package/dist/benchmark.d.ts +44 -0
  4. package/dist/benchmark.d.ts.map +1 -0
  5. package/dist/benchmark.js +118 -0
  6. package/dist/benchmark.js.map +1 -0
  7. package/dist/cli/deploy.d.ts +2 -0
  8. package/dist/cli/deploy.d.ts.map +1 -0
  9. package/dist/cli/deploy.js +120 -0
  10. package/dist/cli/deploy.js.map +1 -0
  11. package/dist/cli/fmt.d.ts +2 -0
  12. package/dist/cli/fmt.d.ts.map +1 -0
  13. package/dist/cli/fmt.js +119 -0
  14. package/dist/cli/fmt.js.map +1 -0
  15. package/dist/cli/index.d.ts +3 -0
  16. package/dist/cli/index.d.ts.map +1 -0
  17. package/dist/cli/index.js +81 -0
  18. package/dist/cli/index.js.map +1 -0
  19. package/dist/cli/init.d.ts +2 -0
  20. package/dist/cli/init.d.ts.map +1 -0
  21. package/dist/cli/init.js +210 -0
  22. package/dist/cli/init.js.map +1 -0
  23. package/dist/cli/validate.d.ts +2 -0
  24. package/dist/cli/validate.d.ts.map +1 -0
  25. package/dist/cli/validate.js +126 -0
  26. package/dist/cli/validate.js.map +1 -0
  27. package/dist/cli/watch.d.ts +2 -0
  28. package/dist/cli/watch.d.ts.map +1 -0
  29. package/dist/cli/watch.js +136 -0
  30. package/dist/cli/watch.js.map +1 -0
  31. package/dist/client.d.ts +56 -0
  32. package/dist/client.d.ts.map +1 -1
  33. package/dist/client.js +117 -30
  34. package/dist/client.js.map +1 -1
  35. package/dist/container.d.ts.map +1 -1
  36. package/dist/container.js +1 -1
  37. package/dist/container.js.map +1 -1
  38. package/dist/deployer.d.ts +86 -0
  39. package/dist/deployer.d.ts.map +1 -0
  40. package/dist/deployer.js +154 -0
  41. package/dist/deployer.js.map +1 -0
  42. package/dist/expect.d.ts +27 -1
  43. package/dist/expect.d.ts.map +1 -1
  44. package/dist/expect.js +47 -2
  45. package/dist/expect.js.map +1 -1
  46. package/dist/index.d.ts +10 -3
  47. package/dist/index.d.ts.map +1 -1
  48. package/dist/index.js +39 -1
  49. package/dist/index.js.map +1 -1
  50. package/dist/preflight.d.ts +70 -0
  51. package/dist/preflight.d.ts.map +1 -0
  52. package/dist/preflight.js +121 -0
  53. package/dist/preflight.js.map +1 -0
  54. package/dist/reporter.d.ts +2 -0
  55. package/dist/reporter.d.ts.map +1 -1
  56. package/dist/reporter.js +24 -1
  57. package/dist/reporter.js.map +1 -1
  58. package/dist/runner.d.ts +83 -2
  59. package/dist/runner.d.ts.map +1 -1
  60. package/dist/runner.js +137 -8
  61. package/dist/runner.js.map +1 -1
  62. package/dist/snapshots.d.ts +84 -0
  63. package/dist/snapshots.d.ts.map +1 -0
  64. package/dist/snapshots.js +230 -0
  65. package/dist/snapshots.js.map +1 -0
  66. package/dist/validator/builtins.d.ts +18 -0
  67. package/dist/validator/builtins.d.ts.map +1 -0
  68. package/dist/validator/builtins.js +265 -0
  69. package/dist/validator/builtins.js.map +1 -0
  70. package/dist/validator/checker.d.ts +13 -0
  71. package/dist/validator/checker.d.ts.map +1 -0
  72. package/dist/validator/checker.js +111 -0
  73. package/dist/validator/checker.js.map +1 -0
  74. package/dist/validator/clobber.d.ts +7 -0
  75. package/dist/validator/clobber.d.ts.map +1 -0
  76. package/dist/validator/clobber.js +102 -0
  77. package/dist/validator/clobber.js.map +1 -0
  78. package/dist/validator/compat.d.ts +19 -0
  79. package/dist/validator/compat.d.ts.map +1 -0
  80. package/dist/validator/compat.js +58 -0
  81. package/dist/validator/compat.js.map +1 -0
  82. package/dist/validator/formatter.d.ts +21 -0
  83. package/dist/validator/formatter.d.ts.map +1 -0
  84. package/dist/validator/formatter.js +120 -0
  85. package/dist/validator/formatter.js.map +1 -0
  86. package/dist/validator/index.d.ts +57 -0
  87. package/dist/validator/index.d.ts.map +1 -0
  88. package/dist/validator/index.js +133 -0
  89. package/dist/validator/index.js.map +1 -0
  90. package/dist/validator/parser.d.ts +16 -0
  91. package/dist/validator/parser.d.ts.map +1 -0
  92. package/dist/validator/parser.js +260 -0
  93. package/dist/validator/parser.js.map +1 -0
  94. package/dist/validator/tokenizer.d.ts +28 -0
  95. package/dist/validator/tokenizer.d.ts.map +1 -0
  96. package/dist/validator/tokenizer.js +174 -0
  97. package/dist/validator/tokenizer.js.map +1 -0
  98. package/dist/validator/types.d.ts +55 -0
  99. package/dist/validator/types.d.ts.map +1 -0
  100. package/dist/validator/types.js +6 -0
  101. package/dist/validator/types.js.map +1 -0
  102. package/dist/watcher.d.ts +44 -0
  103. package/dist/watcher.d.ts.map +1 -0
  104. package/dist/watcher.js +297 -0
  105. package/dist/watcher.js.map +1 -0
  106. package/dist/world.d.ts +79 -0
  107. package/dist/world.d.ts.map +1 -1
  108. package/dist/world.js +167 -1
  109. package/dist/world.js.map +1 -1
  110. package/package.json +19 -3
@@ -0,0 +1,260 @@
1
+ "use strict";
2
+ // ---------------------------------------------------------------------------
3
+ // RhostMUSH softcode recursive-descent parser
4
+ // ---------------------------------------------------------------------------
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.parse = parse;
7
+ // ---------------------------------------------------------------------------
8
+ // Parser class
9
+ // ---------------------------------------------------------------------------
10
+ class Parser {
11
+ constructor(tokens) {
12
+ this.tokens = tokens;
13
+ this.pos = 0;
14
+ this.diagnostics = [];
15
+ }
16
+ peek() {
17
+ return this.pos < this.tokens.length ? this.tokens[this.pos] : null;
18
+ }
19
+ consume() {
20
+ if (this.pos >= this.tokens.length) {
21
+ throw new Error('Unexpected end of token stream');
22
+ }
23
+ return this.tokens[this.pos++];
24
+ }
25
+ // -------------------------------------------------------------------------
26
+ // Top-level entry point
27
+ // -------------------------------------------------------------------------
28
+ /**
29
+ * Parse the entire token stream as a top-level expression.
30
+ * Stray `)` and `]` at the top level are reported as errors.
31
+ * Top-level commas are treated as literal text.
32
+ */
33
+ parseTopLevel() {
34
+ const nodes = [];
35
+ while (this.peek() !== null) {
36
+ const tok = this.peek();
37
+ if (tok.type === 'RPAREN') {
38
+ this.diagnostics.push({
39
+ severity: 'error',
40
+ code: 'E002',
41
+ message: `Unexpected ')' — no matching '(' was opened`,
42
+ offset: tok.offset,
43
+ length: 1,
44
+ });
45
+ this.consume();
46
+ continue;
47
+ }
48
+ if (tok.type === 'RBRACKET') {
49
+ this.diagnostics.push({
50
+ severity: 'error',
51
+ code: 'E004',
52
+ message: `Unexpected ']' — no matching '[' was opened`,
53
+ offset: tok.offset,
54
+ length: 1,
55
+ });
56
+ this.consume();
57
+ continue;
58
+ }
59
+ if (tok.type === 'COMMA') {
60
+ // Top-level comma is literal text (not inside a function call)
61
+ nodes.push({ type: 'RawText', value: ',', offset: tok.offset });
62
+ this.consume();
63
+ continue;
64
+ }
65
+ const node = this.parseNode();
66
+ if (node !== null)
67
+ nodes.push(node);
68
+ }
69
+ return nodes;
70
+ }
71
+ // -------------------------------------------------------------------------
72
+ // Node dispatch
73
+ // -------------------------------------------------------------------------
74
+ /**
75
+ * Parse a single AST node. Returns null only at EOF.
76
+ */
77
+ parseNode() {
78
+ const tok = this.peek();
79
+ if (tok === null)
80
+ return null;
81
+ if (tok.type === 'FNAME') {
82
+ return this.parseFunctionCall();
83
+ }
84
+ if (tok.type === 'LBRACKET') {
85
+ return this.parseBracketEval();
86
+ }
87
+ if (tok.type === 'SUBST') {
88
+ this.consume();
89
+ return { type: 'Substitution', raw: tok.value, offset: tok.offset };
90
+ }
91
+ // ESC, TEXT, LPAREN (bare paren not after identifier), anything else → RawText
92
+ this.consume();
93
+ return { type: 'RawText', value: tok.value, offset: tok.offset };
94
+ }
95
+ // -------------------------------------------------------------------------
96
+ // Function call: FNAME LPAREN [arglist] RPAREN
97
+ // -------------------------------------------------------------------------
98
+ parseFunctionCall() {
99
+ const nameTok = this.consume(); // FNAME
100
+ // Tokenizer guarantees LPAREN immediately follows FNAME
101
+ const lparenTok = this.consume(); // LPAREN
102
+ const node = {
103
+ type: 'FunctionCall',
104
+ name: nameTok.value,
105
+ nameOffset: nameTok.offset,
106
+ args: [],
107
+ offset: lparenTok.offset,
108
+ };
109
+ // Immediately closed: name()
110
+ if (this.peek()?.type === 'RPAREN') {
111
+ this.consume();
112
+ return node;
113
+ }
114
+ // EOF immediately after opening paren
115
+ if (this.peek() === null) {
116
+ this.diagnostics.push(this.unclosedParen(nameTok.value, lparenTok.offset));
117
+ return node;
118
+ }
119
+ // Parse comma-separated argument expressions
120
+ while (true) {
121
+ const arg = this.parseArgExpression(nameTok.value);
122
+ node.args.push(arg);
123
+ const next = this.peek();
124
+ if (next === null) {
125
+ // EOF without closing paren
126
+ this.diagnostics.push(this.unclosedParen(nameTok.value, lparenTok.offset));
127
+ break;
128
+ }
129
+ if (next.type === 'RPAREN') {
130
+ this.consume();
131
+ break;
132
+ }
133
+ if (next.type === 'COMMA') {
134
+ this.consume();
135
+ // Trailing comma — push one final empty arg and close on next RPAREN
136
+ if (this.peek()?.type === 'RPAREN') {
137
+ node.args.push([]);
138
+ this.consume();
139
+ break;
140
+ }
141
+ if (this.peek() === null) {
142
+ node.args.push([]);
143
+ this.diagnostics.push(this.unclosedParen(nameTok.value, lparenTok.offset));
144
+ break;
145
+ }
146
+ continue;
147
+ }
148
+ // Unexpected token — shouldn't happen with a well-formed token stream
149
+ break;
150
+ }
151
+ return node;
152
+ }
153
+ /**
154
+ * Parse one argument expression, stopping (without consuming) at a
155
+ * COMMA or RPAREN that closes the current function call.
156
+ * LBRACKET/RBRACKET are handled recursively by parseBracketEval, so
157
+ * they do NOT prematurely end an argument even if they contain commas.
158
+ */
159
+ parseArgExpression(funcName) {
160
+ const nodes = [];
161
+ while (true) {
162
+ const tok = this.peek();
163
+ if (tok === null)
164
+ break;
165
+ // These end the current argument
166
+ if (tok.type === 'COMMA' || tok.type === 'RPAREN')
167
+ break;
168
+ // Stray ] inside a function argument
169
+ if (tok.type === 'RBRACKET') {
170
+ this.diagnostics.push({
171
+ severity: 'error',
172
+ code: 'E004',
173
+ message: `Unexpected ']' inside argument of '${funcName}'`,
174
+ offset: tok.offset,
175
+ length: 1,
176
+ });
177
+ this.consume();
178
+ continue;
179
+ }
180
+ const node = this.parseNode();
181
+ if (node !== null)
182
+ nodes.push(node);
183
+ }
184
+ return nodes;
185
+ }
186
+ // -------------------------------------------------------------------------
187
+ // Bracket eval: LBRACKET expression RBRACKET
188
+ // -------------------------------------------------------------------------
189
+ parseBracketEval() {
190
+ const lbracketTok = this.consume(); // LBRACKET
191
+ const innerNodes = [];
192
+ while (true) {
193
+ const tok = this.peek();
194
+ if (tok === null) {
195
+ this.diagnostics.push({
196
+ severity: 'error',
197
+ code: 'E003',
198
+ message: `Unclosed '[' — missing closing ']'`,
199
+ offset: lbracketTok.offset,
200
+ length: 1,
201
+ });
202
+ break;
203
+ }
204
+ if (tok.type === 'RBRACKET') {
205
+ this.consume();
206
+ break;
207
+ }
208
+ // Stray ) inside a bracket eval at the bracket's top level
209
+ if (tok.type === 'RPAREN') {
210
+ this.diagnostics.push({
211
+ severity: 'error',
212
+ code: 'E002',
213
+ message: `Unexpected ')' inside '[ ]' expression`,
214
+ offset: tok.offset,
215
+ length: 1,
216
+ });
217
+ this.consume();
218
+ continue;
219
+ }
220
+ // Top-level comma inside a bracket eval is literal text
221
+ if (tok.type === 'COMMA') {
222
+ innerNodes.push({ type: 'RawText', value: ',', offset: tok.offset });
223
+ this.consume();
224
+ continue;
225
+ }
226
+ const node = this.parseNode();
227
+ if (node !== null)
228
+ innerNodes.push(node);
229
+ }
230
+ return { type: 'BracketEval', nodes: innerNodes, offset: lbracketTok.offset };
231
+ }
232
+ // -------------------------------------------------------------------------
233
+ // Helpers
234
+ // -------------------------------------------------------------------------
235
+ unclosedParen(funcName, offset) {
236
+ return {
237
+ severity: 'error',
238
+ code: 'E001',
239
+ message: `Unclosed '(' for function '${funcName}' — missing closing ')'`,
240
+ offset,
241
+ length: 1,
242
+ };
243
+ }
244
+ }
245
+ // ---------------------------------------------------------------------------
246
+ // Public entry point
247
+ // ---------------------------------------------------------------------------
248
+ /**
249
+ * Parse a flat token stream into an AST.
250
+ *
251
+ * Structural errors (unbalanced parens/brackets) are embedded in `diagnostics`.
252
+ * The returned `nodes` array always contains the best-effort parse even when
253
+ * there are errors, enabling downstream semantic checks to still run.
254
+ */
255
+ function parse(tokens) {
256
+ const parser = new Parser(tokens);
257
+ const nodes = parser.parseTopLevel();
258
+ return { nodes, diagnostics: parser.diagnostics };
259
+ }
260
+ //# sourceMappingURL=parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.js","sourceRoot":"","sources":["../../src/validator/parser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ export type TokenType = 'FNAME' | 'LPAREN' | 'RPAREN' | 'COMMA' | 'LBRACKET' | 'RBRACKET' | 'SUBST' | 'ESC' | 'TEXT';
2
+ export interface Token {
3
+ type: TokenType;
4
+ /** The raw text of this token as it appears in the source */
5
+ value: string;
6
+ /** Character offset in the original expression string */
7
+ offset: number;
8
+ }
9
+ /**
10
+ * Tokenize a RhostMUSH softcode expression into a flat token stream.
11
+ *
12
+ * Rules (in priority order):
13
+ * 1. `\X` → ESC(X) — escape always wins
14
+ * 2. `%%` → TEXT('%') — literal percent
15
+ * 3. `%qX` / `%iX` → SUBST('%qX') — 3-char register subst
16
+ * 4. `%X` (known) → SUBST('%X') — 2-char substitution
17
+ * 5. `%` (unknown) → TEXT('%') — treat as literal
18
+ * 6. `[` → LBRACKET
19
+ * 7. `]` → RBRACKET
20
+ * 8. `)` → RPAREN
21
+ * 9. `,` → COMMA
22
+ * 10. `word(` → FNAME(word) + LPAREN('(')
23
+ * 11. `word` → TEXT(word) — identifier not before (
24
+ * 12. `(` → TEXT('(') — ( not after identifier
25
+ * 13. <run> → TEXT(run) — anything else
26
+ */
27
+ export declare function tokenize(input: string): Token[];
28
+ //# sourceMappingURL=tokenizer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokenizer.d.ts","sourceRoot":"","sources":["../../src/validator/tokenizer.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,SAAS,GACjB,OAAO,GACP,QAAQ,GACR,QAAQ,GACR,OAAO,GACP,UAAU,GACV,UAAU,GACV,OAAO,GACP,KAAK,GACL,MAAM,CAAC;AAEX,MAAM,WAAW,KAAK;IACpB,IAAI,EAAE,SAAS,CAAC;IAChB,6DAA6D;IAC7D,KAAK,EAAE,MAAM,CAAC;IACd,yDAAyD;IACzD,MAAM,EAAE,MAAM,CAAC;CAChB;AAsCD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,EAAE,CAoI/C"}
@@ -0,0 +1,174 @@
1
+ "use strict";
2
+ // ---------------------------------------------------------------------------
3
+ // RhostMUSH softcode tokenizer
4
+ // ---------------------------------------------------------------------------
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.tokenize = tokenize;
7
+ // Characters that end a plain-text accumulation run.
8
+ // These must be checked before starting a new TEXT run.
9
+ const STRUCTURAL_RE = /[\\%\[\]()\,,a-zA-Z_]/;
10
+ function isIdentStart(ch) {
11
+ const c = ch.charCodeAt(0);
12
+ return (c >= 65 && c <= 90) || (c >= 97 && c <= 122) || c === 95; // A-Z, a-z, _
13
+ }
14
+ function isIdentChar(ch) {
15
+ const c = ch.charCodeAt(0);
16
+ return ((c >= 65 && c <= 90) ||
17
+ (c >= 97 && c <= 122) ||
18
+ (c >= 48 && c <= 57) ||
19
+ c === 95); // A-Z, a-z, 0-9, _
20
+ }
21
+ /**
22
+ * Single-character percent substitutions recognised by RhostMUSH.
23
+ * Anything else after % is treated as a literal %, not a substitution.
24
+ */
25
+ const SINGLE_CHAR_SUBST = new Set([
26
+ '#', 'N', 'n', 'L', 'l', 'P', 'p', 'T', 't', 'B', 'b',
27
+ 'R', 'r', 'A', 'a', 'O', 'o', 'S', 's', 'X', 'x', '+',
28
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
29
+ 'C', 'c', // ANSI carry
30
+ 'v', 'V', // attribute value
31
+ 'w', 'W', // attribute name
32
+ 'u', 'U', // room name
33
+ 'f', 'F', // from
34
+ 'k', 'K', // money
35
+ 'm', 'M', // last entered command
36
+ ]);
37
+ /**
38
+ * Tokenize a RhostMUSH softcode expression into a flat token stream.
39
+ *
40
+ * Rules (in priority order):
41
+ * 1. `\X` → ESC(X) — escape always wins
42
+ * 2. `%%` → TEXT('%') — literal percent
43
+ * 3. `%qX` / `%iX` → SUBST('%qX') — 3-char register subst
44
+ * 4. `%X` (known) → SUBST('%X') — 2-char substitution
45
+ * 5. `%` (unknown) → TEXT('%') — treat as literal
46
+ * 6. `[` → LBRACKET
47
+ * 7. `]` → RBRACKET
48
+ * 8. `)` → RPAREN
49
+ * 9. `,` → COMMA
50
+ * 10. `word(` → FNAME(word) + LPAREN('(')
51
+ * 11. `word` → TEXT(word) — identifier not before (
52
+ * 12. `(` → TEXT('(') — ( not after identifier
53
+ * 13. <run> → TEXT(run) — anything else
54
+ */
55
+ function tokenize(input) {
56
+ const tokens = [];
57
+ let pos = 0;
58
+ const len = input.length;
59
+ while (pos < len) {
60
+ const ch = input[pos];
61
+ // ---- 1. Backslash escape ----
62
+ if (ch === '\\') {
63
+ if (pos + 1 < len) {
64
+ tokens.push({ type: 'ESC', value: input[pos + 1], offset: pos });
65
+ pos += 2;
66
+ }
67
+ else {
68
+ // Trailing backslash: literal backslash
69
+ tokens.push({ type: 'TEXT', value: '\\', offset: pos });
70
+ pos++;
71
+ }
72
+ continue;
73
+ }
74
+ // ---- 2–5. Percent substitutions ----
75
+ if (ch === '%') {
76
+ if (pos + 1 >= len) {
77
+ tokens.push({ type: 'TEXT', value: '%', offset: pos });
78
+ pos++;
79
+ continue;
80
+ }
81
+ const next = input[pos + 1];
82
+ // %% → literal %
83
+ if (next === '%') {
84
+ tokens.push({ type: 'TEXT', value: '%', offset: pos });
85
+ pos += 2;
86
+ continue;
87
+ }
88
+ // %q0–%q9, %qA–%qZ (q-register), %i0–%i9 (iter var) → 3-char subst
89
+ if ((next === 'q' || next === 'Q' || next === 'i' || next === 'I') &&
90
+ pos + 2 < len && /[0-9a-zA-Z]/.test(input[pos + 2])) {
91
+ tokens.push({ type: 'SUBST', value: input.slice(pos, pos + 3), offset: pos });
92
+ pos += 3;
93
+ continue;
94
+ }
95
+ // Single-char known substitutions
96
+ if (SINGLE_CHAR_SUBST.has(next)) {
97
+ tokens.push({ type: 'SUBST', value: input.slice(pos, pos + 2), offset: pos });
98
+ pos += 2;
99
+ continue;
100
+ }
101
+ // Unknown %X: literal %
102
+ tokens.push({ type: 'TEXT', value: '%', offset: pos });
103
+ pos++;
104
+ continue;
105
+ }
106
+ // ---- 6–9. Single structural characters ----
107
+ if (ch === '[') {
108
+ tokens.push({ type: 'LBRACKET', value: '[', offset: pos });
109
+ pos++;
110
+ continue;
111
+ }
112
+ if (ch === ']') {
113
+ tokens.push({ type: 'RBRACKET', value: ']', offset: pos });
114
+ pos++;
115
+ continue;
116
+ }
117
+ if (ch === ')') {
118
+ tokens.push({ type: 'RPAREN', value: ')', offset: pos });
119
+ pos++;
120
+ continue;
121
+ }
122
+ if (ch === ',') {
123
+ tokens.push({ type: 'COMMA', value: ',', offset: pos });
124
+ pos++;
125
+ continue;
126
+ }
127
+ // ---- 10–11. Identifier: FNAME+LPAREN or plain TEXT ----
128
+ if (isIdentStart(ch)) {
129
+ const start = pos;
130
+ while (pos < len && isIdentChar(input[pos])) {
131
+ pos++;
132
+ }
133
+ const word = input.slice(start, pos);
134
+ if (pos < len && input[pos] === '(') {
135
+ // Function call: emit FNAME then LPAREN
136
+ tokens.push({ type: 'FNAME', value: word, offset: start });
137
+ tokens.push({ type: 'LPAREN', value: '(', offset: pos });
138
+ pos++;
139
+ }
140
+ else {
141
+ // Bare identifier — text
142
+ tokens.push({ type: 'TEXT', value: word, offset: start });
143
+ }
144
+ continue;
145
+ }
146
+ // ---- 12. '(' not after an identifier: literal ----
147
+ if (ch === '(') {
148
+ tokens.push({ type: 'TEXT', value: '(', offset: pos });
149
+ pos++;
150
+ continue;
151
+ }
152
+ // ---- 13. Plain text accumulation ----
153
+ {
154
+ const start = pos;
155
+ while (pos < len) {
156
+ const c = input[pos];
157
+ // Stop at any character that starts a higher-priority rule
158
+ if (c === '\\' || c === '%' ||
159
+ c === '[' || c === ']' ||
160
+ c === '(' || c === ')' ||
161
+ c === ',' ||
162
+ isIdentStart(c)) {
163
+ break;
164
+ }
165
+ pos++;
166
+ }
167
+ if (pos > start) {
168
+ tokens.push({ type: 'TEXT', value: input.slice(start, pos), offset: start });
169
+ }
170
+ }
171
+ }
172
+ return tokens;
173
+ }
174
+ //# sourceMappingURL=tokenizer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokenizer.js","sourceRoot":"","sources":["../../src/validator/tokenizer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ export type Severity = 'error' | 'warning';
2
+ /** A single diagnostic produced by the validator. */
3
+ export interface Diagnostic {
4
+ severity: Severity;
5
+ /** Error/warning code, e.g. 'E001', 'W002' */
6
+ code: string;
7
+ message: string;
8
+ /** Character offset in the original expression string */
9
+ offset: number;
10
+ /** Span length in characters (0 = point diagnostic) */
11
+ length: number;
12
+ }
13
+ /** Result returned by `validate()` */
14
+ export interface ValidationResult {
15
+ /** false if any diagnostic has severity 'error' */
16
+ valid: boolean;
17
+ diagnostics: Diagnostic[];
18
+ }
19
+ export type ASTNode = FunctionCallNode | BracketEvalNode | SubstitutionNode | RawTextNode;
20
+ /** A function call: `name(arg1, arg2, ...)` */
21
+ export interface FunctionCallNode {
22
+ type: 'FunctionCall';
23
+ /** Original casing preserved from source */
24
+ name: string;
25
+ /** Offset of the first character of the name */
26
+ nameOffset: number;
27
+ /**
28
+ * Each element is the list of nodes forming one argument.
29
+ * An empty inner array means an empty/missing argument.
30
+ */
31
+ args: ASTNode[][];
32
+ /** Offset of the opening parenthesis */
33
+ offset: number;
34
+ }
35
+ /** An inline evaluation: `[expression]` */
36
+ export interface BracketEvalNode {
37
+ type: 'BracketEval';
38
+ nodes: ASTNode[];
39
+ /** Offset of the '[' */
40
+ offset: number;
41
+ }
42
+ /** A percent-substitution: `%#`, `%N`, `%q0`, `%0`–`%9`, `%%`, etc. */
43
+ export interface SubstitutionNode {
44
+ type: 'Substitution';
45
+ /** The raw substitution text as it appears in the source */
46
+ raw: string;
47
+ offset: number;
48
+ }
49
+ /** Literal text or an escaped character that requires no further analysis */
50
+ export interface RawTextNode {
51
+ type: 'RawText';
52
+ value: string;
53
+ offset: number;
54
+ }
55
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/validator/types.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAC;AAE3C,qDAAqD;AACrD,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,QAAQ,CAAC;IACnB,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,yDAAyD;IACzD,MAAM,EAAE,MAAM,CAAC;IACf,uDAAuD;IACvD,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,sCAAsC;AACtC,MAAM,WAAW,gBAAgB;IAC/B,mDAAmD;IACnD,KAAK,EAAE,OAAO,CAAC;IACf,WAAW,EAAE,UAAU,EAAE,CAAC;CAC3B;AAMD,MAAM,MAAM,OAAO,GACf,gBAAgB,GAChB,eAAe,GACf,gBAAgB,GAChB,WAAW,CAAC;AAEhB,+CAA+C;AAC/C,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,cAAc,CAAC;IACrB,4CAA4C;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,gDAAgD;IAChD,UAAU,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,IAAI,EAAE,OAAO,EAAE,EAAE,CAAC;IAClB,wCAAwC;IACxC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,2CAA2C;AAC3C,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,aAAa,CAAC;IACpB,KAAK,EAAE,OAAO,EAAE,CAAC;IACjB,wBAAwB;IACxB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,uEAAuE;AACvE,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,cAAc,CAAC;IACrB,4DAA4D;IAC5D,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,6EAA6E;AAC7E,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB"}
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ // ---------------------------------------------------------------------------
3
+ // Shared types for the RhostMUSH softcode validator
4
+ // ---------------------------------------------------------------------------
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/validator/types.ts"],"names":[],"mappings":";AAAA,8EAA8E;AAC9E,oDAAoD;AACpD,8EAA8E"}
@@ -0,0 +1,44 @@
1
+ export interface WatchOptions {
2
+ /** Absolute paths of test files to watch and run */
3
+ files: string[];
4
+ /** Debounce delay in ms before re-running after a change. Default: 300 */
5
+ debounceMs?: number;
6
+ /** Clear terminal between runs. Default: true */
7
+ clearScreen?: boolean;
8
+ }
9
+ export declare class RhostWatcher {
10
+ private readonly options;
11
+ private readonly debounceMs;
12
+ private readonly clearScreen;
13
+ private watcher;
14
+ private debounceTimer;
15
+ private pendingFiles;
16
+ private currentProcess;
17
+ private stopped;
18
+ private runCount;
19
+ constructor(options: WatchOptions);
20
+ /**
21
+ * Start watching. Runs an initial pass immediately, then re-runs on change.
22
+ * Blocks until `stop()` is called (or SIGINT).
23
+ */
24
+ start(): Promise<void>;
25
+ /** Graceful shutdown: cancel pending timers, close watcher, kill child. */
26
+ stop(): Promise<void>;
27
+ private setupWatcher;
28
+ private runFiles;
29
+ private spawnFile;
30
+ private printBanner;
31
+ private printRunHeader;
32
+ /**
33
+ * Find the longest common directory path shared by all given dirs.
34
+ * Falls back to '/' (or drive root on Windows) if nothing in common.
35
+ */
36
+ private commonRoot;
37
+ }
38
+ /**
39
+ * Recursively walk `rootDir` and return all test files matching
40
+ * `*.test.ts`, `*.test.js`, `*.spec.ts`, `*.spec.js`.
41
+ * Skips `node_modules`, `dist`, `.git`, `.next`, `.turbo`.
42
+ */
43
+ export declare function discoverTestFiles(rootDir: string): string[];
44
+ //# sourceMappingURL=watcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watcher.d.ts","sourceRoot":"","sources":["../src/watcher.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,YAAY;IAC3B,oDAAoD;IACpD,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,0EAA0E;IAC1E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iDAAiD;IACjD,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAMD,qBAAa,YAAY;IAWX,OAAO,CAAC,QAAQ,CAAC,OAAO;IAVpC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAU;IAEtC,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,aAAa,CAA+B;IACpD,OAAO,CAAC,YAAY,CAA0B;IAC9C,OAAO,CAAC,cAAc,CAA6B;IACnD,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAK;gBAEQ,OAAO,EAAE,YAAY;IAKlD;;;OAGG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA+B5B,2EAA2E;IACrE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IA2B3B,OAAO,CAAC,YAAY;YA2CN,QAAQ;IAuBtB,OAAO,CAAC,SAAS;IAyCjB,OAAO,CAAC,WAAW;IAanB,OAAO,CAAC,cAAc;IAqBtB;;;OAGG;IACH,OAAO,CAAC,UAAU;CAoBnB;AAMD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAI3D"}