stitchkit 0.100.3 → 0.101.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/CHANGELOG.md +67 -0
- package/dist/entrypoints/application.js +2 -1
- package/dist/entrypoints/observability.js +2 -1
- package/dist/entrypoints/telegram/screens.js +1 -1
- package/dist/entrypoints/telegram.d.ts +8 -4
- package/dist/entrypoints/telegram.d.ts.map +1 -1
- package/dist/entrypoints/telegram.js +277 -49
- package/dist/entrypoints/tools.js +2 -1
- package/dist/{index-6k1svmjh.js → index-e05cjgtf.js} +16 -13
- package/dist/{index-kzz6rq0w.js → index-e6c2spsd.js} +56 -18
- package/dist/index-sznqf8fx.js +20 -0
- package/dist/internal/secret-shapes.d.ts +21 -0
- package/dist/internal/secret-shapes.d.ts.map +1 -0
- package/dist/observability/sanitize.d.ts.map +1 -1
- package/dist/telegram/bot-api.d.ts +7 -4
- package/dist/telegram/bot-api.d.ts.map +1 -1
- package/dist/telegram/init-data.d.ts +3 -44
- package/dist/telegram/init-data.d.ts.map +1 -1
- package/dist/telegram/operator-channel.d.ts +1 -18
- package/dist/telegram/operator-channel.d.ts.map +1 -1
- package/dist/telegram/operator-dedupe.d.ts +44 -0
- package/dist/telegram/operator-dedupe.d.ts.map +1 -0
- package/dist/telegram/send-failure.d.ts +6 -0
- package/dist/telegram/send-failure.d.ts.map +1 -1
- package/dist/telegram/update-intake.d.ts +5 -1
- package/dist/telegram/update-intake.d.ts.map +1 -1
- package/dist/telegram/update-store-check.d.ts +22 -0
- package/dist/telegram/update-store-check.d.ts.map +1 -0
- package/dist/telegram/update-store-postgres.d.ts +33 -0
- package/dist/telegram/update-store-postgres.d.ts.map +1 -0
- package/dist/telegram/user.d.ts +51 -0
- package/dist/telegram/user.d.ts.map +1 -0
- package/llms/observability.txt +13 -1
- package/llms/server.4.txt +16 -0
- package/llms/stitchkit.txt +3 -3
- package/llms/telegram.txt +91 -13
- package/llms/{upgrading-0.74.0-0.100.0.txt → upgrading-0.74.0-0.101.0.txt} +39 -3
- package/llms/upgrading.txt +1 -1
- package/llms-full.txt +159 -17
- package/llms.txt +4 -4
- package/package.json +1 -1
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import {
|
|
2
|
+
maskSecretShapes
|
|
3
|
+
} from "./index-sznqf8fx.js";
|
|
1
4
|
import {
|
|
2
5
|
isUnsafeKey
|
|
3
6
|
} from "./index-zsarb5xp.js";
|
|
@@ -53,7 +56,7 @@ var DEFAULT_MAX_BYTES = 16000;
|
|
|
53
56
|
var DEFAULT_MAX_DEPTH = 20;
|
|
54
57
|
var DEFAULT_MAX_NODES = 20000;
|
|
55
58
|
var MASK = "[redacted]";
|
|
56
|
-
function secretMasker(options) {
|
|
59
|
+
function secretMasker(options, isSecretName) {
|
|
57
60
|
const values = [...options.sensitiveValues ?? []].filter((value) => value !== "").sort((left, right) => right.length - left.length);
|
|
58
61
|
const patterns = options.sensitiveUrlPatterns ?? [];
|
|
59
62
|
return (input) => {
|
|
@@ -66,20 +69,27 @@ function secretMasker(options) {
|
|
|
66
69
|
output = output.replace(global, MASK);
|
|
67
70
|
} catch {}
|
|
68
71
|
}
|
|
69
|
-
return output;
|
|
72
|
+
return maskSecretShapes(output, MASK, isSecretName);
|
|
70
73
|
};
|
|
71
74
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
75
|
+
var ERROR_CORE = new Set(["_type", "name", "message", "stack", "cause"]);
|
|
76
|
+
function isErrorFact(value) {
|
|
77
|
+
if (value === null || typeof value !== "object")
|
|
78
|
+
return typeof value !== "function";
|
|
79
|
+
if (Array.isArray(value) || value instanceof Error || value instanceof Date)
|
|
80
|
+
return true;
|
|
81
|
+
const prototype = Object.getPrototypeOf(value);
|
|
82
|
+
return prototype === Object.prototype || prototype === null;
|
|
83
|
+
}
|
|
84
|
+
function kindOf(value) {
|
|
85
|
+
if (typeof value === "function")
|
|
86
|
+
return "function";
|
|
87
|
+
const maker = isRecord(value) ? value.constructor : undefined;
|
|
88
|
+
return typeof maker === "function" && maker.name !== "" ? maker.name : "object";
|
|
89
|
+
}
|
|
90
|
+
function pathMatcher(patterns) {
|
|
91
|
+
const matchers = patterns.map((pattern) => pattern.split("."));
|
|
92
|
+
const matches = (matcher, path, patternIndex = 0, pathIndex = 0) => {
|
|
83
93
|
if (patternIndex === matcher.length)
|
|
84
94
|
return pathIndex === path.length;
|
|
85
95
|
const segment = matcher[patternIndex];
|
|
@@ -87,21 +97,32 @@ function redact2(value, options = {}) {
|
|
|
87
97
|
if (patternIndex + 1 === matcher.length)
|
|
88
98
|
return true;
|
|
89
99
|
for (let next = pathIndex;next <= path.length; next += 1) {
|
|
90
|
-
if (
|
|
100
|
+
if (matches(matcher, path, patternIndex + 1, next))
|
|
91
101
|
return true;
|
|
92
102
|
}
|
|
93
103
|
return false;
|
|
94
104
|
}
|
|
95
105
|
if (pathIndex === path.length || segment !== "*" && segment !== path[pathIndex])
|
|
96
106
|
return false;
|
|
97
|
-
return
|
|
107
|
+
return matches(matcher, path, patternIndex + 1, pathIndex + 1);
|
|
98
108
|
};
|
|
99
|
-
|
|
109
|
+
return (path) => matchers.some((matcher) => matches(matcher, path));
|
|
110
|
+
}
|
|
111
|
+
function redact2(value, options = {}) {
|
|
112
|
+
const custom = options.sensitiveKeys;
|
|
113
|
+
const isSensitive = custom === undefined ? isSensitiveKeyDefault : (key) => {
|
|
114
|
+
custom.lastIndex = 0;
|
|
115
|
+
return custom.test(key) || options.includeDefaultSensitiveKeys === true && isSensitiveKeyDefault(key);
|
|
116
|
+
};
|
|
117
|
+
const maxDepth = options.maxDepth ?? DEFAULT_MAX_DEPTH;
|
|
118
|
+
const maxStringLength = options.maxStringLength ?? Number.POSITIVE_INFINITY;
|
|
119
|
+
const maxCollectionLength = options.maxCollectionLength ?? Number.POSITIVE_INFINITY;
|
|
120
|
+
const isSensitivePath = pathMatcher(options.sensitivePaths ?? []);
|
|
100
121
|
const stringBoundFor = (key) => {
|
|
101
122
|
const byKey = key === undefined ? undefined : options.maxStringLengthByKey?.[key];
|
|
102
123
|
return byKey ?? maxStringLength;
|
|
103
124
|
};
|
|
104
|
-
const mask = secretMasker(options);
|
|
125
|
+
const mask = secretMasker(options, isSensitive);
|
|
105
126
|
const safeString = (input, key) => {
|
|
106
127
|
const bound = stringBoundFor(key);
|
|
107
128
|
const output = mask(input);
|
|
@@ -176,14 +197,31 @@ function redact2(value, options = {}) {
|
|
|
176
197
|
return "[unreadable]";
|
|
177
198
|
}
|
|
178
199
|
};
|
|
200
|
+
if (ancestors.has(input))
|
|
201
|
+
return "[circular]";
|
|
202
|
+
ancestors.add(input);
|
|
179
203
|
const stack = property("stack");
|
|
180
204
|
const cause = property("cause");
|
|
205
|
+
const own = {};
|
|
206
|
+
for (const key of Object.keys(input)) {
|
|
207
|
+
if (isUnsafeKey(key) || ERROR_CORE.has(key))
|
|
208
|
+
continue;
|
|
209
|
+
const childPath = [...path, key];
|
|
210
|
+
try {
|
|
211
|
+
const field = Reflect.get(input, key);
|
|
212
|
+
own[key] = isSensitive(key) || isSensitivePath(childPath) ? MASK : isErrorFact(field) ? walk(field, depth + 1, childPath) : `[${kindOf(field)}]`;
|
|
213
|
+
} catch {
|
|
214
|
+
own[key] = "[unreadable]";
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
ancestors.delete(input);
|
|
181
218
|
return {
|
|
182
219
|
_type: "error",
|
|
183
220
|
name: safeString(String(property("name")), "name"),
|
|
184
221
|
message: safeString(String(property("message")), "message"),
|
|
185
222
|
...stack !== undefined && { stack: safeString(String(stack), "stack") },
|
|
186
|
-
...cause !== undefined && { cause: walk(cause, depth + 1, [...path, "cause"]) }
|
|
223
|
+
...cause !== undefined && { cause: walk(cause, depth + 1, [...path, "cause"]) },
|
|
224
|
+
...own
|
|
187
225
|
};
|
|
188
226
|
}
|
|
189
227
|
if (Array.isArray(input)) {
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// src/internal/secret-shapes.ts
|
|
2
|
+
var BOT_TOKEN_SECRET = /(?<=\d{5,}:)[A-Za-z0-9_-]{30,}/;
|
|
3
|
+
var URL_PASSWORD = /(\b[a-z][a-z0-9+.-]*:\/\/[^\s/?#@:"'<>]*:)([^\s/?#@"'<>]+)(?=@)/gi;
|
|
4
|
+
var QUERY_PARAMETER = /([?&;])([^=&#\s"'<>]+)=([^&#\s"'<>]*)/g;
|
|
5
|
+
var QUERY_SECRET_NAMES = new Set(["key", "sig", "signature"]);
|
|
6
|
+
function decoded(name) {
|
|
7
|
+
try {
|
|
8
|
+
return decodeURIComponent(name);
|
|
9
|
+
} catch {
|
|
10
|
+
return name;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function maskSecretShapes(input, mask, isSecretName) {
|
|
14
|
+
return input.replace(new RegExp(BOT_TOKEN_SECRET.source, "g"), mask).replace(URL_PASSWORD, `$1${mask}`).replace(QUERY_PARAMETER, (whole, separator, name) => {
|
|
15
|
+
const plain = decoded(name);
|
|
16
|
+
return isSecretName(plain) || QUERY_SECRET_NAMES.has(plain.toLowerCase()) ? `${separator}${name}=${mask}` : whole;
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export { BOT_TOKEN_SECRET, maskSecretShapes };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secrets recognised by their shape, with no list of values to match.
|
|
3
|
+
*
|
|
4
|
+
* A journal line carries secrets nobody named: a request URL with a bot token
|
|
5
|
+
* in its path, a database address with its password, a callback with
|
|
6
|
+
* `?token=` — often another process's, so not in this one's environment. Each
|
|
7
|
+
* shape masks only the secret part, so the line still says which bot, which
|
|
8
|
+
* host, which parameter.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* The secret half of a Telegram bot token, after the bot's numeric id and the
|
|
12
|
+
* colon. The id is not a secret — it is the bot's public user id — and it is
|
|
13
|
+
* what tells two bots' failures apart in a journal.
|
|
14
|
+
*/
|
|
15
|
+
export declare const BOT_TOKEN_SECRET: RegExp;
|
|
16
|
+
/**
|
|
17
|
+
* Mask every shaped secret in `input` with `mask`. `isSecretName` decides a
|
|
18
|
+
* query parameter by its name, the same test a field name gets.
|
|
19
|
+
*/
|
|
20
|
+
export declare function maskSecretShapes(input: string, mask: string, isSecretName: (name: string) => boolean): string;
|
|
21
|
+
//# sourceMappingURL=secret-shapes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secret-shapes.d.ts","sourceRoot":"","sources":["../../src/internal/secret-shapes.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,QAAmC,CAAC;AAuBjE;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,GACtC,MAAM,CAUR"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sanitize.d.ts","sourceRoot":"","sources":["../../src/observability/sanitize.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sanitize.d.ts","sourceRoot":"","sources":["../../src/observability/sanitize.ts"],"names":[],"mappings":"AAQA,8EAA8E;AAC9E,MAAM,MAAM,SAAS,GACjB,MAAM,GACN,MAAM,GACN,OAAO,GACP,IAAI,GACJ,SAAS,EAAE,GACX;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAC;AAEjC,+CAA+C;AAC/C,MAAM,WAAW,eAAe;IAC9B;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,wEAAwE;IACxE,2BAA2B,CAAC,EAAE,OAAO,CAAC;IACtC,8EAA8E;IAC9E,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,uFAAuF;IACvF,oBAAoB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC;;;;OAIG;IACH,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,6FAA6F;IAC7F,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yEAAyE;IACzE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;;;;;;;;;OAaG;IACH,oBAAoB,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACxD,mEAAmE;IACnE,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,mCAAmC;AACnC,MAAM,WAAW,WAAW;IAC1B,wEAAwE;IACxE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,8BAA8B;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,wEAAwE;IACxE,cAAc,CAAC,EAAE,IAAI,CAAC;CACvB;AAwJD;;;;;;GAMG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,GAAE,eAAoB,GAAG,SAAS,CA4L/E;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,SAAoB,GAAG,SAAS,CAgBzF;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,OAAO,EACd,OAAO,GAAE,eAAoB,GAC5B,SAAS,GAAG,IAAI,CAOlB;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,WAAW,CAqBvD"}
|
|
@@ -8,13 +8,16 @@
|
|
|
8
8
|
* reads `error_code` and `parameters.retry_after` before it ever reads prose.
|
|
9
9
|
*/
|
|
10
10
|
/**
|
|
11
|
-
* The
|
|
11
|
+
* The secret half of a bot token — what follows the bot's numeric id and the
|
|
12
|
+
* colon. The id stays readable: it is the bot's public user id, and the only
|
|
13
|
+
* thing in a masked line that tells which bot failed.
|
|
12
14
|
*
|
|
13
|
-
* For masking, not for validation
|
|
14
|
-
* `
|
|
15
|
-
* written to the journal.
|
|
15
|
+
* For masking, not for validation. `redact` and every logger built on it mask
|
|
16
|
+
* it already; `redactTelegramBotToken` masks it in a string of your own.
|
|
16
17
|
*/
|
|
17
18
|
export declare const TELEGRAM_BOT_TOKEN_PATTERN: RegExp;
|
|
19
|
+
/** Every bot token in `text` with its secret half masked: `123456:[redacted]`. */
|
|
20
|
+
export declare function redactTelegramBotToken(text: string): string;
|
|
18
21
|
/** Telegram refused the call. Carries its answer as fields, never the token. */
|
|
19
22
|
export declare class TelegramBotApiError extends Error {
|
|
20
23
|
readonly method: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bot-api.d.ts","sourceRoot":"","sources":["../../src/telegram/bot-api.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"bot-api.d.ts","sourceRoot":"","sources":["../../src/telegram/bot-api.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH;;;;;;;GAOG;AACH,eAAO,MAAM,0BAA0B,EAAE,MAAyB,CAAC;AAEnE,kFAAkF;AAClF,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE3D;AAID,gFAAgF;AAChF,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE;QAAE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAE7F,YACE,MAAM,EAAE,MAAM,EACd,MAAM,EAAE;QACN,UAAU,EAAE,MAAM,CAAC;QACnB,WAAW,EAAE,MAAM,CAAC;QACpB,UAAU,CAAC,EAAE,mBAAmB,CAAC,YAAY,CAAC,CAAC;KAChD,EAQF;CACF;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACpD,iFAAiF;IACjF,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CAC/B;AAQD;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,OAAO,CAAC,CAuCnF"}
|
|
@@ -22,52 +22,12 @@
|
|
|
22
22
|
* Not a domain model (→ ADR 0002): "Telegram signed this string" is a fact
|
|
23
23
|
* about a transport, the same kind of fact as a bearer token being well-formed.
|
|
24
24
|
*/
|
|
25
|
-
import {
|
|
26
|
-
/**
|
|
27
|
-
* The Telegram user record, as our surface names things.
|
|
28
|
-
*
|
|
29
|
-
* Telegram's wire fields are `snake_case`; every other name this package
|
|
30
|
-
* publishes is `camelCase`, and a surface that switches convention because of
|
|
31
|
-
* where a value came from makes the caller remember which is which. The
|
|
32
|
-
* untouched wire pairs stay available in `raw`.
|
|
33
|
-
*/
|
|
34
|
-
declare const TelegramInitDataUserSchema: z.ZodPipe<z.ZodObject<{
|
|
35
|
-
id: z.ZodInt;
|
|
36
|
-
is_bot: z.ZodOptional<z.ZodBoolean>;
|
|
37
|
-
first_name: z.ZodString;
|
|
38
|
-
last_name: z.ZodOptional<z.ZodString>;
|
|
39
|
-
username: z.ZodOptional<z.ZodString>;
|
|
40
|
-
language_code: z.ZodOptional<z.ZodString>;
|
|
41
|
-
is_premium: z.ZodOptional<z.ZodBoolean>;
|
|
42
|
-
allows_write_to_pm: z.ZodOptional<z.ZodBoolean>;
|
|
43
|
-
photo_url: z.ZodOptional<z.ZodString>;
|
|
44
|
-
}, z.core.$strip>, z.ZodTransform<{
|
|
45
|
-
id: number;
|
|
46
|
-
firstName: string;
|
|
47
|
-
isBot?: boolean | undefined;
|
|
48
|
-
lastName?: string | undefined;
|
|
49
|
-
username?: string | undefined;
|
|
50
|
-
languageCode?: string | undefined;
|
|
51
|
-
isPremium?: boolean | undefined;
|
|
52
|
-
allowsWriteToPm?: boolean | undefined;
|
|
53
|
-
photoUrl?: string | undefined;
|
|
54
|
-
}, {
|
|
55
|
-
id: number;
|
|
56
|
-
is_bot?: boolean | undefined;
|
|
57
|
-
first_name: string;
|
|
58
|
-
last_name?: string | undefined;
|
|
59
|
-
username?: string | undefined;
|
|
60
|
-
language_code?: string | undefined;
|
|
61
|
-
is_premium?: boolean | undefined;
|
|
62
|
-
allows_write_to_pm?: boolean | undefined;
|
|
63
|
-
photo_url?: string | undefined;
|
|
64
|
-
}>>;
|
|
65
|
-
export type TelegramInitDataUser = z.infer<typeof TelegramInitDataUserSchema>;
|
|
25
|
+
import { type TelegramUser } from './user.js';
|
|
66
26
|
export interface TelegramInitData {
|
|
67
27
|
/** Absent when the Mini App was opened somewhere Telegram sends no user. */
|
|
68
|
-
user?:
|
|
28
|
+
user?: TelegramUser;
|
|
69
29
|
/** Whose chat the Mini App was opened from, when Telegram named one. */
|
|
70
|
-
receiver?:
|
|
30
|
+
receiver?: TelegramUser;
|
|
71
31
|
/** When Telegram signed this string. */
|
|
72
32
|
authDate: Date;
|
|
73
33
|
/** How old the signature was at verification time. */
|
|
@@ -130,5 +90,4 @@ export interface VerifyTelegramInitDataOptions {
|
|
|
130
90
|
* being configured wrong, and it throws.
|
|
131
91
|
*/
|
|
132
92
|
export declare function verifyTelegramInitData(options: VerifyTelegramInitDataOptions): Promise<TelegramInitDataVerification>;
|
|
133
|
-
export {};
|
|
134
93
|
//# sourceMappingURL=init-data.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init-data.d.ts","sourceRoot":"","sources":["../../src/telegram/init-data.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;
|
|
1
|
+
{"version":3,"file":"init-data.d.ts","sourceRoot":"","sources":["../../src/telegram/init-data.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAGH,OAAO,EAAqB,KAAK,YAAY,EAAE,MAAM,QAAQ,CAAC;AAI9D,MAAM,WAAW,gBAAgB;IAC/B,4EAA4E;IAC5E,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,wEAAwE;IACxE,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,wCAAwC;IACxC,QAAQ,EAAE,IAAI,CAAC;IACf,sDAAsD;IACtD,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;;OAMG;IACH,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACvC;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,uBAAuB,GAC/B,cAAc,GACd,oBAAoB,GACpB,WAAW,GACX,SAAS,CAAC;AAEd,MAAM,MAAM,4BAA4B,GACpC;IAAE,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAA;CAAE,GACzD;IACE,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,uBAAuB,CAAC;IACzC,0EAA0E;IAC1E,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B,CAAC;AAEN,MAAM,WAAW,6BAA6B;IAC5C,yDAAyD;IACzD,QAAQ,EAAE,MAAM,CAAC;IACjB,mEAAmE;IACnE,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gEAAgE;IAChE,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAUD;;;;;;;;GAQG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,6BAA6B,GACrC,OAAO,CAAC,4BAA4B,CAAC,CAuEvC"}
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* What to post stays the application's. This owns the queue, the pacing, the
|
|
14
14
|
* topics, 429s and the bounded way down.
|
|
15
15
|
*/
|
|
16
|
+
import { type TelegramOperatorDedupe } from './operator-dedupe.js';
|
|
16
17
|
import { type TelegramSendFailure } from './send-failure.js';
|
|
17
18
|
export type TelegramChatId = number | string;
|
|
18
19
|
export interface TelegramOperatorMessage {
|
|
@@ -66,24 +67,6 @@ export interface TelegramOperatorChannelConfig<TTopic extends string> {
|
|
|
66
67
|
*/
|
|
67
68
|
readonly dedupe?: TelegramOperatorDedupe<TTopic>;
|
|
68
69
|
}
|
|
69
|
-
export interface TelegramOperatorDedupe<TTopic extends string> {
|
|
70
|
-
/** Default 600 000 — ten minutes. */
|
|
71
|
-
readonly windowMs?: number;
|
|
72
|
-
/** Messages sent per window, whatever their fingerprint. Default 20. */
|
|
73
|
-
readonly maxPerWindow?: number;
|
|
74
|
-
/**
|
|
75
|
-
* What makes two messages the same. Default: the topic and the text with
|
|
76
|
-
* numbers, UUIDs and long identifiers blanked — they differ between two
|
|
77
|
-
* occurrences of one failure without making it another.
|
|
78
|
-
*/
|
|
79
|
-
readonly fingerprint?: (text: string, topic: TTopic | undefined) => string;
|
|
80
|
-
/** The line added to a message after `count` of its repeats were held back. */
|
|
81
|
-
readonly repeatedLine?: (count: number) => string;
|
|
82
|
-
/** The line added to the first message after `count` were held back by the budget. */
|
|
83
|
-
readonly overBudgetLine?: (count: number) => string;
|
|
84
|
-
/** Default `Date.now`. */
|
|
85
|
-
readonly now?: () => number;
|
|
86
|
-
}
|
|
87
70
|
export interface TelegramOperatorChannel<TTopic extends string> {
|
|
88
71
|
/** Queue one message. Returns at once and never throws. */
|
|
89
72
|
post(text: string, topic?: TTopic): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"operator-channel.d.ts","sourceRoot":"","sources":["../../src/telegram/operator-channel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAKH,OAAO,EAA+B,KAAK,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAEvF,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,MAAM,CAAC;AAyB7C,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,iDAAiD;IACjD,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,oBAAoB,CAAC,MAAM,SAAS,MAAM;IACzD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;;;OAMG;IACH,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,SAAS,GAAG,UAAU,GAAG,QAAQ,GAAG,UAAU,GAAG,aAAa,CAAC;IAC7F,QAAQ,CAAC,OAAO,CAAC,EAAE,mBAAmB,CAAC;CACxC;AAED,MAAM,WAAW,6BAA6B,CAAC,MAAM,SAAS,MAAM;IAClE,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,2FAA2F;IAC3F,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;IAC5D,mFAAmF;IACnF,QAAQ,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,uBAAuB,EAAE,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3F,4DAA4D;IAC5D,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,gGAAgG;IAChG,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,4DAA4D;IAC5D,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,oDAAoD;IACpD,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,oBAAoB,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC;IAClE,sDAAsD;IACtD,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9E;;;;OAIG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/C,gFAAgF;IAChF,QAAQ,CAAC,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7C;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,sBAAsB,CAAC,MAAM,CAAC,CAAC;CAClD;AAED,MAAM,WAAW,
|
|
1
|
+
{"version":3,"file":"operator-channel.d.ts","sourceRoot":"","sources":["../../src/telegram/operator-channel.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Repeats and storms held back before they reach a person.
|
|
3
|
+
*
|
|
4
|
+
* One failure in a loop is a hundred identical alerts, and a hundred alerts
|
|
5
|
+
* is a muted chat — after which the one that mattered is not read either. So
|
|
6
|
+
* the same message, by fingerprint, goes once per window and the next one
|
|
7
|
+
* that goes says how many were held back; past a budget per window the rest
|
|
8
|
+
* are counted and the count rides on the first message after. The operator
|
|
9
|
+
* channel uses it through its `dedupe` option; it is exported for senders of
|
|
10
|
+
* their own.
|
|
11
|
+
*/
|
|
12
|
+
export interface TelegramOperatorDedupe<TTopic extends string> {
|
|
13
|
+
/** Default 600 000 — ten minutes. */
|
|
14
|
+
readonly windowMs?: number;
|
|
15
|
+
/** Messages sent per window, whatever their fingerprint. Default 20. */
|
|
16
|
+
readonly maxPerWindow?: number;
|
|
17
|
+
/**
|
|
18
|
+
* What makes two messages the same. Default: the topic and the text with
|
|
19
|
+
* numbers, UUIDs and long identifiers blanked — they differ between two
|
|
20
|
+
* occurrences of one failure without making it another.
|
|
21
|
+
*/
|
|
22
|
+
readonly fingerprint?: (text: string, topic: TTopic | undefined) => string;
|
|
23
|
+
/** The line added to a message after `count` of its repeats were held back. */
|
|
24
|
+
readonly repeatedLine?: (count: number) => string;
|
|
25
|
+
/** The line added to the first message after `count` were held back by the budget. */
|
|
26
|
+
readonly overBudgetLine?: (count: number) => string;
|
|
27
|
+
/** Default `Date.now`. */
|
|
28
|
+
readonly now?: () => number;
|
|
29
|
+
}
|
|
30
|
+
/** Send `text` — the message with its held-back counts — or hold it back, with why. */
|
|
31
|
+
export type TelegramOperatorDedupeVerdict = {
|
|
32
|
+
readonly send: true;
|
|
33
|
+
readonly text: string;
|
|
34
|
+
} | {
|
|
35
|
+
readonly send: false;
|
|
36
|
+
readonly reason: 'repeated' | 'over-budget';
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* The dedupe window on its own, for an application that sends alerts itself —
|
|
40
|
+
* its own bot, its own retry — and wants only the decision: whether this
|
|
41
|
+
* message goes, and the text it goes with.
|
|
42
|
+
*/
|
|
43
|
+
export declare function createTelegramOperatorDedupe<TTopic extends string = never>(options?: TelegramOperatorDedupe<TTopic>): (text: string, topic?: TTopic) => TelegramOperatorDedupeVerdict;
|
|
44
|
+
//# sourceMappingURL=operator-dedupe.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"operator-dedupe.d.ts","sourceRoot":"","sources":["../../src/telegram/operator-dedupe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,WAAW,sBAAsB,CAAC,MAAM,SAAS,MAAM;IAC3D,qCAAqC;IACrC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,wEAAwE;IACxE,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;OAIG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,KAAK,MAAM,CAAC;IAC3E,+EAA+E;IAC/E,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IAClD,sFAAsF;IACtF,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IACpD,0BAA0B;IAC1B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CAC7B;AAUD,uFAAuF;AACvF,MAAM,MAAM,6BAA6B,GACrC;IAAE,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC9C;IAAE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,aAAa,CAAA;CAAE,CAAC;AAE1E;;;;GAIG;AACH,wBAAgB,4BAA4B,CAAC,MAAM,SAAS,MAAM,GAAG,KAAK,EACxE,OAAO,GAAE,sBAAsB,CAAC,MAAM,CAAM,GAC3C,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,6BAA6B,CAwCjE"}
|
|
@@ -63,6 +63,12 @@ export interface TelegramSendFailure {
|
|
|
63
63
|
*/
|
|
64
64
|
evidence: 'parameters' | 'status' | 'description' | 'none';
|
|
65
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* Every reason, for a consumer that maps each one to its own code or text. A
|
|
68
|
+
* `Record<TelegramSendFailureReason, …>` beside it fails to compile when a
|
|
69
|
+
* release adds a reason, instead of falling through to a default at run time.
|
|
70
|
+
*/
|
|
71
|
+
export declare const TELEGRAM_SEND_FAILURE_REASONS: readonly TelegramSendFailureReason[];
|
|
66
72
|
/**
|
|
67
73
|
* Classify one refused send.
|
|
68
74
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"send-failure.d.ts","sourceRoot":"","sources":["../../src/telegram/send-failure.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAIH;;;;;;;;;GASG;AACH,MAAM,MAAM,yBAAyB,GACjC,iBAAiB,GACjB,kBAAkB,GAClB,gBAAgB,GAChB,aAAa,GACb,cAAc,GACd,iBAAiB,GACjB,cAAc,GACd,SAAS,CAAC;AAEd,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,yBAAyB,CAAC;IAClC,6DAA6D;IAC7D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,wDAAwD;IACxD,SAAS,EAAE,OAAO,CAAC;IACnB;;;;;;;;OAQG;IACH,oBAAoB,EAAE,OAAO,CAAC;IAC9B;;;;OAIG;IACH,QAAQ,EAAE,YAAY,GAAG,QAAQ,GAAG,aAAa,GAAG,MAAM,CAAC;CAC5D;
|
|
1
|
+
{"version":3,"file":"send-failure.d.ts","sourceRoot":"","sources":["../../src/telegram/send-failure.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAIH;;;;;;;;;GASG;AACH,MAAM,MAAM,yBAAyB,GACjC,iBAAiB,GACjB,kBAAkB,GAClB,gBAAgB,GAChB,aAAa,GACb,cAAc,GACd,iBAAiB,GACjB,cAAc,GACd,SAAS,CAAC;AAEd,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,yBAAyB,CAAC;IAClC,6DAA6D;IAC7D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,wDAAwD;IACxD,SAAS,EAAE,OAAO,CAAC;IACnB;;;;;;;;OAQG;IACH,oBAAoB,EAAE,OAAO,CAAC;IAC9B;;;;OAIG;IACH,QAAQ,EAAE,YAAY,GAAG,QAAQ,GAAG,aAAa,GAAG,MAAM,CAAC;CAC5D;AA6CD;;;;GAIG;AACH,eAAO,MAAM,6BAA6B,EAAE,SAAS,yBAAyB,EAC7B,CAAC;AAyClD;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,GAAG,mBAAmB,CAyD/E;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,mBAAmB,GAC3B,cAAc,GACd,cAAc,GACd,gBAAgB,GAChB,eAAe,GACf,OAAO,CAAC;AAeZ,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,GAAG,mBAAmB,CAM/E"}
|
|
@@ -34,7 +34,11 @@ export interface TelegramUpdateFailure {
|
|
|
34
34
|
export type TelegramUpdateStoreStep = 'claim' | 'renew' | 'settle' | 'sweep';
|
|
35
35
|
export interface TelegramUpdateIntakeConfig<TUpdate extends TelegramUpdateEnvelope> {
|
|
36
36
|
readonly store: TelegramUpdateStore;
|
|
37
|
-
/**
|
|
37
|
+
/**
|
|
38
|
+
* Handle one update — with grammY, `(update) => bot.handleUpdate(update)`.
|
|
39
|
+
* A grammY `BotError` is unwrapped: `retry`, `onFailure` and the stored
|
|
40
|
+
* error see what the middleware threw.
|
|
41
|
+
*/
|
|
38
42
|
readonly handle: (update: TUpdate) => unknown;
|
|
39
43
|
/**
|
|
40
44
|
* After a failed attempt: milliseconds until the next, or `false` to
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update-intake.d.ts","sourceRoot":"","sources":["../../src/telegram/update-intake.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAC;AAE1D,mFAAmF;AACnF,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,kEAAkE;IAClE,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,MAAM,uBAAuB,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;AAE7E,MAAM,WAAW,0BAA0B,CAAC,OAAO,SAAS,sBAAsB;IAChF,QAAQ,CAAC,KAAK,EAAE,mBAAmB,CAAC;IACpC
|
|
1
|
+
{"version":3,"file":"update-intake.d.ts","sourceRoot":"","sources":["../../src/telegram/update-intake.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAC;AAE1D,mFAAmF;AACnF,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,kEAAkE;IAClE,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,MAAM,uBAAuB,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;AAE7E,MAAM,WAAW,0BAA0B,CAAC,OAAO,SAAS,sBAAsB;IAChF,QAAQ,CAAC,KAAK,EAAE,mBAAmB,CAAC;IACpC;;;;OAIG;IACH,QAAQ,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,OAAO,CAAC;IAC9C;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,KAAK,MAAM,GAAG,KAAK,CAAC;IACrE,iBAAiB;IACjB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,qGAAqG;IACrG,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,kGAAkG;IAClG,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,sBAAsB;IACtB,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,2FAA2F;IAC3F,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,yCAAyC;IACzC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,4BAA4B;IAC5B,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,qBAAqB,KAAK,IAAI,CAAC;IAC9D,6EAA6E;IAC7E,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,uBAAuB,KAAK,IAAI,CAAC;IAChF,0BAA0B;IAC1B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,oBAAoB;IACnC,kFAAkF;IAClF,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAAC;IACxD,iEAAiE;IACjE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,kEAAkE;IAClE,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IACzB,0CAA0C;IAC1C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,2DAA2D;IAC3D,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAsED,wBAAgB,0BAA0B,CAAC,OAAO,SAAS,sBAAsB,EAC/E,MAAM,EAAE,0BAA0B,CAAC,OAAO,CAAC,GAC1C,oBAAoB,CAiJtB"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The rules of `TelegramUpdateStore`, as a check any store can be put
|
|
3
|
+
* through — the ones shipped here and an application's own over its ORM.
|
|
4
|
+
*
|
|
5
|
+
* A store is five conditional statements, and each is easy to write almost
|
|
6
|
+
* right: a claim that does not check the lease, a settle that does not check
|
|
7
|
+
* the attempt, a claim read and then written in two statements so that two
|
|
8
|
+
* processes both take one update. Nothing fails in a test with one process;
|
|
9
|
+
* the update simply runs twice in production. So the check runs every rule on
|
|
10
|
+
* a fresh store, and races claims against each other.
|
|
11
|
+
*
|
|
12
|
+
* It does not depend on a test framework: it returns what was violated, and
|
|
13
|
+
* an empty list is a store that keeps the rules.
|
|
14
|
+
*/
|
|
15
|
+
import type { TelegramUpdateStore } from './update-store.js';
|
|
16
|
+
/**
|
|
17
|
+
* Put a store through every rule, each on a fresh store from `make`. Resolves
|
|
18
|
+
* with the violations — `"<rule>: <what happened>"` — empty when the store
|
|
19
|
+
* keeps them all. A store that throws violates the rule it threw in.
|
|
20
|
+
*/
|
|
21
|
+
export declare function checkTelegramUpdateStore(make: () => TelegramUpdateStore | Promise<TelegramUpdateStore>): Promise<string[]>;
|
|
22
|
+
//# sourceMappingURL=update-store-check.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update-store-check.d.ts","sourceRoot":"","sources":["../../src/telegram/update-store-check.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAqG1D;;;;GAIG;AACH,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,MAAM,mBAAmB,GAAG,OAAO,CAAC,mBAAmB,CAAC,GAC7D,OAAO,CAAC,MAAM,EAAE,CAAC,CAYnB"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The update store in Postgres, through whatever client the application
|
|
3
|
+
* already holds — one function that runs a statement with `$n` parameters and
|
|
4
|
+
* returns its rows:
|
|
5
|
+
*
|
|
6
|
+
* - Bun: `(text, parameters) => sql.unsafe(text, [...parameters])`
|
|
7
|
+
* - Prisma: `(text, parameters) => prisma.$queryRawUnsafe(text, ...parameters)`
|
|
8
|
+
* - `pg`: `(text, parameters) => pool.query(text, [...parameters]).then((result) => result.rows)`
|
|
9
|
+
*
|
|
10
|
+
* Each rule of `TelegramUpdateStore` is one conditional statement, so a claim
|
|
11
|
+
* is atomic across every process sharing the table; every statement returns
|
|
12
|
+
* rows, so a client that has only a "query" call needs nothing else.
|
|
13
|
+
* Parameters carry explicit casts: a driver that sends a JS number as a
|
|
14
|
+
* double still lands in a `bigint` column.
|
|
15
|
+
*/
|
|
16
|
+
import type { TelegramUpdateStore } from './update-store.js';
|
|
17
|
+
/** Run one statement with `$1…$n` parameters; resolve with the rows it returned. */
|
|
18
|
+
export type TelegramPostgresQuery = (text: string, parameters: readonly (string | number | null)[]) => Promise<readonly unknown[]>;
|
|
19
|
+
export interface PostgresTelegramUpdateStoreConfig {
|
|
20
|
+
readonly query: TelegramPostgresQuery;
|
|
21
|
+
/** Default `telegram_updates`. */
|
|
22
|
+
readonly table?: string;
|
|
23
|
+
/**
|
|
24
|
+
* Create the table on first use when it is missing. Default `true`; set
|
|
25
|
+
* `false` when migrations own the schema — `postgresTelegramUpdateStoreSchema`
|
|
26
|
+
* is the statement to put in one.
|
|
27
|
+
*/
|
|
28
|
+
readonly createTable?: boolean;
|
|
29
|
+
}
|
|
30
|
+
/** The table and its index, for a migration. Safe to run twice. */
|
|
31
|
+
export declare function postgresTelegramUpdateStoreSchema(table?: string): string;
|
|
32
|
+
export declare function postgresTelegramUpdateStore(config: PostgresTelegramUpdateStoreConfig): TelegramUpdateStore;
|
|
33
|
+
//# sourceMappingURL=update-store-postgres.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update-store-postgres.d.ts","sourceRoot":"","sources":["../../src/telegram/update-store-postgres.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAA4B,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAEpF,oFAAoF;AACpF,MAAM,MAAM,qBAAqB,GAAG,CAClC,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,SAAS,CAAC,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC,EAAE,KAC5C,OAAO,CAAC,SAAS,OAAO,EAAE,CAAC,CAAC;AAEjC,MAAM,WAAW,iCAAiC;IAChD,QAAQ,CAAC,KAAK,EAAE,qBAAqB,CAAC;IACtC,kCAAkC;IAClC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB;;;;OAIG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;CAChC;AAaD,mEAAmE;AACnE,wBAAgB,iCAAiC,CAAC,KAAK,SAAqB,GAAG,MAAM,CAapF;AAqBD,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,iCAAiC,GACxC,mBAAmB,CAyGrB"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A Telegram user in one shape, wherever it came from.
|
|
3
|
+
*
|
|
4
|
+
* Telegram sends a user in `snake_case` twice over — the Bot API's `from` on
|
|
5
|
+
* every update, the Mini App's signed `user` — and every other name this
|
|
6
|
+
* package publishes is `camelCase`. An application with one `ensureAccount`
|
|
7
|
+
* receives both, and a surface that switched convention by source would make
|
|
8
|
+
* it write the same translation twice. So both pass through this schema and
|
|
9
|
+
* arrive as the same `TelegramUser`.
|
|
10
|
+
*/
|
|
11
|
+
import { z } from 'zod';
|
|
12
|
+
declare const TelegramUserSchema: z.ZodPipe<z.ZodObject<{
|
|
13
|
+
id: z.ZodInt;
|
|
14
|
+
is_bot: z.ZodOptional<z.ZodBoolean>;
|
|
15
|
+
first_name: z.ZodString;
|
|
16
|
+
last_name: z.ZodOptional<z.ZodString>;
|
|
17
|
+
username: z.ZodOptional<z.ZodString>;
|
|
18
|
+
language_code: z.ZodOptional<z.ZodString>;
|
|
19
|
+
is_premium: z.ZodOptional<z.ZodBoolean>;
|
|
20
|
+
allows_write_to_pm: z.ZodOptional<z.ZodBoolean>;
|
|
21
|
+
photo_url: z.ZodOptional<z.ZodString>;
|
|
22
|
+
}, z.core.$strip>, z.ZodTransform<{
|
|
23
|
+
id: number;
|
|
24
|
+
firstName: string;
|
|
25
|
+
isBot?: boolean | undefined;
|
|
26
|
+
lastName?: string | undefined;
|
|
27
|
+
username?: string | undefined;
|
|
28
|
+
languageCode?: string | undefined;
|
|
29
|
+
isPremium?: boolean | undefined;
|
|
30
|
+
allowsWriteToPm?: boolean | undefined;
|
|
31
|
+
photoUrl?: string | undefined;
|
|
32
|
+
}, {
|
|
33
|
+
id: number;
|
|
34
|
+
is_bot?: boolean | undefined;
|
|
35
|
+
first_name: string;
|
|
36
|
+
last_name?: string | undefined;
|
|
37
|
+
username?: string | undefined;
|
|
38
|
+
language_code?: string | undefined;
|
|
39
|
+
is_premium?: boolean | undefined;
|
|
40
|
+
allows_write_to_pm?: boolean | undefined;
|
|
41
|
+
photo_url?: string | undefined;
|
|
42
|
+
}>>;
|
|
43
|
+
/** A Telegram user as this package names it; fields Telegram did not send are absent. */
|
|
44
|
+
export type TelegramUser = z.infer<typeof TelegramUserSchema>;
|
|
45
|
+
/**
|
|
46
|
+
* Telegram's user record — grammY's `ctx.from`, a Bot API `User`, a Mini App's
|
|
47
|
+
* `user` — as a `TelegramUser`; `undefined` when it is not one.
|
|
48
|
+
*/
|
|
49
|
+
export declare function parseTelegramUser(value: unknown): TelegramUser | undefined;
|
|
50
|
+
export {};
|
|
51
|
+
//# sourceMappingURL=user.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user.d.ts","sourceRoot":"","sources":["../../src/telegram/user.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,QAAA,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwBnB,CAAC;AAEN,yFAAyF;AACzF,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,YAAY,GAAG,SAAS,CAG1E"}
|
package/llms/observability.txt
CHANGED
|
@@ -577,6 +577,18 @@ A payload goes into an audit row only after `sanitizePayload`:
|
|
|
577
577
|
`tokenizer`) survive. This applies wherever a key exists — an object field and
|
|
578
578
|
a `Map` entry alike, at any depth. A `Set` member has no key, so nothing there
|
|
579
579
|
is masked by name;
|
|
580
|
+
- **secrets are masked by their shape** in every string, whatever key holds
|
|
581
|
+
it — the secret half of a Telegram bot token (`123456:[redacted]`: the bot's
|
|
582
|
+
id stays, it tells two bots apart), the password in
|
|
583
|
+
`postgresql://user:password@host` (user, host and path stay), and the value
|
|
584
|
+
of a query parameter named as a secret (`?access_token=`, `?api-key=`,
|
|
585
|
+
`?key=`, `?sig=`). A URL in an error message or a request log line is the
|
|
586
|
+
usual way a token reaches a journal;
|
|
587
|
+
- **an error keeps what it says about itself** — its own fields (`error_code`,
|
|
588
|
+
`parameters`, a provider's `status`) are written beside `name`, `message`,
|
|
589
|
+
`stack` and `cause`, masked by the same rules; a field holding a class
|
|
590
|
+
instance other than an error (grammY's `BotError.ctx`) is named, not written,
|
|
591
|
+
so one error cannot push its line past the bound and lose the stack;
|
|
580
592
|
- **binary blobs** (`Uint8Array`, `Blob`, `FormData`) collapse to metadata —
|
|
581
593
|
never the bytes;
|
|
582
594
|
- the result is **capped** — anything over the byte limit becomes a preview.
|
|
@@ -889,7 +901,7 @@ audit event. See the [Observability guide](../guide/observability.md).
|
|
|
889
901
|
| Export | Kind | Summary |
|
|
890
902
|
|--------|------|---------|
|
|
891
903
|
| `sanitizePayload` | function | redact secrets and cap size — [guide](../guide/observability.md#sanitisation) |
|
|
892
|
-
| `redact` | function | mask secret-named keys, drop binary blobs |
|
|
904
|
+
| `redact` | function | mask secret-named keys and secrets recognised by shape in every string — the secret half of a bot token, a password in an address, a query parameter named as a secret; drop binary blobs; an error keeps its own fields (`error_code`, `parameters`), a class instance on one is named, not written |
|
|
893
905
|
| `truncatePreview` | function | cap a value by serialised size |
|
|
894
906
|
| `measureSize` | function | item count + byte size of a result |
|
|
895
907
|
| `JsonValue` | _type_ | a JSON-serialisable value |
|
package/llms/server.4.txt
CHANGED
|
@@ -357,6 +357,11 @@ Three things it does that a hand-written check usually does not:
|
|
|
357
357
|
- `raw` keeps every signed pair, so a field Telegram adds after this release
|
|
358
358
|
does not need a release to reach.
|
|
359
359
|
|
|
360
|
+
The user arrives `camelCase` — `firstName`, `languageCode` — as `TelegramUser`.
|
|
361
|
+
A bot meets the same person as grammY's `ctx.from`, in Telegram's `snake_case`;
|
|
362
|
+
`parseTelegramUser(ctx.from)` gives the same `TelegramUser`, so one
|
|
363
|
+
`ensureAccount(user)` serves the Mini App and the chat.
|
|
364
|
+
|
|
360
365
|
`maxAgeSeconds` is optional and there is no default: only the application knows
|
|
361
366
|
how long its own session is worth. Omitting it means the string never expires,
|
|
362
367
|
which is a decision rather than an oversight.
|
|
@@ -386,6 +391,17 @@ and our payload is wrong, which is the case a list of substrings quietly counts
|
|
|
386
391
|
against the user. An unrecognised refusal leaves the recipient reachable: losing
|
|
387
392
|
a working subscriber forever costs more than one wasted send.
|
|
388
393
|
|
|
394
|
+
`TELEGRAM_SEND_FAILURE_REASONS` lists every reason. A table keyed by
|
|
395
|
+
`TelegramSendFailureReason` — error codes, the text a person reads — fails to
|
|
396
|
+
compile when a release adds one, instead of falling through to a default:
|
|
397
|
+
|
|
398
|
+
```ts
|
|
399
|
+
const codes: Record<TelegramSendFailureReason, string> = {
|
|
400
|
+
'blocked-by-user': 'E_BLOCKED',
|
|
401
|
+
/* … every reason … */
|
|
402
|
+
}
|
|
403
|
+
```
|
|
404
|
+
|
|
389
405
|
The rest of what a bot is assembled from — its resources, journal, operator
|
|
390
406
|
channel, broadcasts and local Bot API files — is in [Telegram bots](telegram.md).
|
|
391
407
|
|