@screenbook/core 0.1.0 → 1.1.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.
- package/README.md +27 -0
- package/dist/index.cjs +145 -2
- package/dist/index.d.cts +162 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +162 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +144 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -2
package/README.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# @screenbook/core
|
|
2
|
+
|
|
3
|
+
Core types and utilities for Screenbook.
|
|
4
|
+
|
|
5
|
+
> **Note**: This package is part of [Screenbook](https://github.com/wadakatu/screenbook).
|
|
6
|
+
> For installation and usage, see the main documentation.
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
pnpm add -D screenbook
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## What's included
|
|
15
|
+
|
|
16
|
+
- `defineScreen()` - Define screen metadata
|
|
17
|
+
- `defineConfig()` - Configure Screenbook
|
|
18
|
+
- TypeScript types and Zod schemas
|
|
19
|
+
|
|
20
|
+
## Links
|
|
21
|
+
|
|
22
|
+
- [Documentation](https://github.com/wadakatu/screenbook#readme)
|
|
23
|
+
- [GitHub](https://github.com/wadakatu/screenbook)
|
|
24
|
+
|
|
25
|
+
## License
|
|
26
|
+
|
|
27
|
+
MIT
|
package/dist/index.cjs
CHANGED
|
@@ -1,6 +1,127 @@
|
|
|
1
1
|
let zod = require("zod");
|
|
2
2
|
|
|
3
|
+
//#region src/extractNavigationTargets.ts
|
|
4
|
+
/**
|
|
5
|
+
* Extracts all navigation target screen IDs from a mock definition.
|
|
6
|
+
*
|
|
7
|
+
* This function collects all `navigateTo`, `itemNavigateTo`, and `rowNavigateTo`
|
|
8
|
+
* values from mock elements to automatically derive the `next` array.
|
|
9
|
+
* It recursively processes child sections.
|
|
10
|
+
*
|
|
11
|
+
* @param mock - The screen mock definition
|
|
12
|
+
* @returns Array of unique screen IDs that can be navigated to
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* const mock = {
|
|
17
|
+
* sections: [{
|
|
18
|
+
* elements: [
|
|
19
|
+
* { type: "button", label: "Edit", navigateTo: "billing.invoice.edit" },
|
|
20
|
+
* { type: "list", label: "Items", itemNavigateTo: "billing.item.detail" },
|
|
21
|
+
* ]
|
|
22
|
+
* }]
|
|
23
|
+
* }
|
|
24
|
+
* extractNavigationTargets(mock)
|
|
25
|
+
* // => ["billing.invoice.edit", "billing.item.detail"]
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
function extractNavigationTargets(mock) {
|
|
29
|
+
const targets = /* @__PURE__ */ new Set();
|
|
30
|
+
function processSection(section) {
|
|
31
|
+
for (const element of section.elements) {
|
|
32
|
+
if ("navigateTo" in element && element.navigateTo) targets.add(element.navigateTo);
|
|
33
|
+
if ("itemNavigateTo" in element && element.itemNavigateTo) targets.add(element.itemNavigateTo);
|
|
34
|
+
if ("rowNavigateTo" in element && element.rowNavigateTo) targets.add(element.rowNavigateTo);
|
|
35
|
+
}
|
|
36
|
+
if (section.children) for (const child of section.children) processSection(child);
|
|
37
|
+
}
|
|
38
|
+
for (const section of mock.sections) processSection(section);
|
|
39
|
+
return Array.from(targets);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
//#endregion
|
|
3
43
|
//#region src/types.ts
|
|
44
|
+
const mockLayoutSchema = zod.z.enum(["vertical", "horizontal"]);
|
|
45
|
+
const mockElementBaseSchema = zod.z.object({
|
|
46
|
+
type: zod.z.enum([
|
|
47
|
+
"button",
|
|
48
|
+
"input",
|
|
49
|
+
"link",
|
|
50
|
+
"text",
|
|
51
|
+
"image",
|
|
52
|
+
"list",
|
|
53
|
+
"table"
|
|
54
|
+
]),
|
|
55
|
+
label: zod.z.string().min(1)
|
|
56
|
+
});
|
|
57
|
+
const mockButtonElementSchema = mockElementBaseSchema.extend({
|
|
58
|
+
type: zod.z.literal("button"),
|
|
59
|
+
navigateTo: zod.z.string().optional(),
|
|
60
|
+
variant: zod.z.enum([
|
|
61
|
+
"primary",
|
|
62
|
+
"secondary",
|
|
63
|
+
"danger"
|
|
64
|
+
]).optional()
|
|
65
|
+
});
|
|
66
|
+
const mockInputElementSchema = mockElementBaseSchema.extend({
|
|
67
|
+
type: zod.z.literal("input"),
|
|
68
|
+
placeholder: zod.z.string().optional(),
|
|
69
|
+
inputType: zod.z.enum([
|
|
70
|
+
"text",
|
|
71
|
+
"email",
|
|
72
|
+
"password",
|
|
73
|
+
"textarea",
|
|
74
|
+
"search"
|
|
75
|
+
]).optional()
|
|
76
|
+
});
|
|
77
|
+
const mockLinkElementSchema = mockElementBaseSchema.extend({
|
|
78
|
+
type: zod.z.literal("link"),
|
|
79
|
+
navigateTo: zod.z.string().optional()
|
|
80
|
+
});
|
|
81
|
+
const mockTextElementSchema = mockElementBaseSchema.extend({
|
|
82
|
+
type: zod.z.literal("text"),
|
|
83
|
+
variant: zod.z.enum([
|
|
84
|
+
"heading",
|
|
85
|
+
"subheading",
|
|
86
|
+
"body",
|
|
87
|
+
"caption"
|
|
88
|
+
]).optional()
|
|
89
|
+
});
|
|
90
|
+
const mockImageElementSchema = mockElementBaseSchema.extend({
|
|
91
|
+
type: zod.z.literal("image"),
|
|
92
|
+
aspectRatio: zod.z.string().optional()
|
|
93
|
+
});
|
|
94
|
+
const mockListElementSchema = mockElementBaseSchema.extend({
|
|
95
|
+
type: zod.z.literal("list"),
|
|
96
|
+
itemCount: zod.z.number().int().positive().optional(),
|
|
97
|
+
itemNavigateTo: zod.z.string().optional()
|
|
98
|
+
});
|
|
99
|
+
const mockTableElementSchema = mockElementBaseSchema.extend({
|
|
100
|
+
type: zod.z.literal("table"),
|
|
101
|
+
columns: zod.z.array(zod.z.string()).optional(),
|
|
102
|
+
rowCount: zod.z.number().int().positive().optional(),
|
|
103
|
+
rowNavigateTo: zod.z.string().optional()
|
|
104
|
+
});
|
|
105
|
+
const mockElementSchema = zod.z.discriminatedUnion("type", [
|
|
106
|
+
mockButtonElementSchema,
|
|
107
|
+
mockInputElementSchema,
|
|
108
|
+
mockLinkElementSchema,
|
|
109
|
+
mockTextElementSchema,
|
|
110
|
+
mockImageElementSchema,
|
|
111
|
+
mockListElementSchema,
|
|
112
|
+
mockTableElementSchema
|
|
113
|
+
]);
|
|
114
|
+
const mockSectionSchema = zod.z.lazy(() => zod.z.object({
|
|
115
|
+
title: zod.z.string().optional(),
|
|
116
|
+
layout: mockLayoutSchema.optional(),
|
|
117
|
+
elements: zod.z.array(mockElementSchema),
|
|
118
|
+
children: zod.z.array(mockSectionSchema).optional()
|
|
119
|
+
}));
|
|
120
|
+
/**
|
|
121
|
+
* Schema for screen mock definition (runtime validation)
|
|
122
|
+
* @internal
|
|
123
|
+
*/
|
|
124
|
+
const screenMockSchema = zod.z.object({ sections: zod.z.array(mockSectionSchema) });
|
|
4
125
|
/**
|
|
5
126
|
* Schema for screen metadata definition (runtime validation)
|
|
6
127
|
* @internal
|
|
@@ -19,7 +140,8 @@ const screenSchema = zod.z.object({
|
|
|
19
140
|
links: zod.z.array(zod.z.object({
|
|
20
141
|
label: zod.z.string(),
|
|
21
142
|
url: zod.z.string().url()
|
|
22
|
-
})).optional()
|
|
143
|
+
})).optional(),
|
|
144
|
+
mock: screenMockSchema.optional()
|
|
23
145
|
});
|
|
24
146
|
/**
|
|
25
147
|
* Schema for progressive adoption configuration (runtime validation)
|
|
@@ -47,6 +169,9 @@ const configSchema = zod.z.object({
|
|
|
47
169
|
/**
|
|
48
170
|
* Define a screen with metadata for the screen catalog.
|
|
49
171
|
*
|
|
172
|
+
* When a `mock` is defined, navigation targets (navigateTo, itemNavigateTo,
|
|
173
|
+
* rowNavigateTo) are automatically extracted and merged into the `next` array.
|
|
174
|
+
*
|
|
50
175
|
* @example
|
|
51
176
|
* ```ts
|
|
52
177
|
* export const screen = defineScreen({
|
|
@@ -58,11 +183,27 @@ const configSchema = zod.z.object({
|
|
|
58
183
|
* dependsOn: ["InvoiceAPI.getDetail"],
|
|
59
184
|
* entryPoints: ["billing.invoice.list"],
|
|
60
185
|
* next: ["billing.invoice.edit"],
|
|
186
|
+
* mock: {
|
|
187
|
+
* sections: [{
|
|
188
|
+
* elements: [
|
|
189
|
+
* { type: "button", label: "Pay", navigateTo: "billing.payment.start" },
|
|
190
|
+
* ],
|
|
191
|
+
* }],
|
|
192
|
+
* },
|
|
61
193
|
* })
|
|
194
|
+
* // next will be ["billing.invoice.edit", "billing.payment.start"]
|
|
62
195
|
* ```
|
|
63
196
|
*/
|
|
64
197
|
function defineScreen(input) {
|
|
65
|
-
|
|
198
|
+
const validated = screenSchema.parse(input);
|
|
199
|
+
if (validated.mock) {
|
|
200
|
+
const mockTargets = extractNavigationTargets(validated.mock);
|
|
201
|
+
if (mockTargets.length > 0) {
|
|
202
|
+
const existingNext = validated.next ?? [];
|
|
203
|
+
validated.next = [...new Set([...existingNext, ...mockTargets])];
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return validated;
|
|
66
207
|
}
|
|
67
208
|
/**
|
|
68
209
|
* Define Screenbook configuration.
|
|
@@ -85,4 +226,6 @@ exports.adoptionSchema = adoptionSchema;
|
|
|
85
226
|
exports.configSchema = configSchema;
|
|
86
227
|
exports.defineConfig = defineConfig;
|
|
87
228
|
exports.defineScreen = defineScreen;
|
|
229
|
+
exports.extractNavigationTargets = extractNavigationTargets;
|
|
230
|
+
exports.screenMockSchema = screenMockSchema;
|
|
88
231
|
exports.screenSchema = screenSchema;
|
package/dist/index.d.cts
CHANGED
|
@@ -104,7 +104,127 @@ interface Screen {
|
|
|
104
104
|
* @example [{ label: "Figma", url: "https://figma.com/file/..." }]
|
|
105
105
|
*/
|
|
106
106
|
links?: ScreenLink[];
|
|
107
|
+
/**
|
|
108
|
+
* Wireframe-level mock definition for UI documentation.
|
|
109
|
+
* When defined, navigation targets (navigateTo, itemNavigateTo, rowNavigateTo)
|
|
110
|
+
* are automatically extracted and merged into the `next` array.
|
|
111
|
+
*/
|
|
112
|
+
mock?: ScreenMock;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Layout direction for mock sections
|
|
116
|
+
*/
|
|
117
|
+
type MockLayout = "vertical" | "horizontal";
|
|
118
|
+
/**
|
|
119
|
+
* Available element types for mock wireframes
|
|
120
|
+
*/
|
|
121
|
+
type MockElementType = "button" | "input" | "link" | "text" | "image" | "list" | "table";
|
|
122
|
+
/**
|
|
123
|
+
* Base interface for all mock elements
|
|
124
|
+
*/
|
|
125
|
+
interface MockElementBase {
|
|
126
|
+
/** Element type identifier */
|
|
127
|
+
type: MockElementType;
|
|
128
|
+
/** Display label for the element */
|
|
129
|
+
label: string;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Button element with optional navigation and styling
|
|
133
|
+
*/
|
|
134
|
+
interface MockButtonElement extends MockElementBase {
|
|
135
|
+
type: "button";
|
|
136
|
+
/** Screen ID to navigate to when clicked */
|
|
137
|
+
navigateTo?: string;
|
|
138
|
+
/** Visual style variant */
|
|
139
|
+
variant?: "primary" | "secondary" | "danger";
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Input field element
|
|
143
|
+
*/
|
|
144
|
+
interface MockInputElement extends MockElementBase {
|
|
145
|
+
type: "input";
|
|
146
|
+
/** Placeholder text */
|
|
147
|
+
placeholder?: string;
|
|
148
|
+
/** Input type for semantics */
|
|
149
|
+
inputType?: "text" | "email" | "password" | "textarea" | "search";
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Link element with optional navigation
|
|
153
|
+
*/
|
|
154
|
+
interface MockLinkElement extends MockElementBase {
|
|
155
|
+
type: "link";
|
|
156
|
+
/** Screen ID to navigate to when clicked */
|
|
157
|
+
navigateTo?: string;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Text element for labels and headings
|
|
161
|
+
*/
|
|
162
|
+
interface MockTextElement extends MockElementBase {
|
|
163
|
+
type: "text";
|
|
164
|
+
/** Text style variant */
|
|
165
|
+
variant?: "heading" | "subheading" | "body" | "caption";
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Image placeholder element
|
|
169
|
+
*/
|
|
170
|
+
interface MockImageElement extends MockElementBase {
|
|
171
|
+
type: "image";
|
|
172
|
+
/** Aspect ratio (e.g., "16:9", "1:1") */
|
|
173
|
+
aspectRatio?: string;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* List element for displaying multiple items
|
|
177
|
+
*/
|
|
178
|
+
interface MockListElement extends MockElementBase {
|
|
179
|
+
type: "list";
|
|
180
|
+
/** Number of items to display (default: 3) */
|
|
181
|
+
itemCount?: number;
|
|
182
|
+
/** Screen ID to navigate to when an item is clicked */
|
|
183
|
+
itemNavigateTo?: string;
|
|
107
184
|
}
|
|
185
|
+
/**
|
|
186
|
+
* Table element for displaying tabular data
|
|
187
|
+
*/
|
|
188
|
+
interface MockTableElement extends MockElementBase {
|
|
189
|
+
type: "table";
|
|
190
|
+
/** Column headers */
|
|
191
|
+
columns?: string[];
|
|
192
|
+
/** Number of rows to display (default: 3) */
|
|
193
|
+
rowCount?: number;
|
|
194
|
+
/** Screen ID to navigate to when a row is clicked */
|
|
195
|
+
rowNavigateTo?: string;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Union type of all mock element variants
|
|
199
|
+
*/
|
|
200
|
+
type MockElement = MockButtonElement | MockInputElement | MockLinkElement | MockTextElement | MockImageElement | MockListElement | MockTableElement;
|
|
201
|
+
/**
|
|
202
|
+
* Section containing grouped mock elements
|
|
203
|
+
*/
|
|
204
|
+
interface MockSection {
|
|
205
|
+
/** Optional section title */
|
|
206
|
+
title?: string;
|
|
207
|
+
/** Layout direction (default: "vertical") */
|
|
208
|
+
layout?: MockLayout;
|
|
209
|
+
/** Elements within this section */
|
|
210
|
+
elements: MockElement[];
|
|
211
|
+
/** Nested child sections */
|
|
212
|
+
children?: MockSection[];
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Complete mock definition for a screen
|
|
216
|
+
*/
|
|
217
|
+
interface ScreenMock {
|
|
218
|
+
/** Sections containing UI elements */
|
|
219
|
+
sections: MockSection[];
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Schema for screen mock definition (runtime validation)
|
|
223
|
+
* @internal
|
|
224
|
+
*/
|
|
225
|
+
declare const screenMockSchema: z.ZodObject<{
|
|
226
|
+
sections: z.ZodArray<z.ZodType<MockSection, unknown, z.core.$ZodTypeInternals<MockSection, unknown>>>;
|
|
227
|
+
}, z.core.$strip>;
|
|
108
228
|
/**
|
|
109
229
|
* Input type for defineScreen function.
|
|
110
230
|
* Same as Screen but used for input validation.
|
|
@@ -129,6 +249,9 @@ declare const screenSchema: z.ZodObject<{
|
|
|
129
249
|
label: z.ZodString;
|
|
130
250
|
url: z.ZodString;
|
|
131
251
|
}, z.core.$strip>>>;
|
|
252
|
+
mock: z.ZodOptional<z.ZodObject<{
|
|
253
|
+
sections: z.ZodArray<z.ZodType<MockSection, unknown, z.core.$ZodTypeInternals<MockSection, unknown>>>;
|
|
254
|
+
}, z.core.$strip>>;
|
|
132
255
|
}, z.core.$strip>;
|
|
133
256
|
/**
|
|
134
257
|
* Progressive adoption configuration for gradual rollout.
|
|
@@ -277,6 +400,9 @@ declare const configSchema: z.ZodObject<{
|
|
|
277
400
|
/**
|
|
278
401
|
* Define a screen with metadata for the screen catalog.
|
|
279
402
|
*
|
|
403
|
+
* When a `mock` is defined, navigation targets (navigateTo, itemNavigateTo,
|
|
404
|
+
* rowNavigateTo) are automatically extracted and merged into the `next` array.
|
|
405
|
+
*
|
|
280
406
|
* @example
|
|
281
407
|
* ```ts
|
|
282
408
|
* export const screen = defineScreen({
|
|
@@ -288,7 +414,15 @@ declare const configSchema: z.ZodObject<{
|
|
|
288
414
|
* dependsOn: ["InvoiceAPI.getDetail"],
|
|
289
415
|
* entryPoints: ["billing.invoice.list"],
|
|
290
416
|
* next: ["billing.invoice.edit"],
|
|
417
|
+
* mock: {
|
|
418
|
+
* sections: [{
|
|
419
|
+
* elements: [
|
|
420
|
+
* { type: "button", label: "Pay", navigateTo: "billing.payment.start" },
|
|
421
|
+
* ],
|
|
422
|
+
* }],
|
|
423
|
+
* },
|
|
291
424
|
* })
|
|
425
|
+
* // next will be ["billing.invoice.edit", "billing.payment.start"]
|
|
292
426
|
* ```
|
|
293
427
|
*/
|
|
294
428
|
declare function defineScreen(input: ScreenInput): Screen;
|
|
@@ -306,5 +440,32 @@ declare function defineScreen(input: ScreenInput): Screen;
|
|
|
306
440
|
*/
|
|
307
441
|
declare function defineConfig(input?: ConfigInput): Config;
|
|
308
442
|
//#endregion
|
|
309
|
-
|
|
443
|
+
//#region src/extractNavigationTargets.d.ts
|
|
444
|
+
/**
|
|
445
|
+
* Extracts all navigation target screen IDs from a mock definition.
|
|
446
|
+
*
|
|
447
|
+
* This function collects all `navigateTo`, `itemNavigateTo`, and `rowNavigateTo`
|
|
448
|
+
* values from mock elements to automatically derive the `next` array.
|
|
449
|
+
* It recursively processes child sections.
|
|
450
|
+
*
|
|
451
|
+
* @param mock - The screen mock definition
|
|
452
|
+
* @returns Array of unique screen IDs that can be navigated to
|
|
453
|
+
*
|
|
454
|
+
* @example
|
|
455
|
+
* ```ts
|
|
456
|
+
* const mock = {
|
|
457
|
+
* sections: [{
|
|
458
|
+
* elements: [
|
|
459
|
+
* { type: "button", label: "Edit", navigateTo: "billing.invoice.edit" },
|
|
460
|
+
* { type: "list", label: "Items", itemNavigateTo: "billing.item.detail" },
|
|
461
|
+
* ]
|
|
462
|
+
* }]
|
|
463
|
+
* }
|
|
464
|
+
* extractNavigationTargets(mock)
|
|
465
|
+
* // => ["billing.invoice.edit", "billing.item.detail"]
|
|
466
|
+
* ```
|
|
467
|
+
*/
|
|
468
|
+
declare function extractNavigationTargets(mock: ScreenMock): string[];
|
|
469
|
+
//#endregion
|
|
470
|
+
export { type AdoptionConfig, type Config, type ConfigInput, type MockButtonElement, type MockElement, type MockElementType, type MockImageElement, type MockInputElement, type MockLayout, type MockLinkElement, type MockListElement, type MockSection, type MockTableElement, type MockTextElement, type Screen, type ScreenInput, type ScreenLink, type ScreenMock, adoptionSchema, configSchema, defineConfig, defineScreen, extractNavigationTargets, screenMockSchema, screenSchema };
|
|
310
471
|
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/types.ts","../src/defineScreen.ts"],"sourcesContent":[],"mappings":";;;;;;AAKA;AA+BiB,UA/BA,UAAA,CA6GR;
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/types.ts","../src/defineScreen.ts","../src/extractNavigationTargets.ts"],"sourcesContent":[],"mappings":";;;;;;AAKA;AA+BiB,UA/BA,UAAA,CA6GR;EAiBG;AAKZ;AAOU;AAeV;AAWA;EAWiB,KAAA,EAAA,MAAA;EASA;AASjB;AASA;AAWA;AAaA;EACG,GAAA,EAAA,MAAA;;;;;;;;AAWH;;;;;AAcA;AAiFA;;;;UA9SiB,MAAA;;;;;AAwTjB;AAMA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAAyB,WAAA,CAAA,EAAA,MAAA,EAAA;EAkCR;AA8BjB;;;;;;;;;;;EASiB,WAAM,CAAA,EAAA,OAoCX;EAeK;AAqCjB;;;;;;;;UAjZS;;;;;;SAOD;;;;;KAUI,UAAA;;;;KAKA,eAAA;;;AClGZ;AA6BA,UDiFU,eAAA,CCjFkB;;QDmFrB;;EE5HS,KAAA,EAAA,MAAA;;;;;UFoIC,iBAAA,SAA0B;;;;;;;;;;UAW1B,gBAAA,SAAyB;;;;;;;;;;UAWzB,eAAA,SAAwB;;;;;;;;UASxB,eAAA,SAAwB;;;;;;;;UASxB,gBAAA,SAAyB;;;;;;;;UASzB,eAAA,SAAwB;;;;;;;;;;UAWxB,gBAAA,SAAyB;;;;;;;;;;;;KAa9B,WAAA,GACT,oBACA,mBACA,kBACA,kBACA,mBACA,kBACA;;;;UAKc,WAAA;;;;WAIP;;YAEC;;aAEC;;;;;UAMK,UAAA;;YAEN;;;;;;cA+EE,kBAAgB,CAAA,CAAA;;;;;;;KAUjB,WAAA,GAAc;;;;;cAMb,cAAY,CAAA,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAkCR,cAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;cA8BJ,gBAAc,CAAA,CAAA;;;;;;;;;;;UASV,MAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAoCL;;;;;;;;;;;;;;UAeK,WAAA;;;;;;;;;;;;;;;;;;;;;;;;;;aA8BL;;;;;;cAOC,cAAY,CAAA,CAAA;;;;;;;;;;;;;;;;;;AA9fzB;AA+BA;AA+FA;AAKA;AAOU;AAeV;AAWA;AAWA;AASA;AASA;AASA;AAWA;AAaA;;;;;;;;;AAYA;;;;;AAciB,iBC3ND,YAAA,CD6NL,KAAW,EC7Nc,WD6Nd,CAAA,EC7N4B,MD6N5B;AA+EtB;;;;;;;;;AAUA;AAMA;;iBC/RgB,YAAA,SAAoB,cAAmB;;;;;AD9DvD;AA+BA;AA+FA;AAKA;AAOU;AAeV;AAWA;AAWA;AASA;AASA;AASA;AAWA;AAaA;;;;;;;;;AAYA;AAIU,iBE7NM,wBAAA,CF6NN,IAAA,EE7NqC,UF6NrC,CAAA,EAAA,MAAA,EAAA"}
|
package/dist/index.d.mts
CHANGED
|
@@ -104,7 +104,127 @@ interface Screen {
|
|
|
104
104
|
* @example [{ label: "Figma", url: "https://figma.com/file/..." }]
|
|
105
105
|
*/
|
|
106
106
|
links?: ScreenLink[];
|
|
107
|
+
/**
|
|
108
|
+
* Wireframe-level mock definition for UI documentation.
|
|
109
|
+
* When defined, navigation targets (navigateTo, itemNavigateTo, rowNavigateTo)
|
|
110
|
+
* are automatically extracted and merged into the `next` array.
|
|
111
|
+
*/
|
|
112
|
+
mock?: ScreenMock;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Layout direction for mock sections
|
|
116
|
+
*/
|
|
117
|
+
type MockLayout = "vertical" | "horizontal";
|
|
118
|
+
/**
|
|
119
|
+
* Available element types for mock wireframes
|
|
120
|
+
*/
|
|
121
|
+
type MockElementType = "button" | "input" | "link" | "text" | "image" | "list" | "table";
|
|
122
|
+
/**
|
|
123
|
+
* Base interface for all mock elements
|
|
124
|
+
*/
|
|
125
|
+
interface MockElementBase {
|
|
126
|
+
/** Element type identifier */
|
|
127
|
+
type: MockElementType;
|
|
128
|
+
/** Display label for the element */
|
|
129
|
+
label: string;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Button element with optional navigation and styling
|
|
133
|
+
*/
|
|
134
|
+
interface MockButtonElement extends MockElementBase {
|
|
135
|
+
type: "button";
|
|
136
|
+
/** Screen ID to navigate to when clicked */
|
|
137
|
+
navigateTo?: string;
|
|
138
|
+
/** Visual style variant */
|
|
139
|
+
variant?: "primary" | "secondary" | "danger";
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Input field element
|
|
143
|
+
*/
|
|
144
|
+
interface MockInputElement extends MockElementBase {
|
|
145
|
+
type: "input";
|
|
146
|
+
/** Placeholder text */
|
|
147
|
+
placeholder?: string;
|
|
148
|
+
/** Input type for semantics */
|
|
149
|
+
inputType?: "text" | "email" | "password" | "textarea" | "search";
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Link element with optional navigation
|
|
153
|
+
*/
|
|
154
|
+
interface MockLinkElement extends MockElementBase {
|
|
155
|
+
type: "link";
|
|
156
|
+
/** Screen ID to navigate to when clicked */
|
|
157
|
+
navigateTo?: string;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Text element for labels and headings
|
|
161
|
+
*/
|
|
162
|
+
interface MockTextElement extends MockElementBase {
|
|
163
|
+
type: "text";
|
|
164
|
+
/** Text style variant */
|
|
165
|
+
variant?: "heading" | "subheading" | "body" | "caption";
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Image placeholder element
|
|
169
|
+
*/
|
|
170
|
+
interface MockImageElement extends MockElementBase {
|
|
171
|
+
type: "image";
|
|
172
|
+
/** Aspect ratio (e.g., "16:9", "1:1") */
|
|
173
|
+
aspectRatio?: string;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* List element for displaying multiple items
|
|
177
|
+
*/
|
|
178
|
+
interface MockListElement extends MockElementBase {
|
|
179
|
+
type: "list";
|
|
180
|
+
/** Number of items to display (default: 3) */
|
|
181
|
+
itemCount?: number;
|
|
182
|
+
/** Screen ID to navigate to when an item is clicked */
|
|
183
|
+
itemNavigateTo?: string;
|
|
107
184
|
}
|
|
185
|
+
/**
|
|
186
|
+
* Table element for displaying tabular data
|
|
187
|
+
*/
|
|
188
|
+
interface MockTableElement extends MockElementBase {
|
|
189
|
+
type: "table";
|
|
190
|
+
/** Column headers */
|
|
191
|
+
columns?: string[];
|
|
192
|
+
/** Number of rows to display (default: 3) */
|
|
193
|
+
rowCount?: number;
|
|
194
|
+
/** Screen ID to navigate to when a row is clicked */
|
|
195
|
+
rowNavigateTo?: string;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Union type of all mock element variants
|
|
199
|
+
*/
|
|
200
|
+
type MockElement = MockButtonElement | MockInputElement | MockLinkElement | MockTextElement | MockImageElement | MockListElement | MockTableElement;
|
|
201
|
+
/**
|
|
202
|
+
* Section containing grouped mock elements
|
|
203
|
+
*/
|
|
204
|
+
interface MockSection {
|
|
205
|
+
/** Optional section title */
|
|
206
|
+
title?: string;
|
|
207
|
+
/** Layout direction (default: "vertical") */
|
|
208
|
+
layout?: MockLayout;
|
|
209
|
+
/** Elements within this section */
|
|
210
|
+
elements: MockElement[];
|
|
211
|
+
/** Nested child sections */
|
|
212
|
+
children?: MockSection[];
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Complete mock definition for a screen
|
|
216
|
+
*/
|
|
217
|
+
interface ScreenMock {
|
|
218
|
+
/** Sections containing UI elements */
|
|
219
|
+
sections: MockSection[];
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Schema for screen mock definition (runtime validation)
|
|
223
|
+
* @internal
|
|
224
|
+
*/
|
|
225
|
+
declare const screenMockSchema: z.ZodObject<{
|
|
226
|
+
sections: z.ZodArray<z.ZodType<MockSection, unknown, z.core.$ZodTypeInternals<MockSection, unknown>>>;
|
|
227
|
+
}, z.core.$strip>;
|
|
108
228
|
/**
|
|
109
229
|
* Input type for defineScreen function.
|
|
110
230
|
* Same as Screen but used for input validation.
|
|
@@ -129,6 +249,9 @@ declare const screenSchema: z.ZodObject<{
|
|
|
129
249
|
label: z.ZodString;
|
|
130
250
|
url: z.ZodString;
|
|
131
251
|
}, z.core.$strip>>>;
|
|
252
|
+
mock: z.ZodOptional<z.ZodObject<{
|
|
253
|
+
sections: z.ZodArray<z.ZodType<MockSection, unknown, z.core.$ZodTypeInternals<MockSection, unknown>>>;
|
|
254
|
+
}, z.core.$strip>>;
|
|
132
255
|
}, z.core.$strip>;
|
|
133
256
|
/**
|
|
134
257
|
* Progressive adoption configuration for gradual rollout.
|
|
@@ -277,6 +400,9 @@ declare const configSchema: z.ZodObject<{
|
|
|
277
400
|
/**
|
|
278
401
|
* Define a screen with metadata for the screen catalog.
|
|
279
402
|
*
|
|
403
|
+
* When a `mock` is defined, navigation targets (navigateTo, itemNavigateTo,
|
|
404
|
+
* rowNavigateTo) are automatically extracted and merged into the `next` array.
|
|
405
|
+
*
|
|
280
406
|
* @example
|
|
281
407
|
* ```ts
|
|
282
408
|
* export const screen = defineScreen({
|
|
@@ -288,7 +414,15 @@ declare const configSchema: z.ZodObject<{
|
|
|
288
414
|
* dependsOn: ["InvoiceAPI.getDetail"],
|
|
289
415
|
* entryPoints: ["billing.invoice.list"],
|
|
290
416
|
* next: ["billing.invoice.edit"],
|
|
417
|
+
* mock: {
|
|
418
|
+
* sections: [{
|
|
419
|
+
* elements: [
|
|
420
|
+
* { type: "button", label: "Pay", navigateTo: "billing.payment.start" },
|
|
421
|
+
* ],
|
|
422
|
+
* }],
|
|
423
|
+
* },
|
|
291
424
|
* })
|
|
425
|
+
* // next will be ["billing.invoice.edit", "billing.payment.start"]
|
|
292
426
|
* ```
|
|
293
427
|
*/
|
|
294
428
|
declare function defineScreen(input: ScreenInput): Screen;
|
|
@@ -306,5 +440,32 @@ declare function defineScreen(input: ScreenInput): Screen;
|
|
|
306
440
|
*/
|
|
307
441
|
declare function defineConfig(input?: ConfigInput): Config;
|
|
308
442
|
//#endregion
|
|
309
|
-
|
|
443
|
+
//#region src/extractNavigationTargets.d.ts
|
|
444
|
+
/**
|
|
445
|
+
* Extracts all navigation target screen IDs from a mock definition.
|
|
446
|
+
*
|
|
447
|
+
* This function collects all `navigateTo`, `itemNavigateTo`, and `rowNavigateTo`
|
|
448
|
+
* values from mock elements to automatically derive the `next` array.
|
|
449
|
+
* It recursively processes child sections.
|
|
450
|
+
*
|
|
451
|
+
* @param mock - The screen mock definition
|
|
452
|
+
* @returns Array of unique screen IDs that can be navigated to
|
|
453
|
+
*
|
|
454
|
+
* @example
|
|
455
|
+
* ```ts
|
|
456
|
+
* const mock = {
|
|
457
|
+
* sections: [{
|
|
458
|
+
* elements: [
|
|
459
|
+
* { type: "button", label: "Edit", navigateTo: "billing.invoice.edit" },
|
|
460
|
+
* { type: "list", label: "Items", itemNavigateTo: "billing.item.detail" },
|
|
461
|
+
* ]
|
|
462
|
+
* }]
|
|
463
|
+
* }
|
|
464
|
+
* extractNavigationTargets(mock)
|
|
465
|
+
* // => ["billing.invoice.edit", "billing.item.detail"]
|
|
466
|
+
* ```
|
|
467
|
+
*/
|
|
468
|
+
declare function extractNavigationTargets(mock: ScreenMock): string[];
|
|
469
|
+
//#endregion
|
|
470
|
+
export { type AdoptionConfig, type Config, type ConfigInput, type MockButtonElement, type MockElement, type MockElementType, type MockImageElement, type MockInputElement, type MockLayout, type MockLinkElement, type MockListElement, type MockSection, type MockTableElement, type MockTextElement, type Screen, type ScreenInput, type ScreenLink, type ScreenMock, adoptionSchema, configSchema, defineConfig, defineScreen, extractNavigationTargets, screenMockSchema, screenSchema };
|
|
310
471
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/defineScreen.ts"],"sourcesContent":[],"mappings":";;;;;;AAKA;AA+BiB,UA/BA,UAAA,CA6GR;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/defineScreen.ts","../src/extractNavigationTargets.ts"],"sourcesContent":[],"mappings":";;;;;;AAKA;AA+BiB,UA/BA,UAAA,CA6GR;EAiBG;AAKZ;AAOU;AAeV;AAWA;EAWiB,KAAA,EAAA,MAAA;EASA;AASjB;AASA;AAWA;AAaA;EACG,GAAA,EAAA,MAAA;;;;;;;;AAWH;;;;;AAcA;AAiFA;;;;UA9SiB,MAAA;;;;;AAwTjB;AAMA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAAyB,WAAA,CAAA,EAAA,MAAA,EAAA;EAkCR;AA8BjB;;;;;;;;;;;EASiB,WAAM,CAAA,EAAA,OAoCX;EAeK;AAqCjB;;;;;;;;UAjZS;;;;;;SAOD;;;;;KAUI,UAAA;;;;KAKA,eAAA;;;AClGZ;AA6BA,UDiFU,eAAA,CCjFkB;;QDmFrB;;EE5HS,KAAA,EAAA,MAAA;;;;;UFoIC,iBAAA,SAA0B;;;;;;;;;;UAW1B,gBAAA,SAAyB;;;;;;;;;;UAWzB,eAAA,SAAwB;;;;;;;;UASxB,eAAA,SAAwB;;;;;;;;UASxB,gBAAA,SAAyB;;;;;;;;UASzB,eAAA,SAAwB;;;;;;;;;;UAWxB,gBAAA,SAAyB;;;;;;;;;;;;KAa9B,WAAA,GACT,oBACA,mBACA,kBACA,kBACA,mBACA,kBACA;;;;UAKc,WAAA;;;;WAIP;;YAEC;;aAEC;;;;;UAMK,UAAA;;YAEN;;;;;;cA+EE,kBAAgB,CAAA,CAAA;;;;;;;KAUjB,WAAA,GAAc;;;;;cAMb,cAAY,CAAA,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAkCR,cAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;cA8BJ,gBAAc,CAAA,CAAA;;;;;;;;;;;UASV,MAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAoCL;;;;;;;;;;;;;;UAeK,WAAA;;;;;;;;;;;;;;;;;;;;;;;;;;aA8BL;;;;;;cAOC,cAAY,CAAA,CAAA;;;;;;;;;;;;;;;;;;AA9fzB;AA+BA;AA+FA;AAKA;AAOU;AAeV;AAWA;AAWA;AASA;AASA;AASA;AAWA;AAaA;;;;;;;;;AAYA;;;;;AAciB,iBC3ND,YAAA,CD6NL,KAAW,EC7Nc,WD6Nd,CAAA,EC7N4B,MD6N5B;AA+EtB;;;;;;;;;AAUA;AAMA;;iBC/RgB,YAAA,SAAoB,cAAmB;;;;;AD9DvD;AA+BA;AA+FA;AAKA;AAOU;AAeV;AAWA;AAWA;AASA;AASA;AASA;AAWA;AAaA;;;;;;;;;AAYA;AAIU,iBE7NM,wBAAA,CF6NN,IAAA,EE7NqC,UF6NrC,CAAA,EAAA,MAAA,EAAA"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,127 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
|
|
3
|
+
//#region src/extractNavigationTargets.ts
|
|
4
|
+
/**
|
|
5
|
+
* Extracts all navigation target screen IDs from a mock definition.
|
|
6
|
+
*
|
|
7
|
+
* This function collects all `navigateTo`, `itemNavigateTo`, and `rowNavigateTo`
|
|
8
|
+
* values from mock elements to automatically derive the `next` array.
|
|
9
|
+
* It recursively processes child sections.
|
|
10
|
+
*
|
|
11
|
+
* @param mock - The screen mock definition
|
|
12
|
+
* @returns Array of unique screen IDs that can be navigated to
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* const mock = {
|
|
17
|
+
* sections: [{
|
|
18
|
+
* elements: [
|
|
19
|
+
* { type: "button", label: "Edit", navigateTo: "billing.invoice.edit" },
|
|
20
|
+
* { type: "list", label: "Items", itemNavigateTo: "billing.item.detail" },
|
|
21
|
+
* ]
|
|
22
|
+
* }]
|
|
23
|
+
* }
|
|
24
|
+
* extractNavigationTargets(mock)
|
|
25
|
+
* // => ["billing.invoice.edit", "billing.item.detail"]
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
function extractNavigationTargets(mock) {
|
|
29
|
+
const targets = /* @__PURE__ */ new Set();
|
|
30
|
+
function processSection(section) {
|
|
31
|
+
for (const element of section.elements) {
|
|
32
|
+
if ("navigateTo" in element && element.navigateTo) targets.add(element.navigateTo);
|
|
33
|
+
if ("itemNavigateTo" in element && element.itemNavigateTo) targets.add(element.itemNavigateTo);
|
|
34
|
+
if ("rowNavigateTo" in element && element.rowNavigateTo) targets.add(element.rowNavigateTo);
|
|
35
|
+
}
|
|
36
|
+
if (section.children) for (const child of section.children) processSection(child);
|
|
37
|
+
}
|
|
38
|
+
for (const section of mock.sections) processSection(section);
|
|
39
|
+
return Array.from(targets);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
//#endregion
|
|
3
43
|
//#region src/types.ts
|
|
44
|
+
const mockLayoutSchema = z.enum(["vertical", "horizontal"]);
|
|
45
|
+
const mockElementBaseSchema = z.object({
|
|
46
|
+
type: z.enum([
|
|
47
|
+
"button",
|
|
48
|
+
"input",
|
|
49
|
+
"link",
|
|
50
|
+
"text",
|
|
51
|
+
"image",
|
|
52
|
+
"list",
|
|
53
|
+
"table"
|
|
54
|
+
]),
|
|
55
|
+
label: z.string().min(1)
|
|
56
|
+
});
|
|
57
|
+
const mockButtonElementSchema = mockElementBaseSchema.extend({
|
|
58
|
+
type: z.literal("button"),
|
|
59
|
+
navigateTo: z.string().optional(),
|
|
60
|
+
variant: z.enum([
|
|
61
|
+
"primary",
|
|
62
|
+
"secondary",
|
|
63
|
+
"danger"
|
|
64
|
+
]).optional()
|
|
65
|
+
});
|
|
66
|
+
const mockInputElementSchema = mockElementBaseSchema.extend({
|
|
67
|
+
type: z.literal("input"),
|
|
68
|
+
placeholder: z.string().optional(),
|
|
69
|
+
inputType: z.enum([
|
|
70
|
+
"text",
|
|
71
|
+
"email",
|
|
72
|
+
"password",
|
|
73
|
+
"textarea",
|
|
74
|
+
"search"
|
|
75
|
+
]).optional()
|
|
76
|
+
});
|
|
77
|
+
const mockLinkElementSchema = mockElementBaseSchema.extend({
|
|
78
|
+
type: z.literal("link"),
|
|
79
|
+
navigateTo: z.string().optional()
|
|
80
|
+
});
|
|
81
|
+
const mockTextElementSchema = mockElementBaseSchema.extend({
|
|
82
|
+
type: z.literal("text"),
|
|
83
|
+
variant: z.enum([
|
|
84
|
+
"heading",
|
|
85
|
+
"subheading",
|
|
86
|
+
"body",
|
|
87
|
+
"caption"
|
|
88
|
+
]).optional()
|
|
89
|
+
});
|
|
90
|
+
const mockImageElementSchema = mockElementBaseSchema.extend({
|
|
91
|
+
type: z.literal("image"),
|
|
92
|
+
aspectRatio: z.string().optional()
|
|
93
|
+
});
|
|
94
|
+
const mockListElementSchema = mockElementBaseSchema.extend({
|
|
95
|
+
type: z.literal("list"),
|
|
96
|
+
itemCount: z.number().int().positive().optional(),
|
|
97
|
+
itemNavigateTo: z.string().optional()
|
|
98
|
+
});
|
|
99
|
+
const mockTableElementSchema = mockElementBaseSchema.extend({
|
|
100
|
+
type: z.literal("table"),
|
|
101
|
+
columns: z.array(z.string()).optional(),
|
|
102
|
+
rowCount: z.number().int().positive().optional(),
|
|
103
|
+
rowNavigateTo: z.string().optional()
|
|
104
|
+
});
|
|
105
|
+
const mockElementSchema = z.discriminatedUnion("type", [
|
|
106
|
+
mockButtonElementSchema,
|
|
107
|
+
mockInputElementSchema,
|
|
108
|
+
mockLinkElementSchema,
|
|
109
|
+
mockTextElementSchema,
|
|
110
|
+
mockImageElementSchema,
|
|
111
|
+
mockListElementSchema,
|
|
112
|
+
mockTableElementSchema
|
|
113
|
+
]);
|
|
114
|
+
const mockSectionSchema = z.lazy(() => z.object({
|
|
115
|
+
title: z.string().optional(),
|
|
116
|
+
layout: mockLayoutSchema.optional(),
|
|
117
|
+
elements: z.array(mockElementSchema),
|
|
118
|
+
children: z.array(mockSectionSchema).optional()
|
|
119
|
+
}));
|
|
120
|
+
/**
|
|
121
|
+
* Schema for screen mock definition (runtime validation)
|
|
122
|
+
* @internal
|
|
123
|
+
*/
|
|
124
|
+
const screenMockSchema = z.object({ sections: z.array(mockSectionSchema) });
|
|
4
125
|
/**
|
|
5
126
|
* Schema for screen metadata definition (runtime validation)
|
|
6
127
|
* @internal
|
|
@@ -19,7 +140,8 @@ const screenSchema = z.object({
|
|
|
19
140
|
links: z.array(z.object({
|
|
20
141
|
label: z.string(),
|
|
21
142
|
url: z.string().url()
|
|
22
|
-
})).optional()
|
|
143
|
+
})).optional(),
|
|
144
|
+
mock: screenMockSchema.optional()
|
|
23
145
|
});
|
|
24
146
|
/**
|
|
25
147
|
* Schema for progressive adoption configuration (runtime validation)
|
|
@@ -47,6 +169,9 @@ const configSchema = z.object({
|
|
|
47
169
|
/**
|
|
48
170
|
* Define a screen with metadata for the screen catalog.
|
|
49
171
|
*
|
|
172
|
+
* When a `mock` is defined, navigation targets (navigateTo, itemNavigateTo,
|
|
173
|
+
* rowNavigateTo) are automatically extracted and merged into the `next` array.
|
|
174
|
+
*
|
|
50
175
|
* @example
|
|
51
176
|
* ```ts
|
|
52
177
|
* export const screen = defineScreen({
|
|
@@ -58,11 +183,27 @@ const configSchema = z.object({
|
|
|
58
183
|
* dependsOn: ["InvoiceAPI.getDetail"],
|
|
59
184
|
* entryPoints: ["billing.invoice.list"],
|
|
60
185
|
* next: ["billing.invoice.edit"],
|
|
186
|
+
* mock: {
|
|
187
|
+
* sections: [{
|
|
188
|
+
* elements: [
|
|
189
|
+
* { type: "button", label: "Pay", navigateTo: "billing.payment.start" },
|
|
190
|
+
* ],
|
|
191
|
+
* }],
|
|
192
|
+
* },
|
|
61
193
|
* })
|
|
194
|
+
* // next will be ["billing.invoice.edit", "billing.payment.start"]
|
|
62
195
|
* ```
|
|
63
196
|
*/
|
|
64
197
|
function defineScreen(input) {
|
|
65
|
-
|
|
198
|
+
const validated = screenSchema.parse(input);
|
|
199
|
+
if (validated.mock) {
|
|
200
|
+
const mockTargets = extractNavigationTargets(validated.mock);
|
|
201
|
+
if (mockTargets.length > 0) {
|
|
202
|
+
const existingNext = validated.next ?? [];
|
|
203
|
+
validated.next = [...new Set([...existingNext, ...mockTargets])];
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return validated;
|
|
66
207
|
}
|
|
67
208
|
/**
|
|
68
209
|
* Define Screenbook configuration.
|
|
@@ -81,5 +222,5 @@ function defineConfig(input = {}) {
|
|
|
81
222
|
}
|
|
82
223
|
|
|
83
224
|
//#endregion
|
|
84
|
-
export { adoptionSchema, configSchema, defineConfig, defineScreen, screenSchema };
|
|
225
|
+
export { adoptionSchema, configSchema, defineConfig, defineScreen, extractNavigationTargets, screenMockSchema, screenSchema };
|
|
85
226
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/types.ts","../src/defineScreen.ts"],"sourcesContent":["import { z } from \"zod\"\n\n/**\n * External link to related resources\n */\nexport interface ScreenLink {\n\t/**\n\t * Display label for the link\n\t * @example \"Figma Design\"\n\t * @example \"Storybook\"\n\t */\n\tlabel: string\n\t/**\n\t * URL to the external resource\n\t * @example \"https://figma.com/file/...\"\n\t * @example \"https://storybook.example.com/?path=/story/billing-invoice\"\n\t */\n\turl: string\n}\n\n/**\n * Screen metadata definition for the screen catalog.\n *\n * @example\n * ```ts\n * const screen: Screen = {\n * id: \"billing.invoice.detail\",\n * title: \"Invoice Detail\",\n * route: \"/billing/invoices/:id\",\n * owner: [\"billing-team\"],\n * tags: [\"billing\", \"invoice\"],\n * dependsOn: [\"InvoiceAPI.getDetail\"],\n * next: [\"billing.invoice.edit\"],\n * }\n * ```\n */\nexport interface Screen {\n\t/**\n\t * Unique identifier for the screen using dot notation\n\t * @example \"billing.invoice.detail\"\n\t * @example \"auth.login\"\n\t * @example \"settings.profile\"\n\t */\n\tid: string\n\n\t/**\n\t * Human-readable title displayed in the screen catalog\n\t * @example \"Invoice Detail\"\n\t * @example \"Login\"\n\t * @example \"User Profile Settings\"\n\t */\n\ttitle: string\n\n\t/**\n\t * Route path pattern with optional dynamic segments\n\t * @example \"/billing/invoices/:id\"\n\t * @example \"/auth/login\"\n\t * @example \"/settings/profile\"\n\t */\n\troute: string\n\n\t/**\n\t * Team(s) or domain(s) that own this screen\n\t * @example [\"billing-team\"]\n\t * @example [\"platform\", \"billing\"]\n\t */\n\towner?: string[]\n\n\t/**\n\t * Tags for categorization and filtering in the catalog\n\t * @example [\"billing\", \"invoice\"]\n\t * @example [\"auth\", \"security\"]\n\t */\n\ttags?: string[]\n\n\t/**\n\t * APIs or services this screen depends on for impact analysis\n\t * @example [\"InvoiceAPI.getDetail\", \"PaymentAPI.getStatus\"]\n\t * @example [\"UserAPI.getProfile\"]\n\t */\n\tdependsOn?: string[]\n\n\t/**\n\t * Screen IDs that can navigate to this screen (incoming edges)\n\t * @example [\"billing.invoice.list\"]\n\t * @example [\"dashboard.home\", \"nav.sidebar\"]\n\t */\n\tentryPoints?: string[]\n\n\t/**\n\t * Screen IDs this screen can navigate to (outgoing edges)\n\t * @example [\"billing.invoice.edit\", \"billing.payment.start\"]\n\t * @example [\"billing.invoice.list\"]\n\t */\n\tnext?: string[]\n\n\t/**\n\t * Allow circular navigation involving this screen.\n\t * When true, cycles that include this screen will not trigger warnings.\n\t * @default false\n\t * @example true\n\t */\n\tallowCycles?: boolean\n\n\t/**\n\t * Optional description explaining the screen's purpose\n\t * @example \"Displays detailed invoice information including line items and payment status\"\n\t */\n\tdescription?: string\n\n\t/**\n\t * Links to external resources like Storybook, Figma, or documentation\n\t * @example [{ label: \"Figma\", url: \"https://figma.com/file/...\" }]\n\t */\n\tlinks?: ScreenLink[]\n}\n\n/**\n * Input type for defineScreen function.\n * Same as Screen but used for input validation.\n */\nexport type ScreenInput = Screen\n\n/**\n * Schema for screen metadata definition (runtime validation)\n * @internal\n */\nexport const screenSchema = z.object({\n\tid: z.string().min(1),\n\ttitle: z.string().min(1),\n\troute: z.string().min(1),\n\towner: z.array(z.string()).optional(),\n\ttags: z.array(z.string()).optional(),\n\tdependsOn: z.array(z.string()).optional(),\n\tentryPoints: z.array(z.string()).optional(),\n\tnext: z.array(z.string()).optional(),\n\tallowCycles: z.boolean().optional(),\n\tdescription: z.string().optional(),\n\tlinks: z\n\t\t.array(\n\t\t\tz.object({\n\t\t\t\tlabel: z.string(),\n\t\t\t\turl: z.string().url(),\n\t\t\t}),\n\t\t)\n\t\t.optional(),\n})\n\n/**\n * Progressive adoption configuration for gradual rollout.\n *\n * @example\n * ```ts\n * const adoption: AdoptionConfig = {\n * mode: \"progressive\",\n * includePatterns: [\"src/pages/billing/**\"],\n * minimumCoverage: 80,\n * }\n * ```\n */\nexport interface AdoptionConfig {\n\t/**\n\t * Adoption mode for screen metadata coverage\n\t * - `\"full\"`: All routes must have screen.meta.ts (default)\n\t * - `\"progressive\"`: Only check coverage within includePatterns\n\t * @default \"full\"\n\t * @example \"full\"\n\t * @example \"progressive\"\n\t */\n\tmode?: \"full\" | \"progressive\"\n\n\t/**\n\t * Glob patterns to include for coverage checking (progressive mode only)\n\t * @example [\"src/pages/billing/**\"]\n\t * @example [\"src/pages/auth/**\", \"src/pages/settings/**\"]\n\t */\n\tincludePatterns?: string[]\n\n\t/**\n\t * Minimum coverage percentage required to pass lint (0-100)\n\t * @example 80\n\t * @example 100\n\t */\n\tminimumCoverage?: number\n}\n\n/**\n * Schema for progressive adoption configuration (runtime validation)\n * @internal\n */\nexport const adoptionSchema = z.object({\n\tmode: z.enum([\"full\", \"progressive\"]).default(\"full\"),\n\tincludePatterns: z.array(z.string()).optional(),\n\tminimumCoverage: z.number().min(0).max(100).optional(),\n})\n\n/**\n * Screenbook configuration options.\n */\nexport interface Config {\n\t/**\n\t * Output directory for generated files\n\t * @default \".screenbook\"\n\t * @example \".screenbook\"\n\t * @example \"dist/screenbook\"\n\t */\n\toutDir: string\n\n\t/**\n\t * Glob pattern for screen metadata files.\n\t * Supports colocation: place screen.meta.ts alongside your route files.\n\t * @default \"src/**\\/screen.meta.ts\"\n\t * @example \"src/**\\/screen.meta.ts\"\n\t * @example \"app/**\\/screen.meta.ts\"\n\t */\n\tmetaPattern: string\n\n\t/**\n\t * Glob pattern for route files (for generate/lint commands)\n\t * @example \"src/pages/**\\/page.tsx\"\n\t * @example \"app/**\\/page.tsx\"\n\t * @example \"src/routes/**\\/*.tsx\"\n\t */\n\troutesPattern?: string\n\n\t/**\n\t * Patterns to ignore when scanning (glob patterns).\n\t * Defaults to node_modules and .git directories.\n\t */\n\tignore: string[]\n\n\t/**\n\t * Progressive adoption configuration for gradual rollout\n\t * @example { mode: \"progressive\", includePatterns: [\"src/pages/billing/**\"], minimumCoverage: 80 }\n\t */\n\tadoption?: AdoptionConfig\n}\n\n/**\n * Input type for defineConfig function.\n * All fields with defaults are optional in input.\n *\n * @example\n * ```ts\n * defineConfig({\n * metaPattern: \"app/**\\/screen.meta.ts\",\n * routesPattern: \"app/**\\/page.tsx\",\n * })\n * ```\n */\nexport interface ConfigInput {\n\t/**\n\t * Output directory for generated files\n\t * @default \".screenbook\"\n\t * @example \".screenbook\"\n\t */\n\toutDir?: string\n\n\t/**\n\t * Glob pattern for screen metadata files\n\t * @default \"src/**\\/screen.meta.ts\"\n\t * @example \"app/**\\/screen.meta.ts\"\n\t */\n\tmetaPattern?: string\n\n\t/**\n\t * Glob pattern for route files (for generate/lint commands)\n\t * @example \"src/pages/**\\/page.tsx\"\n\t */\n\troutesPattern?: string\n\n\t/**\n\t * Patterns to ignore when scanning.\n\t * Defaults to node_modules and .git directories.\n\t */\n\tignore?: string[]\n\n\t/**\n\t * Progressive adoption configuration\n\t */\n\tadoption?: AdoptionConfig\n}\n\n/**\n * Schema for Screenbook configuration (runtime validation)\n * @internal\n */\nexport const configSchema = z.object({\n\toutDir: z.string().default(\".screenbook\"),\n\tmetaPattern: z.string().default(\"src/**/screen.meta.ts\"),\n\troutesPattern: z.string().optional(),\n\tignore: z.array(z.string()).default([\"**/node_modules/**\", \"**/.git/**\"]),\n\tadoption: adoptionSchema.optional(),\n})\n","import {\n\ttype Config,\n\ttype ConfigInput,\n\tconfigSchema,\n\ttype Screen,\n\ttype ScreenInput,\n\tscreenSchema,\n} from \"./types.js\"\n\n/**\n * Define a screen with metadata for the screen catalog.\n *\n * @example\n * ```ts\n * export const screen = defineScreen({\n * id: \"billing.invoice.detail\",\n * title: \"Invoice Detail\",\n * route: \"/billing/invoices/:id\",\n * owner: [\"billing\"],\n * tags: [\"billing\", \"invoice\"],\n * dependsOn: [\"InvoiceAPI.getDetail\"],\n * entryPoints: [\"billing.invoice.list\"],\n * next: [\"billing.invoice.edit\"],\n * })\n * ```\n */\nexport function defineScreen(input: ScreenInput): Screen {\n\treturn screenSchema.parse(input)\n}\n\n/**\n * Define Screenbook configuration.\n *\n * @example\n * ```ts\n * export default defineConfig({\n * screensDir: \"src/screens\",\n * outDir: \".screenbook\",\n * metaPattern: \"**\\/screen.meta.ts\",\n * })\n * ```\n */\nexport function defineConfig(input: ConfigInput = {}): Config {\n\treturn configSchema.parse(input)\n}\n"],"mappings":";;;;;;;AA+HA,MAAa,eAAe,EAAE,OAAO;CACpC,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE;CACrB,OAAO,EAAE,QAAQ,CAAC,IAAI,EAAE;CACxB,OAAO,EAAE,QAAQ,CAAC,IAAI,EAAE;CACxB,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,UAAU;CACrC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,UAAU;CACpC,WAAW,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,UAAU;CACzC,aAAa,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,UAAU;CAC3C,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,UAAU;CACpC,aAAa,EAAE,SAAS,CAAC,UAAU;CACnC,aAAa,EAAE,QAAQ,CAAC,UAAU;CAClC,OAAO,EACL,MACA,EAAE,OAAO;EACR,OAAO,EAAE,QAAQ;EACjB,KAAK,EAAE,QAAQ,CAAC,KAAK;EACrB,CAAC,CACF,CACA,UAAU;CACZ,CAAC;;;;;AA4CF,MAAa,iBAAiB,EAAE,OAAO;CACtC,MAAM,EAAE,KAAK,CAAC,QAAQ,cAAc,CAAC,CAAC,QAAQ,OAAO;CACrD,iBAAiB,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,UAAU;CAC/C,iBAAiB,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,IAAI,CAAC,UAAU;CACtD,CAAC;;;;;AA6FF,MAAa,eAAe,EAAE,OAAO;CACpC,QAAQ,EAAE,QAAQ,CAAC,QAAQ,cAAc;CACzC,aAAa,EAAE,QAAQ,CAAC,QAAQ,wBAAwB;CACxD,eAAe,EAAE,QAAQ,CAAC,UAAU;CACpC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,sBAAsB,aAAa,CAAC;CACzE,UAAU,eAAe,UAAU;CACnC,CAAC;;;;;;;;;;;;;;;;;;;;;AC3QF,SAAgB,aAAa,OAA4B;AACxD,QAAO,aAAa,MAAM,MAAM;;;;;;;;;;;;;;AAejC,SAAgB,aAAa,QAAqB,EAAE,EAAU;AAC7D,QAAO,aAAa,MAAM,MAAM"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["mockSectionSchema: z.ZodType<MockSection>"],"sources":["../src/extractNavigationTargets.ts","../src/types.ts","../src/defineScreen.ts"],"sourcesContent":["import type { MockSection, ScreenMock } from \"./types.js\"\n\n/**\n * Extracts all navigation target screen IDs from a mock definition.\n *\n * This function collects all `navigateTo`, `itemNavigateTo`, and `rowNavigateTo`\n * values from mock elements to automatically derive the `next` array.\n * It recursively processes child sections.\n *\n * @param mock - The screen mock definition\n * @returns Array of unique screen IDs that can be navigated to\n *\n * @example\n * ```ts\n * const mock = {\n * sections: [{\n * elements: [\n * { type: \"button\", label: \"Edit\", navigateTo: \"billing.invoice.edit\" },\n * { type: \"list\", label: \"Items\", itemNavigateTo: \"billing.item.detail\" },\n * ]\n * }]\n * }\n * extractNavigationTargets(mock)\n * // => [\"billing.invoice.edit\", \"billing.item.detail\"]\n * ```\n */\nexport function extractNavigationTargets(mock: ScreenMock): string[] {\n\tconst targets = new Set<string>()\n\n\tfunction processSection(section: MockSection): void {\n\t\tfor (const element of section.elements) {\n\t\t\t// Button and Link elements have navigateTo\n\t\t\tif (\"navigateTo\" in element && element.navigateTo) {\n\t\t\t\ttargets.add(element.navigateTo)\n\t\t\t}\n\n\t\t\t// List elements have itemNavigateTo\n\t\t\tif (\"itemNavigateTo\" in element && element.itemNavigateTo) {\n\t\t\t\ttargets.add(element.itemNavigateTo)\n\t\t\t}\n\n\t\t\t// Table elements have rowNavigateTo\n\t\t\tif (\"rowNavigateTo\" in element && element.rowNavigateTo) {\n\t\t\t\ttargets.add(element.rowNavigateTo)\n\t\t\t}\n\t\t}\n\n\t\t// Recursively process child sections\n\t\tif (section.children) {\n\t\t\tfor (const child of section.children) {\n\t\t\t\tprocessSection(child)\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (const section of mock.sections) {\n\t\tprocessSection(section)\n\t}\n\n\treturn Array.from(targets)\n}\n","import { z } from \"zod\"\n\n/**\n * External link to related resources\n */\nexport interface ScreenLink {\n\t/**\n\t * Display label for the link\n\t * @example \"Figma Design\"\n\t * @example \"Storybook\"\n\t */\n\tlabel: string\n\t/**\n\t * URL to the external resource\n\t * @example \"https://figma.com/file/...\"\n\t * @example \"https://storybook.example.com/?path=/story/billing-invoice\"\n\t */\n\turl: string\n}\n\n/**\n * Screen metadata definition for the screen catalog.\n *\n * @example\n * ```ts\n * const screen: Screen = {\n * id: \"billing.invoice.detail\",\n * title: \"Invoice Detail\",\n * route: \"/billing/invoices/:id\",\n * owner: [\"billing-team\"],\n * tags: [\"billing\", \"invoice\"],\n * dependsOn: [\"InvoiceAPI.getDetail\"],\n * next: [\"billing.invoice.edit\"],\n * }\n * ```\n */\nexport interface Screen {\n\t/**\n\t * Unique identifier for the screen using dot notation\n\t * @example \"billing.invoice.detail\"\n\t * @example \"auth.login\"\n\t * @example \"settings.profile\"\n\t */\n\tid: string\n\n\t/**\n\t * Human-readable title displayed in the screen catalog\n\t * @example \"Invoice Detail\"\n\t * @example \"Login\"\n\t * @example \"User Profile Settings\"\n\t */\n\ttitle: string\n\n\t/**\n\t * Route path pattern with optional dynamic segments\n\t * @example \"/billing/invoices/:id\"\n\t * @example \"/auth/login\"\n\t * @example \"/settings/profile\"\n\t */\n\troute: string\n\n\t/**\n\t * Team(s) or domain(s) that own this screen\n\t * @example [\"billing-team\"]\n\t * @example [\"platform\", \"billing\"]\n\t */\n\towner?: string[]\n\n\t/**\n\t * Tags for categorization and filtering in the catalog\n\t * @example [\"billing\", \"invoice\"]\n\t * @example [\"auth\", \"security\"]\n\t */\n\ttags?: string[]\n\n\t/**\n\t * APIs or services this screen depends on for impact analysis\n\t * @example [\"InvoiceAPI.getDetail\", \"PaymentAPI.getStatus\"]\n\t * @example [\"UserAPI.getProfile\"]\n\t */\n\tdependsOn?: string[]\n\n\t/**\n\t * Screen IDs that can navigate to this screen (incoming edges)\n\t * @example [\"billing.invoice.list\"]\n\t * @example [\"dashboard.home\", \"nav.sidebar\"]\n\t */\n\tentryPoints?: string[]\n\n\t/**\n\t * Screen IDs this screen can navigate to (outgoing edges)\n\t * @example [\"billing.invoice.edit\", \"billing.payment.start\"]\n\t * @example [\"billing.invoice.list\"]\n\t */\n\tnext?: string[]\n\n\t/**\n\t * Allow circular navigation involving this screen.\n\t * When true, cycles that include this screen will not trigger warnings.\n\t * @default false\n\t * @example true\n\t */\n\tallowCycles?: boolean\n\n\t/**\n\t * Optional description explaining the screen's purpose\n\t * @example \"Displays detailed invoice information including line items and payment status\"\n\t */\n\tdescription?: string\n\n\t/**\n\t * Links to external resources like Storybook, Figma, or documentation\n\t * @example [{ label: \"Figma\", url: \"https://figma.com/file/...\" }]\n\t */\n\tlinks?: ScreenLink[]\n\n\t/**\n\t * Wireframe-level mock definition for UI documentation.\n\t * When defined, navigation targets (navigateTo, itemNavigateTo, rowNavigateTo)\n\t * are automatically extracted and merged into the `next` array.\n\t */\n\tmock?: ScreenMock\n}\n\n// =============================================================================\n// Mock Types - Wireframe-level UI mockups for screen flow documentation\n// =============================================================================\n\n/**\n * Layout direction for mock sections\n */\nexport type MockLayout = \"vertical\" | \"horizontal\"\n\n/**\n * Available element types for mock wireframes\n */\nexport type MockElementType =\n\t| \"button\"\n\t| \"input\"\n\t| \"link\"\n\t| \"text\"\n\t| \"image\"\n\t| \"list\"\n\t| \"table\"\n\n/**\n * Base interface for all mock elements\n */\ninterface MockElementBase {\n\t/** Element type identifier */\n\ttype: MockElementType\n\t/** Display label for the element */\n\tlabel: string\n}\n\n/**\n * Button element with optional navigation and styling\n */\nexport interface MockButtonElement extends MockElementBase {\n\ttype: \"button\"\n\t/** Screen ID to navigate to when clicked */\n\tnavigateTo?: string\n\t/** Visual style variant */\n\tvariant?: \"primary\" | \"secondary\" | \"danger\"\n}\n\n/**\n * Input field element\n */\nexport interface MockInputElement extends MockElementBase {\n\ttype: \"input\"\n\t/** Placeholder text */\n\tplaceholder?: string\n\t/** Input type for semantics */\n\tinputType?: \"text\" | \"email\" | \"password\" | \"textarea\" | \"search\"\n}\n\n/**\n * Link element with optional navigation\n */\nexport interface MockLinkElement extends MockElementBase {\n\ttype: \"link\"\n\t/** Screen ID to navigate to when clicked */\n\tnavigateTo?: string\n}\n\n/**\n * Text element for labels and headings\n */\nexport interface MockTextElement extends MockElementBase {\n\ttype: \"text\"\n\t/** Text style variant */\n\tvariant?: \"heading\" | \"subheading\" | \"body\" | \"caption\"\n}\n\n/**\n * Image placeholder element\n */\nexport interface MockImageElement extends MockElementBase {\n\ttype: \"image\"\n\t/** Aspect ratio (e.g., \"16:9\", \"1:1\") */\n\taspectRatio?: string\n}\n\n/**\n * List element for displaying multiple items\n */\nexport interface MockListElement extends MockElementBase {\n\ttype: \"list\"\n\t/** Number of items to display (default: 3) */\n\titemCount?: number\n\t/** Screen ID to navigate to when an item is clicked */\n\titemNavigateTo?: string\n}\n\n/**\n * Table element for displaying tabular data\n */\nexport interface MockTableElement extends MockElementBase {\n\ttype: \"table\"\n\t/** Column headers */\n\tcolumns?: string[]\n\t/** Number of rows to display (default: 3) */\n\trowCount?: number\n\t/** Screen ID to navigate to when a row is clicked */\n\trowNavigateTo?: string\n}\n\n/**\n * Union type of all mock element variants\n */\nexport type MockElement =\n\t| MockButtonElement\n\t| MockInputElement\n\t| MockLinkElement\n\t| MockTextElement\n\t| MockImageElement\n\t| MockListElement\n\t| MockTableElement\n\n/**\n * Section containing grouped mock elements\n */\nexport interface MockSection {\n\t/** Optional section title */\n\ttitle?: string\n\t/** Layout direction (default: \"vertical\") */\n\tlayout?: MockLayout\n\t/** Elements within this section */\n\telements: MockElement[]\n\t/** Nested child sections */\n\tchildren?: MockSection[]\n}\n\n/**\n * Complete mock definition for a screen\n */\nexport interface ScreenMock {\n\t/** Sections containing UI elements */\n\tsections: MockSection[]\n}\n\n// =============================================================================\n// Mock Zod Schemas\n// =============================================================================\n\nconst mockLayoutSchema = z.enum([\"vertical\", \"horizontal\"])\n\nconst mockElementBaseSchema = z.object({\n\ttype: z.enum([\"button\", \"input\", \"link\", \"text\", \"image\", \"list\", \"table\"]),\n\tlabel: z.string().min(1),\n})\n\nconst mockButtonElementSchema = mockElementBaseSchema.extend({\n\ttype: z.literal(\"button\"),\n\tnavigateTo: z.string().optional(),\n\tvariant: z.enum([\"primary\", \"secondary\", \"danger\"]).optional(),\n})\n\nconst mockInputElementSchema = mockElementBaseSchema.extend({\n\ttype: z.literal(\"input\"),\n\tplaceholder: z.string().optional(),\n\tinputType: z\n\t\t.enum([\"text\", \"email\", \"password\", \"textarea\", \"search\"])\n\t\t.optional(),\n})\n\nconst mockLinkElementSchema = mockElementBaseSchema.extend({\n\ttype: z.literal(\"link\"),\n\tnavigateTo: z.string().optional(),\n})\n\nconst mockTextElementSchema = mockElementBaseSchema.extend({\n\ttype: z.literal(\"text\"),\n\tvariant: z.enum([\"heading\", \"subheading\", \"body\", \"caption\"]).optional(),\n})\n\nconst mockImageElementSchema = mockElementBaseSchema.extend({\n\ttype: z.literal(\"image\"),\n\taspectRatio: z.string().optional(),\n})\n\nconst mockListElementSchema = mockElementBaseSchema.extend({\n\ttype: z.literal(\"list\"),\n\titemCount: z.number().int().positive().optional(),\n\titemNavigateTo: z.string().optional(),\n})\n\nconst mockTableElementSchema = mockElementBaseSchema.extend({\n\ttype: z.literal(\"table\"),\n\tcolumns: z.array(z.string()).optional(),\n\trowCount: z.number().int().positive().optional(),\n\trowNavigateTo: z.string().optional(),\n})\n\nconst mockElementSchema = z.discriminatedUnion(\"type\", [\n\tmockButtonElementSchema,\n\tmockInputElementSchema,\n\tmockLinkElementSchema,\n\tmockTextElementSchema,\n\tmockImageElementSchema,\n\tmockListElementSchema,\n\tmockTableElementSchema,\n])\n\nconst mockSectionSchema: z.ZodType<MockSection> = z.lazy(() =>\n\tz.object({\n\t\ttitle: z.string().optional(),\n\t\tlayout: mockLayoutSchema.optional(),\n\t\telements: z.array(mockElementSchema),\n\t\tchildren: z.array(mockSectionSchema).optional(),\n\t}),\n)\n\n/**\n * Schema for screen mock definition (runtime validation)\n * @internal\n */\nexport const screenMockSchema = z.object({\n\tsections: z.array(mockSectionSchema),\n})\n\n// =============================================================================\n\n/**\n * Input type for defineScreen function.\n * Same as Screen but used for input validation.\n */\nexport type ScreenInput = Screen\n\n/**\n * Schema for screen metadata definition (runtime validation)\n * @internal\n */\nexport const screenSchema = z.object({\n\tid: z.string().min(1),\n\ttitle: z.string().min(1),\n\troute: z.string().min(1),\n\towner: z.array(z.string()).optional(),\n\ttags: z.array(z.string()).optional(),\n\tdependsOn: z.array(z.string()).optional(),\n\tentryPoints: z.array(z.string()).optional(),\n\tnext: z.array(z.string()).optional(),\n\tallowCycles: z.boolean().optional(),\n\tdescription: z.string().optional(),\n\tlinks: z\n\t\t.array(\n\t\t\tz.object({\n\t\t\t\tlabel: z.string(),\n\t\t\t\turl: z.string().url(),\n\t\t\t}),\n\t\t)\n\t\t.optional(),\n\tmock: screenMockSchema.optional(),\n})\n\n/**\n * Progressive adoption configuration for gradual rollout.\n *\n * @example\n * ```ts\n * const adoption: AdoptionConfig = {\n * mode: \"progressive\",\n * includePatterns: [\"src/pages/billing/**\"],\n * minimumCoverage: 80,\n * }\n * ```\n */\nexport interface AdoptionConfig {\n\t/**\n\t * Adoption mode for screen metadata coverage\n\t * - `\"full\"`: All routes must have screen.meta.ts (default)\n\t * - `\"progressive\"`: Only check coverage within includePatterns\n\t * @default \"full\"\n\t * @example \"full\"\n\t * @example \"progressive\"\n\t */\n\tmode?: \"full\" | \"progressive\"\n\n\t/**\n\t * Glob patterns to include for coverage checking (progressive mode only)\n\t * @example [\"src/pages/billing/**\"]\n\t * @example [\"src/pages/auth/**\", \"src/pages/settings/**\"]\n\t */\n\tincludePatterns?: string[]\n\n\t/**\n\t * Minimum coverage percentage required to pass lint (0-100)\n\t * @example 80\n\t * @example 100\n\t */\n\tminimumCoverage?: number\n}\n\n/**\n * Schema for progressive adoption configuration (runtime validation)\n * @internal\n */\nexport const adoptionSchema = z.object({\n\tmode: z.enum([\"full\", \"progressive\"]).default(\"full\"),\n\tincludePatterns: z.array(z.string()).optional(),\n\tminimumCoverage: z.number().min(0).max(100).optional(),\n})\n\n/**\n * Screenbook configuration options.\n */\nexport interface Config {\n\t/**\n\t * Output directory for generated files\n\t * @default \".screenbook\"\n\t * @example \".screenbook\"\n\t * @example \"dist/screenbook\"\n\t */\n\toutDir: string\n\n\t/**\n\t * Glob pattern for screen metadata files.\n\t * Supports colocation: place screen.meta.ts alongside your route files.\n\t * @default \"src/**\\/screen.meta.ts\"\n\t * @example \"src/**\\/screen.meta.ts\"\n\t * @example \"app/**\\/screen.meta.ts\"\n\t */\n\tmetaPattern: string\n\n\t/**\n\t * Glob pattern for route files (for generate/lint commands)\n\t * @example \"src/pages/**\\/page.tsx\"\n\t * @example \"app/**\\/page.tsx\"\n\t * @example \"src/routes/**\\/*.tsx\"\n\t */\n\troutesPattern?: string\n\n\t/**\n\t * Patterns to ignore when scanning (glob patterns).\n\t * Defaults to node_modules and .git directories.\n\t */\n\tignore: string[]\n\n\t/**\n\t * Progressive adoption configuration for gradual rollout\n\t * @example { mode: \"progressive\", includePatterns: [\"src/pages/billing/**\"], minimumCoverage: 80 }\n\t */\n\tadoption?: AdoptionConfig\n}\n\n/**\n * Input type for defineConfig function.\n * All fields with defaults are optional in input.\n *\n * @example\n * ```ts\n * defineConfig({\n * metaPattern: \"app/**\\/screen.meta.ts\",\n * routesPattern: \"app/**\\/page.tsx\",\n * })\n * ```\n */\nexport interface ConfigInput {\n\t/**\n\t * Output directory for generated files\n\t * @default \".screenbook\"\n\t * @example \".screenbook\"\n\t */\n\toutDir?: string\n\n\t/**\n\t * Glob pattern for screen metadata files\n\t * @default \"src/**\\/screen.meta.ts\"\n\t * @example \"app/**\\/screen.meta.ts\"\n\t */\n\tmetaPattern?: string\n\n\t/**\n\t * Glob pattern for route files (for generate/lint commands)\n\t * @example \"src/pages/**\\/page.tsx\"\n\t */\n\troutesPattern?: string\n\n\t/**\n\t * Patterns to ignore when scanning.\n\t * Defaults to node_modules and .git directories.\n\t */\n\tignore?: string[]\n\n\t/**\n\t * Progressive adoption configuration\n\t */\n\tadoption?: AdoptionConfig\n}\n\n/**\n * Schema for Screenbook configuration (runtime validation)\n * @internal\n */\nexport const configSchema = z.object({\n\toutDir: z.string().default(\".screenbook\"),\n\tmetaPattern: z.string().default(\"src/**/screen.meta.ts\"),\n\troutesPattern: z.string().optional(),\n\tignore: z.array(z.string()).default([\"**/node_modules/**\", \"**/.git/**\"]),\n\tadoption: adoptionSchema.optional(),\n})\n","import { extractNavigationTargets } from \"./extractNavigationTargets.js\"\nimport {\n\ttype Config,\n\ttype ConfigInput,\n\tconfigSchema,\n\ttype Screen,\n\ttype ScreenInput,\n\tscreenSchema,\n} from \"./types.js\"\n\n/**\n * Define a screen with metadata for the screen catalog.\n *\n * When a `mock` is defined, navigation targets (navigateTo, itemNavigateTo,\n * rowNavigateTo) are automatically extracted and merged into the `next` array.\n *\n * @example\n * ```ts\n * export const screen = defineScreen({\n * id: \"billing.invoice.detail\",\n * title: \"Invoice Detail\",\n * route: \"/billing/invoices/:id\",\n * owner: [\"billing\"],\n * tags: [\"billing\", \"invoice\"],\n * dependsOn: [\"InvoiceAPI.getDetail\"],\n * entryPoints: [\"billing.invoice.list\"],\n * next: [\"billing.invoice.edit\"],\n * mock: {\n * sections: [{\n * elements: [\n * { type: \"button\", label: \"Pay\", navigateTo: \"billing.payment.start\" },\n * ],\n * }],\n * },\n * })\n * // next will be [\"billing.invoice.edit\", \"billing.payment.start\"]\n * ```\n */\nexport function defineScreen(input: ScreenInput): Screen {\n\tconst validated = screenSchema.parse(input)\n\n\t// Auto-derive next from mock if mock exists\n\tif (validated.mock) {\n\t\tconst mockTargets = extractNavigationTargets(validated.mock)\n\t\tif (mockTargets.length > 0) {\n\t\t\t// Merge with existing next, avoiding duplicates\n\t\t\tconst existingNext = validated.next ?? []\n\t\t\tconst mergedNext = [...new Set([...existingNext, ...mockTargets])]\n\t\t\tvalidated.next = mergedNext\n\t\t}\n\t}\n\n\treturn validated\n}\n\n/**\n * Define Screenbook configuration.\n *\n * @example\n * ```ts\n * export default defineConfig({\n * screensDir: \"src/screens\",\n * outDir: \".screenbook\",\n * metaPattern: \"**\\/screen.meta.ts\",\n * })\n * ```\n */\nexport function defineConfig(input: ConfigInput = {}): Config {\n\treturn configSchema.parse(input)\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@screenbook/core",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "Core library for Screenbook - screen metadata definitions and validation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
"module": "./dist/index.mjs",
|
|
15
15
|
"types": "./dist/index.d.mts",
|
|
16
16
|
"files": [
|
|
17
|
-
"dist"
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md"
|
|
18
19
|
],
|
|
19
20
|
"scripts": {
|
|
20
21
|
"build": "tsdown",
|