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.
Files changed (41) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/dist/entrypoints/application.js +2 -1
  3. package/dist/entrypoints/observability.js +2 -1
  4. package/dist/entrypoints/telegram/screens.js +1 -1
  5. package/dist/entrypoints/telegram.d.ts +8 -4
  6. package/dist/entrypoints/telegram.d.ts.map +1 -1
  7. package/dist/entrypoints/telegram.js +277 -49
  8. package/dist/entrypoints/tools.js +2 -1
  9. package/dist/{index-6k1svmjh.js → index-e05cjgtf.js} +16 -13
  10. package/dist/{index-kzz6rq0w.js → index-e6c2spsd.js} +56 -18
  11. package/dist/index-sznqf8fx.js +20 -0
  12. package/dist/internal/secret-shapes.d.ts +21 -0
  13. package/dist/internal/secret-shapes.d.ts.map +1 -0
  14. package/dist/observability/sanitize.d.ts.map +1 -1
  15. package/dist/telegram/bot-api.d.ts +7 -4
  16. package/dist/telegram/bot-api.d.ts.map +1 -1
  17. package/dist/telegram/init-data.d.ts +3 -44
  18. package/dist/telegram/init-data.d.ts.map +1 -1
  19. package/dist/telegram/operator-channel.d.ts +1 -18
  20. package/dist/telegram/operator-channel.d.ts.map +1 -1
  21. package/dist/telegram/operator-dedupe.d.ts +44 -0
  22. package/dist/telegram/operator-dedupe.d.ts.map +1 -0
  23. package/dist/telegram/send-failure.d.ts +6 -0
  24. package/dist/telegram/send-failure.d.ts.map +1 -1
  25. package/dist/telegram/update-intake.d.ts +5 -1
  26. package/dist/telegram/update-intake.d.ts.map +1 -1
  27. package/dist/telegram/update-store-check.d.ts +22 -0
  28. package/dist/telegram/update-store-check.d.ts.map +1 -0
  29. package/dist/telegram/update-store-postgres.d.ts +33 -0
  30. package/dist/telegram/update-store-postgres.d.ts.map +1 -0
  31. package/dist/telegram/user.d.ts +51 -0
  32. package/dist/telegram/user.d.ts.map +1 -0
  33. package/llms/observability.txt +13 -1
  34. package/llms/server.4.txt +16 -0
  35. package/llms/stitchkit.txt +3 -3
  36. package/llms/telegram.txt +91 -13
  37. package/llms/{upgrading-0.74.0-0.100.0.txt → upgrading-0.74.0-0.101.0.txt} +39 -3
  38. package/llms/upgrading.txt +1 -1
  39. package/llms-full.txt +159 -17
  40. package/llms.txt +4 -4
  41. 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
- function redact2(value, options = {}) {
73
- const custom = options.sensitiveKeys;
74
- const isSensitive = custom === undefined ? isSensitiveKeyDefault : (key) => {
75
- custom.lastIndex = 0;
76
- return custom.test(key) || options.includeDefaultSensitiveKeys === true && isSensitiveKeyDefault(key);
77
- };
78
- const maxDepth = options.maxDepth ?? DEFAULT_MAX_DEPTH;
79
- const maxStringLength = options.maxStringLength ?? Number.POSITIVE_INFINITY;
80
- const maxCollectionLength = options.maxCollectionLength ?? Number.POSITIVE_INFINITY;
81
- const pathMatchers = (options.sensitivePaths ?? []).map((pattern) => pattern.split("."));
82
- const matchesPath = (matcher, path, patternIndex = 0, pathIndex = 0) => {
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 (matchesPath(matcher, path, patternIndex + 1, next))
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 matchesPath(matcher, path, patternIndex + 1, pathIndex + 1);
107
+ return matches(matcher, path, patternIndex + 1, pathIndex + 1);
98
108
  };
99
- const isSensitivePath = (path) => pathMatchers.some((matcher) => matchesPath(matcher, path));
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":"AAOA,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;AAmGD;;;;;;GAMG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,GAAE,eAAoB,GAAG,SAAS,CA6L/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"}
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 shape of a bot token: the bot's numeric id, a colon, the secret.
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 — hand it to a logger's
14
- * `sensitiveUrlPatterns` and a token inside a URL or an error message is not
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;AAIH;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,QAA8B,CAAC;AAItE,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"}
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 { z } from 'zod';
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?: TelegramInitDataUser;
28
+ user?: TelegramUser;
69
29
  /** Whose chat the Mini App was opened from, when Telegram named one. */
70
- receiver?: TelegramInitDataUser;
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;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB;;;;;;;GAOG;AACH,QAAA,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsB3B,CAAC;AAEN,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAE9E,MAAM,WAAW,gBAAgB;IAC/B,4EAA4E;IAC5E,IAAI,CAAC,EAAE,oBAAoB,CAAC;IAC5B,wEAAwE;IACxE,QAAQ,CAAC,EAAE,oBAAoB,CAAC;IAChC,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;AAWD;;;;;;;;GAQG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,6BAA6B,GACrC,OAAO,CAAC,4BAA4B,CAAC,CAuEvC"}
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":"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"}
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;AAuED;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,GAAG,mBAAmB,CA0D/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"}
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
- /** Handle one update — with grammY, `(update) => bot.handleUpdate(update)`. */
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,+EAA+E;IAC/E,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;AAsDD,wBAAgB,0BAA0B,CAAC,OAAO,SAAS,sBAAsB,EAC/E,MAAM,EAAE,0BAA0B,CAAC,OAAO,CAAC,GAC1C,oBAAoB,CAgJtB"}
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"}
@@ -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