@neetru/sdk 3.6.0 → 3.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.
Files changed (117) hide show
  1. package/CHANGELOG.md +104 -0
  2. package/README.md +66 -1
  3. package/dist/ai.cjs +298 -391
  4. package/dist/ai.d.cts +1 -1
  5. package/dist/ai.d.ts +1 -1
  6. package/dist/ai.mjs +298 -391
  7. package/dist/auth.cjs +3717 -5005
  8. package/dist/auth.d.cts +1 -1
  9. package/dist/auth.d.ts +1 -1
  10. package/dist/auth.mjs +3714 -5002
  11. package/dist/build-guard.cjs +101 -119
  12. package/dist/build-guard.mjs +91 -109
  13. package/dist/catalog.cjs +220 -225
  14. package/dist/catalog.d.cts +1 -1
  15. package/dist/catalog.d.ts +1 -1
  16. package/dist/catalog.mjs +220 -225
  17. package/dist/checkout.cjs +307 -323
  18. package/dist/checkout.d.cts +1 -1
  19. package/dist/checkout.d.ts +1 -1
  20. package/dist/checkout.mjs +306 -322
  21. package/dist/db-react.cjs +59 -87
  22. package/dist/db-react.d.cts +1 -1
  23. package/dist/db-react.d.ts +1 -1
  24. package/dist/db-react.mjs +56 -84
  25. package/dist/db.cjs +2128 -3014
  26. package/dist/db.d.cts +1 -1
  27. package/dist/db.d.ts +1 -1
  28. package/dist/db.mjs +2141 -3027
  29. package/dist/entitlements.cjs +19 -20
  30. package/dist/entitlements.d.cts +1 -1
  31. package/dist/entitlements.d.ts +1 -1
  32. package/dist/entitlements.mjs +19 -20
  33. package/dist/errors.cjs +16 -12
  34. package/dist/errors.d.cts +59 -3
  35. package/dist/errors.d.ts +59 -3
  36. package/dist/errors.mjs +15 -12
  37. package/dist/firestore-compat.cjs +241 -297
  38. package/dist/firestore-compat.d.cts +1 -1
  39. package/dist/firestore-compat.d.ts +1 -1
  40. package/dist/firestore-compat.mjs +215 -271
  41. package/dist/index.cjs +3942 -5134
  42. package/dist/index.d.cts +181 -5
  43. package/dist/index.d.ts +181 -5
  44. package/dist/index.mjs +4040 -5239
  45. package/dist/mobile.cjs +459 -344
  46. package/dist/mobile.d.cts +59 -4
  47. package/dist/mobile.d.ts +59 -4
  48. package/dist/mobile.mjs +459 -344
  49. package/dist/mocks.cjs +319 -447
  50. package/dist/mocks.d.cts +1 -1
  51. package/dist/mocks.d.ts +1 -1
  52. package/dist/mocks.mjs +313 -441
  53. package/dist/notifications.cjs +296 -317
  54. package/dist/notifications.d.cts +1 -1
  55. package/dist/notifications.d.ts +1 -1
  56. package/dist/notifications.mjs +295 -316
  57. package/dist/react.cjs +557 -707
  58. package/dist/react.d.cts +1 -1
  59. package/dist/react.d.ts +1 -1
  60. package/dist/react.mjs +547 -697
  61. package/dist/support.cjs +402 -328
  62. package/dist/support.d.cts +29 -3
  63. package/dist/support.d.ts +29 -3
  64. package/dist/support.mjs +401 -328
  65. package/dist/telemetry.cjs +267 -304
  66. package/dist/telemetry.d.cts +1 -1
  67. package/dist/telemetry.d.ts +1 -1
  68. package/dist/telemetry.mjs +267 -304
  69. package/dist/{types-Du_iioJ7.d.cts → types-D-9bjlM0.d.cts} +74 -1
  70. package/dist/{types-BPATkXYu.d.ts → types-bVztFQTI.d.ts} +74 -1
  71. package/dist/usage.cjs +233 -230
  72. package/dist/usage.d.cts +1 -1
  73. package/dist/usage.d.ts +1 -1
  74. package/dist/usage.mjs +233 -230
  75. package/dist/webhooks.cjs +328 -362
  76. package/dist/webhooks.d.cts +1 -1
  77. package/dist/webhooks.d.ts +1 -1
  78. package/dist/webhooks.mjs +325 -359
  79. package/package.json +5 -3
  80. package/dist/ai.cjs.map +0 -1
  81. package/dist/ai.mjs.map +0 -1
  82. package/dist/auth.cjs.map +0 -1
  83. package/dist/auth.mjs.map +0 -1
  84. package/dist/build-guard.cjs.map +0 -1
  85. package/dist/build-guard.mjs.map +0 -1
  86. package/dist/catalog.cjs.map +0 -1
  87. package/dist/catalog.mjs.map +0 -1
  88. package/dist/checkout.cjs.map +0 -1
  89. package/dist/checkout.mjs.map +0 -1
  90. package/dist/db-react.cjs.map +0 -1
  91. package/dist/db-react.mjs.map +0 -1
  92. package/dist/db.cjs.map +0 -1
  93. package/dist/db.mjs.map +0 -1
  94. package/dist/entitlements.cjs.map +0 -1
  95. package/dist/entitlements.mjs.map +0 -1
  96. package/dist/errors.cjs.map +0 -1
  97. package/dist/errors.mjs.map +0 -1
  98. package/dist/firestore-compat.cjs.map +0 -1
  99. package/dist/firestore-compat.mjs.map +0 -1
  100. package/dist/index.cjs.map +0 -1
  101. package/dist/index.mjs.map +0 -1
  102. package/dist/mobile.cjs.map +0 -1
  103. package/dist/mobile.mjs.map +0 -1
  104. package/dist/mocks.cjs.map +0 -1
  105. package/dist/mocks.mjs.map +0 -1
  106. package/dist/notifications.cjs.map +0 -1
  107. package/dist/notifications.mjs.map +0 -1
  108. package/dist/react.cjs.map +0 -1
  109. package/dist/react.mjs.map +0 -1
  110. package/dist/support.cjs.map +0 -1
  111. package/dist/support.mjs.map +0 -1
  112. package/dist/telemetry.cjs.map +0 -1
  113. package/dist/telemetry.mjs.map +0 -1
  114. package/dist/usage.cjs.map +0 -1
  115. package/dist/usage.mjs.map +0 -1
  116. package/dist/webhooks.cjs.map +0 -1
  117. package/dist/webhooks.mjs.map +0 -1
package/dist/mocks.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/errors.ts
2
- var NeetruError = class _NeetruError extends Error {
2
+ var l = class m extends Error {
3
3
  code;
4
4
  status;
5
5
  requestId;
@@ -12,24 +12,24 @@ var NeetruError = class _NeetruError extends Error {
12
12
  * para um `NeetruDbErrorCode` mais específico (recuperável vs não).
13
13
  */
14
14
  reason;
15
- constructor(code, message, status, requestId, reason) {
16
- super(message);
17
- this.name = "NeetruError";
18
- this.code = code;
19
- this.status = status;
20
- this.requestId = requestId;
21
- this.reason = reason;
22
- Object.setPrototypeOf(this, _NeetruError.prototype);
15
+ /**
16
+ * 3.8.0: `x-correlation-id` da requisição que falhou. O SDK gera um UUID por
17
+ * chamada (ou usa o que o chamador passou) e manda no header; o Core grava o
18
+ * mesmo id nos logs/auditoria — cite-o ao abrir um ticket de suporte.
19
+ * `undefined` em erros que acontecem antes de qualquer requisição.
20
+ */
21
+ correlationId;
22
+ constructor(e, r, i, o, a, c) {
23
+ super(r), this.name = "NeetruError", this.code = e, this.status = i, this.requestId = o, this.reason = a, this.correlationId = c, Object.setPrototypeOf(this, m.prototype);
23
24
  }
24
25
  };
25
26
 
26
27
  // src/db-errors.ts
27
- var RETRYABLE_CODES = /* @__PURE__ */ new Set([
28
+ var U = /* @__PURE__ */ new Set([
28
29
  "db_unavailable",
29
30
  "db_conflict",
30
31
  "db_timeout"
31
- ]);
32
- var NeetruDbError = class _NeetruDbError extends NeetruError {
32
+ ]), _ = class m extends l {
33
33
  /** Código de erro fechado — específico de DB. */
34
34
  code;
35
35
  /**
@@ -42,79 +42,60 @@ var NeetruDbError = class _NeetruDbError extends NeetruError {
42
42
  * Nunca deve ser exibido ao usuário final.
43
43
  */
44
44
  dbId;
45
- constructor(code, message, dbId) {
46
- super(code, message);
47
- this.name = "NeetruDbError";
48
- this.code = code;
49
- this.retryable = RETRYABLE_CODES.has(code);
50
- this.dbId = dbId;
51
- Object.setPrototypeOf(this, _NeetruDbError.prototype);
45
+ constructor(e, r, i) {
46
+ super(e, r), this.name = "NeetruDbError", this.code = e, this.retryable = U.has(e), this.dbId = i, Object.setPrototypeOf(this, m.prototype);
52
47
  }
53
48
  };
54
49
 
55
50
  // src/runtime-env.ts
56
- function isProductionRuntime() {
51
+ function R() {
57
52
  try {
58
- if (typeof process !== "undefined" && process.env) {
53
+ if (typeof process < "u" && process.env)
59
54
  return process.env.NODE_ENV === "production";
60
- }
61
55
  } catch {
62
56
  }
63
57
  return false;
64
58
  }
65
59
 
66
60
  // src/mocks.ts
67
- var DEV_FIXTURE_USER = Object.freeze({
61
+ var x = Object.freeze({
68
62
  uid: "dev-fixture-uid-0001",
69
63
  email: "dev@neetru.local",
70
64
  emailVerified: true,
71
65
  displayName: "Dev Fixture",
72
66
  isCustomer: true,
73
67
  isStaff: false
74
- });
75
- var MockAuth = class {
68
+ }), A = class {
76
69
  _user;
77
70
  _listeners;
78
71
  _allowUnsignedDecode;
79
- constructor(initialUser = null, options) {
80
- this._user = initialUser;
81
- this._listeners = /* @__PURE__ */ new Set();
82
- this._allowUnsignedDecode = options?.allowUnsignedDecode ?? false;
83
- if (this._allowUnsignedDecode && isProductionRuntime()) {
84
- throw new NeetruError(
72
+ constructor(e = null, r) {
73
+ if (this._user = e, this._listeners = /* @__PURE__ */ new Set(), this._allowUnsignedDecode = r?.allowUnsignedDecode ?? false, this._allowUnsignedDecode && R())
74
+ throw new l(
85
75
  "invalid_config",
86
76
  "MockAuth com allowUnsignedDecode=true \xE9 PROIBIDO em produ\xE7\xE3o (NODE_ENV=production): decodificar JWT sem verificar assinatura \xE9 bypass de autentica\xE7\xE3o. Este modo existe exclusivamente para testes."
87
77
  );
88
- }
89
- if (this._allowUnsignedDecode) {
90
- const isTestEnv = typeof process !== "undefined" && (process.env?.NODE_ENV === "test" || process.env?.VITEST === "true" || process.env?.VITEST != null);
91
- if (!isTestEnv && typeof console !== "undefined") {
92
- console.warn(
93
- "[neetru-sdk] MockAuth.verifyToken est\xE1 em modo allowUnsignedDecode=true: tokens N\xC3O t\xEAm assinatura verificada. Este modo \xE9 exclusivo para testes \u2014 NUNCA use como security boundary em c\xF3digo deployado."
94
- );
95
- }
96
- }
78
+ this._allowUnsignedDecode && !(typeof process < "u" && (process.env?.NODE_ENV === "test" || process.env?.VITEST === "true" || process.env?.VITEST != null)) && typeof console < "u" && console.warn(
79
+ "[neetru-sdk] MockAuth.verifyToken est\xE1 em modo allowUnsignedDecode=true: tokens N\xC3O t\xEAm assinatura verificada. Este modo \xE9 exclusivo para testes \u2014 NUNCA use como security boundary em c\xF3digo deployado."
80
+ );
97
81
  }
98
- async signIn(_options) {
99
- if (!this._user) this._user = { ...DEV_FIXTURE_USER };
100
- this._notify();
101
- return this._user;
82
+ async signIn(e) {
83
+ return this._user || (this._user = { ...x }), this._notify(), this._user;
102
84
  }
103
85
  async signOut() {
104
- this._user = null;
105
- this._notify();
86
+ this._user = null, this._notify();
106
87
  }
107
88
  getUser() {
108
89
  return this._user;
109
90
  }
110
- onAuthStateChanged(listener) {
111
- this._listeners.add(listener);
91
+ onAuthStateChanged(e) {
92
+ this._listeners.add(e);
112
93
  try {
113
- listener(this._user);
94
+ e(this._user);
114
95
  } catch {
115
96
  }
116
97
  return () => {
117
- this._listeners.delete(listener);
98
+ this._listeners.delete(e);
118
99
  };
119
100
  }
120
101
  /**
@@ -127,52 +108,46 @@ var MockAuth = class {
127
108
  *
128
109
  * Para simular falhas ou resultados customizados, use `__mockVerifyToken(fn)`.
129
110
  */
130
- async verifyToken(token) {
131
- if (this._verifyTokenOverride) {
132
- return this._verifyTokenOverride(token);
133
- }
134
- if (!token) {
135
- throw new NeetruError("token_invalid_signature", "Token inv\xE1lido: string vazia");
136
- }
137
- if (!this._allowUnsignedDecode) {
138
- throw new NeetruError(
111
+ async verifyToken(e) {
112
+ if (this._verifyTokenOverride)
113
+ return this._verifyTokenOverride(e);
114
+ if (!e)
115
+ throw new l("token_invalid_signature", "Token inv\xE1lido: string vazia");
116
+ if (!this._allowUnsignedDecode)
117
+ throw new l(
139
118
  "unauthorized",
140
119
  "MockAuth.verifyToken n\xE3o verifica assinaturas JWT e N\xC3O deve ser usado como security boundary em c\xF3digo deployado. Em testes, use new MockAuth(user, { allowUnsignedDecode: true }) para habilitar decodifica\xE7\xE3o sem assinatura, ou __mockVerifyToken(fn) para injetar resultado customizado."
141
120
  );
142
- }
143
121
  try {
144
- const parts = token.split(".");
145
- if (parts.length === 3) {
146
- const b64 = parts[1].replace(/-/g, "+").replace(/_/g, "/");
147
- const pad = "=".repeat((4 - b64.length % 4) % 4);
148
- const decoded = JSON.parse(
149
- typeof atob === "function" ? atob(b64 + pad) : Buffer.from(b64 + pad, "base64").toString("utf-8")
122
+ let r = e.split(".");
123
+ if (r.length === 3) {
124
+ let i = r[1].replace(/-/g, "+").replace(/_/g, "/"), o = "=".repeat((4 - i.length % 4) % 4), a = JSON.parse(
125
+ typeof atob == "function" ? atob(i + o) : Buffer.from(i + o, "base64").toString("utf-8")
150
126
  );
151
- if (typeof decoded.sub === "string") {
127
+ if (typeof a.sub == "string")
152
128
  return {
153
- uid: decoded.sub,
154
- email: typeof decoded.email === "string" ? decoded.email : "",
155
- emailVerified: typeof decoded.email_verified === "boolean" ? decoded.email_verified : void 0,
156
- displayName: typeof decoded.name === "string" ? decoded.name : void 0,
157
- photoURL: typeof decoded.picture === "string" ? decoded.picture : void 0,
158
- isStaff: decoded.is_staff === true,
159
- isCustomer: decoded.is_customer === true,
160
- tenantId: typeof decoded.tenant_id === "string" ? decoded.tenant_id : void 0,
161
- ...decoded
129
+ uid: a.sub,
130
+ email: typeof a.email == "string" ? a.email : "",
131
+ emailVerified: typeof a.email_verified == "boolean" ? a.email_verified : void 0,
132
+ displayName: typeof a.name == "string" ? a.name : void 0,
133
+ photoURL: typeof a.picture == "string" ? a.picture : void 0,
134
+ isStaff: a.is_staff === !0,
135
+ isCustomer: a.is_customer === !0,
136
+ tenantId: typeof a.tenant_id == "string" ? a.tenant_id : void 0,
137
+ ...a
162
138
  };
163
- }
164
139
  }
165
140
  } catch {
166
141
  }
167
142
  if (this._user) return this._user;
168
- throw new NeetruError("token_invalid_signature", "Nenhum user mock dispon\xEDvel e JWT sem sub v\xE1lido");
143
+ throw new l("token_invalid_signature", "Nenhum user mock dispon\xEDvel e JWT sem sub v\xE1lido");
169
144
  }
170
145
  /**
171
146
  * Mock de `handleRedirectCallback` (Bug 2 / 3.1.3) — não há redirect real no
172
147
  * mock, então retorna `null` por default. Use `__setPendingCallback(result)`
173
148
  * em testes pra simular um retorno do IdP.
174
149
  */
175
- async handleRedirectCallback(_currentUrl) {
150
+ async handleRedirectCallback(e) {
176
151
  return this._pendingCallback;
177
152
  }
178
153
  /**
@@ -181,7 +156,7 @@ var MockAuth = class {
181
156
  * contrato "no-op seguro em todo page load" da implementação real). O login
182
157
  * mock acontece direto via `signIn()`.
183
158
  */
184
- async completeSignIn(_currentUrl) {
159
+ async completeSignIn(e) {
185
160
  return null;
186
161
  }
187
162
  /**
@@ -194,12 +169,12 @@ var MockAuth = class {
194
169
  _pendingCallback = null;
195
170
  _idToken = null;
196
171
  /** Test helper — simula o material devolvido por um callback OIDC. */
197
- __setPendingCallback(result) {
198
- this._pendingCallback = result;
172
+ __setPendingCallback(e) {
173
+ this._pendingCallback = e;
199
174
  }
200
175
  /** Test helper — injeta o id_token cru retornado por `getIdToken`. */
201
- __setIdToken(token) {
202
- this._idToken = token;
176
+ __setIdToken(e) {
177
+ this._idToken = e;
203
178
  }
204
179
  _verifyTokenOverride = null;
205
180
  /**
@@ -212,75 +187,63 @@ var MockAuth = class {
212
187
  * mock.__mockVerifyToken(async () => { throw new NeetruError('token_expired', '...'); });
213
188
  * ```
214
189
  */
215
- __mockVerifyToken(fn) {
216
- this._verifyTokenOverride = fn;
190
+ __mockVerifyToken(e) {
191
+ this._verifyTokenOverride = e;
217
192
  }
218
193
  /** Helper de tests — força um user state arbitrário. */
219
- __setUser(user) {
220
- this._user = user;
221
- this._notify();
194
+ __setUser(e) {
195
+ this._user = e, this._notify();
222
196
  }
223
197
  _notify() {
224
- for (const l of this._listeners) {
198
+ for (let e of this._listeners)
225
199
  try {
226
- l(this._user);
200
+ e(this._user);
227
201
  } catch {
228
202
  }
229
- }
230
203
  }
231
- };
232
- var MockUsage = class {
204
+ }, T = class {
233
205
  _reports = [];
234
206
  _quotas;
235
207
  _counters = /* @__PURE__ */ new Map();
236
- constructor(initialQuotas = {}) {
237
- this._quotas = new Map(Object.entries(initialQuotas));
238
- }
239
- async report(resource, qty = 1, options) {
240
- if (!resource) throw new Error("resource required");
241
- const safeQty = Number.isFinite(qty) && qty > 0 ? Math.floor(qty) : 1;
242
- const existing = this._counters.get(resource) ?? 0;
243
- const next = existing + safeQty;
244
- this._counters.set(resource, next);
245
- this._reports.push({
246
- resource,
247
- qty: safeQty,
248
- productId: options?.productId,
249
- tenantId: options?.tenantId,
208
+ constructor(e = {}) {
209
+ this._quotas = new Map(Object.entries(e));
210
+ }
211
+ async report(e, r = 1, i) {
212
+ if (!e) throw new Error("resource required");
213
+ let o = Number.isFinite(r) && r > 0 ? Math.floor(r) : 1, c = (this._counters.get(e) ?? 0) + o;
214
+ this._counters.set(e, c), this._reports.push({
215
+ resource: e,
216
+ qty: o,
217
+ productId: i?.productId,
218
+ tenantId: i?.tenantId,
250
219
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
251
220
  });
252
- const limitFixture = this._quotas.get(resource);
253
- const limit = limitFixture?.limit ?? -1;
221
+ let d = this._quotas.get(e)?.limit ?? -1;
254
222
  return {
255
223
  ok: true,
256
- counterId: `mock_${resource}`,
257
- value: next,
258
- limit,
259
- remaining: limit < 0 ? -1 : Math.max(0, limit - next),
260
- status: limit > 0 && next >= limit ? "read_only_overlimit" : "ok"
224
+ counterId: `mock_${e}`,
225
+ value: c,
226
+ limit: d,
227
+ remaining: d < 0 ? -1 : Math.max(0, d - c),
228
+ status: d > 0 && c >= d ? "read_only_overlimit" : "ok"
261
229
  };
262
230
  }
263
- async check(resource, _options) {
264
- const quota = this._quotas.get(resource);
265
- const used = this._counters.get(resource) ?? 0;
266
- if (!quota) {
231
+ async check(e, r) {
232
+ let i = this._quotas.get(e), o = this._counters.get(e) ?? 0;
233
+ if (!i)
267
234
  return { allowed: true, reason: "granted", limit: -1, remaining: -1, behavior: null };
268
- }
269
- const remaining = quota.limit < 0 ? -1 : Math.max(0, quota.limit - used);
270
- if (remaining === 0) {
271
- return {
272
- allowed: false,
273
- reason: "limit_exceeded",
274
- limit: quota.limit,
275
- remaining: 0,
276
- behavior: "readonly"
277
- };
278
- }
279
- return {
235
+ let a = i.limit < 0 ? -1 : Math.max(0, i.limit - o);
236
+ return a === 0 ? {
237
+ allowed: false,
238
+ reason: "limit_exceeded",
239
+ limit: i.limit,
240
+ remaining: 0,
241
+ behavior: "readonly"
242
+ } : {
280
243
  allowed: true,
281
244
  reason: "granted",
282
- limit: quota.limit,
283
- remaining,
245
+ limit: i.limit,
246
+ remaining: a,
284
247
  behavior: null
285
248
  };
286
249
  }
@@ -293,40 +256,37 @@ var MockUsage = class {
293
256
  return this.__getReports();
294
257
  }
295
258
  /** Test helper — substitui quota fixture. */
296
- __setQuota(metric, quota) {
297
- this._quotas.set(metric, quota);
259
+ __setQuota(e, r) {
260
+ this._quotas.set(e, r);
298
261
  }
299
262
  /** Test helper — limpa estado. */
300
263
  __reset() {
301
- this._reports = [];
302
- this._quotas.clear();
303
- this._counters.clear();
264
+ this._reports = [], this._quotas.clear(), this._counters.clear();
304
265
  }
305
- };
306
- var MockSupport = class {
266
+ }, P = class {
307
267
  _tickets = [];
308
268
  _ticketSeq = 0;
309
- async createTicket(input) {
310
- if (!input?.subject) throw new NeetruError("validation_failed", "subject required");
311
- if (!input?.message) throw new NeetruError("validation_failed", "message required");
312
- if (!input?.productSlug) {
313
- throw new NeetruError(
269
+ async createTicket(e) {
270
+ if (!e?.subject) throw new l("validation_failed", "subject required");
271
+ if (!e?.message) throw new l("validation_failed", "message required");
272
+ if (!e?.productSlug)
273
+ throw new l(
314
274
  "validation_failed",
315
275
  "productSlug required (3.0 breaking \u2014 pass to input or set on createNeetruClient)"
316
276
  );
317
- }
318
- const ticket = {
277
+ let r = {
319
278
  id: `mock-ticket-${++this._ticketSeq}`,
320
- subject: input.subject,
321
- message: input.message,
322
- severity: input.severity ?? "normal",
279
+ subject: e.subject,
280
+ message: e.message,
281
+ severity: e.severity ?? "normal",
323
282
  status: "open",
324
283
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
325
- productSlug: input.productSlug,
284
+ productSlug: e.productSlug,
326
285
  // 3.4 (product-feedback): espelha o contrato — campos novos nullable,
327
286
  // populados a partir do input quando fornecidos.
328
- pageUrl: typeof input.pageUrl === "string" ? input.pageUrl : null,
329
- screenshotUrl: typeof input.screenshotBase64 === "string" ? input.screenshotBase64 : null,
287
+ pageUrl: typeof e.pageUrl == "string" ? e.pageUrl : null,
288
+ screenshotUrl: typeof e.screenshotBase64 == "string" ? e.screenshotBase64 : null,
289
+ screenshotAttached: typeof e.screenshotBase64 == "string",
330
290
  resolvedAt: null,
331
291
  resolvedNotifiedAt: null,
332
292
  satisfactionAttempts: 0,
@@ -335,146 +295,116 @@ var MockSupport = class {
335
295
  rating: null,
336
296
  satisfactionComment: null
337
297
  };
338
- this._tickets.push(ticket);
339
- return ticket;
298
+ return this._tickets.push(r), r;
340
299
  }
341
- async listMyTickets(options) {
342
- if (options?.productSlug) {
343
- return this._tickets.filter((t) => t.productSlug === options.productSlug);
344
- }
345
- return [...this._tickets];
300
+ async listMyTickets(e) {
301
+ return e?.productSlug ? this._tickets.filter((r) => r.productSlug === e.productSlug) : [...this._tickets];
346
302
  }
347
- _find(ticketId) {
348
- if (!ticketId || typeof ticketId !== "string") {
349
- throw new NeetruError("validation_failed", "ticketId is required");
350
- }
351
- const ticket = this._tickets.find((t) => t.id === ticketId);
352
- if (!ticket) {
353
- throw new NeetruError("not_found", `ticket ${ticketId} not found`);
354
- }
355
- return ticket;
303
+ _find(e) {
304
+ if (!e || typeof e != "string")
305
+ throw new l("validation_failed", "ticketId is required");
306
+ let r = this._tickets.find((i) => i.id === e);
307
+ if (!r)
308
+ throw new l("not_found", `ticket ${e} not found`);
309
+ return r;
356
310
  }
357
311
  /** 3.4: espelha `markResolvedSeen` — idempotente (só seta se ainda null). */
358
- async markResolvedSeen(ticketId) {
359
- const ticket = this._find(ticketId);
360
- if (ticket.resolvedNotifiedAt === null) {
361
- ticket.resolvedNotifiedAt = (/* @__PURE__ */ new Date()).toISOString();
362
- }
312
+ async markResolvedSeen(e) {
313
+ let r = this._find(e);
314
+ r.resolvedNotifiedAt === null && (r.resolvedNotifiedAt = (/* @__PURE__ */ new Date()).toISOString());
363
315
  }
364
316
  /** 3.4: espelha `recordSatisfactionAttempt` — incrementa contador. */
365
- async recordSatisfactionAttempt(ticketId) {
366
- const ticket = this._find(ticketId);
367
- ticket.satisfactionAttempts += 1;
368
- ticket.lastSatisfactionAttemptAt = (/* @__PURE__ */ new Date()).toISOString();
317
+ async recordSatisfactionAttempt(e) {
318
+ let r = this._find(e);
319
+ r.satisfactionAttempts += 1, r.lastSatisfactionAttemptAt = (/* @__PURE__ */ new Date()).toISOString();
369
320
  }
370
321
  /** 3.4: espelha `submitSatisfaction` — terminal, seta `satisfiedAt`. */
371
- async submitSatisfaction(input) {
372
- if (!input || typeof input !== "object") {
373
- throw new NeetruError("validation_failed", "input is required");
374
- }
375
- if (input.rating !== null && input.rating !== void 0) {
376
- if (!Number.isInteger(input.rating) || input.rating < 1 || input.rating > 5) {
377
- throw new NeetruError("validation_failed", "rating must be an integer 1-5 or null");
378
- }
379
- }
380
- if (typeof input.comment === "string" && input.comment.length > 2e3) {
381
- throw new NeetruError("validation_failed", "comment max 2000 chars");
382
- }
383
- const ticket = this._find(input.ticketId);
384
- ticket.satisfiedAt = (/* @__PURE__ */ new Date()).toISOString();
385
- ticket.rating = input.rating ?? null;
386
- ticket.satisfactionComment = input.comment ?? null;
322
+ async submitSatisfaction(e) {
323
+ if (!e || typeof e != "object")
324
+ throw new l("validation_failed", "input is required");
325
+ if (e.rating !== null && e.rating !== void 0 && (!Number.isInteger(e.rating) || e.rating < 1 || e.rating > 5))
326
+ throw new l("validation_failed", "rating must be an integer 1-5 or null");
327
+ if (typeof e.comment == "string" && e.comment.length > 2e3)
328
+ throw new l("validation_failed", "comment max 2000 chars");
329
+ let r = this._find(e.ticketId);
330
+ r.satisfiedAt = (/* @__PURE__ */ new Date()).toISOString(), r.rating = e.rating ?? null, r.satisfactionComment = e.comment ?? null;
387
331
  }
388
332
  /** 3.4: espelha `contestTicket` — reabre e zera o ciclo de satisfação. */
389
- async contestTicket(ticketId, opts) {
390
- if (typeof opts?.comment === "string" && opts.comment.length > 2e3) {
391
- throw new NeetruError("validation_failed", "comment max 2000 chars");
392
- }
393
- const ticket = this._find(ticketId);
394
- ticket.status = "open";
395
- ticket.resolvedAt = null;
396
- ticket.resolvedNotifiedAt = null;
397
- ticket.satisfactionAttempts = 0;
398
- ticket.lastSatisfactionAttemptAt = null;
399
- ticket.satisfiedAt = null;
400
- ticket.rating = null;
401
- ticket.satisfactionComment = null;
333
+ async contestTicket(e, r) {
334
+ if (typeof r?.comment == "string" && r.comment.length > 2e3)
335
+ throw new l("validation_failed", "comment max 2000 chars");
336
+ let i = this._find(e);
337
+ i.status = "open", i.resolvedAt = null, i.resolvedNotifiedAt = null, i.satisfactionAttempts = 0, i.lastSatisfactionAttemptAt = null, i.satisfiedAt = null, i.rating = null, i.satisfactionComment = null;
402
338
  }
403
339
  /** Test helper. */
404
340
  __reset() {
405
- this._tickets = [];
406
- this._ticketSeq = 0;
341
+ this._tickets = [], this._ticketSeq = 0;
407
342
  }
408
- };
409
- var MockEntitlements = class {
343
+ }, D = class {
410
344
  _denied = /* @__PURE__ */ new Set();
411
- async check(productSlug, feature) {
412
- return !this._denied.has(`${productSlug}:${feature}`);
345
+ async check(e, r) {
346
+ return !this._denied.has(`${e}:${r}`);
413
347
  }
414
- async checkDetailed(productSlug, feature) {
415
- const allowed = await this.check(productSlug, feature);
348
+ async checkDetailed(e, r) {
349
+ let i = await this.check(e, r);
416
350
  return {
417
- allowed,
418
- productSlug,
419
- feature,
420
- reason: allowed ? "granted" : "mock_denied"
351
+ allowed: i,
352
+ productSlug: e,
353
+ feature: r,
354
+ reason: i ? "granted" : "mock_denied"
421
355
  };
422
356
  }
423
357
  /** Test helper. */
424
- __deny(productSlug, feature) {
425
- this._denied.add(`${productSlug}:${feature}`);
358
+ __deny(e, r) {
359
+ this._denied.add(`${e}:${r}`);
426
360
  }
427
361
  /** Test helper. */
428
362
  __reset() {
429
363
  this._denied.clear();
430
364
  }
431
- };
432
- var MockDb = class {
365
+ }, C = class {
433
366
  _store = /* @__PURE__ */ new Map();
434
367
  _fixtures;
435
- constructor(initialFixtures = {}) {
436
- this._fixtures = new Map(Object.entries(initialFixtures));
437
- }
438
- collection(name) {
439
- const _store = this._store;
440
- const _fixtures = this._fixtures;
441
- if (!_store.has(name)) {
442
- const init = _fixtures.get(name);
443
- _store.set(
444
- name,
445
- init ? new Map(Object.entries(init)) : /* @__PURE__ */ new Map()
368
+ constructor(e = {}) {
369
+ this._fixtures = new Map(Object.entries(e));
370
+ }
371
+ collection(e) {
372
+ let r = this._store, i = this._fixtures;
373
+ if (!r.has(e)) {
374
+ let s = i.get(e);
375
+ r.set(
376
+ e,
377
+ s ? new Map(Object.entries(s)) : /* @__PURE__ */ new Map()
446
378
  );
447
379
  }
448
- const coll = _store.get(name);
449
- const matchesFilter = (doc, f) => {
450
- const v = doc[f.field];
451
- switch (f.op) {
380
+ let o = r.get(e), a = (s, t) => {
381
+ let n = s[t.field];
382
+ switch (t.op) {
452
383
  case "==":
453
- return v === f.value;
384
+ return n === t.value;
454
385
  case "!=":
455
- return v !== f.value;
386
+ return n !== t.value;
456
387
  case "<":
457
- return typeof v === "number" && typeof f.value === "number" && v < f.value;
388
+ return typeof n == "number" && typeof t.value == "number" && n < t.value;
458
389
  case "<=":
459
- return typeof v === "number" && typeof f.value === "number" && v <= f.value;
390
+ return typeof n == "number" && typeof t.value == "number" && n <= t.value;
460
391
  case ">":
461
- return typeof v === "number" && typeof f.value === "number" && v > f.value;
392
+ return typeof n == "number" && typeof t.value == "number" && n > t.value;
462
393
  case ">=":
463
- return typeof v === "number" && typeof f.value === "number" && v >= f.value;
394
+ return typeof n == "number" && typeof t.value == "number" && n >= t.value;
464
395
  case "in":
465
- return Array.isArray(f.value) && f.value.includes(v);
396
+ return Array.isArray(t.value) && t.value.includes(n);
466
397
  case "array-contains":
467
- return Array.isArray(v) && v.includes(f.value);
398
+ return Array.isArray(n) && n.includes(t.value);
468
399
  case "not-in":
469
- return Array.isArray(f.value) && !f.value.includes(v);
400
+ return Array.isArray(t.value) && !t.value.includes(n);
470
401
  default:
471
402
  return true;
472
403
  }
473
- };
474
- let autoSeq = 0;
475
- function buildListResult(items) {
404
+ }, c = 0;
405
+ function y(s) {
476
406
  return {
477
- docs: items.map((d, i) => ({ id: d.id ?? String(i), data: d })),
407
+ docs: s.map((t, n) => ({ id: t.id ?? String(n), data: t })),
478
408
  nextCursor: null,
479
409
  fromCache: false,
480
410
  stale: false,
@@ -482,141 +412,111 @@ var MockDb = class {
482
412
  changes: []
483
413
  };
484
414
  }
485
- function buildGetResult(data, id) {
415
+ function d(s, t) {
486
416
  return {
487
- docs: [{ id, data }],
417
+ docs: [{ id: t, data: s }],
488
418
  fromCache: false,
489
419
  stale: false,
490
420
  hasPendingWrites: false,
491
421
  changes: []
492
422
  };
493
423
  }
494
- const ref = {
495
- async list(q) {
496
- let items = Array.from(coll.values());
497
- if (q?.where && q.where.length > 0) {
498
- items = items.filter(
499
- (doc) => q.where.every((f) => matchesFilter(doc, f))
500
- );
501
- }
502
- if (q?.orderBy) {
503
- const { field, direction } = q.orderBy;
504
- items = items.slice().sort((a, b) => {
505
- const av = a[field];
506
- const bv = b[field];
507
- const avs = av ?? "";
508
- const bvs = bv ?? "";
509
- const cmp = avs < bvs ? -1 : avs > bvs ? 1 : 0;
510
- return direction === "desc" ? -cmp : cmp;
424
+ let f = {
425
+ async list(s) {
426
+ let t = Array.from(o.values());
427
+ if (s?.where && s.where.length > 0 && (t = t.filter(
428
+ (u) => s.where.every((g) => a(u, g))
429
+ )), s?.orderBy) {
430
+ let { field: u, direction: g } = s.orderBy;
431
+ t = t.slice().sort((p, b) => {
432
+ let h = p[u], v = b[u], k = h ?? "", w = v ?? "", S = k < w ? -1 : k > w ? 1 : 0;
433
+ return g === "desc" ? -S : S;
511
434
  });
512
435
  }
513
- const limit = q?.limit ?? 20;
514
- items = items.slice(0, limit);
515
- return buildListResult(items);
436
+ let n = s?.limit ?? 20;
437
+ return t = t.slice(0, n), y(t);
516
438
  },
517
- async get(id) {
518
- const data = coll.get(id);
519
- if (!data) return null;
520
- return buildGetResult(data, id);
439
+ async get(s) {
440
+ let t = o.get(s);
441
+ return t ? d(t, s) : null;
521
442
  },
522
- async add(data) {
523
- const id = `mock-${++autoSeq}-${Math.random().toString(36).slice(2, 8)}`;
524
- coll.set(id, { ...data, id });
525
- return { ok: true, id };
443
+ async add(s) {
444
+ let t = `mock-${++c}-${Math.random().toString(36).slice(2, 8)}`;
445
+ return o.set(t, { ...s, id: t }), { ok: true, id: t };
526
446
  },
527
- async set(id, data) {
528
- coll.set(id, { ...data, id });
529
- return { ok: true };
447
+ async set(s, t) {
448
+ return o.set(s, { ...t, id: s }), { ok: true };
530
449
  },
531
- async update(id, data) {
532
- const cur = coll.get(id);
533
- if (!cur) {
534
- coll.set(id, { ...data, id });
535
- } else {
536
- coll.set(id, { ...cur, ...data });
537
- }
538
- return { ok: true };
450
+ async update(s, t) {
451
+ let n = o.get(s);
452
+ return n ? o.set(s, { ...n, ...t }) : o.set(s, { ...t, id: s }), { ok: true };
539
453
  },
540
- async remove(id) {
541
- coll.delete(id);
542
- return { ok: true };
454
+ async remove(s) {
455
+ return o.delete(s), { ok: true };
543
456
  },
544
- async batch(ops) {
545
- for (const op of ops) {
546
- const collMap = coll;
547
- if (op.kind === "add") {
548
- const id = `mock-batch-${++autoSeq}`;
549
- collMap.set(id, { ...op.data, id });
550
- } else if (op.kind === "set") {
551
- collMap.set(op.id, { ...op.data, id: op.id });
552
- } else if (op.kind === "update") {
553
- const cur = collMap.get(op.id);
554
- collMap.set(op.id, { ...cur ?? {}, ...op.data, id: op.id });
555
- } else if (op.kind === "remove") {
556
- collMap.delete(op.id);
557
- }
457
+ async batch(s) {
458
+ for (let t of s) {
459
+ let n = o;
460
+ if (t.kind === "add") {
461
+ let u = `mock-batch-${++c}`;
462
+ n.set(u, { ...t.data, id: u });
463
+ } else if (t.kind === "set")
464
+ n.set(t.id, { ...t.data, id: t.id });
465
+ else if (t.kind === "update") {
466
+ let u = n.get(t.id);
467
+ n.set(t.id, { ...u ?? {}, ...t.data, id: t.id });
468
+ } else t.kind === "remove" && n.delete(t.id);
558
469
  }
559
470
  return { ok: true };
560
471
  },
561
- onDoc(id, cb) {
562
- const data = coll.get(id);
563
- Promise.resolve().then(() => cb(data ? data : null));
564
- return () => {
472
+ onDoc(s, t) {
473
+ let n = o.get(s);
474
+ return Promise.resolve().then(() => t(n || null)), () => {
565
475
  };
566
476
  },
567
- onSnapshot(q, cb) {
568
- Promise.resolve().then(async () => {
569
- const snap = await ref.list(q);
570
- cb(snap);
571
- });
572
- return () => {
477
+ onSnapshot(s, t) {
478
+ return Promise.resolve().then(async () => {
479
+ let n = await f.list(s);
480
+ t(n);
481
+ }), () => {
573
482
  };
574
483
  },
575
- doc(id) {
484
+ doc(s) {
576
485
  return {
577
486
  get id() {
578
- return id;
487
+ return s;
579
488
  },
580
489
  get path() {
581
- return `${name}/${id}`;
490
+ return `${e}/${s}`;
582
491
  },
583
492
  get collection() {
584
- return name;
493
+ return e;
585
494
  },
586
495
  async get() {
587
- const data = coll.get(id);
588
- if (!data) return null;
589
- return buildGetResult(data, id);
496
+ let t = o.get(s);
497
+ return t ? d(t, s) : null;
590
498
  },
591
- async set(data) {
592
- coll.set(id, { ...data, id });
593
- return { ok: true };
499
+ async set(t) {
500
+ return o.set(s, { ...t, id: s }), { ok: true };
594
501
  },
595
- async update(data) {
596
- const cur = coll.get(id);
597
- if (!cur) {
598
- coll.set(id, { ...data, id });
599
- } else {
600
- coll.set(id, { ...cur, ...data });
601
- }
602
- return { ok: true };
502
+ async update(t) {
503
+ let n = o.get(s);
504
+ return n ? o.set(s, { ...n, ...t }) : o.set(s, { ...t, id: s }), { ok: true };
603
505
  },
604
506
  async remove() {
605
- coll.delete(id);
606
- return { ok: true };
507
+ return o.delete(s), { ok: true };
607
508
  },
608
- onSnapshot(cb) {
609
- const data = coll.get(id);
610
- Promise.resolve().then(
611
- () => cb(data ? buildGetResult(data, id) : null)
612
- );
613
- return () => {
509
+ onSnapshot(t) {
510
+ let n = o.get(s);
511
+ return Promise.resolve().then(
512
+ () => t(n ? d(n, s) : null)
513
+ ), () => {
614
514
  };
615
515
  }
616
516
  };
617
517
  }
618
518
  };
619
- return ref;
519
+ return f;
620
520
  }
621
521
  /**
622
522
  * Implementa SQL in-memory para testes — preferência: `@electric-sql/pglite`
@@ -637,80 +537,66 @@ var MockDb = class {
637
537
  *
638
538
  * Use `options.initSql` pra rodar DDL no setup (CREATE TABLE etc).
639
539
  */
640
- async sql(schema, options) {
641
- const ddls = options?.initSql ? Array.isArray(options.initSql) ? options.initSql : [options.initSql] : [];
540
+ async sql(e, r) {
541
+ let i = r?.initSql ? Array.isArray(r.initSql) ? r.initSql : [r.initSql] : [];
642
542
  try {
643
- const pglitePkg = await import(
543
+ let n = await import(
644
544
  /* webpackIgnore: true */
645
545
  '@electric-sql/pglite'
646
- );
647
- const drizzlePglitePkg = await import(
546
+ ), u = await import(
648
547
  /* webpackIgnore: true */
649
548
  'drizzle-orm/pglite'
650
- );
651
- const pglite = await pglitePkg.PGlite.create();
652
- for (const ddl of ddls) {
653
- await pglite.query(ddl);
654
- }
655
- const pgliteOrm = drizzlePglitePkg.drizzle(pglite, { schema });
656
- const ormAsNode = pgliteOrm;
549
+ ), g = await n.PGlite.create();
550
+ for (let h of i)
551
+ await g.query(h);
552
+ let p = u.drizzle(g, { schema: e });
657
553
  return {
658
- orm: ormAsNode,
659
- async transaction(fn, _opts) {
660
- return await pgliteOrm.transaction(fn);
554
+ orm: p,
555
+ async transaction(h, v) {
556
+ return await p.transaction(h);
661
557
  },
662
558
  async close() {
663
- if (!pglite.closed) await pglite.close();
559
+ g.closed || await g.close();
664
560
  }
665
561
  };
666
- } catch (_pgliteErr) {
562
+ } catch {
667
563
  }
668
- let pgMemMod;
669
- let drizzlePg;
564
+ let o, a;
670
565
  try {
671
- pgMemMod = await import(
566
+ o = await import(
672
567
  /* webpackIgnore: true */
673
568
  'pg-mem'
674
- );
675
- drizzlePg = await import(
569
+ ), a = await import(
676
570
  /* webpackIgnore: true */
677
571
  'drizzle-orm/node-postgres'
678
572
  );
679
573
  } catch {
680
- throw new NeetruDbError(
574
+ throw new _(
681
575
  "db_unavailable",
682
576
  "[MockDb] sql() requer @electric-sql/pglite OU pg-mem + drizzle-orm + pg como devDependencies. Preferido: npm install -D @electric-sql/pglite drizzle-orm. Alternativo: npm install -D pg-mem drizzle-orm pg."
683
577
  );
684
578
  }
685
- const memDb = pgMemMod.newDb({ noAstCoverageCheck: true });
686
- if (ddls.length > 0) {
687
- for (const ddl of ddls) {
688
- memDb.public.none(ddl);
579
+ let c = o.newDb({ noAstCoverageCheck: true });
580
+ if (i.length > 0)
581
+ for (let n of i)
582
+ c.public.none(n);
583
+ let d = c.adapters.createPg().Pool, f = new d(), s = f.query.bind(f);
584
+ f.query = (n, ...u) => {
585
+ if (n !== null && typeof n == "object") {
586
+ let p = n.types;
587
+ p !== null && typeof p == "object" && "getTypeParser" in p && delete p.getTypeParser;
689
588
  }
690
- }
691
- const adapters = memDb.adapters.createPg();
692
- const PoolCtor = adapters.Pool;
693
- const pool = new PoolCtor();
694
- const origQuery = pool.query.bind(pool);
695
- pool.query = (q, ...rest) => {
696
- if (q !== null && typeof q === "object") {
697
- const queryObj = q;
698
- const types = queryObj["types"];
699
- if (types !== null && typeof types === "object" && "getTypeParser" in types) {
700
- delete types["getTypeParser"];
701
- }
702
- }
703
- return origQuery(q, ...rest);
589
+ return s(n, ...u);
704
590
  };
705
- const orm = drizzlePg.drizzle(pool, { schema });
591
+ let t = a.drizzle(f, { schema: e });
706
592
  return {
707
- orm,
708
- async transaction(fn, _opts) {
709
- return await orm.transaction(fn);
593
+ orm: t,
594
+ async transaction(n, u) {
595
+ return await t.transaction(n);
710
596
  },
711
597
  async close() {
712
- const p = pool;
713
- if (p.end) await p.end();
598
+ let n = f;
599
+ n.end && await n.end();
714
600
  }
715
601
  };
716
602
  }
@@ -722,7 +608,7 @@ var MockDb = class {
722
608
  isLeaderTab: true
723
609
  };
724
610
  }
725
- onSyncStateChanged(_cb) {
611
+ onSyncStateChanged(e) {
726
612
  return () => {
727
613
  };
728
614
  }
@@ -735,65 +621,53 @@ var MockDb = class {
735
621
  return [];
736
622
  }
737
623
  /** Test helper — substitui fixture inteira de uma collection. */
738
- __setFixture(name, items) {
739
- this._store.set(name, new Map(Object.entries(items)));
624
+ __setFixture(e, r) {
625
+ this._store.set(e, new Map(Object.entries(r)));
740
626
  }
741
627
  /** Test helper — reset total. */
742
628
  __reset() {
743
629
  this._store.clear();
744
630
  }
745
- };
746
- var MockAi = class {
631
+ }, N = class {
747
632
  _calls = [];
748
633
  /** Extrai texto de um content string OU array multimodal (imagens ignoradas). */
749
- _contentToText(content) {
750
- if (typeof content === "string") return content;
751
- if (!Array.isArray(content)) return "";
752
- return content.filter((p) => !!p && p.type === "text").map((p) => p.text).join(" ");
753
- }
754
- _reply(opts) {
755
- const last = [...opts.messages].reverse().find((m) => m.role === "user") ?? opts.messages[opts.messages.length - 1];
756
- const content = this._contentToText(last?.content);
757
- return content ? `mock-reply: ${content}` : "mock-reply";
758
- }
759
- async chat(opts) {
760
- if (!opts || typeof opts.model !== "string" || opts.model.length === 0) {
761
- throw new NeetruError("validation_failed", "ai: model is required");
762
- }
763
- if (!Array.isArray(opts.messages) || opts.messages.length === 0) {
764
- throw new NeetruError("validation_failed", "ai: messages must be a non-empty array");
765
- }
766
- this._calls.push({ model: opts.model, messages: opts.messages, stream: false });
767
- const text = this._reply(opts);
768
- const promptTokens = opts.messages.reduce((n, m) => n + this._contentToText(m.content).length, 0);
769
- const completionTokens = text.length;
634
+ _contentToText(e) {
635
+ return typeof e == "string" ? e : Array.isArray(e) ? e.filter((r) => !!r && r.type === "text").map((r) => r.text).join(" ") : "";
636
+ }
637
+ _reply(e) {
638
+ let r = [...e.messages].reverse().find((o) => o.role === "user") ?? e.messages[e.messages.length - 1], i = this._contentToText(r?.content);
639
+ return i ? `mock-reply: ${i}` : "mock-reply";
640
+ }
641
+ async chat(e) {
642
+ if (!e || typeof e.model != "string" || e.model.length === 0)
643
+ throw new l("validation_failed", "ai: model is required");
644
+ if (!Array.isArray(e.messages) || e.messages.length === 0)
645
+ throw new l("validation_failed", "ai: messages must be a non-empty array");
646
+ this._calls.push({ model: e.model, messages: e.messages, stream: false });
647
+ let r = this._reply(e), i = e.messages.reduce((a, c) => a + this._contentToText(c.content).length, 0), o = r.length;
770
648
  return {
771
- choices: [{ message: { role: "assistant", content: text }, finish_reason: "stop" }],
649
+ choices: [{ message: { role: "assistant", content: r }, finish_reason: "stop" }],
772
650
  usage: {
773
- prompt_tokens: promptTokens,
774
- completion_tokens: completionTokens,
775
- total_tokens: promptTokens + completionTokens
651
+ prompt_tokens: i,
652
+ completion_tokens: o,
653
+ total_tokens: i + o
776
654
  },
777
- model: opts.model,
655
+ model: e.model,
778
656
  provider: "mock",
779
657
  usageWarning: false
780
658
  };
781
659
  }
782
- stream(opts) {
783
- if (!opts || typeof opts.model !== "string" || opts.model.length === 0) {
784
- throw new NeetruError("validation_failed", "ai: model is required");
785
- }
786
- if (!Array.isArray(opts.messages) || opts.messages.length === 0) {
787
- throw new NeetruError("validation_failed", "ai: messages must be a non-empty array");
788
- }
789
- this._calls.push({ model: opts.model, messages: opts.messages, stream: true });
790
- const text = this._reply(opts);
791
- const parts = text.match(/\S+\s*/g) ?? [text];
792
- const meta = Promise.resolve({ provider: "mock", usageWarning: false });
793
- async function* iterate() {
794
- for (const p of parts) yield p;
660
+ stream(e) {
661
+ if (!e || typeof e.model != "string" || e.model.length === 0)
662
+ throw new l("validation_failed", "ai: model is required");
663
+ if (!Array.isArray(e.messages) || e.messages.length === 0)
664
+ throw new l("validation_failed", "ai: messages must be a non-empty array");
665
+ this._calls.push({ model: e.model, messages: e.messages, stream: true });
666
+ let r = this._reply(e), i = r.match(/\S+\s*/g) ?? [r], o = Promise.resolve({ provider: "mock", usageWarning: false });
667
+ async function* a() {
668
+ for (let c of i) yield c;
795
669
  }
796
- return { meta, [Symbol.asyncIterator]: iterate };
670
+ return { meta: o, [Symbol.asyncIterator]: a };
797
671
  }
798
672
  /** Test helper — histórico de chamadas. */
799
673
  __getCalls() {
@@ -801,6 +675,4 @@ var MockAi = class {
801
675
  }
802
676
  };
803
677
 
804
- export { DEV_FIXTURE_USER, MockAi, MockAuth, MockDb, MockEntitlements, MockSupport, MockUsage };
805
- //# sourceMappingURL=mocks.mjs.map
806
- //# sourceMappingURL=mocks.mjs.map
678
+ export { x as DEV_FIXTURE_USER, N as MockAi, A as MockAuth, C as MockDb, D as MockEntitlements, P as MockSupport, T as MockUsage };