@ankhorage/contracts 7.1.0 → 7.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +1 -1
  3. package/dist/appManifest/bindings.d.ts +4 -0
  4. package/dist/appManifest/bindings.d.ts.map +1 -0
  5. package/dist/appManifest/bindings.js +96 -0
  6. package/dist/appManifest/bindings.js.map +1 -0
  7. package/dist/appManifest/dataSources.d.ts +2 -0
  8. package/dist/appManifest/dataSources.d.ts.map +1 -0
  9. package/dist/appManifest/dataSources.js +133 -0
  10. package/dist/appManifest/dataSources.js.map +1 -0
  11. package/dist/appManifest/generatedApis.d.ts +2 -0
  12. package/dist/appManifest/generatedApis.d.ts.map +1 -0
  13. package/dist/appManifest/generatedApis.js +87 -0
  14. package/dist/appManifest/generatedApis.js.map +1 -0
  15. package/dist/appManifest/infra.d.ts +2 -0
  16. package/dist/appManifest/infra.d.ts.map +1 -0
  17. package/dist/appManifest/infra.js +136 -0
  18. package/dist/appManifest/infra.js.map +1 -0
  19. package/dist/appManifest/media.d.ts +3 -0
  20. package/dist/appManifest/media.d.ts.map +1 -0
  21. package/dist/appManifest/media.js +75 -0
  22. package/dist/appManifest/media.js.map +1 -0
  23. package/dist/appManifest/screens.d.ts +6 -0
  24. package/dist/appManifest/screens.d.ts.map +1 -0
  25. package/dist/appManifest/screens.js +119 -0
  26. package/dist/appManifest/screens.js.map +1 -0
  27. package/dist/appManifest/shared.d.ts +8 -0
  28. package/dist/appManifest/shared.d.ts.map +1 -0
  29. package/dist/appManifest/shared.js +31 -0
  30. package/dist/appManifest/shared.js.map +1 -0
  31. package/dist/appManifest.d.ts +19 -0
  32. package/dist/appManifest.d.ts.map +1 -0
  33. package/dist/appManifest.js +67 -0
  34. package/dist/appManifest.js.map +1 -0
  35. package/dist/index.d.ts +2 -0
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +2 -0
  38. package/dist/index.js.map +1 -1
  39. package/dist/media.d.ts +48 -0
  40. package/dist/media.d.ts.map +1 -0
  41. package/dist/media.js +11 -0
  42. package/dist/media.js.map +1 -0
  43. package/dist/types.d.ts +3 -0
  44. package/dist/types.d.ts.map +1 -1
  45. package/dist/types.js.map +1 -1
  46. package/package.json +8 -2
  47. package/src/appManifest/bindings.ts +135 -0
  48. package/src/appManifest/dataSources.ts +175 -0
  49. package/src/appManifest/generatedApis.ts +122 -0
  50. package/src/appManifest/infra.ts +199 -0
  51. package/src/appManifest/media.ts +101 -0
  52. package/src/appManifest/screens.ts +171 -0
  53. package/src/appManifest/shared.ts +40 -0
  54. package/src/appManifest.test.ts +203 -0
  55. package/src/appManifest.ts +89 -0
  56. package/src/index.ts +2 -0
  57. package/src/media.test.ts +70 -0
  58. package/src/media.ts +68 -0
  59. package/src/types.ts +3 -0
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAgJA,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAU,CAAC;AAGpE,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,eAAe;IACf,uBAAuB;IACvB,iBAAiB;IACjB,oBAAoB;IACpB,qBAAqB;IACrB,eAAe;IACf,YAAY;IACZ,OAAO;IACP,iBAAiB;IACjB,gBAAgB;IAChB,aAAa;IACb,WAAW;IACX,SAAS;IACT,aAAa;IACb,mBAAmB;IACnB,gBAAgB;IAChB,aAAa;IACb,WAAW;IACX,mBAAmB;IACnB,kBAAkB;IAClB,QAAQ;IACR,iBAAiB;IACjB,SAAS;CACD,CAAC;AAGX,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,UAAU,CAAU,CAAC;AAIxD,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,UAAU,CAAU,CAAC;AAIxD,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,KAAK,EAAE,MAAM,CAAU,CAAC;AAGvD,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAU,CAAC;AAG/D,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,QAAQ,CAAU,CAAC;AAInD,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAU,CAAC;AAGxF,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,MAAM,EAAE,MAAM,CAAU,CAAC;AAGrD,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAU,CAAC;AAG3D,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,CAAU,CAAC;AAGrE,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,UAAU,CAAU,CAAC;AAIpD,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,CAAU,CAAC;AAGhF,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,YAAY,EAAE,qBAAqB,CAAU,CAAC;AAGpF,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,GAAG,wBAAwB;IAC3B,WAAW;IACX,UAAU;IACV,aAAa;IACb,WAAW;CACH,CAAC;AAIX,MAAM,CAAC,MAAM,mCAAmC,GAAG,CAAC,YAAY,CAAU,CAAC;AAG3E,MAAM,CAAC,MAAM,8BAA8B,GAAG,CAAC,SAAS,EAAE,KAAK,EAAE,KAAK,CAAU,CAAC;AAGjF,MAAM,CAAC,MAAM,8BAA8B,GAAG,CAAC,KAAK,EAAE,KAAK,CAAU,CAAC","sourcesContent":["import type { ColorHarmony } from '@ankhorage/color-theory';\n\nimport type { AuthFlowConfig, AuthIdentifierKind, AuthOAuthConfig, AuthSignUpField } from './auth';\nimport type {\n BindingValueSource,\n ComponentDataBindingRegistry,\n ScreenDataLoaderDefinition,\n} from './bindings';\nimport type { DataSourceRegistry, GeneratedApiRegistry } from './data';\nimport type { ScreenRequirements } from './requirements';\nimport type { ThemeGlobalTokenOverrides, ThemeRecipeOverrides } from './theme';\n\nexport interface ThemeModeConfig {\n primaryColor: string;\n harmony: ColorHarmony;\n}\n\nexport interface ThemeConfig {\n id: string;\n name: string;\n light: ThemeModeConfig;\n dark: ThemeModeConfig;\n /** Theme-global authored token overrides shared by light and dark mode. */\n tokens?: ThemeGlobalTokenOverrides;\n /** Component/pattern recipe override values; recipe schemas remain package-owned metadata. */\n recipes?: ThemeRecipeOverrides;\n}\n\nexport type ActionType =\n | 'navigate'\n | 'alert'\n | 'console'\n | 'toggleDarkMode'\n | 'setLanguage'\n | 'search'\n | 'filter';\n\nexport interface NavigateAction {\n type: 'navigate';\n payload: {\n route: string;\n params?: Record<string, number | string>;\n };\n}\n\nexport interface AlertAction {\n type: 'alert';\n payload?: {\n message?: string;\n };\n}\n\nexport interface ConsoleAction {\n type: 'console';\n payload?: Record<string, unknown>;\n}\n\nexport interface ToggleDarkModeAction {\n type: 'toggleDarkMode';\n payload?: never;\n}\n\nexport interface SetLanguageAction {\n type: 'setLanguage';\n payload: {\n locale: string;\n };\n}\n\nexport interface SearchAction {\n type: 'search';\n payload: {\n query: string;\n scope?: string;\n };\n}\n\nexport interface FilterAction {\n type: 'filter';\n payload: {\n filterKey: string;\n filterValue: string;\n };\n}\n\nexport type Action =\n | AlertAction\n | ConsoleAction\n | FilterAction\n | NavigateAction\n | SearchAction\n | SetLanguageAction\n | ToggleDarkModeAction;\n\nexport type ManifestValue =\n | string\n | number\n | boolean\n | null\n | readonly ManifestValue[]\n | { readonly [key: string]: ManifestValue };\n\nexport type ComponentEventPayloadValue = ManifestValue;\n\nexport interface ComponentEventDto<\n TType extends string = string,\n TPayload extends object = Record<string, ComponentEventPayloadValue>,\n> {\n readonly type: TType;\n readonly sourceNodeId: string;\n readonly payload: TPayload;\n}\n\nexport type FormSubmitValues = Record<string, ComponentEventPayloadValue>;\n\nexport type FormSubmitEventDto = ComponentEventDto<\n 'form.submit',\n {\n readonly values: FormSubmitValues;\n }\n>;\n\nexport type ButtonPressEventDto = ComponentEventDto<'button.press', Record<string, never>>;\n\nexport interface CollectionItemPressPayload {\n readonly itemId: string | number;\n readonly item: Record<string, ComponentEventPayloadValue>;\n}\n\nexport type CollectionItemPressEventDto = ComponentEventDto<\n 'collection.itemPress',\n CollectionItemPressPayload\n>;\n\nexport type ComponentEventDtoKind =\n | ButtonPressEventDto['type']\n | CollectionItemPressEventDto['type']\n | FormSubmitEventDto['type'];\n\nexport type KnownComponentEventDto =\n | ButtonPressEventDto\n | CollectionItemPressEventDto\n | FormSubmitEventDto;\n\nexport const NAVIGATOR_TYPES = ['stack', 'tabs', 'drawer'] as const;\nexport type NavigatorType = (typeof NAVIGATOR_TYPES)[number];\n\nexport const APP_CATEGORIES = [\n 'books_reading',\n 'business_productivity',\n 'developer_tools',\n 'education_learning',\n 'entertainment_media',\n 'finance_money',\n 'food_drink',\n 'games',\n 'graphics_design',\n 'health_fitness',\n 'kids_family',\n 'lifestyle',\n 'medical',\n 'music_audio',\n 'navigation_travel',\n 'news_magazines',\n 'photo_video',\n 'reference',\n 'shopping_commerce',\n 'social_community',\n 'sports',\n 'utilities_tools',\n 'weather',\n] as const;\nexport type AppCategory = (typeof APP_CATEGORIES)[number];\n\nexport const DEPLOYMENT_TARGETS = ['minikube'] as const;\nexport type KnownDeploymentTarget = (typeof DEPLOYMENT_TARGETS)[number];\nexport type DeploymentTarget = KnownDeploymentTarget | (string & {});\n\nexport const DATABASE_PROVIDERS = ['supabase'] as const;\nexport type KnownDatabaseProvider = (typeof DATABASE_PROVIDERS)[number];\nexport type DatabaseProvider = KnownDatabaseProvider | (string & {});\n\nexport const DATABASE_TIERS = ['dev', 'prod'] as const;\nexport type DatabaseTier = (typeof DATABASE_TIERS)[number];\n\nexport const STORAGE_PROVIDERS = ['auto', 's3', 'r2'] as const;\nexport type StorageProvider = (typeof STORAGE_PROVIDERS)[number];\n\nexport const STATE_PROVIDERS = ['legend'] as const;\nexport type KnownStateProvider = (typeof STATE_PROVIDERS)[number];\nexport type StateProvider = KnownStateProvider | (string & {});\n\nexport const STATE_PERSISTENCE_MODES = ['none', 'local', 'secure', 'database'] as const;\nexport type StatePersistenceMode = (typeof STATE_PERSISTENCE_MODES)[number];\n\nexport const AUTHZ_KINDS = ['RBAC', 'ABAC'] as const;\nexport type AuthzKind = (typeof AUTHZ_KINDS)[number];\n\nexport const AUTHZ_ENGINES = ['cerbos', 'native'] as const;\nexport type AuthzEngine = (typeof AUTHZ_ENGINES)[number];\n\nexport const AUTH_SCOPES = ['global', 'none', 'integrated'] as const;\nexport type AuthScope = (typeof AUTH_SCOPES)[number];\n\nexport const AUTH_PROVIDERS = ['supabase'] as const;\nexport type KnownAuthProvider = (typeof AUTH_PROVIDERS)[number];\nexport type AuthProvider = KnownAuthProvider | (string & {});\n\nexport const AUTH_SIGN_IN_IDENTIFIERS = ['email', 'username', 'phone'] as const;\nexport type AuthSignInIdentifier = AuthIdentifierKind;\n\nexport const AUTH_SIGN_UP_POLICIES = ['autoSignIn', 'requireVerification'] as const;\nexport type AuthSignUpPolicy = (typeof AUTH_SIGN_UP_POLICIES)[number];\n\nexport const AUTH_PROFILE_FIELDS = [\n ...AUTH_SIGN_IN_IDENTIFIERS,\n 'firstName',\n 'lastName',\n 'displayName',\n 'avatarUrl',\n] as const;\nexport type KnownAuthProfileField = (typeof AUTH_PROFILE_FIELDS)[number];\nexport type AuthProfileField = KnownAuthProfileField | (string & {});\n\nexport const AUTH_PROFILE_PRIMARY_KEY_STRATEGIES = ['authUserId'] as const;\nexport type AuthProfilePrimaryKeyStrategy = (typeof AUTH_PROFILE_PRIMARY_KEY_STRATEGIES)[number];\n\nexport const AUTH_PROFILE_CREATE_STRATEGIES = ['trigger', 'api', 'app'] as const;\nexport type AuthProfileCreateStrategy = (typeof AUTH_PROFILE_CREATE_STRATEGIES)[number];\n\nexport const AUTH_PROFILE_UPDATE_STRATEGIES = ['api', 'app'] as const;\nexport type AuthProfileUpdateStrategy = (typeof AUTH_PROFILE_UPDATE_STRATEGIES)[number];\n\nexport interface IconSpec {\n name: string;\n provider?: string;\n size?: number | string;\n color?: string;\n}\n\nexport interface UiNodeRepeatSpec {\n source: BindingValueSource;\n itemAlias?: string;\n keyPath?: string;\n empty?: readonly UiNode[];\n}\n\nexport interface UiNode {\n id: string;\n type: string;\n alias?: string;\n props?: Record<string, unknown>;\n children?: UiNode[];\n style?: Record<string, number | string>;\n repeat?: UiNodeRepeatSpec;\n}\n\nexport interface ScreenSpec {\n id: string;\n name: string;\n title?: string;\n description?: string;\n root: UiNode;\n dataLoaders?: readonly ScreenDataLoaderDefinition[];\n requires?: ScreenRequirements;\n}\n\nexport interface NavigatorSpec {\n type: NavigatorType;\n initialRouteName?: string;\n routes: RouteDefinition[];\n options?: Record<string, unknown>;\n}\n\nexport interface RouteDefinition {\n name: string;\n path?: string;\n label?: string;\n icon?: IconSpec;\n /**\n * Whether this route appears in Tabs and Drawer primary navigation.\n *\n * Omitted routes are visible by default. Setting this to `false` hides the\n * route from primary navigation without making it unnavigable. Stack\n * navigators preserve the value but do not present primary navigation.\n */\n showInPrimaryNavigation?: boolean;\n guards?: string[];\n screenId?: string;\n navigator?: NavigatorSpec;\n}\n\nexport type SplashScreenResizeMode = 'contain' | 'cover' | 'native';\n\nexport interface SplashScreenAssetSpec {\n readonly image?: string;\n readonly imageWidth?: number;\n readonly resizeMode?: SplashScreenResizeMode;\n}\n\nexport interface SplashScreenModeSpec extends SplashScreenAssetSpec {\n readonly backgroundColor?: string;\n}\n\nexport interface SplashScreenSpec extends SplashScreenModeSpec {\n readonly dark?: SplashScreenModeSpec;\n}\n\nexport interface DeploymentSpec {\n target: DeploymentTarget;\n monitoring: boolean;\n}\n\nexport interface DatabaseSpec {\n provider: DatabaseProvider;\n tier: DatabaseTier;\n}\n\nexport interface StorageSpec {\n provider: StorageProvider;\n buckets: string[];\n}\n\nexport interface StateSpec {\n readonly provider: StateProvider;\n readonly persistence?: StatePersistenceMode;\n}\n\nexport interface AuthzSpec {\n kind: AuthzKind;\n engine: AuthzEngine;\n}\n\nexport interface AuthSignInSpec {\n identifiers: AuthSignInIdentifier[];\n}\n\nexport interface AuthSignUpSpec {\n requiredFields: AuthSignUpField[];\n optionalFields?: AuthSignUpField[];\n signUpPolicy?: AuthSignUpPolicy;\n}\n\nexport interface AuthProfileSpec {\n fields: AuthProfileField[];\n table?: string;\n primaryKey?: AuthProfilePrimaryKeyStrategy;\n createStrategy?: AuthProfileCreateStrategy;\n updateStrategy?: AuthProfileUpdateStrategy;\n}\n\nexport interface AuthSpec {\n scope: AuthScope;\n provider: AuthProvider;\n authorization?: AuthzSpec;\n flow?: AuthFlowConfig;\n signIn?: AuthSignInSpec;\n signUp?: AuthSignUpSpec;\n oauth?: AuthOAuthConfig;\n profile?: AuthProfileSpec;\n}\n\nexport interface NetworkingSpec {\n domain?: string;\n cdn: boolean;\n}\n\nexport interface InfraManifest {\n deployment?: DeploymentSpec;\n auth?: AuthSpec;\n database?: DatabaseSpec;\n storage?: StorageSpec;\n state?: StateSpec;\n networking?: NetworkingSpec;\n modules: string[];\n modulesConfig?: Record<string, unknown>;\n}\n\nexport interface AppSettings {\n apiBaseUrl?: string;\n localization: {\n defaultLocale: string;\n locales: string[];\n };\n}\n\nexport interface AppManifest {\n metadata: {\n name: string;\n slug: string;\n version: string;\n category: AppCategory;\n themeId: string;\n created?: string;\n updated?: string;\n };\n themes: ThemeConfig[];\n activeThemeId: string;\n activeThemeMode?: 'dark' | 'light';\n splashScreen?: SplashScreenSpec;\n infra: InfraManifest;\n navigator: NavigatorSpec;\n screens: Record<string, ScreenSpec>;\n generatedApis?: GeneratedApiRegistry;\n dataSources?: DataSourceRegistry;\n dataBindings?: ComponentDataBindingRegistry;\n settings: AppSettings;\n}\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAiJA,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAU,CAAC;AAGpE,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,eAAe;IACf,uBAAuB;IACvB,iBAAiB;IACjB,oBAAoB;IACpB,qBAAqB;IACrB,eAAe;IACf,YAAY;IACZ,OAAO;IACP,iBAAiB;IACjB,gBAAgB;IAChB,aAAa;IACb,WAAW;IACX,SAAS;IACT,aAAa;IACb,mBAAmB;IACnB,gBAAgB;IAChB,aAAa;IACb,WAAW;IACX,mBAAmB;IACnB,kBAAkB;IAClB,QAAQ;IACR,iBAAiB;IACjB,SAAS;CACD,CAAC;AAGX,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,UAAU,CAAU,CAAC;AAIxD,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,UAAU,CAAU,CAAC;AAIxD,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,KAAK,EAAE,MAAM,CAAU,CAAC;AAGvD,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAU,CAAC;AAG/D,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,QAAQ,CAAU,CAAC;AAInD,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAU,CAAC;AAGxF,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,MAAM,EAAE,MAAM,CAAU,CAAC;AAGrD,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAU,CAAC;AAG3D,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,CAAU,CAAC;AAGrE,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,UAAU,CAAU,CAAC;AAIpD,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,CAAU,CAAC;AAGhF,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,YAAY,EAAE,qBAAqB,CAAU,CAAC;AAGpF,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,GAAG,wBAAwB;IAC3B,WAAW;IACX,UAAU;IACV,aAAa;IACb,WAAW;CACH,CAAC;AAIX,MAAM,CAAC,MAAM,mCAAmC,GAAG,CAAC,YAAY,CAAU,CAAC;AAG3E,MAAM,CAAC,MAAM,8BAA8B,GAAG,CAAC,SAAS,EAAE,KAAK,EAAE,KAAK,CAAU,CAAC;AAGjF,MAAM,CAAC,MAAM,8BAA8B,GAAG,CAAC,KAAK,EAAE,KAAK,CAAU,CAAC","sourcesContent":["import type { ColorHarmony } from '@ankhorage/color-theory';\n\nimport type { AuthFlowConfig, AuthIdentifierKind, AuthOAuthConfig, AuthSignUpField } from './auth';\nimport type {\n BindingValueSource,\n ComponentDataBindingRegistry,\n ScreenDataLoaderDefinition,\n} from './bindings';\nimport type { DataSourceRegistry, GeneratedApiRegistry } from './data';\nimport type { MediaManifest } from './media';\nimport type { ScreenRequirements } from './requirements';\nimport type { ThemeGlobalTokenOverrides, ThemeRecipeOverrides } from './theme';\n\nexport interface ThemeModeConfig {\n primaryColor: string;\n harmony: ColorHarmony;\n}\n\nexport interface ThemeConfig {\n id: string;\n name: string;\n light: ThemeModeConfig;\n dark: ThemeModeConfig;\n /** Theme-global authored token overrides shared by light and dark mode. */\n tokens?: ThemeGlobalTokenOverrides;\n /** Component/pattern recipe override values; recipe schemas remain package-owned metadata. */\n recipes?: ThemeRecipeOverrides;\n}\n\nexport type ActionType =\n | 'navigate'\n | 'alert'\n | 'console'\n | 'toggleDarkMode'\n | 'setLanguage'\n | 'search'\n | 'filter';\n\nexport interface NavigateAction {\n type: 'navigate';\n payload: {\n route: string;\n params?: Record<string, number | string>;\n };\n}\n\nexport interface AlertAction {\n type: 'alert';\n payload?: {\n message?: string;\n };\n}\n\nexport interface ConsoleAction {\n type: 'console';\n payload?: Record<string, unknown>;\n}\n\nexport interface ToggleDarkModeAction {\n type: 'toggleDarkMode';\n payload?: never;\n}\n\nexport interface SetLanguageAction {\n type: 'setLanguage';\n payload: {\n locale: string;\n };\n}\n\nexport interface SearchAction {\n type: 'search';\n payload: {\n query: string;\n scope?: string;\n };\n}\n\nexport interface FilterAction {\n type: 'filter';\n payload: {\n filterKey: string;\n filterValue: string;\n };\n}\n\nexport type Action =\n | AlertAction\n | ConsoleAction\n | FilterAction\n | NavigateAction\n | SearchAction\n | SetLanguageAction\n | ToggleDarkModeAction;\n\nexport type ManifestValue =\n | string\n | number\n | boolean\n | null\n | readonly ManifestValue[]\n | { readonly [key: string]: ManifestValue };\n\nexport type ComponentEventPayloadValue = ManifestValue;\n\nexport interface ComponentEventDto<\n TType extends string = string,\n TPayload extends object = Record<string, ComponentEventPayloadValue>,\n> {\n readonly type: TType;\n readonly sourceNodeId: string;\n readonly payload: TPayload;\n}\n\nexport type FormSubmitValues = Record<string, ComponentEventPayloadValue>;\n\nexport type FormSubmitEventDto = ComponentEventDto<\n 'form.submit',\n {\n readonly values: FormSubmitValues;\n }\n>;\n\nexport type ButtonPressEventDto = ComponentEventDto<'button.press', Record<string, never>>;\n\nexport interface CollectionItemPressPayload {\n readonly itemId: string | number;\n readonly item: Record<string, ComponentEventPayloadValue>;\n}\n\nexport type CollectionItemPressEventDto = ComponentEventDto<\n 'collection.itemPress',\n CollectionItemPressPayload\n>;\n\nexport type ComponentEventDtoKind =\n | ButtonPressEventDto['type']\n | CollectionItemPressEventDto['type']\n | FormSubmitEventDto['type'];\n\nexport type KnownComponentEventDto =\n | ButtonPressEventDto\n | CollectionItemPressEventDto\n | FormSubmitEventDto;\n\nexport const NAVIGATOR_TYPES = ['stack', 'tabs', 'drawer'] as const;\nexport type NavigatorType = (typeof NAVIGATOR_TYPES)[number];\n\nexport const APP_CATEGORIES = [\n 'books_reading',\n 'business_productivity',\n 'developer_tools',\n 'education_learning',\n 'entertainment_media',\n 'finance_money',\n 'food_drink',\n 'games',\n 'graphics_design',\n 'health_fitness',\n 'kids_family',\n 'lifestyle',\n 'medical',\n 'music_audio',\n 'navigation_travel',\n 'news_magazines',\n 'photo_video',\n 'reference',\n 'shopping_commerce',\n 'social_community',\n 'sports',\n 'utilities_tools',\n 'weather',\n] as const;\nexport type AppCategory = (typeof APP_CATEGORIES)[number];\n\nexport const DEPLOYMENT_TARGETS = ['minikube'] as const;\nexport type KnownDeploymentTarget = (typeof DEPLOYMENT_TARGETS)[number];\nexport type DeploymentTarget = KnownDeploymentTarget | (string & {});\n\nexport const DATABASE_PROVIDERS = ['supabase'] as const;\nexport type KnownDatabaseProvider = (typeof DATABASE_PROVIDERS)[number];\nexport type DatabaseProvider = KnownDatabaseProvider | (string & {});\n\nexport const DATABASE_TIERS = ['dev', 'prod'] as const;\nexport type DatabaseTier = (typeof DATABASE_TIERS)[number];\n\nexport const STORAGE_PROVIDERS = ['auto', 's3', 'r2'] as const;\nexport type StorageProvider = (typeof STORAGE_PROVIDERS)[number];\n\nexport const STATE_PROVIDERS = ['legend'] as const;\nexport type KnownStateProvider = (typeof STATE_PROVIDERS)[number];\nexport type StateProvider = KnownStateProvider | (string & {});\n\nexport const STATE_PERSISTENCE_MODES = ['none', 'local', 'secure', 'database'] as const;\nexport type StatePersistenceMode = (typeof STATE_PERSISTENCE_MODES)[number];\n\nexport const AUTHZ_KINDS = ['RBAC', 'ABAC'] as const;\nexport type AuthzKind = (typeof AUTHZ_KINDS)[number];\n\nexport const AUTHZ_ENGINES = ['cerbos', 'native'] as const;\nexport type AuthzEngine = (typeof AUTHZ_ENGINES)[number];\n\nexport const AUTH_SCOPES = ['global', 'none', 'integrated'] as const;\nexport type AuthScope = (typeof AUTH_SCOPES)[number];\n\nexport const AUTH_PROVIDERS = ['supabase'] as const;\nexport type KnownAuthProvider = (typeof AUTH_PROVIDERS)[number];\nexport type AuthProvider = KnownAuthProvider | (string & {});\n\nexport const AUTH_SIGN_IN_IDENTIFIERS = ['email', 'username', 'phone'] as const;\nexport type AuthSignInIdentifier = AuthIdentifierKind;\n\nexport const AUTH_SIGN_UP_POLICIES = ['autoSignIn', 'requireVerification'] as const;\nexport type AuthSignUpPolicy = (typeof AUTH_SIGN_UP_POLICIES)[number];\n\nexport const AUTH_PROFILE_FIELDS = [\n ...AUTH_SIGN_IN_IDENTIFIERS,\n 'firstName',\n 'lastName',\n 'displayName',\n 'avatarUrl',\n] as const;\nexport type KnownAuthProfileField = (typeof AUTH_PROFILE_FIELDS)[number];\nexport type AuthProfileField = KnownAuthProfileField | (string & {});\n\nexport const AUTH_PROFILE_PRIMARY_KEY_STRATEGIES = ['authUserId'] as const;\nexport type AuthProfilePrimaryKeyStrategy = (typeof AUTH_PROFILE_PRIMARY_KEY_STRATEGIES)[number];\n\nexport const AUTH_PROFILE_CREATE_STRATEGIES = ['trigger', 'api', 'app'] as const;\nexport type AuthProfileCreateStrategy = (typeof AUTH_PROFILE_CREATE_STRATEGIES)[number];\n\nexport const AUTH_PROFILE_UPDATE_STRATEGIES = ['api', 'app'] as const;\nexport type AuthProfileUpdateStrategy = (typeof AUTH_PROFILE_UPDATE_STRATEGIES)[number];\n\nexport interface IconSpec {\n name: string;\n provider?: string;\n size?: number | string;\n color?: string;\n}\n\nexport interface UiNodeRepeatSpec {\n source: BindingValueSource;\n itemAlias?: string;\n keyPath?: string;\n empty?: readonly UiNode[];\n}\n\nexport interface UiNode {\n id: string;\n type: string;\n alias?: string;\n props?: Record<string, unknown>;\n children?: UiNode[];\n style?: Record<string, number | string>;\n repeat?: UiNodeRepeatSpec;\n}\n\nexport interface ScreenSpec {\n id: string;\n name: string;\n title?: string;\n description?: string;\n root: UiNode;\n dataLoaders?: readonly ScreenDataLoaderDefinition[];\n requires?: ScreenRequirements;\n}\n\nexport interface NavigatorSpec {\n type: NavigatorType;\n initialRouteName?: string;\n routes: RouteDefinition[];\n options?: Record<string, unknown>;\n}\n\nexport interface RouteDefinition {\n name: string;\n path?: string;\n label?: string;\n icon?: IconSpec;\n /**\n * Whether this route appears in Tabs and Drawer primary navigation.\n *\n * Omitted routes are visible by default. Setting this to `false` hides the\n * route from primary navigation without making it unnavigable. Stack\n * navigators preserve the value but do not present primary navigation.\n */\n showInPrimaryNavigation?: boolean;\n guards?: string[];\n screenId?: string;\n navigator?: NavigatorSpec;\n}\n\nexport type SplashScreenResizeMode = 'contain' | 'cover' | 'native';\n\nexport interface SplashScreenAssetSpec {\n readonly image?: string;\n readonly imageWidth?: number;\n readonly resizeMode?: SplashScreenResizeMode;\n}\n\nexport interface SplashScreenModeSpec extends SplashScreenAssetSpec {\n readonly backgroundColor?: string;\n}\n\nexport interface SplashScreenSpec extends SplashScreenModeSpec {\n readonly dark?: SplashScreenModeSpec;\n}\n\nexport interface DeploymentSpec {\n target: DeploymentTarget;\n monitoring: boolean;\n}\n\nexport interface DatabaseSpec {\n provider: DatabaseProvider;\n tier: DatabaseTier;\n}\n\nexport interface StorageSpec {\n provider: StorageProvider;\n buckets: string[];\n}\n\nexport interface StateSpec {\n readonly provider: StateProvider;\n readonly persistence?: StatePersistenceMode;\n}\n\nexport interface AuthzSpec {\n kind: AuthzKind;\n engine: AuthzEngine;\n}\n\nexport interface AuthSignInSpec {\n identifiers: AuthSignInIdentifier[];\n}\n\nexport interface AuthSignUpSpec {\n requiredFields: AuthSignUpField[];\n optionalFields?: AuthSignUpField[];\n signUpPolicy?: AuthSignUpPolicy;\n}\n\nexport interface AuthProfileSpec {\n fields: AuthProfileField[];\n table?: string;\n primaryKey?: AuthProfilePrimaryKeyStrategy;\n createStrategy?: AuthProfileCreateStrategy;\n updateStrategy?: AuthProfileUpdateStrategy;\n}\n\nexport interface AuthSpec {\n scope: AuthScope;\n provider: AuthProvider;\n authorization?: AuthzSpec;\n flow?: AuthFlowConfig;\n signIn?: AuthSignInSpec;\n signUp?: AuthSignUpSpec;\n oauth?: AuthOAuthConfig;\n profile?: AuthProfileSpec;\n}\n\nexport interface NetworkingSpec {\n domain?: string;\n cdn: boolean;\n}\n\nexport interface InfraManifest {\n deployment?: DeploymentSpec;\n auth?: AuthSpec;\n database?: DatabaseSpec;\n storage?: StorageSpec;\n state?: StateSpec;\n networking?: NetworkingSpec;\n modules: string[];\n modulesConfig?: Record<string, unknown>;\n}\n\nexport interface AppSettings {\n apiBaseUrl?: string;\n localization: {\n defaultLocale: string;\n locales: string[];\n };\n}\n\nexport interface AppManifest {\n metadata: {\n name: string;\n slug: string;\n version: string;\n category: AppCategory;\n themeId: string;\n created?: string;\n updated?: string;\n };\n themes: ThemeConfig[];\n activeThemeId: string;\n activeThemeMode?: 'dark' | 'light';\n splashScreen?: SplashScreenSpec;\n /** Studio-managed authoring media. Runtime/user uploads are intentionally separate. */\n media?: MediaManifest;\n infra: InfraManifest;\n navigator: NavigatorSpec;\n screens: Record<string, ScreenSpec>;\n generatedApis?: GeneratedApiRegistry;\n dataSources?: DataSourceRegistry;\n dataBindings?: ComponentDataBindingRegistry;\n settings: AppSettings;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ankhorage/contracts",
3
- "version": "7.1.0",
3
+ "version": "7.3.0",
4
4
  "main": "./dist/index.js",
5
5
  "ankh": {
6
6
  "category": "contracts",
@@ -64,6 +64,10 @@
64
64
  "./ui": {
65
65
  "types": "./dist/ui.d.ts",
66
66
  "default": "./dist/ui.js"
67
+ },
68
+ "./media": {
69
+ "types": "./dist/media.d.ts",
70
+ "default": "./dist/media.js"
67
71
  }
68
72
  },
69
73
  "description": "Serializable app, action, theme, auth, and secret-store contracts for Ankhorage.",
@@ -76,7 +80,9 @@
76
80
  "secrets",
77
81
  "secret-store",
78
82
  "storage",
79
- "adapter"
83
+ "adapter",
84
+ "media",
85
+ "assets"
80
86
  ],
81
87
  "files": [
82
88
  "dist",
@@ -0,0 +1,135 @@
1
+ import { isManifestValue, isOptionalString, isRecord } from './shared';
2
+
3
+ export function isComponentDataBindingRegistry(value: unknown): boolean {
4
+ return isRecord(value) && Object.values(value).every(isComponentDataBinding);
5
+ }
6
+
7
+ export function isBindingValueSource(value: unknown): boolean {
8
+ return (
9
+ isRecord(value) &&
10
+ typeof value.kind === 'string' &&
11
+ ['context', 'event', 'literal', 'operation', 'state'].includes(value.kind) &&
12
+ (value.kind === 'literal'
13
+ ? isManifestValue(value.value)
14
+ : value.kind === 'operation'
15
+ ? isBindingOperationRef(value.operation) && isOptionalString(value.path)
16
+ : typeof value.path === 'string')
17
+ );
18
+ }
19
+
20
+ export function isScreenDataLoaderDefinition(value: unknown): boolean {
21
+ return (
22
+ isRecord(value) &&
23
+ value.kind === 'operation' &&
24
+ isOptionalString(value.id) &&
25
+ isBindingOperationRef(value.operation) &&
26
+ (value.input === undefined || isBindingInputMap(value.input))
27
+ );
28
+ }
29
+
30
+ function isComponentDataBinding(value: unknown): boolean {
31
+ return (
32
+ isRecord(value) &&
33
+ typeof value.componentId === 'string' &&
34
+ isOptionalString(value.componentType) &&
35
+ (value.props === undefined ||
36
+ (isRecord(value.props) && Object.values(value.props).every(isPropBinding))) &&
37
+ (value.events === undefined ||
38
+ (isRecord(value.events) &&
39
+ Object.values(value.events).every(
40
+ (bindings) => Array.isArray(bindings) && bindings.every(isEventBinding),
41
+ )))
42
+ );
43
+ }
44
+
45
+ function isPropBinding(value: unknown): boolean {
46
+ return (
47
+ isRecord(value) &&
48
+ isBindingValueSource(value.source) &&
49
+ (value.fallback === undefined || isBindingFallback(value.fallback)) &&
50
+ (value.loading === undefined || isBindingLifecycleBehavior(value.loading)) &&
51
+ (value.error === undefined || isBindingLifecycleBehavior(value.error)) &&
52
+ (value.empty === undefined || isBindingLifecycleBehavior(value.empty)) &&
53
+ isOptionalBindingTransforms(value.transforms)
54
+ );
55
+ }
56
+
57
+ function isEventBinding(value: unknown): boolean {
58
+ return (
59
+ isRecord(value) &&
60
+ isEventBindingTarget(value.target) &&
61
+ (value.input === undefined || isBindingInputMap(value.input)) &&
62
+ (value.when === undefined || isBindingCondition(value.when))
63
+ );
64
+ }
65
+
66
+ function isEventBindingTarget(value: unknown): boolean {
67
+ return (
68
+ isRecord(value) &&
69
+ ((value.kind === 'action' && typeof value.type === 'string') ||
70
+ (value.kind === 'operation' && isBindingOperationRef(value.operation)))
71
+ );
72
+ }
73
+
74
+ function isBindingCondition(value: unknown): boolean {
75
+ return (
76
+ isRecord(value) &&
77
+ isBindingValueSource(value.source) &&
78
+ ['eq', 'exists', 'neq', 'notExists'].includes(String(value.operator)) &&
79
+ (value.value === undefined || isManifestValue(value.value))
80
+ );
81
+ }
82
+
83
+ function isBindingOperationRef(value: unknown): boolean {
84
+ return (
85
+ isRecord(value) &&
86
+ typeof value.dataSourceId === 'string' &&
87
+ typeof value.operationId === 'string' &&
88
+ isOptionalString(value.endpointId)
89
+ );
90
+ }
91
+
92
+ function isBindingFallback(value: unknown): boolean {
93
+ return (
94
+ isRecord(value) &&
95
+ (value.value === undefined || isManifestValue(value.value)) &&
96
+ (value.source === undefined || isBindingValueSource(value.source))
97
+ );
98
+ }
99
+
100
+ function isBindingLifecycleBehavior(value: unknown): boolean {
101
+ return (
102
+ isRecord(value) &&
103
+ typeof value.state === 'string' &&
104
+ ['empty', 'error', 'loading'].includes(value.state) &&
105
+ (value.fallback === undefined || isBindingFallback(value.fallback)) &&
106
+ isOptionalString(value.message)
107
+ );
108
+ }
109
+
110
+ function isOptionalBindingTransforms(value: unknown): boolean {
111
+ return (
112
+ value === undefined ||
113
+ (Array.isArray(value) &&
114
+ value.every((transform) => ['lowercase', 'trim', 'uppercase'].includes(String(transform))))
115
+ );
116
+ }
117
+
118
+ function isBindingInputMap(value: unknown): boolean {
119
+ return isRecord(value) && Object.values(value).every(isBindingInputValue);
120
+ }
121
+
122
+ function isBindingInputValue(value: unknown): boolean {
123
+ return (
124
+ isRecord(value) &&
125
+ typeof value.kind === 'string' &&
126
+ ((value.kind === 'array' &&
127
+ Array.isArray(value.items) &&
128
+ value.items.every(isBindingInputValue)) ||
129
+ (value.kind === 'literal' && isManifestValue(value.value)) ||
130
+ (value.kind === 'object' && isBindingInputMap(value.fields)) ||
131
+ (value.kind === 'source' &&
132
+ isBindingValueSource(value.source) &&
133
+ isOptionalBindingTransforms(value.transforms)))
134
+ );
135
+ }
@@ -0,0 +1,175 @@
1
+ import { isManifestValue, isOptionalBoolean, isOptionalString, isRecord } from './shared';
2
+
3
+ export function isDataSourceRegistry(value: unknown): boolean {
4
+ return isRecord(value) && Object.values(value).every(isDataSourceConfig);
5
+ }
6
+
7
+ function isDataSourceConfig(value: unknown): boolean {
8
+ if (!hasBaseDataSourceShape(value)) return false;
9
+ if (value.kind === 'database') return isDatabaseDataSource(value);
10
+ if (value.origin === 'generated') return isGeneratedDataSource(value);
11
+ if (value.origin !== 'external') return false;
12
+ return isExternalDataSource(value);
13
+ }
14
+
15
+ function hasBaseDataSourceShape(value: unknown): value is Record<string, unknown> {
16
+ return (
17
+ isRecord(value) &&
18
+ typeof value.id === 'string' &&
19
+ (value.kind === 'api' || value.kind === 'database') &&
20
+ isOptionalString(value.name) &&
21
+ isOptionalString(value.description) &&
22
+ (value.credential === undefined || isCredentialRef(value.credential)) &&
23
+ isDataEndpointRegistry(value.endpoints) &&
24
+ (value.schemas === undefined || isRecord(value.schemas)) &&
25
+ (value.metadata === undefined || isManifestValue(value.metadata))
26
+ );
27
+ }
28
+
29
+ function isDatabaseDataSource(value: Record<string, unknown>): boolean {
30
+ return (
31
+ isRecord(value.adapter) && value.adapter.kind === 'database' && isAdapterRef(value.adapter)
32
+ );
33
+ }
34
+
35
+ function isGeneratedDataSource(value: Record<string, unknown>): boolean {
36
+ return (
37
+ value.protocol === 'rest' &&
38
+ typeof value.generatedApiId === 'string' &&
39
+ isRecord(value.adapter) &&
40
+ value.adapter.kind === 'database' &&
41
+ isAdapterRef(value.adapter)
42
+ );
43
+ }
44
+
45
+ function isExternalDataSource(value: Record<string, unknown>): boolean {
46
+ if (value.protocol === 'rest') {
47
+ return (
48
+ typeof value.baseUrl === 'string' &&
49
+ (value.openApi === undefined || isOpenApiDocumentRef(value.openApi))
50
+ );
51
+ }
52
+
53
+ if (value.protocol === 'graphql') {
54
+ return (
55
+ typeof value.endpointUrl === 'string' &&
56
+ (value.introspection === undefined || isGraphQlIntrospection(value.introspection))
57
+ );
58
+ }
59
+
60
+ return false;
61
+ }
62
+
63
+ function isOpenApiDocumentRef(value: unknown): boolean {
64
+ return (
65
+ isRecord(value) &&
66
+ isOptionalString(value.url) &&
67
+ isOptionalString(value.documentId) &&
68
+ isOptionalString(value.version)
69
+ );
70
+ }
71
+
72
+ function isGraphQlIntrospection(value: unknown): boolean {
73
+ return (
74
+ isRecord(value) && typeof value.enabled === 'boolean' && isOptionalString(value.schemaVersion)
75
+ );
76
+ }
77
+
78
+ function isDataEndpointRegistry(value: unknown): boolean {
79
+ return isRecord(value) && Object.values(value).every(isDataEndpointConfig);
80
+ }
81
+
82
+ function isDataEndpointConfig(value: unknown): boolean {
83
+ return (
84
+ isRecord(value) &&
85
+ typeof value.id === 'string' &&
86
+ typeof value.kind === 'string' &&
87
+ isOptionalString(value.name) &&
88
+ isOptionalString(value.description) &&
89
+ isOptionalString(value.baseUrl) &&
90
+ isOptionalString(value.path) &&
91
+ (value.credential === undefined || isCredentialRef(value.credential)) &&
92
+ isRecord(value.operations) &&
93
+ Object.values(value.operations).every(isDataOperationConfig) &&
94
+ (value.metadata === undefined || isManifestValue(value.metadata))
95
+ );
96
+ }
97
+
98
+ function isDataOperationConfig(value: unknown): boolean {
99
+ return (
100
+ isRecord(value) &&
101
+ typeof value.id === 'string' &&
102
+ isOptionalString(value.endpointId) &&
103
+ isOptionalString(value.name) &&
104
+ isOptionalString(value.description) &&
105
+ typeof value.protocol === 'string' &&
106
+ typeof value.intent === 'string' &&
107
+ ['action', 'create', 'delete', 'read', 'update'].includes(value.intent) &&
108
+ isOptionalString(value.method) &&
109
+ isOptionalString(value.path) &&
110
+ (value.request === undefined || isDataOperationRequest(value.request)) &&
111
+ (value.response === undefined || isDataOperationResponse(value.response)) &&
112
+ (value.pagination === undefined || isRecord(value.pagination)) &&
113
+ (value.credential === undefined || isCredentialRef(value.credential)) &&
114
+ (value.metadata === undefined || isManifestValue(value.metadata))
115
+ );
116
+ }
117
+
118
+ function isDataOperationRequest(value: unknown): boolean {
119
+ return (
120
+ isRecord(value) &&
121
+ isDataSchemaSlot(value) &&
122
+ (value.parameters === undefined ||
123
+ (Array.isArray(value.parameters) && value.parameters.every(isDataOperationParameter)))
124
+ );
125
+ }
126
+
127
+ function isDataOperationParameter(value: unknown): boolean {
128
+ return (
129
+ isRecord(value) &&
130
+ isDataSchemaSlot(value) &&
131
+ typeof value.name === 'string' &&
132
+ typeof value.location === 'string' &&
133
+ ['body', 'cookie', 'header', 'path', 'query'].includes(value.location) &&
134
+ isOptionalBoolean(value.required) &&
135
+ isOptionalString(value.description) &&
136
+ (value.default === undefined || isManifestValue(value.default))
137
+ );
138
+ }
139
+
140
+ function isDataOperationResponse(value: unknown): boolean {
141
+ return (
142
+ isRecord(value) &&
143
+ isDataSchemaSlot(value) &&
144
+ (value.status === undefined ||
145
+ typeof value.status === 'string' ||
146
+ typeof value.status === 'number') &&
147
+ isOptionalString(value.contentType) &&
148
+ isOptionalString(value.description)
149
+ );
150
+ }
151
+
152
+ function isDataSchemaSlot(value: unknown): boolean {
153
+ return isRecord(value) && (value.schema === undefined || isRecord(value.schema));
154
+ }
155
+
156
+ function isCredentialRef(value: unknown): boolean {
157
+ return (
158
+ isRecord(value) &&
159
+ typeof value.id === 'string' &&
160
+ typeof value.kind === 'string' &&
161
+ isOptionalString(value.label) &&
162
+ isOptionalString(value.scope)
163
+ );
164
+ }
165
+
166
+ function isAdapterRef(value: unknown): boolean {
167
+ return (
168
+ isRecord(value) &&
169
+ typeof value.id === 'string' &&
170
+ typeof value.kind === 'string' &&
171
+ isOptionalString(value.packageName) &&
172
+ isOptionalString(value.exportName) &&
173
+ (value.config === undefined || isManifestValue(value.config))
174
+ );
175
+ }
@@ -0,0 +1,122 @@
1
+ import {
2
+ isManifestValue,
3
+ isOptionalBoolean,
4
+ isOptionalString,
5
+ isRecord,
6
+ isStringArray,
7
+ } from './shared';
8
+
9
+ export function isGeneratedApiRegistry(value: unknown): boolean {
10
+ return isRecord(value) && Object.values(value).every(isGeneratedApiDefinition);
11
+ }
12
+
13
+ function isGeneratedApiDefinition(value: unknown): boolean {
14
+ return (
15
+ isRecord(value) &&
16
+ typeof value.id === 'string' &&
17
+ value.protocol === 'rest' &&
18
+ isOptionalString(value.name) &&
19
+ isOptionalString(value.description) &&
20
+ typeof value.basePath === 'string' &&
21
+ isDatabaseAdapterRef(value.database) &&
22
+ Array.isArray(value.resources) &&
23
+ value.resources.every(isGeneratedApiResourceDefinition) &&
24
+ (value.auth === undefined || isGeneratedApiAuthRequirement(value.auth)) &&
25
+ (value.metadata === undefined || isManifestValue(value.metadata))
26
+ );
27
+ }
28
+
29
+ function isDatabaseAdapterRef(value: unknown): boolean {
30
+ return isRecord(value) && value.kind === 'database' && isAdapterRef(value);
31
+ }
32
+
33
+ function isGeneratedApiAuthRequirement(value: unknown): boolean {
34
+ return (
35
+ isRecord(value) &&
36
+ isOptionalBoolean(value.required) &&
37
+ (value.roles === undefined || isStringArray(value.roles)) &&
38
+ (value.permissions === undefined || isStringArray(value.permissions)) &&
39
+ isOptionalString(value.policy) &&
40
+ (value.metadata === undefined || isManifestValue(value.metadata))
41
+ );
42
+ }
43
+
44
+ function isGeneratedApiResourceDefinition(value: unknown): boolean {
45
+ return (
46
+ isRecord(value) &&
47
+ typeof value.id === 'string' &&
48
+ isOptionalString(value.name) &&
49
+ isOptionalString(value.description) &&
50
+ typeof value.path === 'string' &&
51
+ isDbCollectionDefinition(value.collection) &&
52
+ Array.isArray(value.operations) &&
53
+ value.operations.every(isGeneratedApiCrudOperation) &&
54
+ (value.seed === undefined || isSeedRecords(value.seed)) &&
55
+ (value.policies === undefined ||
56
+ (Array.isArray(value.policies) && value.policies.every(isGeneratedApiPolicyRef))) &&
57
+ (value.metadata === undefined || isManifestValue(value.metadata))
58
+ );
59
+ }
60
+
61
+ function isSeedRecords(value: unknown): boolean {
62
+ return (
63
+ Array.isArray(value) &&
64
+ value.every((record) => isRecord(record) && Object.values(record).every(isManifestValue))
65
+ );
66
+ }
67
+
68
+ function isGeneratedApiCrudOperation(value: unknown): boolean {
69
+ return ['create', 'delete', 'list', 'read', 'update'].includes(String(value));
70
+ }
71
+
72
+ function isGeneratedApiPolicyRef(value: unknown): boolean {
73
+ return (
74
+ isRecord(value) &&
75
+ typeof value.id === 'string' &&
76
+ (value.operation === undefined || isGeneratedApiCrudOperation(value.operation))
77
+ );
78
+ }
79
+
80
+ function isDbCollectionDefinition(value: unknown): boolean {
81
+ return (
82
+ isRecord(value) &&
83
+ typeof value.name === 'string' &&
84
+ isOptionalString(value.schema) &&
85
+ Array.isArray(value.fields) &&
86
+ value.fields.every(isDbFieldDefinition) &&
87
+ isOptionalString(value.primaryKey)
88
+ );
89
+ }
90
+
91
+ function isDbFieldDefinition(value: unknown): boolean {
92
+ return (
93
+ isRecord(value) &&
94
+ typeof value.name === 'string' &&
95
+ typeof value.type === 'string' &&
96
+ ['text', 'number', 'boolean', 'datetime', 'json', 'uuid'].includes(value.type) &&
97
+ isOptionalBoolean(value.required) &&
98
+ isOptionalBoolean(value.unique) &&
99
+ isDbDefaultValue(value.defaultValue)
100
+ );
101
+ }
102
+
103
+ function isDbDefaultValue(value: unknown): boolean {
104
+ return (
105
+ value === undefined ||
106
+ value === null ||
107
+ typeof value === 'boolean' ||
108
+ typeof value === 'number' ||
109
+ typeof value === 'string'
110
+ );
111
+ }
112
+
113
+ function isAdapterRef(value: unknown): boolean {
114
+ return (
115
+ isRecord(value) &&
116
+ typeof value.id === 'string' &&
117
+ typeof value.kind === 'string' &&
118
+ isOptionalString(value.packageName) &&
119
+ isOptionalString(value.exportName) &&
120
+ (value.config === undefined || isManifestValue(value.config))
121
+ );
122
+ }