@jay-framework/jay-stack-cli 0.15.5 → 0.15.6

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.
Files changed (36) hide show
  1. package/agent-kit-template/{INSTRUCTIONS.md → designer/INSTRUCTIONS.md} +11 -8
  2. package/agent-kit-template/{jay-html-syntax.md → designer/jay-html-components.md} +89 -158
  3. package/agent-kit-template/designer/jay-html-styling.md +97 -0
  4. package/agent-kit-template/designer/jay-html-syntax.md +44 -0
  5. package/agent-kit-template/designer/jay-html-template-syntax.md +203 -0
  6. package/agent-kit-template/developer/INSTRUCTIONS.md +34 -0
  7. package/agent-kit-template/developer/cli-commands.md +228 -0
  8. package/agent-kit-template/developer/component-data.md +109 -0
  9. package/agent-kit-template/developer/component-refs.md +117 -0
  10. package/agent-kit-template/developer/component-state.md +140 -0
  11. package/agent-kit-template/developer/configuration.md +76 -0
  12. package/agent-kit-template/developer/page-components.md +103 -0
  13. package/agent-kit-template/developer/page-contracts.md +114 -0
  14. package/agent-kit-template/developer/project-structure.md +242 -0
  15. package/agent-kit-template/developer/render-results.md +112 -0
  16. package/agent-kit-template/developer/routing.md +161 -0
  17. package/agent-kit-template/developer/seo-guide.md +93 -0
  18. package/agent-kit-template/plugin/INSTRUCTIONS.md +40 -0
  19. package/agent-kit-template/plugin/actions-guide.md +125 -0
  20. package/agent-kit-template/plugin/component-context.md +103 -0
  21. package/agent-kit-template/plugin/component-data.md +109 -0
  22. package/agent-kit-template/plugin/component-refs.md +117 -0
  23. package/agent-kit-template/plugin/component-state.md +140 -0
  24. package/agent-kit-template/plugin/component-structure.md +174 -0
  25. package/agent-kit-template/plugin/contracts-guide.md +193 -0
  26. package/agent-kit-template/plugin/plugin-structure.md +194 -0
  27. package/agent-kit-template/plugin/render-results.md +112 -0
  28. package/agent-kit-template/plugin/seo-guide.md +93 -0
  29. package/agent-kit-template/plugin/services-guide.md +116 -0
  30. package/agent-kit-template/plugin/validation.md +101 -0
  31. package/dist/index.js +678 -60
  32. package/package.json +10 -10
  33. /package/agent-kit-template/{cli-commands.md → designer/cli-commands.md} +0 -0
  34. /package/agent-kit-template/{contracts-and-plugins.md → designer/contracts-and-plugins.md} +0 -0
  35. /package/agent-kit-template/{project-structure.md → designer/project-structure.md} +0 -0
  36. /package/agent-kit-template/{routing.md → designer/routing.md} +0 -0
@@ -0,0 +1,117 @@
1
+ # Component Refs
2
+
3
+ Refs provide access to DOM elements declared as `interactive` in the contract. They are the second parameter of the interactive constructor.
4
+
5
+ ## Single Refs
6
+
7
+ A ref maps to one DOM element:
8
+
9
+ ```yaml
10
+ # Contract
11
+ - tag: addToCart
12
+ type: interactive
13
+ elementType: HTMLButtonElement
14
+ ```
15
+
16
+ ```typescript
17
+ // Component
18
+ .withInteractive(function MyComp(props, refs) {
19
+ refs.addToCart.onClick(() => {
20
+ // handle click
21
+ });
22
+ })
23
+ ```
24
+
25
+ ### Ref Methods
26
+
27
+ Refs provide type-safe access to the DOM element:
28
+
29
+ ```typescript
30
+ refs.submitButton.onClick(() => {
31
+ /* ... */
32
+ });
33
+
34
+ // exec$ gives direct access to the element and current ViewState
35
+ refs.submitButton.exec$((element, viewState) => {
36
+ element.disabled = viewState.isSubmitting;
37
+ });
38
+ ```
39
+
40
+ ## Collection Refs
41
+
42
+ When an interactive tag is inside a `repeated` sub-contract, the ref becomes a collection. In jay-html, collection refs use the `$` suffix:
43
+
44
+ ```html
45
+ <div forEach="items" trackBy="id">
46
+ <button ref="itemButton$">Click</button>
47
+ </div>
48
+ ```
49
+
50
+ The `$` is stripped from the name in the contract and component code:
51
+
52
+ ```yaml
53
+ # Contract
54
+ - tag: items
55
+ type: sub-contract
56
+ repeated: true
57
+ trackBy: id
58
+ tags:
59
+ - tag: id
60
+ type: data
61
+ dataType: string
62
+ - tag: itemButton
63
+ type: interactive
64
+ elementType: HTMLButtonElement
65
+ ```
66
+
67
+ ### Collection Ref Methods
68
+
69
+ ```typescript
70
+ // Map over all items in the collection
71
+ const labels = refs.itemButton.map((proxy, viewState, coordinate) => {
72
+ return viewState.name;
73
+ });
74
+
75
+ // Find a specific item
76
+ const target = refs.itemButton.find((viewState) => viewState.id === 'target-id');
77
+
78
+ // Find by coordinate
79
+ const target = refs.itemButton.find((viewState, coordinate) =>
80
+ sameCoordinate(coordinate, ['item-2', 'itemButton']),
81
+ );
82
+ ```
83
+
84
+ ## Element Types
85
+
86
+ Common element types for interactive tags:
87
+
88
+ | Element Type | Use For |
89
+ | --------------------- | --------------------- |
90
+ | `HTMLButtonElement` | Buttons, clickable |
91
+ | `HTMLAnchorElement` | Links |
92
+ | `HTMLInputElement` | Text inputs, checkbox |
93
+ | `HTMLSelectElement` | Dropdowns |
94
+ | `HTMLTextAreaElement` | Multi-line text |
95
+ | `HTMLFormElement` | Forms |
96
+ | `HTMLDivElement` | Generic containers |
97
+
98
+ Multiple element types (when the same ref may bind to different elements):
99
+
100
+ ```yaml
101
+ - tag: trigger
102
+ type: interactive
103
+ elementType: HTMLButtonElement | HTMLAnchorElement
104
+ ```
105
+
106
+ ## Data + Interactive
107
+
108
+ A tag can be both data and interactive:
109
+
110
+ ```yaml
111
+ - tag: quantityInput
112
+ type: [data, interactive]
113
+ dataType: number
114
+ elementType: HTMLInputElement
115
+ ```
116
+
117
+ This generates both a ViewState field and a ref.
@@ -0,0 +1,140 @@
1
+ # Component State Hooks
2
+
3
+ All hooks are used inside the interactive phase (the `withInteractive` constructor function). They provide reactive state management for client-side behavior.
4
+
5
+ ## createSignal
6
+
7
+ Creates a reactive getter/setter pair:
8
+
9
+ ```typescript
10
+ import { createSignal } from '@jay-framework/component';
11
+
12
+ const [count, setCount] = createSignal(0);
13
+
14
+ // Read
15
+ count(); // 0
16
+
17
+ // Write
18
+ setCount(5); // set to 5
19
+ setCount((n) => n + 1); // increment
20
+ ```
21
+
22
+ Can initialize from a getter (reactive dependency):
23
+
24
+ ```typescript
25
+ const [label, setLabel] = createSignal(() => 'Hello ' + props.name());
26
+ ```
27
+
28
+ ## createPatchableSignal
29
+
30
+ Creates a signal with JSON Patch support for fine-grained updates to complex objects:
31
+
32
+ ```typescript
33
+ import { createPatchableSignal } from '@jay-framework/component';
34
+ import { REPLACE } from '@jay-framework/json-patch';
35
+
36
+ const [data, setData, patchData] = createPatchableSignal({
37
+ label: 'Hello',
38
+ count: 0,
39
+ nested: { value: 42 },
40
+ });
41
+
42
+ // Patch a specific field
43
+ patchData({ op: REPLACE, path: ['label'], value: 'Updated' });
44
+
45
+ // Patch nested field
46
+ patchData({ op: REPLACE, path: ['nested', 'value'], value: 99 });
47
+ ```
48
+
49
+ See [component-data.md](component-data.md) for more on immutable data and patching.
50
+
51
+ ## createMemo
52
+
53
+ Creates a memoized computed value that recalculates only when dependencies change:
54
+
55
+ ```typescript
56
+ import { createMemo } from '@jay-framework/component';
57
+
58
+ const fullName = createMemo(() => `${firstName()} ${lastName()}`);
59
+
60
+ // Read
61
+ fullName(); // recomputes only when firstName() or lastName() change
62
+ ```
63
+
64
+ With initial value:
65
+
66
+ ```typescript
67
+ const total = createMemo((prev) => prev + latestValue(), 0);
68
+ ```
69
+
70
+ ## createEffect
71
+
72
+ Registers a side effect that runs on mount and when dependencies change. Optional cleanup function:
73
+
74
+ ```typescript
75
+ import { createEffect } from '@jay-framework/component';
76
+
77
+ createEffect(() => {
78
+ const handler = () => setWindowWidth(window.innerWidth);
79
+ window.addEventListener('resize', handler);
80
+ return () => window.removeEventListener('resize', handler); // cleanup
81
+ });
82
+ ```
83
+
84
+ Effects track reactive dependencies automatically:
85
+
86
+ ```typescript
87
+ createEffect(() => {
88
+ document.title = `${count()} items`; // reruns when count() changes
89
+ });
90
+ ```
91
+
92
+ ## createDerivedArray
93
+
94
+ Efficiently maps an array with smart caching. Only remaps items that actually changed:
95
+
96
+ ```typescript
97
+ import { createDerivedArray } from '@jay-framework/component';
98
+
99
+ const displayItems = createDerivedArray(
100
+ () => products(),
101
+ (item, index, length) => ({
102
+ name: item().name,
103
+ displayPrice: formatPrice(item().price),
104
+ isLast: index() === length() - 1,
105
+ }),
106
+ );
107
+
108
+ // Read the mapped array
109
+ displayItems();
110
+ ```
111
+
112
+ Key optimizations:
113
+
114
+ - Reuses mapped items when the source item hasn't changed
115
+ - Only tracks `index()` and `length()` if you actually call them
116
+ - Uses object identity (not deep equality) for cache hits
117
+
118
+ ## createEvent
119
+
120
+ Creates an event emitter for component-to-parent communication:
121
+
122
+ ```typescript
123
+ import { createEvent } from '@jay-framework/component';
124
+
125
+ const onChange = createEvent<{ value: number }>((emitter) => {
126
+ emitter.emit({ value: count() });
127
+ });
128
+ ```
129
+
130
+ ## useReactive
131
+
132
+ Gets the current reactive context for advanced use cases:
133
+
134
+ ```typescript
135
+ import { useReactive } from '@jay-framework/component';
136
+
137
+ const reactive = useReactive();
138
+ ```
139
+
140
+ Most components won't need this — prefer the higher-level hooks above.
@@ -0,0 +1,174 @@
1
+ # Component Structure
2
+
3
+ Full-stack components use `makeJayStackComponent` with a fluent builder API and three rendering phases.
4
+
5
+ ## Basic Structure
6
+
7
+ ```typescript
8
+ import { makeJayStackComponent, phaseOutput } from '@jay-framework/fullstack-component';
9
+ import type { MyContract } from './my-contract.generated';
10
+
11
+ export const myComponent = makeJayStackComponent<MyContract>()
12
+ .withSlowlyRender(async (props) => {
13
+ const data = await fetchStaticData();
14
+ return phaseOutput(
15
+ { title: data.title, description: data.description }, // ViewState
16
+ { productId: data.id }, // CarryForward
17
+ );
18
+ })
19
+ .withFastRender(async (props) => {
20
+ return phaseOutput({ price: await getPrice(), inStock: true }, {});
21
+ })
22
+ .withInteractive(function MyComponent(props, refs) {
23
+ // Client-side hooks here
24
+ return { render: () => ({}) };
25
+ });
26
+ ```
27
+
28
+ ## Builder API
29
+
30
+ ### `.withProps<T>()`
31
+
32
+ Declare the props type (must match contract `props`):
33
+
34
+ ```typescript
35
+ makeJayStackComponent<MyContract>().withProps<{ productId: string; currency?: string }>();
36
+ ```
37
+
38
+ ### `.withServices(...markers)`
39
+
40
+ Inject server-side services:
41
+
42
+ ```typescript
43
+ .withServices(DATABASE_SERVICE, CACHE_SERVICE)
44
+ .withSlowlyRender(async (props, db, cache) => {
45
+ // db and cache are injected
46
+ })
47
+ ```
48
+
49
+ ### `.withContexts(...markers)`
50
+
51
+ Consume client-side contexts in the interactive phase:
52
+
53
+ ```typescript
54
+ .withContexts(CART_CONTEXT)
55
+ .withInteractive(function MyComp(props, refs, cartCtx) {
56
+ // cartCtx available in interactive phase
57
+ })
58
+ ```
59
+
60
+ ### `.withLoadParams(fn)`
61
+
62
+ Generate URL params for SSG (static site generation). Returns an async iterable of param arrays:
63
+
64
+ ```typescript
65
+ .withLoadParams(async function* (db) {
66
+ const products = await db.getAllProducts();
67
+ yield products.map(p => ({ slug: p.slug }));
68
+ })
69
+ ```
70
+
71
+ ### `.withSlowlyRender(fn)` — Build-time rendering
72
+
73
+ Runs during SSG. Has access to props and services. Returns `phaseOutput(viewState, carryForward)`.
74
+
75
+ CarryForward data is passed to the fast phase but not included in the ViewState.
76
+
77
+ ```typescript
78
+ .withSlowlyRender(async (props, db) => {
79
+ const product = await db.getProduct(props.slug);
80
+ if (!product) return notFound('Product not found');
81
+ return phaseOutput(
82
+ { title: product.name, description: product.desc },
83
+ { productId: product.id }, // Available in fast phase
84
+ );
85
+ })
86
+ ```
87
+
88
+ ### `.withFastRender(fn)` — Request-time rendering
89
+
90
+ Runs on each request. Receives props (including `query` for query parameters) and carry-forward from slow phase.
91
+
92
+ ```typescript
93
+ .withFastRender(async (props, db) => {
94
+ const price = await db.getPrice(props.carryForward.productId);
95
+ return phaseOutput({ price, inStock: price > 0 }, {});
96
+ })
97
+ ```
98
+
99
+ ### `.withClientDefaults(fn)` — Default client ViewState
100
+
101
+ Provides default values for client-side ViewState before hydration:
102
+
103
+ ```typescript
104
+ .withClientDefaults(() => ({
105
+ quantity: 1,
106
+ selectedVariant: 'default',
107
+ }))
108
+ ```
109
+
110
+ ### `.withInteractive(ComponentConstructor)` — Client-side logic
111
+
112
+ The interactive phase runs in the browser. Use hooks here (see component-state.md):
113
+
114
+ ```typescript
115
+ .withInteractive(function ProductPage(props, refs) {
116
+ const [quantity, setQuantity] = createSignal(1);
117
+
118
+ refs.addToCart.onClick(() => {
119
+ addToCartAction({ productId: props.productId, quantity: quantity() });
120
+ });
121
+
122
+ return {
123
+ render: () => ({
124
+ quantity: quantity(),
125
+ }),
126
+ };
127
+ })
128
+ ```
129
+
130
+ ## Render Return Types
131
+
132
+ Each phase can return:
133
+
134
+ - `phaseOutput(viewState, carryForward)` — success
135
+ - `notFound()`, `badRequest()`, `unauthorized()`, `forbidden()` — client errors
136
+ - `serverError5xx(status, message)` — server errors
137
+ - `redirect3xx(status, location)` — redirects
138
+
139
+ See [render-results.md](render-results.md) for details.
140
+
141
+ ## Props and Contract Alignment
142
+
143
+ The component's props type must match the contract's `props` section:
144
+
145
+ ```yaml
146
+ # Contract
147
+ props:
148
+ - name: productId
149
+ type: string
150
+ required: true
151
+ ```
152
+
153
+ ```typescript
154
+ // Component
155
+ makeJayStackComponent<MyContract>().withProps<{ productId: string }>();
156
+ ```
157
+
158
+ ## Params and Contract Alignment
159
+
160
+ If the contract has `params`, the component should use `withLoadParams` and the route must have matching dynamic segments:
161
+
162
+ ```yaml
163
+ # Contract
164
+ params:
165
+ slug: string
166
+ ```
167
+
168
+ ```typescript
169
+ // Component
170
+ .withLoadParams(async function* (db) {
171
+ const items = await db.getAll();
172
+ yield items.map(i => ({ slug: i.slug }));
173
+ })
174
+ ```
@@ -0,0 +1,193 @@
1
+ # Contract Authoring Guide
2
+
3
+ Contracts (`.jay-contract` files) are the source of truth for a component's data shape. Define the contract before implementing the component.
4
+
5
+ ## Basic Structure
6
+
7
+ ```yaml
8
+ name: ProductCard
9
+ description: Displays a single product with price and add-to-cart. Use for product grids and featured sections.
10
+ props:
11
+ - name: productId
12
+ type: string
13
+ required: true
14
+ description: The product to display
15
+ params:
16
+ slug: string
17
+ tags:
18
+ - tag: name
19
+ type: data
20
+ dataType: string
21
+ phase: slow
22
+ ```
23
+
24
+ ## Tag Types
25
+
26
+ ### `data` — Read-only values
27
+
28
+ ```yaml
29
+ - tag: productName
30
+ type: data
31
+ dataType: string # string (default), number, boolean, date
32
+ required: true # optional, defaults to false
33
+ phase: slow # slow, fast, or fast+interactive
34
+ description: Display name
35
+ ```
36
+
37
+ ### `variant` — Enum/boolean for conditionals
38
+
39
+ ```yaml
40
+ - tag: status
41
+ type: variant
42
+ dataType: enum (AVAILABLE | OUT_OF_STOCK | PREORDER)
43
+ phase: fast+interactive
44
+ ```
45
+
46
+ ### `interactive` — Element refs for user interaction
47
+
48
+ ```yaml
49
+ - tag: addToCart
50
+ type: interactive
51
+ elementType: HTMLButtonElement # HTMLAnchorElement, HTMLInputElement, HTMLSelectElement, etc.
52
+ ```
53
+
54
+ Interactive tags are always `fast+interactive` — do not specify a phase.
55
+
56
+ A tag can be both data and interactive:
57
+
58
+ ```yaml
59
+ - tag: quantityInput
60
+ type: [data, interactive]
61
+ dataType: number
62
+ elementType: HTMLInputElement
63
+ ```
64
+
65
+ ### `sub-contract` — Nested objects
66
+
67
+ Inline:
68
+
69
+ ```yaml
70
+ - tag: pricing
71
+ type: sub-contract
72
+ tags:
73
+ - tag: amount
74
+ type: data
75
+ dataType: number
76
+ - tag: currency
77
+ type: data
78
+ dataType: string
79
+ ```
80
+
81
+ Linked (reference another contract file):
82
+
83
+ ```yaml
84
+ - tag: author
85
+ type: sub-contract
86
+ link: ./author.jay-contract
87
+ ```
88
+
89
+ ### `sub-contract` with `repeated: true` — Arrays
90
+
91
+ ```yaml
92
+ - tag: items
93
+ type: sub-contract
94
+ repeated: true
95
+ trackBy: id # Required: identifies each item
96
+ phase: fast
97
+ tags:
98
+ - tag: id
99
+ type: data
100
+ dataType: string
101
+ - tag: name
102
+ type: data
103
+ dataType: string
104
+ ```
105
+
106
+ `trackBy` must reference a `data` tag with `string` or `number` type within the same sub-contract.
107
+
108
+ ## Async Data
109
+
110
+ Wrap any tag in `Promise<T>` with `async: true`:
111
+
112
+ ```yaml
113
+ - tag: reviews
114
+ type: data
115
+ async: true
116
+ dataType: string # Compiles to Promise<string>
117
+
118
+ - tag: relatedProducts
119
+ type: sub-contract
120
+ repeated: true
121
+ trackBy: id
122
+ async: true # Compiles to Promise<Array<...>>
123
+ tags:
124
+ - tag: id
125
+ type: data
126
+ dataType: string
127
+ ```
128
+
129
+ ## Rendering Phases
130
+
131
+ Each tag has a phase that determines when its data is available:
132
+
133
+ | Phase | When | Use For |
134
+ | ------------------ | ------------------ | --------------------------------------- |
135
+ | `slow` | Build time (SSG) | Static content, SEO data, product names |
136
+ | `fast` | Request time (SSR) | Per-request data, live pricing, stock |
137
+ | `fast+interactive` | Request + client | Data that also updates on the client |
138
+
139
+ **How to choose:**
140
+
141
+ - Can the data be known at build time? Use `slow`
142
+ - Does it change per request (user, time, session)? Use `fast`
143
+ - Does it also update on the client after interaction? Use `fast+interactive`
144
+ - Interactive tags (refs) are always `fast+interactive`
145
+
146
+ **Phase rules for arrays:** Child phases must be >= parent phase. If the array is `fast`, all children must be `fast` or later.
147
+
148
+ ## Props vs Params
149
+
150
+ ### Props — Component configuration
151
+
152
+ Props are passed by the parent component or jay-html template. Use for component inputs like IDs, configuration flags, display options.
153
+
154
+ ```yaml
155
+ props:
156
+ - name: productId
157
+ type: string
158
+ required: true
159
+ description: The product to display
160
+ - name: showPricing
161
+ type: boolean
162
+ default: 'true'
163
+ ```
164
+
165
+ ### Params — URL route segments
166
+
167
+ Params come from dynamic route segments (`[slug]`, `[[lang]]`, `[...path]`). Use for page-level routing data.
168
+
169
+ ```yaml
170
+ params:
171
+ slug: string # required — from [slug]
172
+ lang: string? # optional — from [[lang]]
173
+ path: string[] # catch-all — from [...path]
174
+ ```
175
+
176
+ ## Description Field
177
+
178
+ Always include a `description` at the contract level explaining when to use this contract:
179
+
180
+ ```yaml
181
+ name: product-search
182
+ description: Product listing with filters, sorting, and pagination. Use for search results and category pages.
183
+ ```
184
+
185
+ ## Validation Rules
186
+
187
+ - Tag names must be unique at each level
188
+ - `repeated: true` requires `trackBy`
189
+ - `trackBy` must reference a `data` tag with `string` or `number` type
190
+ - Interactive tags cannot have an explicit `phase`
191
+ - Sub-contracts must have either `tags` (inline) or `link` (external), not both
192
+ - Array children must have phase >= parent phase
193
+ - Prop names must be unique