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,3 @@
|
|
|
1
|
+
import type { JsonApiResource, JsonApiResponse } from "../../types.js";
|
|
2
|
+
export declare function formatListResponse(response: JsonApiResponse, entityType: string, fields?: string[], summaryFn?: (entity: JsonApiResource) => string): string;
|
|
3
|
+
//# sourceMappingURL=list.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/services/format/list.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAGvE,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,eAAe,EACzB,UAAU,EAAE,MAAM,EAClB,MAAM,CAAC,EAAE,MAAM,EAAE,EACjB,SAAS,GAAE,CAAC,MAAM,EAAE,eAAe,KAAK,MAA4B,GACnE,MAAM,CAwCR"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Text rendering of a search page: one summary line per row, a `Total:`
|
|
3
|
+
* header when the API reports one, and truncation on line boundaries under
|
|
4
|
+
* `CHARACTER_LIMIT` (a mid-line cut used to hide the row count).
|
|
5
|
+
*/
|
|
6
|
+
import { CHARACTER_LIMIT } from "../../constants.js";
|
|
7
|
+
import { formatEntitySummary, formatProjectedSummary } from "./summary.js";
|
|
8
|
+
export function formatListResponse(response, entityType, fields, summaryFn = formatEntitySummary) {
|
|
9
|
+
// `data: null` is what BoondManager answers on some empty windows
|
|
10
|
+
// (`/times-reports` on a month with no report, #243); it used to become
|
|
11
|
+
// `[null]` and crash the per-row summary.
|
|
12
|
+
const data = Array.isArray(response.data) ? response.data : response.data ? [response.data] : [];
|
|
13
|
+
const total = response.meta?.totals?.rows;
|
|
14
|
+
if (data.length === 0) {
|
|
15
|
+
return `Aucun(e) ${entityType} trouvé(e).`;
|
|
16
|
+
}
|
|
17
|
+
const projected = fields !== undefined && fields.length > 0;
|
|
18
|
+
const lines = data.map((item) => (projected ? formatProjectedSummary(item, fields) : summaryFn(item)));
|
|
19
|
+
const header = total !== undefined ? `Total: ${total} ${entityType}(s)\n\n` : "";
|
|
20
|
+
const body = lines.join("\n");
|
|
21
|
+
if (header.length + body.length <= CHARACTER_LIMIT)
|
|
22
|
+
return header + body;
|
|
23
|
+
// Cut on line boundaries and say how many rows were dropped. A mid-line cut
|
|
24
|
+
// produced a half-row indistinguishable from a complete one, and the count
|
|
25
|
+
// is what tells the model to narrow the query (or use `fields`/`pageSize`)
|
|
26
|
+
// instead of trusting an implicitly complete page.
|
|
27
|
+
const notice = (shown) => `\n\n[Résultats tronqués : ${shown}/${lines.length} ligne(s) affichée(s) (limite de ${CHARACTER_LIMIT} caractères). ` +
|
|
28
|
+
`Affinez les filtres, réduisez pageSize, ou utilisez 'fields' pour raccourcir chaque ligne.]`;
|
|
29
|
+
const budget = CHARACTER_LIMIT - header.length - notice(lines.length).length;
|
|
30
|
+
const kept = [];
|
|
31
|
+
let used = 0;
|
|
32
|
+
for (const line of lines) {
|
|
33
|
+
const cost = kept.length === 0 ? line.length : line.length + 1;
|
|
34
|
+
if (used + cost > budget)
|
|
35
|
+
break;
|
|
36
|
+
used += cost;
|
|
37
|
+
kept.push(line);
|
|
38
|
+
}
|
|
39
|
+
// A single row longer than the whole budget still has to show something.
|
|
40
|
+
if (kept.length === 0)
|
|
41
|
+
return header + body.substring(0, Math.max(budget, 0)) + notice(0);
|
|
42
|
+
return header + kept.join("\n") + notice(kept.length);
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.js","sourceRoot":"","sources":["../../../src/services/format/list.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAErD,OAAO,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAE3E,MAAM,UAAU,kBAAkB,CAChC,QAAyB,EACzB,UAAkB,EAClB,MAAiB,EACjB,YAAiD,mBAAmB;IAEpE,kEAAkE;IAClE,wEAAwE;IACxE,0CAA0C;IAC1C,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACjG,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC;IAE1C,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO,YAAY,UAAU,aAAa,CAAC;IAC7C,CAAC;IAED,MAAM,SAAS,GAAG,MAAM,KAAK,SAAS,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;IAC5D,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,sBAAsB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACvG,MAAM,MAAM,GAAG,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,UAAU,KAAK,IAAI,UAAU,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;IACjF,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAE9B,IAAI,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,eAAe;QAAE,OAAO,MAAM,GAAG,IAAI,CAAC;IAEzE,4EAA4E;IAC5E,2EAA2E;IAC3E,2EAA2E;IAC3E,mDAAmD;IACnD,MAAM,MAAM,GAAG,CAAC,KAAa,EAAE,EAAE,CAC/B,6BAA6B,KAAK,IAAI,KAAK,CAAC,MAAM,oCAAoC,eAAe,gBAAgB;QACrH,6FAA6F,CAAC;IAChG,MAAM,MAAM,GAAG,eAAe,GAAG,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC;IAE7E,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;QAC/D,IAAI,IAAI,GAAG,IAAI,GAAG,MAAM;YAAE,MAAM;QAChC,IAAI,IAAI,IAAI,CAAC;QACb,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClB,CAAC;IAED,yEAAyE;IACzE,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IAE1F,OAAO,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AACxD,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single rendering rule for a raw JSON:API attribute value, shared by the
|
|
3
|
+
* fallback summary and the `fields` projection — some Boond amounts come back
|
|
4
|
+
* as `{ amount, currency }` objects, and the two paths used to disagree
|
|
5
|
+
* (`[object Object]` on one side, JSON on the other).
|
|
6
|
+
*/
|
|
7
|
+
export declare function renderAttributeValue(value: unknown): string;
|
|
8
|
+
export declare function formatEntitySummary(entity: unknown): string;
|
|
9
|
+
/**
|
|
10
|
+
* One result line restricted to the caller-selected attribute names.
|
|
11
|
+
* Unknown names are skipped silently (the schemas document this), so a typo
|
|
12
|
+
* degrades to a shorter line rather than an error. Non-primitive values are
|
|
13
|
+
* JSON-serialised — some Boond attributes are nested objects.
|
|
14
|
+
*/
|
|
15
|
+
export declare function formatProjectedSummary(entity: unknown, fields: string[]): string;
|
|
16
|
+
//# sourceMappingURL=summary.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"summary.d.ts","sourceRoot":"","sources":["../../../src/services/format/summary.ts"],"names":[],"mappings":"AAgCA;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAE3D;AAyDD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAqD3D;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAchF"}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One-line summaries of a JSON:API row for list output: the standard summary
|
|
3
|
+
* (`formatEntitySummary`, with its business-identifier fallback) and the
|
|
4
|
+
* `fields` projection. The named-row output is pinned byte-for-byte in tests.
|
|
5
|
+
*/
|
|
6
|
+
import { textExcerpt } from "./html.js";
|
|
7
|
+
/**
|
|
8
|
+
* Business identifiers used as a last resort when a list row has no
|
|
9
|
+
* human-readable identity (no name, no title, no dictionary `value`).
|
|
10
|
+
*
|
|
11
|
+
* Transactional endpoints (`/invoices`, `/orders`, `/actions`,
|
|
12
|
+
* `/deliveries-groupments`, `/projects`…) key their rows on a reference, a
|
|
13
|
+
* number or a date rather than on a name, so the standard summary rendered
|
|
14
|
+
* them as a bare `[order #1234] | Statut: 1` — a line the model cannot act on
|
|
15
|
+
* without a follow-up `_get` per row.
|
|
16
|
+
*
|
|
17
|
+
* These are deliberately NOT appended unconditionally: `/resources` and
|
|
18
|
+
* `/opportunities` also carry `reference` and amount attributes, and their
|
|
19
|
+
* rows already read well (name, title). Enriching them too would only inflate
|
|
20
|
+
* every line. See `hasIdentity` in formatEntitySummary.
|
|
21
|
+
*/
|
|
22
|
+
const AMOUNT_FALLBACK_FIELDS = [
|
|
23
|
+
["turnoverInvoicedExcludingTax", "CA facturé HT"],
|
|
24
|
+
["turnoverOrderedExcludingTax", "CA commandé HT"],
|
|
25
|
+
["turnoverSimulatedExcludingTax", "CA simulé HT"],
|
|
26
|
+
["averageDailyPriceExcludingTax", "TJM HT"],
|
|
27
|
+
];
|
|
28
|
+
/** Max amount entries appended to a fallback line, to keep it scannable. */
|
|
29
|
+
const MAX_FALLBACK_AMOUNTS = 2;
|
|
30
|
+
/**
|
|
31
|
+
* Single rendering rule for a raw JSON:API attribute value, shared by the
|
|
32
|
+
* fallback summary and the `fields` projection — some Boond amounts come back
|
|
33
|
+
* as `{ amount, currency }` objects, and the two paths used to disagree
|
|
34
|
+
* (`[object Object]` on one side, JSON on the other).
|
|
35
|
+
*/
|
|
36
|
+
export function renderAttributeValue(value) {
|
|
37
|
+
return value === null || typeof value === "object" ? JSON.stringify(value) : String(value);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* A row is considered to name itself through `value` only when that value is a
|
|
41
|
+
* non-empty string once rendered. `value: null` / `value: ""` used to both
|
|
42
|
+
* print a bogus token *and* suppress the business-identifier fallback.
|
|
43
|
+
*/
|
|
44
|
+
function hasValueIdentity(value) {
|
|
45
|
+
return value !== undefined && value !== null && renderAttributeValue(value) !== "";
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Secondary identifiers for rows that have no name/title/value. Order is
|
|
49
|
+
* chosen so the most identifying token comes first (number, then reference,
|
|
50
|
+
* then when it happened, then how much).
|
|
51
|
+
*/
|
|
52
|
+
function fallbackIdentityParts(attrs) {
|
|
53
|
+
const parts = [];
|
|
54
|
+
if (attrs.number)
|
|
55
|
+
parts.push(`N°: ${attrs.number}`);
|
|
56
|
+
if (attrs.reference)
|
|
57
|
+
parts.push(`Réf: ${attrs.reference}`);
|
|
58
|
+
if (attrs.date) {
|
|
59
|
+
parts.push(`Date: ${attrs.date}`);
|
|
60
|
+
}
|
|
61
|
+
else if (attrs.startDate && attrs.endDate) {
|
|
62
|
+
parts.push(`Du ${attrs.startDate} au ${attrs.endDate}`);
|
|
63
|
+
}
|
|
64
|
+
else if (attrs.startDate) {
|
|
65
|
+
parts.push(`Début: ${attrs.startDate}`);
|
|
66
|
+
}
|
|
67
|
+
else if (attrs.endDate) {
|
|
68
|
+
parts.push(`Fin: ${attrs.endDate}`);
|
|
69
|
+
}
|
|
70
|
+
let amounts = 0;
|
|
71
|
+
for (const [field, label] of AMOUNT_FALLBACK_FIELDS) {
|
|
72
|
+
if (amounts >= MAX_FALLBACK_AMOUNTS)
|
|
73
|
+
break;
|
|
74
|
+
const value = attrs[field];
|
|
75
|
+
// 0 is meaningful here (an order with no turnover yet), so only
|
|
76
|
+
// undefined/null are skipped.
|
|
77
|
+
if (value === undefined || value === null)
|
|
78
|
+
continue;
|
|
79
|
+
parts.push(`${label}: ${renderAttributeValue(value)}`);
|
|
80
|
+
amounts++;
|
|
81
|
+
}
|
|
82
|
+
// `typeOf` is an integer resolved through boond://dictionary/typeOf/* — on
|
|
83
|
+
// its own it is weak, but on an action it is often the only discriminator.
|
|
84
|
+
if (attrs.typeOf !== undefined && attrs.typeOf !== null)
|
|
85
|
+
parts.push(`Type: ${attrs.typeOf}`);
|
|
86
|
+
// End-user-authored free text: labelled and quoted so the model reads it as
|
|
87
|
+
// a data field of the row and not as server-authored instructions.
|
|
88
|
+
if (typeof attrs.text === "string") {
|
|
89
|
+
const excerpt = textExcerpt(attrs.text);
|
|
90
|
+
if (excerpt !== undefined)
|
|
91
|
+
parts.push(`Note: "${excerpt}"`);
|
|
92
|
+
}
|
|
93
|
+
return parts;
|
|
94
|
+
}
|
|
95
|
+
export function formatEntitySummary(entity) {
|
|
96
|
+
// A few BoondManager endpoints (e.g. `/calendars`, `/application/dictionary`)
|
|
97
|
+
// return reference items as flat objects without a JSON:API `attributes`
|
|
98
|
+
// wrapper. Treating the whole entity as the attribute bag in that case
|
|
99
|
+
// keeps `formatListResponse` from crashing on `attrs.firstName` and yields
|
|
100
|
+
// a still-useful summary.
|
|
101
|
+
const e = (entity ?? {});
|
|
102
|
+
const hasAttrs = e.attributes !== undefined && e.attributes !== null && typeof e.attributes === "object";
|
|
103
|
+
const attrs = hasAttrs ? e.attributes : e;
|
|
104
|
+
const id = e.id !== undefined ? String(e.id) : undefined;
|
|
105
|
+
const type = e.type !== undefined ? String(e.type) : undefined;
|
|
106
|
+
const header = id !== undefined && type !== undefined
|
|
107
|
+
? `[${type} #${id}]`
|
|
108
|
+
: id !== undefined
|
|
109
|
+
? `[#${id}]`
|
|
110
|
+
: type !== undefined
|
|
111
|
+
? `[${type}]`
|
|
112
|
+
: "[item]";
|
|
113
|
+
const parts = [header];
|
|
114
|
+
// Common name fields
|
|
115
|
+
if (attrs.firstName || attrs.lastName) {
|
|
116
|
+
parts.push(`${attrs.firstName || ""} ${attrs.lastName || ""}`.trim());
|
|
117
|
+
}
|
|
118
|
+
if (attrs.name)
|
|
119
|
+
parts.push(String(attrs.name));
|
|
120
|
+
// `value` covers the `/calendars` and dictionary-style payloads. `0` is a
|
|
121
|
+
// legitimate label there, so only null/undefined/"" are skipped.
|
|
122
|
+
if (!attrs.firstName && !attrs.lastName && !attrs.name && hasValueIdentity(attrs.value)) {
|
|
123
|
+
parts.push(renderAttributeValue(attrs.value));
|
|
124
|
+
}
|
|
125
|
+
if (attrs.email1)
|
|
126
|
+
parts.push(`Email: ${attrs.email1}`);
|
|
127
|
+
if (attrs.phone1)
|
|
128
|
+
parts.push(`Tel: ${attrs.phone1}`);
|
|
129
|
+
if (attrs.city)
|
|
130
|
+
parts.push(`Ville: ${attrs.city}`);
|
|
131
|
+
if (attrs.state !== undefined)
|
|
132
|
+
parts.push(`Statut: ${attrs.state}`);
|
|
133
|
+
if (attrs.title)
|
|
134
|
+
parts.push(`Titre: ${attrs.title}`);
|
|
135
|
+
if (attrs.iso !== undefined && String(attrs.iso) !== id)
|
|
136
|
+
parts.push(`ISO: ${attrs.iso}`);
|
|
137
|
+
// Rows that named themselves are already useful — leave them untouched.
|
|
138
|
+
// Only the ones reduced to `[type #id]` (+ maybe a status integer) get the
|
|
139
|
+
// business identifiers appended.
|
|
140
|
+
const hasIdentity = Boolean(attrs.firstName) ||
|
|
141
|
+
Boolean(attrs.lastName) ||
|
|
142
|
+
Boolean(attrs.name) ||
|
|
143
|
+
Boolean(attrs.title) ||
|
|
144
|
+
hasValueIdentity(attrs.value);
|
|
145
|
+
if (!hasIdentity) {
|
|
146
|
+
parts.push(...fallbackIdentityParts(attrs));
|
|
147
|
+
}
|
|
148
|
+
return parts.join(" | ");
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* One result line restricted to the caller-selected attribute names.
|
|
152
|
+
* Unknown names are skipped silently (the schemas document this), so a typo
|
|
153
|
+
* degrades to a shorter line rather than an error. Non-primitive values are
|
|
154
|
+
* JSON-serialised — some Boond attributes are nested objects.
|
|
155
|
+
*/
|
|
156
|
+
export function formatProjectedSummary(entity, fields) {
|
|
157
|
+
const e = (entity ?? {});
|
|
158
|
+
const attrs = (e.attributes ?? e);
|
|
159
|
+
// Reference endpoints return flat rows keyed on something else than `id`
|
|
160
|
+
// (`/calendars` keys countries on `iso`), so a missing id renders as the same
|
|
161
|
+
// `[item]` token the standard summary uses — not as a `[#?]` that reads like
|
|
162
|
+
// a formatting bug.
|
|
163
|
+
const parts = [e.id !== undefined ? `[#${String(e.id)}]` : "[item]"];
|
|
164
|
+
for (const field of fields) {
|
|
165
|
+
const value = attrs[field];
|
|
166
|
+
if (value === undefined)
|
|
167
|
+
continue;
|
|
168
|
+
parts.push(`${field}: ${renderAttributeValue(value)}`);
|
|
169
|
+
}
|
|
170
|
+
return parts.join(" | ");
|
|
171
|
+
}
|
|
172
|
+
//# sourceMappingURL=summary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"summary.js","sourceRoot":"","sources":["../../../src/services/format/summary.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { JsonApiResponse } from "../../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Formate la réponse d'un endpoint d'onglet (ex: /resources/{id}/positionings).
|
|
4
|
+
* Contrairement à formatDetailResponse, un tableau est restitué en entier :
|
|
5
|
+
* certains onglets renvoient plusieurs entités (positionnements, contacts...)
|
|
6
|
+
* et n'afficher que la première masquait les autres.
|
|
7
|
+
*/
|
|
8
|
+
export declare function formatTabResponse(response: JsonApiResponse): string;
|
|
9
|
+
//# sourceMappingURL=tab.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tab.d.ts","sourceRoot":"","sources":["../../../src/services/format/tab.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAGtD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,eAAe,GAAG,MAAM,CA0BnE"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rendering of an entity-tab endpoint (`/resources/{id}/positionings`…).
|
|
3
|
+
*/
|
|
4
|
+
import { CHARACTER_LIMIT } from "../../constants.js";
|
|
5
|
+
import { formatDetailResponse, projectEntity } from "./detail.js";
|
|
6
|
+
/**
|
|
7
|
+
* Formate la réponse d'un endpoint d'onglet (ex: /resources/{id}/positionings).
|
|
8
|
+
* Contrairement à formatDetailResponse, un tableau est restitué en entier :
|
|
9
|
+
* certains onglets renvoient plusieurs entités (positionnements, contacts...)
|
|
10
|
+
* et n'afficher que la première masquait les autres.
|
|
11
|
+
*/
|
|
12
|
+
export function formatTabResponse(response) {
|
|
13
|
+
if (!Array.isArray(response.data)) {
|
|
14
|
+
return formatDetailResponse(response);
|
|
15
|
+
}
|
|
16
|
+
const entities = response.data.map(projectEntity);
|
|
17
|
+
// Some tabs carry aggregates in `meta.totals` beyond the row count —
|
|
18
|
+
// `/orders/{id}/invoices` publishes the ordered / invoiced / remaining
|
|
19
|
+
// turnover there (issue #258). They are the point of reading that tab, so
|
|
20
|
+
// they are rendered ahead of the rows instead of being dropped.
|
|
21
|
+
const totals = response.meta?.totals;
|
|
22
|
+
const aggregates = totals && typeof totals === "object" ? Object.entries(totals).filter(([key]) => key !== "rows") : [];
|
|
23
|
+
const header = aggregates.length > 0
|
|
24
|
+
? `${entities.length} élément(s) — totaux : ${JSON.stringify(Object.fromEntries(aggregates))}`
|
|
25
|
+
: `${entities.length} élément(s)`;
|
|
26
|
+
let result = `${header}\n\n` + JSON.stringify(entities, null, 2);
|
|
27
|
+
if (result.length > CHARACTER_LIMIT) {
|
|
28
|
+
result = result.substring(0, CHARACTER_LIMIT) + "\n\n[Résultat tronqué...]";
|
|
29
|
+
}
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=tab.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tab.js","sourceRoot":"","sources":["../../../src/services/format/tab.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAErD,OAAO,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAElE;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,QAAyB;IACzD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QAClC,OAAO,oBAAoB,CAAC,QAAQ,CAAC,CAAC;IACxC,CAAC;IAED,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;IAElD,qEAAqE;IACrE,uEAAuE;IACvE,0EAA0E;IAC1E,gEAAgE;IAChE,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACrC,MAAM,UAAU,GACd,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACvG,MAAM,MAAM,GACV,UAAU,CAAC,MAAM,GAAG,CAAC;QACnB,CAAC,CAAC,GAAG,QAAQ,CAAC,MAAM,0BAA0B,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC,EAAE;QAC9F,CAAC,CAAC,GAAG,QAAQ,CAAC,MAAM,aAAa,CAAC;IAEtC,IAAI,MAAM,GAAG,GAAG,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAEjE,IAAI,MAAM,CAAC,MAAM,GAAG,eAAe,EAAE,CAAC;QACpC,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,EAAE,eAAe,CAAC,GAAG,2BAA2B,CAAC;IAC9E,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { BoondAuthProvider, BoondConfig } from "../../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Auth provider for the HTTP transport: reads the Bearer token from the
|
|
4
|
+
* per-request AsyncLocalStorage populated by the transport layer and
|
|
5
|
+
* forwards it verbatim to BoondManager as `Authorization: Bearer …`.
|
|
6
|
+
*
|
|
7
|
+
* Errors out clearly if called outside a request context — which would
|
|
8
|
+
* indicate that the transport layer forgot to wrap the request in
|
|
9
|
+
* `oauthContext.run(...)`.
|
|
10
|
+
*/
|
|
11
|
+
export declare const oauthContextAuth: BoondAuthProvider;
|
|
12
|
+
/**
|
|
13
|
+
* Build the BoondManager HS256 JWT. By default the payload is exactly
|
|
14
|
+
* `{ userToken, clientToken }` (BoondManager's documented scheme). When
|
|
15
|
+
* `expiresInSeconds` is provided, standard `iat`/`exp` claims are added so the
|
|
16
|
+
* generated token is no longer replayable forever if it leaks — this requires
|
|
17
|
+
* regenerating the token per request (see `jwtAuth`). Opt-in because not every
|
|
18
|
+
* BoondManager deployment is known to honour `exp`.
|
|
19
|
+
*/
|
|
20
|
+
export declare function buildJwt(userToken: string, clientToken: string, clientKey: string, options?: {
|
|
21
|
+
expiresInSeconds?: number;
|
|
22
|
+
nowSeconds?: number;
|
|
23
|
+
}): string;
|
|
24
|
+
export declare const JWT_HEADER_NAME = "X-Jwt-Client-Boondmanager";
|
|
25
|
+
export declare function initClient(): void;
|
|
26
|
+
/**
|
|
27
|
+
* True when env-based credentials (JWT components, API token, or BasicAuth) are
|
|
28
|
+
* configured. Used by the HTTP transport in static-auth mode
|
|
29
|
+
* (`BOOND_HTTP_STATIC_AUTH=true`) to fail fast with a readable message before
|
|
30
|
+
* `initClient()` throws; the default HTTP mode is an OAuth2 protected resource
|
|
31
|
+
* and never reads these variables.
|
|
32
|
+
*/
|
|
33
|
+
export declare function hasEnvCredentials(): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Install a custom auth provider — used by the HTTP transport bootstrap to
|
|
36
|
+
* wire in an OAuth2 token source (where the access token is refreshed
|
|
37
|
+
* transparently per request rather than baked in at startup).
|
|
38
|
+
*/
|
|
39
|
+
export declare function initClientWithAuth(auth: BoondAuthProvider, baseUrl?: string): void;
|
|
40
|
+
/** Test helper — reset the cached config so the next call re-initialises. */
|
|
41
|
+
export declare function resetClientForTests(): void;
|
|
42
|
+
/** The active configuration, initialising from the environment on first use. */
|
|
43
|
+
export declare function getConfig(): BoondConfig;
|
|
44
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/services/http/auth.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAKrE;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB,EAAE,iBAU9B,CAAC;AAOF;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CACtB,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE;IAAE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GAC3D,MAAM,CAWR;AAED,eAAO,MAAM,eAAe,8BAA8B,CAAC;AAgC3D,wBAAgB,UAAU,IAAI,IAAI,CAsCjC;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAM3C;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,iBAAiB,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAKlF;AAED,6EAA6E;AAC7E,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C;AAED,gFAAgF;AAChF,wBAAgB,SAAS,IAAI,WAAW,CAKvC"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client configuration and the three auth providers (JWT built from
|
|
3
|
+
* components, static header, OAuth Bearer read from the request context).
|
|
4
|
+
*
|
|
5
|
+
* Split out of `boond-client.ts` (issue #239): everything here decides *who*
|
|
6
|
+
* the request is sent as; nothing here sends anything.
|
|
7
|
+
*/
|
|
8
|
+
import { createHmac } from "crypto";
|
|
9
|
+
import { readString, readUrl } from "../../config/env.js";
|
|
10
|
+
import { DEFAULT_BASE_URL } from "../../constants.js";
|
|
11
|
+
import { oauthContext } from "../oauth.js";
|
|
12
|
+
let config = null;
|
|
13
|
+
/**
|
|
14
|
+
* Auth provider for the HTTP transport: reads the Bearer token from the
|
|
15
|
+
* per-request AsyncLocalStorage populated by the transport layer and
|
|
16
|
+
* forwards it verbatim to BoondManager as `Authorization: Bearer …`.
|
|
17
|
+
*
|
|
18
|
+
* Errors out clearly if called outside a request context — which would
|
|
19
|
+
* indicate that the transport layer forgot to wrap the request in
|
|
20
|
+
* `oauthContext.run(...)`.
|
|
21
|
+
*/
|
|
22
|
+
export const oauthContextAuth = () => {
|
|
23
|
+
const ctx = oauthContext.getStore();
|
|
24
|
+
if (!ctx) {
|
|
25
|
+
return Promise.reject(new Error("No OAuth access token in request context. The HTTP transport requires an `Authorization: Bearer <boond_access_token>` header on every request."));
|
|
26
|
+
}
|
|
27
|
+
return Promise.resolve({ name: "Authorization", value: `Bearer ${ctx.accessToken}` });
|
|
28
|
+
};
|
|
29
|
+
function base64url(data) {
|
|
30
|
+
const b64 = Buffer.from(data).toString("base64");
|
|
31
|
+
return b64.replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Build the BoondManager HS256 JWT. By default the payload is exactly
|
|
35
|
+
* `{ userToken, clientToken }` (BoondManager's documented scheme). When
|
|
36
|
+
* `expiresInSeconds` is provided, standard `iat`/`exp` claims are added so the
|
|
37
|
+
* generated token is no longer replayable forever if it leaks — this requires
|
|
38
|
+
* regenerating the token per request (see `jwtAuth`). Opt-in because not every
|
|
39
|
+
* BoondManager deployment is known to honour `exp`.
|
|
40
|
+
*/
|
|
41
|
+
export function buildJwt(userToken, clientToken, clientKey, options) {
|
|
42
|
+
const header = base64url(JSON.stringify({ alg: "HS256", typ: "JWT" }));
|
|
43
|
+
const claims = { userToken, clientToken };
|
|
44
|
+
if (options?.expiresInSeconds && options.expiresInSeconds > 0) {
|
|
45
|
+
const now = options.nowSeconds ?? Math.floor(Date.now() / 1000);
|
|
46
|
+
claims.iat = now;
|
|
47
|
+
claims.exp = now + options.expiresInSeconds;
|
|
48
|
+
}
|
|
49
|
+
const payload = base64url(JSON.stringify(claims));
|
|
50
|
+
const signature = base64url(createHmac("sha256", clientKey).update(`${header}.${payload}`).digest());
|
|
51
|
+
return `${header}.${payload}.${signature}`;
|
|
52
|
+
}
|
|
53
|
+
export const JWT_HEADER_NAME = "X-Jwt-Client-Boondmanager";
|
|
54
|
+
/**
|
|
55
|
+
* Wrap a static header pair in the dynamic AuthProvider contract.
|
|
56
|
+
* Used by the stdio transport, which sticks to the JWT / BasicAuth paths.
|
|
57
|
+
*/
|
|
58
|
+
function staticAuth(name, value) {
|
|
59
|
+
const cached = Promise.resolve({ name, value });
|
|
60
|
+
return () => cached;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* JWT auth provider. When `ttlSeconds` is set (via BOOND_JWT_TTL_SECONDS), a
|
|
64
|
+
* fresh token with `iat`/`exp` is minted per request so a leaked token expires;
|
|
65
|
+
* otherwise the token is built once and cached (legacy, never-expiring).
|
|
66
|
+
*/
|
|
67
|
+
function jwtAuth(userToken, clientToken, clientKey, ttlSeconds) {
|
|
68
|
+
if (!ttlSeconds || ttlSeconds <= 0) {
|
|
69
|
+
return staticAuth(JWT_HEADER_NAME, buildJwt(userToken, clientToken, clientKey));
|
|
70
|
+
}
|
|
71
|
+
return () => Promise.resolve({
|
|
72
|
+
name: JWT_HEADER_NAME,
|
|
73
|
+
value: buildJwt(userToken, clientToken, clientKey, { expiresInSeconds: ttlSeconds }),
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
export function initClient() {
|
|
77
|
+
const baseUrl = readUrl("BOOND_BASE_URL") ?? DEFAULT_BASE_URL;
|
|
78
|
+
// Auth priority (stdio transport):
|
|
79
|
+
// 1. Build JWT from components (userToken + clientToken + clientKey)
|
|
80
|
+
// 2. Pre-built JWT token
|
|
81
|
+
// 3. BasicAuth (user:password)
|
|
82
|
+
//
|
|
83
|
+
// Per BoondManager's JWT spec the token must travel in the
|
|
84
|
+
// `X-Jwt-Client-Boondmanager` header — sending it as `Authorization: Bearer`
|
|
85
|
+
// makes the API reject the request with 422 "Signature verification failed".
|
|
86
|
+
// BasicAuth, on the other hand, uses the standard `Authorization` header.
|
|
87
|
+
//
|
|
88
|
+
// HTTP transport uses OAuth2 exclusively — see `initClientWithAuth`.
|
|
89
|
+
const userToken = readString("BOOND_USER_TOKEN");
|
|
90
|
+
const clientToken = readString("BOOND_CLIENT_TOKEN");
|
|
91
|
+
const clientKey = readString("BOOND_CLIENT_KEY");
|
|
92
|
+
const token = readString("BOOND_API_TOKEN");
|
|
93
|
+
const user = readString("BOOND_USER");
|
|
94
|
+
const password = readString("BOOND_PASSWORD");
|
|
95
|
+
let auth;
|
|
96
|
+
if (userToken && clientToken && clientKey) {
|
|
97
|
+
const ttlRaw = readString("BOOND_JWT_TTL_SECONDS");
|
|
98
|
+
const ttlSeconds = ttlRaw ? Number(ttlRaw) : undefined;
|
|
99
|
+
auth = jwtAuth(userToken, clientToken, clientKey, Number.isFinite(ttlSeconds) ? ttlSeconds : undefined);
|
|
100
|
+
}
|
|
101
|
+
else if (token) {
|
|
102
|
+
auth = staticAuth(JWT_HEADER_NAME, token);
|
|
103
|
+
}
|
|
104
|
+
else if (user && password) {
|
|
105
|
+
auth = staticAuth("Authorization", `Basic ${Buffer.from(`${user}:${password}`).toString("base64")}`);
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
throw new Error("Authentication required. Set BOOND_USER_TOKEN + BOOND_CLIENT_TOKEN + BOOND_CLIENT_KEY, or BOOND_API_TOKEN, or both BOOND_USER and BOOND_PASSWORD.");
|
|
109
|
+
}
|
|
110
|
+
config = { baseUrl, auth };
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* True when env-based credentials (JWT components, API token, or BasicAuth) are
|
|
114
|
+
* configured. Used by the HTTP transport in static-auth mode
|
|
115
|
+
* (`BOOND_HTTP_STATIC_AUTH=true`) to fail fast with a readable message before
|
|
116
|
+
* `initClient()` throws; the default HTTP mode is an OAuth2 protected resource
|
|
117
|
+
* and never reads these variables.
|
|
118
|
+
*/
|
|
119
|
+
export function hasEnvCredentials() {
|
|
120
|
+
return !!((readString("BOOND_USER_TOKEN") && readString("BOOND_CLIENT_TOKEN") && readString("BOOND_CLIENT_KEY")) ||
|
|
121
|
+
readString("BOOND_API_TOKEN") ||
|
|
122
|
+
(readString("BOOND_USER") && readString("BOOND_PASSWORD")));
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Install a custom auth provider — used by the HTTP transport bootstrap to
|
|
126
|
+
* wire in an OAuth2 token source (where the access token is refreshed
|
|
127
|
+
* transparently per request rather than baked in at startup).
|
|
128
|
+
*/
|
|
129
|
+
export function initClientWithAuth(auth, baseUrl) {
|
|
130
|
+
config = {
|
|
131
|
+
baseUrl: baseUrl ?? readUrl("BOOND_BASE_URL") ?? DEFAULT_BASE_URL,
|
|
132
|
+
auth,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
/** Test helper — reset the cached config so the next call re-initialises. */
|
|
136
|
+
export function resetClientForTests() {
|
|
137
|
+
config = null;
|
|
138
|
+
}
|
|
139
|
+
/** The active configuration, initialising from the environment on first use. */
|
|
140
|
+
export function getConfig() {
|
|
141
|
+
if (!config) {
|
|
142
|
+
initClient();
|
|
143
|
+
}
|
|
144
|
+
return config;
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../src/services/http/auth.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,IAAI,MAAM,GAAuB,IAAI,CAAC;AAEtC;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAsB,GAAG,EAAE;IACtD,MAAM,GAAG,GAAG,YAAY,CAAC,QAAQ,EAAE,CAAC;IACpC,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,OAAO,OAAO,CAAC,MAAM,CACnB,IAAI,KAAK,CACP,gJAAgJ,CACjJ,CACF,CAAC;IACJ,CAAC;IACD,OAAO,OAAO,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,UAAU,GAAG,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;AACxF,CAAC,CAAC;AAEF,SAAS,SAAS,CAAC,IAAqB;IACtC,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACjD,OAAO,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;AACvE,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,QAAQ,CACtB,SAAiB,EACjB,WAAmB,EACnB,SAAiB,EACjB,OAA4D;IAE5D,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;IACvE,MAAM,MAAM,GAA4B,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC;IACnE,IAAI,OAAO,EAAE,gBAAgB,IAAI,OAAO,CAAC,gBAAgB,GAAG,CAAC,EAAE,CAAC;QAC9D,MAAM,GAAG,GAAG,OAAO,CAAC,UAAU,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;QAChE,MAAM,CAAC,GAAG,GAAG,GAAG,CAAC;QACjB,MAAM,CAAC,GAAG,GAAG,GAAG,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAC9C,CAAC;IACD,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;IAClD,MAAM,SAAS,GAAG,SAAS,CAAC,UAAU,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC,MAAM,CAAC,GAAG,MAAM,IAAI,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;IACrG,OAAO,GAAG,MAAM,IAAI,OAAO,IAAI,SAAS,EAAE,CAAC;AAC7C,CAAC;AAED,MAAM,CAAC,MAAM,eAAe,GAAG,2BAA2B,CAAC;AAE3D;;;GAGG;AACH,SAAS,UAAU,CAAC,IAAY,EAAE,KAAa;IAC7C,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAChD,OAAO,GAAG,EAAE,CAAC,MAAM,CAAC;AACtB,CAAC;AAED;;;;GAIG;AACH,SAAS,OAAO,CACd,SAAiB,EACjB,WAAmB,EACnB,SAAiB,EACjB,UAA8B;IAE9B,IAAI,CAAC,UAAU,IAAI,UAAU,IAAI,CAAC,EAAE,CAAC;QACnC,OAAO,UAAU,CAAC,eAAe,EAAE,QAAQ,CAAC,SAAS,EAAE,WAAW,EAAE,SAAS,CAAC,CAAC,CAAC;IAClF,CAAC;IACD,OAAO,GAAG,EAAE,CACV,OAAO,CAAC,OAAO,CAAC;QACd,IAAI,EAAE,eAAe;QACrB,KAAK,EAAE,QAAQ,CAAC,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,EAAE,gBAAgB,EAAE,UAAU,EAAE,CAAC;KACrF,CAAC,CAAC;AACP,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,MAAM,OAAO,GAAG,OAAO,CAAC,gBAAgB,CAAC,IAAI,gBAAgB,CAAC;IAE9D,mCAAmC;IACnC,qEAAqE;IACrE,yBAAyB;IACzB,+BAA+B;IAC/B,EAAE;IACF,2DAA2D;IAC3D,6EAA6E;IAC7E,6EAA6E;IAC7E,0EAA0E;IAC1E,EAAE;IACF,qEAAqE;IACrE,MAAM,SAAS,GAAG,UAAU,CAAC,kBAAkB,CAAC,CAAC;IACjD,MAAM,WAAW,GAAG,UAAU,CAAC,oBAAoB,CAAC,CAAC;IACrD,MAAM,SAAS,GAAG,UAAU,CAAC,kBAAkB,CAAC,CAAC;IACjD,MAAM,KAAK,GAAG,UAAU,CAAC,iBAAiB,CAAC,CAAC;IAC5C,MAAM,IAAI,GAAG,UAAU,CAAC,YAAY,CAAC,CAAC;IACtC,MAAM,QAAQ,GAAG,UAAU,CAAC,gBAAgB,CAAC,CAAC;IAE9C,IAAI,IAAuB,CAAC;IAE5B,IAAI,SAAS,IAAI,WAAW,IAAI,SAAS,EAAE,CAAC;QAC1C,MAAM,MAAM,GAAG,UAAU,CAAC,uBAAuB,CAAC,CAAC;QACnD,MAAM,UAAU,GAAG,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QACvD,IAAI,GAAG,OAAO,CAAC,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAC1G,CAAC;SAAM,IAAI,KAAK,EAAE,CAAC;QACjB,IAAI,GAAG,UAAU,CAAC,eAAe,EAAE,KAAK,CAAC,CAAC;IAC5C,CAAC;SAAM,IAAI,IAAI,IAAI,QAAQ,EAAE,CAAC;QAC5B,IAAI,GAAG,UAAU,CAAC,eAAe,EAAE,SAAS,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,IAAI,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IACvG,CAAC;SAAM,CAAC;QACN,MAAM,IAAI,KAAK,CACb,mJAAmJ,CACpJ,CAAC;IACJ,CAAC;IAED,MAAM,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAC7B,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB;IAC/B,OAAO,CAAC,CAAC,CACP,CAAC,UAAU,CAAC,kBAAkB,CAAC,IAAI,UAAU,CAAC,oBAAoB,CAAC,IAAI,UAAU,CAAC,kBAAkB,CAAC,CAAC;QACtG,UAAU,CAAC,iBAAiB,CAAC;QAC7B,CAAC,UAAU,CAAC,YAAY,CAAC,IAAI,UAAU,CAAC,gBAAgB,CAAC,CAAC,CAC3D,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAuB,EAAE,OAAgB;IAC1E,MAAM,GAAG;QACP,OAAO,EAAE,OAAO,IAAI,OAAO,CAAC,gBAAgB,CAAC,IAAI,gBAAgB;QACjE,IAAI;KACL,CAAC;AACJ,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,mBAAmB;IACjC,MAAM,GAAG,IAAI,CAAC;AAChB,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,SAAS;IACvB,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,UAAU,EAAE,CAAC;IACf,CAAC;IACD,OAAO,MAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Binary downloads (`/documents/{id}`): streaming read under a byte cap
|
|
3
|
+
* (issue #235), byte-level progress, and the two guards a document download
|
|
4
|
+
* needs that a JSON request does not — the application-shell detection and
|
|
5
|
+
* the `Content-Disposition` filename.
|
|
6
|
+
*/
|
|
7
|
+
import type { ProgressReporter } from "../progress.js";
|
|
8
|
+
/**
|
|
9
|
+
* Parse the filename out of a `Content-Disposition` header. Handles the
|
|
10
|
+
* common `filename="…"`/`filename=…` forms and the RFC 5987
|
|
11
|
+
* `filename*=UTF-8''…` form. Returns undefined when absent. Exported for
|
|
12
|
+
* unit testing.
|
|
13
|
+
*/
|
|
14
|
+
export declare function parseContentDispositionFilename(header: string | null): string | undefined;
|
|
15
|
+
/**
|
|
16
|
+
* BoondManager writes the plain `filename="…"` form with UTF-8 bytes (often
|
|
17
|
+
* NFD: `e` + combining acute), and `fetch` decodes header bytes as Latin-1 as
|
|
18
|
+
* the spec says — so "Frédéric" arrives as "FreÌdeÌric" (observed live on
|
|
19
|
+
* 2026-09-26, issue #311). Re-decode those bytes as UTF-8 when that yields a
|
|
20
|
+
* clean string, and normalise to NFC so the name compares like a normal one.
|
|
21
|
+
*/
|
|
22
|
+
export declare function repairUtf8Filename(name: string): string;
|
|
23
|
+
export interface DownloadedDocument {
|
|
24
|
+
data: Buffer;
|
|
25
|
+
contentType: string;
|
|
26
|
+
filename?: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Thrown by `apiDownload` when the payload exceeds `maxBytes` — before the
|
|
30
|
+
* body is read when the response announces its size, otherwise the moment the
|
|
31
|
+
* running total crosses the cap (the stream is cancelled, nothing more is
|
|
32
|
+
* buffered). `bytes` is the announced size in the first case and a lower
|
|
33
|
+
* bound (bytes received so far) in the second; `announced` tells which.
|
|
34
|
+
*/
|
|
35
|
+
export declare class DownloadTooLargeError extends Error {
|
|
36
|
+
readonly bytes: number;
|
|
37
|
+
readonly maxBytes: number;
|
|
38
|
+
readonly announced: boolean;
|
|
39
|
+
constructor(path: string, bytes: number, maxBytes: number, announced: boolean);
|
|
40
|
+
}
|
|
41
|
+
/** Options for `apiDownload`. */
|
|
42
|
+
export interface DownloadOptions {
|
|
43
|
+
/** Refuse payloads larger than this many bytes (see `DownloadTooLargeError`). */
|
|
44
|
+
maxBytes?: number;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Download a binary payload (documents, justificatifs…) from the BoondManager
|
|
48
|
+
* API. Same pipeline as `apiRequest` (`send()`: auth, path guard, rate limit,
|
|
49
|
+
* timeout, retry policy for a GET), but the body is streamed raw instead of
|
|
50
|
+
* being parsed as JSON:API.
|
|
51
|
+
*
|
|
52
|
+
* `onProgress` reports bytes received when the client asked for progress and
|
|
53
|
+
* the response carries a `Content-Length`; otherwise nothing is emitted.
|
|
54
|
+
*
|
|
55
|
+
* `options.maxBytes` bounds memory: a `Content-Length` above it is refused
|
|
56
|
+
* before a single body byte is read, and a body without one is cancelled the
|
|
57
|
+
* moment it crosses the cap (`DownloadTooLargeError` either way).
|
|
58
|
+
*/
|
|
59
|
+
export declare function apiDownload(path: string, onProgress?: ProgressReporter, options?: DownloadOptions): Promise<DownloadedDocument>;
|
|
60
|
+
//# sourceMappingURL=download.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"download.d.ts","sourceRoot":"","sources":["../../../src/services/http/download.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAGvD;;;;;GAKG;AACH,wBAAgB,+BAA+B,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,SAAS,CAazF;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKvD;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAUD;;;;;;GAMG;AACH,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;gBAChB,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO;CAS9E;AAED,iCAAiC;AACjC,MAAM,WAAW,eAAe;IAC9B,iFAAiF;IACjF,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AA4DD;;;;;;;;;;;;GAYG;AACH,wBAAsB,WAAW,CAC/B,IAAI,EAAE,MAAM,EACZ,UAAU,CAAC,EAAE,gBAAgB,EAC7B,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,kBAAkB,CAAC,CAoC7B"}
|