micro-contracts 0.13.0 → 0.14.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.
- package/README.md +11 -8
- package/dist/generator/index.d.ts +1 -1
- package/dist/generator/index.d.ts.map +1 -1
- package/dist/generator/linter.d.ts.map +1 -1
- package/dist/generator/linter.js +94 -3
- package/dist/generator/linter.js.map +1 -1
- package/dist/generator/templateProcessor.d.ts +32 -2
- package/dist/generator/templateProcessor.d.ts.map +1 -1
- package/dist/generator/templateProcessor.js +126 -6
- package/dist/generator/templateProcessor.js.map +1 -1
- package/dist/types.d.ts +48 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/docs/micro-contracts-x-event-spec-change.md +731 -0
- package/docs/screen-spec.md +228 -47
- package/package.json +1 -1
package/docs/screen-spec.md
CHANGED
|
@@ -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-
|
|
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-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
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
|
-
|
|
155
|
-
operationId:
|
|
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-
|
|
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** —
|
|
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;
|
|
187
|
-
screenConst: string;
|
|
188
|
-
screenId: string;
|
|
189
|
-
screenName: string;
|
|
190
|
-
operationId: string;
|
|
191
|
-
supportsBack: boolean;
|
|
192
|
-
viewModelSchema: string;
|
|
193
|
-
links: ScreenLink[];
|
|
194
|
-
|
|
195
|
-
|
|
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
|
|
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
|
|
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
|
|
292
|
-
|
|
|
293
|
-
| `SCREEN_MISSING_CONST`
|
|
294
|
-
| `SCREEN_MISSING_NAME`
|
|
295
|
-
| `SCREEN_MISSING_OPERATION_ID`
|
|
296
|
-
| `
|
|
297
|
-
| `
|
|
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
|
|
348
|
-
- Screen context extraction: `TemplateContext.screens`
|
|
349
|
-
-
|
|
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