@fayz-ai/sdk 0.9.0-next.1 → 0.10.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.
- package/README.md +2 -2
- package/dist/ai-builder.js +1 -1
- package/dist/{chunk-2YJXKDHO.js → chunk-6UZOXHGJ.js} +11 -6
- package/dist/chunk-6UZOXHGJ.js.map +1 -0
- package/dist/{chunk-V4GWI7WH.js → chunk-7UXTYT6I.js} +65 -4
- package/dist/chunk-7UXTYT6I.js.map +1 -0
- package/dist/{chunk-7XM4LONE.js → chunk-BJFMBVMP.js} +2 -2
- package/dist/chunk-BJFMBVMP.js.map +1 -0
- package/dist/chunk-HA5O3CW7.js +181 -0
- package/dist/chunk-HA5O3CW7.js.map +1 -0
- package/dist/chunk-VRUPJGFZ.js +135 -0
- package/dist/chunk-VRUPJGFZ.js.map +1 -0
- package/dist/deprecations.d.ts +98 -0
- package/dist/deprecations.d.ts.map +1 -0
- package/dist/deprecations.js +3 -0
- package/dist/deprecations.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -4
- package/dist/release-channels.js +1 -1
- package/dist/shop.d.ts +32 -0
- package/dist/shop.d.ts.map +1 -1
- package/dist/shop.js +2 -1
- package/dist/supported-surface.d.ts +8 -1
- package/dist/supported-surface.d.ts.map +1 -1
- package/dist/supported-surface.js +1 -1
- package/dist/vite.d.ts +30 -1
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +10 -2
- package/dist/vite.js.map +1 -1
- package/package.json +7 -14
- package/dist/chunk-2YJXKDHO.js.map +0 -1
- package/dist/chunk-7XM4LONE.js.map +0 -1
- package/dist/chunk-V4GWI7WH.js.map +0 -1
- package/dist/chunk-YZQW3BJA.js +0 -135
- package/dist/chunk-YZQW3BJA.js.map +0 -1
- package/src/agent.test.ts +0 -114
- package/src/agent.ts +0 -335
- package/src/ai-builder.ts +0 -115
- package/src/app-params.ts +0 -69
- package/src/client.test.ts +0 -176
- package/src/client.ts +0 -229
- package/src/index.ts +0 -94
- package/src/release-channels.json +0 -103
- package/src/release-channels.test.ts +0 -41
- package/src/release-channels.ts +0 -41
- package/src/runtime.test.ts +0 -123
- package/src/runtime.ts +0 -211
- package/src/shop.test.ts +0 -114
- package/src/shop.ts +0 -1118
- package/src/supported-surface.json +0 -40
- package/src/supported-surface.ts +0 -33
- package/src/types.ts +0 -67
- package/src/vite.ts +0 -121
package/src/runtime.test.ts
DELETED
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from 'vitest'
|
|
2
|
-
import {
|
|
3
|
-
createFayzRuntimeClient,
|
|
4
|
-
FayzRuntimeError,
|
|
5
|
-
} from './runtime'
|
|
6
|
-
|
|
7
|
-
function jsonResponse(body: unknown, status = 200): Response {
|
|
8
|
-
return new Response(JSON.stringify(body), {
|
|
9
|
-
status,
|
|
10
|
-
headers: { 'Content-Type': 'application/json' },
|
|
11
|
-
})
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
describe('createFayzRuntimeClient', () => {
|
|
15
|
-
it('exchanges runtime-data tokens for broker tokens without sending provider credentials', async () => {
|
|
16
|
-
const fetcher = vi.fn<typeof fetch>().mockResolvedValue(jsonResponse({
|
|
17
|
-
token: 'broker-token',
|
|
18
|
-
tokenType: 'Bearer',
|
|
19
|
-
expiresIn: 300,
|
|
20
|
-
projectId: 'project-1',
|
|
21
|
-
tenantKey: 'tenant-a',
|
|
22
|
-
environment: 'preview',
|
|
23
|
-
pluginId: '@fayz-ai/plugin-agenda',
|
|
24
|
-
grants: [{ grantId: 'grant-1', provider: 'google-calendar', scopes: ['calendar.read'] }],
|
|
25
|
-
}))
|
|
26
|
-
const client = createFayzRuntimeClient({
|
|
27
|
-
baseUrl: 'https://api.fayz.ai/',
|
|
28
|
-
projectId: 'project-1',
|
|
29
|
-
runtimeToken: 'runtime-token',
|
|
30
|
-
fetcher,
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
const response = await client.exchangePluginOAuth({
|
|
34
|
-
pluginId: '@fayz-ai/plugin-agenda',
|
|
35
|
-
scopes: ['calendar.read'],
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
expect(response.token).toBe('broker-token')
|
|
39
|
-
expect(fetcher).toHaveBeenCalledWith(
|
|
40
|
-
'https://api.fayz.ai/api/v1/runtime/projects/project-1/oauth/exchange',
|
|
41
|
-
expect.objectContaining({
|
|
42
|
-
method: 'POST',
|
|
43
|
-
headers: expect.objectContaining({
|
|
44
|
-
Authorization: 'Bearer runtime-token',
|
|
45
|
-
'Content-Type': 'application/json',
|
|
46
|
-
}),
|
|
47
|
-
body: JSON.stringify({
|
|
48
|
-
pluginId: '@fayz-ai/plugin-agenda',
|
|
49
|
-
environment: 'preview',
|
|
50
|
-
scopes: ['calendar.read'],
|
|
51
|
-
}),
|
|
52
|
-
}),
|
|
53
|
-
)
|
|
54
|
-
expect(JSON.stringify(fetcher.mock.calls)).not.toContain('clientSecret')
|
|
55
|
-
expect(JSON.stringify(fetcher.mock.calls)).not.toContain('refreshToken')
|
|
56
|
-
expect(JSON.stringify(fetcher.mock.calls)).not.toContain('accessToken')
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
it('calls Google Calendar broker routes with the short-lived plugin OAuth token', async () => {
|
|
60
|
-
const fetcher = vi.fn<typeof fetch>()
|
|
61
|
-
.mockResolvedValueOnce(jsonResponse({ provider: 'google-calendar', calendarId: 'primary', events: [] }))
|
|
62
|
-
.mockResolvedValueOnce(jsonResponse({ provider: 'google-calendar', calendarId: 'primary', event: { id: 'event-1' } }, 201))
|
|
63
|
-
.mockResolvedValueOnce(jsonResponse({ provider: 'google-calendar', calendarId: 'primary', event: { id: 'event-1' } }))
|
|
64
|
-
.mockResolvedValueOnce(new Response(null, { status: 204 }))
|
|
65
|
-
const client = createFayzRuntimeClient({
|
|
66
|
-
baseUrl: 'https://api.fayz.ai',
|
|
67
|
-
projectId: 'project-1',
|
|
68
|
-
runtimeToken: 'runtime-token',
|
|
69
|
-
fetcher,
|
|
70
|
-
})
|
|
71
|
-
const calendar = client.googleCalendar('broker-token')
|
|
72
|
-
|
|
73
|
-
await calendar.listEvents({
|
|
74
|
-
calendarId: 'primary',
|
|
75
|
-
timeMin: '2026-06-14T00:00:00.000Z',
|
|
76
|
-
timeMax: '2026-06-15T00:00:00.000Z',
|
|
77
|
-
maxResults: 25,
|
|
78
|
-
})
|
|
79
|
-
await calendar.createEvent('primary', {
|
|
80
|
-
summary: 'Haircut',
|
|
81
|
-
start: { dateTime: '2026-06-14T09:00:00.000Z' },
|
|
82
|
-
end: { dateTime: '2026-06-14T09:30:00.000Z' },
|
|
83
|
-
})
|
|
84
|
-
await calendar.updateEvent('primary', 'event-1', { summary: 'Updated haircut' })
|
|
85
|
-
await calendar.deleteEvent('primary', 'event-1')
|
|
86
|
-
|
|
87
|
-
expect(fetcher.mock.calls[0][0]).toBe(
|
|
88
|
-
'https://api.fayz.ai/api/v1/runtime/projects/project-1/oauth/google-calendar/events?calendarId=primary&timeMin=2026-06-14T00%3A00%3A00.000Z&timeMax=2026-06-15T00%3A00%3A00.000Z&maxResults=25',
|
|
89
|
-
)
|
|
90
|
-
expect(fetcher.mock.calls[1][0]).toBe(
|
|
91
|
-
'https://api.fayz.ai/api/v1/runtime/projects/project-1/oauth/google-calendar/events?calendarId=primary',
|
|
92
|
-
)
|
|
93
|
-
expect(fetcher.mock.calls[2][0]).toBe(
|
|
94
|
-
'https://api.fayz.ai/api/v1/runtime/projects/project-1/oauth/google-calendar/events/event-1?calendarId=primary',
|
|
95
|
-
)
|
|
96
|
-
expect(fetcher.mock.calls[3][0]).toBe(
|
|
97
|
-
'https://api.fayz.ai/api/v1/runtime/projects/project-1/oauth/google-calendar/events/event-1?calendarId=primary',
|
|
98
|
-
)
|
|
99
|
-
expect(fetcher.mock.calls[0][1]).toEqual(expect.objectContaining({
|
|
100
|
-
headers: expect.objectContaining({ Authorization: 'Bearer broker-token' }),
|
|
101
|
-
}))
|
|
102
|
-
expect(fetcher.mock.calls[1][1]).toEqual(expect.objectContaining({ method: 'POST' }))
|
|
103
|
-
expect(fetcher.mock.calls[2][1]).toEqual(expect.objectContaining({ method: 'PATCH' }))
|
|
104
|
-
expect(fetcher.mock.calls[3][1]).toEqual(expect.objectContaining({ method: 'DELETE' }))
|
|
105
|
-
})
|
|
106
|
-
|
|
107
|
-
it('keeps non-json upstream errors debuggable', async () => {
|
|
108
|
-
const fetcher = vi.fn<typeof fetch>().mockResolvedValue(new Response('upstream unavailable', { status: 502 }))
|
|
109
|
-
const client = createFayzRuntimeClient({
|
|
110
|
-
baseUrl: 'https://api.fayz.ai',
|
|
111
|
-
projectId: 'project-1',
|
|
112
|
-
runtimeToken: 'runtime-token',
|
|
113
|
-
fetcher,
|
|
114
|
-
})
|
|
115
|
-
|
|
116
|
-
await expect(client.exchangePluginOAuth({ pluginId: '@fayz-ai/plugin-agenda' }))
|
|
117
|
-
.rejects.toMatchObject({
|
|
118
|
-
name: 'FayzRuntimeError',
|
|
119
|
-
status: 502,
|
|
120
|
-
responseBody: 'upstream unavailable',
|
|
121
|
-
} satisfies Partial<FayzRuntimeError>)
|
|
122
|
-
})
|
|
123
|
-
})
|
package/src/runtime.ts
DELETED
|
@@ -1,211 +0,0 @@
|
|
|
1
|
-
export interface FayzRuntimeClientOptions {
|
|
2
|
-
baseUrl: string
|
|
3
|
-
projectId: string
|
|
4
|
-
runtimeToken: string
|
|
5
|
-
fetcher?: typeof fetch
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export type FayzRuntimeEnvironment = 'preview' | 'production'
|
|
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(`${field} is required`)
|
|
85
|
-
return normalized
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function runtimePath(projectId: string, suffix: string): string {
|
|
89
|
-
return `/api/v1/runtime/projects/${encodeURIComponent(projectId)}${suffix}`
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function queryString(params: Record<string, string | number | undefined>): string {
|
|
93
|
-
const search = new URLSearchParams()
|
|
94
|
-
for (const [key, value] of Object.entries(params)) {
|
|
95
|
-
if (value !== undefined && value !== '') search.set(key, String(value))
|
|
96
|
-
}
|
|
97
|
-
const value = search.toString()
|
|
98
|
-
return value ? `?${value}` : ''
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
async function parseResponse<T>(response: Response): Promise<T> {
|
|
102
|
-
if (response.status === 204) return undefined as T
|
|
103
|
-
|
|
104
|
-
const text = await response.text()
|
|
105
|
-
let body: unknown
|
|
106
|
-
try {
|
|
107
|
-
body = text ? JSON.parse(text) : undefined
|
|
108
|
-
} catch {
|
|
109
|
-
body = text
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
if (!response.ok) {
|
|
113
|
-
const message = typeof body === 'object' && body && 'message' in body
|
|
114
|
-
? String((body as { message: unknown }).message)
|
|
115
|
-
: `Fayz runtime request failed with status ${response.status}`
|
|
116
|
-
throw new FayzRuntimeError(message, response.status, body)
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
return body as T
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
export function createFayzRuntimeClient(options: FayzRuntimeClientOptions) {
|
|
123
|
-
const baseUrl = normalizeBaseUrl(assertRequired(options.baseUrl, 'baseUrl'))
|
|
124
|
-
const projectId = assertRequired(options.projectId, 'projectId')
|
|
125
|
-
const runtimeToken = assertRequired(options.runtimeToken, 'runtimeToken')
|
|
126
|
-
const fetcher = options.fetcher ?? fetch
|
|
127
|
-
|
|
128
|
-
async function request<T>(
|
|
129
|
-
path: string,
|
|
130
|
-
token: string,
|
|
131
|
-
init: Omit<RequestInit, 'headers'> & { headers?: Record<string, string> } = {},
|
|
132
|
-
): Promise<T> {
|
|
133
|
-
const response = await fetcher(`${baseUrl}${path}`, {
|
|
134
|
-
...init,
|
|
135
|
-
headers: {
|
|
136
|
-
...(init.body ? { 'Content-Type': 'application/json' } : {}),
|
|
137
|
-
...init.headers,
|
|
138
|
-
Authorization: `Bearer ${token}`,
|
|
139
|
-
},
|
|
140
|
-
})
|
|
141
|
-
return parseResponse<T>(response)
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
async function exchangePluginOAuth(input: PluginOAuthExchangeInput): Promise<PluginOAuthExchangeResponse> {
|
|
145
|
-
return request<PluginOAuthExchangeResponse>(
|
|
146
|
-
runtimePath(projectId, '/oauth/exchange'),
|
|
147
|
-
runtimeToken,
|
|
148
|
-
{
|
|
149
|
-
method: 'POST',
|
|
150
|
-
body: JSON.stringify({
|
|
151
|
-
pluginId: input.pluginId,
|
|
152
|
-
environment: input.environment ?? 'preview',
|
|
153
|
-
...(input.scopes ? { scopes: input.scopes } : {}),
|
|
154
|
-
}),
|
|
155
|
-
},
|
|
156
|
-
)
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
function googleCalendar(pluginOAuthToken: string) {
|
|
160
|
-
const brokerToken = assertRequired(pluginOAuthToken, 'pluginOAuthToken')
|
|
161
|
-
const eventsPath = runtimePath(projectId, '/oauth/google-calendar/events')
|
|
162
|
-
|
|
163
|
-
return {
|
|
164
|
-
listEvents(input: ListGoogleCalendarEventsInput) {
|
|
165
|
-
const calendarId = assertRequired(input.calendarId, 'calendarId')
|
|
166
|
-
return request<{
|
|
167
|
-
provider: 'google-calendar'
|
|
168
|
-
calendarId: string
|
|
169
|
-
timeMin: string
|
|
170
|
-
timeMax: string
|
|
171
|
-
events: GoogleCalendarEvent[]
|
|
172
|
-
}>(
|
|
173
|
-
`${eventsPath}${queryString({
|
|
174
|
-
calendarId,
|
|
175
|
-
timeMin: input.timeMin,
|
|
176
|
-
timeMax: input.timeMax,
|
|
177
|
-
maxResults: input.maxResults,
|
|
178
|
-
q: input.q,
|
|
179
|
-
})}`,
|
|
180
|
-
brokerToken,
|
|
181
|
-
)
|
|
182
|
-
},
|
|
183
|
-
createEvent(calendarId: string, event: GoogleCalendarEventInput) {
|
|
184
|
-
return request<{ provider: 'google-calendar'; calendarId: string; event: GoogleCalendarEvent }>(
|
|
185
|
-
`${eventsPath}${queryString({ calendarId: assertRequired(calendarId, 'calendarId') })}`,
|
|
186
|
-
brokerToken,
|
|
187
|
-
{ method: 'POST', body: JSON.stringify(event) },
|
|
188
|
-
)
|
|
189
|
-
},
|
|
190
|
-
updateEvent(calendarId: string, eventId: string, updates: Partial<GoogleCalendarEventInput>) {
|
|
191
|
-
return request<{ provider: 'google-calendar'; calendarId: string; event: GoogleCalendarEvent }>(
|
|
192
|
-
`${eventsPath}/${encodeURIComponent(assertRequired(eventId, 'eventId'))}${queryString({ calendarId: assertRequired(calendarId, 'calendarId') })}`,
|
|
193
|
-
brokerToken,
|
|
194
|
-
{ method: 'PATCH', body: JSON.stringify(updates) },
|
|
195
|
-
)
|
|
196
|
-
},
|
|
197
|
-
deleteEvent(calendarId: string, eventId: string) {
|
|
198
|
-
return request<void>(
|
|
199
|
-
`${eventsPath}/${encodeURIComponent(assertRequired(eventId, 'eventId'))}${queryString({ calendarId: assertRequired(calendarId, 'calendarId') })}`,
|
|
200
|
-
brokerToken,
|
|
201
|
-
{ method: 'DELETE' },
|
|
202
|
-
)
|
|
203
|
-
},
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
return {
|
|
208
|
-
exchangePluginOAuth,
|
|
209
|
-
googleCalendar,
|
|
210
|
-
}
|
|
211
|
-
}
|
package/src/shop.test.ts
DELETED
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from 'vitest'
|
|
2
|
-
|
|
3
|
-
import { createFayzShopProvider } from './shop'
|
|
4
|
-
|
|
5
|
-
function jsonResponse(body: unknown, status = 200): Response {
|
|
6
|
-
return new Response(JSON.stringify(body), {
|
|
7
|
-
status,
|
|
8
|
-
headers: { 'Content-Type': 'application/json' },
|
|
9
|
-
})
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
function productRow(overrides: Record<string, unknown> = {}) {
|
|
13
|
-
return {
|
|
14
|
-
id: '11111111-1111-4111-8111-000000000001',
|
|
15
|
-
tenant_id: 'store-1',
|
|
16
|
-
name: 'Runner Vortex Neon',
|
|
17
|
-
slug: 'runner-vortex-neon',
|
|
18
|
-
sku: 'SNK-001',
|
|
19
|
-
price: 499.9,
|
|
20
|
-
metadata: {},
|
|
21
|
-
images: [],
|
|
22
|
-
...overrides,
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
describe('createFayzShopProvider', () => {
|
|
27
|
-
it('uses the Fayz broker without provider credentials when apiBaseUrl and projectId are provided', async () => {
|
|
28
|
-
const fetcher = vi.fn<typeof fetch>().mockResolvedValue(jsonResponse({
|
|
29
|
-
products: [{
|
|
30
|
-
id: '11111111-1111-4111-8111-000000000001',
|
|
31
|
-
tenantId: 'store-1',
|
|
32
|
-
name: 'Aurora Tote',
|
|
33
|
-
slug: 'aurora-tote',
|
|
34
|
-
sku: 'BAG-001',
|
|
35
|
-
price: 149.9,
|
|
36
|
-
metadata: {},
|
|
37
|
-
images: [],
|
|
38
|
-
category: { id: 'cat-1', name: 'Bags' },
|
|
39
|
-
}],
|
|
40
|
-
}))
|
|
41
|
-
|
|
42
|
-
const provider = createFayzShopProvider({
|
|
43
|
-
apiBaseUrl: 'http://localhost:5173/api',
|
|
44
|
-
projectId: 'project-1',
|
|
45
|
-
fetcher,
|
|
46
|
-
})
|
|
47
|
-
|
|
48
|
-
const [product] = await provider.listProducts({ status: 'active', limit: 4 })
|
|
49
|
-
|
|
50
|
-
expect(fetcher).toHaveBeenCalledWith(
|
|
51
|
-
'http://localhost:5173/api/public/projects/project-1/shop/products?status=active&limit=4',
|
|
52
|
-
{},
|
|
53
|
-
)
|
|
54
|
-
expect(product?.categoryId).toBe('cat-1')
|
|
55
|
-
expect(product?.categoryName).toBe('Bags')
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
it('merges app-owned product metadata overlays into listed products', async () => {
|
|
59
|
-
const fetcher = vi.fn<typeof fetch>().mockResolvedValue(jsonResponse([
|
|
60
|
-
productRow({
|
|
61
|
-
metadata: {
|
|
62
|
-
colorway: 'Backend wins',
|
|
63
|
-
},
|
|
64
|
-
}),
|
|
65
|
-
]))
|
|
66
|
-
|
|
67
|
-
const provider = createFayzShopProvider({
|
|
68
|
-
supabaseUrl: 'https://example.supabase.co',
|
|
69
|
-
publishableKey: 'publishable-key',
|
|
70
|
-
storeId: 'store-1',
|
|
71
|
-
productMetadata: [
|
|
72
|
-
{
|
|
73
|
-
sku: 'SNK-001',
|
|
74
|
-
slug: 'runner-vortex-neon',
|
|
75
|
-
metadata: {
|
|
76
|
-
colorway: 'Overlay loses',
|
|
77
|
-
sizes: ['38', '39', '40'],
|
|
78
|
-
},
|
|
79
|
-
},
|
|
80
|
-
],
|
|
81
|
-
fetcher,
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
const [product] = await provider.listProducts({ status: 'active' })
|
|
85
|
-
|
|
86
|
-
expect(product?.metadata.sizes).toEqual(['38', '39', '40'])
|
|
87
|
-
expect(product?.metadata.colorway).toBe('Backend wins')
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
it('uses slug overlays when fetching a single product', async () => {
|
|
91
|
-
const fetcher = vi.fn<typeof fetch>().mockResolvedValue(jsonResponse([
|
|
92
|
-
productRow({ sku: null }),
|
|
93
|
-
]))
|
|
94
|
-
|
|
95
|
-
const provider = createFayzShopProvider({
|
|
96
|
-
supabaseUrl: 'https://example.supabase.co',
|
|
97
|
-
publishableKey: 'publishable-key',
|
|
98
|
-
storeId: 'store-1',
|
|
99
|
-
productMetadata: [
|
|
100
|
-
{
|
|
101
|
-
slug: 'runner-vortex-neon',
|
|
102
|
-
metadata: {
|
|
103
|
-
sizes: ['40', '41'],
|
|
104
|
-
},
|
|
105
|
-
},
|
|
106
|
-
],
|
|
107
|
-
fetcher,
|
|
108
|
-
})
|
|
109
|
-
|
|
110
|
-
const product = await provider.getProduct('11111111-1111-4111-8111-000000000001')
|
|
111
|
-
|
|
112
|
-
expect(product?.metadata.sizes).toEqual(['40', '41'])
|
|
113
|
-
})
|
|
114
|
-
})
|