@neetru/sdk 1.0.0 → 1.1.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/CHANGELOG.md +32 -1
- package/dist/auth.cjs +177 -1
- package/dist/auth.cjs.map +1 -1
- package/dist/auth.d.cts +1 -1
- package/dist/auth.d.ts +1 -1
- package/dist/auth.mjs +177 -1
- package/dist/auth.mjs.map +1 -1
- package/dist/catalog.d.cts +1 -1
- package/dist/catalog.d.ts +1 -1
- package/dist/checkout.cjs +275 -0
- package/dist/checkout.cjs.map +1 -0
- package/dist/checkout.d.cts +1 -0
- package/dist/checkout.d.ts +1 -0
- package/dist/checkout.mjs +272 -0
- package/dist/checkout.mjs.map +1 -0
- package/dist/db.d.cts +1 -1
- package/dist/db.d.ts +1 -1
- package/dist/entitlements.d.cts +1 -1
- package/dist/entitlements.d.ts +1 -1
- package/dist/index.cjs +180 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.mjs +179 -3
- package/dist/index.mjs.map +1 -1
- package/dist/mocks.d.cts +1 -1
- package/dist/mocks.d.ts +1 -1
- package/dist/react.cjs +158 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.d.cts +112 -0
- package/dist/react.d.ts +112 -0
- package/dist/react.mjs +134 -0
- package/dist/react.mjs.map +1 -0
- package/dist/support.d.cts +1 -1
- package/dist/support.d.ts +1 -1
- package/dist/telemetry.d.cts +1 -1
- package/dist/telemetry.d.ts +1 -1
- package/dist/{types-PKUaFtBY.d.cts → types-BA53dd8S.d.cts} +83 -1
- package/dist/{types-PKUaFtBY.d.ts → types-BA53dd8S.d.ts} +83 -1
- package/dist/usage.d.cts +1 -1
- package/dist/usage.d.ts +1 -1
- package/package.json +18 -2
package/dist/mocks.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { p as NeetruUser, A as AuthNamespace, S as SignInOptions, b as AuthStateListener, m as DbNamespace, k as DbCollectionRef, E as EntitlementCheck, s as SupportNamespace, j as CreateTicketInput, v as SupportTicket, x as UsageNamespace, y as UsageQuota } from './types-BA53dd8S.cjs';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Mocks determinísticos do SDK — overridáveis em tests do consumer ou
|
package/dist/mocks.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { p as NeetruUser, A as AuthNamespace, S as SignInOptions, b as AuthStateListener, m as DbNamespace, k as DbCollectionRef, E as EntitlementCheck, s as SupportNamespace, j as CreateTicketInput, v as SupportTicket, x as UsageNamespace, y as UsageQuota } from './types-BA53dd8S.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Mocks determinísticos do SDK — overridáveis em tests do consumer ou
|
package/dist/react.cjs
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var React = require('react');
|
|
4
|
+
|
|
5
|
+
function _interopNamespace(e) {
|
|
6
|
+
if (e && e.__esModule) return e;
|
|
7
|
+
var n = Object.create(null);
|
|
8
|
+
if (e) {
|
|
9
|
+
Object.keys(e).forEach(function (k) {
|
|
10
|
+
if (k !== 'default') {
|
|
11
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
12
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
13
|
+
enumerable: true,
|
|
14
|
+
get: function () { return e[k]; }
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
n.default = e;
|
|
20
|
+
return Object.freeze(n);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
var React__namespace = /*#__PURE__*/_interopNamespace(React);
|
|
24
|
+
|
|
25
|
+
// src/react.ts
|
|
26
|
+
function CheckoutLink({
|
|
27
|
+
client,
|
|
28
|
+
productId,
|
|
29
|
+
planId,
|
|
30
|
+
callbackUrl,
|
|
31
|
+
tenantType,
|
|
32
|
+
tenantId,
|
|
33
|
+
children,
|
|
34
|
+
onIntentCreated,
|
|
35
|
+
onError,
|
|
36
|
+
noAutoRedirect,
|
|
37
|
+
...rest
|
|
38
|
+
}) {
|
|
39
|
+
const [submitting, setSubmitting] = React__namespace.useState(false);
|
|
40
|
+
const checkout = client.checkout;
|
|
41
|
+
const handleClick = React__namespace.useCallback(
|
|
42
|
+
async (event) => {
|
|
43
|
+
event.preventDefault();
|
|
44
|
+
if (submitting) return;
|
|
45
|
+
if (!checkout || typeof checkout.start !== "function") {
|
|
46
|
+
onError?.(new Error("SDK client missing `checkout` namespace. Upgrade @neetru/sdk to 1.1+."));
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
setSubmitting(true);
|
|
50
|
+
try {
|
|
51
|
+
const result = await checkout.start({
|
|
52
|
+
productId,
|
|
53
|
+
planId,
|
|
54
|
+
callbackUrl,
|
|
55
|
+
tenantType,
|
|
56
|
+
tenantId,
|
|
57
|
+
autoRedirect: !noAutoRedirect
|
|
58
|
+
});
|
|
59
|
+
onIntentCreated?.(result);
|
|
60
|
+
} catch (err) {
|
|
61
|
+
onError?.(err instanceof Error ? err : new Error(String(err)));
|
|
62
|
+
} finally {
|
|
63
|
+
setSubmitting(false);
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
[submitting, checkout, productId, planId, callbackUrl, tenantType, tenantId, noAutoRedirect, onIntentCreated, onError]
|
|
67
|
+
);
|
|
68
|
+
return React__namespace.createElement(
|
|
69
|
+
"a",
|
|
70
|
+
{
|
|
71
|
+
...rest,
|
|
72
|
+
href: rest.href ?? "#",
|
|
73
|
+
onClick: handleClick,
|
|
74
|
+
"aria-busy": submitting || void 0,
|
|
75
|
+
"data-neetru-checkout-link": "true"
|
|
76
|
+
},
|
|
77
|
+
children
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
var EntitlementContext = React__namespace.createContext({
|
|
81
|
+
readonly: false
|
|
82
|
+
});
|
|
83
|
+
function useEntitlementContext() {
|
|
84
|
+
return React__namespace.useContext(EntitlementContext);
|
|
85
|
+
}
|
|
86
|
+
function EntitlementGate({
|
|
87
|
+
client,
|
|
88
|
+
feature,
|
|
89
|
+
productSlug,
|
|
90
|
+
mode = "block",
|
|
91
|
+
fallback,
|
|
92
|
+
loading,
|
|
93
|
+
children
|
|
94
|
+
}) {
|
|
95
|
+
const [state, setState] = React__namespace.useState({
|
|
96
|
+
status: "loading"
|
|
97
|
+
});
|
|
98
|
+
const resolvedSlug = productSlug ?? client.config.productId ?? "";
|
|
99
|
+
React__namespace.useEffect(() => {
|
|
100
|
+
let cancelled = false;
|
|
101
|
+
const run = async () => {
|
|
102
|
+
try {
|
|
103
|
+
let result2;
|
|
104
|
+
const usage = client.usage;
|
|
105
|
+
if (usage && typeof usage.check === "function") {
|
|
106
|
+
const res = await usage.check(feature, { productId: resolvedSlug });
|
|
107
|
+
result2 = {
|
|
108
|
+
allowed: res.allowed === true,
|
|
109
|
+
reason: res.reason,
|
|
110
|
+
limit: res.limit,
|
|
111
|
+
currentUsage: res.currentUsage,
|
|
112
|
+
behavior: res.behavior
|
|
113
|
+
};
|
|
114
|
+
} else {
|
|
115
|
+
const allowed2 = await client.entitlements.check(resolvedSlug, feature);
|
|
116
|
+
result2 = { allowed: allowed2 };
|
|
117
|
+
}
|
|
118
|
+
if (!cancelled) setState({ status: "ready", result: result2 });
|
|
119
|
+
} catch (err) {
|
|
120
|
+
if (!cancelled) {
|
|
121
|
+
setState({ status: "ready", result: { allowed: false, reason: "check_failed" } });
|
|
122
|
+
}
|
|
123
|
+
if (typeof console !== "undefined") console.warn("[EntitlementGate] check failed", err);
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
run();
|
|
127
|
+
return () => {
|
|
128
|
+
cancelled = true;
|
|
129
|
+
};
|
|
130
|
+
}, [client, feature, resolvedSlug]);
|
|
131
|
+
if (state.status === "loading") {
|
|
132
|
+
return React__namespace.createElement(React__namespace.Fragment, null, loading ?? null);
|
|
133
|
+
}
|
|
134
|
+
const result = state.result;
|
|
135
|
+
const allowed = result?.allowed === true;
|
|
136
|
+
const isReadonly = !allowed && (mode === "readonly" || result?.behavior === "readonly");
|
|
137
|
+
if (!allowed && mode === "block" && !isReadonly) {
|
|
138
|
+
return React__namespace.createElement(React__namespace.Fragment, null, fallback ?? null);
|
|
139
|
+
}
|
|
140
|
+
return React__namespace.createElement(
|
|
141
|
+
EntitlementContext.Provider,
|
|
142
|
+
{
|
|
143
|
+
value: {
|
|
144
|
+
readonly: isReadonly,
|
|
145
|
+
reason: result?.reason,
|
|
146
|
+
limit: result?.limit,
|
|
147
|
+
currentUsage: result?.currentUsage
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
children
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
exports.CheckoutLink = CheckoutLink;
|
|
155
|
+
exports.EntitlementGate = EntitlementGate;
|
|
156
|
+
exports.useEntitlementContext = useEntitlementContext;
|
|
157
|
+
//# sourceMappingURL=react.cjs.map
|
|
158
|
+
//# sourceMappingURL=react.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/react.ts"],"names":["React","result","allowed"],"mappings":"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","file":"react.cjs","sourcesContent":["/**\n * SDK React helpers (v1.1) — subpath import `@neetru/sdk/react`.\n *\n * Helpers leves pra React 18+/19. Não puxa `react` como dep direta — caller\n * fornece via peer (alinhado com `@stripe/react-stripe-js` / `firebase`).\n *\n * Exports:\n * - `<CheckoutLink>` — wrapper de `<a>` que chama `client.checkout.start()` no click.\n * - `<EntitlementGate>` — wrapper que gateia children por entitlement check\n * (suporte a `mode='readonly'` pro decisão CEO §5).\n * - `useEntitlementContext()` — hook pra ler estado de readonly do contexto.\n *\n * **Bundle**: este file SÓ é carregado por quem importa `@neetru/sdk/react`.\n * Tree-shake garante que SDK consumers Node-only (CLI, edge handlers) não\n * puxam React mesmo via transitive.\n */\nimport * as React from 'react';\nimport type { NeetruClient } from './types';\nimport type { CheckoutStartInput, CheckoutStartResult } from './checkout';\n\n// ─── <CheckoutLink> ─────────────────────────────────────────────────────────\n\nexport interface CheckoutLinkProps\n extends Omit<\n React.AnchorHTMLAttributes<HTMLAnchorElement>,\n 'href' | 'onClick' | 'onError'\n > {\n /** Cliente SDK Neetru. Passado explicitamente pra evitar context provider. */\n client: NeetruClient;\n /** Slug do produto (ex: `neetru-pulse`). */\n productId: string;\n /** Plan id (ex: `pro_monthly`). */\n planId: string;\n /** Pra onde o produto SaaS quer voltar pós-checkout. */\n callbackUrl: string;\n /** Override pra checkout em nome de uma org. Default: PF do uid logado. */\n tenantType?: 'pf' | 'pj';\n tenantId?: string;\n /** Children — texto/JSX do CTA. */\n children: React.ReactNode;\n /** Callback quando intent foi criada (antes de redirect). */\n onIntentCreated?: (result: CheckoutStartResult) => void;\n /** Callback de erro. */\n onError?: (err: Error) => void;\n /** Quando true, NÃO redireciona automaticamente — caller controla. */\n noAutoRedirect?: boolean;\n /** Override de href (default: `#`). */\n href?: string;\n}\n\n/**\n * Wrapper de `<a>` que dispara `client.checkout.start()` no click.\n *\n * Uso típico:\n * ```tsx\n * <CheckoutLink\n * client={client}\n * productId=\"meu-saas\"\n * planId=\"pro_monthly\"\n * callbackUrl=\"https://meu-saas.com/billing/success\"\n * >\n * Assinar Pro\n * </CheckoutLink>\n * ```\n *\n * `href` é stub (`#`) — preventDefault no click. Mantido pra a11y / preview\n * de URL no hover (pode ser sobrescrito via prop spread).\n */\nexport function CheckoutLink({\n client,\n productId,\n planId,\n callbackUrl,\n tenantType,\n tenantId,\n children,\n onIntentCreated,\n onError,\n noAutoRedirect,\n ...rest\n}: CheckoutLinkProps): React.ReactElement {\n const [submitting, setSubmitting] = React.useState(false);\n\n // Lazy access — `client.checkout` foi adicionado em SDK 1.1, fall back gracioso\n // se caller passar um client antigo (mesmo que types resolvam, runtime check).\n const checkout = (client as { checkout?: { start: (input: CheckoutStartInput) => Promise<CheckoutStartResult> } }).checkout;\n\n const handleClick = React.useCallback(\n async (event: React.MouseEvent<HTMLAnchorElement>) => {\n event.preventDefault();\n if (submitting) return;\n if (!checkout || typeof checkout.start !== 'function') {\n onError?.(new Error('SDK client missing `checkout` namespace. Upgrade @neetru/sdk to 1.1+.'));\n return;\n }\n setSubmitting(true);\n try {\n const result = await checkout.start({\n productId,\n planId,\n callbackUrl,\n tenantType,\n tenantId,\n autoRedirect: !noAutoRedirect,\n });\n onIntentCreated?.(result);\n } catch (err) {\n onError?.(err instanceof Error ? err : new Error(String(err)));\n } finally {\n setSubmitting(false);\n }\n },\n [submitting, checkout, productId, planId, callbackUrl, tenantType, tenantId, noAutoRedirect, onIntentCreated, onError],\n );\n\n return React.createElement(\n 'a',\n {\n ...rest,\n href: rest.href ?? '#',\n onClick: handleClick,\n 'aria-busy': submitting || undefined,\n 'data-neetru-checkout-link': 'true',\n },\n children,\n );\n}\n\n// ─── <EntitlementGate> + useEntitlementContext ────────────────────────────\n\n/** Modo de gate quando entitlement não é permitido. */\nexport type EntitlementGateMode = 'block' | 'readonly';\n\ninterface EntitlementContextValue {\n /** True quando feature está em modo read-only (limit exceeded). */\n readonly: boolean;\n /** Reason code da última check. */\n reason?: string;\n /** Limite atual (free tier). */\n limit?: number;\n /** Uso atual. */\n currentUsage?: number;\n}\n\nconst EntitlementContext = React.createContext<EntitlementContextValue>({\n readonly: false,\n});\n\n/** Hook pra ler estado de readonly. Componentes filhos consomem isso pra\n * desabilitar inputs/botões de escrita. */\nexport function useEntitlementContext(): EntitlementContextValue {\n return React.useContext(EntitlementContext);\n}\n\nexport interface EntitlementGateProps {\n /** Cliente SDK Neetru. */\n client: NeetruClient;\n /** Feature key a checar (ex: `export.csv`). */\n feature: string;\n /** Slug do produto. Default: lê do client.config se ausente. */\n productSlug?: string;\n /** Modo:\n * - `block` (default): renderiza fallback quando não permitido.\n * - `readonly`: sempre renderiza children mas injeta `readonly: true` no contexto. */\n mode?: EntitlementGateMode;\n /** Fallback UI quando `mode='block'` e check retorna `allowed=false`. */\n fallback?: React.ReactNode;\n /** Loading UI enquanto check resolve (Suspense-like). */\n loading?: React.ReactNode;\n children: React.ReactNode;\n}\n\ninterface EntitlementCheckResult {\n allowed: boolean;\n reason?: string;\n limit?: number;\n currentUsage?: number;\n behavior?: 'readonly' | null;\n}\n\n/**\n * Wrapper que gateia children por entitlement check.\n *\n * Quando `mode='readonly'` + check retorna `behavior='readonly'`:\n * - Children renderizam normalmente.\n * - Context injeta `{readonly: true}`.\n * - Componentes filhos chamam `useEntitlementContext()` pra desabilitar\n * escritas (botões, forms, inputs).\n *\n * Quando `mode='block'` + check retorna `allowed=false`:\n * - `fallback` é renderizado (ex: paywall, upsell modal).\n *\n * Decisão CEO §5 (overage): plataforma usa `readonly` sempre — produto SaaS\n * deve preferir `mode='readonly'` pra preservar engajamento (não hard-block).\n */\nexport function EntitlementGate({\n client,\n feature,\n productSlug,\n mode = 'block',\n fallback,\n loading,\n children,\n}: EntitlementGateProps): React.ReactElement {\n const [state, setState] = React.useState<{ status: 'loading' | 'ready'; result?: EntitlementCheckResult }>({\n status: 'loading',\n });\n\n // Resolve productSlug from prop or client.config\n const resolvedSlug =\n productSlug ?? (client.config as { productId?: string }).productId ?? '';\n\n React.useEffect(() => {\n let cancelled = false;\n const run = async () => {\n try {\n // Try usage.check first (returns full {allowed, reason, behavior, ...})\n // Falls back to entitlements.check (just boolean) if usage.check unavailable.\n let result: EntitlementCheckResult;\n const usage = (client as { usage?: { check: (resource: string, opts?: { productId?: string }) => Promise<EntitlementCheckResult> } }).usage;\n if (usage && typeof usage.check === 'function') {\n const res = await usage.check(feature, { productId: resolvedSlug });\n result = {\n allowed: res.allowed === true,\n reason: res.reason,\n limit: res.limit,\n currentUsage: res.currentUsage,\n behavior: res.behavior,\n };\n } else {\n const allowed = await client.entitlements.check(resolvedSlug, feature);\n result = { allowed };\n }\n if (!cancelled) setState({ status: 'ready', result });\n } catch (err) {\n // Fail open in dev/prod — caller pode decidir via onError pattern futuro.\n if (!cancelled) {\n setState({ status: 'ready', result: { allowed: false, reason: 'check_failed' } });\n }\n if (typeof console !== 'undefined') console.warn('[EntitlementGate] check failed', err);\n }\n };\n run();\n return () => {\n cancelled = true;\n };\n }, [client, feature, resolvedSlug]);\n\n if (state.status === 'loading') {\n return React.createElement(React.Fragment, null, loading ?? null);\n }\n\n const result = state.result;\n const allowed = result?.allowed === true;\n const isReadonly = !allowed && (mode === 'readonly' || result?.behavior === 'readonly');\n\n if (!allowed && mode === 'block' && !isReadonly) {\n return React.createElement(React.Fragment, null, fallback ?? null);\n }\n\n return React.createElement(\n EntitlementContext.Provider,\n {\n value: {\n readonly: isReadonly,\n reason: result?.reason,\n limit: result?.limit,\n currentUsage: result?.currentUsage,\n },\n },\n children,\n );\n}\n"]}
|
package/dist/react.d.cts
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { a as NeetruClient, h as CheckoutStartResult } from './types-BA53dd8S.cjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* SDK React helpers (v1.1) — subpath import `@neetru/sdk/react`.
|
|
6
|
+
*
|
|
7
|
+
* Helpers leves pra React 18+/19. Não puxa `react` como dep direta — caller
|
|
8
|
+
* fornece via peer (alinhado com `@stripe/react-stripe-js` / `firebase`).
|
|
9
|
+
*
|
|
10
|
+
* Exports:
|
|
11
|
+
* - `<CheckoutLink>` — wrapper de `<a>` que chama `client.checkout.start()` no click.
|
|
12
|
+
* - `<EntitlementGate>` — wrapper que gateia children por entitlement check
|
|
13
|
+
* (suporte a `mode='readonly'` pro decisão CEO §5).
|
|
14
|
+
* - `useEntitlementContext()` — hook pra ler estado de readonly do contexto.
|
|
15
|
+
*
|
|
16
|
+
* **Bundle**: este file SÓ é carregado por quem importa `@neetru/sdk/react`.
|
|
17
|
+
* Tree-shake garante que SDK consumers Node-only (CLI, edge handlers) não
|
|
18
|
+
* puxam React mesmo via transitive.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
interface CheckoutLinkProps extends Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'href' | 'onClick' | 'onError'> {
|
|
22
|
+
/** Cliente SDK Neetru. Passado explicitamente pra evitar context provider. */
|
|
23
|
+
client: NeetruClient;
|
|
24
|
+
/** Slug do produto (ex: `neetru-pulse`). */
|
|
25
|
+
productId: string;
|
|
26
|
+
/** Plan id (ex: `pro_monthly`). */
|
|
27
|
+
planId: string;
|
|
28
|
+
/** Pra onde o produto SaaS quer voltar pós-checkout. */
|
|
29
|
+
callbackUrl: string;
|
|
30
|
+
/** Override pra checkout em nome de uma org. Default: PF do uid logado. */
|
|
31
|
+
tenantType?: 'pf' | 'pj';
|
|
32
|
+
tenantId?: string;
|
|
33
|
+
/** Children — texto/JSX do CTA. */
|
|
34
|
+
children: React.ReactNode;
|
|
35
|
+
/** Callback quando intent foi criada (antes de redirect). */
|
|
36
|
+
onIntentCreated?: (result: CheckoutStartResult) => void;
|
|
37
|
+
/** Callback de erro. */
|
|
38
|
+
onError?: (err: Error) => void;
|
|
39
|
+
/** Quando true, NÃO redireciona automaticamente — caller controla. */
|
|
40
|
+
noAutoRedirect?: boolean;
|
|
41
|
+
/** Override de href (default: `#`). */
|
|
42
|
+
href?: string;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Wrapper de `<a>` que dispara `client.checkout.start()` no click.
|
|
46
|
+
*
|
|
47
|
+
* Uso típico:
|
|
48
|
+
* ```tsx
|
|
49
|
+
* <CheckoutLink
|
|
50
|
+
* client={client}
|
|
51
|
+
* productId="meu-saas"
|
|
52
|
+
* planId="pro_monthly"
|
|
53
|
+
* callbackUrl="https://meu-saas.com/billing/success"
|
|
54
|
+
* >
|
|
55
|
+
* Assinar Pro
|
|
56
|
+
* </CheckoutLink>
|
|
57
|
+
* ```
|
|
58
|
+
*
|
|
59
|
+
* `href` é stub (`#`) — preventDefault no click. Mantido pra a11y / preview
|
|
60
|
+
* de URL no hover (pode ser sobrescrito via prop spread).
|
|
61
|
+
*/
|
|
62
|
+
declare function CheckoutLink({ client, productId, planId, callbackUrl, tenantType, tenantId, children, onIntentCreated, onError, noAutoRedirect, ...rest }: CheckoutLinkProps): React.ReactElement;
|
|
63
|
+
/** Modo de gate quando entitlement não é permitido. */
|
|
64
|
+
type EntitlementGateMode = 'block' | 'readonly';
|
|
65
|
+
interface EntitlementContextValue {
|
|
66
|
+
/** True quando feature está em modo read-only (limit exceeded). */
|
|
67
|
+
readonly: boolean;
|
|
68
|
+
/** Reason code da última check. */
|
|
69
|
+
reason?: string;
|
|
70
|
+
/** Limite atual (free tier). */
|
|
71
|
+
limit?: number;
|
|
72
|
+
/** Uso atual. */
|
|
73
|
+
currentUsage?: number;
|
|
74
|
+
}
|
|
75
|
+
/** Hook pra ler estado de readonly. Componentes filhos consomem isso pra
|
|
76
|
+
* desabilitar inputs/botões de escrita. */
|
|
77
|
+
declare function useEntitlementContext(): EntitlementContextValue;
|
|
78
|
+
interface EntitlementGateProps {
|
|
79
|
+
/** Cliente SDK Neetru. */
|
|
80
|
+
client: NeetruClient;
|
|
81
|
+
/** Feature key a checar (ex: `export.csv`). */
|
|
82
|
+
feature: string;
|
|
83
|
+
/** Slug do produto. Default: lê do client.config se ausente. */
|
|
84
|
+
productSlug?: string;
|
|
85
|
+
/** Modo:
|
|
86
|
+
* - `block` (default): renderiza fallback quando não permitido.
|
|
87
|
+
* - `readonly`: sempre renderiza children mas injeta `readonly: true` no contexto. */
|
|
88
|
+
mode?: EntitlementGateMode;
|
|
89
|
+
/** Fallback UI quando `mode='block'` e check retorna `allowed=false`. */
|
|
90
|
+
fallback?: React.ReactNode;
|
|
91
|
+
/** Loading UI enquanto check resolve (Suspense-like). */
|
|
92
|
+
loading?: React.ReactNode;
|
|
93
|
+
children: React.ReactNode;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Wrapper que gateia children por entitlement check.
|
|
97
|
+
*
|
|
98
|
+
* Quando `mode='readonly'` + check retorna `behavior='readonly'`:
|
|
99
|
+
* - Children renderizam normalmente.
|
|
100
|
+
* - Context injeta `{readonly: true}`.
|
|
101
|
+
* - Componentes filhos chamam `useEntitlementContext()` pra desabilitar
|
|
102
|
+
* escritas (botões, forms, inputs).
|
|
103
|
+
*
|
|
104
|
+
* Quando `mode='block'` + check retorna `allowed=false`:
|
|
105
|
+
* - `fallback` é renderizado (ex: paywall, upsell modal).
|
|
106
|
+
*
|
|
107
|
+
* Decisão CEO §5 (overage): plataforma usa `readonly` sempre — produto SaaS
|
|
108
|
+
* deve preferir `mode='readonly'` pra preservar engajamento (não hard-block).
|
|
109
|
+
*/
|
|
110
|
+
declare function EntitlementGate({ client, feature, productSlug, mode, fallback, loading, children, }: EntitlementGateProps): React.ReactElement;
|
|
111
|
+
|
|
112
|
+
export { CheckoutLink, type CheckoutLinkProps, EntitlementGate, type EntitlementGateMode, type EntitlementGateProps, useEntitlementContext };
|
package/dist/react.d.ts
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { a as NeetruClient, h as CheckoutStartResult } from './types-BA53dd8S.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* SDK React helpers (v1.1) — subpath import `@neetru/sdk/react`.
|
|
6
|
+
*
|
|
7
|
+
* Helpers leves pra React 18+/19. Não puxa `react` como dep direta — caller
|
|
8
|
+
* fornece via peer (alinhado com `@stripe/react-stripe-js` / `firebase`).
|
|
9
|
+
*
|
|
10
|
+
* Exports:
|
|
11
|
+
* - `<CheckoutLink>` — wrapper de `<a>` que chama `client.checkout.start()` no click.
|
|
12
|
+
* - `<EntitlementGate>` — wrapper que gateia children por entitlement check
|
|
13
|
+
* (suporte a `mode='readonly'` pro decisão CEO §5).
|
|
14
|
+
* - `useEntitlementContext()` — hook pra ler estado de readonly do contexto.
|
|
15
|
+
*
|
|
16
|
+
* **Bundle**: este file SÓ é carregado por quem importa `@neetru/sdk/react`.
|
|
17
|
+
* Tree-shake garante que SDK consumers Node-only (CLI, edge handlers) não
|
|
18
|
+
* puxam React mesmo via transitive.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
interface CheckoutLinkProps extends Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'href' | 'onClick' | 'onError'> {
|
|
22
|
+
/** Cliente SDK Neetru. Passado explicitamente pra evitar context provider. */
|
|
23
|
+
client: NeetruClient;
|
|
24
|
+
/** Slug do produto (ex: `neetru-pulse`). */
|
|
25
|
+
productId: string;
|
|
26
|
+
/** Plan id (ex: `pro_monthly`). */
|
|
27
|
+
planId: string;
|
|
28
|
+
/** Pra onde o produto SaaS quer voltar pós-checkout. */
|
|
29
|
+
callbackUrl: string;
|
|
30
|
+
/** Override pra checkout em nome de uma org. Default: PF do uid logado. */
|
|
31
|
+
tenantType?: 'pf' | 'pj';
|
|
32
|
+
tenantId?: string;
|
|
33
|
+
/** Children — texto/JSX do CTA. */
|
|
34
|
+
children: React.ReactNode;
|
|
35
|
+
/** Callback quando intent foi criada (antes de redirect). */
|
|
36
|
+
onIntentCreated?: (result: CheckoutStartResult) => void;
|
|
37
|
+
/** Callback de erro. */
|
|
38
|
+
onError?: (err: Error) => void;
|
|
39
|
+
/** Quando true, NÃO redireciona automaticamente — caller controla. */
|
|
40
|
+
noAutoRedirect?: boolean;
|
|
41
|
+
/** Override de href (default: `#`). */
|
|
42
|
+
href?: string;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Wrapper de `<a>` que dispara `client.checkout.start()` no click.
|
|
46
|
+
*
|
|
47
|
+
* Uso típico:
|
|
48
|
+
* ```tsx
|
|
49
|
+
* <CheckoutLink
|
|
50
|
+
* client={client}
|
|
51
|
+
* productId="meu-saas"
|
|
52
|
+
* planId="pro_monthly"
|
|
53
|
+
* callbackUrl="https://meu-saas.com/billing/success"
|
|
54
|
+
* >
|
|
55
|
+
* Assinar Pro
|
|
56
|
+
* </CheckoutLink>
|
|
57
|
+
* ```
|
|
58
|
+
*
|
|
59
|
+
* `href` é stub (`#`) — preventDefault no click. Mantido pra a11y / preview
|
|
60
|
+
* de URL no hover (pode ser sobrescrito via prop spread).
|
|
61
|
+
*/
|
|
62
|
+
declare function CheckoutLink({ client, productId, planId, callbackUrl, tenantType, tenantId, children, onIntentCreated, onError, noAutoRedirect, ...rest }: CheckoutLinkProps): React.ReactElement;
|
|
63
|
+
/** Modo de gate quando entitlement não é permitido. */
|
|
64
|
+
type EntitlementGateMode = 'block' | 'readonly';
|
|
65
|
+
interface EntitlementContextValue {
|
|
66
|
+
/** True quando feature está em modo read-only (limit exceeded). */
|
|
67
|
+
readonly: boolean;
|
|
68
|
+
/** Reason code da última check. */
|
|
69
|
+
reason?: string;
|
|
70
|
+
/** Limite atual (free tier). */
|
|
71
|
+
limit?: number;
|
|
72
|
+
/** Uso atual. */
|
|
73
|
+
currentUsage?: number;
|
|
74
|
+
}
|
|
75
|
+
/** Hook pra ler estado de readonly. Componentes filhos consomem isso pra
|
|
76
|
+
* desabilitar inputs/botões de escrita. */
|
|
77
|
+
declare function useEntitlementContext(): EntitlementContextValue;
|
|
78
|
+
interface EntitlementGateProps {
|
|
79
|
+
/** Cliente SDK Neetru. */
|
|
80
|
+
client: NeetruClient;
|
|
81
|
+
/** Feature key a checar (ex: `export.csv`). */
|
|
82
|
+
feature: string;
|
|
83
|
+
/** Slug do produto. Default: lê do client.config se ausente. */
|
|
84
|
+
productSlug?: string;
|
|
85
|
+
/** Modo:
|
|
86
|
+
* - `block` (default): renderiza fallback quando não permitido.
|
|
87
|
+
* - `readonly`: sempre renderiza children mas injeta `readonly: true` no contexto. */
|
|
88
|
+
mode?: EntitlementGateMode;
|
|
89
|
+
/** Fallback UI quando `mode='block'` e check retorna `allowed=false`. */
|
|
90
|
+
fallback?: React.ReactNode;
|
|
91
|
+
/** Loading UI enquanto check resolve (Suspense-like). */
|
|
92
|
+
loading?: React.ReactNode;
|
|
93
|
+
children: React.ReactNode;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Wrapper que gateia children por entitlement check.
|
|
97
|
+
*
|
|
98
|
+
* Quando `mode='readonly'` + check retorna `behavior='readonly'`:
|
|
99
|
+
* - Children renderizam normalmente.
|
|
100
|
+
* - Context injeta `{readonly: true}`.
|
|
101
|
+
* - Componentes filhos chamam `useEntitlementContext()` pra desabilitar
|
|
102
|
+
* escritas (botões, forms, inputs).
|
|
103
|
+
*
|
|
104
|
+
* Quando `mode='block'` + check retorna `allowed=false`:
|
|
105
|
+
* - `fallback` é renderizado (ex: paywall, upsell modal).
|
|
106
|
+
*
|
|
107
|
+
* Decisão CEO §5 (overage): plataforma usa `readonly` sempre — produto SaaS
|
|
108
|
+
* deve preferir `mode='readonly'` pra preservar engajamento (não hard-block).
|
|
109
|
+
*/
|
|
110
|
+
declare function EntitlementGate({ client, feature, productSlug, mode, fallback, loading, children, }: EntitlementGateProps): React.ReactElement;
|
|
111
|
+
|
|
112
|
+
export { CheckoutLink, type CheckoutLinkProps, EntitlementGate, type EntitlementGateMode, type EntitlementGateProps, useEntitlementContext };
|
package/dist/react.mjs
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
// src/react.ts
|
|
4
|
+
function CheckoutLink({
|
|
5
|
+
client,
|
|
6
|
+
productId,
|
|
7
|
+
planId,
|
|
8
|
+
callbackUrl,
|
|
9
|
+
tenantType,
|
|
10
|
+
tenantId,
|
|
11
|
+
children,
|
|
12
|
+
onIntentCreated,
|
|
13
|
+
onError,
|
|
14
|
+
noAutoRedirect,
|
|
15
|
+
...rest
|
|
16
|
+
}) {
|
|
17
|
+
const [submitting, setSubmitting] = React.useState(false);
|
|
18
|
+
const checkout = client.checkout;
|
|
19
|
+
const handleClick = React.useCallback(
|
|
20
|
+
async (event) => {
|
|
21
|
+
event.preventDefault();
|
|
22
|
+
if (submitting) return;
|
|
23
|
+
if (!checkout || typeof checkout.start !== "function") {
|
|
24
|
+
onError?.(new Error("SDK client missing `checkout` namespace. Upgrade @neetru/sdk to 1.1+."));
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
setSubmitting(true);
|
|
28
|
+
try {
|
|
29
|
+
const result = await checkout.start({
|
|
30
|
+
productId,
|
|
31
|
+
planId,
|
|
32
|
+
callbackUrl,
|
|
33
|
+
tenantType,
|
|
34
|
+
tenantId,
|
|
35
|
+
autoRedirect: !noAutoRedirect
|
|
36
|
+
});
|
|
37
|
+
onIntentCreated?.(result);
|
|
38
|
+
} catch (err) {
|
|
39
|
+
onError?.(err instanceof Error ? err : new Error(String(err)));
|
|
40
|
+
} finally {
|
|
41
|
+
setSubmitting(false);
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
[submitting, checkout, productId, planId, callbackUrl, tenantType, tenantId, noAutoRedirect, onIntentCreated, onError]
|
|
45
|
+
);
|
|
46
|
+
return React.createElement(
|
|
47
|
+
"a",
|
|
48
|
+
{
|
|
49
|
+
...rest,
|
|
50
|
+
href: rest.href ?? "#",
|
|
51
|
+
onClick: handleClick,
|
|
52
|
+
"aria-busy": submitting || void 0,
|
|
53
|
+
"data-neetru-checkout-link": "true"
|
|
54
|
+
},
|
|
55
|
+
children
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
var EntitlementContext = React.createContext({
|
|
59
|
+
readonly: false
|
|
60
|
+
});
|
|
61
|
+
function useEntitlementContext() {
|
|
62
|
+
return React.useContext(EntitlementContext);
|
|
63
|
+
}
|
|
64
|
+
function EntitlementGate({
|
|
65
|
+
client,
|
|
66
|
+
feature,
|
|
67
|
+
productSlug,
|
|
68
|
+
mode = "block",
|
|
69
|
+
fallback,
|
|
70
|
+
loading,
|
|
71
|
+
children
|
|
72
|
+
}) {
|
|
73
|
+
const [state, setState] = React.useState({
|
|
74
|
+
status: "loading"
|
|
75
|
+
});
|
|
76
|
+
const resolvedSlug = productSlug ?? client.config.productId ?? "";
|
|
77
|
+
React.useEffect(() => {
|
|
78
|
+
let cancelled = false;
|
|
79
|
+
const run = async () => {
|
|
80
|
+
try {
|
|
81
|
+
let result2;
|
|
82
|
+
const usage = client.usage;
|
|
83
|
+
if (usage && typeof usage.check === "function") {
|
|
84
|
+
const res = await usage.check(feature, { productId: resolvedSlug });
|
|
85
|
+
result2 = {
|
|
86
|
+
allowed: res.allowed === true,
|
|
87
|
+
reason: res.reason,
|
|
88
|
+
limit: res.limit,
|
|
89
|
+
currentUsage: res.currentUsage,
|
|
90
|
+
behavior: res.behavior
|
|
91
|
+
};
|
|
92
|
+
} else {
|
|
93
|
+
const allowed2 = await client.entitlements.check(resolvedSlug, feature);
|
|
94
|
+
result2 = { allowed: allowed2 };
|
|
95
|
+
}
|
|
96
|
+
if (!cancelled) setState({ status: "ready", result: result2 });
|
|
97
|
+
} catch (err) {
|
|
98
|
+
if (!cancelled) {
|
|
99
|
+
setState({ status: "ready", result: { allowed: false, reason: "check_failed" } });
|
|
100
|
+
}
|
|
101
|
+
if (typeof console !== "undefined") console.warn("[EntitlementGate] check failed", err);
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
run();
|
|
105
|
+
return () => {
|
|
106
|
+
cancelled = true;
|
|
107
|
+
};
|
|
108
|
+
}, [client, feature, resolvedSlug]);
|
|
109
|
+
if (state.status === "loading") {
|
|
110
|
+
return React.createElement(React.Fragment, null, loading ?? null);
|
|
111
|
+
}
|
|
112
|
+
const result = state.result;
|
|
113
|
+
const allowed = result?.allowed === true;
|
|
114
|
+
const isReadonly = !allowed && (mode === "readonly" || result?.behavior === "readonly");
|
|
115
|
+
if (!allowed && mode === "block" && !isReadonly) {
|
|
116
|
+
return React.createElement(React.Fragment, null, fallback ?? null);
|
|
117
|
+
}
|
|
118
|
+
return React.createElement(
|
|
119
|
+
EntitlementContext.Provider,
|
|
120
|
+
{
|
|
121
|
+
value: {
|
|
122
|
+
readonly: isReadonly,
|
|
123
|
+
reason: result?.reason,
|
|
124
|
+
limit: result?.limit,
|
|
125
|
+
currentUsage: result?.currentUsage
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
children
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export { CheckoutLink, EntitlementGate, useEntitlementContext };
|
|
133
|
+
//# sourceMappingURL=react.mjs.map
|
|
134
|
+
//# sourceMappingURL=react.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/react.ts"],"names":["result","allowed"],"mappings":"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","file":"react.mjs","sourcesContent":["/**\n * SDK React helpers (v1.1) — subpath import `@neetru/sdk/react`.\n *\n * Helpers leves pra React 18+/19. Não puxa `react` como dep direta — caller\n * fornece via peer (alinhado com `@stripe/react-stripe-js` / `firebase`).\n *\n * Exports:\n * - `<CheckoutLink>` — wrapper de `<a>` que chama `client.checkout.start()` no click.\n * - `<EntitlementGate>` — wrapper que gateia children por entitlement check\n * (suporte a `mode='readonly'` pro decisão CEO §5).\n * - `useEntitlementContext()` — hook pra ler estado de readonly do contexto.\n *\n * **Bundle**: este file SÓ é carregado por quem importa `@neetru/sdk/react`.\n * Tree-shake garante que SDK consumers Node-only (CLI, edge handlers) não\n * puxam React mesmo via transitive.\n */\nimport * as React from 'react';\nimport type { NeetruClient } from './types';\nimport type { CheckoutStartInput, CheckoutStartResult } from './checkout';\n\n// ─── <CheckoutLink> ─────────────────────────────────────────────────────────\n\nexport interface CheckoutLinkProps\n extends Omit<\n React.AnchorHTMLAttributes<HTMLAnchorElement>,\n 'href' | 'onClick' | 'onError'\n > {\n /** Cliente SDK Neetru. Passado explicitamente pra evitar context provider. */\n client: NeetruClient;\n /** Slug do produto (ex: `neetru-pulse`). */\n productId: string;\n /** Plan id (ex: `pro_monthly`). */\n planId: string;\n /** Pra onde o produto SaaS quer voltar pós-checkout. */\n callbackUrl: string;\n /** Override pra checkout em nome de uma org. Default: PF do uid logado. */\n tenantType?: 'pf' | 'pj';\n tenantId?: string;\n /** Children — texto/JSX do CTA. */\n children: React.ReactNode;\n /** Callback quando intent foi criada (antes de redirect). */\n onIntentCreated?: (result: CheckoutStartResult) => void;\n /** Callback de erro. */\n onError?: (err: Error) => void;\n /** Quando true, NÃO redireciona automaticamente — caller controla. */\n noAutoRedirect?: boolean;\n /** Override de href (default: `#`). */\n href?: string;\n}\n\n/**\n * Wrapper de `<a>` que dispara `client.checkout.start()` no click.\n *\n * Uso típico:\n * ```tsx\n * <CheckoutLink\n * client={client}\n * productId=\"meu-saas\"\n * planId=\"pro_monthly\"\n * callbackUrl=\"https://meu-saas.com/billing/success\"\n * >\n * Assinar Pro\n * </CheckoutLink>\n * ```\n *\n * `href` é stub (`#`) — preventDefault no click. Mantido pra a11y / preview\n * de URL no hover (pode ser sobrescrito via prop spread).\n */\nexport function CheckoutLink({\n client,\n productId,\n planId,\n callbackUrl,\n tenantType,\n tenantId,\n children,\n onIntentCreated,\n onError,\n noAutoRedirect,\n ...rest\n}: CheckoutLinkProps): React.ReactElement {\n const [submitting, setSubmitting] = React.useState(false);\n\n // Lazy access — `client.checkout` foi adicionado em SDK 1.1, fall back gracioso\n // se caller passar um client antigo (mesmo que types resolvam, runtime check).\n const checkout = (client as { checkout?: { start: (input: CheckoutStartInput) => Promise<CheckoutStartResult> } }).checkout;\n\n const handleClick = React.useCallback(\n async (event: React.MouseEvent<HTMLAnchorElement>) => {\n event.preventDefault();\n if (submitting) return;\n if (!checkout || typeof checkout.start !== 'function') {\n onError?.(new Error('SDK client missing `checkout` namespace. Upgrade @neetru/sdk to 1.1+.'));\n return;\n }\n setSubmitting(true);\n try {\n const result = await checkout.start({\n productId,\n planId,\n callbackUrl,\n tenantType,\n tenantId,\n autoRedirect: !noAutoRedirect,\n });\n onIntentCreated?.(result);\n } catch (err) {\n onError?.(err instanceof Error ? err : new Error(String(err)));\n } finally {\n setSubmitting(false);\n }\n },\n [submitting, checkout, productId, planId, callbackUrl, tenantType, tenantId, noAutoRedirect, onIntentCreated, onError],\n );\n\n return React.createElement(\n 'a',\n {\n ...rest,\n href: rest.href ?? '#',\n onClick: handleClick,\n 'aria-busy': submitting || undefined,\n 'data-neetru-checkout-link': 'true',\n },\n children,\n );\n}\n\n// ─── <EntitlementGate> + useEntitlementContext ────────────────────────────\n\n/** Modo de gate quando entitlement não é permitido. */\nexport type EntitlementGateMode = 'block' | 'readonly';\n\ninterface EntitlementContextValue {\n /** True quando feature está em modo read-only (limit exceeded). */\n readonly: boolean;\n /** Reason code da última check. */\n reason?: string;\n /** Limite atual (free tier). */\n limit?: number;\n /** Uso atual. */\n currentUsage?: number;\n}\n\nconst EntitlementContext = React.createContext<EntitlementContextValue>({\n readonly: false,\n});\n\n/** Hook pra ler estado de readonly. Componentes filhos consomem isso pra\n * desabilitar inputs/botões de escrita. */\nexport function useEntitlementContext(): EntitlementContextValue {\n return React.useContext(EntitlementContext);\n}\n\nexport interface EntitlementGateProps {\n /** Cliente SDK Neetru. */\n client: NeetruClient;\n /** Feature key a checar (ex: `export.csv`). */\n feature: string;\n /** Slug do produto. Default: lê do client.config se ausente. */\n productSlug?: string;\n /** Modo:\n * - `block` (default): renderiza fallback quando não permitido.\n * - `readonly`: sempre renderiza children mas injeta `readonly: true` no contexto. */\n mode?: EntitlementGateMode;\n /** Fallback UI quando `mode='block'` e check retorna `allowed=false`. */\n fallback?: React.ReactNode;\n /** Loading UI enquanto check resolve (Suspense-like). */\n loading?: React.ReactNode;\n children: React.ReactNode;\n}\n\ninterface EntitlementCheckResult {\n allowed: boolean;\n reason?: string;\n limit?: number;\n currentUsage?: number;\n behavior?: 'readonly' | null;\n}\n\n/**\n * Wrapper que gateia children por entitlement check.\n *\n * Quando `mode='readonly'` + check retorna `behavior='readonly'`:\n * - Children renderizam normalmente.\n * - Context injeta `{readonly: true}`.\n * - Componentes filhos chamam `useEntitlementContext()` pra desabilitar\n * escritas (botões, forms, inputs).\n *\n * Quando `mode='block'` + check retorna `allowed=false`:\n * - `fallback` é renderizado (ex: paywall, upsell modal).\n *\n * Decisão CEO §5 (overage): plataforma usa `readonly` sempre — produto SaaS\n * deve preferir `mode='readonly'` pra preservar engajamento (não hard-block).\n */\nexport function EntitlementGate({\n client,\n feature,\n productSlug,\n mode = 'block',\n fallback,\n loading,\n children,\n}: EntitlementGateProps): React.ReactElement {\n const [state, setState] = React.useState<{ status: 'loading' | 'ready'; result?: EntitlementCheckResult }>({\n status: 'loading',\n });\n\n // Resolve productSlug from prop or client.config\n const resolvedSlug =\n productSlug ?? (client.config as { productId?: string }).productId ?? '';\n\n React.useEffect(() => {\n let cancelled = false;\n const run = async () => {\n try {\n // Try usage.check first (returns full {allowed, reason, behavior, ...})\n // Falls back to entitlements.check (just boolean) if usage.check unavailable.\n let result: EntitlementCheckResult;\n const usage = (client as { usage?: { check: (resource: string, opts?: { productId?: string }) => Promise<EntitlementCheckResult> } }).usage;\n if (usage && typeof usage.check === 'function') {\n const res = await usage.check(feature, { productId: resolvedSlug });\n result = {\n allowed: res.allowed === true,\n reason: res.reason,\n limit: res.limit,\n currentUsage: res.currentUsage,\n behavior: res.behavior,\n };\n } else {\n const allowed = await client.entitlements.check(resolvedSlug, feature);\n result = { allowed };\n }\n if (!cancelled) setState({ status: 'ready', result });\n } catch (err) {\n // Fail open in dev/prod — caller pode decidir via onError pattern futuro.\n if (!cancelled) {\n setState({ status: 'ready', result: { allowed: false, reason: 'check_failed' } });\n }\n if (typeof console !== 'undefined') console.warn('[EntitlementGate] check failed', err);\n }\n };\n run();\n return () => {\n cancelled = true;\n };\n }, [client, feature, resolvedSlug]);\n\n if (state.status === 'loading') {\n return React.createElement(React.Fragment, null, loading ?? null);\n }\n\n const result = state.result;\n const allowed = result?.allowed === true;\n const isReadonly = !allowed && (mode === 'readonly' || result?.behavior === 'readonly');\n\n if (!allowed && mode === 'block' && !isReadonly) {\n return React.createElement(React.Fragment, null, fallback ?? null);\n }\n\n return React.createElement(\n EntitlementContext.Provider,\n {\n value: {\n readonly: isReadonly,\n reason: result?.reason,\n limit: result?.limit,\n currentUsage: result?.currentUsage,\n },\n },\n children,\n );\n}\n"]}
|
package/dist/support.d.cts
CHANGED
package/dist/support.d.ts
CHANGED
package/dist/telemetry.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { R as ResolvedConfig,
|
|
1
|
+
import { R as ResolvedConfig, w as TelemetryEventInput, T as TelemetryEventAck, B as TelemetryLogInput, F as TelemetryLogAck } from './types-BA53dd8S.cjs';
|
|
2
2
|
|
|
3
3
|
declare function createTelemetryNamespace(config: ResolvedConfig): {
|
|
4
4
|
/**
|
package/dist/telemetry.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { R as ResolvedConfig,
|
|
1
|
+
import { R as ResolvedConfig, w as TelemetryEventInput, T as TelemetryEventAck, B as TelemetryLogInput, F as TelemetryLogAck } from './types-BA53dd8S.js';
|
|
2
2
|
|
|
3
3
|
declare function createTelemetryNamespace(config: ResolvedConfig): {
|
|
4
4
|
/**
|