@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.
- package/dist/app/render.d.ts +16 -0
- package/dist/app/render.d.ts.map +1 -0
- package/dist/blocks/index.d.ts +25 -0
- package/dist/blocks/index.d.ts.map +1 -0
- package/dist/chunk-2NTHNWEH.cjs +32 -0
- package/dist/chunk-2NTHNWEH.cjs.map +1 -0
- package/dist/chunk-5EAZOMVP.js +1609 -0
- package/dist/chunk-5EAZOMVP.js.map +1 -0
- package/dist/chunk-5TKIZVVJ.cjs +16 -0
- package/dist/chunk-5TKIZVVJ.cjs.map +1 -0
- package/dist/chunk-7RTUHSKY.js +3 -0
- package/dist/chunk-7RTUHSKY.js.map +1 -0
- package/dist/chunk-AXTWKF5X.cjs +38 -0
- package/dist/chunk-AXTWKF5X.cjs.map +1 -0
- package/dist/chunk-EGWWQ7AT.cjs +355 -0
- package/dist/chunk-EGWWQ7AT.cjs.map +1 -0
- package/dist/chunk-KAYQFSR6.js +298 -0
- package/dist/chunk-KAYQFSR6.js.map +1 -0
- package/dist/chunk-LSVBDYFQ.cjs +1639 -0
- package/dist/chunk-LSVBDYFQ.cjs.map +1 -0
- package/dist/chunk-SNQW6TRF.cjs +658 -0
- package/dist/chunk-SNQW6TRF.cjs.map +1 -0
- package/dist/chunk-WKZVSKEU.js +26 -0
- package/dist/chunk-WKZVSKEU.js.map +1 -0
- package/dist/chunk-Y2C2FBQP.js +35 -0
- package/dist/chunk-Y2C2FBQP.js.map +1 -0
- package/dist/chunk-YSBXGHAM.js +643 -0
- package/dist/chunk-YSBXGHAM.js.map +1 -0
- package/dist/data/archetype.d.ts +13 -0
- package/dist/data/archetype.d.ts.map +1 -0
- package/dist/data/cached.d.ts +19 -0
- package/dist/data/cached.d.ts.map +1 -0
- package/dist/data/fayz-api.d.ts +19 -0
- package/dist/data/fayz-api.d.ts.map +1 -0
- package/dist/data/index.cjs +32 -0
- package/dist/data/index.cjs.map +1 -0
- package/dist/data/index.d.ts +9 -0
- package/dist/data/index.d.ts.map +1 -0
- package/dist/data/index.js +3 -0
- package/dist/data/index.js.map +1 -0
- package/dist/data/mock.d.ts +7 -0
- package/dist/data/mock.d.ts.map +1 -0
- package/dist/data/resolve.d.ts +22 -0
- package/dist/data/resolve.d.ts.map +1 -0
- package/dist/data/supabase.d.ts +18 -0
- package/dist/data/supabase.d.ts.map +1 -0
- package/dist/data/types.d.ts +23 -0
- package/dist/data/types.d.ts.map +1 -0
- package/dist/entity/index.cjs +28 -0
- package/dist/entity/index.cjs.map +1 -0
- package/dist/entity/index.d.ts +3 -0
- package/dist/entity/index.d.ts.map +1 -0
- package/dist/entity/index.js +3 -0
- package/dist/entity/index.js.map +1 -0
- package/dist/entity/registry.d.ts +20 -0
- package/dist/entity/registry.d.ts.map +1 -0
- package/dist/events/index.d.ts +16 -0
- package/dist/events/index.d.ts.map +1 -0
- package/dist/i18n/index.cjs +44 -0
- package/dist/i18n/index.cjs.map +1 -0
- package/dist/i18n/index.d.ts +21 -0
- package/dist/i18n/index.d.ts.map +1 -0
- package/dist/i18n/index.js +3 -0
- package/dist/i18n/index.js.map +1 -0
- package/dist/i18n/shell-translations.d.ts +2 -0
- package/dist/i18n/shell-translations.d.ts.map +1 -0
- package/dist/index.cjs +911 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +46 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +596 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/cache.d.ts +21 -0
- package/dist/lib/cache.d.ts.map +1 -0
- package/dist/lib/csv.d.ts +14 -0
- package/dist/lib/csv.d.ts.map +1 -0
- package/dist/lib/format.d.ts +7 -0
- package/dist/lib/format.d.ts.map +1 -0
- package/dist/manifest/index.d.ts +80 -0
- package/dist/manifest/index.d.ts.map +1 -0
- package/dist/plugin/createSafeDataProvider.d.ts +14 -0
- package/dist/plugin/createSafeDataProvider.d.ts.map +1 -0
- package/dist/plugin/index.cjs +32 -0
- package/dist/plugin/index.cjs.map +1 -0
- package/dist/plugin/index.d.ts +2 -0
- package/dist/plugin/index.d.ts.map +1 -0
- package/dist/plugin/index.js +3 -0
- package/dist/plugin/index.js.map +1 -0
- package/dist/plugin/runtime.d.ts +31 -0
- package/dist/plugin/runtime.d.ts.map +1 -0
- package/dist/registry/index.d.ts +88 -0
- package/dist/registry/index.d.ts.map +1 -0
- package/dist/router/index.cjs +16 -0
- package/dist/router/index.cjs.map +1 -0
- package/dist/router/index.d.ts +8 -0
- package/dist/router/index.d.ts.map +1 -0
- package/dist/router/index.js +3 -0
- package/dist/router/index.js.map +1 -0
- package/dist/runtime/index.cjs +16 -0
- package/dist/runtime/index.cjs.map +1 -0
- package/dist/runtime/index.d.ts +3 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +3 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/oauth.d.ts +96 -0
- package/dist/runtime/oauth.d.ts.map +1 -0
- package/dist/tenant/index.d.ts +3 -0
- package/dist/tenant/index.d.ts.map +1 -0
- package/dist/types/auth.d.ts +27 -0
- package/dist/types/auth.d.ts.map +1 -0
- package/dist/types/billing.d.ts +17 -0
- package/dist/types/billing.d.ts.map +1 -0
- package/dist/types/crud.d.ts +107 -0
- package/dist/types/crud.d.ts.map +1 -0
- package/dist/types/entities.d.ts +134 -0
- package/dist/types/entities.d.ts.map +1 -0
- package/dist/types/i18n.d.ts +8 -0
- package/dist/types/i18n.d.ts.map +1 -0
- package/dist/types/index.cjs +4 -0
- package/dist/types/index.cjs.map +1 -0
- package/dist/types/index.d.ts +10 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/org.d.ts +96 -0
- package/dist/types/org.d.ts.map +1 -0
- package/dist/types/permissions.d.ts +25 -0
- package/dist/types/permissions.d.ts.map +1 -0
- package/dist/types/plugins.d.ts +314 -0
- package/dist/types/plugins.d.ts.map +1 -0
- package/dist/types/theme.d.ts +16 -0
- package/dist/types/theme.d.ts.map +1 -0
- package/package.json +98 -0
- package/src/app/render.tsx +69 -0
- package/src/blocks/index.tsx +68 -0
- package/src/data/archetype.ts +210 -0
- package/src/data/cached.ts +55 -0
- package/src/data/fayz-api.ts +200 -0
- package/src/data/index.ts +8 -0
- package/src/data/mock.ts +84 -0
- package/src/data/resolve.ts +108 -0
- package/src/data/supabase.ts +169 -0
- package/src/data/types.ts +20 -0
- package/src/entity/index.ts +2 -0
- package/src/entity/registry.ts +42 -0
- package/src/events/index.tsx +82 -0
- package/src/i18n/index.ts +250 -0
- package/src/i18n/shell-translations.ts +1391 -0
- package/src/index.ts +132 -0
- package/src/lib/cache.ts +118 -0
- package/src/lib/csv.ts +54 -0
- package/src/lib/format.ts +58 -0
- package/src/manifest/app-manifest.schema.json +103 -0
- package/src/manifest/index.ts +394 -0
- package/src/plugin/createSafeDataProvider.ts +30 -0
- package/src/plugin/index.ts +1 -0
- package/src/plugin/runtime.ts +313 -0
- package/src/registry/index.ts +163 -0
- package/src/router/index.ts +29 -0
- package/src/runtime/index.ts +15 -0
- package/src/runtime/oauth.ts +219 -0
- package/src/tenant/index.ts +18 -0
- package/src/types/auth.ts +26 -0
- package/src/types/billing.ts +17 -0
- package/src/types/crud.ts +108 -0
- package/src/types/entities.ts +142 -0
- package/src/types/i18n.ts +7 -0
- package/src/types/index.ts +10 -0
- package/src/types/org.ts +94 -0
- package/src/types/permissions.ts +32 -0
- package/src/types/plugins.ts +363 -0
- 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'
|
package/src/lib/cache.ts
ADDED
|
@@ -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
|
+
}
|