micro-contracts 0.13.0 → 0.14.1

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.
@@ -44,10 +44,17 @@ Only concerns with **no OpenAPI-native representation** need extensions:
44
44
  | `x-screen-id` | Yes | Traceability ID (e.g., `SCR-001`) | Links to requirements and E2E annotations |
45
45
  | `x-screen-name` | Yes* | Generated symbol name (e.g., `HomePage`) | Drives `use{Name}Events()`, `wrap{Name}ViewModel()` |
46
46
  | `x-back-navigation` | No | History-based back navigation support | `links` only cover forward navigation with known targets |
47
- | `x-events` | No | Analytics event declarations | No OpenAPI concept for client-side event tracking |
47
+ | `x-event` | No | Inline analytics event declaration | Type auto-inferred from placement; supports string, object, `$ref` |
48
+ | `x-interactions` | No | In-page interaction bindings | Declares non-navigation UI interactions (swipe, selection, etc.) |
48
49
  | `x-view-model` | No | Explicit ViewModel schema name | Alternative to auto-inference from `$ref` |
49
50
  | `x-view-props` | No | ViewProps interface name | For framework-specific wrapper generation |
50
51
 
52
+ **Deprecated extensions:**
53
+
54
+ | Extension | Status | Migration |
55
+ | ----------- | ------ | --------- |
56
+ | `x-events` | Deprecated (v0.14) — removed in v0.15 | Replace with inline `x-event` on GET, links, actions, or interactions |
57
+
51
58
  \* When `x-screen-id` is present, `x-screen-const` and `x-screen-name` are required (enforced by lint).
52
59
 
53
60
  ---
@@ -114,6 +121,8 @@ Effects of `screen: true`:
114
121
 
115
122
  ## Screen Spec YAML Structure
116
123
 
124
+ ### Basic Example (inline `x-event`)
125
+
117
126
  ```yaml
118
127
  openapi: '3.1.0'
119
128
  info:
@@ -133,13 +142,14 @@ paths:
133
142
  x-screen-id: SCR-001
134
143
  x-screen-name: HomePage
135
144
  x-back-navigation: false
136
- x-events:
137
- - name: home_view
138
- type: screen_view
139
- method: trackView
140
- - name: sign_out
141
- type: user_action
142
- method: trackSignOut
145
+ x-event: home_view # → type: "screen_view" (auto-inferred)
146
+ x-interactions:
147
+ - name: daySwipe
148
+ description: Horizontal day-by-day swipe
149
+ x-event:
150
+ name: day_swipe
151
+ params:
152
+ direction: string
143
153
  summary: Render home page
144
154
  responses:
145
155
  '200':
@@ -151,10 +161,92 @@ paths:
151
161
  links:
152
162
  goToSettings:
153
163
  operationId: renderSettingsPage
154
- goToChat:
155
- operationId: renderChatPage
164
+ goToDetail:
165
+ operationId: renderDetailPage
166
+ x-event: # → type: "user_action" (auto-inferred)
167
+ $ref: '#/components/x-event-defs/itemTap'
168
+ post:
169
+ operationId: actionHomePage
170
+ x-event: mode_switch # → type: "user_action" (auto-inferred)
171
+ responses:
172
+ '200':
173
+ description: OK
174
+
175
+ components:
176
+ x-event-defs:
177
+ itemTap:
178
+ name: item_tap
179
+ params:
180
+ itemId: string
181
+ ```
182
+
183
+ ### `x-event` — Three Forms
184
+
185
+ ```yaml
186
+ # 1. String form (most common)
187
+ x-event: home_view
188
+
189
+ # 2. Object form (type override / extra params)
190
+ x-event:
191
+ name: oauth_callback_result
192
+ type: system
193
+ params:
194
+ success: boolean
195
+
196
+ # 3. $ref form (reusable definitions)
197
+ x-event:
198
+ $ref: '#/components/x-event-defs/itemTap'
199
+ ```
200
+
201
+ `$ref` resolves to `#/components/x-event-defs/*` (local references only).
202
+
203
+ ### `x-event` Placement and Type Inference
204
+
205
+ | Placement | Inferred `type` | Params auto-derivation |
206
+ |-----------|-----------------|------------------------|
207
+ | `get` operation | `screen_view` | From path parameters (e.g., `/calendar/{yearMonth}` → `{ yearMonth: string }`) |
208
+ | `responses.200.links.*` | `user_action` | From target route's path parameters |
209
+ | `post` / `put` / `patch` / `delete` | `user_action` | None — specify explicitly in `params` |
210
+ | `x-interactions.*` | `user_action` | None — specify explicitly in `params` |
211
+
212
+ `type` can be overridden explicitly in object form. Recognized values: `screen_view`, `user_action`, `system`.
213
+
214
+ ### `x-interactions`
215
+
216
+ Declares in-page interaction bindings that don't map to HTTP operations or link navigations.
217
+
218
+ ```yaml
219
+ x-interactions:
220
+ - name: daySwipe
221
+ description: Horizontal day-by-day swipe navigation
222
+ x-event:
223
+ name: day_swipe
224
+ params:
225
+ direction: string
226
+ - name: itemSelect
227
+ description: Select an item from choices
228
+ x-event:
229
+ name: item_select
230
+ params:
231
+ itemId: string
232
+ module: '@ui/selector' # project-specific field (passed through)
233
+ ```
234
+
235
+ Required field: `name`. All other fields are optional. Project-specific fields (e.g., `module`, `factory`) are passed through to templates in `extras`.
236
+
237
+ ### Reusable Event Definitions
238
+
239
+ ```yaml
240
+ components:
241
+ x-event-defs:
242
+ itemTap:
243
+ name: item_tap
244
+ params:
245
+ itemId: string
156
246
  ```
157
247
 
248
+ Referenced with `$ref: '#/components/x-event-defs/itemTap'` from any `x-event` placement.
249
+
158
250
  ### Field Reference
159
251
 
160
252
  **`operationId`** — Unique identifier referenced by generated code. The naming convention `render` + screen name + `Page` is recommended.
@@ -169,11 +261,13 @@ paths:
169
261
 
170
262
  **`x-back-navigation`** — When `true`, declares that the screen supports browser back or UI back button navigation. Unlike forward navigation covered by `links`, the back destination is determined at runtime.
171
263
 
172
- **`x-events`** — Array of analytics event declarations. Each event has `name` (event name), `type` (category), and `method` (generated method name), with an optional `params` map of parameter names to types.
264
+ **`x-event`** — Inline analytics event. Type is auto-inferred from placement. Supports string, object (with `name`, `type?`, `params?`), and `$ref` forms. Params are auto-derived from path parameters for `get` and `links` placements; for actions and interactions, specify params explicitly.
265
+
266
+ **`x-interactions`** — Array of in-page interaction bindings. Each entry has `name` (required), `description` (recommended), optional `x-event`, and any project-specific fields.
173
267
 
174
- **`responses.200.links`** — Forward navigation targets. References target screens by `operationId`.
268
+ **`responses.200.links`** — Forward navigation targets. References target screens by `operationId`. Each link may have an optional `x-event`.
175
269
 
176
- **`POST` operations** — A `POST` on the same path defines user actions (form submissions, button taps, etc.). Specify the action payload schema in `requestBody`.
270
+ **`POST` / `PUT` / `PATCH` / `DELETE` operations** — Mutation operations on the same path define user actions (form submissions, button taps, deletions, etc.). Each may have an optional `x-event`.
177
271
 
178
272
  ---
179
273
 
@@ -183,16 +277,52 @@ When `screen: true` is enabled, `TemplateContext` includes a pre-parsed `screens
183
277
 
184
278
  ```typescript
185
279
  interface ScreenContext {
186
- route: string; // '/home'
187
- screenConst: string; // 'HOME'
188
- screenId: string; // 'SCR-001'
189
- screenName: string; // 'HomePage'
190
- operationId: string; // 'renderHomePage'
191
- supportsBack: boolean; // false
192
- viewModelSchema: string; // 'HomePageViewModel'
193
- links: ScreenLink[]; // [{name, targetRoute, targetOperationId}]
194
- events: ScreenEventDefinition[]; // [{name, type, method, params?}]
195
- requiresAuth: boolean; // true
280
+ route: string; // '/home'
281
+ screenConst: string; // 'HOME'
282
+ screenId: string; // 'SCR-001'
283
+ screenName: string; // 'HomePage'
284
+ operationId: string; // 'renderHomePage'
285
+ supportsBack: boolean; // false
286
+ viewModelSchema: string; // 'HomePageViewModel'
287
+ links: ScreenLink[]; // forward navigation targets
288
+ requiresAuth: boolean; // true
289
+ pathParams: string[]; // ['yearMonth'] for /calendar/{yearMonth}
290
+
291
+ // Inline x-event (v0.14+)
292
+ screenEvent?: InlineEventDefinition; // GET x-event (screen_view)
293
+ actions: ScreenAction[]; // post/put/patch/delete with events
294
+ interactions: ScreenInteraction[]; // x-interactions with events
295
+
296
+ /** @deprecated Use screenEvent / links[].event / actions[].event instead */
297
+ events: ScreenEventDefinition[]; // legacy x-events (backward compat)
298
+ }
299
+
300
+ interface ScreenLink {
301
+ name: string; // 'goToSettings'
302
+ targetRoute: string; // '/settings'
303
+ targetOperationId: string; // 'renderSettingsPage'
304
+ event?: InlineEventDefinition; // link x-event (user_action)
305
+ }
306
+
307
+ interface ScreenAction {
308
+ method: string; // 'post', 'put', 'patch', 'delete'
309
+ operationId: string; // 'actionHomePage'
310
+ summary: string;
311
+ schemaRef: string; // request body $ref name
312
+ event?: InlineEventDefinition; // action x-event (user_action)
313
+ }
314
+
315
+ interface ScreenInteraction {
316
+ name: string; // 'daySwipe'
317
+ description: string; // 'Horizontal day-by-day swipe'
318
+ event?: InlineEventDefinition; // interaction x-event (user_action)
319
+ extras: Record<string, unknown>; // project-specific fields
320
+ }
321
+
322
+ interface InlineEventDefinition {
323
+ name: string; // 'home_view'
324
+ type: string; // 'screen_view' | 'user_action' | 'system'
325
+ params?: Record<string, string>; // auto-derived or explicit
196
326
  }
197
327
  ```
198
328
 
@@ -254,11 +384,60 @@ export const HOME_PAGE_LINKS = {
254
384
  } as const;
255
385
  ```
256
386
 
257
- ### screen-events.hbs
387
+ ### screen-events.hbs (inline x-event)
258
388
 
259
- Generates analytics event hooks. Outputs a `use{ScreenName}Events()` function for each screen that has `x-events`.
389
+ Generates analytics event hooks using the new inline `x-event` structure. Templates can use `screenEvent`, `links[].event`, `actions[].event`, and `interactions[].event`.
260
390
 
261
- Example output:
391
+ Example template:
392
+
393
+ ```handlebars
394
+ {{#each screens}}
395
+ {{#if screenEvent}}
396
+ export function use{{screenName}}ScreenView() {
397
+ const tracked = useRef(false);
398
+ useEffect(() => {
399
+ if (tracked.current) return;
400
+ tracked.current = true;
401
+ trackEvent('{{screenEvent.name}}', '{{screenEvent.type}}', {});
402
+ }, []);
403
+ }
404
+ {{/if}}
405
+
406
+ {{#each links}}
407
+ {{#if this.event}}
408
+ export function navigateVia{{capitalize this.name}}(
409
+ {{eventParamsSignature this.event.params}}
410
+ ) {
411
+ trackEvent('{{this.event.name}}', '{{this.event.type}}',
412
+ { {{#each this.event.params}}{{@key}}{{#unless @last}}, {{/unless}}{{/each}} });
413
+ return '{{this.targetRoute}}';
414
+ }
415
+ {{/if}}
416
+ {{/each}}
417
+
418
+ {{#each actions}}
419
+ {{#if this.event}}
420
+ export function track{{capitalize this.operationId}}Event(
421
+ {{eventParamsSignature this.event.params}}
422
+ ) {
423
+ trackEvent('{{this.event.name}}', '{{this.event.type}}',
424
+ { {{#each this.event.params}}{{@key}}{{#unless @last}}, {{/unless}}{{/each}} });
425
+ }
426
+ {{/if}}
427
+ {{/each}}
428
+ {{/each}}
429
+ ```
430
+
431
+ ### Handlebars Helpers
432
+
433
+ | Helper | Description | Example |
434
+ |--------|-------------|---------|
435
+ | `hasEvent` | Returns `true` if the screen has any events (screenEvent, link events, action events, or interaction events) | `{{#if (hasEvent this)}}...{{/if}}` |
436
+ | `eventParamsSignature` | Expands params map to TypeScript argument list | `{{eventParamsSignature screenEvent.params}}` → `yearMonth: string` |
437
+
438
+ ### Legacy screen-events.hbs (deprecated x-events)
439
+
440
+ The legacy `x-events` array is still available in `events` for backward compatibility:
262
441
 
263
442
  ```typescript
264
443
  export function useHomePageEvents() {
@@ -271,30 +450,24 @@ export function useHomePageEvents() {
271
450
  }
272
451
  ```
273
452
 
274
- Events with parameters are also supported with type safety:
275
-
276
- ```typescript
277
- export function useChatPageEvents() {
278
- return {
279
- trackMessageSend: (message_length: number) =>
280
- trackEvent('chat_message_send', 'user_action', { message_length }),
281
- };
282
- }
283
- ```
284
-
285
453
  ---
286
454
 
287
455
  ## Lint Rules
288
456
 
289
457
  `micro-contracts lint` performs the following screen-specific checks:
290
458
 
291
- | Code | Level | Description |
292
- | ----------------------------- | ------- | -------------------------------------------------------- |
293
- | `SCREEN_MISSING_CONST` | Error | `x-screen-id` present but `x-screen-const` missing |
294
- | `SCREEN_MISSING_NAME` | Error | `x-screen-id` present but `x-screen-name` missing |
295
- | `SCREEN_MISSING_OPERATION_ID` | Error | Screen operation missing `operationId` |
296
- | `SCREEN_INVALID_EVENTS` | Error | `x-events` is not an array |
297
- | `SCREEN_INVALID_EVENT` | Error | `x-events` item missing `name`, `type`, or `method` |
459
+ | Code | Level | Description |
460
+ | -------------------------------- | ------- | -------------------------------------------------------------- |
461
+ | `SCREEN_MISSING_CONST` | Error | `x-screen-id` present but `x-screen-const` missing |
462
+ | `SCREEN_MISSING_NAME` | Error | `x-screen-id` present but `x-screen-name` missing |
463
+ | `SCREEN_MISSING_OPERATION_ID` | Error | Screen operation missing `operationId` |
464
+ | `SCREEN_INVALID_X_EVENT` | Error | `x-event` is not a string, `{name}` object, or `{$ref}` |
465
+ | `SCREEN_CONFLICTING_EVENT_DEFS` | Error | `x-events` and `x-event` coexist on the same operation |
466
+ | `SCREEN_INVALID_INTERACTIONS` | Error | `x-interactions` is not an array |
467
+ | `SCREEN_INVALID_INTERACTION` | Error | `x-interactions` entry missing `name` |
468
+ | `SCREEN_DEPRECATED_X_EVENTS` | Warning | `x-events` (flat list) is deprecated — use inline `x-event` |
469
+ | `SCREEN_INVALID_EVENTS` | Error | `x-events` is not an array (legacy validation) |
470
+ | `SCREEN_INVALID_EVENT` | Error | `x-events` item missing `name` or `type` (legacy validation) |
298
471
 
299
472
  In `screen: true` modules, `MISSING_X_SERVICE` / `MISSING_X_METHOD` warnings are suppressed (screen specs do not need service/method declarations).
300
473
 
@@ -331,6 +504,11 @@ outputs:
331
504
  | Field | Type | Description |
332
505
  | ----------------- | --------- | -------------------------------------------------- |
333
506
  | `screens` | array | `ScreenContext[]` — array of screen definitions |
507
+ | `screens[].screenEvent` | object? | `InlineEventDefinition` — GET screen_view event |
508
+ | `screens[].actions` | array | `ScreenAction[]` — mutation operations with events |
509
+ | `screens[].interactions` | array | `ScreenInteraction[]` — interaction bindings with events |
510
+ | `screens[].pathParams` | string[] | Path parameter names for this route |
511
+ | `screens[].links[].event` | object? | `InlineEventDefinition` — link navigation event |
334
512
  | `spec` | object | Raw OpenAPI spec (for direct access when needed) |
335
513
  | `title` | string | OpenAPI title |
336
514
  | `version` | string | OpenAPI version |
@@ -344,11 +522,14 @@ outputs:
344
522
 
345
523
  ### Tier 1 — Core (built into micro-contracts)
346
524
 
347
- - Type definitions: `x-screen-*` fields on `OperationObject`
348
- - Screen context extraction: `TemplateContext.screens`
349
- - Lint rules: Screen spec consistency checks
525
+ - Type definitions: `x-screen-*`, `x-event`, `x-interactions` on `OperationObject`
526
+ - Screen context extraction: `TemplateContext.screens` (with `screenEvent`, `actions`, `interactions`, `pathParams`)
527
+ - `$ref` resolution: `components.x-event-defs` local references
528
+ - Params auto-derivation: path params for `get` and `links` placements
529
+ - Lint rules: Screen spec consistency, inline event validation, interaction validation
350
530
  - `init --screens`: Starter file generation
351
531
  - Default templates: `screen-navigation.hbs`, `screen-events.hbs`
532
+ - Handlebars helpers: `hasEvent`, `eventParamsSignature`
352
533
 
353
534
  ### Tier 2 — Optional Packages / Official Recipes (future)
354
535
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "micro-contracts",
3
- "version": "0.13.0",
3
+ "version": "0.14.1",
4
4
  "description": "Contract-first OpenAPI toolchain that keeps TypeScript UI and microservices aligned via code generation",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -11,6 +11,7 @@
11
11
  "files": [
12
12
  "dist",
13
13
  "docs",
14
+ "cli-contract.yaml",
14
15
  "README.md",
15
16
  "LICENSE"
16
17
  ],
@@ -55,6 +56,7 @@
55
56
  "devDependencies": {
56
57
  "@types/js-yaml": "^4.0.9",
57
58
  "@types/node": "^20.11.0",
59
+ "cli-contracts": "^0.1.1",
58
60
  "embedoc": "^0.9.0",
59
61
  "tsx": "^4.7.0",
60
62
  "typescript": "^5.3.3",