botql 1.0.1 → 1.1.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.
@@ -0,0 +1,1335 @@
1
+ "use strict";
2
+ var BotQL = (() => {
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
5
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
6
+ }) : x)(function(x) {
7
+ if (typeof require !== "undefined") return require.apply(this, arguments);
8
+ throw Error('Dynamic require of "' + x + '" is not supported');
9
+ });
10
+ var __commonJS = (cb, mod) => function __require2() {
11
+ try {
12
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
13
+ } catch (e) {
14
+ throw mod = 0, e;
15
+ }
16
+ };
17
+
18
+ // Parser.js
19
+ var require_Parser = __commonJS({
20
+ "Parser.js"(exports, module) {
21
+ "use strict";
22
+ var TokenType = { KEYWORD: "KEYWORD", STRING: "STRING", NUMBER: "NUMBER", IDENT: "IDENT", SYMBOL: "SYMBOL", EOF: "EOF" };
23
+ var KEYWORDS = /* @__PURE__ */ new Set(["CREATE", "BOT", "PLATFORM", "CONNECT", "TABLE", "PREVENT", "DEFAULT", "ON", "START", "MESSAGE", "FROM", "WHEN", "CONTAINS", "OR", "OTHERWISE", "KEYWORDS", "THINK", "WAITING", "REPLY", "TO", "FORWARD", "PARSE", "SEND", "INSERT", "INTO", "VALUES", "UPDATE", "SET", "WHERE", "RUN", "IMPORT", "AS", "RESPONSE"]);
24
+ var Token = class {
25
+ constructor(e, t, s) {
26
+ this.type = e, this.value = t, this.line = s;
27
+ }
28
+ };
29
+ var Tokenizer = class {
30
+ constructor(e) {
31
+ this.source = e, this.pos = 0, this.line = 1, this.tokens = [];
32
+ }
33
+ error(e) {
34
+ throw Error(`syntax error: ${e}`);
35
+ }
36
+ peekChar(e = 0) {
37
+ return this.source[this.pos + e];
38
+ }
39
+ tokenize() {
40
+ for (; this.pos < this.source.length; ) {
41
+ let e = this.peekChar();
42
+ if ("\n" === e) {
43
+ this.line++, this.pos++;
44
+ continue;
45
+ }
46
+ if (/\s/.test(e)) {
47
+ this.pos++;
48
+ continue;
49
+ }
50
+ if ("-" === e && "-" === this.peekChar(1)) {
51
+ for (; this.pos < this.source.length && "\n" !== this.peekChar(); ) this.pos++;
52
+ continue;
53
+ }
54
+ if ('"' === e || "'" === e) {
55
+ this.tokens.push(this.readString(e));
56
+ continue;
57
+ }
58
+ if (/[0-9]/.test(e)) {
59
+ this.tokens.push(this.readNumber());
60
+ continue;
61
+ }
62
+ if ("{}(),.=+*;".includes(e)) {
63
+ this.tokens.push(new Token(TokenType.SYMBOL, e, this.line)), this.pos++;
64
+ continue;
65
+ }
66
+ if (/[A-Za-z_]/.test(e)) {
67
+ let t = this.readWord();
68
+ this.tokens.push(t);
69
+ continue;
70
+ }
71
+ this.error(`unexpected character: "${e}"`);
72
+ }
73
+ return this.tokens.push(new Token(TokenType.EOF, null, this.line)), this.tokens;
74
+ }
75
+ readString(e) {
76
+ let t = this.line;
77
+ this.pos++;
78
+ let s = "";
79
+ for (; this.pos < this.source.length && this.peekChar() !== e; ) {
80
+ if ("\\" === this.peekChar()) {
81
+ this.pos++, s += this.peekChar(), this.pos++;
82
+ continue;
83
+ }
84
+ "\n" === this.peekChar() && this.line++, s += this.peekChar(), this.pos++;
85
+ }
86
+ return this.peekChar() !== e && this.error("unterminated string"), this.pos++, new Token(TokenType.STRING, s, t);
87
+ }
88
+ readNumber() {
89
+ let e = this.line, t = "";
90
+ for (; this.pos < this.source.length && /[0-9.]/.test(this.peekChar()); ) t += this.peekChar(), this.pos++;
91
+ return new Token(TokenType.NUMBER, Number(t), e);
92
+ }
93
+ readWord() {
94
+ let e = this.line, t = "";
95
+ for (; this.pos < this.source.length && /[A-Za-z0-9_]/.test(this.peekChar()); ) t += this.peekChar(), this.pos++;
96
+ let s = t.toUpperCase();
97
+ return KEYWORDS.has(s) && t === s ? new Token(TokenType.KEYWORD, s, e) : new Token(TokenType.IDENT, t, e);
98
+ }
99
+ };
100
+ var Parser = class {
101
+ constructor(e) {
102
+ this.tokens = new Tokenizer(e).tokenize(), this.pos = 0;
103
+ }
104
+ error(e) {
105
+ let t = this.current();
106
+ throw Error(`syntax error: ${e}, near "${t ? t.value : "EOF"}"`);
107
+ }
108
+ current() {
109
+ return this.tokens[this.pos];
110
+ }
111
+ at(e, t) {
112
+ let s = this.current();
113
+ return s.type === e && (void 0 === t || s.value === t);
114
+ }
115
+ atKeyword(...e) {
116
+ return this.at(TokenType.KEYWORD) && e.includes(this.current().value);
117
+ }
118
+ atWord(...e) {
119
+ let t = this.current();
120
+ return (t.type === TokenType.KEYWORD || t.type === TokenType.IDENT) && e.includes(t.value);
121
+ }
122
+ advance() {
123
+ let e = this.current();
124
+ return e.type !== TokenType.EOF && this.pos++, e;
125
+ }
126
+ expect(e, t) {
127
+ return this.at(e, t) || this.error(`expected ${t || e}`), this.advance();
128
+ }
129
+ expectKeyword(e) {
130
+ return this.expect(TokenType.KEYWORD, e);
131
+ }
132
+ isAtEnd() {
133
+ return this.at(TokenType.EOF);
134
+ }
135
+ parseProgram() {
136
+ let e = [];
137
+ for (; !this.isAtEnd(); ) e.push(this.parseStatement());
138
+ return { type: "Program", body: e };
139
+ }
140
+ parseBody() {
141
+ if (this.at(TokenType.SYMBOL, "{")) {
142
+ this.advance();
143
+ let e = [];
144
+ for (; !this.at(TokenType.SYMBOL, "}"); ) this.isAtEnd() && this.error('unclosed block "{"'), e.push(this.parseStatement());
145
+ return this.advance(), e;
146
+ }
147
+ return [this.parseStatement()];
148
+ }
149
+ parseStatement() {
150
+ return this.atKeyword("CREATE") ? this.parseCreate() : this.atKeyword("PLATFORM") ? this.parsePlatform() : this.atKeyword("CONNECT") ? this.parseConnect() : this.atKeyword("ON") ? this.parseOn() : this.atKeyword("WHEN") ? this.parseWhen() : this.atKeyword("OTHERWISE") ? this.parseOtherwise() : this.atKeyword("REPLY") ? this.parseReply() : this.atKeyword("THINK") ? this.parseThink() : this.atKeyword("FORWARD") ? this.parseForward() : this.atKeyword("PARSE") ? this.parseParseSignal() : this.atKeyword("SEND") ? this.parseSend() : this.atKeyword("INSERT") ? this.parseInsert() : this.atKeyword("UPDATE") ? this.parseUpdate() : this.atKeyword("RUN") ? this.parseRunBot() : this.atKeyword("IMPORT") ? this.parseImport() : void this.error("unexpected statement");
151
+ }
152
+ parseCreate() {
153
+ if (this.expectKeyword("CREATE"), this.atKeyword("BOT")) {
154
+ this.advance();
155
+ let e = this.expect(TokenType.STRING).value;
156
+ return { type: "CreateBot", name: e };
157
+ }
158
+ if (this.atKeyword("TABLE")) {
159
+ this.advance();
160
+ let t = this.expect(TokenType.IDENT).value, s = this.parseColumnList(), r = this.tryParsePreventDefault(), i = this.tryParseDefaultMessage();
161
+ return { type: "CreateTable", name: t, columns: s, preventDefault: r, defaultMessage: i };
162
+ }
163
+ this.error("expected BOT or TABLE after CREATE");
164
+ }
165
+ parseColumnList() {
166
+ this.expect(TokenType.SYMBOL, "(");
167
+ let e = [];
168
+ for (; !this.at(TokenType.SYMBOL, ")"); ) {
169
+ let t = this.expect(TokenType.IDENT).value, s = [], r = null;
170
+ for (; this.at(TokenType.IDENT) && !this.at(TokenType.SYMBOL, ",") && !this.at(TokenType.SYMBOL, ")"); ) {
171
+ let i = this.advance().value;
172
+ r ? s.push(i) : r = i;
173
+ }
174
+ e.push({ name: t, columnType: r, constraints: s }), this.at(TokenType.SYMBOL, ",") && this.advance();
175
+ }
176
+ return this.expect(TokenType.SYMBOL, ")"), e;
177
+ }
178
+ tryParsePreventDefault() {
179
+ return !!this.atKeyword("PREVENT") && (this.advance(), this.expectKeyword("DEFAULT"), true);
180
+ }
181
+ tryParseDefaultMessage() {
182
+ if (!this.atKeyword("DEFAULT")) return null;
183
+ let e = this.tokens[this.pos + 1];
184
+ if (!e || e.type !== TokenType.KEYWORD || "MESSAGE" !== e.value) return null;
185
+ if (this.advance(), this.advance(), this.isReplyFileForm()) {
186
+ this.expect(TokenType.SYMBOL, "(");
187
+ let t = this.parseFileName();
188
+ this.expect(TokenType.SYMBOL, ",");
189
+ let s = this.parseExpression();
190
+ return this.expect(TokenType.SYMBOL, ")"), { file: t, index: s };
191
+ }
192
+ let r = this.parseExpression();
193
+ return { value: r };
194
+ }
195
+ parsePlatform() {
196
+ this.expectKeyword("PLATFORM");
197
+ let e = this.advance().value;
198
+ return { type: "Platform", value: e };
199
+ }
200
+ parseConnect() {
201
+ if (this.expectKeyword("CONNECT"), this.atKeyword("RESPONSE")) {
202
+ this.advance();
203
+ let e = this.expect(TokenType.IDENT).value;
204
+ return { type: "ConnectResponse", alias: e };
205
+ }
206
+ let t = this.advance().value, s = this.advance().value, r = this.expect(TokenType.STRING).value;
207
+ return { type: "Connect", service: t, kind: s, credential: r };
208
+ }
209
+ parseOn() {
210
+ if (this.expectKeyword("ON"), this.atKeyword("START")) {
211
+ this.advance();
212
+ let e = this.parseBody();
213
+ return { type: "On", event: "START", body: e };
214
+ }
215
+ if (this.atKeyword("MESSAGE")) {
216
+ this.advance();
217
+ let t = this.parseBody();
218
+ return { type: "On", event: "MESSAGE", body: t };
219
+ }
220
+ if (this.atWord("SIGNAL")) {
221
+ this.advance(), this.expectKeyword("FROM");
222
+ let s = this.expect(TokenType.STRING).value, r = this.parseBody();
223
+ return { type: "On", event: "SIGNAL", source: s, body: r };
224
+ }
225
+ this.error("unexpected event after ON");
226
+ }
227
+ parseWhen() {
228
+ this.expectKeyword("WHEN");
229
+ let e = [this.parseCondition()];
230
+ for (; this.atKeyword("OR"); ) this.advance(), e.push(this.parseCondition());
231
+ let t = this.parseBody();
232
+ return { type: "When", conditions: e, body: t };
233
+ }
234
+ parseCondition() {
235
+ if (this.expectKeyword("CONTAINS"), this.at(TokenType.SYMBOL, "(")) {
236
+ let e = this.parseStringList();
237
+ return { op: "CONTAINS_ANY", values: e };
238
+ }
239
+ if (this.atKeyword("KEYWORDS")) {
240
+ this.advance(), this.expect(TokenType.SYMBOL, "(");
241
+ let t = this.parseFileName();
242
+ return this.expect(TokenType.SYMBOL, ")"), { op: "CONTAINS_KEYWORDS_FILE", path: t };
243
+ }
244
+ let s = this.expect(TokenType.STRING).value;
245
+ return { op: "CONTAINS", value: s };
246
+ }
247
+ parseStringList() {
248
+ this.expect(TokenType.SYMBOL, "(");
249
+ let e = [];
250
+ for (; !this.at(TokenType.SYMBOL, ")"); ) e.push(this.expect(TokenType.STRING).value), this.at(TokenType.SYMBOL, ",") && this.advance();
251
+ return this.expect(TokenType.SYMBOL, ")"), e;
252
+ }
253
+ parseFileName() {
254
+ let e = this.expect(TokenType.IDENT).value;
255
+ for (; this.at(TokenType.SYMBOL, "."); ) this.advance(), e += "." + this.expect(TokenType.IDENT).value;
256
+ return e;
257
+ }
258
+ parseOtherwise() {
259
+ this.expectKeyword("OTHERWISE");
260
+ let e = this.parseBody();
261
+ return { type: "Otherwise", body: e };
262
+ }
263
+ parseThink() {
264
+ this.expectKeyword("THINK"), this.expect(TokenType.SYMBOL, "(");
265
+ let e = this.parseFileName();
266
+ this.expect(TokenType.SYMBOL, ")");
267
+ let t = this.tryParseWaiting(), s = null;
268
+ return this.atKeyword("OR") && (this.advance(), s = this.parseReply()), { type: "Think", file: e, waiting: t, fallback: s };
269
+ }
270
+ tryParseWaiting() {
271
+ if (!this.atKeyword("WAITING")) return null;
272
+ this.advance(), this.expect(TokenType.SYMBOL, "(");
273
+ let e = null, t = null;
274
+ return !this.at(TokenType.SYMBOL, ")") && (e = this.parseFileName(), this.at(TokenType.SYMBOL, ",") && (this.advance(), t = this.parseExpression())), this.expect(TokenType.SYMBOL, ")"), { file: e, seconds: t };
275
+ }
276
+ parseReply() {
277
+ this.expectKeyword("REPLY");
278
+ let e = null;
279
+ if (this.atKeyword("TO") && (this.advance(), e = this.advance().value), this.isReplyFileForm()) {
280
+ this.expect(TokenType.SYMBOL, "(");
281
+ let t = this.parseFileName();
282
+ this.expect(TokenType.SYMBOL, ",");
283
+ let s = this.parseExpression();
284
+ return this.expect(TokenType.SYMBOL, ")"), { type: "Reply", target: e, file: t, index: s };
285
+ }
286
+ let r = this.parseExpression();
287
+ return { type: "Reply", target: e, value: r };
288
+ }
289
+ isReplyFileForm() {
290
+ if (!this.at(TokenType.SYMBOL, "(")) return false;
291
+ let e = this.tokens[this.pos + 1], t = this.tokens[this.pos + 2];
292
+ return !!e && e.type === TokenType.IDENT && !!t && t.type === TokenType.SYMBOL && "." === t.value;
293
+ }
294
+ parseForward() {
295
+ this.expectKeyword("FORWARD"), this.expectKeyword("TO");
296
+ let e = this.parseExpression();
297
+ return { type: "ForwardTo", target: e };
298
+ }
299
+ parseParseSignal() {
300
+ return this.expectKeyword("PARSE"), this.atWord("SIGNAL") || this.error("expected SIGNAL after PARSE"), this.advance(), { type: "ParseSignal" };
301
+ }
302
+ parseSend() {
303
+ this.expectKeyword("SEND"), this.expectKeyword("TO");
304
+ let e = this.advance().value;
305
+ return { type: "SendTo", target: e };
306
+ }
307
+ parseInsert() {
308
+ this.expectKeyword("INSERT"), this.expectKeyword("INTO");
309
+ let e = this.expect(TokenType.IDENT).value, t = null;
310
+ this.at(TokenType.SYMBOL, "(") && (t = this.parseExpressionList());
311
+ let s = null;
312
+ return this.atKeyword("VALUES") && (this.advance(), s = this.parseExpressionList()), { type: "Insert", table: e, columns: t, values: s };
313
+ }
314
+ parseExpressionList() {
315
+ this.expect(TokenType.SYMBOL, "(");
316
+ let e = [];
317
+ for (; !this.at(TokenType.SYMBOL, ")"); ) e.push(this.parseExpression()), this.at(TokenType.SYMBOL, ",") && this.advance();
318
+ return this.expect(TokenType.SYMBOL, ")"), e;
319
+ }
320
+ parseUpdate() {
321
+ this.expectKeyword("UPDATE");
322
+ let e = this.expect(TokenType.IDENT).value;
323
+ this.expectKeyword("SET");
324
+ let t = this.expect(TokenType.IDENT).value;
325
+ this.expect(TokenType.SYMBOL, "=");
326
+ let s = this.parseExpression(), r = null;
327
+ return this.atKeyword("WHERE") && (this.advance(), r = this.parseComparison()), { type: "Update", table: e, set: { column: t, value: s }, where: r };
328
+ }
329
+ parseImport() {
330
+ this.expectKeyword("IMPORT"), this.expect(TokenType.SYMBOL, "{");
331
+ let e = this.parseFileName(), t = null;
332
+ this.at(TokenType.SYMBOL, ",") && (this.advance(), t = this.parseExpression()), this.expect(TokenType.SYMBOL, "}");
333
+ let s = null;
334
+ return this.atKeyword("AS") && (this.advance(), s = this.expect(TokenType.IDENT).value), { type: "Import", path: e, index: t, alias: s };
335
+ }
336
+ parseRunBot() {
337
+ return this.expectKeyword("RUN"), this.expectKeyword("BOT"), { type: "RunBot" };
338
+ }
339
+ parseExpression() {
340
+ let e = this.parsePrimary();
341
+ for (; this.at(TokenType.SYMBOL, "+"); ) {
342
+ this.advance();
343
+ let t = this.parsePrimary();
344
+ e = { type: "Binary", op: "+", left: e, right: t };
345
+ }
346
+ return e;
347
+ }
348
+ parseComparison() {
349
+ let e = this.parseExpression();
350
+ if (this.at(TokenType.SYMBOL, "=")) {
351
+ this.advance();
352
+ let t = this.parseExpression();
353
+ return { type: "Binary", op: "=", left: e, right: t };
354
+ }
355
+ return e;
356
+ }
357
+ parsePrimary() {
358
+ let e = this.current();
359
+ if (e.type === TokenType.STRING || e.type === TokenType.NUMBER) return this.advance(), { type: "Literal", value: e.value };
360
+ if (e.type === TokenType.IDENT) {
361
+ this.advance();
362
+ let t = { type: "Identifier", name: e.value };
363
+ if (this.at(TokenType.SYMBOL, "(")) {
364
+ let s = this.parseExpressionList();
365
+ t = { type: "Call", callee: t.name, args: s };
366
+ }
367
+ for (; this.at(TokenType.SYMBOL, "."); ) {
368
+ this.advance();
369
+ let r = this.expect(TokenType.IDENT).value;
370
+ t = { type: "Member", object: t, property: r };
371
+ }
372
+ return t;
373
+ }
374
+ this.error("invalid expression");
375
+ }
376
+ };
377
+ module.exports = { Parser, Tokenizer, TokenType, KEYWORDS };
378
+ }
379
+ });
380
+
381
+ // Database.js
382
+ var require_Database = __commonJS({
383
+ "Database.js"(exports, module) {
384
+ "use strict";
385
+ var CONTEXT_SCHEMA = [{ name: "id", columnType: "INT", constraints: ["PRIMARY", "KEY", "AUTO_INCREMENT"] }, { name: "client", columnType: "TEXT", constraints: [] }, { name: "message", columnType: "TEXT", constraints: [] }, { name: "reply", columnType: "TEXT", constraints: [] }, { name: "created_at", columnType: "DATETIME", constraints: [] }];
386
+ var MemoryDatabase = class {
387
+ constructor() {
388
+ this.tables = /* @__PURE__ */ new Map(), this.autoIncrement = /* @__PURE__ */ new Map();
389
+ }
390
+ createTable(e, t, r) {
391
+ let s = "Context" === e && 0 === t.length ? CONTEXT_SCHEMA : t;
392
+ if (this.tables.has(e)) {
393
+ if (r) return;
394
+ throw Error(`runtime error: table "${e}" already exists`);
395
+ }
396
+ this.tables.set(e, { columns: s, rows: [] }), this.autoIncrement.set(e, 0);
397
+ }
398
+ insert(e, t, r) {
399
+ let s = this.tables.get(e);
400
+ if (!s) throw Error(`runtime error: table "${e}" does not exist`);
401
+ let n = this.autoIncrement.get(e) + 1;
402
+ this.autoIncrement.set(e, n);
403
+ let i = { id: n };
404
+ return t.forEach((e2, t2) => {
405
+ i[e2] = r[t2];
406
+ }), s.rows.push(i), n;
407
+ }
408
+ update(e, t, r, s, n) {
409
+ let i = this.tables.get(e);
410
+ if (!i) throw Error(`runtime error: table "${e}" does not exist`);
411
+ let a = s ? i.rows.find((e2) => e2[s] === n) : i.rows[i.rows.length - 1];
412
+ return a && (a[t] = r), a || null;
413
+ }
414
+ getRows(e) {
415
+ let t = this.tables.get(e);
416
+ return t ? t.rows.slice() : [];
417
+ }
418
+ };
419
+ function mapColumnType(e) {
420
+ let t = (e || "").toUpperCase();
421
+ return "INT" === t || "INTEGER" === t ? "INTEGER" : "TEXT";
422
+ }
423
+ function buildColumnDef(e) {
424
+ let t = mapColumnType(e.columnType), r = e.constraints.includes("PRIMARY") && e.constraints.includes("KEY"), s = e.constraints.includes("AUTO_INCREMENT"), n = `${e.name} ${t}`;
425
+ return r && (n += " PRIMARY KEY"), r && s && (n += " AUTOINCREMENT"), n;
426
+ }
427
+ var SQLiteDatabase = class {
428
+ constructor(e = ":memory:") {
429
+ let { DatabaseSync: t } = __require("node:sqlite");
430
+ this.driver = new t(e);
431
+ }
432
+ createTable(e, t, r) {
433
+ let s = "Context" === e && 0 === t.length ? CONTEXT_SCHEMA : t, n = s.map(buildColumnDef).join(", ");
434
+ this.driver.exec(`CREATE TABLE ${r ? "IF NOT EXISTS " : ""}${e} (${n})`);
435
+ }
436
+ insert(e, t, r) {
437
+ let s = t.map(() => "?").join(", "), n = `INSERT INTO ${e} (${t.join(", ")}) VALUES (${s})`, i = this.driver.prepare(n).run(...r);
438
+ return Number(i.lastInsertRowid);
439
+ }
440
+ update(e, t, r, s, n) {
441
+ if (s) {
442
+ let i = `UPDATE ${e} SET ${t} = ? WHERE ${s} = ?`;
443
+ this.driver.prepare(i).run(r, n);
444
+ } else {
445
+ let a = `UPDATE ${e} SET ${t} = ? WHERE id = (SELECT MAX(id) FROM ${e})`;
446
+ this.driver.prepare(a).run(r);
447
+ }
448
+ }
449
+ getRows(e) {
450
+ return this.driver.prepare(`SELECT * FROM ${e}`).all();
451
+ }
452
+ close() {
453
+ this.driver.close();
454
+ }
455
+ };
456
+ module.exports = { MemoryDatabase, SQLiteDatabase };
457
+ }
458
+ });
459
+
460
+ // FileSystem.js
461
+ var require_FileSystem = __commonJS({
462
+ "FileSystem.js"(exports, module) {
463
+ "use strict";
464
+ var MemoryFileSystem = class {
465
+ constructor(files = {}) {
466
+ this.files = new Map(
467
+ Object.entries(files).map(([k, v]) => [this._normalize(k), v])
468
+ );
469
+ }
470
+ _normalize(p) {
471
+ return String(p).replace(/^\.\//, "").replace(/\/+/g, "/").trim();
472
+ }
473
+ setFile(path, content) {
474
+ this.files.set(this._normalize(path), content);
475
+ }
476
+ exists(path) {
477
+ return this.files.has(this._normalize(path));
478
+ }
479
+ readFile(path) {
480
+ const key = this._normalize(path);
481
+ if (!this.files.has(key)) {
482
+ throw new Error(`BotQL: ficheiro n\xE3o encontrado: "${path}"`);
483
+ }
484
+ return this.files.get(key);
485
+ }
486
+ // basePath e' ignorado de proposito: no browser nao ha nocao real de
487
+ // diretorio corrente, os ficheiros importados sao identificados pelo
488
+ // nome/caminho tal como o utilizador os registou.
489
+ resolve(basePath, relativePath) {
490
+ return this._normalize(relativePath);
491
+ }
492
+ dirname(filePath) {
493
+ const norm = this._normalize(filePath);
494
+ const idx = norm.lastIndexOf("/");
495
+ return idx === -1 ? "" : norm.slice(0, idx);
496
+ }
497
+ };
498
+ var NodeFileSystem = class {
499
+ constructor() {
500
+ this._fs = __require("fs");
501
+ this._path = __require("path");
502
+ }
503
+ exists(path) {
504
+ return this._fs.existsSync(path);
505
+ }
506
+ readFile(path) {
507
+ return this._fs.readFileSync(path, "utf8");
508
+ }
509
+ resolve(basePath, relativePath) {
510
+ return this._path.resolve(basePath, relativePath);
511
+ }
512
+ dirname(filePath) {
513
+ return this._path.dirname(filePath);
514
+ }
515
+ };
516
+ var isNode = typeof process !== "undefined" && process.versions && !!process.versions.node;
517
+ function createDefaultFileSystem() {
518
+ return isNode ? new NodeFileSystem() : new MemoryFileSystem();
519
+ }
520
+ var api = { MemoryFileSystem, NodeFileSystem, createDefaultFileSystem };
521
+ if (typeof module !== "undefined" && module.exports) {
522
+ module.exports = api;
523
+ } else {
524
+ exports.BotQLFileSystem = api;
525
+ }
526
+ }
527
+ });
528
+
529
+ // RAG.js
530
+ var require_RAG = __commonJS({
531
+ "RAG.js"(exports, module) {
532
+ "use strict";
533
+ var STOPWORDS = /* @__PURE__ */ new Set([
534
+ "a",
535
+ "o",
536
+ "as",
537
+ "os",
538
+ "de",
539
+ "da",
540
+ "do",
541
+ "das",
542
+ "dos",
543
+ "e",
544
+ "ou",
545
+ "que",
546
+ "um",
547
+ "uma",
548
+ "uns",
549
+ "umas",
550
+ "em",
551
+ "no",
552
+ "na",
553
+ "nos",
554
+ "nas",
555
+ "por",
556
+ "para",
557
+ "com",
558
+ "sem",
559
+ "se",
560
+ "foi",
561
+ "ser",
562
+ "sao",
563
+ "esta",
564
+ "estao",
565
+ "ao",
566
+ "aos",
567
+ "mas",
568
+ "como",
569
+ "tem",
570
+ "ter",
571
+ "nao",
572
+ "sim",
573
+ "meu",
574
+ "minha",
575
+ "seu",
576
+ "sua",
577
+ "eu",
578
+ "tu",
579
+ "ele",
580
+ "ela",
581
+ "nos",
582
+ "vos",
583
+ "eles",
584
+ "elas",
585
+ "isso",
586
+ "isto",
587
+ "aquilo",
588
+ "quando",
589
+ "onde",
590
+ "porque",
591
+ "qual",
592
+ "quais",
593
+ "muito",
594
+ "muita",
595
+ "ja",
596
+ "so"
597
+ ]);
598
+ function normalizar(texto) {
599
+ return texto.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
600
+ }
601
+ var SUFIXOS_ADJETIVO_ADVERBIO = ["issimamente", "issimo", "issima", "mente"];
602
+ var SUFIXOS_NOMINALIZACAO = ["acoes", "acao", "imentos", "imento", "idades", "idade"];
603
+ function stem(palavra) {
604
+ let p = palavra;
605
+ for (const suf of SUFIXOS_ADJETIVO_ADVERBIO) {
606
+ if (p.length > suf.length + 3 && p.endsWith(suf)) {
607
+ p = p.slice(0, -suf.length);
608
+ break;
609
+ }
610
+ }
611
+ for (const suf of SUFIXOS_NOMINALIZACAO) {
612
+ if (p.length > suf.length + 3 && p.endsWith(suf)) {
613
+ p = p.slice(0, -suf.length);
614
+ break;
615
+ }
616
+ }
617
+ if (p.length > 4 && p.endsWith("s") && !p.endsWith("ns")) {
618
+ p = p.slice(0, -1);
619
+ }
620
+ return p;
621
+ }
622
+ function tokenizar(texto) {
623
+ return normalizar(texto).toLowerCase().replace(/[^a-z0-9\s]/g, " ").split(/\s+/).filter((palavra) => palavra.length > 1 && !STOPWORDS.has(palavra)).map(stem);
624
+ }
625
+ function distanciaEdicao(a, b) {
626
+ if (a === b) return 0;
627
+ const la = a.length;
628
+ const lb = b.length;
629
+ if (la === 0) return lb;
630
+ if (lb === 0) return la;
631
+ let linhaAnterior = new Array(lb + 1);
632
+ for (let j = 0; j <= lb; j++) linhaAnterior[j] = j;
633
+ for (let i = 1; i <= la; i++) {
634
+ const linhaAtual = [i];
635
+ for (let j = 1; j <= lb; j++) {
636
+ const custo = a[i - 1] === b[j - 1] ? 0 : 1;
637
+ linhaAtual[j] = Math.min(
638
+ linhaAtual[j - 1] + 1,
639
+ linhaAnterior[j] + 1,
640
+ linhaAnterior[j - 1] + custo
641
+ );
642
+ }
643
+ linhaAnterior = linhaAtual;
644
+ }
645
+ return linhaAnterior[lb];
646
+ }
647
+ function distanciaMaximaTolerada(tamanho) {
648
+ if (tamanho <= 4) return 0;
649
+ if (tamanho <= 7) return 1;
650
+ return 2;
651
+ }
652
+ var BM25_K1 = 1.5;
653
+ var BM25_B = 0.75;
654
+ var PESO_BM25 = 1;
655
+ var PESO_FRASE = 2.5;
656
+ var PESO_FUZZY = 0.4;
657
+ var CONFIANCA_ALTA = 0.55;
658
+ var CONFIANCA_MINIMA = 0.15;
659
+ var MARGEM_MINIMA = 0.12;
660
+ var SIMILARIDADE_DUPLICADA = 0.75;
661
+ function extrairNomesProprios(texto) {
662
+ const palavras = texto.split(/\s+/);
663
+ const encontrados = [];
664
+ for (let i = 1; i < palavras.length; i++) {
665
+ const limpa = palavras[i].replace(/^[(["']+|[.,;:!?)\]"']+$/g, "");
666
+ if (/^[A-ZÀ-Ý][a-zà-ÿ]+$/.test(limpa)) {
667
+ encontrados.push(limpa);
668
+ }
669
+ }
670
+ return encontrados;
671
+ }
672
+ var KnowledgeIndex = class _KnowledgeIndex {
673
+ /**
674
+ * @param {string} sourceText Conteúdo do ficheiro de conhecimento.
675
+ * @param {Object} [options]
676
+ * @param {Record<string,string>} [options.synonyms] Mapa de termo -> termo
677
+ * canónico, para ligar manualmente palavras que o stemmer não junta
678
+ * sozinho (ex: { horas: 'horario' }).
679
+ */
680
+ constructor(sourceText, options = {}) {
681
+ this.synonyms = {};
682
+ for (const [de, para] of Object.entries(options.synonyms || {})) {
683
+ this.synonyms[stem(normalizar(de.toLowerCase()))] = stem(normalizar(para.toLowerCase()));
684
+ }
685
+ this.blocks = _KnowledgeIndex.parseBlocks(sourceText);
686
+ this._buildIndex();
687
+ }
688
+ static parseBlocks(sourceText) {
689
+ return sourceText.split(/\n\s*\n/).map((bloco) => bloco.trim()).filter((bloco) => bloco.length > 0);
690
+ }
691
+ _aplicarSinonimos(tokens) {
692
+ return tokens.map((t) => this.synonyms[t] || t);
693
+ }
694
+ _buildIndex() {
695
+ this.docs = this.blocks.map((bloco) => tokenizar(bloco));
696
+ this.docTextNormalizado = this.blocks.map((bloco) => normalizar(bloco).toLowerCase());
697
+ this.docFreq = /* @__PURE__ */ new Map();
698
+ this.vocabulario = /* @__PURE__ */ new Set();
699
+ for (const tokens of this.docs) {
700
+ const vistas = new Set(tokens);
701
+ for (const palavra of vistas) {
702
+ this.docFreq.set(palavra, (this.docFreq.get(palavra) || 0) + 1);
703
+ this.vocabulario.add(palavra);
704
+ }
705
+ }
706
+ this.listaVocabulario = Array.from(this.vocabulario);
707
+ this.totalDocs = this.docs.length;
708
+ this.avgDocLen = this.totalDocs === 0 ? 0 : this.docs.reduce((soma, tokens) => soma + tokens.length, 0) / this.totalDocs;
709
+ }
710
+ _idf(palavra) {
711
+ const n = this.docFreq.get(palavra) || 0;
712
+ if (n === 0) return 0;
713
+ return Math.log(1 + (this.totalDocs - n + 0.5) / (n + 0.5));
714
+ }
715
+ _termoMaisProximo(termo) {
716
+ const tolerancia = distanciaMaximaTolerada(termo.length);
717
+ if (tolerancia === 0) return null;
718
+ let melhor = null;
719
+ let melhorDist = tolerancia + 1;
720
+ for (const candidato of this.listaVocabulario) {
721
+ if (Math.abs(candidato.length - termo.length) > tolerancia) continue;
722
+ const d = distanciaEdicao(termo, candidato);
723
+ if (d < melhorDist) {
724
+ melhorDist = d;
725
+ melhor = candidato;
726
+ }
727
+ }
728
+ return melhorDist <= tolerancia ? melhor : null;
729
+ }
730
+ _scoreBM25(queryTokens, docIndex) {
731
+ const tokens = this.docs[docIndex];
732
+ if (tokens.length === 0) return 0;
733
+ const termFreq = /* @__PURE__ */ new Map();
734
+ for (const t of tokens) termFreq.set(t, (termFreq.get(t) || 0) + 1);
735
+ let score = 0;
736
+ for (const termo of queryTokens) {
737
+ let tf = termFreq.get(termo) || 0;
738
+ let idf = this._idf(termo);
739
+ let peso = 1;
740
+ if (tf === 0) {
741
+ const proximo = this._termoMaisProximo(termo);
742
+ if (proximo === null) continue;
743
+ tf = termFreq.get(proximo) || 0;
744
+ if (tf === 0) continue;
745
+ idf = this._idf(proximo);
746
+ peso = PESO_FUZZY;
747
+ }
748
+ const numerador = tf * (BM25_K1 + 1);
749
+ const denominador = tf + BM25_K1 * (1 - BM25_B + BM25_B * (tokens.length / this.avgDocLen));
750
+ score += peso * idf * (numerador / denominador);
751
+ }
752
+ return score;
753
+ }
754
+ _bonusFrase(message, docIndex) {
755
+ const msgNorm = normalizar(message).toLowerCase().replace(/[^a-z0-9\s]/g, " ");
756
+ const palavras = msgNorm.split(/\s+/).filter((p) => p.length > 1);
757
+ if (palavras.length < 2) return 0;
758
+ const textoBloco = this.docTextNormalizado[docIndex];
759
+ let bonus = 0;
760
+ for (let tamanho = Math.min(6, palavras.length); tamanho >= 2; tamanho--) {
761
+ for (let i = 0; i + tamanho <= palavras.length; i++) {
762
+ const frase = palavras.slice(i, i + tamanho).join(" ");
763
+ if (textoBloco.includes(frase)) {
764
+ bonus += tamanho * tamanho;
765
+ }
766
+ }
767
+ }
768
+ return bonus;
769
+ }
770
+ _scoreDoc(message, queryTokens, docIndex) {
771
+ const bm25 = this._scoreBM25(queryTokens, docIndex);
772
+ const frase = this._bonusFrase(message, docIndex);
773
+ return PESO_BM25 * bm25 + PESO_FRASE * frase;
774
+ }
775
+ // Calcula o score de todos os blocos para a mensagem e devolve ordenado
776
+ // do maior para o menor. Base partilhada por search() e analyze().
777
+ _rankTodos(message) {
778
+ const queryTokens = this._aplicarSinonimos(tokenizar(message));
779
+ if (queryTokens.length === 0) return [];
780
+ const resultados = [];
781
+ for (let i = 0; i < this.totalDocs; i++) {
782
+ resultados.push({ index: i, score: this._scoreDoc(message, queryTokens, i) });
783
+ }
784
+ resultados.sort((a, b) => b.score - a.score);
785
+ return resultados;
786
+ }
787
+ /**
788
+ * Procura o bloco mais relevante para a mensagem recebida.
789
+ *
790
+ * @returns {{text: string, score: number, confidence: number, index: number} | null}
791
+ * `confidence` está sempre entre 0 e 1 — não é probabilidade
792
+ * estatística real, é uma escala interpretável para decidir um
793
+ * limiar no `.sql` (ex: "só responde se confidence > 0.3").
794
+ */
795
+ search(message) {
796
+ if (this.totalDocs === 0) return null;
797
+ const ranking = this._rankTodos(message);
798
+ if (ranking.length === 0) return null;
799
+ const melhor = ranking[0];
800
+ if (melhor.score <= 0) return null;
801
+ return {
802
+ text: this.blocks[melhor.index],
803
+ score: melhor.score,
804
+ confidence: melhor.score / (melhor.score + 3),
805
+ index: melhor.index
806
+ };
807
+ }
808
+ // Jaccard sobre os tokens (já com stem aplicado) de dois blocos já
809
+ // indexados. Usado só pra detetar "mesmo conteúdo repetido" (valor
810
+ // alto) — dois blocos sobre o mesmo assunto mas com detalhes
811
+ // diferentes normalmente NÃO têm jaccard alto (a maior parte da frase
812
+ // difere), por isso não serve pra decidir se vale a pena tentar unir.
813
+ _similaridadeBlocos(indexA, indexB) {
814
+ const a = new Set(this.docs[indexA]);
815
+ const b = new Set(this.docs[indexB]);
816
+ if (a.size === 0 || b.size === 0) return 0;
817
+ let intersecao = 0;
818
+ for (const t of a) if (b.has(t)) intersecao++;
819
+ const uniao = a.size + b.size - intersecao;
820
+ return uniao === 0 ? 0 : intersecao / uniao;
821
+ }
822
+ // Quantos tokens (com stem) os dois blocos partilham, em termos
823
+ // absolutos. Usado como gatilho pra tentar unir: basta partilharem UM
824
+ // termo de assunto ("entregamos") — quem garante que a fusão é segura
825
+ // não é isto, é a regra de "exatamente um nome próprio diferente em
826
+ // cada bloco" dentro de _tentarUnir.
827
+ _termosPartilhados(indexA, indexB) {
828
+ const a = new Set(this.docs[indexA]);
829
+ const b = this.docs[indexB];
830
+ let count = 0;
831
+ for (const t of b) if (a.has(t)) count++;
832
+ return count;
833
+ }
834
+ // Compara dois blocos concorrentes pelo nome próprio que cada um
835
+ // menciona, pra decidir se são "a mesma informação" ou "informação
836
+ // complementar que dá pra unir":
837
+ // - mesmo nome próprio nos dois (ex: os dois falam de Luanda)
838
+ // -> { tipo: 'duplicado' }: é a mesma coisa dita de formas
839
+ // diferentes, não há o que unir, usa qualquer um dos dois
840
+ // - nomes próprios diferentes (ex: Luanda vs Huambo)
841
+ // -> { tipo: 'unido', texto: '...' }: funde numa frase só
842
+ // - não dá pra identificar com segurança (nenhum nome próprio, ou
843
+ // mais de um em algum dos blocos) -> null: quem chama decide o
844
+ // que fazer a seguir (normalmente: reanalisar, ou cair no fallback)
845
+ _tentarUnir(textoA, textoB) {
846
+ const entidadesA = extrairNomesProprios(textoA);
847
+ const entidadesB = extrairNomesProprios(textoB);
848
+ if (entidadesA.length !== 1 || entidadesB.length !== 1) return null;
849
+ const entidadeA = entidadesA[0];
850
+ const entidadeB = entidadesB[0];
851
+ if (normalizar(entidadeA).toLowerCase() === normalizar(entidadeB).toLowerCase()) {
852
+ return { tipo: "duplicado" };
853
+ }
854
+ const primeiraPalavra = textoA.trim().split(/\s+/)[0].replace(/[.,;:!?]+$/, "");
855
+ return { tipo: "unido", texto: `${primeiraPalavra} em v\xE1rios lugares, como ${entidadeA} e ${entidadeB}.` };
856
+ }
857
+ // Segunda tentativa de ranking, usada só quando a primeira ficou
858
+ // ambígua: descarta metade dos termos da query (os de menor IDF, ou
859
+ // seja, os mais genéricos/comuns) e refaz o ranking só com os termos
860
+ // mais raros/decisivos. Uma query mais focada às vezes desempata o
861
+ // que uma query "cheia" deixa embolado.
862
+ _reanalisarFocado(message) {
863
+ const tokens = this._aplicarSinonimos(tokenizar(message));
864
+ if (tokens.length <= 2) return null;
865
+ const comIdf = tokens.map((t) => ({ termo: t, idf: this._idf(t) }));
866
+ comIdf.sort((a, b) => b.idf - a.idf);
867
+ const focados = comIdf.slice(0, Math.max(1, Math.ceil(tokens.length / 2))).map((x) => x.termo);
868
+ const resultados = [];
869
+ for (let i = 0; i < this.totalDocs; i++) {
870
+ resultados.push({ index: i, score: this._scoreDoc(message, focados, i) });
871
+ }
872
+ resultados.sort((a, b) => b.score - a.score);
873
+ return resultados;
874
+ }
875
+ /**
876
+ * Versão completa da busca: além do melhor bloco, avalia a evidência
877
+ * (melhor x segundo colocado) e devolve uma decisão explícita, em vez
878
+ * de deixar o `.sql` decidir tudo com um único limiar de confidence.
879
+ *
880
+ * EvidenceEvaluator: compara o melhor resultado com o segundo. Se os
881
+ * dois estão muito próximos, o motor não tem certeza de qual bloco
882
+ * responde à pergunta — mesmo que o score absoluto seja alto.
883
+ *
884
+ * ConfidenceEngine: mesma fórmula de sempre (score / (score + 3)),
885
+ * aplicada só ao melhor resultado.
886
+ *
887
+ * DecisionEngine: cruza confidence com margem para decidir entre:
888
+ * - RESPONDER confidence alta e o melhor bloco se destaca do 2o,
889
+ * OU os dois concorrentes foram reconciliados (ver
890
+ * abaixo) — nesse caso `texto` já vem pronto pra usar
891
+ * - REANALISAR os blocos concorrentes são sobre assuntos diferentes
892
+ * demais pra reconciliar, e nem a retentativa focada
893
+ * resolveu — o `.sql` decide o que fazer (normalmente
894
+ * cair no fallback do OR REPLY)
895
+ * - UNKNOWN confidence baixa demais, não há bloco que sirva
896
+ *
897
+ * Reconciliação (só entra quando o resultado não é decisivo de cara):
898
+ * 1. Se o melhor e o segundo colocado são basicamente o mesmo
899
+ * conteúdo (alta similaridade) — não há nada pra unir, usa o
900
+ * melhor tal como está.
901
+ * 2. Se são blocos diferentes mas do mesmo assunto, e cada um tem
902
+ * exatamente um nome próprio diferente (ex: "entregamos em
903
+ * Luanda" / "entregamos no Huambo") — tenta fundir numa frase só
904
+ * ("entregamos em vários lugares, como Luanda e Huambo").
905
+ * 3. Se nada disso se aplica, tenta de novo com uma versão mais
906
+ * enxuta da pergunta (só os termos mais decisivos) antes de
907
+ * desistir.
908
+ *
909
+ * @returns {{
910
+ * decision: 'RESPONDER'|'REANALISAR'|'UNKNOWN',
911
+ * confidence: number,
912
+ * margem: number,
913
+ * texto: string | null,
914
+ * unificado: boolean,
915
+ * reanalisado: boolean,
916
+ * melhor: {text: string, score: number, index: number} | null,
917
+ * segundo: {text: string, score: number, index: number} | null
918
+ * }}
919
+ */
920
+ analyze(message) {
921
+ const vazio = { decision: "UNKNOWN", confidence: 0, margem: 0, texto: null, unificado: false, reanalisado: false, melhor: null, segundo: null };
922
+ if (this.totalDocs === 0) return vazio;
923
+ const ranking = this._rankTodos(message);
924
+ if (ranking.length === 0 || ranking[0].score <= 0) return vazio;
925
+ const melhor = ranking[0];
926
+ const segundo = ranking[1] || { index: -1, score: 0 };
927
+ const margem = (melhor.score - segundo.score) / melhor.score;
928
+ const confidence = melhor.score / (melhor.score + 3);
929
+ const melhorInfo = { text: this.blocks[melhor.index], score: melhor.score, index: melhor.index };
930
+ const segundoInfo = segundo.index >= 0 ? { text: this.blocks[segundo.index], score: segundo.score, index: segundo.index } : null;
931
+ if (confidence < CONFIANCA_MINIMA) return vazio;
932
+ if (confidence >= CONFIANCA_ALTA && margem >= MARGEM_MINIMA) {
933
+ return {
934
+ decision: "RESPONDER",
935
+ confidence,
936
+ margem,
937
+ texto: melhorInfo.text,
938
+ unificado: false,
939
+ reanalisado: false,
940
+ melhor: melhorInfo,
941
+ segundo: segundoInfo
942
+ };
943
+ }
944
+ if (segundoInfo) {
945
+ const similaridade = this._similaridadeBlocos(melhor.index, segundo.index);
946
+ if (similaridade >= SIMILARIDADE_DUPLICADA) {
947
+ return {
948
+ decision: "RESPONDER",
949
+ confidence,
950
+ margem,
951
+ texto: melhorInfo.text,
952
+ unificado: false,
953
+ reanalisado: false,
954
+ melhor: melhorInfo,
955
+ segundo: segundoInfo
956
+ };
957
+ }
958
+ const termosPartilhados = this._termosPartilhados(melhor.index, segundo.index);
959
+ if (termosPartilhados >= 1) {
960
+ const resultado = this._tentarUnir(melhorInfo.text, segundoInfo.text);
961
+ if (resultado && resultado.tipo === "duplicado") {
962
+ return {
963
+ decision: "RESPONDER",
964
+ confidence,
965
+ margem,
966
+ texto: melhorInfo.text,
967
+ unificado: false,
968
+ reanalisado: false,
969
+ melhor: melhorInfo,
970
+ segundo: segundoInfo
971
+ };
972
+ }
973
+ if (resultado && resultado.tipo === "unido") {
974
+ return {
975
+ decision: "RESPONDER",
976
+ confidence,
977
+ margem,
978
+ texto: resultado.texto,
979
+ unificado: true,
980
+ reanalisado: false,
981
+ melhor: melhorInfo,
982
+ segundo: segundoInfo
983
+ };
984
+ }
985
+ }
986
+ }
987
+ const tentativa2 = this._reanalisarFocado(message);
988
+ if (tentativa2 && tentativa2.length > 0 && tentativa2[0].score > 0) {
989
+ const melhor2 = tentativa2[0];
990
+ const segundo2 = tentativa2[1] || { index: -1, score: 0 };
991
+ const margem2 = (melhor2.score - segundo2.score) / melhor2.score;
992
+ const confidence2 = melhor2.score / (melhor2.score + 3);
993
+ if (confidence2 >= CONFIANCA_ALTA && margem2 >= MARGEM_MINIMA) {
994
+ return {
995
+ decision: "RESPONDER",
996
+ confidence: confidence2,
997
+ margem: margem2,
998
+ texto: this.blocks[melhor2.index],
999
+ unificado: false,
1000
+ reanalisado: true,
1001
+ melhor: { text: this.blocks[melhor2.index], score: melhor2.score, index: melhor2.index },
1002
+ segundo: segundo2.index >= 0 ? { text: this.blocks[segundo2.index], score: segundo2.score, index: segundo2.index } : null
1003
+ };
1004
+ }
1005
+ }
1006
+ return {
1007
+ decision: "REANALISAR",
1008
+ confidence,
1009
+ margem,
1010
+ texto: null,
1011
+ unificado: false,
1012
+ reanalisado: false,
1013
+ melhor: melhorInfo,
1014
+ segundo: segundoInfo
1015
+ };
1016
+ }
1017
+ };
1018
+ var KnowledgeCache = class {
1019
+ constructor(fileSystem) {
1020
+ this.fileSystem = fileSystem;
1021
+ this.cache = /* @__PURE__ */ new Map();
1022
+ }
1023
+ get(resolvedPath, options) {
1024
+ if (this.cache.has(resolvedPath)) return this.cache.get(resolvedPath);
1025
+ if (!this.fileSystem.exists(resolvedPath)) {
1026
+ throw new Error(`BotQL/THINK: ficheiro de conhecimento n\xE3o encontrado: "${resolvedPath}"`);
1027
+ }
1028
+ const texto = this.fileSystem.readFile(resolvedPath);
1029
+ const index = new KnowledgeIndex(texto, options);
1030
+ this.cache.set(resolvedPath, index);
1031
+ return index;
1032
+ }
1033
+ };
1034
+ module.exports = { KnowledgeIndex, KnowledgeCache, tokenizar, normalizar, stem, distanciaEdicao };
1035
+ }
1036
+ });
1037
+
1038
+ // botql.js
1039
+ var require_botql = __commonJS({
1040
+ "botql.js"(exports, module) {
1041
+ var { Parser: e } = require_Parser();
1042
+ var { MemoryDatabase: t } = require_Database();
1043
+ var { createDefaultFileSystem: s } = require_FileSystem();
1044
+ var { KnowledgeCache: i } = require_RAG();
1045
+ var WAITING_TEXTO_PADRAO = "Pensando...";
1046
+ var BotQLInterpreter = class _BotQLInterpreter {
1047
+ constructor(e2 = {}) {
1048
+ this.db = e2.db || new t(), this.onReply = e2.onReply || null, this.onForward = e2.onForward || null, this.onSend = e2.onSend || null, this.signalParser = e2.signalParser || ((e3) => e3), this.onThinking = e2.onThinking || null, this.botName = null, this.platform = null, this.connections = [], this.handlers = { START: [], MESSAGE: [], SIGNAL: [] }, this.running = false, this.responseConnections = /* @__PURE__ */ new Set(), this.onResponse = e2.onResponse || null, this.defaultMessageConfig = null, this.nativeFuncs = /* @__PURE__ */ new Map(), this.registerFunction("NOW", () => (/* @__PURE__ */ new Date()).toISOString()), this.fileSystem = e2.fileSystem || s();
1049
+ let n = "undefined" != typeof process && process.cwd ? process.cwd() : "";
1050
+ this._basePathStack = [e2.basePath || n], this._importedFiles = /* @__PURE__ */ new Set(), this._rootBasePath = e2.basePath || n, this._keywordsFileCache = /* @__PURE__ */ new Map(), this._replyFileCache = /* @__PURE__ */ new Map(), this.envValues = /* @__PURE__ */ new Map(), this.knowledgeCache = new i(this.fileSystem);
1051
+ }
1052
+ static fromSource(t2, s2 = {}) {
1053
+ let i2 = new e(t2).parseProgram(), n = new _BotQLInterpreter(s2);
1054
+ return n.load(i2), n;
1055
+ }
1056
+ static fromFile(e2, t2 = {}) {
1057
+ let i2 = t2.fileSystem || s(), n = i2.resolve("", e2), r = i2.readFile(n);
1058
+ return _BotQLInterpreter.fromSource(r, { ...t2, fileSystem: i2, basePath: i2.dirname(n) });
1059
+ }
1060
+ registerFunction(e2, t2) {
1061
+ this.nativeFuncs.set(e2, t2);
1062
+ }
1063
+ load(e2, t2 = this._basePathStack[this._basePathStack.length - 1]) {
1064
+ let s2 = this._flattenImports(e2.body, t2);
1065
+ for (let i2 of s2) switch (i2.type) {
1066
+ case "CreateBot":
1067
+ this.botName = i2.name;
1068
+ break;
1069
+ case "Platform":
1070
+ this.platform = i2.value;
1071
+ break;
1072
+ case "Connect":
1073
+ this.connections.push({ service: i2.service, kind: i2.kind, credential: i2.credential });
1074
+ break;
1075
+ case "ConnectResponse":
1076
+ if (!this.envValues.has(i2.alias)) throw Error(`runtime error: CONNECT RESPONSE failed, alias "${i2.alias}" was not imported (use IMPORT {..., N} AS ${i2.alias})`);
1077
+ this.responseConnections.add(i2.alias);
1078
+ break;
1079
+ case "CreateTable":
1080
+ if (this.db.createTable(i2.name, i2.columns, i2.preventDefault), i2.defaultMessage) {
1081
+ let n = "Context" === i2.name ? "client" : (i2.columns.find((e3) => "client" === e3.name || "sender" === e3.name) || {}).name;
1082
+ if (!n) throw Error(`runtime error: DEFAULT MESSAGE on table "${i2.name}" requires a "client" or "sender" column`);
1083
+ this.defaultMessageConfig = { table: i2.name, senderColumn: n, ...i2.defaultMessage };
1084
+ }
1085
+ break;
1086
+ case "On":
1087
+ this.handlers[i2.event].push(i2);
1088
+ break;
1089
+ case "RunBot":
1090
+ this.running = true;
1091
+ break;
1092
+ default:
1093
+ throw Error(`runtime error: unsupported top-level statement: ${i2.type}`);
1094
+ }
1095
+ }
1096
+ _flattenImports(t2, s2) {
1097
+ let i2 = [], n = [];
1098
+ for (let r of t2) {
1099
+ if ("Import" !== r.type) {
1100
+ n.push(r);
1101
+ continue;
1102
+ }
1103
+ if (null !== r.index && void 0 !== r.index) {
1104
+ let a = this.evalExpr(r.index, this.createContext()), l = this._loadIndexedFile(r.path, s2), o = l.get(Number(a));
1105
+ if (void 0 === o) throw Error(`runtime error: IMPORT failed, entry ${a} not found in "${r.path}"`);
1106
+ let h = r.alias || r.path.replace(/\.[^.]+$/, "");
1107
+ this.envValues.set(h, o);
1108
+ continue;
1109
+ }
1110
+ let u = this.fileSystem.resolve(s2, r.path);
1111
+ if (this._importedFiles.has(u)) continue;
1112
+ if (!this.fileSystem.exists(u)) throw Error(`runtime error: IMPORT failed, file not found: "${u}"`);
1113
+ this._importedFiles.add(u);
1114
+ let c = this.fileSystem.readFile(u), f = new e(c).parseProgram(), d = this._flattenImports(f.body, this.fileSystem.dirname(u));
1115
+ i2.push(...d);
1116
+ }
1117
+ return [...i2, ...n];
1118
+ }
1119
+ createContext(e2 = {}) {
1120
+ return { vars: { client: e2.client || null, message: e2.message || null, lastMsg: null, SIGNAL: null }, rawSignal: e2.rawSignal || null, lastInsertId: null };
1121
+ }
1122
+ async start() {
1123
+ for (let e2 of this.handlers.START) {
1124
+ let t2 = this.createContext();
1125
+ await this.run(e2.body, t2);
1126
+ }
1127
+ }
1128
+ async receiveMessage(e2, t2) {
1129
+ let s2 = this.createContext({ client: e2, message: t2 }), i2 = await this.greetIfNew(e2);
1130
+ if (i2) return s2;
1131
+ for (let n of this.handlers.MESSAGE) await this.run(n.body, s2);
1132
+ return s2;
1133
+ }
1134
+ async greetIfNew(e2) {
1135
+ if (!this.defaultMessageConfig) return false;
1136
+ let { table: t2, senderColumn: s2 } = this.defaultMessageConfig, i2 = this.db.getRows(t2).some((t3) => t3[s2] === e2);
1137
+ if (i2) return false;
1138
+ let n = this.createContext({ client: e2 }), r = this.resolveDefaultMessageText(this.defaultMessageConfig, n);
1139
+ return n.vars.lastMsg = r, this.db.insert(t2, [s2], [e2]), this.onReply && await this.onReply({ target: null, text: r, client: e2 }), true;
1140
+ }
1141
+ resolveDefaultMessageText(e2, t2) {
1142
+ return e2.file ? this.resolveReplyFromFile(e2.file, e2.index, t2) : String(this.evalExpr(e2.value, t2));
1143
+ }
1144
+ async receiveSignal(e2, t2) {
1145
+ let s2 = this.createContext({ rawSignal: t2 });
1146
+ for (let i2 of this.handlers.SIGNAL) i2.source === e2 && await this.run(i2.body, s2);
1147
+ return s2;
1148
+ }
1149
+ async run(e2, t2) {
1150
+ let s2 = false;
1151
+ for (let i2 = 0; i2 < e2.length; i2++) {
1152
+ let n = e2[i2];
1153
+ if ("When" === n.type) {
1154
+ (0 === i2 || "When" !== e2[i2 - 1].type) && (s2 = false);
1155
+ let r = n.conditions.some((e3) => this.evalCondition(e3, t2));
1156
+ r && (await this.run(n.body, t2), s2 = true);
1157
+ continue;
1158
+ }
1159
+ if ("Otherwise" === n.type) {
1160
+ s2 || await this.run(n.body, t2), s2 = false;
1161
+ continue;
1162
+ }
1163
+ await this.execStatement(n, t2);
1164
+ }
1165
+ }
1166
+ evalCondition(e2, t2) {
1167
+ if ("string" != typeof t2.vars.message) return false;
1168
+ let s2 = t2.vars.message.toLowerCase();
1169
+ if ("CONTAINS" === e2.op) return s2.includes(e2.value.toLowerCase());
1170
+ if ("CONTAINS_ANY" === e2.op) return e2.values.some((e3) => s2.includes(e3.toLowerCase()));
1171
+ if ("CONTAINS_KEYWORDS_FILE" === e2.op) {
1172
+ let i2 = this._loadKeywordsFile(e2.path);
1173
+ return i2.some((e3) => s2.includes(e3.toLowerCase()));
1174
+ }
1175
+ throw Error(`runtime error: unsupported condition: ${e2.op}`);
1176
+ }
1177
+ _loadKeywordsFile(e2) {
1178
+ if (this._keywordsFileCache.has(e2)) return this._keywordsFileCache.get(e2);
1179
+ let t2 = this.fileSystem.resolve(this._rootBasePath, e2);
1180
+ if (!this.fileSystem.exists(t2)) throw Error(`runtime error: KEYWORDS failed, file not found: "${t2}"`);
1181
+ let s2 = this.fileSystem.readFile(t2), i2 = s2.split("\n").map((e3) => e3.trim()).filter((e3) => e3.length > 0);
1182
+ return this._keywordsFileCache.set(e2, i2), i2;
1183
+ }
1184
+ resolveWaitingConfig(e2, t2) {
1185
+ if (!e2) return { text: WAITING_TEXTO_PADRAO, seconds: 3 };
1186
+ let s2 = WAITING_TEXTO_PADRAO;
1187
+ if (e2.file) {
1188
+ let i2 = this.fileSystem.resolve(this._rootBasePath, e2.file);
1189
+ if (!this.fileSystem.exists(i2)) throw Error(`runtime error: WAITING failed, file not found: "${i2}"`);
1190
+ s2 = this.fileSystem.readFile(i2).trim();
1191
+ }
1192
+ let n = 3;
1193
+ return e2.seconds && (n = Number(this.evalExpr(e2.seconds, t2))), { text: s2, seconds: n };
1194
+ }
1195
+ resolveReplyFromFile(e2, t2, s2) {
1196
+ let i2 = this.evalExpr(t2, s2), n = this._loadIndexedFile(e2, this._rootBasePath), r = n.get(Number(i2));
1197
+ if (void 0 === r) throw Error(`runtime error: REPLY failed, entry ${i2} not found in "${e2}"`);
1198
+ return r;
1199
+ }
1200
+ _loadIndexedFile(e2, t2) {
1201
+ if (this._replyFileCache.has(e2)) return this._replyFileCache.get(e2);
1202
+ let s2 = this.fileSystem.resolve(t2, e2);
1203
+ if (!this.fileSystem.exists(s2)) throw Error(`runtime error: file not found: "${s2}"`);
1204
+ let i2 = this.fileSystem.readFile(s2), n = this._parseIndexedEntries(i2, e2);
1205
+ return this._replyFileCache.set(e2, n), n;
1206
+ }
1207
+ _parseIndexedEntries(e2, t2) {
1208
+ let s2 = /* @__PURE__ */ new Map(), i2 = e2.length, n = 0;
1209
+ for (; n < i2; ) {
1210
+ let r = n, a = r;
1211
+ for (; a < i2 && (" " === e2[a] || " " === e2[a]); ) a++;
1212
+ let l = /^\d+/.exec(e2.slice(a));
1213
+ if (!l || "-" !== e2[a + l[0].length]) {
1214
+ let o = e2.indexOf("\n", r);
1215
+ n = -1 === o ? i2 : o + 1;
1216
+ continue;
1217
+ }
1218
+ let h = Number(l[0]), u = a + l[0].length + 1;
1219
+ if ("{" === e2[u]) {
1220
+ let c = 1, f = u + 1, d = f;
1221
+ for (; d < i2 && c > 0; ) "{" === e2[d] ? c++ : "}" === e2[d] && c--, d++;
1222
+ if (0 !== c) throw Error(`runtime error: unclosed "{" for entry ${h} in "${t2}"`);
1223
+ let p = e2.slice(f, d - 1).trim();
1224
+ s2.set(h, p), n = d;
1225
+ continue;
1226
+ }
1227
+ let m = e2.indexOf("\n", u), w = -1 === m ? i2 : m, g = e2.slice(u, w).trim();
1228
+ s2.set(h, g), n = -1 === m ? i2 : m + 1;
1229
+ }
1230
+ return s2;
1231
+ }
1232
+ async execStatement(e2, t2) {
1233
+ switch (e2.type) {
1234
+ case "Reply": {
1235
+ let s2 = e2.file ? this.resolveReplyFromFile(e2.file, e2.index, t2) : String(await this.evalReplyValue(e2.value, t2));
1236
+ t2.vars.lastMsg = s2, this.onReply && await this.onReply({ target: e2.target, text: s2, client: t2.vars.client });
1237
+ break;
1238
+ }
1239
+ case "Think": {
1240
+ let i2 = this.resolveWaitingConfig(e2.waiting, t2), n = Date.now();
1241
+ this.onThinking && await this.onThinking({ client: t2.vars.client, text: i2.text });
1242
+ let r = this.fileSystem.resolve(this._rootBasePath, e2.file), a = this.knowledgeCache.get(r), l = a.analyze(t2.vars.message), o = Date.now() - n, h = 1e3 * i2.seconds - o;
1243
+ if (h > 0 && await new Promise((e3) => setTimeout(e3, h)), "RESPONDER" === l.decision) {
1244
+ let u = l.texto;
1245
+ t2.vars.lastMsg = u, this.onReply && await this.onReply({ target: null, text: u, client: t2.vars.client });
1246
+ } else e2.fallback && await this.execStatement(e2.fallback, t2);
1247
+ break;
1248
+ }
1249
+ case "ForwardTo": {
1250
+ let c = String(this.evalExpr(e2.target, t2));
1251
+ this.onForward && await this.onForward({ target: c, client: t2.vars.client });
1252
+ break;
1253
+ }
1254
+ case "ParseSignal":
1255
+ t2.vars.SIGNAL = await this.signalParser(t2.rawSignal);
1256
+ break;
1257
+ case "SendTo":
1258
+ this.onSend && await this.onSend({ target: e2.target, signal: t2.vars.SIGNAL });
1259
+ break;
1260
+ case "Insert":
1261
+ this.execInsert(e2, t2);
1262
+ break;
1263
+ case "Update":
1264
+ this.execUpdate(e2, t2);
1265
+ break;
1266
+ default:
1267
+ throw Error(`runtime error: unsupported statement inside event: ${e2.type}`);
1268
+ }
1269
+ }
1270
+ execInsert(e2, t2) {
1271
+ let s2, i2;
1272
+ "Context" !== e2.table || e2.columns && 0 !== e2.columns.length || e2.values ? (s2 = (e2.columns || []).map((e3) => e3.name), i2 = (e2.values || []).map((e3) => this.evalExpr(e3, t2))) : (s2 = ["client", "message", "created_at"], i2 = [t2.vars.client, t2.vars.message, (/* @__PURE__ */ new Date()).toISOString()]), t2.lastInsertId = this.db.insert(e2.table, s2, i2);
1273
+ }
1274
+ execUpdate(e2, t2) {
1275
+ let s2 = this.evalExpr(e2.set.value, t2), i2 = null, n = null;
1276
+ e2.where && (i2 = e2.where.left.name, n = this.evalExpr(e2.where.right, t2)), this.db.update(e2.table, e2.set.column, s2, i2, n);
1277
+ }
1278
+ async evalReplyValue(e2, t2) {
1279
+ return "Call" === e2.type && "Response" === e2.callee ? this.execResponse(e2, t2) : this.evalExpr(e2, t2);
1280
+ }
1281
+ async execResponse(e2, t2) {
1282
+ if (!this.onResponse) throw Error("runtime error: Response() failed, no AI connected (missing onResponse handler)");
1283
+ let s2;
1284
+ if (0 === e2.args.length) {
1285
+ if (0 === this.responseConnections.size) throw Error("runtime error: Response() failed, no CONNECT RESPONSE found");
1286
+ if (this.responseConnections.size > 1) throw Error(`runtime error: Response() is ambiguous with multiple CONNECT RESPONSE (${[...this.responseConnections].join(", ")}); use Response(alias)`);
1287
+ s2 = [...this.responseConnections][0];
1288
+ } else {
1289
+ let i2 = e2.args[0];
1290
+ if ("Identifier" !== i2.type) throw Error("runtime error: Response(alias) expects an alias name, not a string or expression");
1291
+ if (s2 = i2.name, !this.responseConnections.has(s2)) throw Error(`runtime error: Response(${s2}) failed, no CONNECT RESPONSE ${s2} found`);
1292
+ }
1293
+ let n = this.envValues.get(s2), r = await this.onResponse({ alias: s2, token: n, message: t2.vars.message, client: t2.vars.client });
1294
+ return r;
1295
+ }
1296
+ evalExpr(e2, t2) {
1297
+ switch (e2.type) {
1298
+ case "Literal":
1299
+ return e2.value;
1300
+ case "Identifier":
1301
+ if ("env" === e2.name) return Object.fromEntries(this.envValues);
1302
+ if (Object.prototype.hasOwnProperty.call(t2.vars, e2.name)) return t2.vars[e2.name];
1303
+ if (this.envValues.has(e2.name)) return this.envValues.get(e2.name);
1304
+ throw Error(`runtime error: unknown identifier: "${e2.name}"`);
1305
+ case "Member": {
1306
+ let s2 = this.evalExpr(e2.object, t2);
1307
+ if (null == s2) return;
1308
+ return s2[e2.property];
1309
+ }
1310
+ case "Call": {
1311
+ if ("LAST_INSERT_ID" === e2.callee) return t2.lastInsertId;
1312
+ let i2 = this.nativeFuncs.get(e2.callee);
1313
+ if (!i2) throw Error(`runtime error: unknown function: "${e2.callee}"`);
1314
+ let n = e2.args.map((e3) => this.evalExpr(e3, t2));
1315
+ return i2(...n);
1316
+ }
1317
+ case "Binary": {
1318
+ let r = this.evalExpr(e2.left, t2), a = this.evalExpr(e2.right, t2);
1319
+ if ("+" === e2.op) {
1320
+ if ("number" == typeof r && "number" == typeof a) return r + a;
1321
+ return String(r) + String(a);
1322
+ }
1323
+ if ("=" === e2.op) return r === a;
1324
+ throw Error(`runtime error: unsupported operator: "${e2.op}"`);
1325
+ }
1326
+ default:
1327
+ throw Error(`runtime error: unsupported expression: ${e2.type}`);
1328
+ }
1329
+ }
1330
+ };
1331
+ module.exports = { BotQLInterpreter, MemoryDatabase: t };
1332
+ }
1333
+ });
1334
+ return require_botql();
1335
+ })();