@energio/holded-mcp 1.3.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +33 -5
  2. package/dist/index.js +1 -1
  3. package/dist/schemas/common.d.ts +6 -12
  4. package/dist/schemas/common.d.ts.map +1 -1
  5. package/dist/schemas/common.js +7 -10
  6. package/dist/schemas/common.js.map +1 -1
  7. package/dist/schemas/crm/funnels.d.ts +1 -4
  8. package/dist/schemas/crm/funnels.d.ts.map +1 -1
  9. package/dist/schemas/crm/leads.d.ts +1 -4
  10. package/dist/schemas/crm/leads.d.ts.map +1 -1
  11. package/dist/schemas/invoicing/attachment-input.d.ts.map +1 -1
  12. package/dist/schemas/invoicing/attachment-input.js +0 -1
  13. package/dist/schemas/invoicing/attachment-input.js.map +1 -1
  14. package/dist/schemas/invoicing/documents.d.ts +2 -9
  15. package/dist/schemas/invoicing/documents.d.ts.map +1 -1
  16. package/dist/schemas/invoicing/documents.js +23 -3
  17. package/dist/schemas/invoicing/documents.js.map +1 -1
  18. package/dist/tools/crm/funnels.d.ts.map +1 -1
  19. package/dist/tools/crm/funnels.js +13 -1
  20. package/dist/tools/crm/funnels.js.map +1 -1
  21. package/dist/tools/crm/leads.d.ts.map +1 -1
  22. package/dist/tools/crm/leads.js +14 -1
  23. package/dist/tools/crm/leads.js.map +1 -1
  24. package/dist/tools/factory.d.ts +15 -0
  25. package/dist/tools/factory.d.ts.map +1 -1
  26. package/dist/tools/factory.js +19 -9
  27. package/dist/tools/factory.js.map +1 -1
  28. package/dist/tools/invoicing/contacts.d.ts.map +1 -1
  29. package/dist/tools/invoicing/contacts.js +2 -0
  30. package/dist/tools/invoicing/contacts.js.map +1 -1
  31. package/dist/tools/invoicing/documents.d.ts.map +1 -1
  32. package/dist/tools/invoicing/documents.js +52 -2
  33. package/dist/tools/invoicing/documents.js.map +1 -1
  34. package/dist/types.d.ts +11 -5
  35. package/dist/types.d.ts.map +1 -1
  36. package/dist/utils/custom-fields.d.ts +66 -0
  37. package/dist/utils/custom-fields.d.ts.map +1 -0
  38. package/dist/utils/custom-fields.js +133 -0
  39. package/dist/utils/custom-fields.js.map +1 -0
  40. package/package.json +1 -1
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Custom-fields map serialization and repair.
3
+ *
4
+ * Holded's `POST /documents/{docType}` and `POST|PUT /contacts[{id}]` endpoints
5
+ * decompose each `customFields` entry via their own `Object.entries` — every
6
+ * own-prop becomes a separate `{field: <key>, value: <val>}` row. The single
7
+ * reliable workaround is to send one own-prop per entry: `[{"src":"val"}]` →
8
+ * Holded unpacks to `[{field:"src", value:"val"}]`.
9
+ *
10
+ * Other endpoints behave normally and accept the documented `[{field, value}]`
11
+ * shape, or (for funnel PUT) store the array byte-for-byte.
12
+ *
13
+ * See `docs/superpowers/specs/2026-04-16-customfields-map-interface-design.md`
14
+ * and DRIFT entries DRIFT-INV-14 / DRIFT-INV-15.
15
+ *
16
+ * Per-resource variant lookup:
17
+ *
18
+ * | Resource | POST | PUT |
19
+ * |----------|----------------|----------------|
20
+ * | Document | map-per-entry | documented |
21
+ * | Contact | map-per-entry | map-per-entry |
22
+ * | Lead | documented | documented |
23
+ * | Funnel | (n/a) | map-per-entry |
24
+ */
25
+ /**
26
+ * Convert a caller-facing customFields map to the wire shape expected by a
27
+ * specific Holded endpoint variant. Returns `undefined` when the input is
28
+ * undefined or empty so callers can spread the result straight into a body
29
+ * without introducing a `customFields: []` noise field.
30
+ */
31
+ export function serialize(map, variant) {
32
+ if (!map)
33
+ return undefined;
34
+ const keys = Object.keys(map);
35
+ if (keys.length === 0)
36
+ return undefined;
37
+ if (variant === "documented") {
38
+ return keys.map((k) => ({ field: k, value: map[k] }));
39
+ }
40
+ // map-per-entry
41
+ return keys.map((k) => ({ [k]: map[k] }));
42
+ }
43
+ /**
44
+ * Parse any observed wire shape back into a caller-facing map. Idempotent
45
+ * on arrays of known shapes; returns `{}` for any non-array input (including
46
+ * an already-parsed map — callers must not re-parse).
47
+ *
48
+ * Priority order:
49
+ * 1. null/undefined/non-array → {}
50
+ * 2. empty array → {}
51
+ * 3. whole-array mangled pairs → collapse each pair into {K: V}
52
+ * 4. entry-by-entry:
53
+ * {field, value} exact → {K: V}
54
+ * single own-prop {K: V} → {K: V}
55
+ * otherwise → skip
56
+ *
57
+ * Rule 3 runs before rule 4: a legitimate pair
58
+ * [{field:"field", value:X}, {field:"value", value:Y}]
59
+ * is indistinguishable from the mangled output of `[{field:X, value:Y}]`;
60
+ * the mangled interpretation wins. Custom fields literally named "field"
61
+ * or "value" are accepted as an unavoidable heuristic cost.
62
+ */
63
+ export function parse(raw) {
64
+ if (!Array.isArray(raw))
65
+ return {};
66
+ if (raw.length === 0)
67
+ return {};
68
+ // Rule 3: whole-array mangled pairs
69
+ if (raw.length % 2 === 0 && raw.every(isRowObject) && allMangledPairs(raw)) {
70
+ const out = {};
71
+ for (let i = 0; i < raw.length; i += 2) {
72
+ const key = String(raw[i].value);
73
+ const val = String(raw[i + 1].value);
74
+ out[key] = val;
75
+ }
76
+ return out;
77
+ }
78
+ // Rule 4: entry-by-entry
79
+ const out = {};
80
+ for (const entry of raw) {
81
+ if (!isRowObject(entry))
82
+ continue;
83
+ const keys = Object.keys(entry);
84
+ if (keys.length === 2 && "field" in entry && "value" in entry) {
85
+ out[String(entry.field)] = String(entry.value);
86
+ }
87
+ else if (keys.length === 1) {
88
+ const [k] = keys;
89
+ const v = entry[k];
90
+ if (typeof v === "string") {
91
+ out[k] = v;
92
+ }
93
+ }
94
+ // else: skip silently
95
+ }
96
+ return out;
97
+ }
98
+ /**
99
+ * Mutate-in-place: if `item` is an object with a `customFields` property,
100
+ * overwrite it with `parse(item.customFields)`. No-op otherwise. Returns
101
+ * `item` for chaining.
102
+ *
103
+ * Call sites:
104
+ * - Direct handlers (documents): after `makeApiRequest`, call on each
105
+ * returned document.
106
+ * - Factory `responseTransform`: return `repairCustomFieldsInPlace(item)`.
107
+ */
108
+ export function repairCustomFieldsInPlace(item) {
109
+ if (item && typeof item === "object" && "customFields" in item) {
110
+ const target = item;
111
+ target.customFields = parse(target.customFields);
112
+ }
113
+ return item;
114
+ }
115
+ function isRowObject(v) {
116
+ return typeof v === "object" && v !== null && !Array.isArray(v);
117
+ }
118
+ function allMangledPairs(rows) {
119
+ for (let i = 0; i < rows.length; i += 2) {
120
+ const a = rows[i];
121
+ const b = rows[i + 1];
122
+ if (Object.keys(a).length !== 2 ||
123
+ a.field !== "field" ||
124
+ typeof a.value !== "string")
125
+ return false;
126
+ if (Object.keys(b).length !== 2 ||
127
+ b.field !== "value" ||
128
+ typeof b.value !== "string")
129
+ return false;
130
+ }
131
+ return true;
132
+ }
133
+ //# sourceMappingURL=custom-fields.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom-fields.js","sourceRoot":"","sources":["../../src/utils/custom-fields.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@energio/holded-mcp",
3
- "version": "1.3.0",
3
+ "version": "1.5.0",
4
4
  "description": "MCP server for Holded — invoicing, accounting, CRM, projects, and team",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",