@fayz-ai/core 0.1.6

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 (172) hide show
  1. package/dist/app/render.d.ts +16 -0
  2. package/dist/app/render.d.ts.map +1 -0
  3. package/dist/blocks/index.d.ts +25 -0
  4. package/dist/blocks/index.d.ts.map +1 -0
  5. package/dist/chunk-2NTHNWEH.cjs +32 -0
  6. package/dist/chunk-2NTHNWEH.cjs.map +1 -0
  7. package/dist/chunk-5EAZOMVP.js +1609 -0
  8. package/dist/chunk-5EAZOMVP.js.map +1 -0
  9. package/dist/chunk-5TKIZVVJ.cjs +16 -0
  10. package/dist/chunk-5TKIZVVJ.cjs.map +1 -0
  11. package/dist/chunk-7RTUHSKY.js +3 -0
  12. package/dist/chunk-7RTUHSKY.js.map +1 -0
  13. package/dist/chunk-AXTWKF5X.cjs +38 -0
  14. package/dist/chunk-AXTWKF5X.cjs.map +1 -0
  15. package/dist/chunk-EGWWQ7AT.cjs +355 -0
  16. package/dist/chunk-EGWWQ7AT.cjs.map +1 -0
  17. package/dist/chunk-KAYQFSR6.js +298 -0
  18. package/dist/chunk-KAYQFSR6.js.map +1 -0
  19. package/dist/chunk-LSVBDYFQ.cjs +1639 -0
  20. package/dist/chunk-LSVBDYFQ.cjs.map +1 -0
  21. package/dist/chunk-SNQW6TRF.cjs +658 -0
  22. package/dist/chunk-SNQW6TRF.cjs.map +1 -0
  23. package/dist/chunk-WKZVSKEU.js +26 -0
  24. package/dist/chunk-WKZVSKEU.js.map +1 -0
  25. package/dist/chunk-Y2C2FBQP.js +35 -0
  26. package/dist/chunk-Y2C2FBQP.js.map +1 -0
  27. package/dist/chunk-YSBXGHAM.js +643 -0
  28. package/dist/chunk-YSBXGHAM.js.map +1 -0
  29. package/dist/data/archetype.d.ts +13 -0
  30. package/dist/data/archetype.d.ts.map +1 -0
  31. package/dist/data/cached.d.ts +19 -0
  32. package/dist/data/cached.d.ts.map +1 -0
  33. package/dist/data/fayz-api.d.ts +19 -0
  34. package/dist/data/fayz-api.d.ts.map +1 -0
  35. package/dist/data/index.cjs +32 -0
  36. package/dist/data/index.cjs.map +1 -0
  37. package/dist/data/index.d.ts +9 -0
  38. package/dist/data/index.d.ts.map +1 -0
  39. package/dist/data/index.js +3 -0
  40. package/dist/data/index.js.map +1 -0
  41. package/dist/data/mock.d.ts +7 -0
  42. package/dist/data/mock.d.ts.map +1 -0
  43. package/dist/data/resolve.d.ts +22 -0
  44. package/dist/data/resolve.d.ts.map +1 -0
  45. package/dist/data/supabase.d.ts +18 -0
  46. package/dist/data/supabase.d.ts.map +1 -0
  47. package/dist/data/types.d.ts +23 -0
  48. package/dist/data/types.d.ts.map +1 -0
  49. package/dist/entity/index.cjs +28 -0
  50. package/dist/entity/index.cjs.map +1 -0
  51. package/dist/entity/index.d.ts +3 -0
  52. package/dist/entity/index.d.ts.map +1 -0
  53. package/dist/entity/index.js +3 -0
  54. package/dist/entity/index.js.map +1 -0
  55. package/dist/entity/registry.d.ts +20 -0
  56. package/dist/entity/registry.d.ts.map +1 -0
  57. package/dist/events/index.d.ts +16 -0
  58. package/dist/events/index.d.ts.map +1 -0
  59. package/dist/i18n/index.cjs +44 -0
  60. package/dist/i18n/index.cjs.map +1 -0
  61. package/dist/i18n/index.d.ts +21 -0
  62. package/dist/i18n/index.d.ts.map +1 -0
  63. package/dist/i18n/index.js +3 -0
  64. package/dist/i18n/index.js.map +1 -0
  65. package/dist/i18n/shell-translations.d.ts +2 -0
  66. package/dist/i18n/shell-translations.d.ts.map +1 -0
  67. package/dist/index.cjs +911 -0
  68. package/dist/index.cjs.map +1 -0
  69. package/dist/index.d.ts +46 -0
  70. package/dist/index.d.ts.map +1 -0
  71. package/dist/index.js +596 -0
  72. package/dist/index.js.map +1 -0
  73. package/dist/lib/cache.d.ts +21 -0
  74. package/dist/lib/cache.d.ts.map +1 -0
  75. package/dist/lib/csv.d.ts +14 -0
  76. package/dist/lib/csv.d.ts.map +1 -0
  77. package/dist/lib/format.d.ts +7 -0
  78. package/dist/lib/format.d.ts.map +1 -0
  79. package/dist/manifest/index.d.ts +80 -0
  80. package/dist/manifest/index.d.ts.map +1 -0
  81. package/dist/plugin/createSafeDataProvider.d.ts +14 -0
  82. package/dist/plugin/createSafeDataProvider.d.ts.map +1 -0
  83. package/dist/plugin/index.cjs +32 -0
  84. package/dist/plugin/index.cjs.map +1 -0
  85. package/dist/plugin/index.d.ts +2 -0
  86. package/dist/plugin/index.d.ts.map +1 -0
  87. package/dist/plugin/index.js +3 -0
  88. package/dist/plugin/index.js.map +1 -0
  89. package/dist/plugin/runtime.d.ts +31 -0
  90. package/dist/plugin/runtime.d.ts.map +1 -0
  91. package/dist/registry/index.d.ts +88 -0
  92. package/dist/registry/index.d.ts.map +1 -0
  93. package/dist/router/index.cjs +16 -0
  94. package/dist/router/index.cjs.map +1 -0
  95. package/dist/router/index.d.ts +8 -0
  96. package/dist/router/index.d.ts.map +1 -0
  97. package/dist/router/index.js +3 -0
  98. package/dist/router/index.js.map +1 -0
  99. package/dist/runtime/index.cjs +16 -0
  100. package/dist/runtime/index.cjs.map +1 -0
  101. package/dist/runtime/index.d.ts +3 -0
  102. package/dist/runtime/index.d.ts.map +1 -0
  103. package/dist/runtime/index.js +3 -0
  104. package/dist/runtime/index.js.map +1 -0
  105. package/dist/runtime/oauth.d.ts +96 -0
  106. package/dist/runtime/oauth.d.ts.map +1 -0
  107. package/dist/tenant/index.d.ts +3 -0
  108. package/dist/tenant/index.d.ts.map +1 -0
  109. package/dist/types/auth.d.ts +27 -0
  110. package/dist/types/auth.d.ts.map +1 -0
  111. package/dist/types/billing.d.ts +17 -0
  112. package/dist/types/billing.d.ts.map +1 -0
  113. package/dist/types/crud.d.ts +107 -0
  114. package/dist/types/crud.d.ts.map +1 -0
  115. package/dist/types/entities.d.ts +134 -0
  116. package/dist/types/entities.d.ts.map +1 -0
  117. package/dist/types/i18n.d.ts +8 -0
  118. package/dist/types/i18n.d.ts.map +1 -0
  119. package/dist/types/index.cjs +4 -0
  120. package/dist/types/index.cjs.map +1 -0
  121. package/dist/types/index.d.ts +10 -0
  122. package/dist/types/index.d.ts.map +1 -0
  123. package/dist/types/index.js +3 -0
  124. package/dist/types/index.js.map +1 -0
  125. package/dist/types/org.d.ts +96 -0
  126. package/dist/types/org.d.ts.map +1 -0
  127. package/dist/types/permissions.d.ts +25 -0
  128. package/dist/types/permissions.d.ts.map +1 -0
  129. package/dist/types/plugins.d.ts +314 -0
  130. package/dist/types/plugins.d.ts.map +1 -0
  131. package/dist/types/theme.d.ts +16 -0
  132. package/dist/types/theme.d.ts.map +1 -0
  133. package/package.json +98 -0
  134. package/src/app/render.tsx +69 -0
  135. package/src/blocks/index.tsx +68 -0
  136. package/src/data/archetype.ts +210 -0
  137. package/src/data/cached.ts +55 -0
  138. package/src/data/fayz-api.ts +200 -0
  139. package/src/data/index.ts +8 -0
  140. package/src/data/mock.ts +84 -0
  141. package/src/data/resolve.ts +108 -0
  142. package/src/data/supabase.ts +169 -0
  143. package/src/data/types.ts +20 -0
  144. package/src/entity/index.ts +2 -0
  145. package/src/entity/registry.ts +42 -0
  146. package/src/events/index.tsx +82 -0
  147. package/src/i18n/index.ts +250 -0
  148. package/src/i18n/shell-translations.ts +1391 -0
  149. package/src/index.ts +132 -0
  150. package/src/lib/cache.ts +118 -0
  151. package/src/lib/csv.ts +54 -0
  152. package/src/lib/format.ts +58 -0
  153. package/src/manifest/app-manifest.schema.json +103 -0
  154. package/src/manifest/index.ts +394 -0
  155. package/src/plugin/createSafeDataProvider.ts +30 -0
  156. package/src/plugin/index.ts +1 -0
  157. package/src/plugin/runtime.ts +313 -0
  158. package/src/registry/index.ts +163 -0
  159. package/src/router/index.ts +29 -0
  160. package/src/runtime/index.ts +15 -0
  161. package/src/runtime/oauth.ts +219 -0
  162. package/src/tenant/index.ts +18 -0
  163. package/src/types/auth.ts +26 -0
  164. package/src/types/billing.ts +17 -0
  165. package/src/types/crud.ts +108 -0
  166. package/src/types/entities.ts +142 -0
  167. package/src/types/i18n.ts +7 -0
  168. package/src/types/index.ts +10 -0
  169. package/src/types/org.ts +94 -0
  170. package/src/types/permissions.ts +32 -0
  171. package/src/types/plugins.ts +363 -0
  172. package/src/types/theme.ts +17 -0
package/src/index.ts ADDED
@@ -0,0 +1,132 @@
1
+ // Types
2
+ export type { EntityArchetype, BaseEntity, PersonEntity, ProductEntity, ServiceEntity, OrderEntity, TransactionEntity, LocationEntity, ScheduleEntity, CategoryEntity } from './types/entities'
3
+ export type { EntityDef, FieldDef, FieldType, FieldGroup, DetailTab, FormLayout, ComputedFieldValue } from './types/crud'
4
+ export type { PluginManifest, PluginScope, PluginStatus, ResolvedPluginManifest, PluginRuntime, PluginRuntimeContext, PluginWidgetZone, PluginWidgetDefinition, PluginNavigationEntry, PluginSettingsTab, PluginRouteDefinition, PluginAITool, PluginRegistryDef, PluginMigration, VerticalId, ScaffoldType, TenantPluginBinding, DashboardWidgetDef, DashboardWidgetKind, DashboardSurface, DashboardLayoutConfig, ResolvedDashboardWidget } from './types/plugins'
5
+ export { WidgetZone } from './types/plugins'
6
+ export type { AuthAdapter, AuthUser, AuthSession, AuthProvider } from './types/auth'
7
+ export type { OrgAdapter, Organization, OrgMember, OrgMembership, Location, Invite, CreateOrgOptions } from './types/org'
8
+ export type { PermissionsConfig, FeatureDeclaration, PermissionAction, PermissionProfile } from './types/permissions'
9
+ export type { Plan, BillingConfig } from './types/billing'
10
+ export type { SaasTheme, ThemeConfig, ThemeBrand, ThemeRadius, ThemeMode } from './types/theme'
11
+ export type { LocaleConfig } from './types/i18n'
12
+
13
+ // Data providers
14
+ export { createSupabaseProvider, setGlobalSupabaseClient, getSupabaseClientOptional } from './data/supabase'
15
+ export { createFayzApiProvider } from './data/fayz-api'
16
+ export { createMockProvider } from './data/mock'
17
+ export { createArchetypeProvider } from './data/archetype'
18
+ export { withCache } from './data/cached'
19
+ export { resolveDataProvider } from './data/resolve'
20
+ export { createSafeDataProvider } from './plugin/createSafeDataProvider'
21
+ export type { DataProvider, CrudQuery, CrudResult, SupabaseProviderConfig, FayzApiProviderConfig } from './data/index'
22
+
23
+ // Tenant context (runtime DI for the data layer)
24
+ export { setActiveTenantId, getActiveTenantId } from './tenant'
25
+
26
+ // Runtime broker helpers
27
+ export { createFayzRuntimeClient, FayzRuntimeError } from './runtime'
28
+ export type {
29
+ FayzRuntimeClientOptions,
30
+ FayzRuntimeEnvironment,
31
+ GoogleCalendarEvent,
32
+ GoogleCalendarEventInput,
33
+ GoogleCalendarEventTime,
34
+ ListGoogleCalendarEventsInput,
35
+ PluginOAuthExchangeInput,
36
+ PluginOAuthExchangeResponse,
37
+ RuntimePluginOAuthGrant,
38
+ } from './runtime'
39
+
40
+ // Shared utilities
41
+ export { formatCurrency, formatDate, formatDateTime, getActiveLocale, getDefaultCurrency, setDefaultCurrency } from './lib/format'
42
+ export { exportCSV, buildCSV, downloadCSV } from './lib/csv'
43
+ export type { CSVColumn } from './lib/csv'
44
+ export { globalCache, createCacheStore, stableKey, clearGlobalCache } from './lib/cache'
45
+ export type { CacheStore } from './lib/cache'
46
+
47
+ // Plugin system
48
+ export { definePlugin, resolvePluginRuntime, getWidgetsForZone, getDashboardWidgets, PluginRuntimeProvider, usePluginRuntime, usePluginRuntimeOptional, PLUGIN_API_VERSION, resolvePluginComponent } from './plugin/runtime'
49
+ export type { PluginEventDefinition, PluginQuickAction } from './types/plugins'
50
+
51
+ // Event bus
52
+ export { createEventBus, eventBus, useOnEvent } from './events'
53
+ export type { EventBus, EventHandler } from './events'
54
+
55
+ // Entity registry
56
+ export { registerEntity, getEntityByKey, getAllEntities, clearEntityRegistry, deriveEntityKey } from './entity/registry'
57
+ export type { RegisteredEntity } from './entity/registry'
58
+
59
+ // Uniform registry (component / block / page / metric / scaffold / plugin)
60
+ export {
61
+ Registry,
62
+ isCustomId,
63
+ componentRegistry,
64
+ blockRegistry,
65
+ pageRegistry,
66
+ scaffoldRegistry,
67
+ metricRegistry,
68
+ pluginRegistry,
69
+ registerComponent,
70
+ getComponent,
71
+ registerBlock,
72
+ getBlock,
73
+ listBlocks,
74
+ registerPage,
75
+ getPage,
76
+ registerScaffold,
77
+ getScaffold,
78
+ registerMetric,
79
+ getMetric,
80
+ listMetrics,
81
+ registerPlugin,
82
+ getPlugin,
83
+ listPlugins,
84
+ pluginFactoryRegistry,
85
+ registerPluginFactory,
86
+ getPluginFactory,
87
+ listPluginFactories,
88
+ } from './registry'
89
+ export type { PluginFactory } from './registry'
90
+ export type {
91
+ JsonSchema,
92
+ RegistrySource,
93
+ RegistryMeta,
94
+ RegistryEntry,
95
+ MetricFormat,
96
+ MetricContext,
97
+ MetricValue,
98
+ MetricDefinition,
99
+ } from './registry'
100
+
101
+ // Block system (universal page primitive)
102
+ export { BlockRenderer, renderBlocks, BlockChildren } from './blocks'
103
+ export type { BlockNode, BlockComponentProps } from './blocks'
104
+
105
+ // App manifest (the app, as data) + migration runner
106
+ export {
107
+ CURRENT_MANIFEST_VERSION,
108
+ registerManifestMigration,
109
+ migrateManifest,
110
+ validateManifest,
111
+ } from './manifest'
112
+ export type {
113
+ AppManifest,
114
+ BackendProvider,
115
+ BackendRef,
116
+ SurfaceManifest,
117
+ PageManifest,
118
+ PluginRef,
119
+ ManifestMigration,
120
+ } from './manifest'
121
+ export { default as appManifestSchema } from './manifest/app-manifest.schema.json'
122
+
123
+ // App entry points (manifest → running app)
124
+ export { defineApp, renderApp, useManifest } from './app/render'
125
+ export type { RenderAppOptions } from './app/render'
126
+
127
+ // i18n
128
+ export { I18nProvider, useI18nConfig, useTranslation, coreTranslations, mergeTranslations, registerTranslations, getCurrentLocale, setCurrentLocale, setLocaleStore } from './i18n/index'
129
+
130
+ // Router
131
+ export { hashRouterAdapter, windowRouterAdapter } from './router/index'
132
+ export type { RouterAdapter } from './router/index'
@@ -0,0 +1,118 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Types
3
+ // ---------------------------------------------------------------------------
4
+
5
+ interface CacheEntry<T = unknown> {
6
+ data: T
7
+ expiresAt: number
8
+ lastAccess: number
9
+ }
10
+
11
+ export interface CacheStore {
12
+ get<T>(key: string): T | undefined
13
+ set<T>(key: string, data: T, ttl?: number): void
14
+ /** Clear all entries whose key starts with `prefix` */
15
+ invalidate(prefix: string): void
16
+ clear(): void
17
+ size(): number
18
+ }
19
+
20
+ export interface CacheConfig {
21
+ /** Default time-to-live in ms (default: 60 000 — 1 minute) */
22
+ defaultTTL?: number
23
+ /** Maximum cached entries before LRU eviction (default: 500) */
24
+ maxEntries?: number
25
+ }
26
+
27
+ // ---------------------------------------------------------------------------
28
+ // Stable key helper — deterministic JSON for cache keys
29
+ // ---------------------------------------------------------------------------
30
+
31
+ function sortDeep(value: unknown): unknown {
32
+ if (value === null || value === undefined || typeof value !== 'object') return value
33
+ if (Array.isArray(value)) return value.map(sortDeep)
34
+ const sorted: Record<string, unknown> = {}
35
+ for (const k of Object.keys(value as Record<string, unknown>).sort()) {
36
+ sorted[k] = sortDeep((value as Record<string, unknown>)[k])
37
+ }
38
+ return sorted
39
+ }
40
+
41
+ export function stableKey(...parts: unknown[]): string {
42
+ return parts.map((p) => (typeof p === 'string' ? p : JSON.stringify(sortDeep(p)))).join(':')
43
+ }
44
+
45
+ // ---------------------------------------------------------------------------
46
+ // CacheStore implementation
47
+ // ---------------------------------------------------------------------------
48
+
49
+ export function createCacheStore(config?: CacheConfig): CacheStore {
50
+ const defaultTTL = config?.defaultTTL ?? 60_000
51
+ const maxEntries = config?.maxEntries ?? 500
52
+ const entries = new Map<string, CacheEntry>()
53
+
54
+ function evictLRU() {
55
+ if (entries.size <= maxEntries) return
56
+ // Find the entry with the oldest lastAccess
57
+ let oldestKey: string | null = null
58
+ let oldestAccess = Infinity
59
+ for (const [key, entry] of entries) {
60
+ if (entry.lastAccess < oldestAccess) {
61
+ oldestAccess = entry.lastAccess
62
+ oldestKey = key
63
+ }
64
+ }
65
+ if (oldestKey) entries.delete(oldestKey)
66
+ }
67
+
68
+ return {
69
+ get<T>(key: string): T | undefined {
70
+ const entry = entries.get(key)
71
+ if (!entry) return undefined
72
+ if (Date.now() > entry.expiresAt) {
73
+ entries.delete(key)
74
+ return undefined
75
+ }
76
+ entry.lastAccess = Date.now()
77
+ return entry.data as T
78
+ },
79
+
80
+ set<T>(key: string, data: T, ttl?: number): void {
81
+ const now = Date.now()
82
+ entries.set(key, {
83
+ data,
84
+ expiresAt: now + (ttl ?? defaultTTL),
85
+ lastAccess: now,
86
+ })
87
+ evictLRU()
88
+ },
89
+
90
+ invalidate(prefix: string): void {
91
+ for (const key of entries.keys()) {
92
+ if (key.startsWith(prefix)) {
93
+ entries.delete(key)
94
+ }
95
+ }
96
+ },
97
+
98
+ clear(): void {
99
+ entries.clear()
100
+ },
101
+
102
+ size(): number {
103
+ return entries.size
104
+ },
105
+ }
106
+ }
107
+
108
+ // ---------------------------------------------------------------------------
109
+ // Global singleton — auto-flushes on tenant/org switch
110
+ // ---------------------------------------------------------------------------
111
+
112
+ export const globalCache = createCacheStore()
113
+
114
+ /** Flush the global cache — called by the tenant accessor when the active
115
+ * tenant changes (replaces the old org-store subscription). */
116
+ export function clearGlobalCache(): void {
117
+ globalCache.clear()
118
+ }
package/src/lib/csv.ts ADDED
@@ -0,0 +1,54 @@
1
+ /** Column descriptor for generic CSV export */
2
+ export interface CSVColumn {
3
+ key: string
4
+ label: string
5
+ format?: (value: any, row: Record<string, any>) => string
6
+ }
7
+
8
+ export function escapeCSVField(value: unknown): string {
9
+ if (value == null) return ''
10
+ const str = String(value)
11
+ if (str.includes(',') || str.includes('"') || str.includes('\n')) {
12
+ return `"${str.replace(/"/g, '""')}"`
13
+ }
14
+ return str
15
+ }
16
+
17
+ /** Build a CSV string from columns + rows. Each cell runs through `column.format` if provided, then escaping. */
18
+ export function buildCSV(
19
+ columns: CSVColumn[],
20
+ rows: Record<string, any>[],
21
+ ): string {
22
+ const header = columns.map((c) => escapeCSVField(c.label)).join(',')
23
+ const body = rows.map((row) =>
24
+ columns
25
+ .map((col) => {
26
+ const raw = row[col.key]
27
+ const formatted = col.format ? col.format(raw, row) : raw
28
+ return escapeCSVField(formatted)
29
+ })
30
+ .join(','),
31
+ )
32
+ return [header, ...body].join('\n')
33
+ }
34
+
35
+ /** Trigger a CSV file download in the browser. */
36
+ export function downloadCSV(csv: string, filename: string): void {
37
+ const bom = '\uFEFF'
38
+ const blob = new Blob([bom + csv], { type: 'text/csv;charset=utf-8;' })
39
+ const url = URL.createObjectURL(blob)
40
+ const a = document.createElement('a')
41
+ a.href = url
42
+ a.download = filename.endsWith('.csv') ? filename : `${filename}.csv`
43
+ a.click()
44
+ URL.revokeObjectURL(url)
45
+ }
46
+
47
+ /** Convenience: build + download in one call. */
48
+ export function exportCSV(
49
+ columns: CSVColumn[],
50
+ rows: Record<string, any>[],
51
+ filename: string,
52
+ ): void {
53
+ downloadCSV(buildCSV(columns, rows), filename)
54
+ }
@@ -0,0 +1,58 @@
1
+ import { getCurrentLocale } from '../i18n'
2
+
3
+ // Currency inferred from the active locale unless the app sets one explicitly
4
+ // via createSaasApp({ locale: { currency: 'BRL' } }).
5
+ const CURRENCY_BY_LOCALE: Record<string, string> = {
6
+ 'pt-BR': 'BRL',
7
+ 'pt-PT': 'EUR',
8
+ en: 'USD',
9
+ 'en-US': 'USD',
10
+ 'en-GB': 'GBP',
11
+ es: 'EUR',
12
+ }
13
+
14
+ let _defaultCurrency: string | null = null
15
+
16
+ export function setDefaultCurrency(code: string | undefined): void {
17
+ _defaultCurrency = code ?? null
18
+ }
19
+
20
+ export function getActiveLocale(): string {
21
+ try {
22
+ return getCurrentLocale() || 'en'
23
+ } catch {
24
+ return 'en'
25
+ }
26
+ }
27
+
28
+ export function getDefaultCurrency(): string {
29
+ if (_defaultCurrency) return _defaultCurrency
30
+ return CURRENCY_BY_LOCALE[getActiveLocale()] ?? 'USD'
31
+ }
32
+
33
+ export function formatCurrency(value: unknown, currency?: string): string {
34
+ const num = typeof value === 'number' ? value : parseFloat(String(value))
35
+ if (isNaN(num)) return String(value ?? '')
36
+ return new Intl.NumberFormat(getActiveLocale(), {
37
+ style: 'currency',
38
+ currency: currency ?? getDefaultCurrency(),
39
+ }).format(num)
40
+ }
41
+
42
+ export function formatDate(value: unknown): string {
43
+ if (!value) return ''
44
+ try {
45
+ return new Intl.DateTimeFormat(getActiveLocale(), { dateStyle: 'medium' }).format(new Date(value as string))
46
+ } catch {
47
+ return String(value)
48
+ }
49
+ }
50
+
51
+ export function formatDateTime(value: unknown): string {
52
+ if (!value) return ''
53
+ try {
54
+ return new Intl.DateTimeFormat(getActiveLocale(), { dateStyle: 'medium', timeStyle: 'short' }).format(new Date(value as string))
55
+ } catch {
56
+ return String(value)
57
+ }
58
+ }
@@ -0,0 +1,103 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://fayz.ai/schemas/app-manifest/v2.json",
4
+ "title": "Fayz AppManifest",
5
+ "description": "The canonical, JSON-serializable definition of a Fayz app. Code is referenced by registry id; never inlined.",
6
+ "type": "object",
7
+ "required": ["manifestVersion", "id", "name", "surfaces"],
8
+ "additionalProperties": false,
9
+ "properties": {
10
+ "manifestVersion": {
11
+ "type": "integer",
12
+ "const": 2,
13
+ "description": "Current AppManifest schema version. Older or newer versions require an explicit SDK migration before validation."
14
+ },
15
+ "id": { "type": "string", "minLength": 1, "pattern": "^[a-z0-9][a-z0-9-]*$" },
16
+ "name": { "type": "string", "minLength": 1 },
17
+ "backend": {
18
+ "type": "object",
19
+ "additionalProperties": false,
20
+ "required": ["provider"],
21
+ "properties": {
22
+ "provider": { "enum": ["supabase", "fayz-api", "fayz-shop", "mock", "custom"] },
23
+ "projectRef": { "type": "string", "minLength": 1 },
24
+ "url": { "type": "string", "minLength": 1 },
25
+ "adapterId": { "type": "string", "minLength": 1 },
26
+ "options": { "type": "object" }
27
+ }
28
+ },
29
+ "locale": { "type": "object" },
30
+ "theme": { "type": "object" },
31
+ "permissions": { "type": "object" },
32
+ "billing": { "type": "object" },
33
+ "entities": { "type": "array", "items": { "type": "object" } },
34
+ "surfaces": {
35
+ "type": "object",
36
+ "minProperties": 1,
37
+ "additionalProperties": { "$ref": "#/$defs/surface" }
38
+ }
39
+ },
40
+ "$defs": {
41
+ "surface": {
42
+ "type": "object",
43
+ "required": ["scaffold"],
44
+ "additionalProperties": false,
45
+ "properties": {
46
+ "scaffold": { "type": "string", "minLength": 1 },
47
+ "options": { "type": "object" },
48
+ "plugins": { "type": "array", "items": { "$ref": "#/$defs/pluginRef" } },
49
+ "pages": { "type": "array", "items": { "$ref": "#/$defs/page" } }
50
+ }
51
+ },
52
+ "pluginRef": {
53
+ "type": "object",
54
+ "required": ["id"],
55
+ "additionalProperties": false,
56
+ "properties": {
57
+ "id": { "type": "string", "minLength": 1 },
58
+ "config": { "type": "object" },
59
+ "enabled": { "type": "boolean" }
60
+ }
61
+ },
62
+ "page": {
63
+ "type": "object",
64
+ "required": ["path"],
65
+ "additionalProperties": false,
66
+ "description": "Exactly one of blocks/entity/component must be set.",
67
+ "properties": {
68
+ "path": { "type": "string", "minLength": 1 },
69
+ "label": { "type": "string" },
70
+ "icon": { "type": "string" },
71
+ "section": { "enum": ["main", "secondary", "settings"] },
72
+ "blocks": { "type": "array", "items": { "$ref": "#/$defs/block" } },
73
+ "entity": { "type": "string", "minLength": 1 },
74
+ "component": { "type": "string", "minLength": 1 },
75
+ "permission": {
76
+ "type": "object",
77
+ "required": ["feature", "action"],
78
+ "additionalProperties": false,
79
+ "properties": {
80
+ "feature": { "type": "string", "minLength": 1 },
81
+ "action": { "type": "string", "minLength": 1 }
82
+ }
83
+ }
84
+ },
85
+ "oneOf": [
86
+ { "required": ["blocks"] },
87
+ { "required": ["entity"] },
88
+ { "required": ["component"] }
89
+ ]
90
+ },
91
+ "block": {
92
+ "type": "object",
93
+ "required": ["type"],
94
+ "additionalProperties": false,
95
+ "properties": {
96
+ "type": { "type": "string", "minLength": 1 },
97
+ "id": { "type": "string" },
98
+ "props": { "type": "object" },
99
+ "children": { "type": "array", "items": { "$ref": "#/$defs/block" } }
100
+ }
101
+ }
102
+ }
103
+ }