boondmanager-mcp-server 2.15.2 → 2.17.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/LICENSE +1 -1
- package/NOTICE +16 -2
- package/README.md +60 -15
- package/dist/config/access-policy.d.ts.map +1 -1
- package/dist/config/access-policy.js +6 -18
- package/dist/config/access-policy.js.map +1 -1
- package/dist/config/dictionary-overrides.d.ts.map +1 -1
- package/dist/config/dictionary-overrides.js +2 -12
- package/dist/config/dictionary-overrides.js.map +1 -1
- package/dist/config/env.d.ts +63 -0
- package/dist/config/env.d.ts.map +1 -0
- package/dist/config/env.js +116 -0
- package/dist/config/env.js.map +1 -0
- package/dist/config/profiles.d.ts +22 -8
- package/dist/config/profiles.d.ts.map +1 -1
- package/dist/config/profiles.js +48 -4
- package/dist/config/profiles.js.map +1 -1
- package/dist/constants.d.ts +13 -2
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +25 -1
- package/dist/constants.js.map +1 -1
- package/dist/icons.d.ts.map +1 -1
- package/dist/icons.js +6 -1
- package/dist/icons.js.map +1 -1
- package/dist/index.js +33 -15
- package/dist/index.js.map +1 -1
- package/dist/instructions.d.ts +1 -1
- package/dist/instructions.d.ts.map +1 -1
- package/dist/instructions.js +6 -6
- package/dist/instructions.js.map +1 -1
- package/dist/prompts/index.d.ts +19 -1
- package/dist/prompts/index.d.ts.map +1 -1
- package/dist/prompts/index.js +495 -25
- package/dist/prompts/index.js.map +1 -1
- package/dist/prompts/periods.d.ts +45 -0
- package/dist/prompts/periods.d.ts.map +1 -0
- package/dist/prompts/periods.js +154 -0
- package/dist/prompts/periods.js.map +1 -0
- package/dist/resources/index.d.ts +14 -0
- package/dist/resources/index.d.ts.map +1 -1
- package/dist/resources/index.js +285 -0
- package/dist/resources/index.js.map +1 -1
- package/dist/resources/templates.d.ts +1 -1
- package/dist/resources/templates.d.ts.map +1 -1
- package/dist/resources/templates.js +2 -0
- package/dist/resources/templates.js.map +1 -1
- package/dist/schema-dialect.js.map +1 -1
- package/dist/schemas/filter-aliases.d.ts +1 -1
- package/dist/schemas/filter-aliases.d.ts.map +1 -1
- package/dist/schemas/filter-aliases.js +101 -2
- package/dist/schemas/filter-aliases.js.map +1 -1
- package/dist/schemas/index.d.ts +987 -161
- package/dist/schemas/index.d.ts.map +1 -1
- package/dist/schemas/index.js +853 -323
- package/dist/schemas/index.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +10 -3
- package/dist/server.js.map +1 -1
- package/dist/services/boond-client.d.ts +29 -213
- package/dist/services/boond-client.d.ts.map +1 -1
- package/dist/services/boond-client.js +29 -1168
- package/dist/services/boond-client.js.map +1 -1
- package/dist/services/dictionary.d.ts +27 -3
- package/dist/services/dictionary.d.ts.map +1 -1
- package/dist/services/dictionary.js +60 -23
- package/dist/services/dictionary.js.map +1 -1
- package/dist/services/document-text.d.ts +32 -0
- package/dist/services/document-text.d.ts.map +1 -0
- package/dist/services/document-text.js +120 -0
- package/dist/services/document-text.js.map +1 -0
- package/dist/services/format/detail.d.ts +16 -0
- package/dist/services/format/detail.d.ts.map +1 -0
- package/dist/services/format/detail.js +36 -0
- package/dist/services/format/detail.js.map +1 -0
- package/dist/services/format/html.d.ts +19 -0
- package/dist/services/format/html.d.ts.map +1 -0
- package/dist/services/format/html.js +87 -0
- package/dist/services/format/html.js.map +1 -0
- package/dist/services/format/list.d.ts +3 -0
- package/dist/services/format/list.d.ts.map +1 -0
- package/dist/services/format/list.js +44 -0
- package/dist/services/format/list.js.map +1 -0
- package/dist/services/format/summary.d.ts +16 -0
- package/dist/services/format/summary.d.ts.map +1 -0
- package/dist/services/format/summary.js +172 -0
- package/dist/services/format/summary.js.map +1 -0
- package/dist/services/format/tab.d.ts +9 -0
- package/dist/services/format/tab.d.ts.map +1 -0
- package/dist/services/format/tab.js +32 -0
- package/dist/services/format/tab.js.map +1 -0
- package/dist/services/http/auth.d.ts +44 -0
- package/dist/services/http/auth.d.ts.map +1 -0
- package/dist/services/http/auth.js +146 -0
- package/dist/services/http/auth.js.map +1 -0
- package/dist/services/http/download.d.ts +60 -0
- package/dist/services/http/download.d.ts.map +1 -0
- package/dist/services/http/download.js +160 -0
- package/dist/services/http/download.js.map +1 -0
- package/dist/services/http/errors.d.ts +45 -0
- package/dist/services/http/errors.d.ts.map +1 -0
- package/dist/services/http/errors.js +188 -0
- package/dist/services/http/errors.js.map +1 -0
- package/dist/services/http/rate-limit.d.ts +40 -0
- package/dist/services/http/rate-limit.d.ts.map +1 -0
- package/dist/services/http/rate-limit.js +92 -0
- package/dist/services/http/rate-limit.js.map +1 -0
- package/dist/services/http/retry.d.ts +46 -0
- package/dist/services/http/retry.d.ts.map +1 -0
- package/dist/services/http/retry.js +101 -0
- package/dist/services/http/retry.js.map +1 -0
- package/dist/services/http/transport.d.ts +73 -0
- package/dist/services/http/transport.d.ts.map +1 -0
- package/dist/services/http/transport.js +272 -0
- package/dist/services/http/transport.js.map +1 -0
- package/dist/services/logger.d.ts +53 -2
- package/dist/services/logger.d.ts.map +1 -1
- package/dist/services/logger.js +82 -21
- package/dist/services/logger.js.map +1 -1
- package/dist/services/oauth.d.ts +12 -0
- package/dist/services/oauth.d.ts.map +1 -1
- package/dist/services/oauth.js +21 -8
- package/dist/services/oauth.js.map +1 -1
- package/dist/services/rate-limiter.d.ts +9 -2
- package/dist/services/rate-limiter.d.ts.map +1 -1
- package/dist/services/rate-limiter.js +18 -6
- package/dist/services/rate-limiter.js.map +1 -1
- package/dist/services/request-context.d.ts +47 -0
- package/dist/services/request-context.d.ts.map +1 -0
- package/dist/services/request-context.js +112 -0
- package/dist/services/request-context.js.map +1 -0
- package/dist/services/search.d.ts +26 -0
- package/dist/services/search.d.ts.map +1 -0
- package/dist/services/search.js +98 -0
- package/dist/services/search.js.map +1 -0
- package/dist/services/update-checker.d.ts.map +1 -1
- package/dist/services/update-checker.js +15 -8
- package/dist/services/update-checker.js.map +1 -1
- package/dist/tools/absences.d.ts +9 -0
- package/dist/tools/absences.d.ts.map +1 -1
- package/dist/tools/absences.js +65 -20
- package/dist/tools/absences.js.map +1 -1
- package/dist/tools/actions.d.ts.map +1 -1
- package/dist/tools/actions.js +11 -27
- package/dist/tools/actions.js.map +1 -1
- package/dist/tools/advantages.d.ts +2 -0
- package/dist/tools/advantages.d.ts.map +1 -1
- package/dist/tools/advantages.js +66 -13
- package/dist/tools/advantages.js.map +1 -1
- package/dist/tools/alerts.d.ts +18 -0
- package/dist/tools/alerts.d.ts.map +1 -0
- package/dist/tools/alerts.js +82 -0
- package/dist/tools/alerts.js.map +1 -0
- package/dist/tools/application.d.ts +2 -1
- package/dist/tools/application.d.ts.map +1 -1
- package/dist/tools/application.js +9 -3
- package/dist/tools/application.js.map +1 -1
- package/dist/tools/contacts.d.ts.map +1 -1
- package/dist/tools/contacts.js +3 -7
- package/dist/tools/contacts.js.map +1 -1
- package/dist/tools/contracts.d.ts +22 -0
- package/dist/tools/contracts.d.ts.map +1 -1
- package/dist/tools/contracts.js +185 -52
- package/dist/tools/contracts.js.map +1 -1
- package/dist/tools/crud-factory.d.ts +33 -2
- package/dist/tools/crud-factory.d.ts.map +1 -1
- package/dist/tools/crud-factory.js +42 -15
- package/dist/tools/crud-factory.js.map +1 -1
- package/dist/tools/deliveries.d.ts +3 -0
- package/dist/tools/deliveries.d.ts.map +1 -1
- package/dist/tools/deliveries.js +50 -94
- package/dist/tools/deliveries.js.map +1 -1
- package/dist/tools/description-builders.d.ts +0 -1
- package/dist/tools/description-builders.d.ts.map +1 -1
- package/dist/tools/description-builders.js +46 -3
- package/dist/tools/description-builders.js.map +1 -1
- package/dist/tools/documents.d.ts.map +1 -1
- package/dist/tools/documents.js +73 -21
- package/dist/tools/documents.js.map +1 -1
- package/dist/tools/expenses.d.ts.map +1 -1
- package/dist/tools/expenses.js +2 -11
- package/dist/tools/expenses.js.map +1 -1
- package/dist/tools/find.d.ts +61 -0
- package/dist/tools/find.d.ts.map +1 -0
- package/dist/tools/find.js +221 -0
- package/dist/tools/find.js.map +1 -0
- package/dist/tools/flags.d.ts +5 -0
- package/dist/tools/flags.d.ts.map +1 -1
- package/dist/tools/flags.js +116 -1
- package/dist/tools/flags.js.map +1 -1
- package/dist/tools/forms.d.ts +5 -0
- package/dist/tools/forms.d.ts.map +1 -0
- package/dist/tools/forms.js +64 -0
- package/dist/tools/forms.js.map +1 -0
- package/dist/tools/groupments.d.ts +5 -0
- package/dist/tools/groupments.d.ts.map +1 -0
- package/dist/tools/groupments.js +68 -0
- package/dist/tools/groupments.js.map +1 -0
- package/dist/tools/inactivities.d.ts +5 -0
- package/dist/tools/inactivities.d.ts.map +1 -0
- package/dist/tools/inactivities.js +59 -0
- package/dist/tools/inactivities.js.map +1 -0
- package/dist/tools/index.d.ts +4 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +4 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/invoices.d.ts.map +1 -1
- package/dist/tools/invoices.js +27 -2
- package/dist/tools/invoices.js.map +1 -1
- package/dist/tools/linked-entity-filters.d.ts +48 -0
- package/dist/tools/linked-entity-filters.d.ts.map +1 -0
- package/dist/tools/linked-entity-filters.js +64 -0
- package/dist/tools/linked-entity-filters.js.map +1 -0
- package/dist/tools/notifications.d.ts.map +1 -1
- package/dist/tools/notifications.js +0 -5
- package/dist/tools/notifications.js.map +1 -1
- package/dist/tools/orders.d.ts.map +1 -1
- package/dist/tools/orders.js +33 -4
- package/dist/tools/orders.js.map +1 -1
- package/dist/tools/parameter-disclosure.js.map +1 -1
- package/dist/tools/payments.d.ts +3 -0
- package/dist/tools/payments.d.ts.map +1 -1
- package/dist/tools/payments.js +43 -109
- package/dist/tools/payments.js.map +1 -1
- package/dist/tools/positionings.d.ts.map +1 -1
- package/dist/tools/positionings.js +9 -39
- package/dist/tools/positionings.js.map +1 -1
- package/dist/tools/projects.d.ts.map +1 -1
- package/dist/tools/projects.js +5 -12
- package/dist/tools/projects.js.map +1 -1
- package/dist/tools/provider-invoices.d.ts +2 -0
- package/dist/tools/provider-invoices.d.ts.map +1 -1
- package/dist/tools/provider-invoices.js +30 -111
- package/dist/tools/provider-invoices.js.map +1 -1
- package/dist/tools/purchases.d.ts +2 -0
- package/dist/tools/purchases.d.ts.map +1 -1
- package/dist/tools/purchases.js +50 -104
- package/dist/tools/purchases.js.map +1 -1
- package/dist/tools/registration-decorators.d.ts +28 -0
- package/dist/tools/registration-decorators.d.ts.map +1 -1
- package/dist/tools/registration-decorators.js +107 -0
- package/dist/tools/registration-decorators.js.map +1 -1
- package/dist/tools/resources.d.ts.map +1 -1
- package/dist/tools/resources.js +27 -5
- package/dist/tools/resources.js.map +1 -1
- package/dist/tools/rights.d.ts +101 -0
- package/dist/tools/rights.d.ts.map +1 -0
- package/dist/tools/rights.js +75 -0
- package/dist/tools/rights.js.map +1 -0
- package/dist/tools/tab-tools.js +3 -3
- package/dist/tools/tab-tools.js.map +1 -1
- package/dist/tools/timesheets.d.ts +29 -0
- package/dist/tools/timesheets.d.ts.map +1 -1
- package/dist/tools/timesheets.js +196 -126
- package/dist/tools/timesheets.js.map +1 -1
- package/dist/tools/todolists.d.ts +2 -0
- package/dist/tools/todolists.d.ts.map +1 -1
- package/dist/tools/todolists.js +64 -1
- package/dist/tools/todolists.js.map +1 -1
- package/dist/tools/validations.d.ts +9 -0
- package/dist/tools/validations.d.ts.map +1 -1
- package/dist/tools/validations.js +86 -9
- package/dist/tools/validations.js.map +1 -1
- package/dist/tools/workflows.js +1 -1
- package/dist/tools/workflows.js.map +1 -1
- package/dist/transports/http.d.ts +116 -0
- package/dist/transports/http.d.ts.map +1 -1
- package/dist/transports/http.js +422 -92
- package/dist/transports/http.js.map +1 -1
- package/dist/types.d.ts +3 -3
- package/dist/types.d.ts.map +1 -1
- package/manifest.json +7 -7
- package/package.json +14 -10
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retry policy for the BoondManager client: configuration, the retryability
|
|
3
|
+
* decision, `Retry-After` parsing and full-jitter backoff. Pure functions,
|
|
4
|
+
* exported for unit testing; `send()` in `transport.ts` is the only consumer.
|
|
5
|
+
*/
|
|
6
|
+
import { readPositiveInt } from "../../config/env.js";
|
|
7
|
+
import { DEFAULT_HTTP_MAX_RETRIES, DEFAULT_HTTP_RETRY_BASE_MS, DEFAULT_HTTP_RETRY_MAX_MS } from "../../constants.js";
|
|
8
|
+
/** Resolve retry configuration from env, with safe fallbacks. Exported for tests. */
|
|
9
|
+
export function resolveRetryConfig() {
|
|
10
|
+
return {
|
|
11
|
+
maxRetries: readPositiveInt("BOOND_HTTP_MAX_RETRIES", DEFAULT_HTTP_MAX_RETRIES, process.env, { allowZero: true }),
|
|
12
|
+
baseDelayMs: readPositiveInt("BOOND_HTTP_RETRY_BASE_MS", DEFAULT_HTTP_RETRY_BASE_MS),
|
|
13
|
+
maxDelayMs: readPositiveInt("BOOND_HTTP_RETRY_MAX_MS", DEFAULT_HTTP_RETRY_MAX_MS),
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Decide whether a failed attempt is worth retrying.
|
|
18
|
+
*
|
|
19
|
+
* Retry policy is intentionally conservative for non-idempotent verbs to avoid
|
|
20
|
+
* silently duplicating writes when the server's response was lost or delayed:
|
|
21
|
+
* - 429 (Too Many Requests) is always retried — the server explicitly
|
|
22
|
+
* rejected the request before processing it, so it is safe regardless of
|
|
23
|
+
* verb.
|
|
24
|
+
* - For GET only, 5xx responses, network failures, and timeouts are retried
|
|
25
|
+
* because GET is idempotent.
|
|
26
|
+
* - 4xx responses (other than 429) are never retried — the client must change
|
|
27
|
+
* the request before another attempt makes sense.
|
|
28
|
+
*
|
|
29
|
+
* Exported for unit testing.
|
|
30
|
+
*/
|
|
31
|
+
export function isRetryable(method, status, isNetworkOrTimeout) {
|
|
32
|
+
if (status === 429)
|
|
33
|
+
return true;
|
|
34
|
+
if (method !== "GET")
|
|
35
|
+
return false;
|
|
36
|
+
if (isNetworkOrTimeout)
|
|
37
|
+
return true;
|
|
38
|
+
if (status !== undefined && status >= 500 && status < 600)
|
|
39
|
+
return true;
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Parse a `Retry-After` header value into milliseconds.
|
|
44
|
+
*
|
|
45
|
+
* Accepts either a non-negative number of seconds or an HTTP-date. Returns
|
|
46
|
+
* null when the value is absent or unparseable. Negative computed delays are
|
|
47
|
+
* clamped to 0. Exported for unit testing.
|
|
48
|
+
*/
|
|
49
|
+
export function parseRetryAfter(value, now = Date.now()) {
|
|
50
|
+
if (!value)
|
|
51
|
+
return null;
|
|
52
|
+
const trimmed = value.trim();
|
|
53
|
+
if (trimmed === "")
|
|
54
|
+
return null;
|
|
55
|
+
const seconds = Number(trimmed);
|
|
56
|
+
if (Number.isFinite(seconds)) {
|
|
57
|
+
// Numeric form is authoritative once we recognise it as a number — falling
|
|
58
|
+
// through to Date.parse on a negative/odd numeric would silently produce
|
|
59
|
+
// weird timestamps (e.g. Date.parse("-1") → year -1).
|
|
60
|
+
return seconds >= 0 ? Math.floor(seconds * 1000) : null;
|
|
61
|
+
}
|
|
62
|
+
const date = Date.parse(trimmed);
|
|
63
|
+
if (!Number.isNaN(date))
|
|
64
|
+
return Math.max(0, date - now);
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Compute the next backoff delay using full jitter:
|
|
69
|
+
* delay = random(0, min(maxMs, baseMs * 2^attempt))
|
|
70
|
+
*
|
|
71
|
+
* Full jitter (vs. exponential-only) reduces thundering-herd risk when many
|
|
72
|
+
* clients retry in lockstep. Exported for unit testing.
|
|
73
|
+
*/
|
|
74
|
+
export function computeBackoffMs(attempt, baseMs, maxMs, random = Math.random) {
|
|
75
|
+
const exp = baseMs * 2 ** attempt;
|
|
76
|
+
const capped = Math.min(maxMs, exp);
|
|
77
|
+
return Math.floor(random() * capped);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Backoff sleep. With a `signal`, the wait ends the moment it fires — a
|
|
81
|
+
* cancelled caller does not sit out a 5 s `Retry-After` (issue #231); the
|
|
82
|
+
* rejection carries the signal's reason.
|
|
83
|
+
*/
|
|
84
|
+
export function sleep(ms, signal) {
|
|
85
|
+
if (signal?.aborted)
|
|
86
|
+
return Promise.reject(signal.reason ?? new Error("aborted"));
|
|
87
|
+
if (ms <= 0)
|
|
88
|
+
return Promise.resolve();
|
|
89
|
+
return new Promise((resolve, reject) => {
|
|
90
|
+
const timer = setTimeout(() => {
|
|
91
|
+
signal?.removeEventListener("abort", onAbort);
|
|
92
|
+
resolve();
|
|
93
|
+
}, ms);
|
|
94
|
+
function onAbort() {
|
|
95
|
+
clearTimeout(timer);
|
|
96
|
+
reject(signal.reason ?? new Error("aborted"));
|
|
97
|
+
}
|
|
98
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=retry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry.js","sourceRoot":"","sources":["../../../src/services/http/retry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { JsonApiResponse } from "../../types.js";
|
|
2
|
+
export type QueryValue = string | number | Array<string | number> | undefined;
|
|
3
|
+
export type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
4
|
+
/**
|
|
5
|
+
* Resolve the per-request HTTP timeout in milliseconds.
|
|
6
|
+
*
|
|
7
|
+
* Reads BOOND_HTTP_TIMEOUT_MS at call time so tests / runtime overrides take
|
|
8
|
+
* effect without restarting the process. Falls back to the default for
|
|
9
|
+
* unset, non-numeric, or non-positive values.
|
|
10
|
+
*
|
|
11
|
+
* Exported for unit testing.
|
|
12
|
+
*/
|
|
13
|
+
export declare function resolveTimeoutMs(): number;
|
|
14
|
+
/**
|
|
15
|
+
* Defense-in-depth against path traversal / query injection through entity
|
|
16
|
+
* ids interpolated into API paths at ~40 call sites. Even though the id
|
|
17
|
+
* schemas are now numeric-only, a future tool could forget to validate, so we
|
|
18
|
+
* assert here that the path is well-formed: it must start with `/`, carry no
|
|
19
|
+
* query (`?`) or fragment (`#`) — those arrive via `queryParams`, never the
|
|
20
|
+
* path — and contain no traversal (`..`) or percent/backslash escapes. Built
|
|
21
|
+
* paths only ever combine static segments with numeric ids and hyphenated tab
|
|
22
|
+
* names, so this rejects nothing legitimate. Exported for unit testing.
|
|
23
|
+
*/
|
|
24
|
+
export declare function assertSafeApiPath(path: string): void;
|
|
25
|
+
/**
|
|
26
|
+
* Validates `path` and resolves it against `baseUrl`, returning the
|
|
27
|
+
* constructed URL. Throws if the path is unsafe (see `assertSafeApiPath`) or
|
|
28
|
+
* if the resolved URL escapes the configured API base origin/path. Exported
|
|
29
|
+
* for unit testing.
|
|
30
|
+
*/
|
|
31
|
+
export declare function resolveApiUrl(baseUrl: string, path: string): URL;
|
|
32
|
+
/** What a helper hands to `send()`: everything but the plumbing. */
|
|
33
|
+
export interface SendOptions {
|
|
34
|
+
method?: HttpMethod;
|
|
35
|
+
query?: Record<string, QueryValue>;
|
|
36
|
+
/**
|
|
37
|
+
* Request body, reused verbatim on every attempt: a JSON string or a
|
|
38
|
+
* `FormData` (both are re-serialised by `fetch` per call, so a retry never
|
|
39
|
+
* sends a consumed stream).
|
|
40
|
+
*/
|
|
41
|
+
body?: string | FormData;
|
|
42
|
+
/** Extra headers (`Accept`, `Content-Type`); the auth header is added per attempt. */
|
|
43
|
+
headers?: Record<string, string>;
|
|
44
|
+
/**
|
|
45
|
+
* Cancellation signal. Defaults to the current MCP request's `extra.signal`
|
|
46
|
+
* (`currentRequestSignal()`, issue #231) — pass one only to override it.
|
|
47
|
+
*/
|
|
48
|
+
signal?: AbortSignal;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Send one request through the whole pipeline — URL guard, per-identity rate
|
|
52
|
+
* limiter, auth header resolved per attempt (so an OAuth refresh between two
|
|
53
|
+
* tries is picked up), per-attempt timeout, retry policy with `Retry-After`,
|
|
54
|
+
* error mapping — and return the first **2xx** `Response`, body unread.
|
|
55
|
+
*
|
|
56
|
+
* Throws `BoondApiError` on a non-2xx that the policy does not retry (or once
|
|
57
|
+
* retries are exhausted), the standard timeout `Error` when an attempt never
|
|
58
|
+
* completed, `RequestCancelledError` (name `AbortError`) when the client
|
|
59
|
+
* cancelled — checked before each attempt, while waiting for a rate-limit
|
|
60
|
+
* token, during the fetch itself and during a backoff, and never retried —
|
|
61
|
+
* and the raw network error otherwise.
|
|
62
|
+
*/
|
|
63
|
+
export declare function send(path: string, options?: SendOptions): Promise<Response>;
|
|
64
|
+
/** JSON:API request. `body` is serialised for POST / PUT / PATCH only. */
|
|
65
|
+
export declare function apiRequest(path: string, method?: HttpMethod, body?: unknown, queryParams?: Record<string, QueryValue>): Promise<JsonApiResponse>;
|
|
66
|
+
/**
|
|
67
|
+
* POST a multipart/form-data payload to the BoondManager API (document
|
|
68
|
+
* upload). Form values are simple string fields — the file itself travels by
|
|
69
|
+
* reference via the `fileUrl` field (Boond downloads it server-side), so the
|
|
70
|
+
* MCP server never buffers file bytes.
|
|
71
|
+
*/
|
|
72
|
+
export declare function apiUploadForm(path: string, fields: Record<string, string>): Promise<JsonApiResponse>;
|
|
73
|
+
//# sourceMappingURL=transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../../src/services/http/transport.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAQtD,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,GAAG,SAAS,CAAC;AAC9E,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC;AAErE;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAWD;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CASpD;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,GAAG,CAUhE;AAoBD,oEAAoE;AACpE,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACnC;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IACzB,sFAAsF;IACtF,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC;;;OAGG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,QAAQ,CAAC,CAiIrF;AAkBD,0EAA0E;AAC1E,wBAAsB,UAAU,CAC9B,IAAI,EAAE,MAAM,EACZ,MAAM,GAAE,UAAkB,EAC1B,IAAI,CAAC,EAAE,OAAO,EACd,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,GACvC,OAAO,CAAC,eAAe,CAAC,CAU1B;AAED;;;;;GAKG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,eAAe,CAAC,CAQ1G"}
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The single HTTP send path towards BoondManager (issue #239).
|
|
3
|
+
*
|
|
4
|
+
* Before the split, `apiRequest`, `apiDownload` and `apiUploadForm` each
|
|
5
|
+
* carried their own copy of "resolve URL, acquire a rate-limit token, resolve
|
|
6
|
+
* the auth header, fetch with a timeout, map the error" — and they had
|
|
7
|
+
* diverged: the upload helper did not translate timeouts (a bare
|
|
8
|
+
* `TimeoutError` with no endpoint), retried nothing, and neither the download
|
|
9
|
+
* nor the upload helper honoured a 429. `send()` is now the only place that
|
|
10
|
+
* calls `fetch`, so the timeout, the rate limiter, the retry policy and the
|
|
11
|
+
* error shaping are the same for JSON, binary and multipart requests. The
|
|
12
|
+
* three helpers are thin wrappers that only decide what to do with the body.
|
|
13
|
+
*/
|
|
14
|
+
import { readPositiveInt } from "../../config/env.js";
|
|
15
|
+
import { DEFAULT_HTTP_TIMEOUT_MS } from "../../constants.js";
|
|
16
|
+
import { getConfig } from "./auth.js";
|
|
17
|
+
import { BoondApiError, formatApiError, nonJsonResponseError, timeoutError } from "./errors.js";
|
|
18
|
+
import { getRateLimiter } from "./rate-limit.js";
|
|
19
|
+
import { computeBackoffMs, isRetryable, parseRetryAfter, resolveRetryConfig, sleep } from "./retry.js";
|
|
20
|
+
import { currentRequestContext, RequestCancelledError, throwIfCancelled } from "../request-context.js";
|
|
21
|
+
import { logger } from "../logger.js";
|
|
22
|
+
/**
|
|
23
|
+
* Resolve the per-request HTTP timeout in milliseconds.
|
|
24
|
+
*
|
|
25
|
+
* Reads BOOND_HTTP_TIMEOUT_MS at call time so tests / runtime overrides take
|
|
26
|
+
* effect without restarting the process. Falls back to the default for
|
|
27
|
+
* unset, non-numeric, or non-positive values.
|
|
28
|
+
*
|
|
29
|
+
* Exported for unit testing.
|
|
30
|
+
*/
|
|
31
|
+
export function resolveTimeoutMs() {
|
|
32
|
+
return readPositiveInt("BOOND_HTTP_TIMEOUT_MS", DEFAULT_HTTP_TIMEOUT_MS);
|
|
33
|
+
}
|
|
34
|
+
/** True when an error from fetch() came from an AbortSignal firing. */
|
|
35
|
+
function isAbortError(err) {
|
|
36
|
+
if (!(err instanceof Error))
|
|
37
|
+
return false;
|
|
38
|
+
// AbortSignal.timeout() rejects with a DOMException whose name is "TimeoutError";
|
|
39
|
+
// generic aborts surface as "AbortError". Both indicate the request never
|
|
40
|
+
// completed end-to-end and should be reported as a timeout.
|
|
41
|
+
return err.name === "TimeoutError" || err.name === "AbortError";
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Defense-in-depth against path traversal / query injection through entity
|
|
45
|
+
* ids interpolated into API paths at ~40 call sites. Even though the id
|
|
46
|
+
* schemas are now numeric-only, a future tool could forget to validate, so we
|
|
47
|
+
* assert here that the path is well-formed: it must start with `/`, carry no
|
|
48
|
+
* query (`?`) or fragment (`#`) — those arrive via `queryParams`, never the
|
|
49
|
+
* path — and contain no traversal (`..`) or percent/backslash escapes. Built
|
|
50
|
+
* paths only ever combine static segments with numeric ids and hyphenated tab
|
|
51
|
+
* names, so this rejects nothing legitimate. Exported for unit testing.
|
|
52
|
+
*/
|
|
53
|
+
export function assertSafeApiPath(path) {
|
|
54
|
+
if (!path.startsWith("/")) {
|
|
55
|
+
throw new Error(`Invalid API path (must start with "/"): ${path}`);
|
|
56
|
+
}
|
|
57
|
+
// `?`/`#` would inject a query/fragment; `%`/`\` could encode a traversal;
|
|
58
|
+
// `..` is a literal traversal segment.
|
|
59
|
+
if (/[?#%\\]/.test(path) || path.includes("..")) {
|
|
60
|
+
throw new Error(`Unsafe API path rejected: ${path}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Validates `path` and resolves it against `baseUrl`, returning the
|
|
65
|
+
* constructed URL. Throws if the path is unsafe (see `assertSafeApiPath`) or
|
|
66
|
+
* if the resolved URL escapes the configured API base origin/path. Exported
|
|
67
|
+
* for unit testing.
|
|
68
|
+
*/
|
|
69
|
+
export function resolveApiUrl(baseUrl, path) {
|
|
70
|
+
assertSafeApiPath(path);
|
|
71
|
+
const url = new URL(`${baseUrl}${path}`);
|
|
72
|
+
// Belt-and-braces: confirm the constructed URL did not escape the API base
|
|
73
|
+
// origin/path despite the textual guard above.
|
|
74
|
+
const base = new URL(baseUrl);
|
|
75
|
+
if (url.origin !== base.origin || !url.pathname.startsWith(base.pathname)) {
|
|
76
|
+
throw new Error(`API path escaped the configured base URL: ${path}`);
|
|
77
|
+
}
|
|
78
|
+
return url;
|
|
79
|
+
}
|
|
80
|
+
/** Append query parameters; arrays use BoondManager's repeated bracket notation (`key[]=v1&key[]=v2`). */
|
|
81
|
+
function applyQuery(url, queryParams) {
|
|
82
|
+
if (!queryParams)
|
|
83
|
+
return;
|
|
84
|
+
for (const [key, value] of Object.entries(queryParams)) {
|
|
85
|
+
if (value === undefined || value === null)
|
|
86
|
+
continue;
|
|
87
|
+
if (Array.isArray(value)) {
|
|
88
|
+
const bracketKey = key.endsWith("[]") ? key : `${key}[]`;
|
|
89
|
+
for (const v of value) {
|
|
90
|
+
if (v !== undefined && v !== null && v !== "") {
|
|
91
|
+
url.searchParams.append(bracketKey, String(v));
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
url.searchParams.set(key, String(value));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Send one request through the whole pipeline — URL guard, per-identity rate
|
|
102
|
+
* limiter, auth header resolved per attempt (so an OAuth refresh between two
|
|
103
|
+
* tries is picked up), per-attempt timeout, retry policy with `Retry-After`,
|
|
104
|
+
* error mapping — and return the first **2xx** `Response`, body unread.
|
|
105
|
+
*
|
|
106
|
+
* Throws `BoondApiError` on a non-2xx that the policy does not retry (or once
|
|
107
|
+
* retries are exhausted), the standard timeout `Error` when an attempt never
|
|
108
|
+
* completed, `RequestCancelledError` (name `AbortError`) when the client
|
|
109
|
+
* cancelled — checked before each attempt, while waiting for a rate-limit
|
|
110
|
+
* token, during the fetch itself and during a backoff, and never retried —
|
|
111
|
+
* and the raw network error otherwise.
|
|
112
|
+
*/
|
|
113
|
+
export async function send(path, options = {}) {
|
|
114
|
+
const method = options.method ?? "GET";
|
|
115
|
+
const { baseUrl, auth } = getConfig();
|
|
116
|
+
const url = resolveApiUrl(baseUrl, path);
|
|
117
|
+
applyQuery(url, options.query);
|
|
118
|
+
const timeoutMs = resolveTimeoutMs();
|
|
119
|
+
const retry = resolveRetryConfig();
|
|
120
|
+
const totalAttempts = retry.maxRetries + 1;
|
|
121
|
+
const limiter = getRateLimiter();
|
|
122
|
+
const context = currentRequestContext();
|
|
123
|
+
const signal = options.signal ?? context?.signal;
|
|
124
|
+
const corrId = context?.corrId;
|
|
125
|
+
// Correlation towards BoondManager (#236): the request id every log line
|
|
126
|
+
// carries, and the client's W3C trace context when it sent one. Header
|
|
127
|
+
// propagation only — no tracing SDK.
|
|
128
|
+
const correlationHeaders = {
|
|
129
|
+
...(corrId ? { "X-Request-Id": corrId } : {}),
|
|
130
|
+
...(context?.traceparent ? { traceparent: context.traceparent } : {}),
|
|
131
|
+
};
|
|
132
|
+
let lastError;
|
|
133
|
+
for (let attempt = 0; attempt < totalAttempts; attempt++) {
|
|
134
|
+
throwIfCancelled(signal, method, path);
|
|
135
|
+
// Acquire a token before each attempt so retries also count toward the
|
|
136
|
+
// rate budget — this is what actually protects us from feedback loops
|
|
137
|
+
// (transient 5xx → retry → transient 5xx → …) saturating the API.
|
|
138
|
+
if (limiter) {
|
|
139
|
+
try {
|
|
140
|
+
await limiter.acquire(signal);
|
|
141
|
+
}
|
|
142
|
+
catch (err) {
|
|
143
|
+
throwIfCancelled(signal, method, path);
|
|
144
|
+
throw err;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
const authHeader = await auth();
|
|
148
|
+
const headers = {
|
|
149
|
+
[authHeader.name]: authHeader.value,
|
|
150
|
+
...correlationHeaders,
|
|
151
|
+
...options.headers,
|
|
152
|
+
};
|
|
153
|
+
const attemptStartedAt = Date.now();
|
|
154
|
+
const fetchOptions = {
|
|
155
|
+
method,
|
|
156
|
+
headers,
|
|
157
|
+
// Each attempt gets its own timeout signal — once a signal has fired it
|
|
158
|
+
// can't be reused for the next try. The request's cancellation signal
|
|
159
|
+
// is composed in so `notifications/cancelled` aborts the socket too.
|
|
160
|
+
signal: signal ? AbortSignal.any([signal, AbortSignal.timeout(timeoutMs)]) : AbortSignal.timeout(timeoutMs),
|
|
161
|
+
};
|
|
162
|
+
if (options.body !== undefined)
|
|
163
|
+
fetchOptions.body = options.body;
|
|
164
|
+
let response;
|
|
165
|
+
let networkError;
|
|
166
|
+
try {
|
|
167
|
+
response = await fetch(url.toString(), fetchOptions);
|
|
168
|
+
}
|
|
169
|
+
catch (err) {
|
|
170
|
+
// A cancellation is not a network failure: no retry, and the error
|
|
171
|
+
// says "cancelled", not "timed out".
|
|
172
|
+
if (signal?.aborted)
|
|
173
|
+
throw new RequestCancelledError(method, path, err);
|
|
174
|
+
networkError = isAbortError(err)
|
|
175
|
+
? timeoutError(timeoutMs, method, path, err)
|
|
176
|
+
: err instanceof Error
|
|
177
|
+
? err
|
|
178
|
+
: new Error(String(err));
|
|
179
|
+
}
|
|
180
|
+
// The query string is deliberately left out of every log line: `keywords`
|
|
181
|
+
// and the perimeter filters are end-user data.
|
|
182
|
+
const logFields = { corrId, method, path, attempt: attempt + 1, durationMs: Date.now() - attemptStartedAt };
|
|
183
|
+
if (response && response.ok) {
|
|
184
|
+
logger.debug({ ...logFields, status: response.status }, "boondmanager request");
|
|
185
|
+
return response;
|
|
186
|
+
}
|
|
187
|
+
let attemptError;
|
|
188
|
+
let isNetworkOrTimeout = false;
|
|
189
|
+
if (response) {
|
|
190
|
+
const errorText = await response.text().catch(() => "");
|
|
191
|
+
attemptError = new BoondApiError(formatApiError(response.status, response.statusText, method, path, errorText), response.status, method, path);
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
attemptError = networkError;
|
|
195
|
+
isNetworkOrTimeout = true;
|
|
196
|
+
}
|
|
197
|
+
const hasMoreAttempts = attempt < totalAttempts - 1;
|
|
198
|
+
const timedOut = isNetworkOrTimeout && isAbortError(networkError.cause);
|
|
199
|
+
const failure = response ? { status: response.status } : { reason: timedOut ? "timeout" : "network" };
|
|
200
|
+
if (!hasMoreAttempts || !isRetryable(method, response?.status, isNetworkOrTimeout)) {
|
|
201
|
+
// A final 429 or timeout is worth a warning on its own; any other final
|
|
202
|
+
// failure reaches the tool log as `ok: false` and the tool's error text.
|
|
203
|
+
if (response?.status === 429 || timedOut) {
|
|
204
|
+
logger.warn({ ...logFields, ...failure, totalAttempts }, "boondmanager request failed");
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
logger.debug({ ...logFields, ...failure, totalAttempts }, "boondmanager request failed");
|
|
208
|
+
}
|
|
209
|
+
throw attemptError;
|
|
210
|
+
}
|
|
211
|
+
// Only inspect Retry-After once we've actually decided to retry — keeps
|
|
212
|
+
// the fast path off the headers object and matches tests that build
|
|
213
|
+
// minimal Response stubs.
|
|
214
|
+
const retryAfterMs = response ? parseRetryAfter(response.headers?.get("retry-after") ?? null) : null;
|
|
215
|
+
const backoff = retryAfterMs !== null
|
|
216
|
+
? Math.min(retry.maxDelayMs, retryAfterMs)
|
|
217
|
+
: computeBackoffMs(attempt, retry.baseDelayMs, retry.maxDelayMs);
|
|
218
|
+
logger.warn({ ...logFields, ...failure, totalAttempts, backoffMs: backoff }, "boondmanager request retried");
|
|
219
|
+
try {
|
|
220
|
+
await sleep(backoff, signal);
|
|
221
|
+
}
|
|
222
|
+
catch (err) {
|
|
223
|
+
throw new RequestCancelledError(method, path, err);
|
|
224
|
+
}
|
|
225
|
+
lastError = attemptError;
|
|
226
|
+
}
|
|
227
|
+
// Defensive — the loop always returns or throws. If somehow exhausted:
|
|
228
|
+
throw lastError ?? new Error("BoondManager API request exhausted retries with no recorded error.");
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Read a 2xx body as JSON:API. A `204` / empty body (DELETE) is an empty page;
|
|
232
|
+
* a body that is not JSON becomes an error that names the endpoint instead of
|
|
233
|
+
* a bare `SyntaxError`.
|
|
234
|
+
*/
|
|
235
|
+
async function readJsonApi(response, method, path) {
|
|
236
|
+
if (response.status === 204 || response.headers.get("content-length") === "0") {
|
|
237
|
+
return { data: [] };
|
|
238
|
+
}
|
|
239
|
+
try {
|
|
240
|
+
return (await response.json());
|
|
241
|
+
}
|
|
242
|
+
catch (err) {
|
|
243
|
+
throw nonJsonResponseError(response.status, method, path, err);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
/** JSON:API request. `body` is serialised for POST / PUT / PATCH only. */
|
|
247
|
+
export async function apiRequest(path, method = "GET", body, queryParams) {
|
|
248
|
+
const serialized = body && (method === "POST" || method === "PUT" || method === "PATCH") ? JSON.stringify(body) : undefined;
|
|
249
|
+
const response = await send(path, {
|
|
250
|
+
method,
|
|
251
|
+
...(queryParams !== undefined ? { query: queryParams } : {}),
|
|
252
|
+
...(serialized !== undefined ? { body: serialized } : {}),
|
|
253
|
+
headers: { Accept: "application/json", "Content-Type": "application/json" },
|
|
254
|
+
});
|
|
255
|
+
return readJsonApi(response, method, path);
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* POST a multipart/form-data payload to the BoondManager API (document
|
|
259
|
+
* upload). Form values are simple string fields — the file itself travels by
|
|
260
|
+
* reference via the `fileUrl` field (Boond downloads it server-side), so the
|
|
261
|
+
* MCP server never buffers file bytes.
|
|
262
|
+
*/
|
|
263
|
+
export async function apiUploadForm(path, fields) {
|
|
264
|
+
const form = new FormData();
|
|
265
|
+
for (const [key, value] of Object.entries(fields)) {
|
|
266
|
+
form.set(key, value);
|
|
267
|
+
}
|
|
268
|
+
// No Content-Type header: fetch derives the multipart boundary from FormData.
|
|
269
|
+
const response = await send(path, { method: "POST", body: form, headers: { Accept: "application/json" } });
|
|
270
|
+
return readJsonApi(response, "POST", path);
|
|
271
|
+
}
|
|
272
|
+
//# sourceMappingURL=transport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../../../src/services/http/transport.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,14 +3,65 @@ import pino from "pino";
|
|
|
3
3
|
* Redaction paths for the structured logger. Defence-in-depth: nothing in the
|
|
4
4
|
* codebase currently logs auth material, but if a request/error object that
|
|
5
5
|
* carries credentials is ever passed to the logger, these paths censor it
|
|
6
|
-
* before it reaches
|
|
6
|
+
* before it reaches stderr / a log aggregator. Covers the BoondManager JWT
|
|
7
7
|
* header, OAuth Bearer headers, and raw access tokens at one level of nesting.
|
|
8
8
|
*/
|
|
9
9
|
export declare const REDACT_PATHS: string[];
|
|
10
|
+
/**
|
|
11
|
+
* File descriptor every log line is written to: **stderr, on every transport**.
|
|
12
|
+
*
|
|
13
|
+
* On the stdio transport stdout *is* the JSON-RPC channel — anything that is
|
|
14
|
+
* not an MCP message corrupts the stream and the client drops the connection.
|
|
15
|
+
* Pino defaults to fd 1, and so does the pino-pretty transport when no
|
|
16
|
+
* `destination` is given, which is exactly how the "Access policy active"
|
|
17
|
+
* line, the update notice and the dictionary-override warnings used to land in
|
|
18
|
+
* the middle of the protocol (issue #225). stderr is what Claude Desktop
|
|
19
|
+
* captures into its log viewer, and it is equally fine for the HTTP transport,
|
|
20
|
+
* so there is one destination rather than one per transport.
|
|
21
|
+
*/
|
|
22
|
+
export declare const LOG_DESTINATION_FD = 2;
|
|
23
|
+
/**
|
|
24
|
+
* Read the log level from env, falling back to 'info' for production-friendly
|
|
25
|
+
* defaults. DEBUG / trace logs are useful during development but too noisy
|
|
26
|
+
* in production. Pino's level hierarchy: trace < debug < info < warn < error < fatal.
|
|
27
|
+
*/
|
|
28
|
+
export declare function resolveLogLevel(env?: NodeJS.ProcessEnv): pino.Level;
|
|
29
|
+
/** Human-readable (pretty) output in dev, JSON in prod. Override via LOG_FORMAT. */
|
|
30
|
+
export declare function usePrettyOutput(env?: NodeJS.ProcessEnv): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* `pino-pretty` is a **devDependency** (issue #246): it only serves the
|
|
33
|
+
* human-readable output of a development shell, and the `.mcpb` bundle and
|
|
34
|
+
* the Docker image are built with `--omit=dev`. Pino loads a transport by
|
|
35
|
+
* module name in a worker thread and throws at logger creation when the
|
|
36
|
+
* target is missing, so the pretty branch is taken only when the module
|
|
37
|
+
* resolves — otherwise the logger silently falls back to JSON on stderr,
|
|
38
|
+
* which is the production shape anyway.
|
|
39
|
+
*/
|
|
40
|
+
export declare function isPrettyTransportAvailable(): boolean;
|
|
41
|
+
export type LoggerConfig = {
|
|
42
|
+
format: "pretty";
|
|
43
|
+
options: pino.LoggerOptions & {
|
|
44
|
+
transport: pino.TransportSingleOptions;
|
|
45
|
+
};
|
|
46
|
+
} | {
|
|
47
|
+
format: "json";
|
|
48
|
+
options: pino.LoggerOptions;
|
|
49
|
+
destinationFd: number;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* The two shapes the logger can take, as data — so a test can assert where each
|
|
53
|
+
* one writes without spawning a process. Pino refuses a stream argument when
|
|
54
|
+
* `transport` is set (the transport runs in a worker thread), which is why the
|
|
55
|
+
* destination travels inside the pino-pretty options on one branch and as a
|
|
56
|
+
* `pino.destination()` on the other.
|
|
57
|
+
*/
|
|
58
|
+
export declare function resolveLoggerConfig(env?: NodeJS.ProcessEnv, prettyAvailable?: boolean): LoggerConfig;
|
|
59
|
+
export declare function createLogger(env?: NodeJS.ProcessEnv): pino.Logger;
|
|
10
60
|
/**
|
|
11
61
|
* Centralized structured logger. Use this instead of console.log/error for
|
|
12
62
|
* all application logging — it provides timestamps, levels, and JSON output
|
|
13
|
-
* (when LOG_FORMAT=json) that plays nicely with log aggregators.
|
|
63
|
+
* (when LOG_FORMAT=json) that plays nicely with log aggregators. It always
|
|
64
|
+
* writes to stderr (see `LOG_DESTINATION_FD`).
|
|
14
65
|
*
|
|
15
66
|
* Example:
|
|
16
67
|
* logger.info({ sessionId: "abc", userId: 123 }, "Session initialized");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/services/logger.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/services/logger.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AAKxB;;;;;;GAMG;AACH,eAAO,MAAM,YAAY,UAYxB,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAIpC;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,IAAI,CAAC,KAAK,CAIhF;AAED,oFAAoF;AACpF,wBAAgB,eAAe,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAE7E;AAED;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,IAAI,OAAO,CAOpD;AAED,MAAM,MAAM,YAAY,GACpB;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,IAAI,CAAC,aAAa,GAAG;QAAE,SAAS,EAAE,IAAI,CAAC,sBAAsB,CAAA;KAAE,CAAA;CAAE,GAC9F;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,IAAI,CAAC,aAAa,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3E;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,GAAE,MAAM,CAAC,UAAwB,EACpC,eAAe,GAAE,OAAsC,GACtD,YAAY,CAuBd;AAED,wBAAgB,YAAY,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,IAAI,CAAC,MAAM,CAI9E;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,MAAM,6BAAiB,CAAC;AAErC;;;;GAIG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAK9C"}
|