openuispec 0.2.10 → 0.2.12
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 +3 -1
- package/check/index.ts +17 -0
- package/cli/index.ts +21 -3
- package/cli/init.ts +224 -10
- package/docs/cli.md +13 -8
- package/docs/file-formats.md +36 -0
- package/docs/implementation-notes.md +7 -0
- package/drift/index.ts +281 -40
- package/mcp-server/index.ts +179 -119
- package/mcp-server/screenshot.ts +19 -4
- package/package.json +5 -2
- package/prepare/index.ts +155 -18
- package/schema/openuispec.schema.json +59 -0
- package/schema/semantic-lint.ts +25 -1
- package/scripts/take-all-screenshots.ts +507 -0
- package/spec/openuispec-v0.1.md +13 -0
- package/status/index.ts +72 -2
- package/examples/social-app/.mcp.json +0 -10
- package/examples/social-app/AGENTS.md +0 -124
- package/examples/social-app/CLAUDE.md +0 -124
- package/examples/social-app/backend/.gitkeep +0 -1
- package/examples/social-app/generated/android/social-app/app/.paparazzi-hashes.json +0 -3
- package/examples/social-app/generated/android/social-app/app/build.gradle.kts +0 -94
- package/examples/social-app/generated/android/social-app/app/src/main/AndroidManifest.xml +0 -26
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/AppContainer.kt +0 -20
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/MainActivity.kt +0 -35
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/SocialAppApplication.kt +0 -13
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/data/MockData.kt +0 -98
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/data/preferences/AppPreferences.kt +0 -19
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/data/preferences/DataStorePreferencesRepository.kt +0 -68
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/data/preferences/PreferencesRepository.kt +0 -15
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/model/Models.kt +0 -34
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/MainShell.kt +0 -390
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/components/Components.kt +0 -234
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/components/ContractPrimitives.kt +0 -641
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/navigation/RootComponent.kt +0 -113
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/ChatDetailScreen.kt +0 -212
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/CreatePostScreen.kt +0 -113
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/DiscoverScreen.kt +0 -137
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/EditProfileScreen.kt +0 -180
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/HomeFeedScreen.kt +0 -169
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/MessagesInboxScreen.kt +0 -85
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/NotificationsScreen.kt +0 -74
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/PostDetailScreen.kt +0 -293
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/ProfileSelfScreen.kt +0 -116
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/SearchResultsScreen.kt +0 -161
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/SettingsScreen.kt +0 -164
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/SettingsStore.kt +0 -95
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/UserProfileScreen.kt +0 -123
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/theme/Color.kt +0 -33
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/theme/Shape.kt +0 -41
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/theme/Spacing.kt +0 -20
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/theme/Theme.kt +0 -82
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/theme/Type.kt +0 -60
- package/examples/social-app/generated/android/social-app/app/src/main/res/drawable/ic_launcher_foreground.xml +0 -9
- package/examples/social-app/generated/android/social-app/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +0 -5
- package/examples/social-app/generated/android/social-app/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +0 -5
- package/examples/social-app/generated/android/social-app/app/src/main/res/values/strings.xml +0 -91
- package/examples/social-app/generated/android/social-app/app/src/main/res/values/themes.xml +0 -10
- package/examples/social-app/generated/android/social-app/app/src/main/res/values-ru/strings.xml +0 -79
- package/examples/social-app/generated/android/social-app/app/src/main/res/values-uz/strings.xml +0 -79
- package/examples/social-app/generated/android/social-app/app/src/main/xml/AndroidManifest.xml +0 -23
- package/examples/social-app/generated/android/social-app/app/src/test/kotlin/com/social/app/screenshots/HomeFeedScreenshotTest.kt +0 -34
- package/examples/social-app/generated/android/social-app/build.gradle.kts +0 -7
- package/examples/social-app/generated/android/social-app/gradle/libs.versions.toml +0 -50
- package/examples/social-app/generated/android/social-app/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/examples/social-app/generated/android/social-app/gradle/wrapper/gradle-wrapper.properties +0 -8
- package/examples/social-app/generated/android/social-app/gradle.properties +0 -11
- package/examples/social-app/generated/android/social-app/gradlew +0 -248
- package/examples/social-app/generated/android/social-app/settings.gradle.kts +0 -27
- package/examples/social-app/generated/web/social-app/index.html +0 -12
- package/examples/social-app/generated/web/social-app/package-lock.json +0 -2517
- package/examples/social-app/generated/web/social-app/package.json +0 -27
- package/examples/social-app/generated/web/social-app/src/app/App.tsx +0 -58
- package/examples/social-app/generated/web/social-app/src/components/Shell.tsx +0 -259
- package/examples/social-app/generated/web/social-app/src/components/cards.tsx +0 -317
- package/examples/social-app/generated/web/social-app/src/components/ui.tsx +0 -340
- package/examples/social-app/generated/web/social-app/src/flows/CreatePostFlow.tsx +0 -86
- package/examples/social-app/generated/web/social-app/src/i18n.tsx +0 -59
- package/examples/social-app/generated/web/social-app/src/lib/icons.tsx +0 -85
- package/examples/social-app/generated/web/social-app/src/lib/tokens.ts +0 -70
- package/examples/social-app/generated/web/social-app/src/lib/utils.ts +0 -97
- package/examples/social-app/generated/web/social-app/src/locales/en.json +0 -67
- package/examples/social-app/generated/web/social-app/src/locales/ru.json +0 -67
- package/examples/social-app/generated/web/social-app/src/locales/uz.json +0 -67
- package/examples/social-app/generated/web/social-app/src/main.tsx +0 -16
- package/examples/social-app/generated/web/social-app/src/screens/ChatDetailScreen.tsx +0 -90
- package/examples/social-app/generated/web/social-app/src/screens/DiscoverScreen.tsx +0 -86
- package/examples/social-app/generated/web/social-app/src/screens/EditProfileScreen.tsx +0 -57
- package/examples/social-app/generated/web/social-app/src/screens/HomeFeedScreen.tsx +0 -103
- package/examples/social-app/generated/web/social-app/src/screens/MessagesInboxScreen.tsx +0 -52
- package/examples/social-app/generated/web/social-app/src/screens/NotificationsScreen.tsx +0 -41
- package/examples/social-app/generated/web/social-app/src/screens/PostDetailScreen.tsx +0 -115
- package/examples/social-app/generated/web/social-app/src/screens/ProfileSelfScreen.tsx +0 -57
- package/examples/social-app/generated/web/social-app/src/screens/ProfileUserScreen.tsx +0 -76
- package/examples/social-app/generated/web/social-app/src/screens/SearchResultsScreen.tsx +0 -96
- package/examples/social-app/generated/web/social-app/src/screens/SettingsScreen.tsx +0 -79
- package/examples/social-app/generated/web/social-app/src/state/store.ts +0 -592
- package/examples/social-app/generated/web/social-app/src/styles.css +0 -125
- package/examples/social-app/generated/web/social-app/src/vite-env.d.ts +0 -1
- package/examples/social-app/generated/web/social-app/tsconfig.json +0 -22
- package/examples/social-app/generated/web/social-app/tsconfig.node.json +0 -13
- package/examples/social-app/generated/web/social-app/tsconfig.node.tsbuildinfo +0 -1
- package/examples/social-app/generated/web/social-app/tsconfig.tsbuildinfo +0 -1
- package/examples/social-app/generated/web/social-app/vite.config.d.ts +0 -2
- package/examples/social-app/generated/web/social-app/vite.config.js +0 -6
- package/examples/social-app/generated/web/social-app/vite.config.ts +0 -7
- package/examples/social-app/package.json +0 -13
- package/examples/social-app/take-web-screenshots.ts +0 -97
- package/examples/taskflow/.codex/config.toml +0 -4
- package/examples/taskflow/.mcp.json +0 -10
- package/examples/taskflow/AGENTS.md +0 -124
- package/examples/taskflow/CLAUDE.md +0 -124
- package/examples/taskflow/backend/.gitkeep +0 -1
- package/examples/taskflow/generated/android/TaskFlow/README.md +0 -43
- package/examples/taskflow/generated/android/TaskFlow/app/build.gradle.kts +0 -76
- package/examples/taskflow/generated/android/TaskFlow/app/proguard-rules.pro +0 -1
- package/examples/taskflow/generated/android/TaskFlow/app/src/main/AndroidManifest.xml +0 -21
- package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/MainActivity.kt +0 -19
- package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/TaskFlowApp.kt +0 -283
- package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/model/DomainModels.kt +0 -106
- package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/model/SampleData.kt +0 -57
- package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/components/Common.kt +0 -109
- package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/screens/HomeScreen.kt +0 -112
- package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/screens/ProjectsScreen.kt +0 -61
- package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/screens/SettingsScreen.kt +0 -82
- package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/screens/TaskDetailScreen.kt +0 -111
- package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/sheets/Sheets.kt +0 -77
- package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/theme/Color.kt +0 -30
- package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/theme/Theme.kt +0 -86
- package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/theme/Type.kt +0 -57
- package/examples/taskflow/generated/android/TaskFlow/app/src/main/res/values/strings.xml +0 -155
- package/examples/taskflow/generated/android/TaskFlow/app/src/main/res/values/themes.xml +0 -4
- package/examples/taskflow/generated/android/TaskFlow/build.gradle.kts +0 -5
- package/examples/taskflow/generated/android/TaskFlow/gradle/gradle-daemon-jvm.properties +0 -12
- package/examples/taskflow/generated/android/TaskFlow/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/examples/taskflow/generated/android/TaskFlow/gradle/wrapper/gradle-wrapper.properties +0 -7
- package/examples/taskflow/generated/android/TaskFlow/gradle.properties +0 -4
- package/examples/taskflow/generated/android/TaskFlow/gradlew +0 -18
- package/examples/taskflow/generated/android/TaskFlow/gradlew.bat +0 -12
- package/examples/taskflow/generated/android/TaskFlow/settings.gradle.kts +0 -18
- package/examples/taskflow/generated/ios/TaskFlow/README.md +0 -21
- package/examples/taskflow/generated/ios/TaskFlow/Resources/en.lproj/Localizable.strings +0 -115
- package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/App/TaskFlowApp.swift +0 -24
- package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Components/AppChrome.swift +0 -150
- package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Flows/TaskEditorSheet.swift +0 -220
- package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Models/DomainModels.swift +0 -122
- package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Screens/CalendarView.swift +0 -21
- package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Screens/HomeView.swift +0 -201
- package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Screens/ProfileEditView.swift +0 -48
- package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Screens/ProjectDetailView.swift +0 -59
- package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Screens/ProjectsView.swift +0 -63
- package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Screens/SettingsView.swift +0 -85
- package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Screens/TaskDetailView.swift +0 -219
- package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Support/AppModel.swift +0 -320
- package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Support/AppSupport.swift +0 -41
- package/examples/taskflow/generated/ios/TaskFlow/project.yml +0 -31
- package/examples/taskflow/generated/web/TaskFlow/README.md +0 -19
- package/examples/taskflow/generated/web/TaskFlow/index.html +0 -12
- package/examples/taskflow/generated/web/TaskFlow/package-lock.json +0 -1908
- package/examples/taskflow/generated/web/TaskFlow/package.json +0 -24
- package/examples/taskflow/generated/web/TaskFlow/src/App.tsx +0 -58
- package/examples/taskflow/generated/web/TaskFlow/src/AppShell.tsx +0 -55
- package/examples/taskflow/generated/web/TaskFlow/src/components/Common.tsx +0 -82
- package/examples/taskflow/generated/web/TaskFlow/src/components/Modals.tsx +0 -191
- package/examples/taskflow/generated/web/TaskFlow/src/components/Nav.tsx +0 -41
- package/examples/taskflow/generated/web/TaskFlow/src/generated/messages.ts +0 -131
- package/examples/taskflow/generated/web/TaskFlow/src/hooks.ts +0 -25
- package/examples/taskflow/generated/web/TaskFlow/src/i18n.ts +0 -39
- package/examples/taskflow/generated/web/TaskFlow/src/locales.en.json +0 -111
- package/examples/taskflow/generated/web/TaskFlow/src/main.tsx +0 -13
- package/examples/taskflow/generated/web/TaskFlow/src/screens/HomeScreen.tsx +0 -111
- package/examples/taskflow/generated/web/TaskFlow/src/screens/ProjectsScreen.tsx +0 -82
- package/examples/taskflow/generated/web/TaskFlow/src/screens/SettingsScreens.tsx +0 -132
- package/examples/taskflow/generated/web/TaskFlow/src/screens/TaskDetail.tsx +0 -105
- package/examples/taskflow/generated/web/TaskFlow/src/store.ts +0 -216
- package/examples/taskflow/generated/web/TaskFlow/src/styles.css +0 -617
- package/examples/taskflow/generated/web/TaskFlow/src/types.ts +0 -64
- package/examples/taskflow/generated/web/TaskFlow/src/utils.ts +0 -78
- package/examples/taskflow/generated/web/TaskFlow/tsconfig.json +0 -21
- package/examples/taskflow/generated/web/TaskFlow/vite.config.ts +0 -6
- package/examples/todo-orbit/.codex/config.toml +0 -4
- package/examples/todo-orbit/.mcp.json +0 -10
- package/examples/todo-orbit/AGENTS.md +0 -124
- package/examples/todo-orbit/CLAUDE.md +0 -124
- package/examples/todo-orbit/backend/.gitkeep +0 -1
- package/examples/todo-orbit/generated/android/Todo Orbit/README.md +0 -14
- package/examples/todo-orbit/generated/android/Todo Orbit/app/build.gradle.kts +0 -58
- package/examples/todo-orbit/generated/android/Todo Orbit/app/proguard-rules.pro +0 -1
- package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/AndroidManifest.xml +0 -20
- package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/MainActivity.kt +0 -14
- package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/TodoOrbitApp.kt +0 -345
- package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/support/AppLogic.kt +0 -231
- package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/support/Models.kt +0 -169
- package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/support/Strings.kt +0 -8
- package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/components/CommonComponents.kt +0 -236
- package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/screens/AnalyticsScreen.kt +0 -193
- package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/screens/SettingsScreen.kt +0 -102
- package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/screens/TasksScreen.kt +0 -347
- package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/sheets/EditorSheets.kt +0 -347
- package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/theme/TodoOrbitTheme.kt +0 -59
- package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/res/values/strings.xml +0 -149
- package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/res/values-ru/strings.xml +0 -155
- package/examples/todo-orbit/generated/android/Todo Orbit/build.gradle.kts +0 -4
- package/examples/todo-orbit/generated/android/Todo Orbit/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/examples/todo-orbit/generated/android/Todo Orbit/gradle/wrapper/gradle-wrapper.properties +0 -7
- package/examples/todo-orbit/generated/android/Todo Orbit/gradle.properties +0 -4
- package/examples/todo-orbit/generated/android/Todo Orbit/gradlew +0 -248
- package/examples/todo-orbit/generated/android/Todo Orbit/gradlew.bat +0 -93
- package/examples/todo-orbit/generated/android/Todo Orbit/settings.gradle.kts +0 -18
- package/examples/todo-orbit/generated/ios/Todo Orbit/.screenshot-uitest/Sources/ScreenshotUITest.swift +0 -36
- package/examples/todo-orbit/generated/ios/Todo Orbit/README.md +0 -29
- package/examples/todo-orbit/generated/ios/Todo Orbit/Resources/en.lproj/Localizable.strings +0 -119
- package/examples/todo-orbit/generated/ios/Todo Orbit/Resources/ru.lproj/Localizable.strings +0 -119
- package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/App/TodoOrbitApp.swift +0 -50
- package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Components/OrbitChrome.swift +0 -204
- package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Components/SchedulePreviewView.swift +0 -126
- package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Components/TrendChartView.swift +0 -70
- package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Flows/RecurringRuleSheet.swift +0 -126
- package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Flows/TaskEditorSheet.swift +0 -61
- package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Models/DomainModels.swift +0 -238
- package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Screens/AnalyticsView.swift +0 -94
- package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Screens/SettingsView.swift +0 -76
- package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Screens/TasksHomeView.swift +0 -364
- package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Support/AppModel.swift +0 -324
- package/examples/todo-orbit/generated/ios/Todo Orbit/TodoOrbit.xcodeproj/project.pbxproj +0 -439
- package/examples/todo-orbit/generated/ios/Todo Orbit/TodoOrbit.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
- package/examples/todo-orbit/generated/ios/Todo Orbit/TodoOrbit.xcodeproj/xcshareddata/xcschemes/TodoOrbit.xcscheme +0 -89
- package/examples/todo-orbit/generated/ios/Todo Orbit/project.yml +0 -32
- package/examples/todo-orbit/generated/web/Todo Orbit/index.html +0 -16
- package/examples/todo-orbit/generated/web/Todo Orbit/package-lock.json +0 -1087
- package/examples/todo-orbit/generated/web/Todo Orbit/package.json +0 -24
- package/examples/todo-orbit/generated/web/Todo Orbit/src/App.tsx +0 -2167
- package/examples/todo-orbit/generated/web/Todo Orbit/src/main.tsx +0 -13
- package/examples/todo-orbit/generated/web/Todo Orbit/src/styles.css +0 -926
- package/examples/todo-orbit/generated/web/Todo Orbit/tsconfig.json +0 -19
- package/examples/todo-orbit/generated/web/Todo Orbit/vite.config.ts +0 -6
package/drift/index.ts
CHANGED
|
@@ -73,6 +73,33 @@ export interface ExplainResult {
|
|
|
73
73
|
files: FileExplanation[];
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
// ── shared layer types ───────────────────────────────────────────────
|
|
77
|
+
|
|
78
|
+
export interface SharedLayerConfig {
|
|
79
|
+
name: string;
|
|
80
|
+
platforms: string[];
|
|
81
|
+
language: string;
|
|
82
|
+
root: string; // resolved absolute path
|
|
83
|
+
paths: Record<string, string>;
|
|
84
|
+
tracks: string[]; // spec categories to track for drift
|
|
85
|
+
scope: string; // what code belongs in this layer
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface SharedLayerState {
|
|
89
|
+
spec_version: string;
|
|
90
|
+
snapshot_at: string;
|
|
91
|
+
layer_name: string;
|
|
92
|
+
generated_by_target: string;
|
|
93
|
+
baseline?: BaselineRef;
|
|
94
|
+
files: Record<string, FileEntry>;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface SharedLayerDriftResult {
|
|
98
|
+
layer: SharedLayerConfig;
|
|
99
|
+
drift: DriftResult;
|
|
100
|
+
state: SharedLayerState | null;
|
|
101
|
+
}
|
|
102
|
+
|
|
76
103
|
// ── helpers ───────────────────────────────────────────────────────────
|
|
77
104
|
|
|
78
105
|
export function listFiles(dir: string, ext: string): string[] {
|
|
@@ -166,16 +193,43 @@ export function discoverSpecFiles(projectDir: string): string[] {
|
|
|
166
193
|
return files;
|
|
167
194
|
}
|
|
168
195
|
|
|
169
|
-
|
|
170
|
-
|
|
196
|
+
/** Classify a relative spec path into a lowercase category. */
|
|
197
|
+
export function specCategory(relPath: string): string {
|
|
198
|
+
if (relPath === "openuispec.yaml") return "manifest";
|
|
171
199
|
const dir = dirname(relPath);
|
|
172
|
-
if (dir === "tokens") return "
|
|
173
|
-
if (dir === "screens") return "
|
|
174
|
-
if (dir === "flows") return "
|
|
175
|
-
if (dir === "platform") return "
|
|
176
|
-
if (dir === "locales") return "
|
|
177
|
-
if (dir === "contracts") return "
|
|
178
|
-
return "
|
|
200
|
+
if (dir === "tokens") return "tokens";
|
|
201
|
+
if (dir === "screens") return "screens";
|
|
202
|
+
if (dir === "flows") return "flows";
|
|
203
|
+
if (dir === "platform") return "platform";
|
|
204
|
+
if (dir === "locales") return "locales";
|
|
205
|
+
if (dir === "contracts") return "contracts";
|
|
206
|
+
return "other";
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function categorize(relPath: string): string {
|
|
210
|
+
const cat = specCategory(relPath);
|
|
211
|
+
return cat.charAt(0).toUpperCase() + cat.slice(1);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/** Returns true when a DriftResult contains any changes. */
|
|
215
|
+
export function hasDriftChanges(d: DriftResult): boolean {
|
|
216
|
+
return d.changed.length > 0 || d.added.length > 0 || d.removed.length > 0;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/** Hash spec files into FileEntry records keyed by relative path. */
|
|
220
|
+
export function buildFileEntries(
|
|
221
|
+
projectDir: string,
|
|
222
|
+
files: string[]
|
|
223
|
+
): { entries: Record<string, FileEntry>; stubs: number } {
|
|
224
|
+
const entries: Record<string, FileEntry> = {};
|
|
225
|
+
let stubs = 0;
|
|
226
|
+
for (const f of files) {
|
|
227
|
+
const rel = relative(projectDir, f);
|
|
228
|
+
const status = hasStatusSemantics(rel) ? readStatus(f) : "ready";
|
|
229
|
+
entries[rel] = { hash: hashFile(f), status };
|
|
230
|
+
if (status === "stub") stubs++;
|
|
231
|
+
}
|
|
232
|
+
return { entries, stubs };
|
|
179
233
|
}
|
|
180
234
|
|
|
181
235
|
function runGit(args: string[], cwd: string): string | null {
|
|
@@ -536,35 +590,187 @@ function normalizeEntry(value: string | FileEntry): FileEntry {
|
|
|
536
590
|
return value;
|
|
537
591
|
}
|
|
538
592
|
|
|
593
|
+
// ── shared layers ────────────────────────────────────────────────────
|
|
594
|
+
|
|
595
|
+
/** Parse `generation.shared` from the manifest and resolve roots to absolute paths. */
|
|
596
|
+
export function readSharedLayers(projectDir: string): SharedLayerConfig[] {
|
|
597
|
+
const manifest = readManifest(projectDir);
|
|
598
|
+
const shared = manifest.generation?.shared;
|
|
599
|
+
if (!shared || typeof shared !== "object") return [];
|
|
600
|
+
|
|
601
|
+
return Object.entries(shared).map(([name, config]) => {
|
|
602
|
+
const cfg = config as Record<string, any>;
|
|
603
|
+
return {
|
|
604
|
+
name,
|
|
605
|
+
platforms: Array.isArray(cfg.platforms) ? cfg.platforms : [],
|
|
606
|
+
language: typeof cfg.language === "string" ? cfg.language : "",
|
|
607
|
+
root: resolve(projectDir, cfg.root ?? "."),
|
|
608
|
+
paths: typeof cfg.paths === "object" && cfg.paths !== null ? cfg.paths : {},
|
|
609
|
+
tracks: Array.isArray(cfg.tracks) ? cfg.tracks : [],
|
|
610
|
+
scope: typeof cfg.scope === "string" ? cfg.scope : "",
|
|
611
|
+
};
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
/** Return shared layers whose `platforms` array includes the given target. */
|
|
616
|
+
export function sharedLayersForTarget(projectDir: string, target: string): SharedLayerConfig[] {
|
|
617
|
+
return readSharedLayers(projectDir).filter((layer) => layer.platforms.includes(target));
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
/** Returns the state file path for a shared layer. */
|
|
621
|
+
export function sharedLayerStatePath(layer: SharedLayerConfig): string {
|
|
622
|
+
return join(layer.root, `.openuispec-shared-${layer.name}.json`);
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
/** Read an existing shared layer state file, or null if it doesn't exist. */
|
|
626
|
+
export function readSharedLayerState(layer: SharedLayerConfig): SharedLayerState | null {
|
|
627
|
+
try {
|
|
628
|
+
return JSON.parse(readFileSync(sharedLayerStatePath(layer), "utf-8"));
|
|
629
|
+
} catch {
|
|
630
|
+
return null;
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
/** Filter spec files to only those in the given categories. */
|
|
635
|
+
function filterSpecFilesByCategories(
|
|
636
|
+
projectDir: string,
|
|
637
|
+
files: string[],
|
|
638
|
+
categories: string[]
|
|
639
|
+
): string[] {
|
|
640
|
+
const catSet = new Set(categories);
|
|
641
|
+
return files.filter((f) => catSet.has(specCategory(relative(projectDir, f))));
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
/** Create a snapshot of spec state for a shared layer. */
|
|
645
|
+
export function createSharedSnapshot(
|
|
646
|
+
cwd: string,
|
|
647
|
+
layerName: string,
|
|
648
|
+
generatedByTarget: string
|
|
649
|
+
): SnapshotResult {
|
|
650
|
+
const projectDir = findProjectDir(cwd);
|
|
651
|
+
const layers = readSharedLayers(projectDir);
|
|
652
|
+
const layer = layers.find((l) => l.name === layerName);
|
|
653
|
+
if (!layer) {
|
|
654
|
+
throw new Error(`Shared layer "${layerName}" not found in generation.shared`);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
const manifest = readManifest(projectDir);
|
|
658
|
+
const allFiles = discoverSpecFiles(projectDir);
|
|
659
|
+
const files = filterSpecFilesByCategories(projectDir, allFiles, layer.tracks);
|
|
660
|
+
const baseline = captureBaseline(projectDir, allFiles);
|
|
661
|
+
|
|
662
|
+
const { entries, stubs: stubCount } = buildFileEntries(projectDir, files);
|
|
663
|
+
|
|
664
|
+
const state: SharedLayerState = {
|
|
665
|
+
spec_version: manifest.spec_version ?? "0.1",
|
|
666
|
+
snapshot_at: new Date().toISOString(),
|
|
667
|
+
layer_name: layerName,
|
|
668
|
+
generated_by_target: generatedByTarget,
|
|
669
|
+
baseline,
|
|
670
|
+
files: entries,
|
|
671
|
+
};
|
|
672
|
+
|
|
673
|
+
const outPath = sharedLayerStatePath(layer);
|
|
674
|
+
writeFileSync(outPath, JSON.stringify(state, null, 2) + "\n");
|
|
675
|
+
|
|
676
|
+
return {
|
|
677
|
+
target: `shared:${layerName}`,
|
|
678
|
+
snapshot_at: state.snapshot_at,
|
|
679
|
+
files_hashed: Object.keys(entries).length,
|
|
680
|
+
stubs: stubCount,
|
|
681
|
+
state_path: relative(cwd, outPath),
|
|
682
|
+
baseline: formatBaseline(baseline),
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
/** Compute drift for a shared layer against its saved state. */
|
|
687
|
+
export function computeSharedDrift(
|
|
688
|
+
projectDir: string,
|
|
689
|
+
layer: SharedLayerConfig
|
|
690
|
+
): SharedLayerDriftResult {
|
|
691
|
+
const state = readSharedLayerState(layer);
|
|
692
|
+
if (!state) {
|
|
693
|
+
return {
|
|
694
|
+
layer,
|
|
695
|
+
drift: { changed: [], added: [], removed: [], unchanged: [] },
|
|
696
|
+
state: null,
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
const allFiles = discoverSpecFiles(projectDir);
|
|
701
|
+
const files = filterSpecFilesByCategories(projectDir, allFiles, layer.tracks);
|
|
702
|
+
const { entries: current } = buildFileEntries(projectDir, files);
|
|
703
|
+
|
|
704
|
+
const drift: DriftResult = { changed: [], added: [], removed: [], unchanged: [] };
|
|
705
|
+
|
|
706
|
+
for (const [rel, entry] of Object.entries(current)) {
|
|
707
|
+
const snapshotEntry = state.files[rel]
|
|
708
|
+
? normalizeEntry(state.files[rel] as string | FileEntry)
|
|
709
|
+
: null;
|
|
710
|
+
if (!snapshotEntry) {
|
|
711
|
+
drift.added.push(rel);
|
|
712
|
+
} else if (snapshotEntry.hash !== entry.hash) {
|
|
713
|
+
drift.changed.push(rel);
|
|
714
|
+
} else {
|
|
715
|
+
drift.unchanged.push(rel);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
for (const rel of Object.keys(state.files)) {
|
|
720
|
+
if (!(rel in current)) {
|
|
721
|
+
drift.removed.push(rel);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
return { layer, drift, state };
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
/** Read `generation.structure[target]` from the manifest. */
|
|
729
|
+
export function readTargetStructure(
|
|
730
|
+
projectDir: string,
|
|
731
|
+
target: string
|
|
732
|
+
): { root: string; paths: Record<string, string>; scope: string } | null {
|
|
733
|
+
const manifest = readManifest(projectDir);
|
|
734
|
+
const structure = manifest.generation?.structure?.[target];
|
|
735
|
+
if (!structure || typeof structure !== "object" || typeof structure.root !== "string") {
|
|
736
|
+
return null;
|
|
737
|
+
}
|
|
738
|
+
return {
|
|
739
|
+
root: resolve(projectDir, structure.root),
|
|
740
|
+
paths: typeof structure.paths === "object" && structure.paths !== null ? structure.paths : {},
|
|
741
|
+
scope: typeof structure.scope === "string" ? structure.scope : "",
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
|
|
539
745
|
// ── snapshot ──────────────────────────────────────────────────────────
|
|
540
746
|
|
|
541
|
-
|
|
747
|
+
export interface SnapshotResult {
|
|
748
|
+
target: string;
|
|
749
|
+
snapshot_at: string;
|
|
750
|
+
files_hashed: number;
|
|
751
|
+
stubs: number;
|
|
752
|
+
state_path: string;
|
|
753
|
+
baseline: string | null;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
export function createSnapshot(cwd: string, target: string): SnapshotResult {
|
|
757
|
+
const projectDir = findProjectDir(cwd);
|
|
542
758
|
const projectName = readProjectName(projectDir);
|
|
543
759
|
const outDir = resolveOutputDir(projectDir, projectName, target);
|
|
544
760
|
if (!existsSync(outDir)) {
|
|
545
|
-
|
|
546
|
-
`
|
|
761
|
+
throw new Error(
|
|
762
|
+
`Output directory not found: ${relative(cwd, outDir)}\n` +
|
|
547
763
|
`Run code generation for "${target}" first.`
|
|
548
764
|
);
|
|
549
|
-
process.exit(1);
|
|
550
765
|
}
|
|
551
766
|
|
|
767
|
+
const manifest = readManifest(projectDir);
|
|
552
768
|
const files = discoverSpecFiles(projectDir);
|
|
553
|
-
const doc = YAML.parse(readFileSync(join(projectDir, "openuispec.yaml"), "utf-8"));
|
|
554
769
|
const baseline = captureBaseline(projectDir, files);
|
|
555
|
-
|
|
556
|
-
const entries: Record<string, FileEntry> = {};
|
|
557
|
-
let stubCount = 0;
|
|
558
|
-
|
|
559
|
-
for (const f of files) {
|
|
560
|
-
const rel = relative(projectDir, f);
|
|
561
|
-
const status = hasStatusSemantics(rel) ? readStatus(f) : "ready";
|
|
562
|
-
entries[rel] = { hash: hashFile(f), status };
|
|
563
|
-
if (status === "stub") stubCount++;
|
|
564
|
-
}
|
|
770
|
+
const { entries, stubs: stubCount } = buildFileEntries(projectDir, files);
|
|
565
771
|
|
|
566
772
|
const state: StateFile = {
|
|
567
|
-
spec_version:
|
|
773
|
+
spec_version: manifest.spec_version ?? "0.1",
|
|
568
774
|
snapshot_at: new Date().toISOString(),
|
|
569
775
|
target,
|
|
570
776
|
baseline,
|
|
@@ -573,15 +779,46 @@ function snapshot(cwd: string, projectDir: string, target: string): void {
|
|
|
573
779
|
|
|
574
780
|
const outPath = stateFilePath(projectDir, projectName, target);
|
|
575
781
|
writeFileSync(outPath, JSON.stringify(state, null, 2) + "\n");
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
782
|
+
|
|
783
|
+
// Auto-snapshot shared layers with tracks for this target if their state file doesn't exist yet
|
|
784
|
+
const sharedLayers = sharedLayersForTarget(projectDir, target);
|
|
785
|
+
for (const layer of sharedLayers) {
|
|
786
|
+
if (layer.tracks.length === 0) continue;
|
|
787
|
+
const layerStatePath = sharedLayerStatePath(layer);
|
|
788
|
+
if (!existsSync(layerStatePath) && existsSync(layer.root)) {
|
|
789
|
+
try {
|
|
790
|
+
createSharedSnapshot(cwd, layer.name, target);
|
|
791
|
+
} catch {
|
|
792
|
+
// Non-fatal: shared layer snapshot is best-effort during target snapshot
|
|
793
|
+
}
|
|
794
|
+
}
|
|
580
795
|
}
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
796
|
+
|
|
797
|
+
return {
|
|
798
|
+
target,
|
|
799
|
+
snapshot_at: state.snapshot_at,
|
|
800
|
+
files_hashed: Object.keys(entries).length,
|
|
801
|
+
stubs: stubCount,
|
|
802
|
+
state_path: relative(cwd, outPath),
|
|
803
|
+
baseline: formatBaseline(baseline),
|
|
804
|
+
};
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
function snapshot(cwd: string, projectDir: string, target: string): void {
|
|
808
|
+
try {
|
|
809
|
+
const result = createSnapshot(cwd, target);
|
|
810
|
+
console.log(`Snapshot saved: ${result.state_path}`);
|
|
811
|
+
console.log(` ${result.files_hashed} files hashed`);
|
|
812
|
+
if (result.stubs > 0) {
|
|
813
|
+
console.log(` ${result.stubs} stubs (not tracked for drift)`);
|
|
814
|
+
}
|
|
815
|
+
console.log(` target: ${result.target}`);
|
|
816
|
+
if (result.baseline) {
|
|
817
|
+
console.log(` baseline: ${result.baseline}`);
|
|
818
|
+
}
|
|
819
|
+
} catch (err) {
|
|
820
|
+
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
821
|
+
process.exit(1);
|
|
585
822
|
}
|
|
586
823
|
}
|
|
587
824
|
|
|
@@ -593,6 +830,7 @@ export interface CheckResult {
|
|
|
593
830
|
stubDrift: DriftResult;
|
|
594
831
|
statuses: Record<string, string>;
|
|
595
832
|
explanation?: ExplainResult;
|
|
833
|
+
shared_layer_drift?: SharedLayerDriftResult[];
|
|
596
834
|
}
|
|
597
835
|
|
|
598
836
|
export function computeDrift(
|
|
@@ -601,13 +839,7 @@ export function computeDrift(
|
|
|
601
839
|
includeAll: boolean
|
|
602
840
|
): CheckResult {
|
|
603
841
|
const files = discoverSpecFiles(projectDir);
|
|
604
|
-
|
|
605
|
-
const current: Record<string, FileEntry> = {};
|
|
606
|
-
for (const f of files) {
|
|
607
|
-
const rel = relative(projectDir, f);
|
|
608
|
-
const status = hasStatusSemantics(rel) ? readStatus(f) : "ready";
|
|
609
|
-
current[rel] = { hash: hashFile(f), status };
|
|
610
|
-
}
|
|
842
|
+
const { entries: current } = buildFileEntries(projectDir, files);
|
|
611
843
|
|
|
612
844
|
const drift: DriftResult = { changed: [], added: [], removed: [], unchanged: [] };
|
|
613
845
|
const stubDrift: DriftResult = { changed: [], added: [], removed: [], unchanged: [] };
|
|
@@ -662,6 +894,15 @@ export function loadTargetDrift(
|
|
|
662
894
|
result.explanation = explainDrift(projectDir, result);
|
|
663
895
|
}
|
|
664
896
|
|
|
897
|
+
// Include shared layer drift for this target (only layers with tracks configured)
|
|
898
|
+
const sharedLayers = sharedLayersForTarget(projectDir, target);
|
|
899
|
+
const trackingLayers = sharedLayers.filter((l) => l.tracks.length > 0);
|
|
900
|
+
if (trackingLayers.length > 0) {
|
|
901
|
+
result.shared_layer_drift = trackingLayers.map((layer) =>
|
|
902
|
+
computeSharedDrift(projectDir, layer)
|
|
903
|
+
);
|
|
904
|
+
}
|
|
905
|
+
|
|
665
906
|
return { projectDir, projectName, statePath, result };
|
|
666
907
|
}
|
|
667
908
|
|