@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.
@@ -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
+ ```
@@ -0,0 +1,242 @@
1
+ # Project Structure
2
+
3
+ ## Directory Layout
4
+
5
+ A jay-stack project follows this structure:
6
+
7
+ ```
8
+ my-project/
9
+ ├── .jay # Dev server configuration (optional)
10
+ ├── package.json # Dependencies and scripts
11
+ ├── tsconfig.json # TypeScript config
12
+ ├── public/ # Static assets (images, fonts, etc.)
13
+ ├── config/ # Plugin configuration (generated by jay-stack setup)
14
+ │ ├── project.conf.yaml # Project metadata (name, etc.)
15
+ │ └── <plugin-name>.yaml # Plugin-specific config files
16
+ ├── src/
17
+ │ ├── pages/ # Pages (directory-based routing)
18
+ │ │ ├── page.jay-html # Homepage → /
19
+ │ │ ├── page.jay-contract # Homepage contract (optional)
20
+ │ │ ├── products/
21
+ │ │ │ ├── page.jay-html # Products list → /products
22
+ │ │ │ └── [slug]/
23
+ │ │ │ └── page.jay-html # Product detail → /products/:slug
24
+ │ │ └── cart/
25
+ │ │ └── page.jay-html # Cart → /cart
26
+ │ └── styles/
27
+ │ └── theme.css # Global theme stylesheet
28
+ └── agent-kit/ # Generated by jay-stack agent-kit
29
+ ├── INSTRUCTIONS.md
30
+ ├── materialized-contracts/ # Contracts and indexes
31
+ └── references/ # Plugin reference data (product catalogs, schemas)
32
+ └── <plugin-name>/ # Per-plugin discovery data
33
+ ```
34
+
35
+ ## What You Create
36
+
37
+ As an agent building pages, you typically create:
38
+
39
+ 1. **`src/pages/**/\*.jay-html`\*\* — Page templates (the main output)
40
+ 2. **`src/pages/**/\*.jay-contract`\*\* — Page-level contracts (when the page has its own data)
41
+ 3. **`src/styles/*.css`** — Theme stylesheet (one per project, reused across pages)
42
+
43
+ You do **not** need to create: `package.json`, `tsconfig.json`, `.jay`, `page.conf.yaml`, `src/init.ts`, server actions, or services — these are set up by the project scaffolding, the Figma plugin, or provided by plugins.
44
+
45
+ ## Styling
46
+
47
+ ### Global Theme (src/styles/)
48
+
49
+ Each project has a theme CSS file in `src/styles/` with CSS custom properties (design tokens):
50
+
51
+ ```css
52
+ :root {
53
+ /* Colors */
54
+ --bg-primary: #faf8f5;
55
+ --bg-card: #ffffff;
56
+ --text-primary: #2d2a26;
57
+ --text-secondary: #6b665e;
58
+ --accent: #c45c3e;
59
+ --accent-hover: #d4704f;
60
+ --border: #e8e4dd;
61
+
62
+ /* Typography */
63
+ --font-serif: 'Cormorant Garamond', Georgia, serif;
64
+ --font-sans: 'DM Sans', -apple-system, sans-serif;
65
+
66
+ /* Spacing & Layout */
67
+ --radius-md: 10px;
68
+ --radius-lg: 14px;
69
+ --container-max: 1400px;
70
+ --page-padding: 48px;
71
+ --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.04);
72
+ --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.06);
73
+ }
74
+ ```
75
+
76
+ The theme provides reusable CSS classes for common UI patterns:
77
+
78
+ - **Layout**: `.container`, `.container-narrow`
79
+ - **Cards**: `.card`, `.card-elevated`
80
+ - **Buttons**: `.btn`, `.btn-primary`, `.btn-secondary`, `.btn-ghost`, `.btn-sm`, `.btn-lg`
81
+ - **Forms**: `.input`, `.select`, `.checkbox`
82
+ - **Badges**: `.badge`, `.badge-accent`, `.badge-success`, `.badge-error`
83
+ - **Typography**: `.page-title`, `.section-title`, `.label`
84
+ - **Product components**: `.product-card`, `.product-card-image`, `.product-card-content`, `.product-card-price`
85
+
86
+ ### Linking the Theme from Pages
87
+
88
+ Use a relative path from the page to the theme file:
89
+
90
+ ```html
91
+ <!-- From src/pages/page.jay-html (depth 1) -->
92
+ <link rel="stylesheet" href="../styles/theme.css" />
93
+
94
+ <!-- From src/pages/products/page.jay-html (depth 2) -->
95
+ <link rel="stylesheet" href="../../styles/theme.css" />
96
+
97
+ <!-- From src/pages/products/[slug]/page.jay-html (depth 3) -->
98
+ <link rel="stylesheet" href="../../../styles/theme.css" />
99
+ ```
100
+
101
+ ### Page-Specific Styles
102
+
103
+ Add page-specific styles in `<style>` tags within the jay-html `<head>`:
104
+
105
+ ```html
106
+ <head>
107
+ <link rel="stylesheet" href="../../styles/theme.css" />
108
+ <style>
109
+ .hero {
110
+ padding: 80px 0;
111
+ text-align: center;
112
+ }
113
+ .featured-grid {
114
+ display: grid;
115
+ grid-template-columns: repeat(3, 1fr);
116
+ gap: 24px;
117
+ }
118
+ </style>
119
+ </head>
120
+ ```
121
+
122
+ ### External Fonts
123
+
124
+ Import fonts in the theme CSS or via `<link>` in jay-html:
125
+
126
+ ```css
127
+ /* In theme.css */
128
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
129
+ ```
130
+
131
+ ### Responsive Design
132
+
133
+ Use media queries in the theme CSS or page-specific styles:
134
+
135
+ ```css
136
+ @media (max-width: 1024px) {
137
+ .product-page-layout {
138
+ grid-template-columns: 1fr;
139
+ }
140
+ }
141
+ @media (max-width: 768px) {
142
+ .products-grid {
143
+ grid-template-columns: 1fr;
144
+ }
145
+ }
146
+ ```
147
+
148
+ ## Configuration Files
149
+
150
+ ### config/ (Plugin Configuration)
151
+
152
+ The `config/` folder holds plugin configuration files, typically generated by `jay-stack setup`:
153
+
154
+ ```
155
+ config/
156
+ ├── project.conf.yaml # Project name and metadata
157
+ └── wix-data.yaml # Plugin-specific config (auto-generated by setup)
158
+ ```
159
+
160
+ Plugins create their config files here during setup. The location is configured via `configBase` in the `.jay` file (defaults to `./config`).
161
+
162
+ You generally don't need to create files here manually — plugins handle this via `jay-stack setup`. However, you may need to read these files to understand how a plugin is configured.
163
+
164
+ ### .jay (Dev Server Config)
165
+
166
+ Optional YAML file at project root:
167
+
168
+ ```yaml
169
+ devServer:
170
+ portRange:
171
+ - 3000
172
+ - 3100
173
+ pagesBase: ./src/pages
174
+ componentsBase: ./src/components
175
+ publicFolder: ./public
176
+ editorServer:
177
+ portRange:
178
+ - 3101
179
+ - 3200
180
+ ```
181
+
182
+ ### package.json (Scripts)
183
+
184
+ Standard scripts for jay-stack projects:
185
+
186
+ ```json
187
+ {
188
+ "scripts": {
189
+ "dev": "jay-stack-cli dev",
190
+ "validate": "jay-stack-cli validate",
191
+ "definitions": "jay-cli definitions src",
192
+ "build": "npm run definitions"
193
+ }
194
+ }
195
+ ```
196
+
197
+ ## Common Page Patterns
198
+
199
+ ### Product List / Search Page
200
+
201
+ Uses a product search contract with filters, sorting, and pagination:
202
+
203
+ ```
204
+ src/pages/products/
205
+ └── page.jay-html # Search input, filters sidebar, product grid, load more
206
+ ```
207
+
208
+ ### Product Detail Page (Dynamic Route)
209
+
210
+ Uses a product page contract with media gallery, options, and add-to-cart:
211
+
212
+ ```
213
+ src/pages/products/[slug]/
214
+ └── page.jay-html # Gallery, options, quantity, actions
215
+ ```
216
+
217
+ ### Category Page
218
+
219
+ Uses a category contract with product grid:
220
+
221
+ ```
222
+ src/pages/categories/[slug]/
223
+ └── page.jay-html # Category hero, product grid, load more
224
+ ```
225
+
226
+ ### Cart Page
227
+
228
+ Uses a cart contract with line items and checkout:
229
+
230
+ ```
231
+ src/pages/cart/
232
+ └── page.jay-html # Line items, quantity controls, summary, coupon, checkout
233
+ ```
234
+
235
+ ### Homepage
236
+
237
+ Combines multiple headless components:
238
+
239
+ ```
240
+ src/pages/
241
+ └── page.jay-html # Hero, featured products, categories, etc.
242
+ ```
@@ -0,0 +1,112 @@
1
+ # Directory-Based Routing
2
+
3
+ ## Route Structure
4
+
5
+ Pages live under `src/pages/`. Directory names become URL segments.
6
+
7
+ ```
8
+ src/pages/
9
+ ├── page.jay-html → /
10
+ ├── about/
11
+ │ └── page.jay-html → /about
12
+ ├── products/
13
+ │ ├── page.jay-html → /products
14
+ │ └── [slug]/
15
+ │ └── page.jay-html → /products/:slug
16
+ ├── blog/
17
+ │ ├── page.jay-html → /blog
18
+ │ └── [[slug]]/
19
+ │ └── page.jay-html → /blog/:slug (optional)
20
+ └── files/
21
+ └── [...path]/
22
+ └── page.jay-html → /files/* (catch-all)
23
+ ```
24
+
25
+ ## Dynamic Routes
26
+
27
+ | Syntax | Meaning | Example |
28
+ | ------------ | ------------------ | --------------------------------------- |
29
+ | `[param]` | Required parameter | `[slug]` → `/products/:slug` |
30
+ | `[[param]]` | Optional parameter | `[[slug]]` → `/blog` or `/blog/my-post` |
31
+ | `[...param]` | Catch-all | `[...path]` → matches any sub-path |
32
+
33
+ ## Route Priority
34
+
35
+ Static routes match before dynamic routes (most specific first):
36
+
37
+ 1. **Static segments** (exact match) — highest priority
38
+ 2. **`[param]`** — required dynamic param
39
+ 3. **`[[param]]`** — optional param
40
+ 4. **`[...param]`** — catch-all — lowest priority
41
+
42
+ Static override alongside a dynamic route:
43
+
44
+ ```
45
+ src/pages/products/
46
+ ├── [slug]/page.jay-html # dynamic: /products/:slug
47
+ └── ceramic-flower-vase/page.jay-html # static override for this specific product
48
+ ```
49
+
50
+ ## Page Files
51
+
52
+ Each page directory can contain:
53
+
54
+ | File | Purpose |
55
+ | ------------------- | ----------------------------------- |
56
+ | `page.jay-html` | Template (required for rendering) |
57
+ | `page.jay-contract` | Page-level data contract (optional) |
58
+
59
+ ### page.jay-contract
60
+
61
+ Defines the page's own ViewState — data that the page's server-side code provides:
62
+
63
+ ```yaml
64
+ name: Page
65
+ tags:
66
+ - tag: title
67
+ type: data
68
+ dataType: string
69
+ phase: slow
70
+ - tag: items
71
+ type: sub-contract
72
+ repeated: true
73
+ trackBy: id
74
+ tags:
75
+ - tag: id
76
+ type: data
77
+ dataType: string
78
+ - tag: name
79
+ type: data
80
+ dataType: string
81
+ ```
82
+
83
+ ## Dynamic Routes and Contract Params
84
+
85
+ When a contract declares `params`, it means the component expects those URL parameters to be provided by the route. This tells you that the page using this contract **should be placed in a matching dynamic route directory**.
86
+
87
+ For example, if a contract declares:
88
+
89
+ ```yaml
90
+ name: product-page
91
+ params:
92
+ slug: string
93
+ tags:
94
+ - ...
95
+ ```
96
+
97
+ Then the page using this contract should live at a route that provides a `slug` param:
98
+
99
+ ```
100
+ src/pages/products/[slug]/page.jay-html
101
+ ```
102
+
103
+ ### Discovering Param Values
104
+
105
+ For SSG with dynamic routes, the plugin component provides a `loadParams` generator that yields all valid param combinations. Use it to discover what routes will be generated:
106
+
107
+ ```bash
108
+ jay-stack params wix-stores/product-page
109
+ # Output: [{"slug": "blue-shirt"}, {"slug": "red-hat"}, ...]
110
+ ```
111
+
112
+ Params are always strings (URL params).
package/dist/index.d.ts CHANGED
@@ -1,7 +1,15 @@
1
+ import { LogLevel } from '@jay-framework/logger';
1
2
  import { PublishMessage, PublishResponse, SaveImageMessage, SaveImageResponse, HasImageMessage, HasImageResponse, GetProjectInfoMessage, GetProjectInfoResponse } from '@jay-framework/editor-protocol';
3
+ export { ContractIndexEntry, ContractsIndex, MaterializeContractsOptions, MaterializeResult, listContracts, materializeContracts } from '@jay-framework/stack-server-runtime';
2
4
 
3
5
  interface StartDevServerOptions {
4
6
  projectPath?: string;
7
+ /** Enable test endpoints (/_jay/health, /_jay/shutdown) */
8
+ testMode?: boolean;
9
+ /** Auto-shutdown after N seconds */
10
+ timeout?: number;
11
+ /** Log level for output */
12
+ logLevel?: LogLevel;
5
13
  }
6
14
  declare function startDevServer(options?: StartDevServerOptions): Promise<void>;
7
15