@jay-framework/jay-stack-cli 0.11.0 → 0.13.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.
@@ -0,0 +1,293 @@
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
+ actions:
21
+ - name: searchProducts
22
+ description: Search products with text/filter/sort/pagination
23
+ path: ./node_modules/@wix/stores/lib/actions/search-products.jay-action
24
+ - name: getProductBySlug
25
+ description: Get a single product by URL slug
26
+ path: ./node_modules/@wix/stores/lib/actions/get-product-by-slug.jay-action
27
+ ```
28
+
29
+ Fields:
30
+
31
+ - `name` — plugin name (use in `plugin="..."` attributes in jay-html)
32
+ - `path` — path to plugin root (relative to project root)
33
+ - `contracts[].name` — contract name (use in `contract="..."` attributes)
34
+ - `contracts[].type` — `static` (defined in source) or `dynamic` (generated at runtime)
35
+ - `contracts[].path` — path to the `.jay-contract` file you can read
36
+ - `actions[].name` — action name (use with `jay-stack action <plugin>/<action>`)
37
+ - `actions[].description` — short description of what the action does
38
+ - `actions[].path` — path to the `.jay-action` file with full input/output schemas
39
+
40
+ ## Discovery: Contracts Index
41
+
42
+ `materialized-contracts/contracts-index.yaml` lists all contracts across all plugins:
43
+
44
+ ```yaml
45
+ contracts:
46
+ - plugin: wix-stores
47
+ name: product-page
48
+ type: static
49
+ path: ./node_modules/@wix/stores/lib/contracts/product-page.jay-contract
50
+ ```
51
+
52
+ ## Reading plugin.yaml
53
+
54
+ Each plugin has a `plugin.yaml` at its root (the `path` from plugins-index):
55
+
56
+ ```yaml
57
+ name: wix-stores
58
+ contracts:
59
+ - name: product-page
60
+ contract: product-page.jay-contract
61
+ component: productPage
62
+ description: Complete headless product page with server-side rendering
63
+ - name: product-search
64
+ contract: product-search.jay-contract
65
+ component: productSearch
66
+ description: Headless product search page
67
+ actions:
68
+ - name: searchProducts
69
+ action: search-products.jay-action
70
+ - name: getProductBySlug
71
+ action: get-product-by-slug.jay-action
72
+ - name: getCategories
73
+ action: get-categories.jay-action
74
+ ```
75
+
76
+ Key fields:
77
+
78
+ - `contracts[].name` — use in `contract="..."` in jay-html
79
+ - `contracts[].description` — what the component does (helps you decide which to use)
80
+ - `actions[].name` — action name (use with `jay-stack action <plugin>/<action>`)
81
+ - `actions[].action` — path to `.jay-action` file with full metadata (description, input/output schemas)
82
+
83
+ ## Reading .jay-contract Files
84
+
85
+ Contracts define the data shape (ViewState), interaction points (Refs), and rendering phases.
86
+
87
+ ### Tag Types
88
+
89
+ | Type | Purpose | Jay-HTML Usage |
90
+ | --------------------------------- | -------------------------------- | --------------------------------- |
91
+ | `data` | Read-only data value | `{tagName}` binding |
92
+ | `variant` | Enum or boolean for conditions | `if="tagName===value"` |
93
+ | `interactive` | Element ref for user interaction | `ref="tagName"` |
94
+ | `[data, interactive]` | Both data and interactive | `{tagName}` + `ref="tagName"` |
95
+ | `sub-contract` | Nested object | `{parent.child}` |
96
+ | `sub-contract` + `repeated: true` | Array for loops | `forEach="tagName" trackBy="..."` |
97
+
98
+ ### Phases
99
+
100
+ | Phase | When | Example |
101
+ | ------------------ | ------------------------ | ----------------------------------------- |
102
+ | `slow` | Build time (SSG) | Product name, description, static content |
103
+ | `fast` | Request time (SSR) | Live pricing, stock status |
104
+ | `fast+interactive` | Request + client updates | Price that updates on variant selection |
105
+ | _(no phase)_ | All phases | Available everywhere |
106
+
107
+ ### Props
108
+
109
+ Components that accept props:
110
+
111
+ ```yaml
112
+ props:
113
+ - name: productId
114
+ type: string
115
+ required: true
116
+ description: The ID of the product to display
117
+ ```
118
+
119
+ Use in jay-html: `<jay:contract-name productId="value">`.
120
+
121
+ ### Params
122
+
123
+ Page components with dynamic routes:
124
+
125
+ ```yaml
126
+ params:
127
+ slug: string
128
+ ```
129
+
130
+ Params are always strings. Discover values with `jay-stack params`.
131
+
132
+ ### Linked Sub-Contracts
133
+
134
+ A sub-contract can reference another contract file:
135
+
136
+ ```yaml
137
+ - tag: mediaGallery
138
+ type: sub-contract
139
+ phase: fast+interactive
140
+ link: ./media-gallery # refers to media-gallery.jay-contract in same directory
141
+ ```
142
+
143
+ Read the linked file to see the nested tags.
144
+
145
+ ## Contract Examples
146
+
147
+ **Simple component with props:**
148
+
149
+ ```yaml
150
+ name: ProductWidget
151
+ props:
152
+ - name: productId
153
+ type: string
154
+ required: true
155
+ tags:
156
+ - tag: name
157
+ type: data
158
+ dataType: string
159
+ phase: slow
160
+ - tag: price
161
+ type: data
162
+ dataType: number
163
+ phase: slow
164
+ - tag: inStock
165
+ type: variant
166
+ dataType: boolean
167
+ phase: fast+interactive
168
+ - tag: addToCart
169
+ type: interactive
170
+ elementType: HTMLButtonElement
171
+ ```
172
+
173
+ **Page with nested loops:**
174
+
175
+ ```yaml
176
+ name: product-page
177
+ tags:
178
+ - tag: productName
179
+ type: data
180
+ dataType: string
181
+ - tag: price
182
+ type: data
183
+ dataType: string
184
+ phase: fast+interactive
185
+ - tag: addToCartButton
186
+ type: interactive
187
+ elementType: HTMLButtonElement
188
+ - tag: options
189
+ type: sub-contract
190
+ repeated: true
191
+ trackBy: _id
192
+ tags:
193
+ - tag: _id
194
+ type: data
195
+ dataType: string
196
+ - tag: name
197
+ type: data
198
+ dataType: string
199
+ - tag: choices
200
+ type: sub-contract
201
+ repeated: true
202
+ trackBy: choiceId
203
+ tags:
204
+ - tag: choiceId
205
+ type: data
206
+ dataType: string
207
+ - tag: name
208
+ type: data
209
+ dataType: string
210
+ - tag: isSelected
211
+ type: variant
212
+ dataType: boolean
213
+ phase: fast+interactive
214
+ - tag: choiceButton
215
+ type: interactive
216
+ elementType: HTMLButtonElement
217
+ ```
218
+
219
+ ## Reading .jay-action Files
220
+
221
+ Actions with `.jay-action` files have rich metadata: name, description, and typed input/output schemas. Read the file at the `path` from `plugins-index.yaml` (or from `plugin.yaml`'s `actions[].action` field).
222
+
223
+ ### .jay-action Format
224
+
225
+ ```yaml
226
+ name: searchProducts
227
+ description: Search products with text/filter/sort/pagination
228
+
229
+ import:
230
+ productCard: product-card.jay-contract
231
+
232
+ inputSchema:
233
+ query: string
234
+ filters?:
235
+ inStockOnly?: boolean
236
+ minPrice?: number
237
+ maxPrice?: number
238
+ sortBy?: enum(relevance | price_asc | price_desc)
239
+ pageSize?: number
240
+
241
+ outputSchema:
242
+ products:
243
+ - productCard
244
+ totalCount: number
245
+ hasMore: boolean
246
+ ```
247
+
248
+ ### Jay-Type Notation
249
+
250
+ Schemas use a compact type notation:
251
+
252
+ | Notation | Meaning |
253
+ | --- | --- |
254
+ | `propName: string` | Required string property |
255
+ | `propName?: number` | Optional number property |
256
+ | `propName: boolean` | Required boolean |
257
+ | `propName: enum(a \| b \| c)` | Required enum |
258
+ | `propName:` + nested block | Nested object |
259
+ | `propName:` + `- childProp: type` | Array of objects (YAML list) |
260
+ | `propName: importedName` | Type from `import:` block (references a `.jay-contract`) |
261
+ | `- importedName` | Array of imported type |
262
+
263
+ ### Using Action Metadata
264
+
265
+ 1. **Discovery** — read `plugins-index.yaml` for action names and descriptions
266
+ 2. **Details** — read the `.jay-action` file at the path for full input/output schemas
267
+ 3. **Run** — use `jay-stack action <plugin>/<action> --input '{...}'` with a JSON body matching the inputSchema
268
+
269
+ ## From Contract to Jay-HTML
270
+
271
+ ### Step by step
272
+
273
+ 1. **Read the contract** — identify tags, their types, and phases
274
+ 2. **Map `data` tags** → `{tagName}` bindings
275
+ 3. **Map `variant` tags** → `if="tagName===value"` conditions
276
+ 4. **Map `interactive` tags** → `ref="tagName"` on appropriate element types
277
+ 5. **Map `sub-contract` + `repeated: true`** → `forEach="tagName" trackBy="..."` loops
278
+ 6. **Map nested `sub-contract`** → dotted paths or nested context inside forEach
279
+ 7. **Respect phases** — don't assume fast-only data is available at build time
280
+
281
+ ### Quick mapping
282
+
283
+ | Contract Tag | Jay-HTML |
284
+ | ---------------------------------------------------------------- | --------------------------------------------- |
285
+ | `{tag: title, type: data}` | `<h1>{title}</h1>` |
286
+ | `{tag: active, type: variant, dataType: boolean}` | `<span if="active">Active</span>` |
287
+ | `{tag: status, type: variant, dataType: "enum (A \| B)"}` | `<div if="status===A">...</div>` |
288
+ | `{tag: btn, type: interactive, elementType: HTMLButtonElement}` | `<button ref="btn">Click</button>` |
289
+ | `{tag: link, type: interactive, elementType: HTMLAnchorElement}` | `<a ref="link">Go</a>` |
290
+ | `{tag: input, type: interactive, elementType: HTMLInputElement}` | `<input ref="input" value="{val}" />` |
291
+ | `{tag: sel, type: interactive, elementType: HTMLSelectElement}` | `<select ref="sel">...</select>` |
292
+ | `{tag: items, type: sub-contract, repeated: true, trackBy: id}` | `<div forEach="items" trackBy="id">...</div>` |
293
+ | `{tag: detail, type: sub-contract}` | `{detail.fieldName}` |
@@ -0,0 +1,312 @@
1
+ # Jay-HTML Syntax Reference
2
+
3
+ ## File Structure
4
+
5
+ A `.jay-html` file is standard HTML with jay-specific extensions.
6
+
7
+ ```html
8
+ <html>
9
+ <head>
10
+ <!-- Page contract (optional — defines page-level data) -->
11
+ <script type="application/jay-data" contract="./page.jay-contract"></script>
12
+
13
+ <!-- Headless component imports -->
14
+ <script type="application/jay-headless" plugin="..." contract="..." key="..."></script>
15
+
16
+ <!-- Styles -->
17
+ <style>
18
+ /* inline CSS */
19
+ </style>
20
+ <link rel="stylesheet" href="../../styles/theme.css" />
21
+ </head>
22
+ <body>
23
+ <!-- Template with data bindings -->
24
+ <h1>{title}</h1>
25
+ </body>
26
+ </html>
27
+ ```
28
+
29
+ ## Data Binding
30
+
31
+ Use `{expression}` to bind contract data:
32
+
33
+ ```html
34
+ <h1>{productName}</h1>
35
+ <!-- simple -->
36
+ <span>{product.price}</span>
37
+ <!-- nested via key -->
38
+ <div style="color: {textColor}">{msg}</div>
39
+ <!-- in attributes -->
40
+ <a href="/products/{slug}">{name}</a>
41
+ <!-- interpolated in attr values -->
42
+ ```
43
+
44
+ ## Conditional Rendering
45
+
46
+ Use the `if` attribute:
47
+
48
+ ```html
49
+ <span if="inStock">In Stock</span>
50
+ <span if="!inStock">Out of Stock</span>
51
+ <div if="type===physical">Ships to your door</div>
52
+ <div if="type===virtual">Instant download</div>
53
+ ```
54
+
55
+ Rules:
56
+
57
+ - Boolean: `if="tagName"` / `if="!tagName"`
58
+ - Enum variant: `if="tagName===value"` / `if="tagName!==value"` (no quotes around value)
59
+ - Negation: `!` prefix
60
+
61
+ ## Loops (forEach / trackBy)
62
+
63
+ Iterate over repeated sub-contracts:
64
+
65
+ ```html
66
+ <li forEach="products" trackBy="id">
67
+ <a href="/products/{slug}">
68
+ <div>{name}</div>
69
+ <div>{price}</div>
70
+ </a>
71
+ </li>
72
+ ```
73
+
74
+ - `forEach` — the repeated tag name from the contract
75
+ - `trackBy` — stable unique key for each item (must match contract's trackBy)
76
+ - Inside the loop, bindings resolve to the **current item's** tags
77
+
78
+ **Nested loops:**
79
+
80
+ ```html
81
+ <div forEach="options" trackBy="_id">
82
+ <h3>{name}</h3>
83
+ <div forEach="choices" trackBy="choiceId">
84
+ <button ref="choiceButton">{name}</button>
85
+ </div>
86
+ </div>
87
+ ```
88
+
89
+ ## Refs (Interactions)
90
+
91
+ Map elements to contract `interactive` tags using `ref`:
92
+
93
+ ```html
94
+ <button ref="addToCart">Add to Cart</button>
95
+ <input value="{quantity}" ref="quantityInput" />
96
+ <a ref="productLink" href="/products/{slug}">{name}</a>
97
+ <select ref="sizeSelector">
98
+ ...
99
+ </select>
100
+ ```
101
+
102
+ Match the element type to the contract's `elementType`:
103
+
104
+ - `HTMLButtonElement` → `<button>`
105
+ - `HTMLInputElement` → `<input>`
106
+ - `HTMLAnchorElement` → `<a>`
107
+ - `HTMLSelectElement` → `<select>`
108
+
109
+ **Key-based headless refs** — prefix with the key:
110
+
111
+ ```html
112
+ <button ref="rating.submitButton">Submit</button> <button ref="mt.happy">+1 Happy</button>
113
+ ```
114
+
115
+ **Refs inside forEach** — use the tag path from the contract:
116
+
117
+ ```html
118
+ <div forEach="options" trackBy="_id">
119
+ <div forEach="choices" trackBy="choiceId">
120
+ <button ref="choiceButton">{name}</button>
121
+ </div>
122
+ </div>
123
+ ```
124
+
125
+ ## Headless Components
126
+
127
+ ### Pattern 1: Key-Based Import
128
+
129
+ Data merged into parent ViewState under a key. Use when you have **one instance** of a component per page.
130
+
131
+ Declare in `<head>` with a `key` attribute:
132
+
133
+ ```html
134
+ <head>
135
+ <script
136
+ type="application/jay-headless"
137
+ plugin="wix-stores"
138
+ contract="product-page"
139
+ key="productPage"
140
+ ></script>
141
+ </head>
142
+ ```
143
+
144
+ Access data and refs with the key prefix:
145
+
146
+ ```html
147
+ <h1>{productPage.productName}</h1>
148
+ <span>{productPage.price}</span>
149
+ <button ref="productPage.addToCartButton">Add to Cart</button>
150
+
151
+ <!-- Nested repeated sub-contracts -->
152
+ <div forEach="productPage.options" trackBy="_id">
153
+ <h3>{name}</h3>
154
+ <div forEach="choices" trackBy="choiceId">
155
+ <button ref="choiceButton">{name}</button>
156
+ </div>
157
+ </div>
158
+ ```
159
+
160
+ ### Pattern 2: Instance-Based (jay: prefix)
161
+
162
+ Multiple instances with props and inline templates. Use when you need **multiple instances** or need to pass **props**.
163
+
164
+ Declare in `<head>` **without** a `key`:
165
+
166
+ ```html
167
+ <head>
168
+ <script
169
+ type="application/jay-headless"
170
+ plugin="product-widget"
171
+ contract="product-widget"
172
+ ></script>
173
+ </head>
174
+ ```
175
+
176
+ Use `<jay:contract-name>` tags with props:
177
+
178
+ ```html
179
+ <!-- Static props — each instance renders independently -->
180
+ <jay:product-widget productId="prod-1">
181
+ <h3>{name}</h3>
182
+ <div>${price}</div>
183
+ <button ref="addToCart">Add</button>
184
+ </jay:product-widget>
185
+
186
+ <jay:product-widget productId="prod-2">
187
+ <h3>{name}</h3>
188
+ <button ref="addToCart">Add</button>
189
+ </jay:product-widget>
190
+ ```
191
+
192
+ **With forEach** (dynamic props from parent data):
193
+
194
+ ```html
195
+ <div forEach="featuredProducts" trackBy="_id">
196
+ <jay:product-widget productId="{_id}">
197
+ <h3>{name}</h3>
198
+ <div>${price}</div>
199
+ <button ref="addToCart">Add</button>
200
+ </jay:product-widget>
201
+ </div>
202
+ ```
203
+
204
+ Inside `<jay:...>`, bindings resolve to **that instance's** contract tags (not the parent).
205
+
206
+ ## Page-Level Contract
207
+
208
+ A page can define its own data contract:
209
+
210
+ ```html
211
+ <script type="application/jay-data" contract="./page.jay-contract"></script>
212
+ ```
213
+
214
+ Tags from the page contract are bound directly (no key prefix).
215
+
216
+ ## Styling
217
+
218
+ **Inline `<style>`:**
219
+
220
+ ```html
221
+ <head>
222
+ <style>
223
+ .product-card {
224
+ border: 1px solid #ccc;
225
+ padding: 16px;
226
+ }
227
+ .price {
228
+ font-weight: bold;
229
+ color: #2d7d2d;
230
+ }
231
+ </style>
232
+ </head>
233
+ ```
234
+
235
+ **External stylesheets:**
236
+
237
+ ```html
238
+ <link rel="stylesheet" href="../../styles/theme.css" />
239
+ ```
240
+
241
+ **Dynamic style bindings:**
242
+
243
+ ```html
244
+ <div style="color: {textColor}; width: {width}px">styled</div>
245
+ ```
246
+
247
+ ## Complete Example
248
+
249
+ A homepage with key-based and instance-based headless components:
250
+
251
+ ```html
252
+ <html>
253
+ <head>
254
+ <script
255
+ type="application/jay-headless"
256
+ plugin="mood-tracker"
257
+ contract="mood-tracker"
258
+ key="mt"
259
+ ></script>
260
+ <script
261
+ type="application/jay-headless"
262
+ plugin="product-widget"
263
+ contract="product-widget"
264
+ ></script>
265
+ <script type="application/jay-data" contract="./page.jay-contract"></script>
266
+ <style>
267
+ .section {
268
+ margin: 20px 0;
269
+ padding: 10px;
270
+ }
271
+ .product-card {
272
+ border: 1px solid #ccc;
273
+ padding: 10px;
274
+ display: inline-block;
275
+ }
276
+ </style>
277
+ </head>
278
+ <body>
279
+ <h1>Homepage</h1>
280
+
281
+ <!-- Key-based: mood tracker -->
282
+ <div class="section">
283
+ <div>Happy: {mt.happy} <button ref="mt.happy">more</button></div>
284
+ <span if="mt.currentMood === happy">:)</span>
285
+ <span if="mt.currentMood === sad">:(</span>
286
+ </div>
287
+
288
+ <!-- Instance-based: static product widgets -->
289
+ <div class="section">
290
+ <jay:product-widget productId="1">
291
+ <h3>{name}</h3>
292
+ <div>${price}</div>
293
+ <span if="inStock">In Stock</span>
294
+ <button ref="addToCart">Add</button>
295
+ </jay:product-widget>
296
+ </div>
297
+
298
+ <!-- Instance-based: dynamic from forEach -->
299
+ <div class="section">
300
+ <div forEach="featuredProducts" trackBy="_id">
301
+ <div class="product-card">
302
+ <jay:product-widget productId="{_id}">
303
+ <h3>{name}</h3>
304
+ <div>${price}</div>
305
+ <button ref="addToCart">Add</button>
306
+ </jay:product-widget>
307
+ </div>
308
+ </div>
309
+ </div>
310
+ </body>
311
+ </html>
312
+ ```