@fayz-ai/sdk 0.9.0 → 0.10.1
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-KVMRRT3O.js +135 -0
- package/dist/chunk-KVMRRT3O.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/deprecations.json +119 -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/release-channels.json +103 -0
- 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/{src → dist}/supported-surface.json +5 -3
- 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 +10 -17
- package/dist/chunk-2YJXKDHO.js.map +0 -1
- package/dist/chunk-7XM4LONE.js.map +0 -1
- package/dist/chunk-GQPVDQEB.js +0 -135
- package/dist/chunk-GQPVDQEB.js.map +0 -1
- package/dist/chunk-V4GWI7WH.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.ts +0 -33
- package/src/types.ts +0 -67
- package/src/vite.ts +0 -121
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"channels": {
|
|
3
|
-
"stable": {
|
|
4
|
-
"@fayz-ai/sdk": "^0.9.0",
|
|
5
|
-
"@fayz-ai/core": "^0.9.0",
|
|
6
|
-
"@fayz-ai/auth": "^0.9.0",
|
|
7
|
-
"@fayz-ai/ui": "^0.9.0",
|
|
8
|
-
"@fayz-ai/saas": "^0.9.0",
|
|
9
|
-
"@fayz-ai/shop": "^0.9.0",
|
|
10
|
-
"@fayz-ai/storefront": "^0.9.0",
|
|
11
|
-
"@fayz-ai/portal": "^0.9.0",
|
|
12
|
-
"@fayz-ai/courses": "^0.9.0",
|
|
13
|
-
"@fayz-ai/db": "^0.9.0",
|
|
14
|
-
"@fayz-ai/plugin-agenda": "^0.9.0",
|
|
15
|
-
"@fayz-ai/plugin-auth": "^0.9.0",
|
|
16
|
-
"@fayz-ai/plugin-automations": "^0.9.0",
|
|
17
|
-
"@fayz-ai/plugin-blog": "^0.9.0",
|
|
18
|
-
"@fayz-ai/plugin-conversations": "^0.9.0",
|
|
19
|
-
"@fayz-ai/plugin-courses": "^0.9.0",
|
|
20
|
-
"@fayz-ai/plugin-crm": "^0.9.0",
|
|
21
|
-
"@fayz-ai/plugin-dashboard": "^0.9.0",
|
|
22
|
-
"@fayz-ai/plugin-financial": "^0.9.0",
|
|
23
|
-
"@fayz-ai/plugin-forms": "^0.9.0",
|
|
24
|
-
"@fayz-ai/plugin-inventory": "^0.9.0",
|
|
25
|
-
"@fayz-ai/plugin-marketing": "^0.9.0",
|
|
26
|
-
"@fayz-ai/plugin-menu": "^0.9.0",
|
|
27
|
-
"@fayz-ai/plugin-orders": "^0.9.0",
|
|
28
|
-
"@fayz-ai/plugin-payments": "^0.9.0",
|
|
29
|
-
"@fayz-ai/plugin-reports": "^0.9.0",
|
|
30
|
-
"@fayz-ai/plugin-reputation": "^0.9.0",
|
|
31
|
-
"@fayz-ai/plugin-shop": "^0.9.0",
|
|
32
|
-
"@fayz-ai/plugin-sites": "^0.9.0",
|
|
33
|
-
"@fayz-ai/plugin-tables": "^0.9.0",
|
|
34
|
-
"@fayz-ai/plugin-tasks": "^0.9.0"
|
|
35
|
-
},
|
|
36
|
-
"latest": {
|
|
37
|
-
"@fayz-ai/sdk": "^0.9.0",
|
|
38
|
-
"@fayz-ai/core": "^0.9.0",
|
|
39
|
-
"@fayz-ai/auth": "^0.9.0",
|
|
40
|
-
"@fayz-ai/ui": "^0.9.0",
|
|
41
|
-
"@fayz-ai/saas": "^0.9.0",
|
|
42
|
-
"@fayz-ai/shop": "^0.9.0",
|
|
43
|
-
"@fayz-ai/storefront": "^0.9.0",
|
|
44
|
-
"@fayz-ai/portal": "^0.9.0",
|
|
45
|
-
"@fayz-ai/courses": "^0.9.0",
|
|
46
|
-
"@fayz-ai/db": "^0.9.0",
|
|
47
|
-
"@fayz-ai/plugin-agenda": "^0.9.0",
|
|
48
|
-
"@fayz-ai/plugin-auth": "^0.9.0",
|
|
49
|
-
"@fayz-ai/plugin-automations": "^0.9.0",
|
|
50
|
-
"@fayz-ai/plugin-blog": "^0.9.0",
|
|
51
|
-
"@fayz-ai/plugin-conversations": "^0.9.0",
|
|
52
|
-
"@fayz-ai/plugin-courses": "^0.9.0",
|
|
53
|
-
"@fayz-ai/plugin-crm": "^0.9.0",
|
|
54
|
-
"@fayz-ai/plugin-dashboard": "^0.9.0",
|
|
55
|
-
"@fayz-ai/plugin-financial": "^0.9.0",
|
|
56
|
-
"@fayz-ai/plugin-forms": "^0.9.0",
|
|
57
|
-
"@fayz-ai/plugin-inventory": "^0.9.0",
|
|
58
|
-
"@fayz-ai/plugin-marketing": "^0.9.0",
|
|
59
|
-
"@fayz-ai/plugin-menu": "^0.9.0",
|
|
60
|
-
"@fayz-ai/plugin-orders": "^0.9.0",
|
|
61
|
-
"@fayz-ai/plugin-payments": "^0.9.0",
|
|
62
|
-
"@fayz-ai/plugin-reports": "^0.9.0",
|
|
63
|
-
"@fayz-ai/plugin-reputation": "^0.9.0",
|
|
64
|
-
"@fayz-ai/plugin-shop": "^0.9.0",
|
|
65
|
-
"@fayz-ai/plugin-sites": "^0.9.0",
|
|
66
|
-
"@fayz-ai/plugin-tables": "^0.9.0",
|
|
67
|
-
"@fayz-ai/plugin-tasks": "^0.9.0"
|
|
68
|
-
},
|
|
69
|
-
"preview": {
|
|
70
|
-
"@fayz-ai/sdk": "^0.9.0",
|
|
71
|
-
"@fayz-ai/core": "^0.9.0",
|
|
72
|
-
"@fayz-ai/auth": "^0.9.0",
|
|
73
|
-
"@fayz-ai/ui": "^0.9.0",
|
|
74
|
-
"@fayz-ai/saas": "^0.9.0",
|
|
75
|
-
"@fayz-ai/shop": "^0.9.0",
|
|
76
|
-
"@fayz-ai/storefront": "^0.9.0",
|
|
77
|
-
"@fayz-ai/portal": "^0.9.0",
|
|
78
|
-
"@fayz-ai/courses": "^0.9.0",
|
|
79
|
-
"@fayz-ai/db": "^0.9.0",
|
|
80
|
-
"@fayz-ai/plugin-agenda": "^0.9.0",
|
|
81
|
-
"@fayz-ai/plugin-auth": "^0.9.0",
|
|
82
|
-
"@fayz-ai/plugin-automations": "^0.9.0",
|
|
83
|
-
"@fayz-ai/plugin-blog": "^0.9.0",
|
|
84
|
-
"@fayz-ai/plugin-conversations": "^0.9.0",
|
|
85
|
-
"@fayz-ai/plugin-courses": "^0.9.0",
|
|
86
|
-
"@fayz-ai/plugin-crm": "^0.9.0",
|
|
87
|
-
"@fayz-ai/plugin-dashboard": "^0.9.0",
|
|
88
|
-
"@fayz-ai/plugin-financial": "^0.9.0",
|
|
89
|
-
"@fayz-ai/plugin-forms": "^0.9.0",
|
|
90
|
-
"@fayz-ai/plugin-inventory": "^0.9.0",
|
|
91
|
-
"@fayz-ai/plugin-marketing": "^0.9.0",
|
|
92
|
-
"@fayz-ai/plugin-menu": "^0.9.0",
|
|
93
|
-
"@fayz-ai/plugin-orders": "^0.9.0",
|
|
94
|
-
"@fayz-ai/plugin-payments": "^0.9.0",
|
|
95
|
-
"@fayz-ai/plugin-reports": "^0.9.0",
|
|
96
|
-
"@fayz-ai/plugin-reputation": "^0.9.0",
|
|
97
|
-
"@fayz-ai/plugin-shop": "^0.9.0",
|
|
98
|
-
"@fayz-ai/plugin-sites": "^0.9.0",
|
|
99
|
-
"@fayz-ai/plugin-tables": "^0.9.0",
|
|
100
|
-
"@fayz-ai/plugin-tasks": "^0.9.0"
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest'
|
|
2
|
-
import releaseChannels from './release-channels.json' with { type: 'json' }
|
|
3
|
-
import {
|
|
4
|
-
resolveFayzPackageDependencies,
|
|
5
|
-
fayzPackageVersionSets,
|
|
6
|
-
resolveFayzPackageVersion,
|
|
7
|
-
resolveFayzPackageVersions,
|
|
8
|
-
} from './release-channels'
|
|
9
|
-
|
|
10
|
-
describe('release channels', () => {
|
|
11
|
-
it('keeps stable as the default channel', () => {
|
|
12
|
-
expect(resolveFayzPackageVersions()).toEqual({
|
|
13
|
-
channel: 'stable',
|
|
14
|
-
packages: releaseChannels.channels.stable,
|
|
15
|
-
})
|
|
16
|
-
expect(resolveFayzPackageVersions().packages['@fayz-ai/sdk']).toBe(releaseChannels.channels.stable['@fayz-ai/sdk'])
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
it('returns a copy of package dependencies', () => {
|
|
20
|
-
const dependencies = resolveFayzPackageDependencies()
|
|
21
|
-
dependencies['@fayz-ai/sdk'] = 'tampered'
|
|
22
|
-
|
|
23
|
-
expect(resolveFayzPackageDependencies()['@fayz-ai/sdk']).toBe(releaseChannels.channels.stable['@fayz-ai/sdk'])
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
it('throws when a package is not mapped for a channel', () => {
|
|
27
|
-
expect(() => resolveFayzPackageVersion('@fayz-ai/app-runtime')).toThrow(
|
|
28
|
-
'No Fayz package version configured for @fayz-ai/app-runtime in stable channel',
|
|
29
|
-
)
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
it('keeps the machine-readable release-channel manifest aligned with the typed export', () => {
|
|
33
|
-
expect(releaseChannels).toEqual({
|
|
34
|
-
channels: {
|
|
35
|
-
stable: fayzPackageVersionSets.stable.packages,
|
|
36
|
-
latest: fayzPackageVersionSets.latest.packages,
|
|
37
|
-
preview: fayzPackageVersionSets.preview.packages,
|
|
38
|
-
},
|
|
39
|
-
})
|
|
40
|
-
})
|
|
41
|
-
})
|
package/src/release-channels.ts
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import releaseChannels from './release-channels.json'
|
|
2
|
-
|
|
3
|
-
export type FayzPackageChannel = 'stable' | 'latest' | 'preview'
|
|
4
|
-
|
|
5
|
-
export interface FayzPackageVersionSet {
|
|
6
|
-
channel: FayzPackageChannel
|
|
7
|
-
packages: Record<string, string>
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const releaseChannelPackages = releaseChannels.channels as Record<FayzPackageChannel, Record<string, string>>
|
|
11
|
-
|
|
12
|
-
function buildVersionSet(channel: FayzPackageChannel): FayzPackageVersionSet {
|
|
13
|
-
return {
|
|
14
|
-
channel,
|
|
15
|
-
packages: { ...releaseChannelPackages[channel] },
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export const fayzPackageVersionSets: Record<FayzPackageChannel, FayzPackageVersionSet> = {
|
|
20
|
-
stable: buildVersionSet('stable'),
|
|
21
|
-
latest: buildVersionSet('latest'),
|
|
22
|
-
preview: buildVersionSet('preview'),
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function resolveFayzPackageVersions(channel: FayzPackageChannel = 'stable'): FayzPackageVersionSet {
|
|
26
|
-
return fayzPackageVersionSets[channel]
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function resolveFayzPackageDependencies(channel: FayzPackageChannel = 'stable'): Record<string, string> {
|
|
30
|
-
return { ...resolveFayzPackageVersions(channel).packages }
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export function resolveFayzPackageVersion(packageName: string, channel: FayzPackageChannel = 'stable'): string {
|
|
34
|
-
const version = resolveFayzPackageVersions(channel).packages[packageName]
|
|
35
|
-
|
|
36
|
-
if (!version) {
|
|
37
|
-
throw new Error(`No Fayz package version configured for ${packageName} in ${channel} channel`)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
return version
|
|
41
|
-
}
|
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
|
-
})
|