@fayz-ai/core 0.7.0 → 0.7.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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/seo/index.ts"],"names":[],"mappings":"AAeA,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAA;IAC5C,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,mFAAmF;IACnF,MAAM,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;CACxC;AAED,8CAA8C;AAC9C,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAED,mEAAmE;AACnE,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEhD;AA8BD,oFAAoF;AACpF,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA0BhD;AAED,uEAAuE;AACvE,wBAAgB,MAAM,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAO9C;AAID,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;CACZ;AAED,gEAAgE;AAChE,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,cAAc,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAWjF;AAED,+CAA+C;AAC/C,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEzG"}
@@ -0,0 +1,38 @@
1
+ export type PaymentMethod = 'pix' | (string & {});
2
+ export type ChargeStatus = 'pending' | 'paid' | 'expired' | 'failed';
3
+ export interface CreateChargeInput {
4
+ /** Amount in the currency's major unit (e.g. 220.00 = R$ 220,00). */
5
+ amount: number;
6
+ /** ISO 4217 currency code, e.g. 'BRL'. */
7
+ currency: string;
8
+ method: PaymentMethod;
9
+ /** Human-readable description (statement/receipt). */
10
+ description?: string;
11
+ /** The booking/order this charge settles, for reconciliation. */
12
+ orderId?: string;
13
+ /** Payer contact, when known (name/email/phone). */
14
+ customer?: {
15
+ name?: string;
16
+ email?: string;
17
+ phone?: string;
18
+ };
19
+ }
20
+ export interface PixCharge {
21
+ chargeId: string;
22
+ status: ChargeStatus;
23
+ amount: number;
24
+ currency: string;
25
+ /** QR image payload. For Pix this is the BR Code string encoded into the QR. */
26
+ pixQrCode: string;
27
+ /** "Copia e cola" — the raw Pix BR Code the payer pastes into their bank app. */
28
+ pixCopyPaste: string;
29
+ /** ISO datetime the charge/QR expires. */
30
+ expiresAt: string;
31
+ }
32
+ export interface PaymentProvider {
33
+ /** Create a charge and return its payable artifacts (Pix QR + copia-e-cola). */
34
+ createCharge(input: CreateChargeInput): Promise<PixCharge>;
35
+ /** Current status of a charge (polled by the UI, or driven by a webhook later). */
36
+ getChargeStatus(chargeId: string): Promise<ChargeStatus>;
37
+ }
38
+ //# sourceMappingURL=payment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"payment.d.ts","sourceRoot":"","sources":["../../src/types/payment.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAA;AAEjD,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,CAAA;AAEpE,MAAM,WAAW,iBAAiB;IAChC,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAA;IACd,0CAA0C;IAC1C,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,aAAa,CAAA;IACrB,sDAAsD;IACtD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,iEAAiE;IACjE,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,oDAAoD;IACpD,QAAQ,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CAC7D;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,YAAY,CAAA;IACpB,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;IAChB,gFAAgF;IAChF,SAAS,EAAE,MAAM,CAAA;IACjB,iFAAiF;IACjF,YAAY,EAAE,MAAM,CAAA;IACpB,0CAA0C;IAC1C,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,gFAAgF;IAChF,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,SAAS,CAAC,CAAA;IAC1D,mFAAmF;IACnF,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;CACzD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fayz-ai/core",
3
- "version": "0.7.0",
3
+ "version": "0.7.1",
4
4
  "description": "Fayz SDK core — data providers, entity system, plugin runtime, i18n, routing",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -79,7 +79,7 @@
79
79
  "dependencies": {
80
80
  "@supabase/supabase-js": "^2.45.0",
81
81
  "zustand": "^4.5.0",
82
- "@fayz-ai/sdk": "^0.6.5"
82
+ "@fayz-ai/sdk": "^0.6.7"
83
83
  },
84
84
  "devDependencies": {
85
85
  "@types/react": "^18.3.0",
package/src/index.ts CHANGED
@@ -18,6 +18,17 @@ export { createArchetypeProvider } from './data/archetype'
18
18
  export { withCache } from './data/cached'
19
19
  export { resolveDataProvider } from './data/resolve'
20
20
  export { createSafeDataProvider } from './plugin/createSafeDataProvider'
21
+ export {
22
+ COUNTRIES, DEFAULT_COUNTRY, getCountry, unmaskPhone, maskPhone, maskDigitCount,
23
+ } from './phone'
24
+ export type { CountryDef } from './phone'
25
+ export type {
26
+ PaymentProvider, PaymentMethod, ChargeStatus, CreateChargeInput, PixCharge,
27
+ } from './types/payment'
28
+ export {
29
+ useSeo, applySeo, absoluteUrl, seoOrigin, breadcrumbJsonLd, organizationJsonLd,
30
+ } from './seo'
31
+ export type { SeoConfig, BreadcrumbItem } from './seo'
21
32
  export type { DataProvider, CrudQuery, CrudResult, SupabaseProviderConfig, FayzApiProviderConfig } from './data/index'
22
33
 
23
34
  // Tenant context (runtime DI for the data layer)
@@ -0,0 +1,85 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Reusable phone / country helpers (SDK-level — shared by any plugin that
3
+ // collects a phone number: booking, CRM, forms, auth…). Provides the country
4
+ // list (flag + dial code + national mask) and mask/unmask utilities.
5
+ // ---------------------------------------------------------------------------
6
+
7
+ export interface CountryDef {
8
+ /** ISO 3166-1 alpha-2 code, e.g. 'BR'. */
9
+ iso2: string
10
+ /** Localized-ish display name. */
11
+ name: string
12
+ /** International dial code including '+', e.g. '+55'. */
13
+ dial: string
14
+ /** Flag emoji (derived from iso2). */
15
+ flag: string
16
+ /** National number mask; '#' = one digit. e.g. '(##) #####-####'. */
17
+ mask: string
18
+ }
19
+
20
+ /** Derive a flag emoji from an ISO2 code via regional indicator symbols. */
21
+ function flagOf(iso2: string): string {
22
+ return iso2
23
+ .toUpperCase()
24
+ .replace(/[^A-Z]/g, '')
25
+ .split('')
26
+ .map((c) => String.fromCodePoint(0x1f1e6 + c.charCodeAt(0) - 65))
27
+ .join('')
28
+ }
29
+
30
+ const RAW: Omit<CountryDef, 'flag'>[] = [
31
+ { iso2: 'BR', name: 'Brasil', dial: '+55', mask: '(##) #####-####' },
32
+ { iso2: 'US', name: 'Estados Unidos', dial: '+1', mask: '(###) ###-####' },
33
+ { iso2: 'PT', name: 'Portugal', dial: '+351', mask: '### ### ###' },
34
+ { iso2: 'AR', name: 'Argentina', dial: '+54', mask: '(##) ####-####' },
35
+ { iso2: 'ES', name: 'Espanha', dial: '+34', mask: '### ### ###' },
36
+ { iso2: 'MX', name: 'México', dial: '+52', mask: '## #### ####' },
37
+ { iso2: 'GB', name: 'Reino Unido', dial: '+44', mask: '##### ######' },
38
+ { iso2: 'FR', name: 'França', dial: '+33', mask: '# ## ## ## ##' },
39
+ { iso2: 'DE', name: 'Alemanha', dial: '+49', mask: '#### #######' },
40
+ { iso2: 'IT', name: 'Itália', dial: '+39', mask: '### #######' },
41
+ ]
42
+
43
+ export const COUNTRIES: CountryDef[] = RAW.map((c) => ({ ...c, flag: flagOf(c.iso2) }))
44
+
45
+ export const DEFAULT_COUNTRY = 'BR'
46
+
47
+ const BY_ISO = new Map(COUNTRIES.map((c) => [c.iso2, c]))
48
+
49
+ /** Look up a country by ISO2 (falls back to the default). */
50
+ export function getCountry(iso2: string | undefined): CountryDef {
51
+ return (iso2 && BY_ISO.get(iso2.toUpperCase())) || BY_ISO.get(DEFAULT_COUNTRY)!
52
+ }
53
+
54
+ /** Keep only digit characters. */
55
+ export function unmaskPhone(value: string): string {
56
+ return (value || '').replace(/\D/g, '')
57
+ }
58
+
59
+ /**
60
+ * Apply a '#'-placeholder mask to a raw string, consuming its digits. Extra
61
+ * digits beyond the mask are dropped; partial input masks progressively.
62
+ *
63
+ * maskPhone('11987654321', '(##) #####-####') -> '(11) 98765-4321'
64
+ */
65
+ export function maskPhone(value: string, mask: string): string {
66
+ const digits = unmaskPhone(value)
67
+ let out = ''
68
+ let di = 0
69
+ for (const ch of mask) {
70
+ if (di >= digits.length) break
71
+ if (ch === '#') {
72
+ out += digits[di++]
73
+ } else {
74
+ out += ch
75
+ }
76
+ }
77
+ return out
78
+ }
79
+
80
+ /** Count how many digits a mask expects. */
81
+ export function maskDigitCount(mask: string): number {
82
+ let n = 0
83
+ for (const ch of mask) if (ch === '#') n++
84
+ return n
85
+ }
@@ -0,0 +1,128 @@
1
+ import { useEffect } from 'react'
2
+
3
+ // ---------------------------------------------------------------------------
4
+ // Generic, domain-agnostic SEO / structured-data head manager (SDK-level).
5
+ // ANY surface (blog, booking, reviews, product, landing) uses `useSeo` to set
6
+ // <title>, meta description, canonical, OpenGraph/Twitter cards and JSON-LD per
7
+ // route. Domain plugins build their own schema.org objects (BlogPosting,
8
+ // Product, Event…) and pass them via `jsonLd` — the mechanics live here once.
9
+ //
10
+ // NOTE: a SPA still benefits from SSR/prerendering for the strongest SEO — that
11
+ // is the real upgrade; this covers everything achievable client-side.
12
+ // ---------------------------------------------------------------------------
13
+
14
+ const MANAGED = 'data-fayz-seo'
15
+
16
+ export interface SeoConfig {
17
+ title: string
18
+ description?: string
19
+ canonical?: string
20
+ image?: string
21
+ type?: 'website' | 'article' | (string & {})
22
+ siteName?: string
23
+ /** One or more JSON-LD objects injected as <script type="application/ld+json">. */
24
+ jsonLd?: Array<Record<string, unknown>>
25
+ }
26
+
27
+ /** Current page origin (empty during SSR). */
28
+ export function seoOrigin(): string {
29
+ return typeof window !== 'undefined' ? window.location.origin : ''
30
+ }
31
+
32
+ /** Build an absolute URL from a path, using the current origin. */
33
+ export function absoluteUrl(path: string): string {
34
+ return `${seoOrigin()}${path.startsWith('/') ? path : `/${path}`}`
35
+ }
36
+
37
+ function upsertMeta(attr: 'name' | 'property', key: string, content: string) {
38
+ if (!content) return
39
+ let el = document.head.querySelector<HTMLMetaElement>(`meta[${attr}="${key}"]`)
40
+ if (!el) {
41
+ el = document.createElement('meta')
42
+ el.setAttribute(attr, key)
43
+ el.setAttribute(MANAGED, '')
44
+ document.head.appendChild(el)
45
+ }
46
+ el.setAttribute('content', content)
47
+ }
48
+
49
+ function upsertLink(rel: string, href: string) {
50
+ if (!href) return
51
+ let el = document.head.querySelector<HTMLLinkElement>(`link[rel="${rel}"]`)
52
+ if (!el) {
53
+ el = document.createElement('link')
54
+ el.setAttribute('rel', rel)
55
+ el.setAttribute(MANAGED, '')
56
+ document.head.appendChild(el)
57
+ }
58
+ el.setAttribute('href', href)
59
+ }
60
+
61
+ function clearManaged() {
62
+ document.head.querySelectorAll(`[${MANAGED}]`).forEach((el) => el.remove())
63
+ }
64
+
65
+ /** Imperatively apply SEO tags (used by useSeo; exported for non-React callers). */
66
+ export function applySeo(config: SeoConfig): void {
67
+ if (typeof document === 'undefined') return
68
+ clearManaged()
69
+ document.title = config.title
70
+ upsertMeta('name', 'description', config.description ?? '')
71
+ if (config.canonical) upsertLink('canonical', config.canonical)
72
+
73
+ upsertMeta('property', 'og:title', config.title)
74
+ upsertMeta('property', 'og:description', config.description ?? '')
75
+ upsertMeta('property', 'og:type', config.type === 'article' ? 'article' : 'website')
76
+ if (config.canonical) upsertMeta('property', 'og:url', config.canonical)
77
+ if (config.image) upsertMeta('property', 'og:image', config.image)
78
+ if (config.siteName) upsertMeta('property', 'og:site_name', config.siteName)
79
+
80
+ upsertMeta('name', 'twitter:card', config.image ? 'summary_large_image' : 'summary')
81
+ upsertMeta('name', 'twitter:title', config.title)
82
+ upsertMeta('name', 'twitter:description', config.description ?? '')
83
+ if (config.image) upsertMeta('name', 'twitter:image', config.image)
84
+
85
+ for (const obj of config.jsonLd ?? []) {
86
+ const script = document.createElement('script')
87
+ script.type = 'application/ld+json'
88
+ script.setAttribute(MANAGED, '')
89
+ script.text = JSON.stringify(obj)
90
+ document.head.appendChild(script)
91
+ }
92
+ }
93
+
94
+ /** Apply SEO head tags for the current route; cleans up on unmount. */
95
+ export function useSeo(config: SeoConfig): void {
96
+ const key = JSON.stringify(config)
97
+ useEffect(() => {
98
+ applySeo(config)
99
+ return () => clearManaged()
100
+ // eslint-disable-next-line react-hooks/exhaustive-deps
101
+ }, [key])
102
+ }
103
+
104
+ // --- Generic schema.org builders (reusable across plugins) -----------------
105
+
106
+ export interface BreadcrumbItem {
107
+ name: string
108
+ url: string
109
+ }
110
+
111
+ /** schema.org BreadcrumbList from an ordered list of crumbs. */
112
+ export function breadcrumbJsonLd(items: BreadcrumbItem[]): Record<string, unknown> {
113
+ return {
114
+ '@context': 'https://schema.org',
115
+ '@type': 'BreadcrumbList',
116
+ itemListElement: items.map((c, i) => ({
117
+ '@type': 'ListItem',
118
+ position: i + 1,
119
+ name: c.name,
120
+ item: c.url,
121
+ })),
122
+ }
123
+ }
124
+
125
+ /** schema.org Organization publisher block. */
126
+ export function organizationJsonLd(name: string, extra?: Record<string, unknown>): Record<string, unknown> {
127
+ return { '@type': 'Organization', name, ...extra }
128
+ }
@@ -0,0 +1,44 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Payment provider contract (SDK-level, gateway-agnostic). A booking/checkout
3
+ // flow declares "collect X" and calls createCharge; the concrete provider (mock
4
+ // Pix now, MercadoPago/Stripe later) decides HOW. Kept in core so any plugin can
5
+ // depend on the interface without depending on a payments plugin.
6
+ // ---------------------------------------------------------------------------
7
+
8
+ export type PaymentMethod = 'pix' | (string & {})
9
+
10
+ export type ChargeStatus = 'pending' | 'paid' | 'expired' | 'failed'
11
+
12
+ export interface CreateChargeInput {
13
+ /** Amount in the currency's major unit (e.g. 220.00 = R$ 220,00). */
14
+ amount: number
15
+ /** ISO 4217 currency code, e.g. 'BRL'. */
16
+ currency: string
17
+ method: PaymentMethod
18
+ /** Human-readable description (statement/receipt). */
19
+ description?: string
20
+ /** The booking/order this charge settles, for reconciliation. */
21
+ orderId?: string
22
+ /** Payer contact, when known (name/email/phone). */
23
+ customer?: { name?: string; email?: string; phone?: string }
24
+ }
25
+
26
+ export interface PixCharge {
27
+ chargeId: string
28
+ status: ChargeStatus
29
+ amount: number
30
+ currency: string
31
+ /** QR image payload. For Pix this is the BR Code string encoded into the QR. */
32
+ pixQrCode: string
33
+ /** "Copia e cola" — the raw Pix BR Code the payer pastes into their bank app. */
34
+ pixCopyPaste: string
35
+ /** ISO datetime the charge/QR expires. */
36
+ expiresAt: string
37
+ }
38
+
39
+ export interface PaymentProvider {
40
+ /** Create a charge and return its payable artifacts (Pix QR + copia-e-cola). */
41
+ createCharge(input: CreateChargeInput): Promise<PixCharge>
42
+ /** Current status of a charge (polled by the UI, or driven by a webhook later). */
43
+ getChargeStatus(chargeId: string): Promise<ChargeStatus>
44
+ }