@wootsup/mcp 0.1.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 +157 -83
- package/README.md +31 -27
- package/SECURITY.md +15 -6
- package/dist/auth/keychain.d.ts +27 -1
- package/dist/auth/keychain.js +48 -2
- package/dist/auth/keychain.js.map +1 -1
- 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/cli-hint.d.ts +22 -0
- package/dist/cli-hint.js +55 -0
- package/dist/cli-hint.js.map +1 -0
- package/dist/index.js +129 -22
- package/dist/index.js.map +1 -1
- package/dist/install-skill.js +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/cache.js +25 -17
- package/dist/modules/apimapper/cache.js.map +1 -1
- package/dist/modules/apimapper/client.d.ts +115 -4
- package/dist/modules/apimapper/client.js +699 -304
- 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 +449 -127
- package/dist/modules/apimapper/connections.js.map +1 -1
- package/dist/modules/apimapper/credential-sanitizer.d.ts +5 -0
- package/dist/modules/apimapper/credential-sanitizer.js +60 -1
- package/dist/modules/apimapper/credential-sanitizer.js.map +1 -1
- package/dist/modules/apimapper/credentials.js +105 -61
- package/dist/modules/apimapper/credentials.js.map +1 -1
- package/dist/modules/apimapper/diagnose.js +21 -2
- package/dist/modules/apimapper/diagnose.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 +275 -120
- 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 +66 -106
- 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 +21 -2
- 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 +74 -9
- 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 +152 -48
- package/dist/modules/apimapper/graph.js.map +1 -1
- package/dist/modules/apimapper/index.js +27 -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 +553 -88
- package/dist/modules/apimapper/library.js.map +1 -1
- package/dist/modules/apimapper/license.js +12 -36
- package/dist/modules/apimapper/license.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 +100 -57
- 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/misc.js +13 -27
- package/dist/modules/apimapper/misc.js.map +1 -1
- 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 +59 -1
- package/dist/modules/apimapper/onboarding.js +231 -28
- 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/schema.js +9 -18
- package/dist/modules/apimapper/schema.js.map +1 -1
- package/dist/modules/apimapper/settings.js +49 -52
- package/dist/modules/apimapper/settings.js.map +1 -1
- package/dist/modules/apimapper/sites-tools.d.ts +29 -0
- package/dist/modules/apimapper/sites-tools.js +165 -0
- package/dist/modules/apimapper/sites-tools.js.map +1 -0
- package/dist/modules/apimapper/tool-result.d.ts +66 -0
- package/dist/modules/apimapper/tool-result.js +125 -0
- package/dist/modules/apimapper/tool-result.js.map +1 -0
- package/dist/modules/apimapper/toolslist-size.d.ts +12 -11
- package/dist/modules/apimapper/toolslist-size.js +34 -21
- package/dist/modules/apimapper/toolslist-size.js.map +1 -1
- package/dist/modules/apimapper/types.d.ts +34 -0
- package/dist/modules/apimapper/types.js +1 -1
- package/dist/modules/apimapper/types.js.map +1 -1
- package/dist/modules/apimapper/whitelist-drift.d.ts +85 -0
- package/dist/modules/apimapper/whitelist-drift.js +375 -0
- package/dist/modules/apimapper/whitelist-drift.js.map +1 -0
- package/dist/modules/apimapper/workflows.js +302 -58
- package/dist/modules/apimapper/workflows.js.map +1 -1
- package/dist/modules/apimapper/yootheme-binding.d.ts +35 -0
- package/dist/modules/apimapper/yootheme-binding.js +267 -0
- package/dist/modules/apimapper/yootheme-binding.js.map +1 -0
- package/dist/platform/index.d.ts +56 -0
- package/dist/platform/index.js +158 -2
- 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/detect-clients.d.ts +40 -1
- package/dist/setup/detect-clients.js +148 -1
- package/dist/setup/detect-clients.js.map +1 -1
- 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/probe-handshake.js +40 -7
- package/dist/setup/probe-handshake.js.map +1 -1
- package/dist/setup/remove-config.d.ts +8 -0
- package/dist/setup/remove-config.js +145 -0
- package/dist/setup/remove-config.js.map +1 -0
- package/dist/setup/uninstall.d.ts +34 -0
- package/dist/setup/uninstall.js +147 -0
- package/dist/setup/uninstall.js.map +1 -0
- package/dist/setup-cli.d.ts +16 -0
- package/dist/setup-cli.js +63 -1
- package/dist/setup-cli.js.map +1 -1
- package/dist/sites/loader.d.ts +48 -0
- package/dist/sites/loader.js +134 -0
- package/dist/sites/loader.js.map +1 -0
- package/dist/sites/schema.d.ts +69 -0
- package/dist/sites/schema.js +71 -0
- package/dist/sites/schema.js.map +1 -0
- package/dist/sites/secret-resolver.d.ts +47 -0
- package/dist/sites/secret-resolver.js +150 -0
- package/dist/sites/secret-resolver.js.map +1 -0
- package/dist/skill-instructions.d.ts +14 -1
- package/dist/skill-instructions.js +35 -6
- package/dist/skill-instructions.js.map +1 -1
- package/dist/transports/stdio.js +4 -4
- package/dist/transports/stdio.js.map +1 -1
- package/dist/uninstall-skill.d.ts +27 -0
- package/dist/uninstall-skill.js +89 -0
- package/dist/uninstall-skill.js.map +1 -0
- package/docs/architecture.md +21 -21
- package/docs/customgraph-internal-migration.md +4 -4
- package/docs/security.md +2 -21
- package/docs/tools.md +40 -12
- package/manifest.json +77 -79
- package/package.json +69 -65
- package/skills/apimapper/SKILL.md +128 -7
- package/skills/apimapper/reference/conditional-style-multi-items.md +114 -0
- 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 +189 -0
- package/skills/apimapper/reference/joomla.md +1 -1
- package/skills/apimapper/reference/library-template-discovery.md +65 -0
- package/skills/apimapper/reference/merge-two-sources-on-key.md +204 -0
- package/skills/apimapper/reference/oauth.md +143 -52
- package/skills/apimapper/reference/troubleshooting.md +22 -2
- package/skills/apimapper/reference/yootheme-source-to-builder-handoff.md +348 -0
- package/skills/apimapper/reference/yootheme.md +75 -44
- package/dist/auth/oauth-provider.d.ts +0 -68
- package/dist/auth/oauth-provider.js +0 -232
- package/dist/auth/oauth-provider.js.map +0 -1
- package/dist/server-http.d.ts +0 -22
- package/dist/server-http.js +0 -159
- package/dist/server-http.js.map +0 -1
- package/dist/transports/http.d.ts +0 -29
- package/dist/transports/http.js +0 -267
- package/dist/transports/http.js.map +0 -1
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
# Handoff: API Mapper source → YOOtheme Builder element
|
|
2
|
+
|
|
3
|
+
This is the bridge between the two halves of the job: you have a **published API Mapper flow** (built and published on THIS server), and you want it to actually drive a YOOtheme Builder element (Grid / List / Slider / **Map**) on the page. The element-side work happens on the **yt-builder-mcp** server, through its `yootheme_builder_*` tools. This topic walks the exact call sequence and the three things that bite cold agents: the `*_item` binding level, the `field_mappings` full-replace semantics, and the `map_item.location` `'lat,lng'` contract.
|
|
4
|
+
|
|
5
|
+
> **Two servers.** Steps 1–2 are API Mapper (`apimapper_*`). Steps 3–5 are yt-builder-mcp (`yootheme_builder_*`). `apimapper_yootheme_binding_for_flow` is the only tool that spans the boundary — it tells you the exact source name + field names the yt-builder bind call needs.
|
|
6
|
+
|
|
7
|
+
## The end-to-end sequence
|
|
8
|
+
|
|
9
|
+
### Step 1 — Publish the flow (API Mapper)
|
|
10
|
+
|
|
11
|
+
```jsonc
|
|
12
|
+
apimapper_flow_full_recompile_publish({ flow_id: "flow_abc" })
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
This registers the flow as a YOOtheme source and flushes the schema cache. **Always** use this (not a bare compile) — derived/Transform fields are invisible downstream until a recompile-publish.
|
|
16
|
+
|
|
17
|
+
### Step 2 — Discover the source name + bindable fields (API Mapper)
|
|
18
|
+
|
|
19
|
+
```jsonc
|
|
20
|
+
apimapper_yootheme_binding_for_flow({ flow_id: "flow_abc" })
|
|
21
|
+
// → {
|
|
22
|
+
// source_name_singular: "apiMapperFlowAbc",
|
|
23
|
+
// source_name_list: "apiMapperFlowAbcList", // the Multi-Items target
|
|
24
|
+
// fields: [ { name: "title", type: "String" }, { name: "location", ... }, ... ],
|
|
25
|
+
// example_field_mappings: { title: "name", content: "popup_body", ... },
|
|
26
|
+
// ready_for_binding: true
|
|
27
|
+
// }
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
- `source_name_list` is the **Multi-Items** target (grid / list / slider / map — one container, N children).
|
|
31
|
+
- `source_name_singular` is the single-item wrapper (rare; for a single record).
|
|
32
|
+
- `ready_for_binding: false` means the flow isn't compiled or its output node has no schema fields — fix that before binding.
|
|
33
|
+
|
|
34
|
+
### Step 3 — Read the element's ETag (yt-builder-mcp)
|
|
35
|
+
|
|
36
|
+
The bind tool is a write and requires an ETag (optimistic lock):
|
|
37
|
+
|
|
38
|
+
```jsonc
|
|
39
|
+
yootheme_builder_get_etag({ template_id: "tpl_home" })
|
|
40
|
+
// → { etag: "W/\"…\"" }
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Step 4 — Bind the source on the `*_item` CHILD (yt-builder-mcp)
|
|
44
|
+
|
|
45
|
+
`yootheme_builder_element_bind_source` is a **first-class** tool in `tools/list` (promoted to L1 by F116). Call it **directly**:
|
|
46
|
+
|
|
47
|
+
```jsonc
|
|
48
|
+
yootheme_builder_element_bind_source({
|
|
49
|
+
template_id: "tpl_home",
|
|
50
|
+
element_path: "…/grid_item", // the *_item child, NOT the grid container
|
|
51
|
+
source_name: "apiMapperFlowAbcList", // the LIST name from step 2
|
|
52
|
+
bindingLevel: "item",
|
|
53
|
+
field_mappings: {
|
|
54
|
+
title: "name",
|
|
55
|
+
content: "popup_body",
|
|
56
|
+
image: "photo"
|
|
57
|
+
},
|
|
58
|
+
etag: "W/\"…\""
|
|
59
|
+
})
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
> **Older server builds (pre-F116).** If your yt-builder-mcp host plugin predates the F116 promotion, the bind tool is still an *advanced* tool and a direct call returns `unknown tool`. On those builds, reach it through the gateway: `yootheme_builder_advanced({ tool: "yootheme_builder_element_bind_source", arguments: { … } })`. On a current build the gateway wrap returns `unknown_tool` — call the tool directly.
|
|
63
|
+
|
|
64
|
+
### Step 5 — Publish the page and verify
|
|
65
|
+
|
|
66
|
+
Publish via the yt-builder page tools, then confirm the element renders N children with real field values (not defaults / blanks).
|
|
67
|
+
|
|
68
|
+
## Rule 1 — Bind on the `*_item` child, never the container
|
|
69
|
+
|
|
70
|
+
For "1 container with N dynamic children", bind `source` + Multi-Items on the **`*_item` child**, never on the container. YT Pro clones the source-bearing element N times as siblings of its parent — binding on a `grid` produces N stacked grids, not 1 grid with N children.
|
|
71
|
+
|
|
72
|
+
| Container | Bind target (the child) |
|
|
73
|
+
|-----------|-------------------------|
|
|
74
|
+
| `grid` | `grid_item` |
|
|
75
|
+
| `list` | `list_item` |
|
|
76
|
+
| `slider` | `slider_item` |
|
|
77
|
+
| `slideshow` | `slideshow_item` |
|
|
78
|
+
| `map` | `map_item` |
|
|
79
|
+
|
|
80
|
+
`bindingLevel: "item"` makes a container `element_path` auto-resolve to its first `*_item` child; `bindingLevel: "container"` binds on the container and the response carries a cloning warning. When the target is already a leaf under a bound `*_item`, the tool auto-uses the YT-Pro INHERIT form (`__node_item__` sentinel) — a standalone query there renders empty on YT5/Joomla.
|
|
81
|
+
|
|
82
|
+
## Rule 2 — `field_mappings` is a FULL REPLACE, not a merge (F43)
|
|
83
|
+
|
|
84
|
+
Each `yootheme_builder_element_bind_source` call writes `source.props` **from scratch** out of the `field_mappings` you pass — it does **not** merge with the prior binding. Whatever you omit is dropped. So:
|
|
85
|
+
|
|
86
|
+
- To refine one mapping, re-send **all** the mappings you want to keep, every call.
|
|
87
|
+
- A bind call with `field_mappings: { content: "popup_body" }` after one with `{ title: "name", content: "old" }` leaves the element bound to `content` only — `title` is gone.
|
|
88
|
+
|
|
89
|
+
Treat `field_mappings` as the complete desired prop→field map, not a patch.
|
|
90
|
+
|
|
91
|
+
## Rule 3 — Map needs ONE `'lat,lng'` location field (A9 / F39)
|
|
92
|
+
|
|
93
|
+
A `map_item` positions each pin from a **single** `location` field formatted `"lat,lng"`. Source APIs almost always deliver `lat` and `lng` as **separate** numeric fields, so you must synthesize the combined field in a Transform node BEFORE publishing:
|
|
94
|
+
|
|
95
|
+
```jsonc
|
|
96
|
+
// Transform projection (API Mapper side, step 0 — before step 1):
|
|
97
|
+
[*].{
|
|
98
|
+
title: name,
|
|
99
|
+
location: join(',', [to_string(lat), to_string(lng)]), // "37.77,-122.41"
|
|
100
|
+
popup_body: name // or a composed derived field — see below
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Then bind `location` on the `map_item`:
|
|
105
|
+
|
|
106
|
+
```jsonc
|
|
107
|
+
yootheme_builder_element_bind_source({
|
|
108
|
+
template_id, element_path: "…/map_item", source_name: "apiMapperFlowAbcList",
|
|
109
|
+
bindingLevel: "item",
|
|
110
|
+
field_mappings: { location: "location", title: "title", content: "popup_body" },
|
|
111
|
+
etag
|
|
112
|
+
})
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
> **Silent zero-pin trap.** If `location` is left unbound, the Map renders structurally fine and the inspector reports it "correct" — but **zero pins** appear. Always bind `location`, and verify pins after publish.
|
|
116
|
+
|
|
117
|
+
### Rich popups beyond the 4 slots (F42)
|
|
118
|
+
|
|
119
|
+
A `map_item` popup has only `title` / `meta` / `content` / `image`. For more facets, compose them into one **derived field** in the Transform (`popup_body` above), then bind it to `content` — see `apimapper_get_skill({ topic: "yootheme" })` for the full derived-field recipe.
|
|
120
|
+
|
|
121
|
+
## Rule 4 — Schema-changing Transform edits force recompile → re-bind (F44)
|
|
122
|
+
|
|
123
|
+
Any Transform edit that adds or renames a field (a new derived `location`, a new `popup_body`) is **invisible** to `apimapper_yootheme_binding_for_flow` and to the yt-builder bind call until you re-publish. The loop is:
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
edit Transform → apimapper_flow_full_recompile_publish →
|
|
127
|
+
apimapper_yootheme_binding_for_flow (re-read fields) → re-bind (full field_mappings)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Skipping the recompile leaves you binding against the OLD schema — the new field name resolves to nothing and the element renders blank.
|
|
131
|
+
|
|
132
|
+
## Two binding shapes — pick the one that matches the job
|
|
133
|
+
|
|
134
|
+
Everything above is the **`field_mappings` shape**: you bind a flow into a *fresh* Builder element (a blank grid / list / map you just dropped in) and let `element_bind_source` author the `source.props` for you. That is the right shape for "build me a new grid from this API".
|
|
135
|
+
|
|
136
|
+
There is a SECOND shape you reach for when the goal is **"dynamize an EXISTING, already-designed theme layout"** — the customer hand-built a beautiful section (match rows with logos, badges, a red highlight row) and just wants those *same* rows fed from your flow, pixel-identical. There you do NOT bind through `field_mappings`; you read the layout's own JSON and swap the query. This is the **theme-canonical `#parent` repeater form**, the form YT5 itself emits and renders. (Cold agents have hand-authored it twice successfully; it is documented here so you don't have to reverse-engineer it from a `page_get_layout` dump again.)
|
|
137
|
+
|
|
138
|
+
> **For dynamizing an EXISTING layout, prefer `yootheme_builder_page_dynamize`
|
|
139
|
+
> (one call).** It runs the copy-swap-write-publish below for you — you supply
|
|
140
|
+
> only the section, the source name, and a `leaf_map` of leaf→field names. See
|
|
141
|
+
> `apimapper_get_skill({ topic: "dynamize-existing-layout" })` for the worked
|
|
142
|
+
> example. The `#parent` anatomy below is **background + fallback**: read it to
|
|
143
|
+
> understand what the tool authors by construction, and use it for the
|
|
144
|
+
> hand-edit path when `page_dynamize` is unavailable or can't author a shape you
|
|
145
|
+
> need.
|
|
146
|
+
|
|
147
|
+
### The `#parent` repeater anatomy
|
|
148
|
+
|
|
149
|
+
An iterating list in a theme layout has three layers:
|
|
150
|
+
|
|
151
|
+
1. **The repeating element** (a `column`, `panel`, row item, …) carries the source query:
|
|
152
|
+
```jsonc
|
|
153
|
+
"source": { "query": { "name": "customArticles", "arguments": { … } } }
|
|
154
|
+
```
|
|
155
|
+
**To dynamize it, swap ONLY that query name** to your published flow's LIST source:
|
|
156
|
+
```jsonc
|
|
157
|
+
"source": { "query": { "name": "apiMapperFlow<Id>List" } } // from binding_for_flow → source_name_list
|
|
158
|
+
```
|
|
159
|
+
The swap *is* the dynamization — the rest of the designed markup is untouched.
|
|
160
|
+
|
|
161
|
+
2. **The child `fragment`** = the clone body. It binds to the parent iteration via the `#parent` sentinel:
|
|
162
|
+
```jsonc
|
|
163
|
+
"source": { "query": { "name": "#parent" } }
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
3. **Each leaf** (the bits that actually print a value) reads `#parent` plus a `props.content` pointing at one field, with formatting filters living **inside the binding**:
|
|
167
|
+
```jsonc
|
|
168
|
+
"source": {
|
|
169
|
+
"query": { "name": "#parent" },
|
|
170
|
+
"props": { "content": { "name": "field.match_date", "filters": { "date": "D, d.m.y" } } }
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
Field names use dot-paths (`field.match_date`, `club_crest.imagefile`). For a nested object, the leaf uses the subquery form: `"query": { "name": "#parent", "field": { "name": "field.match_home_team", "directives": [ … ] } }`.
|
|
174
|
+
|
|
175
|
+
### `_condition` only bites with a `filters.condition` operator
|
|
176
|
+
|
|
177
|
+
A leaf or element shows/hides via `_condition` — but `_condition: { name: X }` **alone is wireless** (it renders unconditionally). The operator must ride in `filters.condition`:
|
|
178
|
+
|
|
179
|
+
- `"filters": { "condition": "!" }` → render only when the field is **EMPTY**.
|
|
180
|
+
- `"filters": { "condition": "!!" }` → render only when the field is **FILLED**.
|
|
181
|
+
|
|
182
|
+
### Boolean fields filter unreliably → use empty/filled STRING semantics
|
|
183
|
+
|
|
184
|
+
Do **not** drive a `_condition` off a boolean flow field (`played`, `is_next`) — boolean binding is unreliable here. Instead, make the flow emit a **string that is empty or filled**, then condition on `!`/`!!`:
|
|
185
|
+
|
|
186
|
+
```jsonc
|
|
187
|
+
// Transform projection (API Mapper side):
|
|
188
|
+
{
|
|
189
|
+
"result": "(played && score) || ''", // '' when not played → hide score block with '!'
|
|
190
|
+
"next": "(is_next && 'next') || ''" // '' unless next → show the red highlight row with '!!'
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
So the per-row highlight (exactly one red "next match" row) is just: emit `next` as `''`/`'next'`, then `_condition` with `filters.condition: '!!'` on the highlight element.
|
|
195
|
+
|
|
196
|
+
### The dynamize-an-existing-layout loop
|
|
197
|
+
|
|
198
|
+
```
|
|
199
|
+
page_get_layout (read the section's JSON)
|
|
200
|
+
→ find the repeating element's source.query.name
|
|
201
|
+
→ swap it to apiMapperFlow<Id>List
|
|
202
|
+
→ remap each leaf's props.content to your flow's field names (keep the filters)
|
|
203
|
+
→ convert any boolean _condition to a '!'/'!!' string condition
|
|
204
|
+
→ write the layout back (page create/update)
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
**This loop is the ONLY path to a design-exact result. COPY the reference
|
|
208
|
+
subtree — NEVER rebuild the design element-by-element with `element_add`.**
|
|
209
|
+
A hand-rebuilt "equivalent" layout renders visibly different (measured on the
|
|
210
|
+
FC-Greenfield probe: 213px row height vs the reference's 90px, wrapped team
|
|
211
|
+
names, missing league icon, missing CTA column) and FAILS any visual judge.
|
|
212
|
+
The reference JSON carries dozens of tuned props (`title_grid_width`,
|
|
213
|
+
`image_align`, grid breakpoints, column `layout` strings) you will not guess.
|
|
214
|
+
|
|
215
|
+
**INSERT NOTHING into the copied subtree either.** Adding even ONE extra
|
|
216
|
+
column/slot (e.g. a new "league position" text next to the matchup) shifts
|
|
217
|
+
every sibling's computed width — measured on a probe: team-name cells
|
|
218
|
+
collapsed from 213px to 55–67px, UIkit fell back to `uk-grid-stack`, names
|
|
219
|
+
wrapped and crests overlapped the text. Extra data goes into EXISTING leaves:
|
|
220
|
+
compose it into a field your flow already feeds a bound leaf with (e.g.
|
|
221
|
+
`"AFC Richmond (3rd)"` as the team-name value, or the meta line) — never into
|
|
222
|
+
a new element. Copy = byte-identical structure; only leaf field NAMES change.
|
|
223
|
+
|
|
224
|
+
**KEEP every leaf you have no flow field for** (a static league/matchday icon,
|
|
225
|
+
decorative images): leave its original binding untouched or feed it a flow
|
|
226
|
+
constant. REMOVE a leaf only when its target cannot exist for the new data
|
|
227
|
+
(e.g. a "go to review" link whose target article doesn't exist for sheet
|
|
228
|
+
rows) — default KEEP, remove only with a reason. And NEVER invent asset paths:
|
|
229
|
+
if you did not read the path from the reference layout or an API response, do
|
|
230
|
+
not guess one (a guessed `…-league-logo.svg` 404s silently).
|
|
231
|
+
|
|
232
|
+
**"But the original binds RELATIONAL/nested fields and my flow is FLAT" is
|
|
233
|
+
NOT a reason to rebuild.** The element STRUCTURE and all its props transfer
|
|
234
|
+
verbatim regardless of the binding shape. Only the leaf `source.props.*.name`
|
|
235
|
+
values change: a nested original leaf like
|
|
236
|
+
`field.match_home_team.club_crest.imagefile` simply becomes your flat field
|
|
237
|
+
name (`home_crest`). Keep every other prop byte-identical. If the original
|
|
238
|
+
leaves bind via `#parent`, keep the `#parent` query form too — just swap the
|
|
239
|
+
field names inside `props`.
|
|
240
|
+
|
|
241
|
+
Practical copy mechanics: take the section subtree from `page_get_layout`,
|
|
242
|
+
perform the swaps above on the JSON, then create the page with
|
|
243
|
+
`yootheme_builder_pages_create({ layout })` (or write a full layout via the
|
|
244
|
+
pages update path). One write — no element-by-element surgery.
|
|
245
|
+
|
|
246
|
+
### Leaves you have no flow field for: default KEEP, remove only with a reason
|
|
247
|
+
|
|
248
|
+
When you copy the reference subtree you will hit leaves that your flow does
|
|
249
|
+
**not** supply a value for — a static matchday/league icon, a divider image, a
|
|
250
|
+
decorative crest. **The default is KEEP, not delete.** Deleting a leaf changes
|
|
251
|
+
the visual composition the design depends on, so removal needs an explicit
|
|
252
|
+
justification on a per-leaf basis. The two correct ways to keep an unmapped
|
|
253
|
+
leaf:
|
|
254
|
+
|
|
255
|
+
- **Static asset (an image / icon that never varies):** leave its binding
|
|
256
|
+
**exactly as the reference had it** — a static `image` URL or a theme asset
|
|
257
|
+
reference renders the same icon for every cloned row. Do not touch it.
|
|
258
|
+
- **A value the flow can supply as a constant:** bind it to a **flow constant**
|
|
259
|
+
field (emit a fixed string from the Transform, e.g. `"league": 'Premier'`)
|
|
260
|
+
and point the leaf's `props.content` at that field. The icon/value is now
|
|
261
|
+
uniform across rows but flows through the same `#parent` plumbing.
|
|
262
|
+
|
|
263
|
+
**Only REMOVE a leaf when its TARGET cannot exist for the new data.** The
|
|
264
|
+
canonical example from the FC-Greenfield probe: the original row carried a
|
|
265
|
+
`GO TO MATCH REVIEW` link whose `href` resolved to a CMS *article* per match.
|
|
266
|
+
Sheet rows have **no backing article**, so that link target genuinely does not
|
|
267
|
+
exist — removing that one leaf is **CORRECT**. Contrast the league icon in the
|
|
268
|
+
same probe: it is a *static* asset with a target that still exists, so dropping
|
|
269
|
+
it was a **regression** (the R2 visual delta flagged "missing league icon").
|
|
270
|
+
Same probe, opposite verdicts — because one target vanished for sheet data and
|
|
271
|
+
the other did not.
|
|
272
|
+
|
|
273
|
+
Rule of thumb when judging a copy: walk every leaf and ask *"does this leaf's
|
|
274
|
+
target still exist for my data?"* — KEEP if yes (static asset or flow
|
|
275
|
+
constant), REMOVE only when the answer is a concrete no.
|
|
276
|
+
|
|
277
|
+
### Authoring per-row visibility through `bind` — RESOLVED 2026-06-10 (F96 / F97); F95 still open
|
|
278
|
+
|
|
279
|
+
**F96 / F97 — RESOLVED 2026-06-10.** You CAN now author a *filtered*
|
|
280
|
+
`_condition` (e.g. "show this block only for played matches") directly through
|
|
281
|
+
`element_bind_source` — no separate pre-filtered flows, no hand-authored
|
|
282
|
+
`#parent`-JSON layout write. The fix landed in the yt-builder bind tool
|
|
283
|
+
(`builders.ts` field_mappings, F96/F82, 2026-06-10): a `field_mappings` entry
|
|
284
|
+
may now be an **object** carrying the operator, not just a bare field-name
|
|
285
|
+
string.
|
|
286
|
+
|
|
287
|
+
**The object-form `field_mappings` (the one-call recipe).** A mapping value is
|
|
288
|
+
EITHER the old string (`element_prop: "source_field"`) OR an object
|
|
289
|
+
`{ name, filters }` that rides a `filters.condition` operator straight onto the
|
|
290
|
+
leaf:
|
|
291
|
+
|
|
292
|
+
```jsonc
|
|
293
|
+
yootheme_builder_element_bind_source({
|
|
294
|
+
template_id: "tpl_home",
|
|
295
|
+
element_path: "…/grid_item", // the *_item child, NOT the container
|
|
296
|
+
source_name: "apiMapperFlowAbcList",
|
|
297
|
+
bindingLevel: "item",
|
|
298
|
+
field_mappings: {
|
|
299
|
+
// plain string mapping — unconditional
|
|
300
|
+
title: "name",
|
|
301
|
+
content: "popup_body",
|
|
302
|
+
// OBJECT mapping — binds the field AND a filtered _condition in ONE call.
|
|
303
|
+
// '!!' → render only when `next_marker` is FILLED (the highlight row).
|
|
304
|
+
highlight: { name: "next_marker", filters: { condition: "!!" } }
|
|
305
|
+
// '!' → render only when the field is EMPTY.
|
|
306
|
+
},
|
|
307
|
+
etag: "W/\"…\""
|
|
308
|
+
})
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
`handlers-bind.ts` forwards the object-form verbatim and the PHP side
|
|
312
|
+
normalises it, so the `filters.condition` lands as a real, wired `_condition`
|
|
313
|
+
on the leaf — exactly the operator the `#parent`-JSON layout write produces.
|
|
314
|
+
This is the one-call path: **bind the field and its visibility condition
|
|
315
|
+
together**; you no longer need a separate flow per visibility bucket just to get
|
|
316
|
+
per-row show/hide.
|
|
317
|
+
|
|
318
|
+
> Same `'!'` / `'!!'` string-semantics rule as above applies — drive the
|
|
319
|
+
> condition off a flow field that is **empty or filled** (emit `''` / `'next'`),
|
|
320
|
+
> not off a raw boolean (`played`, `is_next`), because boolean binding is
|
|
321
|
+
> unreliable here.
|
|
322
|
+
|
|
323
|
+
**F95 — still OPEN: large inline-layout `pages_create` drops its params.** When
|
|
324
|
+
the inline layout JSON gets large (~10 KB+), the create call's parameters are
|
|
325
|
+
dropped in transit (an empty page is created); a single-row create (~4 KB) goes
|
|
326
|
+
through fine. **Workaround:** write the layout in **smaller chunks** — one
|
|
327
|
+
row-block create that goes through, then add the remaining rows — rather than
|
|
328
|
+
one ~10 KB inline create.
|
|
329
|
+
|
|
330
|
+
## F81 — Finding the design: it may be a NAMED SECTION inside another article
|
|
331
|
+
|
|
332
|
+
The page the customer calls "the X page" frequently is **not its own template** — it is a **named section embedded inside another article** (e.g. the "Matches" rows live as a named section in the Home article). If `pages_list` / a route lookup doesn't surface it, don't assume it's missing.
|
|
333
|
+
|
|
334
|
+
**Discovery tool:** `yootheme_builder_template_summary` lists a template's `named_sections`. Scan those to locate the design, then `page_get_layout` that template and work on the section's subtree. This turns a multi-scan hunt into one lookup.
|
|
335
|
+
|
|
336
|
+
## F91 — Article-context cells render ~20% narrower than category templates
|
|
337
|
+
|
|
338
|
+
The SAME layout JSON gets **~20% less cell width** in an **article context** than in a **category template** (measured ≈200px vs ≈253px on the FC match rows). So enrichment bindings you add to a designed row — an extra crest image, a meta line, a second value in a panel — can **wrap** in the article context even though they fit in the category template the design was lifted from.
|
|
339
|
+
|
|
340
|
+
- After adding any enrichment binding (logo + meta on a panel, a second score line), **verify visually at the target viewport** — a text/DOM check will pass while the row visibly wraps.
|
|
341
|
+
- If it wraps, drop the enrichment or move it to a slot with more room; do not assume "fits in the source template ⇒ fits here".
|
|
342
|
+
|
|
343
|
+
## Related topics
|
|
344
|
+
|
|
345
|
+
- `apimapper_get_skill({ topic: "yootheme" })` — publishing flows as sources; the derived-field rich-popup recipe.
|
|
346
|
+
- `apimapper_get_skill({ topic: "merge-two-sources-on-key" })` — when the source itself is a merge of two APIs.
|
|
347
|
+
- `apimapper_get_skill({ topic: "jmespath-pitfalls" })` — Transform projection traps + the date/number primitives used to synthesize derived fields.
|
|
348
|
+
- `apimapper_get_skill({ topic: "jmespath-cookbook" })` — copy-paste recipes, incl. the two-transform split for the depth limit you hit when composing the string-condition fields above.
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
API Mapper sources show up in the YOOtheme Builder Source dropdown ONLY when a flow ends in a YOOtheme Output node and the flow is **published** (not just saved). This is the most common "source isn't appearing" cause.
|
|
4
4
|
|
|
5
|
+
> **REST tools only.** Build flows with the real `apimapper_*` REST tools below — `flow_setup_with_sources`, `flow_create`, `flow_update`, `flow_full_recompile_publish`, `yootheme_binding_for_flow`. This server has no UI-automation node-editing tools (the `add-node` / `configure-output` style tools belong to a separate UI MCP and are not callable here). To go from a published source into the actual Builder element, see `apimapper_get_skill({ topic: "yootheme-source-to-builder-handoff" })`.
|
|
6
|
+
|
|
5
7
|
## The publish pipeline
|
|
6
8
|
|
|
7
9
|
```
|
|
@@ -16,49 +18,82 @@ Source ─► [Filter] ─► [Transform] ─► [Merge] ─► Output
|
|
|
16
18
|
|
|
17
19
|
Both output types can coexist on the same flow.
|
|
18
20
|
|
|
19
|
-
## Step-by-step
|
|
21
|
+
## Step-by-step (REST)
|
|
20
22
|
|
|
21
|
-
1. **Build the flow
|
|
23
|
+
1. **Build the flow.** The one-shot path lays out source → (merge) → (filter) → output for you, including the YOOtheme output node:
|
|
22
24
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
)
|
|
25
|
+
```jsonc
|
|
26
|
+
apimapper_flow_setup_with_sources({
|
|
27
|
+
name: "Marketing Posts",
|
|
28
|
+
sources: [{ connection: "conn_blog" }],
|
|
29
|
+
output: { type: "yootheme", name: "Marketing Posts" }
|
|
30
|
+
})
|
|
30
31
|
```
|
|
31
|
-
The `source_name` is what appears in the YOOtheme Source dropdown. Keep it short and human-readable.
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
```
|
|
37
|
-
This catches:
|
|
38
|
-
- Disconnected nodes
|
|
39
|
-
- Type drift between Transform output and Output input
|
|
40
|
-
- Missing required fields
|
|
41
|
-
- Filter expressions referencing fields that don't exist on the upstream
|
|
33
|
+
The output `name` is what appears in the YOOtheme Source dropdown. Keep it short and human-readable. For full control over nodes/edges use `apimapper_flow_create`; to edit an existing graph use `apimapper_flow_update` (advanced — call via `apimapper_advanced({ tool: "apimapper_flow_update", arguments: {…} })`).
|
|
34
|
+
|
|
35
|
+
2. **Publish (compile + register + invalidate cache) in one call:**
|
|
42
36
|
|
|
43
|
-
|
|
37
|
+
```jsonc
|
|
38
|
+
apimapper_flow_full_recompile_publish({ flow_id: "flow_abc" })
|
|
44
39
|
```
|
|
45
|
-
|
|
40
|
+
|
|
41
|
+
This compiles the graph (catching disconnected nodes, type drift, missing fields, and filter expressions referencing non-existent fields), writes the flow into the YOOtheme Source registry, and invalidates the YOOtheme schema cache. Prefer this over a bare compile — a plain `apimapper_flow_compile` is gateway-only and does not force a schema-cache flush.
|
|
42
|
+
|
|
43
|
+
3. **Discover the exact source name + bindable fields:**
|
|
44
|
+
|
|
45
|
+
```jsonc
|
|
46
|
+
apimapper_yootheme_binding_for_flow({ flow_id: "flow_abc" })
|
|
47
|
+
// → { source_name_singular, source_name_list, fields, ready_for_binding, ... }
|
|
46
48
|
```
|
|
47
|
-
This writes the flow into the YOOtheme Source registry and invalidates the YOOtheme schema cache.
|
|
48
49
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
4. **Verify in the YOOtheme Builder:** open any page/template, add a Grid / Slider / Map element, and look for **the name you set in step 1** ("Marketing Posts") in the Source dropdown — NOT a "API Mapper" entry.
|
|
51
|
+
|
|
52
|
+
## Binding the published source into a Builder element
|
|
53
|
+
|
|
54
|
+
Once published, the source is bound to a Builder element through the **yt-builder-mcp** server, not here. The full bridge (recompile → `binding_for_flow` → `yootheme_builder_element_bind_source` with `bindingLevel`, `field_mappings`, and the Multi-Items `*_item` rule) lives in its own topic:
|
|
55
|
+
|
|
56
|
+
```jsonc
|
|
57
|
+
apimapper_get_skill({ topic: "yootheme-source-to-builder-handoff" })
|
|
58
|
+
```
|
|
53
59
|
|
|
54
60
|
## Schema for YOOtheme dynamic content
|
|
55
61
|
|
|
56
|
-
YOOtheme reads the published flow's output schema
|
|
62
|
+
YOOtheme reads the published flow's output schema. Each field on the schema becomes a mappable property in the YOOtheme element editor (title, image, link, date, …).
|
|
63
|
+
|
|
64
|
+
To control which fields YOOtheme sees, use a `Transform` node with an explicit JMESPath projection — e.g. `[*].{title: name, image: cover.url, link: permalink}`. The Visual Expression Builder in the UI produces equivalent expressions; both end up in the same compiled flow.
|
|
65
|
+
|
|
66
|
+
### Rich popups / multi-facet displays via a DERIVED Transform field (F42)
|
|
67
|
+
|
|
68
|
+
A `map_item` popup (and several other item slots) exposes only a small fixed set of bind targets — `title`, `meta`, `content`, `image`. When the customer wants **more facets than there are slots** (e.g. price + beds + baths + rating + review-count all in one popup), do **not** look for extra slots — compose them into a single **derived field** in the Transform node, then bind that one field to `content`:
|
|
69
|
+
|
|
70
|
+
```jsonc
|
|
71
|
+
// Transform projection — synthesize a composite field the schema will expose.
|
|
72
|
+
[*].{
|
|
73
|
+
title: name,
|
|
74
|
+
image: photo,
|
|
75
|
+
location: join(',', [to_string(lat), to_string(lng)]), // map_item.location wants ONE "lat,lng" field
|
|
76
|
+
popup_body: join(' · ', [
|
|
77
|
+
join('', ['$', to_string(price)]),
|
|
78
|
+
join(' ', [to_string(beds), 'bd']),
|
|
79
|
+
join(' ', [to_string(baths), 'ba']),
|
|
80
|
+
join('', ['★', to_string(rating)])
|
|
81
|
+
])
|
|
82
|
+
}
|
|
83
|
+
```
|
|
57
84
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
- `
|
|
61
|
-
|
|
85
|
+
After re-publishing, `popup_body` is a first-class field you bind to the popup's `content` slot. This is the canonical pattern for "5+ facets, 4 slots". (For the `'lat,lng'` `location` contract and the map binding itself, see the handoff topic.)
|
|
86
|
+
|
|
87
|
+
> Derived fields are invisible to `apimapper_yootheme_binding_for_flow` until you re-run `apimapper_flow_full_recompile_publish`. Change the projection → recompile → re-read bindings → re-bind.
|
|
88
|
+
|
|
89
|
+
## The map sequence (F46) — ETag-locked bind + publish, in order
|
|
90
|
+
|
|
91
|
+
A Map element is wired with the same Multi-Items discipline as Grid/Slider, plus the coordinate + popup specifics. The end-to-end sequence:
|
|
92
|
+
|
|
93
|
+
1. `apimapper_flow_full_recompile_publish({ flow_id })` — publish the source (with the derived `location` + popup fields above).
|
|
94
|
+
2. `apimapper_yootheme_binding_for_flow({ flow_id })` — get `source_name_list` + field names.
|
|
95
|
+
3. In yt-builder-mcp, read the element's ETag, then bind on the **`map_item` child** (never the `map` container) by calling `yootheme_builder_element_bind_source` directly (a first-class tool in tools/list), mapping `location` to the single `'lat,lng'` field and the popup slots to your fields. The exact call is in the handoff topic.
|
|
96
|
+
4. Publish the page and verify markers render. If `location` is unbound the Map renders structurally fine with **zero pins** — bind `location` or no markers appear.
|
|
62
97
|
|
|
63
98
|
## Where YOOtheme features live (NOT in the flow)
|
|
64
99
|
|
|
@@ -72,25 +107,21 @@ These belong in YOOtheme, not in API Mapper:
|
|
|
72
107
|
| Pagination | YOOtheme element + URL handling |
|
|
73
108
|
| Per-page query overrides | YOOtheme element bindings (`source`) |
|
|
74
109
|
|
|
75
|
-
API Mapper's job is to deliver typed clean JSON.
|
|
110
|
+
API Mapper's job is to deliver typed clean JSON. Render-time slicing / sorting / filtering lives in YOOtheme — don't reimplement it in a Transform node.
|
|
76
111
|
|
|
77
112
|
## Shortcode output (WordPress only)
|
|
78
113
|
|
|
79
|
-
|
|
80
|
-
- Embedding in classic editor posts
|
|
81
|
-
- Using outside YOOtheme (page builders, plugins)
|
|
82
|
-
- Quick previews in admin
|
|
83
|
-
|
|
84
|
-
Shortcode currently supports a subset of the YOOtheme render-time features. See `wootsup.com/docs/mcp/` (the shortcode subpage will land in Phase 2 — track via GitHub issues).
|
|
114
|
+
The same flow can ALSO emit a Shortcode output (`output.type = "shortcode"`). Use this for embedding in classic editor posts, using outside YOOtheme, or quick admin previews. Shortcode currently supports a subset of the YOOtheme render-time features.
|
|
85
115
|
|
|
86
116
|
## Common pitfalls
|
|
87
117
|
|
|
88
|
-
1. **Save vs Publish** — saving stores the flow
|
|
89
|
-
2. **Looking for "API Mapper" in the dropdown** — wrong. Sources appear under the **flow's published
|
|
90
|
-
3. **Stale YOOtheme schema cache** —
|
|
91
|
-
4. **
|
|
92
|
-
5. **Dynamic content fields show as empty** — usually case sensitivity. YOOtheme expects field names
|
|
118
|
+
1. **Save vs Publish** — saving stores the flow but does NOT register it as a YOOtheme Source. Always publish via `apimapper_flow_full_recompile_publish`.
|
|
119
|
+
2. **Looking for "API Mapper" in the dropdown** — wrong. Sources appear under the **flow's published source name**.
|
|
120
|
+
3. **Stale YOOtheme schema cache** — `apimapper_flow_full_recompile_publish` flushes it; a bare publish or compile may not. Always use the recompile-publish tool after a schema change.
|
|
121
|
+
4. **Derived fields missing after a Transform edit** — re-run `apimapper_flow_full_recompile_publish`, then re-read `apimapper_yootheme_binding_for_flow`. New fields are invisible to binding until recompile.
|
|
122
|
+
5. **Dynamic content fields show as empty** — usually case sensitivity. YOOtheme expects lowercase field names; the Transform projection must produce lowercase keys.
|
|
123
|
+
6. **Map with zero pins** — the `map_item.location` slot is unbound, or `location` is two separate lat/lng fields instead of one `'lat,lng'` field. Synthesize a single `location` field in the Transform.
|
|
93
124
|
|
|
94
125
|
## Multi-platform behaviour
|
|
95
126
|
|
|
96
|
-
WordPress and Joomla produce identical YOOtheme schemas — the Source/Type registration goes through the same YOOtheme PHP API.
|
|
127
|
+
WordPress and Joomla produce identical YOOtheme schemas — the Source/Type registration goes through the same YOOtheme PHP API. The underlying REST surface differs (see `apimapper_get_skill({ topic: "joomla" })`). Tools auto-detect the platform and route correctly.
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
export interface OAuthClientRegistration {
|
|
2
|
-
client_id: string;
|
|
3
|
-
client_id_issued_at: number;
|
|
4
|
-
redirect_uris: string[];
|
|
5
|
-
client_name?: string;
|
|
6
|
-
token_endpoint_auth_method: "none";
|
|
7
|
-
grant_types: ["authorization_code", "refresh_token"];
|
|
8
|
-
response_types: ["code"];
|
|
9
|
-
}
|
|
10
|
-
export interface IssueCodeArgs {
|
|
11
|
-
client_id: string;
|
|
12
|
-
redirect_uri: string;
|
|
13
|
-
code_challenge: string;
|
|
14
|
-
code_challenge_method: "S256" | "plain";
|
|
15
|
-
scope: string;
|
|
16
|
-
state?: string;
|
|
17
|
-
}
|
|
18
|
-
export interface ExchangeCodeArgs {
|
|
19
|
-
client_id: string;
|
|
20
|
-
code: string;
|
|
21
|
-
redirect_uri: string;
|
|
22
|
-
code_verifier: string;
|
|
23
|
-
}
|
|
24
|
-
export interface RefreshArgs {
|
|
25
|
-
client_id: string;
|
|
26
|
-
refresh_token: string;
|
|
27
|
-
}
|
|
28
|
-
export interface TokenResponse {
|
|
29
|
-
access_token: string;
|
|
30
|
-
token_type: "Bearer";
|
|
31
|
-
expires_in: number;
|
|
32
|
-
refresh_token: string;
|
|
33
|
-
scope: string;
|
|
34
|
-
}
|
|
35
|
-
export interface AccessTokenInfo {
|
|
36
|
-
client_id: string;
|
|
37
|
-
scope: string;
|
|
38
|
-
expires_at: number;
|
|
39
|
-
}
|
|
40
|
-
export interface OAuthProviderOptions {
|
|
41
|
-
/** Clock function returning unix seconds. Default: Date.now()/1000. */
|
|
42
|
-
now?: () => number;
|
|
43
|
-
/** Authorization code TTL (default 600s = 10 min). */
|
|
44
|
-
codeTtlSeconds?: number;
|
|
45
|
-
/** Access token TTL (default 86400s = 24 h). */
|
|
46
|
-
tokenTtlSeconds?: number;
|
|
47
|
-
/** Refresh token TTL (default 604800s = 7 d). */
|
|
48
|
-
refreshTtlSeconds?: number;
|
|
49
|
-
}
|
|
50
|
-
export interface OAuthProvider {
|
|
51
|
-
registerClient(args: {
|
|
52
|
-
redirect_uris: string[];
|
|
53
|
-
client_name?: string;
|
|
54
|
-
}): OAuthClientRegistration;
|
|
55
|
-
getClient(client_id: string): OAuthClientRegistration | null;
|
|
56
|
-
issueCode(args: IssueCodeArgs): string;
|
|
57
|
-
exchangeCode(args: ExchangeCodeArgs): TokenResponse;
|
|
58
|
-
refreshToken(args: RefreshArgs): TokenResponse;
|
|
59
|
-
verifyAccessToken(token: string): AccessTokenInfo | null;
|
|
60
|
-
sweep(): number;
|
|
61
|
-
stats(): {
|
|
62
|
-
clients: number;
|
|
63
|
-
codes: number;
|
|
64
|
-
tokens: number;
|
|
65
|
-
refreshes: number;
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
export declare function createOAuthProvider(options?: OAuthProviderOptions): OAuthProvider;
|