@jay-framework/jay-stack-cli 0.10.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.
@@ -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}` |
@@ -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
+ ```