@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
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
export type FayzRuntimeEnvironment = 'preview' | 'production'
|
|
2
|
+
|
|
3
|
+
export interface FayzRuntimeClientOptions {
|
|
4
|
+
baseUrl: string
|
|
5
|
+
projectId: string
|
|
6
|
+
runtimeToken: string | (() => string | Promise<string>)
|
|
7
|
+
fetcher?: typeof fetch
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface PluginOAuthExchangeInput {
|
|
11
|
+
pluginId: string
|
|
12
|
+
environment?: FayzRuntimeEnvironment
|
|
13
|
+
scopes?: string[]
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface RuntimePluginOAuthGrant {
|
|
17
|
+
grantId: string
|
|
18
|
+
provider: string
|
|
19
|
+
scopes: string[]
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface PluginOAuthExchangeResponse {
|
|
23
|
+
token: string
|
|
24
|
+
tokenType: 'Bearer'
|
|
25
|
+
expiresIn: number
|
|
26
|
+
projectId: string
|
|
27
|
+
tenantKey: string
|
|
28
|
+
environment: FayzRuntimeEnvironment
|
|
29
|
+
pluginId: string
|
|
30
|
+
grants: RuntimePluginOAuthGrant[]
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface GoogleCalendarEventTime {
|
|
34
|
+
dateTime: string
|
|
35
|
+
timeZone?: string
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface GoogleCalendarEventInput {
|
|
39
|
+
summary: string
|
|
40
|
+
description?: string
|
|
41
|
+
location?: string
|
|
42
|
+
start: GoogleCalendarEventTime
|
|
43
|
+
end: GoogleCalendarEventTime
|
|
44
|
+
attendees?: Array<{ email: string }>
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface GoogleCalendarEvent {
|
|
48
|
+
id: string
|
|
49
|
+
summary?: string
|
|
50
|
+
description?: string
|
|
51
|
+
location?: string
|
|
52
|
+
start: { dateTime?: string; date?: string; timeZone?: string }
|
|
53
|
+
end: { dateTime?: string; date?: string; timeZone?: string }
|
|
54
|
+
status?: string
|
|
55
|
+
htmlLink?: string
|
|
56
|
+
[key: string]: unknown
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface ListGoogleCalendarEventsInput {
|
|
60
|
+
calendarId: string
|
|
61
|
+
timeMin?: string
|
|
62
|
+
timeMax?: string
|
|
63
|
+
maxResults?: number
|
|
64
|
+
q?: string
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export class FayzRuntimeError extends Error {
|
|
68
|
+
constructor(
|
|
69
|
+
message: string,
|
|
70
|
+
readonly status: number,
|
|
71
|
+
readonly responseBody?: unknown,
|
|
72
|
+
) {
|
|
73
|
+
super(message)
|
|
74
|
+
this.name = 'FayzRuntimeError'
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function normalizeBaseUrl(baseUrl: string): string {
|
|
79
|
+
return baseUrl.replace(/\/+$/, '')
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function assertRequired(value: string, field: string): string {
|
|
83
|
+
const normalized = value.trim()
|
|
84
|
+
if (!normalized) throw new Error(`[@fayz-ai/core] ${field} is required`)
|
|
85
|
+
return normalized
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function resolveRuntimeToken(
|
|
89
|
+
runtimeToken: FayzRuntimeClientOptions['runtimeToken'],
|
|
90
|
+
): Promise<string> {
|
|
91
|
+
const value = typeof runtimeToken === 'function' ? await runtimeToken() : runtimeToken
|
|
92
|
+
return assertRequired(value, 'runtimeToken')
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function runtimePath(projectId: string, suffix: string): string {
|
|
96
|
+
return `/api/v1/runtime/projects/${encodeURIComponent(projectId)}${suffix}`
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function queryString(params: Record<string, string | number | undefined>): string {
|
|
100
|
+
const search = new URLSearchParams()
|
|
101
|
+
for (const [key, value] of Object.entries(params)) {
|
|
102
|
+
if (value !== undefined && value !== '') search.set(key, String(value))
|
|
103
|
+
}
|
|
104
|
+
const value = search.toString()
|
|
105
|
+
return value ? `?${value}` : ''
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async function parseResponse<T>(response: Response): Promise<T> {
|
|
109
|
+
if (response.status === 204) return undefined as T
|
|
110
|
+
|
|
111
|
+
const text = await response.text()
|
|
112
|
+
let body: unknown
|
|
113
|
+
try {
|
|
114
|
+
body = text ? JSON.parse(text) : undefined
|
|
115
|
+
} catch {
|
|
116
|
+
body = text
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (!response.ok) {
|
|
120
|
+
const message = typeof body === 'object' && body && 'message' in body
|
|
121
|
+
? String((body as { message: unknown }).message)
|
|
122
|
+
: `[@fayz-ai/core] Runtime request failed with status ${response.status}`
|
|
123
|
+
throw new FayzRuntimeError(message, response.status, body)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return body as T
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function createFayzRuntimeClient(options: FayzRuntimeClientOptions) {
|
|
130
|
+
const baseUrl = normalizeBaseUrl(assertRequired(options.baseUrl, 'baseUrl'))
|
|
131
|
+
const projectId = assertRequired(options.projectId, 'projectId')
|
|
132
|
+
const fetcher = options.fetcher ?? fetch
|
|
133
|
+
|
|
134
|
+
async function request<T>(
|
|
135
|
+
path: string,
|
|
136
|
+
token: string,
|
|
137
|
+
init: Omit<RequestInit, 'headers'> & { headers?: Record<string, string> } = {},
|
|
138
|
+
): Promise<T> {
|
|
139
|
+
const response = await fetcher(`${baseUrl}${path}`, {
|
|
140
|
+
...init,
|
|
141
|
+
headers: {
|
|
142
|
+
...(init.body ? { 'Content-Type': 'application/json' } : {}),
|
|
143
|
+
...init.headers,
|
|
144
|
+
Authorization: `Bearer ${token}`,
|
|
145
|
+
},
|
|
146
|
+
})
|
|
147
|
+
return parseResponse<T>(response)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
async function exchangePluginOAuth(
|
|
151
|
+
input: PluginOAuthExchangeInput,
|
|
152
|
+
): Promise<PluginOAuthExchangeResponse> {
|
|
153
|
+
return request<PluginOAuthExchangeResponse>(
|
|
154
|
+
runtimePath(projectId, '/oauth/exchange'),
|
|
155
|
+
await resolveRuntimeToken(options.runtimeToken),
|
|
156
|
+
{
|
|
157
|
+
method: 'POST',
|
|
158
|
+
body: JSON.stringify({
|
|
159
|
+
pluginId: assertRequired(input.pluginId, 'pluginId'),
|
|
160
|
+
environment: input.environment ?? 'preview',
|
|
161
|
+
...(input.scopes ? { scopes: input.scopes } : {}),
|
|
162
|
+
}),
|
|
163
|
+
},
|
|
164
|
+
)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function googleCalendar(pluginOAuthToken: string) {
|
|
168
|
+
const brokerToken = assertRequired(pluginOAuthToken, 'pluginOAuthToken')
|
|
169
|
+
const eventsPath = runtimePath(projectId, '/oauth/google-calendar/events')
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
listEvents(input: ListGoogleCalendarEventsInput) {
|
|
173
|
+
const calendarId = assertRequired(input.calendarId, 'calendarId')
|
|
174
|
+
return request<{
|
|
175
|
+
provider: 'google-calendar'
|
|
176
|
+
calendarId: string
|
|
177
|
+
timeMin: string
|
|
178
|
+
timeMax: string
|
|
179
|
+
events: GoogleCalendarEvent[]
|
|
180
|
+
}>(
|
|
181
|
+
`${eventsPath}${queryString({
|
|
182
|
+
calendarId,
|
|
183
|
+
timeMin: input.timeMin,
|
|
184
|
+
timeMax: input.timeMax,
|
|
185
|
+
maxResults: input.maxResults,
|
|
186
|
+
q: input.q,
|
|
187
|
+
})}`,
|
|
188
|
+
brokerToken,
|
|
189
|
+
)
|
|
190
|
+
},
|
|
191
|
+
createEvent(calendarId: string, event: GoogleCalendarEventInput) {
|
|
192
|
+
return request<{ provider: 'google-calendar'; calendarId: string; event: GoogleCalendarEvent }>(
|
|
193
|
+
`${eventsPath}${queryString({ calendarId: assertRequired(calendarId, 'calendarId') })}`,
|
|
194
|
+
brokerToken,
|
|
195
|
+
{ method: 'POST', body: JSON.stringify(event) },
|
|
196
|
+
)
|
|
197
|
+
},
|
|
198
|
+
updateEvent(calendarId: string, eventId: string, updates: Partial<GoogleCalendarEventInput>) {
|
|
199
|
+
return request<{ provider: 'google-calendar'; calendarId: string; event: GoogleCalendarEvent }>(
|
|
200
|
+
`${eventsPath}/${encodeURIComponent(assertRequired(eventId, 'eventId'))}${queryString({ calendarId: assertRequired(calendarId, 'calendarId') })}`,
|
|
201
|
+
brokerToken,
|
|
202
|
+
{ method: 'PATCH', body: JSON.stringify(updates) },
|
|
203
|
+
)
|
|
204
|
+
},
|
|
205
|
+
deleteEvent(calendarId: string, eventId: string) {
|
|
206
|
+
return request<void>(
|
|
207
|
+
`${eventsPath}/${encodeURIComponent(assertRequired(eventId, 'eventId'))}${queryString({ calendarId: assertRequired(calendarId, 'calendarId') })}`,
|
|
208
|
+
brokerToken,
|
|
209
|
+
{ method: 'DELETE' },
|
|
210
|
+
)
|
|
211
|
+
},
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return {
|
|
216
|
+
exchangePluginOAuth,
|
|
217
|
+
googleCalendar,
|
|
218
|
+
}
|
|
219
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Active tenant context — a global accessor the data layer reads for tenant
|
|
2
|
+
// scoping and cache keying. The host (saas shell / app) sets it from its org
|
|
3
|
+
// store via setActiveTenantId; the data providers stay decoupled from any
|
|
4
|
+
// specific org-store implementation (runtime DI).
|
|
5
|
+
import { clearGlobalCache } from '../lib/cache'
|
|
6
|
+
|
|
7
|
+
let _activeTenantId: string | undefined
|
|
8
|
+
|
|
9
|
+
export function setActiveTenantId(id: string | undefined): void {
|
|
10
|
+
if (id !== _activeTenantId) {
|
|
11
|
+
_activeTenantId = id
|
|
12
|
+
clearGlobalCache()
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function getActiveTenantId(): string | undefined {
|
|
17
|
+
return _activeTenantId
|
|
18
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export type AuthProvider = 'google' | 'github' | 'apple' | 'facebook' | (string & {})
|
|
2
|
+
|
|
3
|
+
export interface AuthUser {
|
|
4
|
+
id: string
|
|
5
|
+
email: string
|
|
6
|
+
fullName?: string
|
|
7
|
+
avatarUrl?: string
|
|
8
|
+
role?: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface AuthSession {
|
|
12
|
+
user: AuthUser
|
|
13
|
+
accessToken: string
|
|
14
|
+
refreshToken?: string
|
|
15
|
+
expiresAt?: number
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface AuthAdapter {
|
|
19
|
+
getSession(): Promise<{ user: AuthUser; session: AuthSession } | null>
|
|
20
|
+
onAuthStateChange(cb: (user: AuthUser | null) => void): () => void
|
|
21
|
+
signIn(email: string, password: string): Promise<AuthSession>
|
|
22
|
+
signUp(email: string, password: string, fullName: string): Promise<AuthSession>
|
|
23
|
+
signOut(): Promise<void>
|
|
24
|
+
signInWithOAuth(provider: AuthProvider): Promise<void>
|
|
25
|
+
resetPassword(email: string): Promise<void>
|
|
26
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface Plan {
|
|
2
|
+
id: string
|
|
3
|
+
name: string
|
|
4
|
+
description?: string
|
|
5
|
+
price: number
|
|
6
|
+
currency: string
|
|
7
|
+
interval: 'month' | 'year'
|
|
8
|
+
features: string[]
|
|
9
|
+
highlighted?: boolean
|
|
10
|
+
stripePriceId?: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface BillingConfig {
|
|
14
|
+
plans: Plan[]
|
|
15
|
+
stripePublishableKey?: string
|
|
16
|
+
portalUrl?: string
|
|
17
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import type React from 'react'
|
|
2
|
+
import type { EntityArchetype } from './entities'
|
|
3
|
+
|
|
4
|
+
export type FieldType =
|
|
5
|
+
| 'text' | 'email' | 'phone' | 'url' | 'image'
|
|
6
|
+
| 'number' | 'currency'
|
|
7
|
+
| 'select' | 'multiselect' | 'segmented'
|
|
8
|
+
| 'date' | 'datetime' | 'time'
|
|
9
|
+
| 'boolean' | 'textarea'
|
|
10
|
+
| 'color' | 'computed'
|
|
11
|
+
|
|
12
|
+
/** Read-only display produced by a `computed` field's `compute()`. */
|
|
13
|
+
export interface ComputedFieldValue {
|
|
14
|
+
display: string
|
|
15
|
+
/** Drives the value colour (green / red / muted). Default: neutral. */
|
|
16
|
+
tone?: 'positive' | 'negative' | 'neutral'
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface FieldDef {
|
|
20
|
+
key: string
|
|
21
|
+
label: string
|
|
22
|
+
type: FieldType
|
|
23
|
+
required?: boolean
|
|
24
|
+
placeholder?: string
|
|
25
|
+
/** Small helper text shown under the field input. */
|
|
26
|
+
hint?: string
|
|
27
|
+
/** Options for `select` / `multiselect` / `segmented`. The optional
|
|
28
|
+
* `description` is shown under the label on `segmented` radio-cards. */
|
|
29
|
+
options?: string[] | { label: string; value: string; description?: string }[]
|
|
30
|
+
min?: number
|
|
31
|
+
max?: number
|
|
32
|
+
/** ISO currency code for `currency` fields (e.g. 'BRL'). */
|
|
33
|
+
currency?: string
|
|
34
|
+
/** Symbol/locale for the `currency` field's CurrencyInput. Default 'R$' / 'pt-BR'. */
|
|
35
|
+
currencySymbol?: string
|
|
36
|
+
currencyLocale?: string
|
|
37
|
+
showInTable?: boolean
|
|
38
|
+
showInForm?: boolean
|
|
39
|
+
showInDetail?: boolean
|
|
40
|
+
sortable?: boolean
|
|
41
|
+
searchable?: boolean
|
|
42
|
+
/** Custom cell renderer (code escape hatch). For a serializable EntityDef in
|
|
43
|
+
* an AppManifest, reference a registered cell renderer by id instead. */
|
|
44
|
+
renderCell?: (value: unknown, row: unknown) => React.ReactNode
|
|
45
|
+
renderCellId?: string
|
|
46
|
+
/** Derives a read-only value from the other form values (for `computed`
|
|
47
|
+
* fields, e.g. margin). Returns null to render an em-dash. */
|
|
48
|
+
compute?: (values: Record<string, unknown>) => ComputedFieldValue | null
|
|
49
|
+
defaultValue?: unknown
|
|
50
|
+
group?: string
|
|
51
|
+
span?: 1 | 2
|
|
52
|
+
inlineToggle?: boolean
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface FieldGroup {
|
|
56
|
+
id: string
|
|
57
|
+
label: string
|
|
58
|
+
description?: string
|
|
59
|
+
columns?: 1 | 2 | 3
|
|
60
|
+
/** Renders a decorative image slot to the left of the group's fields. */
|
|
61
|
+
imageSlot?: boolean
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface DetailTab {
|
|
65
|
+
id: string
|
|
66
|
+
label: string
|
|
67
|
+
icon?: string
|
|
68
|
+
/** Tab content component, or a registered component id (componentId) for a
|
|
69
|
+
* serializable EntityDef. Exactly one is expected. */
|
|
70
|
+
component?: React.ComponentType<{ item: unknown; entityDef: EntityDef; [key: string]: unknown }>
|
|
71
|
+
componentId?: string
|
|
72
|
+
visibleFor?: string[]
|
|
73
|
+
props?: Record<string, unknown>
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export type FormLayout = 'person' | 'product' | 'service' | 'location' | 'order' | 'subject' | 'generic'
|
|
77
|
+
|
|
78
|
+
export interface EntityDef<T = Record<string, unknown>> {
|
|
79
|
+
name: string
|
|
80
|
+
namePlural?: string
|
|
81
|
+
icon: string
|
|
82
|
+
layout?: FormLayout
|
|
83
|
+
fields: FieldDef[]
|
|
84
|
+
fieldGroups?: FieldGroup[]
|
|
85
|
+
detailTabs?: DetailTab[]
|
|
86
|
+
data?: {
|
|
87
|
+
table: string
|
|
88
|
+
schema?: string
|
|
89
|
+
tenantScoped?: boolean
|
|
90
|
+
tenantIdColumn?: string
|
|
91
|
+
searchColumns?: string[]
|
|
92
|
+
selectColumns?: string
|
|
93
|
+
columnMap?: Record<string, string>
|
|
94
|
+
archetype?: EntityArchetype
|
|
95
|
+
archetypeKind?: string
|
|
96
|
+
filters?: Record<string, string>
|
|
97
|
+
defaults?: Record<string, unknown>
|
|
98
|
+
cacheTTL?: number
|
|
99
|
+
}
|
|
100
|
+
defaultSort?: string
|
|
101
|
+
defaultSortDir?: 'asc' | 'desc'
|
|
102
|
+
displayField?: string
|
|
103
|
+
subtitleField?: string
|
|
104
|
+
imageField?: string
|
|
105
|
+
/** Faceted filters shown as pills below the list search box. Each references a
|
|
106
|
+
* field whose `options` supply the pills; an "All" pill clears the filter. */
|
|
107
|
+
facets?: { field: string; allLabel?: string }[]
|
|
108
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
export type EntityArchetype = 'person' | 'category' | 'product' | 'service' | 'order' | 'transaction' | 'schedule' | 'location'
|
|
2
|
+
|
|
3
|
+
export interface BaseEntity {
|
|
4
|
+
id: string
|
|
5
|
+
tenantId: string
|
|
6
|
+
createdAt: string
|
|
7
|
+
updatedAt: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface PersonEntity extends BaseEntity {
|
|
11
|
+
archetype: 'person'
|
|
12
|
+
kind: string
|
|
13
|
+
name: string
|
|
14
|
+
email?: string
|
|
15
|
+
phone?: string
|
|
16
|
+
documentNumber?: string
|
|
17
|
+
avatarUrl?: string
|
|
18
|
+
dateOfBirth?: string
|
|
19
|
+
address?: string
|
|
20
|
+
city?: string
|
|
21
|
+
state?: string
|
|
22
|
+
country: string
|
|
23
|
+
postalCode?: string
|
|
24
|
+
tags: string[]
|
|
25
|
+
isActive: boolean
|
|
26
|
+
notes?: string
|
|
27
|
+
metadata: Record<string, unknown>
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface CategoryEntity extends BaseEntity {
|
|
31
|
+
archetype: 'category'
|
|
32
|
+
kind: string
|
|
33
|
+
name: string
|
|
34
|
+
slug?: string
|
|
35
|
+
parentId?: string
|
|
36
|
+
icon?: string
|
|
37
|
+
color?: string
|
|
38
|
+
sortOrder: number
|
|
39
|
+
isActive: boolean
|
|
40
|
+
metadata: Record<string, unknown>
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface ProductEntity extends BaseEntity {
|
|
44
|
+
archetype: 'product'
|
|
45
|
+
categoryId?: string
|
|
46
|
+
name: string
|
|
47
|
+
description?: string
|
|
48
|
+
sku?: string
|
|
49
|
+
price?: number
|
|
50
|
+
cost?: number
|
|
51
|
+
currency: string
|
|
52
|
+
unit?: string
|
|
53
|
+
imageUrl?: string
|
|
54
|
+
stock?: number
|
|
55
|
+
minStock?: number
|
|
56
|
+
status: string
|
|
57
|
+
isActive: boolean
|
|
58
|
+
tags: string[]
|
|
59
|
+
metadata: Record<string, unknown>
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface ServiceEntity extends BaseEntity {
|
|
63
|
+
archetype: 'service'
|
|
64
|
+
categoryId?: string
|
|
65
|
+
name: string
|
|
66
|
+
description?: string
|
|
67
|
+
price?: number
|
|
68
|
+
cost?: number
|
|
69
|
+
currency: string
|
|
70
|
+
durationMinutes?: number
|
|
71
|
+
imageUrl?: string
|
|
72
|
+
status: string
|
|
73
|
+
isActive: boolean
|
|
74
|
+
tags: string[]
|
|
75
|
+
metadata: Record<string, unknown>
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface OrderEntity extends BaseEntity {
|
|
79
|
+
archetype: 'order'
|
|
80
|
+
kind: string
|
|
81
|
+
referenceNumber?: string
|
|
82
|
+
status: string
|
|
83
|
+
partyId?: string
|
|
84
|
+
assigneeId?: string
|
|
85
|
+
locationId?: string
|
|
86
|
+
subtotal: number
|
|
87
|
+
discount: number
|
|
88
|
+
tax: number
|
|
89
|
+
total: number
|
|
90
|
+
currency: string
|
|
91
|
+
dueAt?: string
|
|
92
|
+
completedAt?: string
|
|
93
|
+
notes?: string
|
|
94
|
+
tags: string[]
|
|
95
|
+
metadata: Record<string, unknown>
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface TransactionEntity extends BaseEntity {
|
|
99
|
+
archetype: 'transaction'
|
|
100
|
+
kind: string
|
|
101
|
+
orderId?: string
|
|
102
|
+
partyId?: string
|
|
103
|
+
amount: number
|
|
104
|
+
currency: string
|
|
105
|
+
paymentMethod?: string
|
|
106
|
+
reference?: string
|
|
107
|
+
status: string
|
|
108
|
+
transactedAt: string
|
|
109
|
+
notes?: string
|
|
110
|
+
metadata: Record<string, unknown>
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export interface LocationEntity extends BaseEntity {
|
|
114
|
+
archetype: 'location'
|
|
115
|
+
kind: string
|
|
116
|
+
name: string
|
|
117
|
+
email?: string
|
|
118
|
+
phone?: string
|
|
119
|
+
address?: string
|
|
120
|
+
city?: string
|
|
121
|
+
state?: string
|
|
122
|
+
country: string
|
|
123
|
+
postalCode?: string
|
|
124
|
+
isHeadquarters: boolean
|
|
125
|
+
isActive: boolean
|
|
126
|
+
tags: string[]
|
|
127
|
+
notes?: string
|
|
128
|
+
metadata: Record<string, unknown>
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export interface ScheduleEntity extends BaseEntity {
|
|
132
|
+
archetype: 'schedule'
|
|
133
|
+
kind: string
|
|
134
|
+
assigneeId?: string
|
|
135
|
+
locationId?: string
|
|
136
|
+
dayOfWeek?: number
|
|
137
|
+
specificDate?: string
|
|
138
|
+
startsAt: string
|
|
139
|
+
endsAt: string
|
|
140
|
+
isActive: boolean
|
|
141
|
+
metadata: Record<string, unknown>
|
|
142
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Re-export all core types
|
|
2
|
+
export type { EntityArchetype, BaseEntity, PersonEntity, ProductEntity, ServiceEntity, OrderEntity, TransactionEntity, LocationEntity, ScheduleEntity, CategoryEntity } from './entities'
|
|
3
|
+
export type { EntityDef, FieldDef, FieldType, FieldGroup, DetailTab, FormLayout, ComputedFieldValue } from './crud'
|
|
4
|
+
export type { PluginManifest, PluginScope, PluginStatus, ResolvedPluginManifest, PluginRuntime, PluginRuntimeContext, PluginWidgetZone, PluginWidgetDefinition, PluginNavigationEntry, PluginSettingsTab, PluginRouteDefinition, PluginAITool, PluginRegistryDef, PluginMigration, VerticalId, ScaffoldType } from './plugins'
|
|
5
|
+
export type { AuthAdapter, AuthUser, AuthSession } from './auth'
|
|
6
|
+
export type { OrgAdapter, Organization, OrgMember } from './org'
|
|
7
|
+
export type { PermissionsConfig, FeatureDeclaration, PermissionAction } from './permissions'
|
|
8
|
+
export type { Plan, BillingConfig } from './billing'
|
|
9
|
+
export type { SaasTheme, ThemeConfig } from './theme'
|
|
10
|
+
export type { LocaleConfig } from './i18n'
|
package/src/types/org.ts
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type { PermissionProfile } from './permissions'
|
|
2
|
+
|
|
3
|
+
export interface Organization {
|
|
4
|
+
id: string
|
|
5
|
+
name: string
|
|
6
|
+
slug: string
|
|
7
|
+
logoUrl?: string
|
|
8
|
+
verticalId?: string
|
|
9
|
+
plan?: string
|
|
10
|
+
settings?: Record<string, unknown>
|
|
11
|
+
createdAt: string
|
|
12
|
+
updatedAt: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface OrgMember {
|
|
16
|
+
id: string
|
|
17
|
+
userId: string
|
|
18
|
+
orgId: string
|
|
19
|
+
profileId: string
|
|
20
|
+
profileName: string
|
|
21
|
+
user: {
|
|
22
|
+
id: string
|
|
23
|
+
email: string
|
|
24
|
+
fullName: string
|
|
25
|
+
avatarUrl?: string
|
|
26
|
+
}
|
|
27
|
+
joinedAt: string
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface OrgMembership {
|
|
31
|
+
orgId: string
|
|
32
|
+
orgName: string
|
|
33
|
+
orgSlug: string
|
|
34
|
+
orgLogoUrl?: string
|
|
35
|
+
profileId: string
|
|
36
|
+
profileName: string
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface Location {
|
|
40
|
+
id: string
|
|
41
|
+
tenantId: string
|
|
42
|
+
name: string
|
|
43
|
+
address?: string
|
|
44
|
+
city?: string
|
|
45
|
+
state?: string
|
|
46
|
+
country?: string
|
|
47
|
+
phone?: string
|
|
48
|
+
isHeadquarters: boolean
|
|
49
|
+
isActive: boolean
|
|
50
|
+
createdAt: string
|
|
51
|
+
updatedAt: string
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface Invite {
|
|
55
|
+
id: string
|
|
56
|
+
orgId: string
|
|
57
|
+
email: string
|
|
58
|
+
profileId: string
|
|
59
|
+
profileName: string
|
|
60
|
+
invitedBy: string
|
|
61
|
+
token: string
|
|
62
|
+
status: 'pending' | 'accepted' | 'expired'
|
|
63
|
+
createdAt: string
|
|
64
|
+
expiresAt: string
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface CreateOrgOptions {
|
|
68
|
+
verticalId?: string
|
|
69
|
+
timezone?: string
|
|
70
|
+
currency?: string
|
|
71
|
+
locale?: string
|
|
72
|
+
teamSize?: string
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface OrgAdapter {
|
|
76
|
+
listUserOrgs(userId: string): Promise<OrgMembership[]>
|
|
77
|
+
getOrg(orgId: string): Promise<Organization>
|
|
78
|
+
createOrg(name: string, userId: string, options?: CreateOrgOptions): Promise<Organization>
|
|
79
|
+
updateOrg(orgId: string, data: Partial<Organization>): Promise<Organization>
|
|
80
|
+
listMembers(orgId: string): Promise<OrgMember[]>
|
|
81
|
+
updateMemberProfile(orgId: string, memberId: string, profileId: string): Promise<void>
|
|
82
|
+
removeMember(orgId: string, memberId: string): Promise<void>
|
|
83
|
+
listProfiles(orgId: string): Promise<PermissionProfile[]>
|
|
84
|
+
createProfile(orgId: string, profile: Omit<PermissionProfile, 'id' | 'isSystem'>): Promise<PermissionProfile>
|
|
85
|
+
updateProfile(orgId: string, profileId: string, data: Partial<PermissionProfile>): Promise<PermissionProfile>
|
|
86
|
+
deleteProfile(orgId: string, profileId: string): Promise<void>
|
|
87
|
+
listInvites(orgId: string): Promise<Invite[]>
|
|
88
|
+
createInvite(orgId: string, email: string, profileId: string, invitedBy: string): Promise<Invite>
|
|
89
|
+
bulkInvite(orgId: string, emails: string[], profileId: string, invitedBy: string): Promise<Invite[]>
|
|
90
|
+
revokeInvite(orgId: string, inviteId: string): Promise<void>
|
|
91
|
+
resendInvite(orgId: string, inviteId: string): Promise<Invite>
|
|
92
|
+
listLocations(orgId: string): Promise<Location[]>
|
|
93
|
+
createLocation(orgId: string, data: { name: string; address?: string; city?: string; state?: string; country?: string; phone?: string; isHeadquarters?: boolean }): Promise<Location>
|
|
94
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export type PermissionAction = 'read' | 'create' | 'edit' | 'write' | 'delete' | 'manage' | (string & {})
|
|
2
|
+
|
|
3
|
+
export type SystemPermission =
|
|
4
|
+
| 'manage_team'
|
|
5
|
+
| 'manage_billing'
|
|
6
|
+
| 'manage_settings'
|
|
7
|
+
| 'manage_permissions'
|
|
8
|
+
|
|
9
|
+
export interface FeatureDeclaration {
|
|
10
|
+
id: string
|
|
11
|
+
label: string
|
|
12
|
+
description?: string
|
|
13
|
+
/** Settings group this feature appears under (matches saas-core runtime) */
|
|
14
|
+
group?: string
|
|
15
|
+
actions?: PermissionAction[]
|
|
16
|
+
pluginId?: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface PermissionProfile {
|
|
20
|
+
id: string
|
|
21
|
+
name: string
|
|
22
|
+
description?: string
|
|
23
|
+
isSystem?: boolean
|
|
24
|
+
systemPermissions?: SystemPermission[]
|
|
25
|
+
/** feature key → allowed actions. The canonical permission map (saas-core shell name). */
|
|
26
|
+
grants: Record<string, PermissionAction[]>
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface PermissionsConfig {
|
|
30
|
+
features: FeatureDeclaration[]
|
|
31
|
+
defaultProfiles?: PermissionProfile[]
|
|
32
|
+
}
|