@fayz-ai/sdk 0.6.6 → 0.8.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 -0
- package/dist/ai-builder.cjs +4 -4
- package/dist/ai-builder.js +1 -1
- package/dist/{chunk-VUSZUJX3.js → chunk-2YJXKDHO.js} +5 -5
- package/dist/chunk-2YJXKDHO.js.map +1 -0
- package/dist/{chunk-RCX43OHW.cjs → chunk-3UTEZ2QL.cjs} +77 -60
- package/dist/chunk-3UTEZ2QL.cjs.map +1 -0
- package/dist/{chunk-UMI2WELN.js → chunk-7XM4LONE.js} +3 -3
- package/dist/chunk-7XM4LONE.js.map +1 -0
- package/dist/{chunk-OXIIHQFK.cjs → chunk-B6FHOO34.cjs} +5 -5
- package/dist/chunk-B6FHOO34.cjs.map +1 -0
- package/dist/{chunk-VDWMKACF.cjs → chunk-DAUDGR5N.cjs} +3 -3
- package/dist/chunk-DAUDGR5N.cjs.map +1 -0
- package/dist/chunk-HK5FEKDG.js +135 -0
- package/dist/chunk-HK5FEKDG.js.map +1 -0
- package/dist/chunk-LYK5TDPK.cjs +140 -0
- package/dist/chunk-LYK5TDPK.cjs.map +1 -0
- package/dist/{chunk-6JDIFOJG.js → chunk-PRCW5CVJ.js} +77 -60
- package/dist/chunk-PRCW5CVJ.js.map +1 -0
- package/dist/index.cjs +17 -17
- package/dist/index.js +4 -4
- package/dist/release-channels.cjs +5 -5
- package/dist/release-channels.js +1 -1
- package/dist/shop.cjs +3 -3
- package/dist/shop.d.ts +146 -1
- package/dist/shop.d.ts.map +1 -1
- package/dist/shop.js +1 -1
- package/dist/supported-surface.cjs +5 -5
- package/dist/supported-surface.js +1 -1
- package/dist/vite.cjs +3 -0
- package/dist/vite.cjs.map +1 -1
- package/dist/vite.js +3 -0
- package/dist/vite.js.map +1 -1
- package/package.json +4 -1
- package/src/ai-builder.ts +2 -2
- package/src/client.test.ts +2 -2
- package/src/release-channels.json +93 -3
- package/src/shop.test.ts +3 -3
- package/src/shop.ts +90 -64
- package/src/supported-surface.json +3 -3
- package/src/vite.ts +2 -2
- package/dist/chunk-6JDIFOJG.js.map +0 -1
- package/dist/chunk-OXIIHQFK.cjs.map +0 -1
- package/dist/chunk-RCX43OHW.cjs.map +0 -1
- package/dist/chunk-UMI2WELN.js.map +0 -1
- package/dist/chunk-UXWBB43F.js +0 -45
- package/dist/chunk-UXWBB43F.js.map +0 -1
- package/dist/chunk-VDWMKACF.cjs.map +0 -1
- package/dist/chunk-VUSZUJX3.js.map +0 -1
- package/dist/chunk-YRTMUHMQ.cjs +0 -50
- package/dist/chunk-YRTMUHMQ.cjs.map +0 -1
package/dist/vite.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/vite.ts"],"names":[],"mappings":";;;;AAOA,IAAM,YAAA,GAAe;AAAA,EACnB,KAAA;AAAA,EAAO,MAAA;AAAA,EAAQ,MAAA;AAAA,EAAQ,IAAA;AAAA,EAAM,MAAA;AAAA,EAAQ,MAAA;AAAA,EAAQ,YAAA;AAAA,EAAc,QAAA;AAAA,EAAU,SAAA;AAAA,EAAW;AAClF,CAAA;AAEA,IAAM,WAAA,GAAc;AAAA,EAClB,QAAA;AAAA,EAAU,aAAA;AAAA,EAAe,eAAA;AAAA,EAAiB,SAAA;AAAA,EAAW,KAAA;AAAA,EAAO,WAAA;AAAA,
|
|
1
|
+
{"version":3,"sources":["../src/vite.ts"],"names":[],"mappings":"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","file":"vite.js","sourcesContent":["import { existsSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport type { UserConfig, PluginOption, AliasOptions } from 'vite'\n\n// All publishable @fayz-ai/* packages (root src). Subpaths (e.g.\n// @fayz-ai/saas/ui, @fayz-ai/storefront/catalog) resolve automatically via\n// Vite's prefix alias matching — the shim files mirror the subpath under src/.\nconst SDK_PACKAGES = [\n 'sdk', 'core', 'auth', 'ui', 'saas', 'shop', 'storefront', 'portal', 'courses', 'db',\n] as const\n\nconst SDK_PLUGINS = [\n 'agenda', 'auth', 'automations', 'blog', 'conversations', 'courses', 'crm', 'dashboard',\n 'financial', 'forms', 'inventory', 'marketing', 'menu', 'orders', 'payments', 'reports',\n 'reputation', 'shop', 'sites', 'tables', 'tasks',\n] as const\n\nexport interface FayzViteOptions {\n /** Dev server port. */\n port?: number\n /** Fail instead of incrementing if the port is taken. */\n strictPort?: boolean\n /** Extra app-specific aliases, merged after the SDK aliases. */\n aliases?: Record<string, string>\n /** Extra Vite plugins, appended after @vitejs/plugin-react. */\n plugins?: PluginOption[]\n /** SDK source location relative to the app root (default ../../fayz-sdk). */\n sdkDir?: string\n /** App root directory (default process.cwd()). */\n root?: string\n /**\n * Extra Vite `server` config, merged over the SDK server defaults. The\n * preview-container contract (host/cors/allowedHosts/headers) is applied\n * first, so an override can add e.g. `proxy` without dropping it.\n */\n server?: UserConfig['server']\n /**\n * Extra Vite `resolve` config, merged over the SDK resolve defaults. The SDK\n * alias map / dedupe / conditions win for `@fayz-ai/*` local-source\n * resolution; any other resolve keys (and extra aliases) are merged in.\n */\n resolve?: UserConfig['resolve']\n}\n\n/**\n * Shared Vite config for Fayz dogfood apps. Resolves `@fayz-ai/*` from the local\n * SDK source when it is checked out next to the app AND `FAYZ_SDK_SOURCE` is not\n * `published`; otherwise from node_modules (the Fayz editor sandbox / published\n * npm). Apps no longer hand-maintain alias maps or the local-vs-npm guard.\n *\n * import { defineConfig } from 'vite'\n * import react from '@vitejs/plugin-react'\n * import { fayzVite } from '@fayz-ai/sdk/vite'\n * export default defineConfig(fayzVite({ port: 5185, plugins: [react()] }))\n *\n * The app passes its own `@vitejs/plugin-react` so `@fayz-ai/sdk` carries no\n * build-tool peer deps (which otherwise choke npm's peer resolution, since sdk\n * sits in every dependency path).\n */\nexport function fayzVite(opts: FayzViteOptions = {}): UserConfig {\n const root = opts.root ?? process.cwd()\n const fayzSdk = resolve(root, opts.sdkDir ?? '../../fayz-sdk')\n const wantLocal = process.env.FAYZ_SDK_SOURCE !== 'published'\n const localPresent = existsSync(resolve(fayzSdk, 'packages/core/src/index.ts'))\n const useLocal = wantLocal && localPresent\n\n const localAliases: Record<string, string> = {}\n if (useLocal) {\n for (const p of SDK_PACKAGES) localAliases[`@fayz-ai/${p}`] = resolve(fayzSdk, `packages/${p}/src`)\n for (const p of SDK_PLUGINS) localAliases[`@fayz-ai/plugin-${p}`] = resolve(fayzSdk, `plugins/plugin-${p}/src`)\n }\n\n // Pull SDK-owned resolve keys out of any caller override so they can't clobber\n // local-source @fayz-ai/* resolution; the rest of `resolve` is merged in.\n const { alias: userAlias, dedupe: userDedupe, conditions: _ignore, ...restResolve } =\n (opts.resolve ?? {}) as Exclude<UserConfig['resolve'], undefined>\n\n return {\n plugins: [...(opts.plugins ?? [])],\n envPrefix: ['VITE_', 'PUBLIC_'],\n resolve: {\n ...restResolve,\n alias: {\n '@': resolve(root, 'src'),\n ...localAliases,\n ...(opts.aliases ?? {}),\n ...((userAlias as Record<string, string> | undefined) ?? {}),\n } as AliasOptions,\n // lucide-react is a direct dep of both the app and every @fayz-ai/*\n // package; without dedupe a version skew installs two copies and esbuild\n // pre-bundles each barrel separately (~1,500 icon modules each).\n // Collapsing to one copy is the single biggest cut to the editor-container\n // optimize RAM/CPU spike. Only dedupe deps the *app* directly depends on\n // (so they're hoisted to its root node_modules) — deduping a transitive\n // like @tanstack/react-table makes Vite resolve it from the app root,\n // which fails in local-source mode and 500s the aliased UI source.\n dedupe: userDedupe ?? ['react', 'react-dom', 'zustand', 'lucide-react'],\n conditions: useLocal ? ['source', 'browser', 'module', 'jsnext:main', 'jsnext'] : undefined,\n },\n optimizeDeps: { exclude: useLocal ? Object.keys(localAliases) : [] },\n // The preview-container runtime contract: Vite 5.4 host-check 403s any\n // non-local Host (browser iframe via Caddy, health probe via\n // host.docker.internal) unless allowedHosts is set. host/cors/headers keep\n // the proxied iframe + cross-origin asset fetches working. Owned here\n // because the app's vite.config has no literal `server: {` for the\n // container's text patchers to anchor on. Caller `server` merges last.\n server: {\n host: true,\n port: opts.port,\n strictPort: opts.strictPort,\n cors: true,\n allowedHosts: true,\n headers: {\n 'Access-Control-Allow-Origin': '*',\n 'Cross-Origin-Resource-Policy': 'cross-origin',\n },\n fs: { allow: [root, fayzSdk] },\n ...(opts.server ?? {}),\n },\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fayz-ai/sdk",
|
|
3
|
-
"
|
|
3
|
+
"fayz": {
|
|
4
|
+
"status": "beta"
|
|
5
|
+
},
|
|
6
|
+
"version": "0.8.0",
|
|
4
7
|
"description": "Fayz SDK — API client, app params, runtime broker helpers, and shared types for generated Fayz projects",
|
|
5
8
|
"type": "module",
|
|
6
9
|
"main": "./dist/index.cjs",
|
package/src/ai-builder.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// The AI Builder request taxonomy — the contract the platform's request
|
|
2
2
|
// classifier targets. The classifier itself lives in the `fayz` platform repo;
|
|
3
3
|
// this is the canonical source of truth for the request classes and the
|
|
4
|
-
// architecture layer each is allowed to touch (docs/
|
|
4
|
+
// architecture layer each is allowed to touch (docs/ARCHITECTURE.md).
|
|
5
5
|
|
|
6
6
|
/** Ownership layer a request may modify. */
|
|
7
7
|
export type FayzLayer = 'app' | 'plugin-config' | 'private-extension' | 'platform' | 'none'
|
|
@@ -65,7 +65,7 @@ export const AI_BUILDER_REQUEST_CLASSES: Record<AiBuilderRequestClass, AiBuilder
|
|
|
65
65
|
needsCode: true,
|
|
66
66
|
needsDeploy: true,
|
|
67
67
|
description:
|
|
68
|
-
'Add new business behaviour as an app-local plugin (own entities, data, migrations, AI tools, connectors). The sanctioned partner/Silvio path — scaffold with `fayz create plugin`. See
|
|
68
|
+
'Add new business behaviour as an app-local plugin (own entities, data, migrations, AI tools, connectors). The sanctioned partner/Silvio path — scaffold with `fayz create plugin`. See CUSTOMIZATION.md.',
|
|
69
69
|
examples: [
|
|
70
70
|
'Build a loyalty-points engine with its own table',
|
|
71
71
|
'Add a custom importer for a legacy ERP',
|
package/src/client.test.ts
CHANGED
|
@@ -53,7 +53,7 @@ describe('createFayzClient', () => {
|
|
|
53
53
|
|
|
54
54
|
const result = await client.data.listRows<{ id: string }>({
|
|
55
55
|
projectId: 'project 1',
|
|
56
|
-
table: '
|
|
56
|
+
table: 'v_appointments',
|
|
57
57
|
filters: [
|
|
58
58
|
{ column: 'starts_at', operator: 'gte', value: '2026-06-14T00:00:00.000Z' },
|
|
59
59
|
{ column: 'status', operator: 'neq', value: 'cancelled' },
|
|
@@ -68,7 +68,7 @@ describe('createFayzClient', () => {
|
|
|
68
68
|
expect(result.total).toBe(12)
|
|
69
69
|
|
|
70
70
|
const [url, init] = fetcher.mock.calls[0] ?? []
|
|
71
|
-
expect(url).toContain('https://api.fayz.ai/api/v1/runtime/projects/project%201/database/tables/
|
|
71
|
+
expect(url).toContain('https://api.fayz.ai/api/v1/runtime/projects/project%201/database/tables/v_appointments/rows?')
|
|
72
72
|
expect(url).toContain('filters=')
|
|
73
73
|
expect(url).toContain('sortColumn=starts_at')
|
|
74
74
|
expect(url).toContain('sortDirection=asc')
|
|
@@ -1,13 +1,103 @@
|
|
|
1
1
|
{
|
|
2
2
|
"channels": {
|
|
3
3
|
"stable": {
|
|
4
|
-
"@fayz-ai/sdk": "^0.
|
|
4
|
+
"@fayz-ai/sdk": "^0.6.7",
|
|
5
|
+
"@fayz-ai/core": "^0.7.2",
|
|
6
|
+
"@fayz-ai/auth": "^0.7.1",
|
|
7
|
+
"@fayz-ai/ui": "^0.7.0",
|
|
8
|
+
"@fayz-ai/saas": "^0.7.0",
|
|
9
|
+
"@fayz-ai/shop": "^0.7.0",
|
|
10
|
+
"@fayz-ai/storefront": "^0.7.0",
|
|
11
|
+
"@fayz-ai/portal": "^0.7.0",
|
|
12
|
+
"@fayz-ai/courses": "^0.7.0",
|
|
13
|
+
"@fayz-ai/db": "^0.1.2",
|
|
14
|
+
"@fayz-ai/plugin-agenda": "^0.4.0",
|
|
15
|
+
"@fayz-ai/plugin-auth": "^0.1.3",
|
|
16
|
+
"@fayz-ai/plugin-automations": "^0.2.3",
|
|
17
|
+
"@fayz-ai/plugin-blog": "^0.1.0",
|
|
18
|
+
"@fayz-ai/plugin-conversations": "^0.2.4",
|
|
19
|
+
"@fayz-ai/plugin-courses": "^0.2.3",
|
|
20
|
+
"@fayz-ai/plugin-crm": "^0.3.0",
|
|
21
|
+
"@fayz-ai/plugin-dashboard": "^0.1.6",
|
|
22
|
+
"@fayz-ai/plugin-financial": "^0.2.0",
|
|
23
|
+
"@fayz-ai/plugin-forms": "^0.2.0",
|
|
24
|
+
"@fayz-ai/plugin-inventory": "^0.1.7",
|
|
25
|
+
"@fayz-ai/plugin-marketing": "^0.2.1",
|
|
26
|
+
"@fayz-ai/plugin-menu": "^0.3.0",
|
|
27
|
+
"@fayz-ai/plugin-orders": "^0.3.0",
|
|
28
|
+
"@fayz-ai/plugin-payments": "^0.1.1",
|
|
29
|
+
"@fayz-ai/plugin-reports": "^0.2.0",
|
|
30
|
+
"@fayz-ai/plugin-reputation": "^0.2.4",
|
|
31
|
+
"@fayz-ai/plugin-shop": "^0.2.5",
|
|
32
|
+
"@fayz-ai/plugin-sites": "^0.2.3",
|
|
33
|
+
"@fayz-ai/plugin-tables": "^0.3.0",
|
|
34
|
+
"@fayz-ai/plugin-tasks": "^0.1.7"
|
|
5
35
|
},
|
|
6
36
|
"latest": {
|
|
7
|
-
"@fayz-ai/sdk": "^0.
|
|
37
|
+
"@fayz-ai/sdk": "^0.6.7",
|
|
38
|
+
"@fayz-ai/core": "^0.7.2",
|
|
39
|
+
"@fayz-ai/auth": "^0.7.1",
|
|
40
|
+
"@fayz-ai/ui": "^0.7.0",
|
|
41
|
+
"@fayz-ai/saas": "^0.7.0",
|
|
42
|
+
"@fayz-ai/shop": "^0.7.0",
|
|
43
|
+
"@fayz-ai/storefront": "^0.7.0",
|
|
44
|
+
"@fayz-ai/portal": "^0.7.0",
|
|
45
|
+
"@fayz-ai/courses": "^0.7.0",
|
|
46
|
+
"@fayz-ai/db": "^0.1.2",
|
|
47
|
+
"@fayz-ai/plugin-agenda": "^0.4.0",
|
|
48
|
+
"@fayz-ai/plugin-auth": "^0.1.3",
|
|
49
|
+
"@fayz-ai/plugin-automations": "^0.2.3",
|
|
50
|
+
"@fayz-ai/plugin-blog": "^0.1.0",
|
|
51
|
+
"@fayz-ai/plugin-conversations": "^0.2.4",
|
|
52
|
+
"@fayz-ai/plugin-courses": "^0.2.3",
|
|
53
|
+
"@fayz-ai/plugin-crm": "^0.3.0",
|
|
54
|
+
"@fayz-ai/plugin-dashboard": "^0.1.6",
|
|
55
|
+
"@fayz-ai/plugin-financial": "^0.2.0",
|
|
56
|
+
"@fayz-ai/plugin-forms": "^0.2.0",
|
|
57
|
+
"@fayz-ai/plugin-inventory": "^0.1.7",
|
|
58
|
+
"@fayz-ai/plugin-marketing": "^0.2.1",
|
|
59
|
+
"@fayz-ai/plugin-menu": "^0.3.0",
|
|
60
|
+
"@fayz-ai/plugin-orders": "^0.3.0",
|
|
61
|
+
"@fayz-ai/plugin-payments": "^0.1.1",
|
|
62
|
+
"@fayz-ai/plugin-reports": "^0.2.0",
|
|
63
|
+
"@fayz-ai/plugin-reputation": "^0.2.4",
|
|
64
|
+
"@fayz-ai/plugin-shop": "^0.2.5",
|
|
65
|
+
"@fayz-ai/plugin-sites": "^0.2.3",
|
|
66
|
+
"@fayz-ai/plugin-tables": "^0.3.0",
|
|
67
|
+
"@fayz-ai/plugin-tasks": "^0.1.7"
|
|
8
68
|
},
|
|
9
69
|
"preview": {
|
|
10
|
-
"@fayz-ai/sdk": "^0.
|
|
70
|
+
"@fayz-ai/sdk": "^0.6.7",
|
|
71
|
+
"@fayz-ai/core": "^0.7.2",
|
|
72
|
+
"@fayz-ai/auth": "^0.7.1",
|
|
73
|
+
"@fayz-ai/ui": "^0.7.0",
|
|
74
|
+
"@fayz-ai/saas": "^0.7.0",
|
|
75
|
+
"@fayz-ai/shop": "^0.7.0",
|
|
76
|
+
"@fayz-ai/storefront": "^0.7.0",
|
|
77
|
+
"@fayz-ai/portal": "^0.7.0",
|
|
78
|
+
"@fayz-ai/courses": "^0.7.0",
|
|
79
|
+
"@fayz-ai/db": "^0.1.2",
|
|
80
|
+
"@fayz-ai/plugin-agenda": "^0.4.0",
|
|
81
|
+
"@fayz-ai/plugin-auth": "^0.1.3",
|
|
82
|
+
"@fayz-ai/plugin-automations": "^0.2.3",
|
|
83
|
+
"@fayz-ai/plugin-blog": "^0.1.0",
|
|
84
|
+
"@fayz-ai/plugin-conversations": "^0.2.4",
|
|
85
|
+
"@fayz-ai/plugin-courses": "^0.2.3",
|
|
86
|
+
"@fayz-ai/plugin-crm": "^0.3.0",
|
|
87
|
+
"@fayz-ai/plugin-dashboard": "^0.1.6",
|
|
88
|
+
"@fayz-ai/plugin-financial": "^0.2.0",
|
|
89
|
+
"@fayz-ai/plugin-forms": "^0.2.0",
|
|
90
|
+
"@fayz-ai/plugin-inventory": "^0.1.7",
|
|
91
|
+
"@fayz-ai/plugin-marketing": "^0.2.1",
|
|
92
|
+
"@fayz-ai/plugin-menu": "^0.3.0",
|
|
93
|
+
"@fayz-ai/plugin-orders": "^0.3.0",
|
|
94
|
+
"@fayz-ai/plugin-payments": "^0.1.1",
|
|
95
|
+
"@fayz-ai/plugin-reports": "^0.2.0",
|
|
96
|
+
"@fayz-ai/plugin-reputation": "^0.2.4",
|
|
97
|
+
"@fayz-ai/plugin-shop": "^0.2.5",
|
|
98
|
+
"@fayz-ai/plugin-sites": "^0.2.3",
|
|
99
|
+
"@fayz-ai/plugin-tables": "^0.3.0",
|
|
100
|
+
"@fayz-ai/plugin-tasks": "^0.1.7"
|
|
11
101
|
}
|
|
12
102
|
}
|
|
13
103
|
}
|
package/src/shop.test.ts
CHANGED
|
@@ -11,7 +11,7 @@ function jsonResponse(body: unknown, status = 200): Response {
|
|
|
11
11
|
|
|
12
12
|
function productRow(overrides: Record<string, unknown> = {}) {
|
|
13
13
|
return {
|
|
14
|
-
id: '
|
|
14
|
+
id: '11111111-1111-4111-8111-000000000001',
|
|
15
15
|
tenant_id: 'store-1',
|
|
16
16
|
name: 'Runner Vortex Neon',
|
|
17
17
|
slug: 'runner-vortex-neon',
|
|
@@ -27,7 +27,7 @@ describe('createFayzShopProvider', () => {
|
|
|
27
27
|
it('uses the Fayz broker without provider credentials when apiBaseUrl and projectId are provided', async () => {
|
|
28
28
|
const fetcher = vi.fn<typeof fetch>().mockResolvedValue(jsonResponse({
|
|
29
29
|
products: [{
|
|
30
|
-
id: '
|
|
30
|
+
id: '11111111-1111-4111-8111-000000000001',
|
|
31
31
|
tenantId: 'store-1',
|
|
32
32
|
name: 'Aurora Tote',
|
|
33
33
|
slug: 'aurora-tote',
|
|
@@ -107,7 +107,7 @@ describe('createFayzShopProvider', () => {
|
|
|
107
107
|
fetcher,
|
|
108
108
|
})
|
|
109
109
|
|
|
110
|
-
const product = await provider.getProduct('
|
|
110
|
+
const product = await provider.getProduct('11111111-1111-4111-8111-000000000001')
|
|
111
111
|
|
|
112
112
|
expect(product?.metadata.sizes).toEqual(['40', '41'])
|
|
113
113
|
})
|
package/src/shop.ts
CHANGED
|
@@ -464,20 +464,24 @@ export function createFayzShopProvider(options: FayzShopProviderOptions) {
|
|
|
464
464
|
return {
|
|
465
465
|
async listProducts(options?: FayzShopListProductsOptions) {
|
|
466
466
|
const query = tenantQuery()
|
|
467
|
-
query.set('select', '*,images:
|
|
467
|
+
query.set('select', '*,images:plg_shop_product_images(*),category:plg_shop_categories(name)')
|
|
468
468
|
if (options?.status) query.set('status', `eq.${options.status}`)
|
|
469
469
|
if (options?.slug) query.set('slug', `eq.${options.slug}`)
|
|
470
470
|
if (options?.search) query.set('name', `ilike.*${options.search}*`)
|
|
471
471
|
query.set('order', `${options?.orderBy ?? 'sort_order'}.${options?.order ?? 'asc'}`)
|
|
472
472
|
if (options?.limit) query.set('limit', String(options.limit))
|
|
473
473
|
if (options?.offset) query.set('offset', String(options.offset))
|
|
474
|
-
const products = (await request<ProductRow[]>('
|
|
474
|
+
const products = (await request<ProductRow[]>('plg_shop_products', { query })).map((row) => rowToProduct(row, productMetadataOverlay))
|
|
475
475
|
return options?.categoryId ? products.filter((product) => product.categoryId === options.categoryId) : products
|
|
476
476
|
},
|
|
477
477
|
async getProduct(id: string) {
|
|
478
|
+
// Cart lines can carry non-uuid ids (mock catalog, or a cart persisted in
|
|
479
|
+
// an earlier mock-mode session). PostgREST would 400 on `id=eq.p-05`
|
|
480
|
+
// (invalid uuid), so treat any non-uuid id as "not found" instead.
|
|
481
|
+
if (!asUuid(id)) return null
|
|
478
482
|
const query = singleById(id)
|
|
479
|
-
query.set('select', '*,images:
|
|
480
|
-
const rows = await request<ProductRow[]>('
|
|
483
|
+
query.set('select', '*,images:plg_shop_product_images(*),category:plg_shop_categories(name)')
|
|
484
|
+
const rows = await request<ProductRow[]>('plg_shop_products', { query })
|
|
481
485
|
return rows[0] ? rowToProduct(rows[0], productMetadataOverlay) : null
|
|
482
486
|
},
|
|
483
487
|
async createProduct() {
|
|
@@ -499,14 +503,14 @@ export function createFayzShopProvider(options: FayzShopProviderOptions) {
|
|
|
499
503
|
const productQuery = tenantQuery()
|
|
500
504
|
productQuery.set('select', 'metadata')
|
|
501
505
|
productQuery.set('order', 'sort_order.asc')
|
|
502
|
-
const products = await request<ProductRow[]>('
|
|
506
|
+
const products = await request<ProductRow[]>('plg_shop_products', { query: productQuery })
|
|
503
507
|
const storeCategories = deriveStoreCategories(products, storeId)
|
|
504
508
|
if (storeCategories.length > 0) return storeCategories
|
|
505
509
|
|
|
506
510
|
const query = new URLSearchParams()
|
|
507
511
|
query.set('select', '*')
|
|
508
512
|
query.set('order', 'sort_order.asc')
|
|
509
|
-
return (await request<CategoryRow[]>('
|
|
513
|
+
return (await request<CategoryRow[]>('plg_shop_categories', { query })).map(rowToCategory)
|
|
510
514
|
},
|
|
511
515
|
async createCategory() {
|
|
512
516
|
throw new FayzShopError('Category writes require the Fayz admin/broker API.', 501)
|
|
@@ -519,7 +523,7 @@ export function createFayzShopProvider(options: FayzShopProviderOptions) {
|
|
|
519
523
|
},
|
|
520
524
|
async listOrders(options?: FayzShopListOrdersOptions) {
|
|
521
525
|
const query = tenantQuery()
|
|
522
|
-
query.set('select', '*,items:
|
|
526
|
+
query.set('select', '*,items:plg_shop_order_items(*)')
|
|
523
527
|
if (options?.status) query.set('status', `eq.${options.status}`)
|
|
524
528
|
if (options?.financialStatus) query.set('financial_status', `eq.${options.financialStatus}`)
|
|
525
529
|
if (options?.fulfillmentStatus) query.set('fulfillment_status', `eq.${options.fulfillmentStatus}`)
|
|
@@ -529,13 +533,42 @@ export function createFayzShopProvider(options: FayzShopProviderOptions) {
|
|
|
529
533
|
query.set('order', 'created_at.desc')
|
|
530
534
|
if (options?.limit) query.set('limit', String(options.limit))
|
|
531
535
|
if (options?.offset) query.set('offset', String(options.offset))
|
|
532
|
-
return (await request<OrderRow[]>('
|
|
536
|
+
return (await request<OrderRow[]>('plg_shop_orders', { query })).map(rowToOrder)
|
|
533
537
|
},
|
|
534
538
|
async getOrder(id: string) {
|
|
535
539
|
const query = singleById(id)
|
|
536
|
-
query.set('select', '*,items:
|
|
537
|
-
|
|
538
|
-
|
|
540
|
+
query.set('select', '*,items:plg_shop_order_items(*)')
|
|
541
|
+
// Anon storefronts have no table grant at all — the direct read throws
|
|
542
|
+
// (401) rather than returning empty, so it must not short-circuit the
|
|
543
|
+
// RPC fallback below.
|
|
544
|
+
let rows: OrderRow[] = []
|
|
545
|
+
try {
|
|
546
|
+
rows = await request<OrderRow[]>('plg_shop_orders', { query })
|
|
547
|
+
} catch {
|
|
548
|
+
/* fall through to shop_get_order */
|
|
549
|
+
}
|
|
550
|
+
if (rows[0]) return rowToOrder(rows[0])
|
|
551
|
+
// Anon storefronts can't SELECT plg_shop_orders (RLS); the order uuid acts
|
|
552
|
+
// as the read capability through the whitelisted RPC.
|
|
553
|
+
try {
|
|
554
|
+
const row = await request<OrderRow | null>('rpc/shop_get_order', {
|
|
555
|
+
method: 'POST',
|
|
556
|
+
body: JSON.stringify({ p_order_id: id }),
|
|
557
|
+
})
|
|
558
|
+
return row ? rowToOrder(row) : null
|
|
559
|
+
} catch {
|
|
560
|
+
return null
|
|
561
|
+
}
|
|
562
|
+
},
|
|
563
|
+
async confirmPayment(id: string, reference?: string) {
|
|
564
|
+
// Mock/dev payment seam: anon has no UPDATE grant on plg_shop_orders,
|
|
565
|
+
// so the pending->paid transition goes through the whitelisted RPC
|
|
566
|
+
// (order uuid = capability, same contract as shop_get_order).
|
|
567
|
+
const row = await request<OrderRow | null>('rpc/shop_confirm_payment', {
|
|
568
|
+
method: 'POST',
|
|
569
|
+
body: JSON.stringify({ p_order_id: id, p_reference: reference ?? null }),
|
|
570
|
+
})
|
|
571
|
+
return row ? rowToOrder(row) : null
|
|
539
572
|
},
|
|
540
573
|
async createOrder(input: {
|
|
541
574
|
customerId?: string
|
|
@@ -552,7 +585,7 @@ export function createFayzShopProvider(options: FayzShopProviderOptions) {
|
|
|
552
585
|
const discountTotal = input.discountTotal ?? 0
|
|
553
586
|
const shippingTotal = input.shippingTotal ?? 0
|
|
554
587
|
const total = Math.round((subtotal - discountTotal + shippingTotal) * 100) / 100
|
|
555
|
-
const [order] = await request<OrderRow[]>('
|
|
588
|
+
const [order] = await request<OrderRow[]>('plg_shop_orders', {
|
|
556
589
|
method: 'POST',
|
|
557
590
|
body: JSON.stringify({
|
|
558
591
|
tenant_id: storeId,
|
|
@@ -573,7 +606,7 @@ export function createFayzShopProvider(options: FayzShopProviderOptions) {
|
|
|
573
606
|
})
|
|
574
607
|
if (!order) throw new FayzShopError('Order insert did not return a row.', 500)
|
|
575
608
|
if (input.items.length) {
|
|
576
|
-
await request<OrderItemRow[]>('
|
|
609
|
+
await request<OrderItemRow[]>('plg_shop_order_items', {
|
|
577
610
|
method: 'POST',
|
|
578
611
|
body: JSON.stringify(input.items.map((item) => ({
|
|
579
612
|
order_id: order.id,
|
|
@@ -590,7 +623,7 @@ export function createFayzShopProvider(options: FayzShopProviderOptions) {
|
|
|
590
623
|
return this.getOrder(order.id)
|
|
591
624
|
},
|
|
592
625
|
async updateOrder(id: string, input: Parameters<typeof updateOrderPayload>[0]) {
|
|
593
|
-
await request<OrderRow[]>('
|
|
626
|
+
await request<OrderRow[]>('plg_shop_orders', {
|
|
594
627
|
method: 'PATCH',
|
|
595
628
|
query: singleById(id),
|
|
596
629
|
body: JSON.stringify(updateOrderPayload(input)),
|
|
@@ -607,50 +640,30 @@ export function createFayzShopProvider(options: FayzShopProviderOptions) {
|
|
|
607
640
|
shippingTotal?: number
|
|
608
641
|
items: Array<{ productId: string; quantity: number; optionsLabel?: string }>
|
|
609
642
|
}) {
|
|
610
|
-
// Trusted placement
|
|
611
|
-
//
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
const ts = nowIso()
|
|
631
|
-
const valid = !!discount && discount.status === 'active' && discount.startsAt <= ts
|
|
632
|
-
&& (!discount.endsAt || discount.endsAt >= ts)
|
|
633
|
-
&& (discount.usageLimit == null || discount.timesUsed < discount.usageLimit)
|
|
634
|
-
if (valid && discount) {
|
|
635
|
-
appliedCode = discount.code ?? undefined
|
|
636
|
-
if (discount.type === 'percentage') discountTotal = Math.round(subtotal * discount.value) / 100
|
|
637
|
-
else if (discount.type === 'fixed_amount') discountTotal = Math.min(discount.value, subtotal)
|
|
638
|
-
else if (discount.type === 'free_shipping') shippingTotal = 0
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
discountTotal = Math.round(Math.min(Math.max(discountTotal, 0), subtotal) * 100) / 100
|
|
642
|
-
|
|
643
|
-
return this.createOrder({
|
|
644
|
-
customerId: input.customerId,
|
|
645
|
-
customerName: input.customer?.name,
|
|
646
|
-
customerEmail: input.customer?.email,
|
|
647
|
-
currency: input.currency,
|
|
648
|
-
notes: input.notes,
|
|
649
|
-
discountCode: appliedCode,
|
|
650
|
-
discountTotal,
|
|
651
|
-
shippingTotal,
|
|
652
|
-
items: orderItems,
|
|
643
|
+
// Trusted placement: the shop_place_order SECURITY DEFINER RPC re-reads
|
|
644
|
+
// prices, validates discount/stock and writes order+items atomically.
|
|
645
|
+
// Direct INSERTs are blocked for anon by RLS on the shared FayzApi.
|
|
646
|
+
const orderId = await request<string>('rpc/shop_place_order', {
|
|
647
|
+
method: 'POST',
|
|
648
|
+
body: JSON.stringify({
|
|
649
|
+
p_tenant_id: input.tenantId ?? storeId,
|
|
650
|
+
p_items: input.items.map((line) => ({
|
|
651
|
+
product_id: line.productId,
|
|
652
|
+
quantity: line.quantity,
|
|
653
|
+
options_label: line.optionsLabel ?? null,
|
|
654
|
+
})),
|
|
655
|
+
p_customer_id: asUuid(input.customerId ?? undefined),
|
|
656
|
+
p_customer_name: input.customer?.name ?? null,
|
|
657
|
+
p_customer_email: input.customer?.email ?? null,
|
|
658
|
+
p_currency: input.currency ?? 'BRL',
|
|
659
|
+
p_discount_code: input.discountCode ?? null,
|
|
660
|
+
p_shipping_total: input.shippingTotal ?? 0,
|
|
661
|
+
p_notes: input.notes ?? null,
|
|
662
|
+
}),
|
|
653
663
|
})
|
|
664
|
+
const order = await this.getOrder(orderId)
|
|
665
|
+
if (!order) throw new FayzShopError('Order was placed but could not be read back.', 500)
|
|
666
|
+
return order
|
|
654
667
|
},
|
|
655
668
|
async listCustomers(options?: FayzShopListCustomersOptions) {
|
|
656
669
|
const query = tenantQuery()
|
|
@@ -659,14 +672,27 @@ export function createFayzShopProvider(options: FayzShopProviderOptions) {
|
|
|
659
672
|
if (options?.search) query.set('or', `(first_name.ilike.*${options.search}*,last_name.ilike.*${options.search}*,email.ilike.*${options.search}*)`)
|
|
660
673
|
if (options?.limit) query.set('limit', String(options.limit))
|
|
661
674
|
if (options?.offset) query.set('offset', String(options.offset))
|
|
662
|
-
return (await request<CustomerRow[]>('
|
|
675
|
+
return (await request<CustomerRow[]>('plg_shop_customers', { query })).map(rowToCustomer)
|
|
663
676
|
},
|
|
664
677
|
async getCustomer(id: string) {
|
|
665
|
-
const rows = await request<CustomerRow[]>('
|
|
678
|
+
const rows = await request<CustomerRow[]>('plg_shop_customers', { query: singleById(id) })
|
|
666
679
|
return rows[0] ? rowToCustomer(rows[0]) : null
|
|
667
680
|
},
|
|
681
|
+
async resolveCustomer(input: { email: string; name?: string }) {
|
|
682
|
+
const row = await request<CustomerRow | CustomerRow[]>('rpc/shop_resolve_customer', {
|
|
683
|
+
method: 'POST',
|
|
684
|
+
body: JSON.stringify({
|
|
685
|
+
p_tenant_id: storeId,
|
|
686
|
+
p_email: input.email,
|
|
687
|
+
p_name: input.name ?? null,
|
|
688
|
+
}),
|
|
689
|
+
})
|
|
690
|
+
const customer = Array.isArray(row) ? row[0] : row
|
|
691
|
+
if (!customer) throw new FayzShopError('shop_resolve_customer returned no row.', 500)
|
|
692
|
+
return rowToCustomer(customer)
|
|
693
|
+
},
|
|
668
694
|
async createCustomer(input: { firstName: string; lastName?: string; email?: string; phone?: string; notes?: string }) {
|
|
669
|
-
const [customer] = await request<CustomerRow[]>('
|
|
695
|
+
const [customer] = await request<CustomerRow[]>('plg_shop_customers', {
|
|
670
696
|
method: 'POST',
|
|
671
697
|
body: JSON.stringify({
|
|
672
698
|
tenant_id: storeId,
|
|
@@ -687,7 +713,7 @@ export function createFayzShopProvider(options: FayzShopProviderOptions) {
|
|
|
687
713
|
if (input.email !== undefined) updates.email = input.email
|
|
688
714
|
if (input.phone !== undefined) updates.phone = input.phone
|
|
689
715
|
if (input.notes !== undefined) updates.notes = input.notes
|
|
690
|
-
const [customer] = await request<CustomerRow[]>('
|
|
716
|
+
const [customer] = await request<CustomerRow[]>('plg_shop_customers', {
|
|
691
717
|
method: 'PATCH',
|
|
692
718
|
query: singleById(id),
|
|
693
719
|
body: JSON.stringify(updates),
|
|
@@ -696,7 +722,7 @@ export function createFayzShopProvider(options: FayzShopProviderOptions) {
|
|
|
696
722
|
return rowToCustomer(customer)
|
|
697
723
|
},
|
|
698
724
|
async deleteCustomer(id: string) {
|
|
699
|
-
await request<unknown>('
|
|
725
|
+
await request<unknown>('plg_shop_customers', { method: 'DELETE', query: singleById(id) })
|
|
700
726
|
},
|
|
701
727
|
async listDiscounts(options?: FayzShopListDiscountsOptions) {
|
|
702
728
|
const query = tenantQuery()
|
|
@@ -707,10 +733,10 @@ export function createFayzShopProvider(options: FayzShopProviderOptions) {
|
|
|
707
733
|
if (options?.search) query.set('title', `ilike.*${options.search}*`)
|
|
708
734
|
if (options?.limit) query.set('limit', String(options.limit))
|
|
709
735
|
if (options?.offset) query.set('offset', String(options.offset))
|
|
710
|
-
return (await request<DiscountRow[]>('
|
|
736
|
+
return (await request<DiscountRow[]>('plg_shop_discounts', { query })).map(rowToDiscount)
|
|
711
737
|
},
|
|
712
738
|
async getDiscount(id: string) {
|
|
713
|
-
const rows = await request<DiscountRow[]>('
|
|
739
|
+
const rows = await request<DiscountRow[]>('plg_shop_discounts', { query: singleById(id) })
|
|
714
740
|
return rows[0] ? rowToDiscount(rows[0]) : null
|
|
715
741
|
},
|
|
716
742
|
async createDiscount() {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"surfaceVersion": 1,
|
|
3
|
-
"description": "Packages a generated Fayz app may depend on. fayz doctor warns on any app dependency outside this list. See docs/
|
|
3
|
+
"description": "Packages a generated Fayz app may depend on. fayz doctor warns on any app dependency outside this list. See docs/ARCHITECTURE.md (boundary model).",
|
|
4
4
|
"tiers": {
|
|
5
5
|
"stable": "semver-tracked, supported public API"
|
|
6
6
|
},
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"@fayz-ai/core": "stable",
|
|
10
10
|
"@fayz-ai/courses": "stable",
|
|
11
11
|
"@fayz-ai/db": "stable",
|
|
12
|
+
"@fayz-ai/portal": "stable",
|
|
12
13
|
"@fayz-ai/saas": "stable",
|
|
13
14
|
"@fayz-ai/sdk": "stable",
|
|
14
15
|
"@fayz-ai/shop": "stable",
|
|
@@ -34,7 +35,6 @@
|
|
|
34
35
|
"@fayz-ai/plugin-tasks": "stable"
|
|
35
36
|
},
|
|
36
37
|
"internal": [
|
|
37
|
-
"@fayz-ai/app-runtime"
|
|
38
|
-
"@fayz-ai/portal"
|
|
38
|
+
"@fayz-ai/app-runtime"
|
|
39
39
|
]
|
|
40
40
|
}
|
package/src/vite.ts
CHANGED
|
@@ -10,8 +10,8 @@ const SDK_PACKAGES = [
|
|
|
10
10
|
] as const
|
|
11
11
|
|
|
12
12
|
const SDK_PLUGINS = [
|
|
13
|
-
'agenda', 'automations', 'conversations', 'courses', 'crm', 'dashboard',
|
|
14
|
-
'financial', 'forms', 'inventory', 'marketing', 'menu', 'orders', 'reports',
|
|
13
|
+
'agenda', 'auth', 'automations', 'blog', 'conversations', 'courses', 'crm', 'dashboard',
|
|
14
|
+
'financial', 'forms', 'inventory', 'marketing', 'menu', 'orders', 'payments', 'reports',
|
|
15
15
|
'reputation', 'shop', 'sites', 'tables', 'tasks',
|
|
16
16
|
] as const
|
|
17
17
|
|