@wootsup/mcp 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -5
- package/dist/catalog/build-catalog.d.ts +31 -0
- package/dist/catalog/build-catalog.js +68 -0
- package/dist/catalog/build-catalog.js.map +1 -0
- package/dist/index.js +37 -5
- package/dist/index.js.map +1 -1
- package/dist/modules/apimapper/auto-layout.d.ts +21 -0
- package/dist/modules/apimapper/auto-layout.js +54 -0
- package/dist/modules/apimapper/auto-layout.js.map +1 -0
- package/dist/modules/apimapper/client.d.ts +54 -4
- package/dist/modules/apimapper/client.js +145 -14
- package/dist/modules/apimapper/client.js.map +1 -1
- package/dist/modules/apimapper/connections-format.d.ts +31 -1
- package/dist/modules/apimapper/connections-format.js +97 -5
- package/dist/modules/apimapper/connections-format.js.map +1 -1
- package/dist/modules/apimapper/connections.d.ts +9 -7
- package/dist/modules/apimapper/connections.js +225 -58
- package/dist/modules/apimapper/connections.js.map +1 -1
- package/dist/modules/apimapper/credentials.js +86 -14
- package/dist/modules/apimapper/credentials.js.map +1 -1
- package/dist/modules/apimapper/elicitation.d.ts +29 -0
- package/dist/modules/apimapper/elicitation.js +62 -0
- package/dist/modules/apimapper/elicitation.js.map +1 -1
- package/dist/modules/apimapper/example-extract.d.ts +13 -0
- package/dist/modules/apimapper/example-extract.js +111 -0
- package/dist/modules/apimapper/example-extract.js.map +1 -0
- package/dist/modules/apimapper/filter-operators.d.ts +24 -0
- package/dist/modules/apimapper/filter-operators.js +103 -0
- package/dist/modules/apimapper/filter-operators.js.map +1 -0
- package/dist/modules/apimapper/flows-format.js +92 -22
- package/dist/modules/apimapper/flows-format.js.map +1 -1
- package/dist/modules/apimapper/flows.d.ts +8 -7
- package/dist/modules/apimapper/flows.js +216 -44
- package/dist/modules/apimapper/flows.js.map +1 -1
- package/dist/modules/apimapper/gateway/advanced-read-tool.d.ts +9 -0
- package/dist/modules/apimapper/gateway/advanced-read-tool.js +172 -0
- package/dist/modules/apimapper/gateway/advanced-read-tool.js.map +1 -0
- package/dist/modules/apimapper/gateway/advanced-tool.js +39 -130
- package/dist/modules/apimapper/gateway/advanced-tool.js.map +1 -1
- package/dist/modules/apimapper/gateway/collect-module-tools.d.ts +17 -0
- package/dist/modules/apimapper/gateway/collect-module-tools.js +44 -0
- package/dist/modules/apimapper/gateway/collect-module-tools.js.map +1 -0
- package/dist/modules/apimapper/gateway/essentials.d.ts +1 -1
- package/dist/modules/apimapper/gateway/essentials.js +19 -7
- package/dist/modules/apimapper/gateway/essentials.js.map +1 -1
- package/dist/modules/apimapper/gateway/gateway-shared.d.ts +21 -0
- package/dist/modules/apimapper/gateway/gateway-shared.js +124 -0
- package/dist/modules/apimapper/gateway/gateway-shared.js.map +1 -0
- package/dist/modules/apimapper/gateway/test-support.d.ts +1 -17
- package/dist/modules/apimapper/gateway/test-support.js +4 -33
- package/dist/modules/apimapper/gateway/test-support.js.map +1 -1
- package/dist/modules/apimapper/get-skill-cores.d.ts +4 -0
- package/dist/modules/apimapper/get-skill-cores.js +220 -0
- package/dist/modules/apimapper/get-skill-cores.js.map +1 -0
- package/dist/modules/apimapper/get-skill.d.ts +1 -1
- package/dist/modules/apimapper/get-skill.js +30 -3
- package/dist/modules/apimapper/get-skill.js.map +1 -1
- package/dist/modules/apimapper/graph-builder.d.ts +85 -2
- package/dist/modules/apimapper/graph-builder.js +151 -15
- package/dist/modules/apimapper/graph-builder.js.map +1 -1
- package/dist/modules/apimapper/graph.js +115 -15
- package/dist/modules/apimapper/graph.js.map +1 -1
- package/dist/modules/apimapper/index.js +25 -13
- package/dist/modules/apimapper/index.js.map +1 -1
- package/dist/modules/apimapper/jmespath-test.d.ts +4 -0
- package/dist/modules/apimapper/jmespath-test.js +152 -0
- package/dist/modules/apimapper/jmespath-test.js.map +1 -0
- package/dist/modules/apimapper/library.js +131 -8
- package/dist/modules/apimapper/library.js.map +1 -1
- package/dist/modules/apimapper/list-footer.d.ts +27 -0
- package/dist/modules/apimapper/list-footer.js +57 -0
- package/dist/modules/apimapper/list-footer.js.map +1 -0
- package/dist/modules/apimapper/local-sources.js +88 -31
- package/dist/modules/apimapper/local-sources.js.map +1 -1
- package/dist/modules/apimapper/mcp-client-identity.d.ts +32 -0
- package/dist/modules/apimapper/mcp-client-identity.js +70 -0
- package/dist/modules/apimapper/mcp-client-identity.js.map +1 -0
- package/dist/modules/apimapper/merge-constants.d.ts +6 -0
- package/dist/modules/apimapper/merge-constants.js +26 -0
- package/dist/modules/apimapper/merge-constants.js.map +1 -0
- package/dist/modules/apimapper/node-schema.d.ts +52 -2
- package/dist/modules/apimapper/node-schema.js +95 -4
- package/dist/modules/apimapper/node-schema.js.map +1 -1
- package/dist/modules/apimapper/onboarding.d.ts +29 -0
- package/dist/modules/apimapper/onboarding.js +117 -9
- package/dist/modules/apimapper/onboarding.js.map +1 -1
- package/dist/modules/apimapper/read-cache.d.ts +16 -3
- package/dist/modules/apimapper/read-cache.js +59 -4
- package/dist/modules/apimapper/read-cache.js.map +1 -1
- package/dist/modules/apimapper/render/index.js +26 -5
- package/dist/modules/apimapper/render/index.js.map +1 -1
- package/dist/modules/apimapper/resource-id.d.ts +13 -0
- package/dist/modules/apimapper/resource-id.js +69 -0
- package/dist/modules/apimapper/resource-id.js.map +1 -0
- package/dist/modules/apimapper/tool-result.d.ts +20 -0
- package/dist/modules/apimapper/tool-result.js +67 -5
- package/dist/modules/apimapper/tool-result.js.map +1 -1
- package/dist/modules/apimapper/toolslist-size.d.ts +10 -10
- package/dist/modules/apimapper/toolslist-size.js +29 -18
- package/dist/modules/apimapper/toolslist-size.js.map +1 -1
- package/dist/modules/apimapper/types.d.ts +13 -0
- package/dist/modules/apimapper/types.js +1 -1
- package/dist/modules/apimapper/types.js.map +1 -1
- package/dist/modules/apimapper/whitelist-drift.js +16 -1
- package/dist/modules/apimapper/whitelist-drift.js.map +1 -1
- package/dist/modules/apimapper/workflows.js +221 -32
- package/dist/modules/apimapper/workflows.js.map +1 -1
- package/dist/modules/apimapper/yootheme-binding.js +103 -22
- package/dist/modules/apimapper/yootheme-binding.js.map +1 -1
- package/dist/platform/index.js +7 -0
- package/dist/platform/index.js.map +1 -1
- package/dist/proxy/bridge.d.ts +35 -0
- package/dist/proxy/bridge.js +129 -0
- package/dist/proxy/bridge.js.map +1 -0
- package/dist/proxy/mode.d.ts +9 -0
- package/dist/proxy/mode.js +20 -0
- package/dist/proxy/mode.js.map +1 -0
- package/dist/setup/probe-auth.d.ts +51 -0
- package/dist/setup/probe-auth.js +141 -0
- package/dist/setup/probe-auth.js.map +1 -0
- package/dist/setup-cli.d.ts +9 -0
- package/dist/setup-cli.js +34 -0
- package/dist/setup-cli.js.map +1 -1
- package/dist/sites/loader.d.ts +7 -0
- package/dist/sites/loader.js +16 -1
- package/dist/sites/loader.js.map +1 -1
- package/dist/skill-instructions.d.ts +14 -1
- package/dist/skill-instructions.js +30 -6
- package/dist/skill-instructions.js.map +1 -1
- package/manifest.json +2 -2
- package/package.json +3 -2
- package/skills/apimapper/SKILL.md +78 -3
- package/skills/apimapper/reference/dynamize-existing-layout.md +158 -0
- package/skills/apimapper/reference/jmespath-cookbook.md +241 -0
- package/skills/apimapper/reference/jmespath-pitfalls.md +81 -0
- package/skills/apimapper/reference/library-template-discovery.md +1 -1
- package/skills/apimapper/reference/merge-two-sources-on-key.md +117 -12
- package/skills/apimapper/reference/oauth.md +143 -52
- package/skills/apimapper/reference/troubleshooting.md +2 -2
- package/skills/apimapper/reference/yootheme-source-to-builder-handoff.md +348 -0
- package/skills/apimapper/reference/yootheme.md +75 -44
|
@@ -10,7 +10,7 @@ allowed-tools:
|
|
|
10
10
|
|
|
11
11
|
# API Mapper
|
|
12
12
|
|
|
13
|
-
Bridge any REST API into YOOtheme via a saved flow (Source → Filter → Transform → Output). One server, one Bearer key,
|
|
13
|
+
Bridge any REST API into YOOtheme via a saved flow (Source → Filter → Transform → Output). One server, one Bearer key, 20-tool surface; 60 more behind `apimapper_advanced`; 80 total.
|
|
14
14
|
|
|
15
15
|
## Quickstart
|
|
16
16
|
|
|
@@ -44,7 +44,7 @@ detection, and a YOOtheme source schema baked in.
|
|
|
44
44
|
|
|
45
45
|
```
|
|
46
46
|
apimapper_library_featured() # the top featured templates
|
|
47
|
-
apimapper_library_list({
|
|
47
|
+
apimapper_library_list({ search: 'sheets' }) # search for any API by name (param is `search`, 3+ chars)
|
|
48
48
|
apimapper_credential_list({}) # ALWAYS check credentials BEFORE activating an auth-protected template
|
|
49
49
|
apimapper_library_activate({ id: '<template-id>', credential_id: '<cred-id>' }) # the canonical activation path
|
|
50
50
|
```
|
|
@@ -68,7 +68,7 @@ Tasks**, and more. Library activations come with:
|
|
|
68
68
|
|
|
69
69
|
## Step 2: Custom connection_create — ONLY when no template fits
|
|
70
70
|
|
|
71
|
-
If `library_list({
|
|
71
|
+
If `library_list({ search: '<api-name>' })` returns no match, fall back to
|
|
72
72
|
`apimapper_connection_create` for niche or unknown APIs.
|
|
73
73
|
|
|
74
74
|
**Server-enforced (not just prose).** A custom `connection_create` whose
|
|
@@ -94,6 +94,81 @@ the audited override. Always prefer activation.
|
|
|
94
94
|
|
|
95
95
|
For topic docs: `apimapper_get_skill topic="..."` or read `skill://apimapper/<topic>`.
|
|
96
96
|
|
|
97
|
+
## Dynamize an existing designed layout ("keep the design exactly as it is")
|
|
98
|
+
|
|
99
|
+
When the customer points at an EXISTING hand-designed section/page and wants it
|
|
100
|
+
fed from your flow — *"bind these fields onto my section"*, *"the design must
|
|
101
|
+
not change"*, *"use the layout from my X page"* — there is exactly one tool.
|
|
102
|
+
|
|
103
|
+
> **Iron Law:** Dynamize an existing design ⇒ `yootheme_builder_page_dynamize`.
|
|
104
|
+
> NEVER hand-edit layout JSON. NEVER `element_add` a "rebuild".
|
|
105
|
+
|
|
106
|
+
The tool copies the reference subtree byte-identically server-side, swaps only
|
|
107
|
+
the names you map, inserts nothing, rebuilds nothing, and publishes. You supply
|
|
108
|
+
language-level inputs only.
|
|
109
|
+
|
|
110
|
+
### The 3-step procedure
|
|
111
|
+
|
|
112
|
+
1. **FIND the section** — `yootheme_builder_template_summary` → `named_sections`.
|
|
113
|
+
The customer's "X page" is often a NAMED SECTION inside another article, not
|
|
114
|
+
its own template. Copy the customer's NAMED section — never a category
|
|
115
|
+
template or a sibling mobile/card variant.
|
|
116
|
+
2. **READ the flow** — `apimapper_yootheme_binding_for_flow({ flow_id })` →
|
|
117
|
+
the source name (`apiMapperFlow<Id>List`) + the flat field list.
|
|
118
|
+
3. **DYNAMIZE — ONE call:**
|
|
119
|
+
```jsonc
|
|
120
|
+
yootheme_builder_page_dynamize({
|
|
121
|
+
template_id, section_path,
|
|
122
|
+
source_name: "apiMapperFlowFcMatchesList", // step 2
|
|
123
|
+
leaf_map: {
|
|
124
|
+
"field.home_team.crest.imagefile": "home_crest", // nested original → flat flow field
|
|
125
|
+
"field.home_team.name": "home_name",
|
|
126
|
+
"field.away_team.name": "away_name",
|
|
127
|
+
"field.match_date": "match_time"
|
|
128
|
+
},
|
|
129
|
+
publish: true
|
|
130
|
+
})
|
|
131
|
+
```
|
|
132
|
+
FC-Greenfield: the flow emits FLAT records; the original binds NESTED
|
|
133
|
+
relational leaves. That mismatch is NOT a reason to rebuild — `leaf_map` just
|
|
134
|
+
renames leaves. Unmapped leaves are KEPT. Check `summary.unmatched_map_keys`
|
|
135
|
+
in the response.
|
|
136
|
+
|
|
137
|
+
Full recipe (read before executing): `apimapper_get_skill topic="dynamize-existing-layout"`.
|
|
138
|
+
|
|
139
|
+
### Rationalization table
|
|
140
|
+
|
|
141
|
+
| Excuse | Reality |
|
|
142
|
+
|--------|---------|
|
|
143
|
+
| "My flow is flat but the design binds nested fields — I'll rebuild it" (R1) | Structure + every tuned prop transfer verbatim; `leaf_map` only renames leaves. Rebuilt-by-hand shipped 213px rows vs the 90px original. Copy, don't rebuild. |
|
|
144
|
+
| "I'll add a column/slot for the league position" (R3) | INSERT NOTHING. Extra data goes INTO an existing leaf (compose `"AFC Richmond (3rd)"` in the flow). An inserted column collapsed sibling cells 213px→55px; names wrapped, crests overlapped. |
|
|
145
|
+
| "I'll copy whichever row variant I find first" (R4a) | A section holds a desktop single-line row AND a mobile stacked card. Copying the card ships a 233px two-tier block. Copy the variant VISIBLE at the customer's desktop viewport (read `visibility` props). |
|
|
146
|
+
| "This text field is close enough for the image slot" (R4b) | Binding a TEXT field (competition NAME) to an IMAGE prop renders a broken 0x0 image. Map image leaves to image/path fields only. Never invent an asset path — a guessed path 404s silently. |
|
|
147
|
+
| "I'll copy from the category template, it looks the same" (R5) | The category template is NOT the customer's section — copying it shipped a 2-column masonry with missing date/matchday and 40px crests. Copy ONLY the customer's NAMED section from `template_summary`. |
|
|
148
|
+
|
|
149
|
+
### Red flags — STOP
|
|
150
|
+
|
|
151
|
+
- About to call `element_add` / `element_clone` while dynamizing → STOP.
|
|
152
|
+
- About to write or hand-patch layout JSON → STOP, use the one call.
|
|
153
|
+
- About to bind a TEXT field to an IMAGE prop → STOP, wrong field type.
|
|
154
|
+
- About to invent an asset/image path you never read → STOP, it will 404.
|
|
155
|
+
- About to copy a category template or mobile-card variant → STOP, copy the customer's named desktop section.
|
|
156
|
+
|
|
157
|
+
All of these mean: one `yootheme_builder_page_dynamize` call against the right section.
|
|
158
|
+
|
|
159
|
+
### Map COMPLETELY, then self-check
|
|
160
|
+
|
|
161
|
+
Map EVERY bound leaf the section uses (incl. secondary slots like the TIME
|
|
162
|
+
filter on a date leaf — a date-only field renders '0:00 AM'). After the call,
|
|
163
|
+
read `summary`: `unmatched_map_keys` = your typos; `kept_unresolvable` = leaves
|
|
164
|
+
that WILL render empty — extend `leaf_map` and re-run until both are empty.
|
|
165
|
+
|
|
166
|
+
### Before you declare success
|
|
167
|
+
|
|
168
|
+
Render the published URL and visually compare ONE row against the original at
|
|
169
|
+
the customer's viewport. Same row height, no new wraps, images same size, same
|
|
170
|
+
highlight count. "Similar but looser" = you rebuilt instead of copied — fail.
|
|
171
|
+
|
|
97
172
|
## Common Pitfalls
|
|
98
173
|
|
|
99
174
|
- **Source not in YOOtheme Builder** → flow is *saved* but not *published*. Click Publish. Published name (not "API Mapper") appears in YOOtheme Source dropdown.
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# Dynamize an existing designed layout (keep the design EXACTLY as it is)
|
|
2
|
+
|
|
3
|
+
Customer says: *"bind these fields onto my existing section"*, *"keep the design
|
|
4
|
+
exactly as it is"*, *"use the layout from my X page"*, *"the design must not
|
|
5
|
+
change"*. This topic is THE recipe for that job. It exists because two cold
|
|
6
|
+
agents failed it the same way before it was written: one **rebuilt** the design
|
|
7
|
+
element-by-element, one **inserted** an extra slot into the copied layout — both
|
|
8
|
+
shipped visually broken pages that a human caught instantly.
|
|
9
|
+
|
|
10
|
+
## The one rule
|
|
11
|
+
|
|
12
|
+
**COPY the reference layout subtree byte-identically. Change ONLY leaf field
|
|
13
|
+
names. Insert nothing. Rebuild nothing.**
|
|
14
|
+
|
|
15
|
+
A designed YOOtheme section carries dozens of tuned props (`title_grid_width`,
|
|
16
|
+
`image_align`, grid breakpoints, column `layout` strings) you cannot guess. Any
|
|
17
|
+
hand-built "equivalent" renders visibly different — measured failures: 213px
|
|
18
|
+
row height vs the original's 90px; one inserted column collapsed sibling cells
|
|
19
|
+
from 213px to 55px so names wrapped and images overlapped text.
|
|
20
|
+
|
|
21
|
+
## Pick the RIGHT variant before copying (F99)
|
|
22
|
+
|
|
23
|
+
A designed section often contains MULTIPLE sibling variants of the same row —
|
|
24
|
+
typically a compact single-line DESKTOP row AND a taller stacked CARD for
|
|
25
|
+
mobile, switched via visibility props (`visibility: 'hidden@m'` etc.). Copying
|
|
26
|
+
the stacked card produces a 233px two-tier block where the customer expects
|
|
27
|
+
the 90px single-line row (measured failure). Before copying:
|
|
28
|
+
|
|
29
|
+
1. List the candidate subtrees and read their `visibility` props.
|
|
30
|
+
2. Copy the variant that is VISIBLE at the customer's desktop viewport —
|
|
31
|
+
usually the one hidden on small screens, not the one hidden on `@m`+.
|
|
32
|
+
3. If unsure, copy ALL sibling variants together with their visibility props
|
|
33
|
+
intact (they are part of the design) — never just one of them.
|
|
34
|
+
|
|
35
|
+
And when a leaf binds an IMAGE, map it to an image/path field — binding a TEXT
|
|
36
|
+
field (e.g. a competition NAME) to an image prop renders a broken 0x0 image.
|
|
37
|
+
|
|
38
|
+
## The loop — TWO discovery steps, then ONE call
|
|
39
|
+
|
|
40
|
+
Steps 1-2 stay (you must tell the tool WHICH section and WHICH flow). Steps 3-5
|
|
41
|
+
of the old hand-transform loop collapse into a **single
|
|
42
|
+
`yootheme_builder_page_dynamize` call** — the server runs the deterministic
|
|
43
|
+
copy-swap-write-publish itself. You supply only the language-level inputs (which
|
|
44
|
+
section, which flow, and the leaf→field name map). This is the *hybrid
|
|
45
|
+
execution* path: the recipe is now CODE, not a 100-call hand-edit.
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
1. FIND yootheme_builder_template_summary → named_sections
|
|
49
|
+
(the customer's "X page" is often a NAMED SECTION inside another
|
|
50
|
+
article, not its own template; this is where the design lives)
|
|
51
|
+
2. READ apimapper_yootheme_binding_for_flow({ flow_id }) → the source name
|
|
52
|
+
(source_name_list = apiMapperFlow<Id>List) + the flat field list you
|
|
53
|
+
map leaves onto. (No page_get_layout hand-copy needed — the tool
|
|
54
|
+
reads the layout itself.)
|
|
55
|
+
3. DYNAMIZE — ONE call:
|
|
56
|
+
yootheme_builder_page_dynamize({
|
|
57
|
+
template_id, section_path,
|
|
58
|
+
source_name: "apiMapperFlow<Id>List",
|
|
59
|
+
leaf_map: { "<original leaf field path>": "<your flat flow field>", … },
|
|
60
|
+
publish: true
|
|
61
|
+
})
|
|
62
|
+
The server copies the section subtree byte-identically, swaps the
|
|
63
|
+
iterating query name + every leaf field name from leaf_map, inserts
|
|
64
|
+
nothing, rebuilds nothing, and publishes. One call replaces the old
|
|
65
|
+
SWAP+WRITE+(manual)PUBLISH steps.
|
|
66
|
+
4. VERIFY render the public URL and compare ONE row against the original at
|
|
67
|
+
the customer's viewport — wrap/overlap/missing icons are failures
|
|
68
|
+
even when the data is right.
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Worked example — the FC-Greenfield case
|
|
72
|
+
|
|
73
|
+
The customer hand-designed a "Matches" section: each row is a single-line panel
|
|
74
|
+
with a home crest, the two team names, a league-position meta, a kickoff time,
|
|
75
|
+
and a "next match" highlight row. The flow (`flow_FcMatches`) emits a FLAT
|
|
76
|
+
record — `home_crest`, `home_name`, `away_name`, `match_time`, `next` — while the
|
|
77
|
+
original layout binds NESTED relational leaves
|
|
78
|
+
(`field.home_team.crest.imagefile`, `field.home_team.name`, …). That mismatch is
|
|
79
|
+
NOT a reason to rebuild; `leaf_map` just renames the leaves:
|
|
80
|
+
|
|
81
|
+
```jsonc
|
|
82
|
+
yootheme_builder_page_dynamize({
|
|
83
|
+
template_id: "tpl_home", // from template_summary
|
|
84
|
+
section_path: ".../section/matches", // the named section that holds the rows
|
|
85
|
+
source_name: "apiMapperFlowFcMatchesList", // binding_for_flow → source_name_list
|
|
86
|
+
leaf_map: {
|
|
87
|
+
// nested ORIGINAL leaf → your FLAT flow field
|
|
88
|
+
"field.home_team.crest.imagefile": "home_crest",
|
|
89
|
+
"field.home_team.name": "home_name",
|
|
90
|
+
"field.away_team.name": "away_name",
|
|
91
|
+
"field.match_date": "match_time",
|
|
92
|
+
"field.is_next": "next" // string '' / 'next' → highlight via _condition '!!'
|
|
93
|
+
},
|
|
94
|
+
publish: true
|
|
95
|
+
})
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
The tool keeps the `#parent` repeater form and every tuned prop
|
|
99
|
+
(`title_grid_width`, `image_align`, grid breakpoints, column `layout` strings)
|
|
100
|
+
byte-identical; only the names inside `leaf_map` change. A leaf you have no flow
|
|
101
|
+
field for is simply left OUT of `leaf_map` — the tool keeps its original binding
|
|
102
|
+
(default KEEP). Per-row visibility (the one red "next" row) rides on the `next`
|
|
103
|
+
string field via the copied `_condition` with `filters.condition: '!!'`.
|
|
104
|
+
|
|
105
|
+
> **When `page_dynamize` is not available** (older yt-builder host, or you need a
|
|
106
|
+
> shape it doesn't yet author): fall back to the hand-transform — read the
|
|
107
|
+
> subtree with `yootheme_builder_page_get_layout`, apply the swaps in the JSON
|
|
108
|
+
> yourself, and write it with `yootheme_builder_pages_create({ layout })`. The
|
|
109
|
+
> `yootheme-source-to-builder-handoff` topic documents that `#parent`-JSON
|
|
110
|
+
> anatomy in full. The hard rules below are exactly what the tool guarantees by
|
|
111
|
+
> construction; if you hand-edit, you must honour them yourself.
|
|
112
|
+
|
|
113
|
+
## Hard rules (the tool guarantees these by construction)
|
|
114
|
+
|
|
115
|
+
`yootheme_builder_page_dynamize` enforces every rule in this section
|
|
116
|
+
automatically — it copies the subtree, swaps only the `leaf_map` names, inserts
|
|
117
|
+
nothing, and publishes. They are kept here as **background**: so you understand
|
|
118
|
+
WHAT a design-exact dynamization is, and so that if you ever hand-edit the layout
|
|
119
|
+
instead (the fallback above), you follow them yourself.
|
|
120
|
+
|
|
121
|
+
These rules were learned from real failures:
|
|
122
|
+
|
|
123
|
+
- **"My flow is FLAT but the original binds NESTED/relational fields" is NOT a
|
|
124
|
+
reason to rebuild.** Structure and props transfer verbatim; only leaf field
|
|
125
|
+
names change.
|
|
126
|
+
- **INSERT NOTHING.** Extra data (a league position, a status) goes INTO an
|
|
127
|
+
existing bound leaf — compose it in the flow (`"AFC Richmond (3rd)"` as the
|
|
128
|
+
name value), never as a new element/column.
|
|
129
|
+
- **Leaves you have no flow field for: default KEEP.** A static icon keeps its
|
|
130
|
+
original binding (or gets a flow constant). REMOVE a leaf only when its
|
|
131
|
+
TARGET cannot exist for the new data (a detail-link whose article doesn't
|
|
132
|
+
exist for API rows). Ask: "does this leaf's target still exist for my data?"
|
|
133
|
+
- **Never invent asset paths.** If you didn't read the path from the layout or
|
|
134
|
+
an API response, don't guess one — a guessed path 404s silently.
|
|
135
|
+
- **Per-row visibility (played vs upcoming vs highlight):** prefer emitting
|
|
136
|
+
string flags in the flow (`''`/`'next'`) and `_condition` with
|
|
137
|
+
`filters.condition: '!'`/`'!!'` in the copied JSON. If element-level tools
|
|
138
|
+
fight you, pre-filter in the FLOW (separate published lists per row group) —
|
|
139
|
+
that pattern passed a strict visual judge. The element-side bind/update tools
|
|
140
|
+
currently **cannot** author a filtered `_condition` (F95/F96/F97) — the
|
|
141
|
+
`yootheme-source-to-builder-handoff` topic's "Known tool limits when
|
|
142
|
+
authoring per-row visibility" section has the verified detail and the
|
|
143
|
+
pre-filtered-flows workaround.
|
|
144
|
+
- The flow data work (connections, join, transforms) is unchanged by this
|
|
145
|
+
topic — see `merge-two-sources-on-key` and `jmespath-cookbook`.
|
|
146
|
+
|
|
147
|
+
## Before you declare success — checklist
|
|
148
|
+
|
|
149
|
+
- [ ] Section located via `template_summary` → `named_sections` (it may live
|
|
150
|
+
inside another article, not its own template)
|
|
151
|
+
- [ ] Source name + flat fields read from `apimapper_yootheme_binding_for_flow`
|
|
152
|
+
- [ ] Dynamization done in ONE `yootheme_builder_page_dynamize` call (or, on the
|
|
153
|
+
hand-edit fallback, the layout JSON came from `page_get_layout` — never
|
|
154
|
+
hand-built — with zero elements added/removed without a documented reason)
|
|
155
|
+
- [ ] `leaf_map` covers every leaf you have a flow field for; unmapped leaves are
|
|
156
|
+
left KEPT (default), removed only with a "target cannot exist" reason
|
|
157
|
+
- [ ] Published + routed (anonymous visitors can load it)
|
|
158
|
+
- [ ] One row visually compared against the original at the target viewport
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
# JMESPath Cookbook
|
|
2
|
+
|
|
3
|
+
Copy-paste recipes for the Transform node. Every recipe below is verified
|
|
4
|
+
against the actual API Mapper JMESPath engine (same one the Transform pipeline
|
|
5
|
+
runs). For the trap list and date/number primitive reference, see
|
|
6
|
+
`jmespath-pitfalls`. For joining two sources, see `merge-two-sources-on-key`.
|
|
7
|
+
|
|
8
|
+
> **Iterate with `apimapper_jmespath_test`.** Before you wire an expression into
|
|
9
|
+
> a Transform node, dry-run it: `apimapper_jmespath_test({ expression, sample_rows })`
|
|
10
|
+
> returns `{ result, row_count }`, and a bad expression returns the SAME
|
|
11
|
+
> teaching hint (arithmetic, depth-split) you'd hit at flow time — but instantly,
|
|
12
|
+
> with no flow to clean up. Grab real rows with `apimapper_connection_data` first.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## If / else — `conditional()` (preferred) or the `(cond && X) || Y` ternary
|
|
17
|
+
|
|
18
|
+
JMESPath has no native `if/else` statement. API Mapper ships a `conditional()`
|
|
19
|
+
primitive AND the classic short-circuit ternary; pick whichever reads cleaner.
|
|
20
|
+
|
|
21
|
+
### `conditional(condition, then_value [, else_value])`
|
|
22
|
+
|
|
23
|
+
```jmespath
|
|
24
|
+
[*].{
|
|
25
|
+
name: name,
|
|
26
|
+
status: conditional(stock > `0`, 'in stock', 'sold out')
|
|
27
|
+
}
|
|
28
|
+
# {stock:5} → "in stock" {stock:0} → "sold out"
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Returns `then_value` when `condition` is truthy, `else_value` (default `null`
|
|
32
|
+
when omitted) when falsy. Truthiness mirrors the engine: `false`, `null`,
|
|
33
|
+
`''`, `[]`, `{}` are falsy; **everything else — including `0` — is truthy**.
|
|
34
|
+
|
|
35
|
+
It is flatter and more readable than the ternary for a simple two-way branch,
|
|
36
|
+
and it **side-steps the ternary's falsy-`X` trap**: `conditional(cond, '', Y)`
|
|
37
|
+
returns the empty string verbatim, whereas `(cond && '') || Y` would wrongly
|
|
38
|
+
fall through to `Y`. (It is not "free" against the depth cap — a function call
|
|
39
|
+
still costs +1 nesting level — but for the simple case it reads better.)
|
|
40
|
+
|
|
41
|
+
### Ternary — `(cond && X) || Y`
|
|
42
|
+
|
|
43
|
+
The short-circuit idiom: when `cond` is truthy the `&&` yields `X`; otherwise
|
|
44
|
+
the `||` falls through to `Y`.
|
|
45
|
+
|
|
46
|
+
```jmespath
|
|
47
|
+
[*].{
|
|
48
|
+
name: name,
|
|
49
|
+
status: (stock > `0` && 'in stock') || 'sold out'
|
|
50
|
+
}
|
|
51
|
+
# {stock:5} → "in stock" {stock:0} → "sold out"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**Gotcha:** if `X` itself can be falsy (e.g. the string `''`, `0`, `false`), the
|
|
55
|
+
`|| Y` branch fires even when `cond` was true. Keep `X` a non-empty constant,
|
|
56
|
+
use `conditional()` instead, or restructure. For a numeric comparison on a
|
|
57
|
+
string field, cast first: `(to_number(price) > \`100\` && 'premium') || 'standard'`.
|
|
58
|
+
You can chain ternaries for >2 branches (`(a && 'x') || (b && 'y') || 'z'`);
|
|
59
|
+
`conditional()` covers the two-way case.
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## String composition — `join`
|
|
64
|
+
|
|
65
|
+
`join(separator, array_of_strings)` concatenates. All elements must be strings —
|
|
66
|
+
cast numbers with `to_string()`.
|
|
67
|
+
|
|
68
|
+
```jmespath
|
|
69
|
+
# "A - 5"
|
|
70
|
+
[*].join(' - ', [name, to_string(stock)])
|
|
71
|
+
|
|
72
|
+
# Prefix a currency symbol onto a formatted number
|
|
73
|
+
[*].{price: join('', ['$', format_number(price)])}
|
|
74
|
+
# {price:"890000"} → "$890,000"
|
|
75
|
+
|
|
76
|
+
# Build a full address from parts
|
|
77
|
+
[*].{label: join(', ', [street, city, country])}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
To concatenate with no separator, pass `''` as the first arg.
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Empty / filled field as a condition (result-pattern)
|
|
85
|
+
|
|
86
|
+
To branch on whether a field is present-and-non-empty, reuse the ternary —
|
|
87
|
+
an empty string, `null`, or `[]` is falsy:
|
|
88
|
+
|
|
89
|
+
```jmespath
|
|
90
|
+
[*].{
|
|
91
|
+
name: name,
|
|
92
|
+
has_tags: (tags && 'yes') || 'no'
|
|
93
|
+
}
|
|
94
|
+
# {tags:["x"]} → "yes" {tags:[]} → "no" {missing} → "no"
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
To FILTER to only the filled rows:
|
|
98
|
+
|
|
99
|
+
```jmespath
|
|
100
|
+
[?image_url] # keeps rows whose image_url is non-empty
|
|
101
|
+
[?length(tags) > `0`] # keeps rows with at least one tag
|
|
102
|
+
[?contains(keys(@), 'price')] # keeps rows where the KEY exists (any value)
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
(See pitfall #8 in `jmespath-pitfalls` for empty-array vs missing-key.)
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Sorting — `sort_by`
|
|
110
|
+
|
|
111
|
+
`sort_by(array, &field)` sorts ascending by a field. Wrap in `reverse(...)` for
|
|
112
|
+
descending. The `&` makes an expression-reference (don't forget it).
|
|
113
|
+
|
|
114
|
+
```jmespath
|
|
115
|
+
# Ascending by price
|
|
116
|
+
sort_by([*], &price)
|
|
117
|
+
|
|
118
|
+
# Descending by date, then take the first 3
|
|
119
|
+
reverse(sort_by([*], &published_at))[:3]
|
|
120
|
+
|
|
121
|
+
# Sort, then project just the names
|
|
122
|
+
sort_by([*], &stock)[*].name
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
`sort_by` needs a comparable key. If the field is a numeric STRING, sorting is
|
|
126
|
+
lexical (`"100" < "20"`); normalise upstream or accept string order.
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Coordinate keys for a Map element
|
|
131
|
+
|
|
132
|
+
YOOtheme's Map element binds a `coordinates` array `[lat, lng]`. Build it inline:
|
|
133
|
+
|
|
134
|
+
```jmespath
|
|
135
|
+
[*].{
|
|
136
|
+
title: title,
|
|
137
|
+
coordinates: [lat, lng]
|
|
138
|
+
}
|
|
139
|
+
# → [{ title: "HQ", coordinates: [52.5, 13.4] }]
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
If lat/lng arrive as strings, cast: `coordinates: [to_number(lat), to_number(lng)]`.
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## Number formatting — `format_number`
|
|
147
|
+
|
|
148
|
+
`format_number(value [, decimals=0 [, decimal_sep='.' [, thousands_sep=',']]])`
|
|
149
|
+
groups thousands. Accepts numbers OR numeric strings; returns `null` on
|
|
150
|
+
non-numeric input.
|
|
151
|
+
|
|
152
|
+
```jmespath
|
|
153
|
+
# 890000 → "890,000"
|
|
154
|
+
[*].{name: name, price: format_number(price)}
|
|
155
|
+
|
|
156
|
+
# Two decimals: 1234.5 → "1,234.50"
|
|
157
|
+
format_number(`1234.5`, `2`)
|
|
158
|
+
|
|
159
|
+
# European grouping: 1234567.89 → "1.234.567,89"
|
|
160
|
+
format_number(price, `2`, ',', '.')
|
|
161
|
+
|
|
162
|
+
# Guard a column with stray non-numeric rows
|
|
163
|
+
[*].coalesce_empty(format_number(price), 'n/a')
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## Date primitives — weekday / time / date / window
|
|
169
|
+
|
|
170
|
+
JMESPath has no native date support; API Mapper ships four primitives. The
|
|
171
|
+
optional last arg is an IANA timezone (`'Europe/Berlin'`, `'America/New_York'`,
|
|
172
|
+
`'UTC'`); omit it to keep the offset already in the string. All return `null`
|
|
173
|
+
(or `false` for `time_in_window`) on malformed input.
|
|
174
|
+
|
|
175
|
+
```jmespath
|
|
176
|
+
# Calendly slot → Sheet-comparable weekday + local time
|
|
177
|
+
[*].{
|
|
178
|
+
day: date_weekday(start_time, 'Europe/Berlin'), # "Wednesday"
|
|
179
|
+
local_time: date_iso_to_time(start_time, 'Europe/Berlin'), # "09:30"
|
|
180
|
+
ymd: date_iso_to_date(start_time, 'Europe/Berlin') # "2026-06-03"
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
# Keep only slots inside business hours
|
|
184
|
+
[?time_in_window(date_iso_to_time(start_time, 'Europe/Berlin'), '09:00', '17:00')]
|
|
185
|
+
|
|
186
|
+
# Cross-midnight window (10pm..2am): from > to is supported
|
|
187
|
+
[?time_in_window(local_time, '22:00', '02:00')]
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
See `merge-two-sources-on-key` for using these to join a weekday-name source
|
|
191
|
+
against an ISO-datetime source.
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## Two-Transform split against the depth limit
|
|
196
|
+
|
|
197
|
+
Expression depth is capped at **10** levels of bracket / pipe nesting. A pipe
|
|
198
|
+
`|` resets the depth counter, so the universal fix for a "too deeply nested"
|
|
199
|
+
422 is to **split one Transform into two piped together** — either inside one
|
|
200
|
+
expression with a pipe, or as two separate Transform nodes wired in series.
|
|
201
|
+
|
|
202
|
+
```jmespath
|
|
203
|
+
# Too deep — slice + multi-level hash in one breath
|
|
204
|
+
[1:][*].{day: [0], hours: {open: [1], close: [2], extra: {note: [3]}}}
|
|
205
|
+
|
|
206
|
+
# Fixed — pipe resets, second stage starts fresh and shallow
|
|
207
|
+
[1:] | [*].{day: [0], hours: {open: [1], close: [2]}}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
If you hit the cap, also reconsider whether you need the nesting at all — flat
|
|
211
|
+
`[*].{a: x, b: y}` bucket shapes are almost always enough for a YOOtheme
|
|
212
|
+
binding. (Full walk-through: `jmespath-pitfalls` → "Depth-cap warning".)
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## GIBT-ES-NICHT — things JMESPath does NOT have (and the workaround)
|
|
217
|
+
|
|
218
|
+
Stop trying these — they raise a syntax error (now annotated with a hint) or
|
|
219
|
+
silently misbehave. Use the workaround.
|
|
220
|
+
|
|
221
|
+
| You want… | Does NOT exist | Workaround |
|
|
222
|
+
|-----------|----------------|------------|
|
|
223
|
+
| Arithmetic `a + b`, `a - b`, `a * b`, `a / b` | No arithmetic operators at all. `price + tax` is a **syntax error** (annotated *"JMESPath has no arithmetic operators"*). | Compute upstream (in the API, a spreadsheet, or an n8n step), or in a YOOtheme element. For display-only concatenation use `join('', [a, b])`. |
|
|
224
|
+
| Split a string `split(s, ',')` | No `split`. | Pre-split upstream, or if the API returns an array use it directly. For a CSV cell, request a structured endpoint instead. |
|
|
225
|
+
| Regex match / replace | No regex. | Use `contains(@, 'substr')`, `starts_with`, `ends_with` for membership. For replace, normalise the data upstream. |
|
|
226
|
+
| `if / else` / `switch` | No statement forms. | `conditional(cond, X, Y)` (flat, two-way) or the ternary `(cond && X) || Y` (above). Chain ternaries for >2 branches: `(a && 'x') || (b && 'y') || 'z'`. |
|
|
227
|
+
| Uppercase / lowercase | No case functions. | Normalise casing upstream (the source API or a Transform-feeding step). |
|
|
228
|
+
| Sum / count-with-condition | No `sum`/aggregate over a predicate. | `length([?cond])` counts matching rows. For a real sum, aggregate upstream. |
|
|
229
|
+
| Date math (`+ 1 day`) | No date arithmetic. | Use the date primitives for extraction/formatting/windowing only; do calendar math upstream. |
|
|
230
|
+
|
|
231
|
+
When in doubt, paste the expression into `apimapper_jmespath_test` — a failing
|
|
232
|
+
expression tells you exactly which of these you tripped over.
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## See also
|
|
237
|
+
|
|
238
|
+
- `jmespath-pitfalls` — the 9 traps + primitive reference + depth-cap detail.
|
|
239
|
+
- `merge-two-sources-on-key` — joining two sources, with date-primitive bridging.
|
|
240
|
+
- `conditional-style-multi-items` — per-row YOOtheme styles from a row field.
|
|
241
|
+
- `yootheme-source-to-builder-handoff` — where the empty/filled string fields you build here drive a theme layout's `_condition` (`'!'`/`'!!'`) in the `#parent` repeater form.
|
|
@@ -85,6 +85,87 @@ Cross-midnight windows (bars, support shifts):
|
|
|
85
85
|
[?time_in_window(local_time, '22:00', '02:00')] # 10pm..2am
|
|
86
86
|
```
|
|
87
87
|
|
|
88
|
+
## Number formatting — `format_number()`
|
|
89
|
+
|
|
90
|
+
JMESPath has no arithmetic and no number-format support, so a price field
|
|
91
|
+
arrives as raw digits and renders as `$890000` instead of `$890,000`. The
|
|
92
|
+
`format_number()` primitive (new in 2.0.13) groups thousands and fixes
|
|
93
|
+
decimals — the number-format analogue of the date primitives above.
|
|
94
|
+
|
|
95
|
+
| Signature | Returns |
|
|
96
|
+
|-----------|---------|
|
|
97
|
+
| `format_number(value [, decimals [, decimal_sep [, thousands_sep]]])` | grouped string, or `null` on non-numeric input |
|
|
98
|
+
|
|
99
|
+
- `value` — a number OR a numeric string (Sheets/REST often deliver `"1250000"`). Anything non-numeric (including `true`/arrays/empty string) → `null`.
|
|
100
|
+
- `decimals` — digits after the decimal separator (default `0`). Rounds half-up.
|
|
101
|
+
- `decimal_sep` — default `"."`.
|
|
102
|
+
- `thousands_sep` — default `","`.
|
|
103
|
+
|
|
104
|
+
```jmespath
|
|
105
|
+
# US dollars, no cents — the SunWest case
|
|
106
|
+
[*].{name: name, price_display: format_number(price)}
|
|
107
|
+
# 890000 → "890,000"
|
|
108
|
+
# 1450000 → "1,450,000"
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
```jmespath
|
|
112
|
+
# Two decimals
|
|
113
|
+
format_number(`1234.5`, `2`) # "1,234.50"
|
|
114
|
+
|
|
115
|
+
# European grouping (1.234.567,89)
|
|
116
|
+
format_number(price, `2`, ',', '.')
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Prefix the currency symbol in the same expression with `join`:
|
|
120
|
+
|
|
121
|
+
```jmespath
|
|
122
|
+
[*].{price: join('', ['$', format_number(price)])} # "$890,000"
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Because `format_number` returns `null` on bad input, wrap it with
|
|
126
|
+
`coalesce_empty` when a column may contain stray non-numeric rows:
|
|
127
|
+
|
|
128
|
+
```jmespath
|
|
129
|
+
coalesce_empty(format_number(price), 'n/a')
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
> Reminder: JMESPath still has **no arithmetic operators** (`+ - * /`). If you
|
|
133
|
+
> try `price + tax` the engine raises a syntax error now annotated with
|
|
134
|
+
> *"JMESPath has no arithmetic operators"*. Compute totals upstream, in a
|
|
135
|
+
> YOOtheme element, or concatenate strings with `join('', [a, b])`.
|
|
136
|
+
|
|
137
|
+
## Logic primitive — `conditional()`
|
|
138
|
+
|
|
139
|
+
JMESPath has no `if/else` statement. The classic workaround is the
|
|
140
|
+
short-circuit ternary `(cond && X) || Y`, but it has a sharp edge: when `X` is
|
|
141
|
+
itself falsy (`''`, `0`, `false`) the `|| Y` branch fires even though `cond` was
|
|
142
|
+
true. The `conditional()` primitive is a flat, readable if/else without that
|
|
143
|
+
trap.
|
|
144
|
+
|
|
145
|
+
| Signature | Returns |
|
|
146
|
+
|-----------|---------|
|
|
147
|
+
| `conditional(condition, then_value [, else_value])` | `then_value` when `condition` is truthy; `else_value` (default `null`) when falsy |
|
|
148
|
+
|
|
149
|
+
Truthiness mirrors the engine: `false`, `null`, `''` (empty string), `[]`
|
|
150
|
+
(empty array) and `{}` (empty object) are falsy — **everything else, including
|
|
151
|
+
`0`, is truthy**.
|
|
152
|
+
|
|
153
|
+
```jmespath
|
|
154
|
+
# per-row in-stock badge from a stock field
|
|
155
|
+
[*].{name: name, in_stock: conditional(stock > `0`, 'in stock', 'sold out')}
|
|
156
|
+
# {stock:5} → "in stock" {stock:0} → "sold out"
|
|
157
|
+
|
|
158
|
+
# returns a falsy then-value verbatim — no ternary fall-through
|
|
159
|
+
[*].{label: conditional(is_featured, '', 'standard')}
|
|
160
|
+
# is_featured truthy → "" (the ternary (is_featured && '') || 'standard' would
|
|
161
|
+
# have wrongly returned "standard")
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
`conditional()` is **flatter and more readable** than the ternary for a simple
|
|
165
|
+
two-way branch — but it does NOT dodge the depth cap: a function call still
|
|
166
|
+
costs `+1` nesting level. For >2 branches, chain ternaries
|
|
167
|
+
(`(a && 'x') || (b && 'y') || 'z'`); `conditional()` covers the two-way case.
|
|
168
|
+
|
|
88
169
|
## Depth-cap warning
|
|
89
170
|
|
|
90
171
|
JMESPath expression depth is capped at **10 levels** of bracket / pipe nesting. Above that, the engine returns `expression too deeply nested (N levels)`.
|
|
@@ -16,7 +16,7 @@ to debug from downstream tool calls.
|
|
|
16
16
|
|
|
17
17
|
```
|
|
18
18
|
apimapper_library_featured({}) # 1. the curated short-list
|
|
19
|
-
apimapper_library_list({
|
|
19
|
+
apimapper_library_list({ search: '<api-name>' }) # 2. search the full catalog by name (param is `search`, 3+ chars)
|
|
20
20
|
apimapper_library_connection_detail({ id: '<id>' }) # 3. read the template's full contract
|
|
21
21
|
apimapper_credential_list({}) # 4. find a matching credential (auth-protected templates)
|
|
22
22
|
apimapper_library_activate({ id, credential_id, extra_fields }) # 5. activate, fully configured
|