@jay-framework/jay-stack-cli 0.11.0 โ†’ 0.12.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/README.md CHANGED
@@ -27,6 +27,57 @@ jay-stack dev ./my-project # Start dev server in specified directory
27
27
 
28
28
  This will start both the development server and editor server with default configuration.
29
29
 
30
+ ##### Test Mode
31
+
32
+ For automated testing, CI pipelines, and smoke tests, use the `--test-mode` flag:
33
+
34
+ ```bash
35
+ jay-stack dev --test-mode # Enable test endpoints
36
+ jay-stack dev --timeout 60 # Auto-shutdown after 60 seconds (implies --test-mode)
37
+ jay-stack dev --test-mode --timeout 120 # Both options
38
+ ```
39
+
40
+ Test mode enables special endpoints for reliable server management:
41
+
42
+ | Endpoint | Method | Description |
43
+ | ---------------- | ------ | ----------------------------------------------------------------------- |
44
+ | `/_jay/health` | GET | Returns `{"status":"ready","port":3300,"editorPort":3301,"uptime":5.2}` |
45
+ | `/_jay/shutdown` | POST | Gracefully shuts down the server, returns `{"status":"shutting_down"}` |
46
+
47
+ **Example smoke test workflow:**
48
+
49
+ ```bash
50
+ # Start server with 2-minute timeout
51
+ jay-stack dev --test-mode --timeout 120 &
52
+
53
+ # Wait for health endpoint
54
+ for i in {1..30}; do
55
+ if curl -s http://localhost:3300/_jay/health | grep -q "ready"; then
56
+ break
57
+ fi
58
+ sleep 1
59
+ done
60
+
61
+ # Run your tests
62
+ curl -s http://localhost:3300/products/ | grep -q "<!doctype html"
63
+
64
+ # Clean shutdown
65
+ curl -X POST http://localhost:3300/_jay/shutdown
66
+ ```
67
+
68
+ When test mode is enabled, the startup output includes the test endpoints:
69
+
70
+ ```
71
+ ๐Ÿš€ Jay Stack dev server started successfully!
72
+ ๐Ÿ“ฑ Dev Server: http://localhost:3300
73
+ ๐ŸŽจ Editor Server: http://localhost:3301 (ID: init)
74
+ ๐Ÿ“ Pages directory: ./src/pages
75
+ ๐Ÿงช Test Mode: enabled
76
+ Health: http://localhost:3300/_jay/health
77
+ Shutdown: curl -X POST http://localhost:3300/_jay/shutdown
78
+ Timeout: 60s
79
+ ```
80
+
30
81
  #### `jay-stack validate [path]`
31
82
 
32
83
  Validate all `.jay-html` and `.jay-contract` files in the project without creating output files:
@@ -203,6 +254,13 @@ The CLI is built using:
203
254
  | `jay-stack validate [path]` | Validate jay-html and jay-contract files |
204
255
  | `jay-stack validate-plugin [path]` | Validate a plugin package |
205
256
 
257
+ ### Dev Command Options
258
+
259
+ | Option | Description |
260
+ | ------------------ | -------------------------------------------------------- |
261
+ | `--test-mode` | Enable test endpoints (`/_jay/health`, `/_jay/shutdown`) |
262
+ | `--timeout <secs>` | Auto-shutdown after N seconds (implies `--test-mode`) |
263
+
206
264
  ### Validate Command Options
207
265
 
208
266
  | Option | Description |
@@ -0,0 +1,116 @@
1
+ # Jay Stack Agent Kit
2
+
3
+ This folder contains everything you need to create jay-html pages for a jay-stack application.
4
+
5
+ ## What is Jay Stack?
6
+
7
+ Jay Stack is a full-stack framework where:
8
+
9
+ - **Plugins** provide headless components (data + interactions, no UI)
10
+ - **Contracts** define the data shape and interaction points of each component
11
+ - **jay-html** templates provide the UI that binds to contract data
12
+ - **Rendering phases** determine when data is available (build-time, request-time, client-side)
13
+
14
+ Your job is to create `.jay-html` pages that bind to the data and interactions defined by contracts.
15
+
16
+ ## Rendering Phases
17
+
18
+ | Phase | When | Use For |
19
+ | -------------------- | ------------------ | ------------------------------------------------- |
20
+ | **slow** | Build time (SSG) | Static content, SEO data, pre-rendered lists |
21
+ | **fast** | Request time (SSR) | Per-request data (prices, stock, personalization) |
22
+ | **fast+interactive** | Request + client | Data that also updates on the client |
23
+
24
+ There is no standalone "interactive" phase. Any tag with `type: interactive` (refs/interactions) is automatically `fast+interactive`. Tags without an explicit phase are available in all phases.
25
+
26
+ ## Workflow
27
+
28
+ 1. **Read this file** for overview and workflow
29
+ 2. **Discover plugins** โ€” read `materialized-contracts/plugins-index.yaml` to see available plugins and their contracts. Read `materialized-contracts/contracts-index.yaml` for the full contract list.
30
+ 3. **Read plugin.yaml** โ€” for each plugin, read its `plugin.yaml` (at the path from plugins-index) to see contract descriptions and available **actions** (e.g., `searchProducts`, `getCategories`).
31
+ 4. **Read contracts** โ€” read the `.jay-contract` files to understand data shapes, tag types, phases, and props.
32
+ 5. **Read references** โ€” check `references/<plugin>/` for pre-generated discovery data (product catalogs, collection schemas, etc.). These are generated by `jay-stack agent-kit` and contain real data from the site.
33
+ 6. **Discover data** โ€” run `jay-stack params <plugin>/<contract>` for SSG route params, `jay-stack action <plugin>/<action>` for data discovery (action names come from plugin.yaml). Use reference files (step 5) first when available โ€” they're faster than running CLI commands.
34
+ 7. **Create pages** โ€” write `.jay-html` files under `src/pages/` following directory-based routing.
35
+ 8. **Validate** โ€” run `jay-stack validate` to check for errors.
36
+ 9. **Test** โ€” run `jay-stack dev --test-mode` and verify pages render.
37
+
38
+ ## Reference Docs
39
+
40
+ | File | Topic |
41
+ | ---------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
42
+ | [project-structure.md](project-structure.md) | Project layout, styling patterns (CSS themes, design tokens), configuration files |
43
+ | [jay-html-syntax.md](jay-html-syntax.md) | Jay-HTML template syntax: data binding, conditions, loops, headless components |
44
+ | [routing.md](routing.md) | Directory-based routing: page structure, dynamic routes, route priority |
45
+ | [contracts-and-plugins.md](contracts-and-plugins.md) | Reading contracts, plugin.yaml, and the materialized indexes |
46
+ | [cli-commands.md](cli-commands.md) | CLI commands: setup, validate, params, action, dev server |
47
+ | `references/<plugin>/` | Pre-generated discovery data: product catalogs, collection schemas (from `jay-stack agent-kit`) |
48
+
49
+ ## Quick Start
50
+
51
+ ### 1. Discover plugins and contracts
52
+
53
+ Read `materialized-contracts/plugins-index.yaml`:
54
+
55
+ ```yaml
56
+ plugins:
57
+ - name: wix-stores
58
+ path: ./node_modules/@wix/stores
59
+ contracts:
60
+ - name: product-page
61
+ type: static
62
+ path: ./node_modules/@wix/stores/lib/contracts/product-page.jay-contract
63
+ ```
64
+
65
+ ### 2. Read a contract
66
+
67
+ The `.jay-contract` file at the path from the index defines the data shape:
68
+
69
+ ```yaml
70
+ name: ProductWidget
71
+ props:
72
+ - name: productId
73
+ type: string
74
+ required: true
75
+ tags:
76
+ - tag: name
77
+ type: data
78
+ dataType: string
79
+ phase: slow
80
+ - tag: inStock
81
+ type: variant
82
+ dataType: boolean
83
+ phase: fast+interactive
84
+ - tag: addToCart
85
+ type: interactive
86
+ elementType: HTMLButtonElement
87
+ ```
88
+
89
+ ### 3. Create a page
90
+
91
+ Create `src/pages/page.jay-html`:
92
+
93
+ ```html
94
+ <html>
95
+ <head>
96
+ <script
97
+ type="application/jay-headless"
98
+ plugin="wix-stores"
99
+ contract="product-page"
100
+ key="product"
101
+ ></script>
102
+ </head>
103
+ <body>
104
+ <h1>{product.name}</h1>
105
+ <span if="product.inStock">In Stock</span>
106
+ <button ref="product.addToCart">Add to Cart</button>
107
+ </body>
108
+ </html>
109
+ ```
110
+
111
+ ### 4. Validate and run
112
+
113
+ ```bash
114
+ jay-stack validate
115
+ jay-stack dev
116
+ ```
@@ -0,0 +1,229 @@
1
+ # CLI Commands Reference
2
+
3
+ ## jay-stack setup
4
+
5
+ Run plugin setup. Plugins can create configuration files, generate reference data, and validate their prerequisites.
6
+
7
+ ```bash
8
+ # Run setup for all installed plugins
9
+ jay-stack setup
10
+
11
+ # Run setup for a specific plugin
12
+ jay-stack setup wix-stores
13
+
14
+ # Re-run setup (e.g., after config change)
15
+ jay-stack setup wix-data --force
16
+ ```
17
+
18
+ Plugins declare their setup handler in `plugin.yaml`. Setup does two things:
19
+
20
+ 1. **Config templates**: Creates `config/<plugin>.yaml` with placeholder credentials if missing
21
+ 2. **Credential validation**: Attempts to initialize services, reports success or failure
22
+
23
+ Reference data (product catalogs, collection schemas) is generated by `jay-stack agent-kit`, not by setup.
24
+
25
+ Run this after installing new plugins, before `jay-stack agent-kit`.
26
+
27
+ ## jay-stack agent-kit
28
+
29
+ Materialize contracts, generate discovery indexes, and produce plugin reference data. Run this after setup.
30
+
31
+ ```bash
32
+ # Default: writes to agent-kit/
33
+ jay-stack agent-kit
34
+
35
+ # Custom output directory for contracts
36
+ jay-stack agent-kit --output my-output/
37
+
38
+ # List contracts without writing files
39
+ jay-stack agent-kit --list
40
+
41
+ # Filter to specific plugin
42
+ jay-stack agent-kit --plugin wix-stores
43
+
44
+ # Force re-materialization
45
+ jay-stack agent-kit --force
46
+
47
+ # Skip reference data generation
48
+ jay-stack agent-kit --no-references
49
+ ```
50
+
51
+ Outputs:
52
+
53
+ - `materialized-contracts/contracts-index.yaml`
54
+ - `materialized-contracts/plugins-index.yaml`
55
+ - `materialized-contracts/<plugin>/*.jay-contract` (dynamic contracts)
56
+ - `references/<plugin>/` โ€” plugin reference data (product catalogs, collection schemas, etc.)
57
+ - Documentation files (INSTRUCTIONS.md and reference docs)
58
+
59
+ ## jay-stack validate
60
+
61
+ Validate all `.jay-html` and `.jay-contract` files.
62
+
63
+ ```bash
64
+ # Validate entire project
65
+ jay-stack validate
66
+
67
+ # Validate a specific path
68
+ jay-stack validate src/pages/products/
69
+
70
+ # Verbose (per-file status)
71
+ jay-stack validate -v
72
+
73
+ # JSON output
74
+ jay-stack validate --json
75
+ ```
76
+
77
+ Example output:
78
+
79
+ ```
80
+ โœ… Jay Stack validation successful!
81
+ Scanned 5 .jay-html files, 3 .jay-contract files
82
+ No errors found.
83
+ ```
84
+
85
+ On failure:
86
+
87
+ ```
88
+ โŒ Jay Stack validation failed
89
+
90
+ Errors:
91
+ โŒ src/pages/products/page.jay-html
92
+ Unknown ref "nonExistentRef" - not found in contract
93
+
94
+ 1 error(s) found, 7 file(s) valid.
95
+ ```
96
+
97
+ Always run validate after creating or editing jay-html and contract files.
98
+
99
+ ## jay-stack params
100
+
101
+ Discover load param values for SSG route generation.
102
+
103
+ ```bash
104
+ # Discover slug values for product pages
105
+ jay-stack params wix-stores/product-page
106
+
107
+ # YAML output
108
+ jay-stack params wix-stores/product-page --yaml
109
+
110
+ # Verbose
111
+ jay-stack params wix-stores/product-page -v
112
+ ```
113
+
114
+ Format: `<plugin-name>/<contract-name>`
115
+
116
+ Example output:
117
+
118
+ ```json
119
+ [
120
+ { "slug": "ceramic-flower-vase" },
121
+ { "slug": "blue-running-shoes" },
122
+ { "slug": "organic-cotton-tshirt" }
123
+ ]
124
+
125
+ โœ… Found 3 param combination(s)
126
+ ```
127
+
128
+ Use this to discover what param values exist for dynamic routes like `[slug]`. Only works on contracts whose component has `loadParams`.
129
+
130
+ ## jay-stack action
131
+
132
+ Run a plugin action from the CLI. Use to discover data for populating pages.
133
+
134
+ ```bash
135
+ # Run with default input
136
+ jay-stack action wix-stores/searchProducts
137
+
138
+ # Run with input
139
+ jay-stack action wix-stores/searchProducts --input '{"query": "shoes", "limit": 5}'
140
+
141
+ # YAML output
142
+ jay-stack action wix-stores/getCategories --yaml
143
+
144
+ # Verbose
145
+ jay-stack action wix-stores/getProductBySlug --input '{"slug": "blue-shirt"}' -v
146
+ ```
147
+
148
+ Format: `<plugin-name>/<action-name>`
149
+
150
+ Action names are listed in the plugin's `plugin.yaml` under `actions:`.
151
+
152
+ Example output:
153
+
154
+ ```json
155
+ {
156
+ "items": [
157
+ { "_id": "prod-1", "name": "Blue Shirt", "slug": "blue-shirt", "price": 29.99 },
158
+ { "_id": "prod-2", "name": "Red Hat", "slug": "red-hat", "price": 19.99 }
159
+ ],
160
+ "totalCount": 2
161
+ }
162
+ ```
163
+
164
+ If not found, lists available actions:
165
+
166
+ ```
167
+ โŒ Action "badName" not found.
168
+ Available actions: searchProducts, getProductBySlug, getCategories
169
+ ```
170
+
171
+ ## jay-stack dev
172
+
173
+ Start the development server.
174
+
175
+ ```bash
176
+ # Normal dev mode
177
+ jay-stack dev
178
+
179
+ # Test mode (enables health/shutdown endpoints)
180
+ jay-stack dev --test-mode
181
+
182
+ # Auto-timeout (implies test mode)
183
+ jay-stack dev --timeout 60
184
+ ```
185
+
186
+ ### Test mode endpoints
187
+
188
+ | Endpoint | Method | Response |
189
+ | ---------------- | ------ | --------------------------------------------------------------- |
190
+ | `/_jay/health` | GET | `{"status":"ready","port":3300,"editorPort":3301,"uptime":5.2}` |
191
+ | `/_jay/shutdown` | POST | `{"status":"shutting_down"}` |
192
+
193
+ ### Wait for server ready
194
+
195
+ Poll the health endpoint:
196
+
197
+ ```bash
198
+ # Bash
199
+ for i in {1..30}; do
200
+ curl -s http://localhost:3300/_jay/health | grep -q "ready" && break
201
+ sleep 1
202
+ done
203
+ ```
204
+
205
+ ```typescript
206
+ // TypeScript
207
+ async function waitForServer(timeout = 30000): Promise<string> {
208
+ const start = Date.now();
209
+ while (Date.now() - start < timeout) {
210
+ try {
211
+ const res = await fetch('http://localhost:3300/_jay/health');
212
+ if (res.ok) {
213
+ const { port } = await res.json();
214
+ return `http://localhost:${port}`;
215
+ }
216
+ } catch {
217
+ /* not ready */
218
+ }
219
+ await new Promise((r) => setTimeout(r, 500));
220
+ }
221
+ throw new Error('Server not ready');
222
+ }
223
+ ```
224
+
225
+ ### Shutdown
226
+
227
+ ```bash
228
+ curl -X POST http://localhost:3300/_jay/shutdown
229
+ ```
@@ -0,0 +1,229 @@
1
+ # Contracts and Plugins
2
+
3
+ ## Discovery: Plugins Index
4
+
5
+ After running `jay-stack agent-kit`, read `materialized-contracts/plugins-index.yaml`:
6
+
7
+ ```yaml
8
+ materialized_at: '2026-02-09T...'
9
+ jay_stack_version: '1.0.0'
10
+ plugins:
11
+ - name: wix-stores
12
+ path: ./node_modules/@wix/stores
13
+ contracts:
14
+ - name: product-page
15
+ type: static
16
+ path: ./node_modules/@wix/stores/lib/contracts/product-page.jay-contract
17
+ - name: product-search
18
+ type: static
19
+ path: ./node_modules/@wix/stores/lib/contracts/product-search.jay-contract
20
+ ```
21
+
22
+ Fields:
23
+
24
+ - `name` โ€” plugin name (use in `plugin="..."` attributes in jay-html)
25
+ - `path` โ€” path to plugin root (relative to project root)
26
+ - `contracts[].name` โ€” contract name (use in `contract="..."` attributes)
27
+ - `contracts[].type` โ€” `static` (defined in source) or `dynamic` (generated at runtime)
28
+ - `contracts[].path` โ€” path to the `.jay-contract` file you can read
29
+
30
+ ## Discovery: Contracts Index
31
+
32
+ `materialized-contracts/contracts-index.yaml` lists all contracts across all plugins:
33
+
34
+ ```yaml
35
+ contracts:
36
+ - plugin: wix-stores
37
+ name: product-page
38
+ type: static
39
+ path: ./node_modules/@wix/stores/lib/contracts/product-page.jay-contract
40
+ ```
41
+
42
+ ## Reading plugin.yaml
43
+
44
+ Each plugin has a `plugin.yaml` at its root (the `path` from plugins-index):
45
+
46
+ ```yaml
47
+ name: wix-stores
48
+ contracts:
49
+ - name: product-page
50
+ contract: product-page.jay-contract
51
+ component: productPage
52
+ description: Complete headless product page with server-side rendering
53
+ - name: product-search
54
+ contract: product-search.jay-contract
55
+ component: productSearch
56
+ description: Headless product search page
57
+ actions:
58
+ - searchProducts
59
+ - getProductBySlug
60
+ - getCategories
61
+ ```
62
+
63
+ Key fields:
64
+
65
+ - `contracts[].name` โ€” use in `contract="..."` in jay-html
66
+ - `contracts[].description` โ€” what the component does (helps you decide which to use)
67
+ - `actions[]` โ€” action names you can run with `jay-stack action <plugin>/<action>`
68
+
69
+ ## Reading .jay-contract Files
70
+
71
+ Contracts define the data shape (ViewState), interaction points (Refs), and rendering phases.
72
+
73
+ ### Tag Types
74
+
75
+ | Type | Purpose | Jay-HTML Usage |
76
+ | --------------------------------- | -------------------------------- | --------------------------------- |
77
+ | `data` | Read-only data value | `{tagName}` binding |
78
+ | `variant` | Enum or boolean for conditions | `if="tagName===value"` |
79
+ | `interactive` | Element ref for user interaction | `ref="tagName"` |
80
+ | `[data, interactive]` | Both data and interactive | `{tagName}` + `ref="tagName"` |
81
+ | `sub-contract` | Nested object | `{parent.child}` |
82
+ | `sub-contract` + `repeated: true` | Array for loops | `forEach="tagName" trackBy="..."` |
83
+
84
+ ### Phases
85
+
86
+ | Phase | When | Example |
87
+ | ------------------ | ------------------------ | ----------------------------------------- |
88
+ | `slow` | Build time (SSG) | Product name, description, static content |
89
+ | `fast` | Request time (SSR) | Live pricing, stock status |
90
+ | `fast+interactive` | Request + client updates | Price that updates on variant selection |
91
+ | _(no phase)_ | All phases | Available everywhere |
92
+
93
+ ### Props
94
+
95
+ Components that accept props:
96
+
97
+ ```yaml
98
+ props:
99
+ - name: productId
100
+ type: string
101
+ required: true
102
+ description: The ID of the product to display
103
+ ```
104
+
105
+ Use in jay-html: `<jay:contract-name productId="value">`.
106
+
107
+ ### Params
108
+
109
+ Page components with dynamic routes:
110
+
111
+ ```yaml
112
+ params:
113
+ slug: string
114
+ ```
115
+
116
+ Params are always strings. Discover values with `jay-stack params`.
117
+
118
+ ### Linked Sub-Contracts
119
+
120
+ A sub-contract can reference another contract file:
121
+
122
+ ```yaml
123
+ - tag: mediaGallery
124
+ type: sub-contract
125
+ phase: fast+interactive
126
+ link: ./media-gallery # refers to media-gallery.jay-contract in same directory
127
+ ```
128
+
129
+ Read the linked file to see the nested tags.
130
+
131
+ ## Contract Examples
132
+
133
+ **Simple component with props:**
134
+
135
+ ```yaml
136
+ name: ProductWidget
137
+ props:
138
+ - name: productId
139
+ type: string
140
+ required: true
141
+ tags:
142
+ - tag: name
143
+ type: data
144
+ dataType: string
145
+ phase: slow
146
+ - tag: price
147
+ type: data
148
+ dataType: number
149
+ phase: slow
150
+ - tag: inStock
151
+ type: variant
152
+ dataType: boolean
153
+ phase: fast+interactive
154
+ - tag: addToCart
155
+ type: interactive
156
+ elementType: HTMLButtonElement
157
+ ```
158
+
159
+ **Page with nested loops:**
160
+
161
+ ```yaml
162
+ name: product-page
163
+ tags:
164
+ - tag: productName
165
+ type: data
166
+ dataType: string
167
+ - tag: price
168
+ type: data
169
+ dataType: string
170
+ phase: fast+interactive
171
+ - tag: addToCartButton
172
+ type: interactive
173
+ elementType: HTMLButtonElement
174
+ - tag: options
175
+ type: sub-contract
176
+ repeated: true
177
+ trackBy: _id
178
+ tags:
179
+ - tag: _id
180
+ type: data
181
+ dataType: string
182
+ - tag: name
183
+ type: data
184
+ dataType: string
185
+ - tag: choices
186
+ type: sub-contract
187
+ repeated: true
188
+ trackBy: choiceId
189
+ tags:
190
+ - tag: choiceId
191
+ type: data
192
+ dataType: string
193
+ - tag: name
194
+ type: data
195
+ dataType: string
196
+ - tag: isSelected
197
+ type: variant
198
+ dataType: boolean
199
+ phase: fast+interactive
200
+ - tag: choiceButton
201
+ type: interactive
202
+ elementType: HTMLButtonElement
203
+ ```
204
+
205
+ ## From Contract to Jay-HTML
206
+
207
+ ### Step by step
208
+
209
+ 1. **Read the contract** โ€” identify tags, their types, and phases
210
+ 2. **Map `data` tags** โ†’ `{tagName}` bindings
211
+ 3. **Map `variant` tags** โ†’ `if="tagName===value"` conditions
212
+ 4. **Map `interactive` tags** โ†’ `ref="tagName"` on appropriate element types
213
+ 5. **Map `sub-contract` + `repeated: true`** โ†’ `forEach="tagName" trackBy="..."` loops
214
+ 6. **Map nested `sub-contract`** โ†’ dotted paths or nested context inside forEach
215
+ 7. **Respect phases** โ€” don't assume fast-only data is available at build time
216
+
217
+ ### Quick mapping
218
+
219
+ | Contract Tag | Jay-HTML |
220
+ | ---------------------------------------------------------------- | --------------------------------------------- |
221
+ | `{tag: title, type: data}` | `<h1>{title}</h1>` |
222
+ | `{tag: active, type: variant, dataType: boolean}` | `<span if="active">Active</span>` |
223
+ | `{tag: status, type: variant, dataType: "enum (A \| B)"}` | `<div if="status===A">...</div>` |
224
+ | `{tag: btn, type: interactive, elementType: HTMLButtonElement}` | `<button ref="btn">Click</button>` |
225
+ | `{tag: link, type: interactive, elementType: HTMLAnchorElement}` | `<a ref="link">Go</a>` |
226
+ | `{tag: input, type: interactive, elementType: HTMLInputElement}` | `<input ref="input" value="{val}" />` |
227
+ | `{tag: sel, type: interactive, elementType: HTMLSelectElement}` | `<select ref="sel">...</select>` |
228
+ | `{tag: items, type: sub-contract, repeated: true, trackBy: id}` | `<div forEach="items" trackBy="id">...</div>` |
229
+ | `{tag: detail, type: sub-contract}` | `{detail.fieldName}` |