@neetru/sdk 3.5.0 → 3.7.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 +71 -1
- package/dist/ai.cjs +254 -391
- package/dist/ai.d.cts +1 -1
- package/dist/ai.d.ts +1 -1
- package/dist/ai.mjs +254 -391
- package/dist/auth.cjs +3560 -4993
- package/dist/auth.d.cts +1 -1
- package/dist/auth.d.ts +1 -1
- package/dist/auth.mjs +3557 -4990
- package/dist/build-guard.cjs +101 -119
- package/dist/build-guard.mjs +91 -109
- package/dist/catalog.cjs +152 -224
- package/dist/catalog.d.cts +1 -1
- package/dist/catalog.d.ts +1 -1
- package/dist/catalog.mjs +152 -224
- package/dist/checkout.cjs +237 -320
- package/dist/checkout.d.cts +1 -1
- package/dist/checkout.d.ts +1 -1
- package/dist/checkout.mjs +236 -319
- package/dist/db-react.cjs +59 -87
- package/dist/db-react.d.cts +1 -1
- package/dist/db-react.d.ts +1 -1
- package/dist/db-react.mjs +56 -84
- package/dist/db.cjs +2055 -3013
- package/dist/db.d.cts +1 -1
- package/dist/db.d.ts +1 -1
- package/dist/db.mjs +2068 -3026
- package/dist/entitlements.cjs +12 -20
- package/dist/entitlements.d.cts +1 -1
- package/dist/entitlements.d.ts +1 -1
- package/dist/entitlements.mjs +12 -20
- package/dist/errors.cjs +9 -12
- package/dist/errors.d.cts +37 -2
- package/dist/errors.d.ts +37 -2
- package/dist/errors.mjs +8 -12
- package/dist/firestore-compat.cjs +241 -297
- package/dist/firestore-compat.d.cts +1 -1
- package/dist/firestore-compat.d.ts +1 -1
- package/dist/firestore-compat.mjs +215 -271
- package/dist/index.cjs +3627 -5135
- package/dist/index.d.cts +6 -4
- package/dist/index.d.ts +6 -4
- package/dist/index.mjs +3731 -5239
- package/dist/mobile.cjs +411 -0
- package/dist/mobile.d.cts +89 -0
- package/dist/mobile.d.ts +89 -0
- package/dist/mobile.mjs +409 -0
- package/dist/mocks.cjs +312 -447
- package/dist/mocks.d.cts +1 -1
- package/dist/mocks.d.ts +1 -1
- package/dist/mocks.mjs +306 -441
- package/dist/notifications.cjs +226 -314
- package/dist/notifications.d.cts +1 -1
- package/dist/notifications.d.ts +1 -1
- package/dist/notifications.mjs +225 -313
- package/dist/react.cjs +522 -693
- package/dist/react.d.cts +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/react.mjs +512 -683
- package/dist/support.cjs +329 -325
- package/dist/support.d.cts +25 -3
- package/dist/support.d.ts +25 -3
- package/dist/support.mjs +328 -325
- package/dist/telemetry.cjs +197 -301
- package/dist/telemetry.d.cts +1 -1
- package/dist/telemetry.d.ts +1 -1
- package/dist/telemetry.mjs +197 -301
- package/dist/{types-Du_iioJ7.d.cts → types-Bx4kTLmk.d.cts} +26 -0
- package/dist/{types-BPATkXYu.d.ts → types-Dx0IrOSF.d.ts} +26 -0
- package/dist/usage.cjs +163 -227
- package/dist/usage.d.cts +1 -1
- package/dist/usage.d.ts +1 -1
- package/dist/usage.mjs +163 -227
- package/dist/webhooks.cjs +258 -359
- package/dist/webhooks.d.cts +1 -1
- package/dist/webhooks.d.ts +1 -1
- package/dist/webhooks.mjs +255 -356
- package/package.json +9 -3
- package/dist/ai.cjs.map +0 -1
- package/dist/ai.mjs.map +0 -1
- package/dist/auth.cjs.map +0 -1
- package/dist/auth.mjs.map +0 -1
- package/dist/build-guard.cjs.map +0 -1
- package/dist/build-guard.mjs.map +0 -1
- package/dist/catalog.cjs.map +0 -1
- package/dist/catalog.mjs.map +0 -1
- package/dist/checkout.cjs.map +0 -1
- package/dist/checkout.mjs.map +0 -1
- package/dist/db-react.cjs.map +0 -1
- package/dist/db-react.mjs.map +0 -1
- package/dist/db.cjs.map +0 -1
- package/dist/db.mjs.map +0 -1
- package/dist/entitlements.cjs.map +0 -1
- package/dist/entitlements.mjs.map +0 -1
- package/dist/errors.cjs.map +0 -1
- package/dist/errors.mjs.map +0 -1
- package/dist/firestore-compat.cjs.map +0 -1
- package/dist/firestore-compat.mjs.map +0 -1
- package/dist/index.cjs.map +0 -1
- package/dist/index.mjs.map +0 -1
- package/dist/mocks.cjs.map +0 -1
- package/dist/mocks.mjs.map +0 -1
- package/dist/notifications.cjs.map +0 -1
- package/dist/notifications.mjs.map +0 -1
- package/dist/react.cjs.map +0 -1
- package/dist/react.mjs.map +0 -1
- package/dist/support.cjs.map +0 -1
- package/dist/support.mjs.map +0 -1
- package/dist/telemetry.cjs.map +0 -1
- package/dist/telemetry.mjs.map +0 -1
- package/dist/usage.cjs.map +0 -1
- package/dist/usage.mjs.map +0 -1
- package/dist/webhooks.cjs.map +0 -1
- package/dist/webhooks.mjs.map +0 -1
package/dist/mobile.cjs
ADDED
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/errors.ts
|
|
4
|
+
var r = class t extends Error {
|
|
5
|
+
code;
|
|
6
|
+
status;
|
|
7
|
+
requestId;
|
|
8
|
+
/**
|
|
9
|
+
* Motivo semântico opcional propagado pelo Core no corpo do erro (campo
|
|
10
|
+
* `reason` no TOPO do body, fora de `error`). Usado principalmente pelo 503
|
|
11
|
+
* de lease de DB, onde `reason` discrimina o estado do ambiente
|
|
12
|
+
* (`suspended` | `archived` | `revoked` | `provisioning` | `degraded` |
|
|
13
|
+
* `no_route` | `expired`). Camadas superiores (ex.: `db/sql`) mapeiam isso
|
|
14
|
+
* para um `NeetruDbErrorCode` mais específico (recuperável vs não).
|
|
15
|
+
*/
|
|
16
|
+
reason;
|
|
17
|
+
constructor(e, i, s, o, l) {
|
|
18
|
+
super(i), this.name = "NeetruError", this.code = e, this.status = s, this.requestId = o, this.reason = l, Object.setPrototypeOf(this, t.prototype);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// src/types.ts
|
|
23
|
+
var L = "https://api.neetru.com";
|
|
24
|
+
|
|
25
|
+
// src/base-url-allowlist.ts
|
|
26
|
+
var O = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "[::1]", "::1"]), F = ".neetru.com", J = "neetru.com", Y = /^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?\.(test|localhost)$/;
|
|
27
|
+
function D(t) {
|
|
28
|
+
return Y.test(t);
|
|
29
|
+
}
|
|
30
|
+
function X(t) {
|
|
31
|
+
return t.toLowerCase().replace(/\.+$/, "");
|
|
32
|
+
}
|
|
33
|
+
function M(t, e = []) {
|
|
34
|
+
if (typeof t != "string" || t.length === 0)
|
|
35
|
+
throw new r("invalid_config", "baseUrl is required");
|
|
36
|
+
let i;
|
|
37
|
+
try {
|
|
38
|
+
i = new URL(t);
|
|
39
|
+
} catch {
|
|
40
|
+
throw new r("invalid_config", `baseUrl is not a valid URL: ${t}`);
|
|
41
|
+
}
|
|
42
|
+
let s = i.protocol, o = X(i.hostname);
|
|
43
|
+
if (Z())
|
|
44
|
+
return typeof console < "u" && console.warn(
|
|
45
|
+
`[neetru-sdk] NEETRU_ALLOW_INSECURE_BASEURL=1 ativo. baseUrl=${t} aceito sem valida\xE7\xE3o.`
|
|
46
|
+
), t.replace(/\/+$/, "");
|
|
47
|
+
if (s !== "https:" && s !== "http:")
|
|
48
|
+
throw new r(
|
|
49
|
+
"invalid_config",
|
|
50
|
+
`baseUrl protocol must be http: or https:, got ${s}`
|
|
51
|
+
);
|
|
52
|
+
if (s === "http:") {
|
|
53
|
+
if (!(O.has(o) || D(o)))
|
|
54
|
+
throw new r(
|
|
55
|
+
"invalid_config",
|
|
56
|
+
`baseUrl with http: only allowed for localhost/127.0.0.1/*.test/*.localhost. Got: ${o}. Use https: or set NEETRU_ALLOW_INSECURE_BASEURL=1 to bypass.`
|
|
57
|
+
);
|
|
58
|
+
return t.replace(/\/+$/, "");
|
|
59
|
+
}
|
|
60
|
+
let l = o === J || o.endsWith(F), A = e.some((m) => {
|
|
61
|
+
let k = m.toLowerCase();
|
|
62
|
+
return o === k || o.endsWith(`.${k}`);
|
|
63
|
+
}), R = O.has(o) || D(o);
|
|
64
|
+
if (!l && !A && !R)
|
|
65
|
+
throw new r(
|
|
66
|
+
"invalid_config",
|
|
67
|
+
`baseUrl ${o} is not in the allowlist. Allowed: *.neetru.com, localhost, or configure { allowedHosts: ['...'] }. Bypass: NEETRU_ALLOW_INSECURE_BASEURL=1.`
|
|
68
|
+
);
|
|
69
|
+
return t.replace(/\/+$/, "");
|
|
70
|
+
}
|
|
71
|
+
function Z() {
|
|
72
|
+
try {
|
|
73
|
+
let e = globalThis.process?.env?.NEETRU_ALLOW_INSECURE_BASEURL;
|
|
74
|
+
if (e === "1" || e === "true") return !0;
|
|
75
|
+
} catch {
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
let t = globalThis.NEETRU_ALLOW_INSECURE_BASEURL;
|
|
79
|
+
if (t === "1" || t === "true" || t === !0) return !0;
|
|
80
|
+
} catch {
|
|
81
|
+
}
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// src/api-key.ts
|
|
86
|
+
var G = /^nrt_([A-Za-z0-9]+)_([A-Za-z0-9]+)$/;
|
|
87
|
+
function K(t) {
|
|
88
|
+
if (typeof t != "string" || t.length === 0)
|
|
89
|
+
throw new r(
|
|
90
|
+
"invalid_config",
|
|
91
|
+
"apiKey is required (formato nrt_<keyId>_<secret>)"
|
|
92
|
+
);
|
|
93
|
+
let e = G.exec(t);
|
|
94
|
+
if (!e)
|
|
95
|
+
throw new r(
|
|
96
|
+
"invalid_config",
|
|
97
|
+
"apiKey malformado - esperado formato nrt_<keyId>_<secret>"
|
|
98
|
+
);
|
|
99
|
+
return { keyId: e[1], secret: e[2] };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// src/auth-pkce-shared.ts
|
|
103
|
+
function H() {
|
|
104
|
+
let t = globalThis.crypto;
|
|
105
|
+
if (!t || typeof t.getRandomValues != "function" || !t.subtle)
|
|
106
|
+
throw new r(
|
|
107
|
+
"runtime_unsupported",
|
|
108
|
+
"auth.signIn requer WebCrypto (crypto.subtle) \u2014 indispon\xEDvel neste runtime. Use HTTPS (ou http://localhost) num browser moderno, ou instale um polyfill WebCrypto (ex: react-native-quick-crypto) em React Native."
|
|
109
|
+
);
|
|
110
|
+
return t;
|
|
111
|
+
}
|
|
112
|
+
function $(t) {
|
|
113
|
+
let e = "";
|
|
114
|
+
for (let s of t) e += String.fromCharCode(s);
|
|
115
|
+
return (typeof btoa == "function" ? btoa(e) : Buffer.from(e, "binary").toString("base64")).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
116
|
+
}
|
|
117
|
+
function v(t, e) {
|
|
118
|
+
let i = new Uint8Array(e);
|
|
119
|
+
return t.getRandomValues(i), $(i);
|
|
120
|
+
}
|
|
121
|
+
async function B(t, e) {
|
|
122
|
+
let i = new TextEncoder().encode(e), s = await t.subtle.digest("SHA-256", i);
|
|
123
|
+
return $(new Uint8Array(s));
|
|
124
|
+
}
|
|
125
|
+
function U(t) {
|
|
126
|
+
return (typeof globalThis.NEETRU_AUTH_URL == "string" ? globalThis.NEETRU_AUTH_URL : null) ?? t.replace(/^https?:\/\/api\./, "https://auth.");
|
|
127
|
+
}
|
|
128
|
+
function I(t) {
|
|
129
|
+
let e = t.oidcClientId;
|
|
130
|
+
if (!e && t.apiKey && (e = K(t.apiKey).keyId), !e)
|
|
131
|
+
throw new r(
|
|
132
|
+
"invalid_config",
|
|
133
|
+
"auth OIDC requer `oidcClientId` (ou `apiKey`) configurado para montar o client_id."
|
|
134
|
+
);
|
|
135
|
+
return e;
|
|
136
|
+
}
|
|
137
|
+
function q(t) {
|
|
138
|
+
let e = new URL("/api/v1/oauth/authorize", t.idpOrigin);
|
|
139
|
+
return e.searchParams.set("response_type", "code"), e.searchParams.set("client_id", t.clientId), e.searchParams.set("redirect_uri", t.redirectUri), e.searchParams.set("scope", t.scope), e.searchParams.set("state", t.state), e.searchParams.set("nonce", t.nonce), e.searchParams.set("code_challenge", t.codeChallenge), e.searchParams.set("code_challenge_method", "S256"), e;
|
|
140
|
+
}
|
|
141
|
+
var W = "/api/v1/oauth/token";
|
|
142
|
+
function N(t) {
|
|
143
|
+
let e = t.split(".");
|
|
144
|
+
if (e.length !== 3) return null;
|
|
145
|
+
try {
|
|
146
|
+
let i = e[1].replace(/-/g, "+").replace(/_/g, "/"), s = "=".repeat((4 - i.length % 4) % 4), o = typeof atob == "function" ? atob(i + s) : Buffer.from(i + s, "base64").toString("utf-8");
|
|
147
|
+
return JSON.parse(o);
|
|
148
|
+
} catch {
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
function _(t) {
|
|
153
|
+
let e = N(t);
|
|
154
|
+
return !e || typeof e.sub != "string" || typeof e.exp == "number" && e.exp * 1e3 < Date.now() ? null : {
|
|
155
|
+
uid: e.sub,
|
|
156
|
+
email: typeof e.email == "string" ? e.email : "",
|
|
157
|
+
emailVerified: typeof e.email_verified == "boolean" ? e.email_verified : void 0,
|
|
158
|
+
displayName: typeof e.name == "string" ? e.name : void 0,
|
|
159
|
+
photoURL: typeof e.picture == "string" ? e.picture : void 0,
|
|
160
|
+
isStaff: e.is_staff === true,
|
|
161
|
+
isCustomer: e.is_customer === true,
|
|
162
|
+
tenantId: typeof e.tenant_id == "string" ? e.tenant_id : void 0,
|
|
163
|
+
...e
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// src/mobile.ts
|
|
168
|
+
var h = "neetru_oauth_tx", y = "neetru_id_token", S = "neetru_refresh_token";
|
|
169
|
+
function Q(t) {
|
|
170
|
+
if (!t.storage)
|
|
171
|
+
throw new r(
|
|
172
|
+
"invalid_config",
|
|
173
|
+
"createMobileAuthNamespace requer `storage` (MobileAuthStorage) \u2014 ver JSDoc de src/mobile.ts requisito 4."
|
|
174
|
+
);
|
|
175
|
+
if (typeof t.openAuthUrl != "function")
|
|
176
|
+
throw new r(
|
|
177
|
+
"invalid_config",
|
|
178
|
+
"createMobileAuthNamespace requer `openAuthUrl` (browser externo, NUNCA WebView embutida) \u2014 ver JSDoc de src/mobile.ts requisito 3."
|
|
179
|
+
);
|
|
180
|
+
let e = t.fetch ?? globalThis.fetch;
|
|
181
|
+
if (typeof e != "function")
|
|
182
|
+
throw new r(
|
|
183
|
+
"invalid_config",
|
|
184
|
+
"fetch is not available in this runtime. Pass `fetch` explicitly to createMobileAuthNamespace."
|
|
185
|
+
);
|
|
186
|
+
let i = M(t.baseUrl ?? L, t.allowedHosts ?? []);
|
|
187
|
+
return {
|
|
188
|
+
fetchImpl: e.bind(globalThis),
|
|
189
|
+
baseUrl: i,
|
|
190
|
+
oidcClientId: t.oidcClientId,
|
|
191
|
+
apiKey: t.apiKey,
|
|
192
|
+
storage: t.storage,
|
|
193
|
+
openAuthUrl: t.openAuthUrl
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
async function z(t, e, i) {
|
|
197
|
+
let s;
|
|
198
|
+
try {
|
|
199
|
+
s = await t(e, {
|
|
200
|
+
method: "POST",
|
|
201
|
+
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
202
|
+
body: i.toString()
|
|
203
|
+
});
|
|
204
|
+
} catch (l) {
|
|
205
|
+
throw new r(
|
|
206
|
+
"network_error",
|
|
207
|
+
`POST ${e} falhou (network): ${l?.message ?? String(l)}`
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
let o = null;
|
|
211
|
+
try {
|
|
212
|
+
o = await s.json();
|
|
213
|
+
} catch {
|
|
214
|
+
o = null;
|
|
215
|
+
}
|
|
216
|
+
if (!s.ok || typeof o?.error == "string") {
|
|
217
|
+
let l = typeof o?.error_description == "string" && o.error_description || typeof o?.error == "string" && o.error || `HTTP ${s.status}`;
|
|
218
|
+
throw new r(
|
|
219
|
+
"token_exchange_failed",
|
|
220
|
+
`Troca de tokens falhou no /token: ${l}`,
|
|
221
|
+
s.status
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
return o ?? {};
|
|
225
|
+
}
|
|
226
|
+
function ge(t) {
|
|
227
|
+
let e = Q(t), i = /* @__PURE__ */ new Set();
|
|
228
|
+
function s() {
|
|
229
|
+
return `${U(e.baseUrl)}${W}`;
|
|
230
|
+
}
|
|
231
|
+
async function o() {
|
|
232
|
+
let n = await m();
|
|
233
|
+
for (let a of i)
|
|
234
|
+
try {
|
|
235
|
+
a(n);
|
|
236
|
+
} catch {
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
async function l(n) {
|
|
240
|
+
if (!n?.redirectUri)
|
|
241
|
+
throw new r(
|
|
242
|
+
"invalid_config",
|
|
243
|
+
"mobile auth.signIn requer `redirectUri` (Universal Link/App Link https://... registrado no Core) \u2014 n\xE3o h\xE1 location.origin impl\xEDcito em React Native."
|
|
244
|
+
);
|
|
245
|
+
let a = H(), u = n.scope ?? "openid profile email", c = I(e), d = v(a, 16), f = v(a, 16), g = v(a, 32), p = await B(a, g), T = {
|
|
246
|
+
state: d,
|
|
247
|
+
nonce: f,
|
|
248
|
+
codeVerifier: g,
|
|
249
|
+
redirectUri: n.redirectUri,
|
|
250
|
+
postLoginRedirect: n.postLoginRedirect
|
|
251
|
+
};
|
|
252
|
+
await e.storage.setItem(h, JSON.stringify(T));
|
|
253
|
+
let C = U(e.baseUrl), w = q({
|
|
254
|
+
idpOrigin: C,
|
|
255
|
+
clientId: c,
|
|
256
|
+
redirectUri: n.redirectUri,
|
|
257
|
+
scope: u,
|
|
258
|
+
state: d,
|
|
259
|
+
nonce: f,
|
|
260
|
+
codeChallenge: p
|
|
261
|
+
});
|
|
262
|
+
await e.openAuthUrl(w.toString());
|
|
263
|
+
}
|
|
264
|
+
async function A(n) {
|
|
265
|
+
if (!n || typeof n != "string")
|
|
266
|
+
throw new r(
|
|
267
|
+
"invalid_config",
|
|
268
|
+
"mobile auth.completeSignIn requer `callbackUrl` (a URL completa do deep link de retorno) \u2014 mobile n\xE3o tem location.href impl\xEDcito."
|
|
269
|
+
);
|
|
270
|
+
let a;
|
|
271
|
+
try {
|
|
272
|
+
a = new URL(n);
|
|
273
|
+
} catch {
|
|
274
|
+
throw new r("invalid_config", `callbackUrl n\xE3o \xE9 uma URL v\xE1lida: ${n}`);
|
|
275
|
+
}
|
|
276
|
+
let u = a.searchParams, c = u.get("error");
|
|
277
|
+
if (c) {
|
|
278
|
+
let j = u.get("error_description") ?? c;
|
|
279
|
+
throw await e.storage.removeItem(h), new r(c, `OIDC authorize falhou: ${j}`);
|
|
280
|
+
}
|
|
281
|
+
let d = u.get("code");
|
|
282
|
+
if (!d)
|
|
283
|
+
throw await e.storage.removeItem(h), new r(
|
|
284
|
+
"invalid_config",
|
|
285
|
+
"callbackUrl n\xE3o cont\xE9m `?code=` nem `?error=` \u2014 n\xE3o \xE9 um callback OIDC v\xE1lido."
|
|
286
|
+
);
|
|
287
|
+
let f = u.get("state"), g = await e.storage.getItem(h), p = null;
|
|
288
|
+
if (g)
|
|
289
|
+
try {
|
|
290
|
+
p = JSON.parse(g);
|
|
291
|
+
} catch {
|
|
292
|
+
p = null;
|
|
293
|
+
}
|
|
294
|
+
if (!p)
|
|
295
|
+
throw new r(
|
|
296
|
+
"oidc_state_mismatch",
|
|
297
|
+
"Callback OIDC sem transa\xE7\xE3o pendente \u2014 `signIn` n\xE3o foi iniciado (ou j\xE1 consumido)."
|
|
298
|
+
);
|
|
299
|
+
if (!f || f !== p.state)
|
|
300
|
+
throw await e.storage.removeItem(h), new r(
|
|
301
|
+
"oidc_state_mismatch",
|
|
302
|
+
"state do callback OIDC n\xE3o confere com o da transa\xE7\xE3o (poss\xEDvel CSRF)."
|
|
303
|
+
);
|
|
304
|
+
await e.storage.removeItem(h);
|
|
305
|
+
let T = I(e), C = new URLSearchParams({
|
|
306
|
+
grant_type: "authorization_code",
|
|
307
|
+
code: d,
|
|
308
|
+
redirect_uri: p.redirectUri,
|
|
309
|
+
client_id: T,
|
|
310
|
+
code_verifier: p.codeVerifier
|
|
311
|
+
}), w = await z(e.fetchImpl, s(), C), b = w.id_token;
|
|
312
|
+
if (typeof b != "string" || b.length === 0)
|
|
313
|
+
throw new r(
|
|
314
|
+
"token_exchange_failed",
|
|
315
|
+
"Resposta do /token sem `id_token` \u2014 o login n\xE3o pode ser completado."
|
|
316
|
+
);
|
|
317
|
+
let x = N(b);
|
|
318
|
+
if (!x)
|
|
319
|
+
throw new r(
|
|
320
|
+
"token_exchange_failed",
|
|
321
|
+
"id_token retornado pelo /token \xE9 malformado (payload indecodific\xE1vel)."
|
|
322
|
+
);
|
|
323
|
+
if (x.nonce !== p.nonce)
|
|
324
|
+
throw new r(
|
|
325
|
+
"oidc_nonce_mismatch",
|
|
326
|
+
"nonce do id_token n\xE3o confere com o da transa\xE7\xE3o OIDC (poss\xEDvel replay). Token descartado."
|
|
327
|
+
);
|
|
328
|
+
let E = _(b);
|
|
329
|
+
if (!E)
|
|
330
|
+
throw new r(
|
|
331
|
+
"token_exchange_failed",
|
|
332
|
+
"id_token retornado pelo /token n\xE3o tem `sub` ou j\xE1 est\xE1 expirado."
|
|
333
|
+
);
|
|
334
|
+
await e.storage.setItem(y, b);
|
|
335
|
+
let P = w.refresh_token;
|
|
336
|
+
return typeof P == "string" && P.length > 0 && await e.storage.setItem(S, P), await o(), { user: E, postLoginRedirect: p.postLoginRedirect };
|
|
337
|
+
}
|
|
338
|
+
async function R() {
|
|
339
|
+
let n = await e.storage.getItem(y);
|
|
340
|
+
return n && _(n) ? n : null;
|
|
341
|
+
}
|
|
342
|
+
async function m() {
|
|
343
|
+
let n = await e.storage.getItem(y);
|
|
344
|
+
return n ? _(n) : null;
|
|
345
|
+
}
|
|
346
|
+
async function k() {
|
|
347
|
+
let n = await e.storage.getItem(S);
|
|
348
|
+
if (!n) return false;
|
|
349
|
+
let a;
|
|
350
|
+
try {
|
|
351
|
+
a = I(e);
|
|
352
|
+
} catch {
|
|
353
|
+
return false;
|
|
354
|
+
}
|
|
355
|
+
let u = new URLSearchParams({
|
|
356
|
+
grant_type: "refresh_token",
|
|
357
|
+
refresh_token: n,
|
|
358
|
+
client_id: a
|
|
359
|
+
}), c;
|
|
360
|
+
try {
|
|
361
|
+
c = await z(e.fetchImpl, s(), u);
|
|
362
|
+
} catch {
|
|
363
|
+
return false;
|
|
364
|
+
}
|
|
365
|
+
let d = c.id_token;
|
|
366
|
+
if (typeof d != "string" || d.length === 0 || !_(d)) return false;
|
|
367
|
+
await e.storage.setItem(y, d);
|
|
368
|
+
let g = c.refresh_token;
|
|
369
|
+
return typeof g == "string" && g.length > 0 && await e.storage.setItem(S, g), await o(), true;
|
|
370
|
+
}
|
|
371
|
+
async function V() {
|
|
372
|
+
let n = await e.storage.getItem(y);
|
|
373
|
+
if (await e.storage.removeItem(y), await e.storage.removeItem(S), n) {
|
|
374
|
+
let u = `${U(e.baseUrl)}/api/v1/oauth/revoke`;
|
|
375
|
+
try {
|
|
376
|
+
await e.fetchImpl(u, {
|
|
377
|
+
method: "POST",
|
|
378
|
+
headers: { "content-type": "application/json" },
|
|
379
|
+
body: JSON.stringify({ token: n })
|
|
380
|
+
});
|
|
381
|
+
} catch (c) {
|
|
382
|
+
typeof console < "u" && console.warn(
|
|
383
|
+
"[neetru-sdk] mobile signOut: server-side revoke falhou. Refresh token pode estar stale.",
|
|
384
|
+
c
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
await o();
|
|
389
|
+
}
|
|
390
|
+
return {
|
|
391
|
+
signIn: l,
|
|
392
|
+
completeSignIn: A,
|
|
393
|
+
getIdToken: R,
|
|
394
|
+
getUser: m,
|
|
395
|
+
onAuthStateChanged(n) {
|
|
396
|
+
return i.add(n), m().then((a) => {
|
|
397
|
+
if (i.has(n))
|
|
398
|
+
try {
|
|
399
|
+
n(a);
|
|
400
|
+
} catch {
|
|
401
|
+
}
|
|
402
|
+
}), () => {
|
|
403
|
+
i.delete(n);
|
|
404
|
+
};
|
|
405
|
+
},
|
|
406
|
+
refreshTokens: k,
|
|
407
|
+
signOut: V
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
exports.createMobileAuthNamespace = ge;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { X as NeetruClientConfig, v as CompleteSignInResult, a5 as NeetruUser, m as AuthStateListener } from './types-Bx4kTLmk.cjs';
|
|
2
|
+
import 'drizzle-orm/node-postgres';
|
|
3
|
+
import './errors.cjs';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Storage assíncrono chave-valor que o app RN injeta — ex: um wrapper fino
|
|
7
|
+
* sobre `expo-secure-store` ou `@react-native-async-storage/async-storage`.
|
|
8
|
+
* O SDK não assume NENHUMA implementação concreta.
|
|
9
|
+
*/
|
|
10
|
+
interface MobileAuthStorage {
|
|
11
|
+
getItem(key: string): Promise<string | null>;
|
|
12
|
+
setItem(key: string, value: string): Promise<void>;
|
|
13
|
+
removeItem(key: string): Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
interface MobileAuthConfig extends NeetruClientConfig {
|
|
16
|
+
/** Storage persistente (idealmente seguro) — ver requisito 4 no JSDoc do módulo. */
|
|
17
|
+
storage: MobileAuthStorage;
|
|
18
|
+
/**
|
|
19
|
+
* Abre a URL de authorize num browser EXTERNO (nunca WebView embutida) —
|
|
20
|
+
* ver requisito 3 no JSDoc do módulo. Ex: `WebBrowser.openAuthSessionAsync`
|
|
21
|
+
* (expo-web-browser) ou `Linking.openURL`.
|
|
22
|
+
*/
|
|
23
|
+
openAuthUrl: (url: string) => Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
interface MobileSignInOptions {
|
|
26
|
+
/**
|
|
27
|
+
* Universal Link / App Link (`https://...`) registrado como `redirect_uri`
|
|
28
|
+
* do OIDC client mobile no Core. Obrigatório — diferente do namespace web,
|
|
29
|
+
* não há `location.origin` implícito pra usar como default.
|
|
30
|
+
*/
|
|
31
|
+
redirectUri: string;
|
|
32
|
+
/** OIDC scope. Default `openid profile email`. */
|
|
33
|
+
scope?: string;
|
|
34
|
+
/** Destino pós-login que o app decide interpretar após `completeSignIn`. */
|
|
35
|
+
postLoginRedirect?: string;
|
|
36
|
+
}
|
|
37
|
+
interface MobileAuthNamespace {
|
|
38
|
+
/**
|
|
39
|
+
* Inicia o fluxo OIDC Authorization Code + PKCE: gera `state`/`nonce`/
|
|
40
|
+
* `code_verifier`/`code_challenge` (S256), persiste a transação via
|
|
41
|
+
* `config.storage`, e chama `config.openAuthUrl(url)` — NUNCA navega
|
|
42
|
+
* sozinho (sem `location` em RN).
|
|
43
|
+
*
|
|
44
|
+
* @throws {NeetruError} `runtime_unsupported` se `crypto.subtle` ausente.
|
|
45
|
+
*/
|
|
46
|
+
signIn(options: MobileSignInOptions): Promise<void>;
|
|
47
|
+
/**
|
|
48
|
+
* Completa o login: recebe a URL COMPLETA do deep link de retorno (o app
|
|
49
|
+
* lê via `Linking.getInitialURL()` / listener `Linking.addEventListener`)
|
|
50
|
+
* e troca o `code` pelos tokens no mesmo `/api/v1/oauth/token` que o
|
|
51
|
+
* namespace web usa (POST x-www-form-urlencoded, PKCE puro, sem
|
|
52
|
+
* client_secret). Valida `state` (CSRF) e `nonce` (anti-replay), grava os
|
|
53
|
+
* tokens em `config.storage` e notifica `onAuthStateChanged`.
|
|
54
|
+
*
|
|
55
|
+
* `callbackUrl` é OBRIGATÓRIO — mobile não tem `location.href` implícito.
|
|
56
|
+
*
|
|
57
|
+
* @throws {NeetruError} `oidc_state_mismatch`, `oidc_nonce_mismatch`,
|
|
58
|
+
* `token_exchange_failed`, `network_error`, ou `invalid_config` se a URL
|
|
59
|
+
* não é um callback OIDC válido (sem `?code=`/`?error=`) ou não há
|
|
60
|
+
* transação pendente no storage.
|
|
61
|
+
*/
|
|
62
|
+
completeSignIn(callbackUrl: string): Promise<CompleteSignInResult>;
|
|
63
|
+
/** `id_token` cru armazenado, ou `null` se ausente/expirado. */
|
|
64
|
+
getIdToken(): Promise<string | null>;
|
|
65
|
+
/** Usuário atual (claims do `id_token` armazenado), ou `null`. */
|
|
66
|
+
getUser(): Promise<NeetruUser | null>;
|
|
67
|
+
/**
|
|
68
|
+
* Subscreve a mudanças de estado de auth. Diferente do namespace web, o
|
|
69
|
+
* listener inicial é disparado de forma ASSÍNCRONA (a leitura do storage
|
|
70
|
+
* injetado é sempre `Promise`) — não confie em ordem síncrona de chamada.
|
|
71
|
+
* Retorna função de unsubscribe (síncrona).
|
|
72
|
+
*/
|
|
73
|
+
onAuthStateChanged(listener: AuthStateListener): () => void;
|
|
74
|
+
/**
|
|
75
|
+
* Renova os tokens via `grant_type=refresh_token`, se houver refresh_token
|
|
76
|
+
* armazenado. `true` se renovou; `false` em qualquer falha (sem refresh
|
|
77
|
+
* token, `invalid_grant`, rede) — não lança.
|
|
78
|
+
*/
|
|
79
|
+
refreshTokens(): Promise<boolean>;
|
|
80
|
+
/** Limpa a sessão local (storage) + melhor esforço de revoke no servidor. */
|
|
81
|
+
signOut(): Promise<void>;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Cria o namespace de auth mobile. Ver JSDoc do módulo pros requisitos do
|
|
85
|
+
* app consumidor (polyfill WebCrypto, browser externo, storage seguro).
|
|
86
|
+
*/
|
|
87
|
+
declare function createMobileAuthNamespace(config: MobileAuthConfig): MobileAuthNamespace;
|
|
88
|
+
|
|
89
|
+
export { type MobileAuthConfig, type MobileAuthNamespace, type MobileAuthStorage, type MobileSignInOptions, createMobileAuthNamespace };
|
package/dist/mobile.d.ts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { X as NeetruClientConfig, v as CompleteSignInResult, a5 as NeetruUser, m as AuthStateListener } from './types-Dx0IrOSF.js';
|
|
2
|
+
import 'drizzle-orm/node-postgres';
|
|
3
|
+
import './errors.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Storage assíncrono chave-valor que o app RN injeta — ex: um wrapper fino
|
|
7
|
+
* sobre `expo-secure-store` ou `@react-native-async-storage/async-storage`.
|
|
8
|
+
* O SDK não assume NENHUMA implementação concreta.
|
|
9
|
+
*/
|
|
10
|
+
interface MobileAuthStorage {
|
|
11
|
+
getItem(key: string): Promise<string | null>;
|
|
12
|
+
setItem(key: string, value: string): Promise<void>;
|
|
13
|
+
removeItem(key: string): Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
interface MobileAuthConfig extends NeetruClientConfig {
|
|
16
|
+
/** Storage persistente (idealmente seguro) — ver requisito 4 no JSDoc do módulo. */
|
|
17
|
+
storage: MobileAuthStorage;
|
|
18
|
+
/**
|
|
19
|
+
* Abre a URL de authorize num browser EXTERNO (nunca WebView embutida) —
|
|
20
|
+
* ver requisito 3 no JSDoc do módulo. Ex: `WebBrowser.openAuthSessionAsync`
|
|
21
|
+
* (expo-web-browser) ou `Linking.openURL`.
|
|
22
|
+
*/
|
|
23
|
+
openAuthUrl: (url: string) => Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
interface MobileSignInOptions {
|
|
26
|
+
/**
|
|
27
|
+
* Universal Link / App Link (`https://...`) registrado como `redirect_uri`
|
|
28
|
+
* do OIDC client mobile no Core. Obrigatório — diferente do namespace web,
|
|
29
|
+
* não há `location.origin` implícito pra usar como default.
|
|
30
|
+
*/
|
|
31
|
+
redirectUri: string;
|
|
32
|
+
/** OIDC scope. Default `openid profile email`. */
|
|
33
|
+
scope?: string;
|
|
34
|
+
/** Destino pós-login que o app decide interpretar após `completeSignIn`. */
|
|
35
|
+
postLoginRedirect?: string;
|
|
36
|
+
}
|
|
37
|
+
interface MobileAuthNamespace {
|
|
38
|
+
/**
|
|
39
|
+
* Inicia o fluxo OIDC Authorization Code + PKCE: gera `state`/`nonce`/
|
|
40
|
+
* `code_verifier`/`code_challenge` (S256), persiste a transação via
|
|
41
|
+
* `config.storage`, e chama `config.openAuthUrl(url)` — NUNCA navega
|
|
42
|
+
* sozinho (sem `location` em RN).
|
|
43
|
+
*
|
|
44
|
+
* @throws {NeetruError} `runtime_unsupported` se `crypto.subtle` ausente.
|
|
45
|
+
*/
|
|
46
|
+
signIn(options: MobileSignInOptions): Promise<void>;
|
|
47
|
+
/**
|
|
48
|
+
* Completa o login: recebe a URL COMPLETA do deep link de retorno (o app
|
|
49
|
+
* lê via `Linking.getInitialURL()` / listener `Linking.addEventListener`)
|
|
50
|
+
* e troca o `code` pelos tokens no mesmo `/api/v1/oauth/token` que o
|
|
51
|
+
* namespace web usa (POST x-www-form-urlencoded, PKCE puro, sem
|
|
52
|
+
* client_secret). Valida `state` (CSRF) e `nonce` (anti-replay), grava os
|
|
53
|
+
* tokens em `config.storage` e notifica `onAuthStateChanged`.
|
|
54
|
+
*
|
|
55
|
+
* `callbackUrl` é OBRIGATÓRIO — mobile não tem `location.href` implícito.
|
|
56
|
+
*
|
|
57
|
+
* @throws {NeetruError} `oidc_state_mismatch`, `oidc_nonce_mismatch`,
|
|
58
|
+
* `token_exchange_failed`, `network_error`, ou `invalid_config` se a URL
|
|
59
|
+
* não é um callback OIDC válido (sem `?code=`/`?error=`) ou não há
|
|
60
|
+
* transação pendente no storage.
|
|
61
|
+
*/
|
|
62
|
+
completeSignIn(callbackUrl: string): Promise<CompleteSignInResult>;
|
|
63
|
+
/** `id_token` cru armazenado, ou `null` se ausente/expirado. */
|
|
64
|
+
getIdToken(): Promise<string | null>;
|
|
65
|
+
/** Usuário atual (claims do `id_token` armazenado), ou `null`. */
|
|
66
|
+
getUser(): Promise<NeetruUser | null>;
|
|
67
|
+
/**
|
|
68
|
+
* Subscreve a mudanças de estado de auth. Diferente do namespace web, o
|
|
69
|
+
* listener inicial é disparado de forma ASSÍNCRONA (a leitura do storage
|
|
70
|
+
* injetado é sempre `Promise`) — não confie em ordem síncrona de chamada.
|
|
71
|
+
* Retorna função de unsubscribe (síncrona).
|
|
72
|
+
*/
|
|
73
|
+
onAuthStateChanged(listener: AuthStateListener): () => void;
|
|
74
|
+
/**
|
|
75
|
+
* Renova os tokens via `grant_type=refresh_token`, se houver refresh_token
|
|
76
|
+
* armazenado. `true` se renovou; `false` em qualquer falha (sem refresh
|
|
77
|
+
* token, `invalid_grant`, rede) — não lança.
|
|
78
|
+
*/
|
|
79
|
+
refreshTokens(): Promise<boolean>;
|
|
80
|
+
/** Limpa a sessão local (storage) + melhor esforço de revoke no servidor. */
|
|
81
|
+
signOut(): Promise<void>;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Cria o namespace de auth mobile. Ver JSDoc do módulo pros requisitos do
|
|
85
|
+
* app consumidor (polyfill WebCrypto, browser externo, storage seguro).
|
|
86
|
+
*/
|
|
87
|
+
declare function createMobileAuthNamespace(config: MobileAuthConfig): MobileAuthNamespace;
|
|
88
|
+
|
|
89
|
+
export { type MobileAuthConfig, type MobileAuthNamespace, type MobileAuthStorage, type MobileSignInOptions, createMobileAuthNamespace };
|