micro-contracts 0.12.3 → 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.
@@ -0,0 +1,547 @@
1
+ # Screen Spec — Frontend Screen Contracts in OpenAPI
2
+
3
+ The Screen Spec feature repurposes standard OpenAPI constructs (`paths`, `schemas`, `links`, `security`) to declaratively define **frontend screen contracts** — bridging the API layer and UI components. A single YAML file drives frontend types, test scaffolding, and quality analysis.
4
+
5
+ ```
6
+ Screen Spec (OpenAPI YAML)
7
+ ├── micro-contracts generate ──┬── ViewModel Types (type-safe)
8
+ │ ├── Navigation Map (typed routing)
9
+ │ └── Event Hooks (typed analytics)
10
+ ├── E2E scaffold ─── Playwright test stubs + coverage
11
+ └── Data lineage ─── ViewModel → API → DB traceability
12
+ ```
13
+
14
+ > **Important**: This is NOT "auto-generated UI from schema" but rather **screen-level contract definition**, which aligns with micro-contracts' "Contract-first vertical slices" philosophy.
15
+
16
+ ---
17
+
18
+ ## The HTTP-Screen Analogy
19
+
20
+ Standard OpenAPI semantics map directly to screen specification concepts:
21
+
22
+ | OpenAPI Construct | API Usage | Screen Spec Usage |
23
+ | ---------------------- | ---------------------- | ------------------------------------------------------- |
24
+ | `paths: /home` | Endpoint | React/Vue route |
25
+ | `GET /home` | Fetch resource | **Render screen** — what the user sees |
26
+ | `POST /home` | Create/modify resource | **User action** — what the user can do |
27
+ | `parameters` | Filter/identify | **Screen init params** — route/query params |
28
+ | `responses.200.schema` | Response shape | **ViewModel** — typed interface for the screen |
29
+ | `requestBody` | Submitted data shape | **Action payload** — typed user action interface |
30
+ | `links` | HATEOAS related resources | **Forward navigation** — statically known screen targets |
31
+ | `security` | Auth requirement | **Auth guard** — does the screen require login? |
32
+
33
+ All standard OpenAPI tooling works out of the box: linters, documentation generators, and diff tools.
34
+
35
+ ---
36
+
37
+ ## Required Extensions (`x-*`)
38
+
39
+ Only concerns with **no OpenAPI-native representation** need extensions:
40
+
41
+ | Extension | Required | Purpose | Justification |
42
+ | ------------------- | -------- | ----------------------------------------- | --------------------------------------------------------------- |
43
+ | `x-screen-const` | Yes* | Stable constant name (e.g., `HOME`) | No OpenAPI equivalent for generated symbol names |
44
+ | `x-screen-id` | Yes | Traceability ID (e.g., `SCR-001`) | Links to requirements and E2E annotations |
45
+ | `x-screen-name` | Yes* | Generated symbol name (e.g., `HomePage`) | Drives `use{Name}Events()`, `wrap{Name}ViewModel()` |
46
+ | `x-back-navigation` | No | History-based back navigation support | `links` only cover forward navigation with known targets |
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.) |
49
+ | `x-view-model` | No | Explicit ViewModel schema name | Alternative to auto-inference from `$ref` |
50
+ | `x-view-props` | No | ViewProps interface name | For framework-specific wrapper generation |
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
+
58
+ \* When `x-screen-id` is present, `x-screen-const` and `x-screen-name` are required (enforced by lint).
59
+
60
+ ---
61
+
62
+ ## Quick Start
63
+
64
+ ### 1. Initialize a Screen Module
65
+
66
+ ```bash
67
+ npx micro-contracts init myScreens --screens
68
+ ```
69
+
70
+ This command generates:
71
+ - `spec/myScreens/openapi/myScreens.yaml` — Starter screen spec (2-screen scaffold)
72
+ - `spec/default/templates/screen-navigation.hbs` — Navigation map template
73
+ - `spec/default/templates/screen-events.hbs` — Event hooks template
74
+ - `micro-contracts.config.yaml` — Module config with `screen: true`
75
+
76
+ ### 2. Edit the Screen Spec
77
+
78
+ Edit `spec/myScreens/openapi/myScreens.yaml` to add your screen definitions.
79
+
80
+ ### 3. Generate Code
81
+
82
+ ```bash
83
+ npx micro-contracts generate
84
+ ```
85
+
86
+ Generated artifacts:
87
+ - `packages/contract/myScreens/schemas/types.ts` — ViewModel TypeScript types
88
+ - `frontend/src/screens/navigation.generated.ts` — Navigation map
89
+ - `frontend/src/screens/events.generated.ts` — Analytics event hooks
90
+
91
+ ---
92
+
93
+ ## Configuration
94
+
95
+ ### `micro-contracts.config.yaml`
96
+
97
+ Screen modules are enabled with the `screen: true` flag:
98
+
99
+ ```yaml
100
+ modules:
101
+ myScreens:
102
+ openapi: spec/myScreens/openapi/myScreens.yaml
103
+ screen: true
104
+ outputs:
105
+ frontend-api:
106
+ enabled: false # Not needed for screen modules
107
+ screen-navigation:
108
+ output: frontend/src/screens/navigation.generated.ts
109
+ template: screen-navigation.hbs
110
+ screen-events:
111
+ output: frontend/src/screens/events.generated.ts
112
+ template: screen-events.hbs
113
+ ```
114
+
115
+ Effects of `screen: true`:
116
+ - Suppresses `x-micro-contracts-service` / `x-micro-contracts-method` lint warnings
117
+ - Populates `TemplateContext.screens` with pre-parsed `ScreenContext[]`
118
+ - Enables screen-specific lint rules (`x-screen-id` requires `x-screen-const`/`x-screen-name`)
119
+
120
+ ---
121
+
122
+ ## Screen Spec YAML Structure
123
+
124
+ ### Basic Example (inline `x-event`)
125
+
126
+ ```yaml
127
+ openapi: '3.1.0'
128
+ info:
129
+ title: App Screen Specification
130
+ version: '1.0.0'
131
+ description: Screen contracts — not a server API
132
+ servers:
133
+ - url: /
134
+ description: Screen routes (client-side)
135
+
136
+ paths:
137
+ /home:
138
+ get:
139
+ operationId: renderHomePage
140
+ security: [{session: []}]
141
+ x-screen-const: HOME
142
+ x-screen-id: SCR-001
143
+ x-screen-name: HomePage
144
+ x-back-navigation: false
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
153
+ summary: Render home page
154
+ responses:
155
+ '200':
156
+ description: Home page ViewModel
157
+ content:
158
+ application/json:
159
+ schema:
160
+ $ref: '#/components/schemas/HomePageViewModel'
161
+ links:
162
+ goToSettings:
163
+ operationId: renderSettingsPage
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
246
+ ```
247
+
248
+ Referenced with `$ref: '#/components/x-event-defs/itemTap'` from any `x-event` placement.
249
+
250
+ ### Field Reference
251
+
252
+ **`operationId`** — Unique identifier referenced by generated code. The naming convention `render` + screen name + `Page` is recommended.
253
+
254
+ **`security`** — Auth guard. `[{session: []}]` means authentication is required; `[]` means no authentication needed.
255
+
256
+ **`x-screen-const`** — SCREAMING_SNAKE_CASE name used for constant access like `SCREENS.HOME`.
257
+
258
+ **`x-screen-id`** — Unique ID for requirements and test traceability. The format `SCR-{domain}-{number}` is recommended.
259
+
260
+ **`x-screen-name`** — PascalCase name for generated symbols like `useHomePageEvents()`.
261
+
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.
263
+
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.
267
+
268
+ **`responses.200.links`** — Forward navigation targets. References target screens by `operationId`. Each link may have an optional `x-event`.
269
+
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`.
271
+
272
+ ---
273
+
274
+ ## TemplateContext.screens
275
+
276
+ When `screen: true` is enabled, `TemplateContext` includes a pre-parsed `screens: ScreenContext[]`. This eliminates the need to iterate over deeply nested `spec.paths` in templates.
277
+
278
+ ```typescript
279
+ interface ScreenContext {
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
326
+ }
327
+ ```
328
+
329
+ ### Template Usage Comparison
330
+
331
+ **Using `screens[]` (recommended):**
332
+
333
+ ```handlebars
334
+ {{#each screens}}
335
+ {{screenConst}}: { id: '{{screenId}}', route: '{{route}}' },
336
+ {{/each}}
337
+ ```
338
+
339
+ **Iterating `spec.paths` directly (not recommended):**
340
+
341
+ ```handlebars
342
+ {{#each spec.paths}}
343
+ {{#each this}}
344
+ {{#unless (eq @key "parameters")}}
345
+ {{#unless (eq @key "servers")}}
346
+ {{#unless (eq @key "summary")}}
347
+ {{#unless (eq @key "description")}}
348
+ {{#if this.x-screen-id}}
349
+ {{this.x-screen-const}}: { id: '{{this.x-screen-id}}', route: '{{@../key}}' },
350
+ {{/if}}
351
+ {{/unless}}
352
+ {{/unless}}
353
+ {{/unless}}
354
+ {{/unless}}
355
+ {{/each}}
356
+ {{/each}}
357
+ ```
358
+
359
+ ---
360
+
361
+ ## Default Templates
362
+
363
+ ### screen-navigation.hbs
364
+
365
+ Generates a navigation map. Outputs a `SCREENS` constant and `*_PAGE_LINKS` for each screen.
366
+
367
+ Example output:
368
+
369
+ ```typescript
370
+ const _OP_ROUTES = {
371
+ renderHomePage: '/home',
372
+ renderSettingsPage: '/settings',
373
+ renderChatPage: '/chat',
374
+ } as const;
375
+
376
+ export const SCREENS = {
377
+ HOME: { id: 'SCR-001' as const, route: _OP_ROUTES.renderHomePage, supportsBack: false } as const,
378
+ SETTINGS: { id: 'SCR-002' as const, route: _OP_ROUTES.renderSettingsPage, supportsBack: true } as const,
379
+ } as const;
380
+
381
+ export const HOME_PAGE_LINKS = {
382
+ goToSettings: _OP_ROUTES.renderSettingsPage,
383
+ goToChat: _OP_ROUTES.renderChatPage,
384
+ } as const;
385
+ ```
386
+
387
+ ### screen-events.hbs (inline x-event)
388
+
389
+ Generates analytics event hooks using the new inline `x-event` structure. Templates can use `screenEvent`, `links[].event`, `actions[].event`, and `interactions[].event`.
390
+
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:
441
+
442
+ ```typescript
443
+ export function useHomePageEvents() {
444
+ return {
445
+ trackView: () =>
446
+ trackEvent('home_view', 'screen_view', {}),
447
+ trackSignOut: () =>
448
+ trackEvent('sign_out', 'user_action', {}),
449
+ };
450
+ }
451
+ ```
452
+
453
+ ---
454
+
455
+ ## Lint Rules
456
+
457
+ `micro-contracts lint` performs the following screen-specific checks:
458
+
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) |
471
+
472
+ In `screen: true` modules, `MISSING_X_SERVICE` / `MISSING_X_METHOD` warnings are suppressed (screen specs do not need service/method declarations).
473
+
474
+ ---
475
+
476
+ ## Custom Templates
477
+
478
+ You can extend the default templates or create new ones for additional generated artifacts. All `ScreenContext` fields are available in templates.
479
+
480
+ ### ViewProps Template Example
481
+
482
+ `spec/default/templates/screen-view-props.hbs`:
483
+
484
+ ```handlebars
485
+ {{#each screens}}
486
+ export interface {{screenName}}ViewProps {
487
+ // ViewModel: {{viewModelSchema}}
488
+ // Screen ID: {{screenId}}
489
+ }
490
+ {{/each}}
491
+ ```
492
+
493
+ Add to config:
494
+
495
+ ```yaml
496
+ outputs:
497
+ screen-view-props:
498
+ output: frontend/src/screens/view-props.generated.ts
499
+ template: screen-view-props.hbs
500
+ ```
501
+
502
+ ### Available Template Context
503
+
504
+ | Field | Type | Description |
505
+ | ----------------- | --------- | -------------------------------------------------- |
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 |
512
+ | `spec` | object | Raw OpenAPI spec (for direct access when needed) |
513
+ | `title` | string | OpenAPI title |
514
+ | `version` | string | OpenAPI version |
515
+ | `moduleName` | string | Module name |
516
+ | `contractPackage` | string | Contract package import path |
517
+ | `schemaNames` | string[] | All schema names |
518
+
519
+ ---
520
+
521
+ ## Tiered Integration
522
+
523
+ ### Tier 1 — Core (built into micro-contracts)
524
+
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
530
+ - `init --screens`: Starter file generation
531
+ - Default templates: `screen-navigation.hbs`, `screen-events.hbs`
532
+ - Handlebars helpers: `hasEvent`, `eventParamsSignature`
533
+
534
+ ### Tier 2 — Optional Packages / Official Recipes (future)
535
+
536
+ | Package | Purpose |
537
+ | ------------------------------ | ------------------------------------------------------------------------ |
538
+ | `@micro-contracts/e2e-scaffold`| Parse screen spec to generate Playwright test stubs with coverage |
539
+ | ViewProp / data-testid pattern | Derive stable test IDs from ViewModel fields + `x-screen-const` |
540
+
541
+ ### Tier 3 — Documentation Only (future)
542
+
543
+ | Pattern | Description |
544
+ | ------------------------- | -------------------------------------------------------------------- |
545
+ | Design tool integration | Link screen IDs to design tool frames via `x-pen-node` or similar |
546
+ | Requirements traceability | Link screens to user stories via `x-satisfies` |
547
+ | Data lineage declarations | Declare upstream API/DB origins on ViewModel schemas via `x-lineage-source` |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "micro-contracts",
3
- "version": "0.12.3",
3
+ "version": "0.14.0",
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",