@neetru/sdk 3.1.7 → 3.1.10

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 (69) hide show
  1. package/dist/ai.cjs +304 -0
  2. package/dist/ai.cjs.map +1 -0
  3. package/dist/ai.d.cts +3 -0
  4. package/dist/ai.d.ts +3 -0
  5. package/dist/ai.mjs +302 -0
  6. package/dist/ai.mjs.map +1 -0
  7. package/dist/auth.cjs +348 -23
  8. package/dist/auth.cjs.map +1 -1
  9. package/dist/auth.d.cts +1 -1
  10. package/dist/auth.d.ts +1 -1
  11. package/dist/auth.mjs +348 -23
  12. package/dist/auth.mjs.map +1 -1
  13. package/dist/catalog.d.cts +1 -1
  14. package/dist/catalog.d.ts +1 -1
  15. package/dist/checkout.d.cts +1 -1
  16. package/dist/checkout.d.ts +1 -1
  17. package/dist/db-react.d.cts +1 -1
  18. package/dist/db-react.d.ts +1 -1
  19. package/dist/db.cjs +55 -16
  20. package/dist/db.cjs.map +1 -1
  21. package/dist/db.d.cts +1 -1
  22. package/dist/db.d.ts +1 -1
  23. package/dist/db.mjs +55 -16
  24. package/dist/db.mjs.map +1 -1
  25. package/dist/entitlements.d.cts +1 -1
  26. package/dist/entitlements.d.ts +1 -1
  27. package/dist/firestore-compat.cjs +4 -9
  28. package/dist/firestore-compat.cjs.map +1 -1
  29. package/dist/firestore-compat.d.cts +2 -2
  30. package/dist/firestore-compat.d.ts +2 -2
  31. package/dist/firestore-compat.mjs +4 -9
  32. package/dist/firestore-compat.mjs.map +1 -1
  33. package/dist/index.cjs +351 -24
  34. package/dist/index.cjs.map +1 -1
  35. package/dist/index.d.cts +3 -3
  36. package/dist/index.d.ts +3 -3
  37. package/dist/index.mjs +350 -25
  38. package/dist/index.mjs.map +1 -1
  39. package/dist/mocks.cjs +52 -0
  40. package/dist/mocks.cjs.map +1 -1
  41. package/dist/mocks.d.cts +20 -2
  42. package/dist/mocks.d.ts +20 -2
  43. package/dist/mocks.mjs +52 -1
  44. package/dist/mocks.mjs.map +1 -1
  45. package/dist/notifications.d.cts +1 -1
  46. package/dist/notifications.d.ts +1 -1
  47. package/dist/react.cjs.map +1 -1
  48. package/dist/react.d.cts +1 -1
  49. package/dist/react.d.ts +1 -1
  50. package/dist/react.mjs.map +1 -1
  51. package/dist/support.d.cts +1 -1
  52. package/dist/support.d.ts +1 -1
  53. package/dist/telemetry.cjs +5 -1
  54. package/dist/telemetry.cjs.map +1 -1
  55. package/dist/telemetry.d.cts +1 -1
  56. package/dist/telemetry.d.ts +1 -1
  57. package/dist/telemetry.mjs +5 -1
  58. package/dist/telemetry.mjs.map +1 -1
  59. package/dist/{types-BXvGHppn.d.cts → types-Cog9S-tC.d.cts} +103 -3
  60. package/dist/{types-CQVak2a3.d.ts → types-dagUxKYz.d.ts} +103 -3
  61. package/dist/usage.cjs +2 -2
  62. package/dist/usage.cjs.map +1 -1
  63. package/dist/usage.d.cts +1 -1
  64. package/dist/usage.d.ts +1 -1
  65. package/dist/usage.mjs +2 -2
  66. package/dist/usage.mjs.map +1 -1
  67. package/dist/webhooks.d.cts +1 -1
  68. package/dist/webhooks.d.ts +1 -1
  69. package/package.json +6 -1
package/dist/index.d.cts CHANGED
@@ -1,7 +1,7 @@
1
1
  export { createNeetruClient } from './auth.cjs';
2
2
  export { NeetruError, NeetruErrorCode } from './errors.cjs';
3
- export { A as AuthNamespace, a as AuthStateListener, C as CatalogListOptions, b as CatalogListResponse, d as CheckoutIntentInfo, e as CheckoutIntentStatus, f as CheckoutNamespace, g as CheckoutStartInput, h as CheckoutStartResult, i as CheckoutTenantType, l as CreateTicketInput, D as DEFAULT_BASE_URL, m as DbBatchOp, n as DbChangeType, o as DbCollectionRef, p as DbDoc, q as DbDocRef, r as DbGetResult, s as DbListResult, t as DbNamespace, u as DbQuery, v as DbSqlLease, w as DbWhereFilter, E as EntitlementCheck, F as FieldSentinel, I as IncrementSentinel, L as ListNotificationsOptions, M as MockCheckout, y as MockNotifications, z as MockWebhooks, N as NEETRU_SENTINEL_KEY, B as NeetruClient, G as NeetruClientConfig, H as NeetruDb, K as NeetruDbEngine, O as NeetruDbError, P as NeetruDbErrorCode, Q as NeetruDbOptions, R as NeetruDbTransport, S as NeetruEnv, T as NeetruSqlClient, U as NeetruUser, V as NotificationScopeOptions, W as NotificationsNamespace, X as Product, Y as ProductNotification, Z as ProductNotificationSeverity, _ as ProductPlan, $ as ProductStatus, a1 as RedirectCallbackResult, a2 as RegisterWebhookInput, a3 as ResolvedConfig, a4 as SendNotificationInput, a5 as ServerTimestampSentinel, a6 as SignInOptions, a8 as SupportNamespace, a9 as SupportSeverity, aa as SupportStatus, ab as SupportTicket, ad as TelemetryEventAck, ae as TelemetryEventInput, ai as UsageEventInput, aj as UsageNamespace, ak as UsageQuota, al as VerifyTokenOptions, am as VerifyWebhookRequestOptions, an as VerifyWebhookRequestResult, ao as WebhookEndpoint, ap as WebhookEvent, aq as WebhookScopeOptions, ar as WebhookTestResult, as as WebhooksNamespace, at as WithFieldSentinels, au as createCheckoutNamespace, av as createNeetruDb, aw as createNotificationsNamespace, ay as createWebhooksNamespace, aA as increment, aB as isFieldSentinel, aC as isIncrementSentinel, aD as isServerTimestampSentinel, aE as serverTimestamp, aF as verifyWebhookRequest, aG as verifyWebhookSignature } from './types-BXvGHppn.cjs';
4
- export { DEV_FIXTURE_USER, MockAuth, MockAuthOptions, MockDb, MockEntitlements, MockSupport, MockUsage } from './mocks.cjs';
3
+ export { A as AiChatChoice, a as AiChatMessage, b as AiChatOptions, c as AiChatResult, d as AiChatUsage, e as AiNamespace, f as AiStreamHandle, g as AiStreamMeta, h as AuthNamespace, i as AuthStateListener, C as CatalogListOptions, j as CatalogListResponse, l as CheckoutIntentInfo, m as CheckoutIntentStatus, n as CheckoutNamespace, o as CheckoutStartInput, p as CheckoutStartResult, q as CheckoutTenantType, t as CreateTicketInput, D as DEFAULT_BASE_URL, u as DbBatchOp, v as DbChangeType, w as DbCollectionRef, x as DbDoc, y as DbDocRef, z as DbGetResult, B as DbListResult, E as DbNamespace, F as DbQuery, G as DbSqlLease, H as DbWhereFilter, J as EntitlementCheck, K as FieldSentinel, L as IncrementSentinel, M as ListNotificationsOptions, N as MockCheckout, O as MockNotifications, P as MockWebhooks, Q as NEETRU_SENTINEL_KEY, R as NeetruClient, S as NeetruClientConfig, T as NeetruDb, V as NeetruDbEngine, W as NeetruDbError, X as NeetruDbErrorCode, Y as NeetruDbOptions, Z as NeetruDbTransport, _ as NeetruEnv, $ as NeetruSqlClient, a0 as NeetruUser, a1 as NotificationScopeOptions, a2 as NotificationsNamespace, a3 as Product, a4 as ProductNotification, a5 as ProductNotificationSeverity, a6 as ProductPlan, a7 as ProductStatus, a9 as RedirectCallbackResult, aa as RegisterWebhookInput, ab as ResolvedConfig, ac as SendNotificationInput, ad as ServerTimestampSentinel, ae as SignInOptions, ag as SupportNamespace, ah as SupportSeverity, ai as SupportStatus, aj as SupportTicket, al as TelemetryEventAck, am as TelemetryEventInput, aq as UsageEventInput, ar as UsageNamespace, as as UsageQuota, at as VerifyTokenOptions, au as VerifyWebhookRequestOptions, av as VerifyWebhookRequestResult, aw as WebhookEndpoint, ax as WebhookEvent, ay as WebhookScopeOptions, az as WebhookTestResult, aA as WebhooksNamespace, aB as WithFieldSentinels, aC as createAiNamespace, aD as createCheckoutNamespace, aE as createNeetruDb, aF as createNotificationsNamespace, aH as createWebhooksNamespace, aJ as increment, aK as isFieldSentinel, aL as isIncrementSentinel, aM as isServerTimestampSentinel, aN as serverTimestamp, aO as verifyWebhookRequest, aP as verifyWebhookSignature } from './types-Cog9S-tC.cjs';
4
+ export { DEV_FIXTURE_USER, MockAi, MockAuth, MockAuthOptions, MockDb, MockEntitlements, MockSupport, MockUsage } from './mocks.cjs';
5
5
  import 'drizzle-orm/node-postgres';
6
6
 
7
7
  /**
@@ -42,6 +42,6 @@ declare function checkCoreVersionCompat(coreVersion: string): void;
42
42
  */
43
43
 
44
44
  /** Versão do SDK — exportada explicitamente pra detecção em runtime. */
45
- declare const VERSION: "3.1.7";
45
+ declare const VERSION: "3.1.10";
46
46
 
47
47
  export { VERSION, checkCoreVersionCompat };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  export { createNeetruClient } from './auth.js';
2
2
  export { NeetruError, NeetruErrorCode } from './errors.js';
3
- export { A as AuthNamespace, a as AuthStateListener, C as CatalogListOptions, b as CatalogListResponse, d as CheckoutIntentInfo, e as CheckoutIntentStatus, f as CheckoutNamespace, g as CheckoutStartInput, h as CheckoutStartResult, i as CheckoutTenantType, l as CreateTicketInput, D as DEFAULT_BASE_URL, m as DbBatchOp, n as DbChangeType, o as DbCollectionRef, p as DbDoc, q as DbDocRef, r as DbGetResult, s as DbListResult, t as DbNamespace, u as DbQuery, v as DbSqlLease, w as DbWhereFilter, E as EntitlementCheck, F as FieldSentinel, I as IncrementSentinel, L as ListNotificationsOptions, M as MockCheckout, y as MockNotifications, z as MockWebhooks, N as NEETRU_SENTINEL_KEY, B as NeetruClient, G as NeetruClientConfig, H as NeetruDb, K as NeetruDbEngine, O as NeetruDbError, P as NeetruDbErrorCode, Q as NeetruDbOptions, R as NeetruDbTransport, S as NeetruEnv, T as NeetruSqlClient, U as NeetruUser, V as NotificationScopeOptions, W as NotificationsNamespace, X as Product, Y as ProductNotification, Z as ProductNotificationSeverity, _ as ProductPlan, $ as ProductStatus, a1 as RedirectCallbackResult, a2 as RegisterWebhookInput, a3 as ResolvedConfig, a4 as SendNotificationInput, a5 as ServerTimestampSentinel, a6 as SignInOptions, a8 as SupportNamespace, a9 as SupportSeverity, aa as SupportStatus, ab as SupportTicket, ad as TelemetryEventAck, ae as TelemetryEventInput, ai as UsageEventInput, aj as UsageNamespace, ak as UsageQuota, al as VerifyTokenOptions, am as VerifyWebhookRequestOptions, an as VerifyWebhookRequestResult, ao as WebhookEndpoint, ap as WebhookEvent, aq as WebhookScopeOptions, ar as WebhookTestResult, as as WebhooksNamespace, at as WithFieldSentinels, au as createCheckoutNamespace, av as createNeetruDb, aw as createNotificationsNamespace, ay as createWebhooksNamespace, aA as increment, aB as isFieldSentinel, aC as isIncrementSentinel, aD as isServerTimestampSentinel, aE as serverTimestamp, aF as verifyWebhookRequest, aG as verifyWebhookSignature } from './types-CQVak2a3.js';
4
- export { DEV_FIXTURE_USER, MockAuth, MockAuthOptions, MockDb, MockEntitlements, MockSupport, MockUsage } from './mocks.js';
3
+ export { A as AiChatChoice, a as AiChatMessage, b as AiChatOptions, c as AiChatResult, d as AiChatUsage, e as AiNamespace, f as AiStreamHandle, g as AiStreamMeta, h as AuthNamespace, i as AuthStateListener, C as CatalogListOptions, j as CatalogListResponse, l as CheckoutIntentInfo, m as CheckoutIntentStatus, n as CheckoutNamespace, o as CheckoutStartInput, p as CheckoutStartResult, q as CheckoutTenantType, t as CreateTicketInput, D as DEFAULT_BASE_URL, u as DbBatchOp, v as DbChangeType, w as DbCollectionRef, x as DbDoc, y as DbDocRef, z as DbGetResult, B as DbListResult, E as DbNamespace, F as DbQuery, G as DbSqlLease, H as DbWhereFilter, J as EntitlementCheck, K as FieldSentinel, L as IncrementSentinel, M as ListNotificationsOptions, N as MockCheckout, O as MockNotifications, P as MockWebhooks, Q as NEETRU_SENTINEL_KEY, R as NeetruClient, S as NeetruClientConfig, T as NeetruDb, V as NeetruDbEngine, W as NeetruDbError, X as NeetruDbErrorCode, Y as NeetruDbOptions, Z as NeetruDbTransport, _ as NeetruEnv, $ as NeetruSqlClient, a0 as NeetruUser, a1 as NotificationScopeOptions, a2 as NotificationsNamespace, a3 as Product, a4 as ProductNotification, a5 as ProductNotificationSeverity, a6 as ProductPlan, a7 as ProductStatus, a9 as RedirectCallbackResult, aa as RegisterWebhookInput, ab as ResolvedConfig, ac as SendNotificationInput, ad as ServerTimestampSentinel, ae as SignInOptions, ag as SupportNamespace, ah as SupportSeverity, ai as SupportStatus, aj as SupportTicket, al as TelemetryEventAck, am as TelemetryEventInput, aq as UsageEventInput, ar as UsageNamespace, as as UsageQuota, at as VerifyTokenOptions, au as VerifyWebhookRequestOptions, av as VerifyWebhookRequestResult, aw as WebhookEndpoint, ax as WebhookEvent, ay as WebhookScopeOptions, az as WebhookTestResult, aA as WebhooksNamespace, aB as WithFieldSentinels, aC as createAiNamespace, aD as createCheckoutNamespace, aE as createNeetruDb, aF as createNotificationsNamespace, aH as createWebhooksNamespace, aJ as increment, aK as isFieldSentinel, aL as isIncrementSentinel, aM as isServerTimestampSentinel, aN as serverTimestamp, aO as verifyWebhookRequest, aP as verifyWebhookSignature } from './types-dagUxKYz.js';
4
+ export { DEV_FIXTURE_USER, MockAi, MockAuth, MockAuthOptions, MockDb, MockEntitlements, MockSupport, MockUsage } from './mocks.js';
5
5
  import 'drizzle-orm/node-postgres';
6
6
 
7
7
  /**
@@ -42,6 +42,6 @@ declare function checkCoreVersionCompat(coreVersion: string): void;
42
42
  */
43
43
 
44
44
  /** Versão do SDK — exportada explicitamente pra detecção em runtime. */
45
- declare const VERSION: "3.1.7";
45
+ declare const VERSION: "3.1.10";
46
46
 
47
47
  export { VERSION, checkCoreVersionCompat };
package/dist/index.mjs CHANGED
@@ -446,7 +446,7 @@ __export(sql_client_exports, {
446
446
  function mapEnvToCore(sdkEnv) {
447
447
  if (sdkEnv === "workspace") return "staging";
448
448
  if (sdkEnv === "prod") return "production";
449
- return "dev";
449
+ return "dev-local";
450
450
  }
451
451
  function createHttpLeaseFetcher(config) {
452
452
  return async (opts) => {
@@ -955,7 +955,11 @@ function createTelemetryNamespace(config) {
955
955
  path: "/api/sdk/v1/telemetry/event",
956
956
  body,
957
957
  requireAuth: true,
958
- retries: 1
958
+ retries: 1,
959
+ // caça-bruxas: retry sem Idempotency-Key duplicava o evento se o
960
+ // servidor já tinha persistido antes da falha transiente. Mesmo
961
+ // padrão do usage.report (key estável por chamada, reusada no retry).
962
+ idempotencyKey: true
959
963
  });
960
964
  } catch (err) {
961
965
  if (typeof console !== "undefined") {
@@ -1159,8 +1163,8 @@ function createUsageNamespace(config) {
1159
1163
  if (!resource || typeof resource !== "string") {
1160
1164
  throw new NeetruError("validation_failed", "resource is required");
1161
1165
  }
1162
- if (!Number.isFinite(qty) || qty <= 0) {
1163
- throw new NeetruError("validation_failed", "qty must be positive integer");
1166
+ if (!Number.isFinite(qty) || qty <= 0 || !Number.isInteger(qty)) {
1167
+ throw new NeetruError("validation_failed", "qty must be a positive integer");
1164
1168
  }
1165
1169
  const productId = options?.productId ?? config.productId;
1166
1170
  const tenantId = options?.tenantId ?? config.tenantId;
@@ -1409,7 +1413,12 @@ function evaluateFilter(data, filter) {
1409
1413
  if (!Array.isArray(value)) return true;
1410
1414
  return !value.includes(fieldValue);
1411
1415
  default:
1412
- return false;
1416
+ if (op === "array-contains-any") {
1417
+ return Array.isArray(fieldValue) && Array.isArray(value) && value.some((v) => fieldValue.includes(v));
1418
+ }
1419
+ throw new Error(
1420
+ `QueryEngine: operador de filtro '${String(op)}' n\xE3o suportado em leitura offline/cache. Use um operador suportado (==, !=, <, <=, >, >=, array-contains, array-contains-any, in, not-in) ou fa\xE7a a query server-side.`
1421
+ );
1413
1422
  }
1414
1423
  }
1415
1424
  function matchesAllFilters(data, filters) {
@@ -1939,7 +1948,7 @@ var MutationQueue = class {
1939
1948
  * - `{ removed: true }` — a mutação existente deve ser deletada (add+remove)
1940
1949
  * - `null` — não é possível coalescir
1941
1950
  */
1942
- tryCoalesce(existing, newOp, newPayload, newBatchId) {
1951
+ tryCoalesce(existing, newOp, newPayload, newBatchId, newUpsert) {
1943
1952
  if (existing.batchId !== newBatchId) return null;
1944
1953
  if (existing.status === "inflight") return null;
1945
1954
  const existingOp = existing.op;
@@ -1947,10 +1956,12 @@ var MutationQueue = class {
1947
1956
  return { removed: true };
1948
1957
  }
1949
1958
  if (existingOp === "update" && newOp === "update") {
1959
+ const mergedUpsert = existing.upsert || newUpsert;
1950
1960
  return {
1951
1961
  coalesced: {
1952
1962
  ...existing,
1953
- payload: { ...existing.payload ?? {}, ...newPayload ?? {} }
1963
+ payload: { ...existing.payload ?? {}, ...newPayload ?? {} },
1964
+ ...mergedUpsert ? { upsert: true } : {}
1954
1965
  }
1955
1966
  };
1956
1967
  }
@@ -1999,13 +2010,13 @@ var MutationQueue = class {
1999
2010
  * Atômico: a persistência final é uma única operação putMutation.
2000
2011
  */
2001
2012
  async enqueue(params) {
2002
- const { collection, op, payload, baseVersion, batchId } = params;
2013
+ const { collection, op, payload, baseVersion, batchId, upsert } = params;
2003
2014
  const docId = params.docId ?? (op === "add" ? generateUUIDv4() : "");
2004
2015
  await this.syncSeq();
2005
2016
  const existingMutations = await this.store.listMutations({ collection, docId });
2006
2017
  const lastQueued = existingMutations.filter((m) => m.status === "queued").sort((a, b) => b.seq - a.seq)[0];
2007
2018
  if (lastQueued !== void 0) {
2008
- const coalesceResult = this.tryCoalesce(lastQueued, op, payload, batchId);
2019
+ const coalesceResult = this.tryCoalesce(lastQueued, op, payload, batchId, upsert);
2009
2020
  if (coalesceResult !== null) {
2010
2021
  if ("removed" in coalesceResult) {
2011
2022
  await this.store.deleteMutation(lastQueued.mutationId);
@@ -2043,7 +2054,9 @@ var MutationQueue = class {
2043
2054
  attempts: 0,
2044
2055
  lastError: null,
2045
2056
  status: "queued",
2046
- batchId
2057
+ batchId,
2058
+ // bug_0273e4dd: só persiste o flag quando verdadeiro (mantém o doc limpo).
2059
+ ...upsert ? { upsert: true } : {}
2047
2060
  };
2048
2061
  await this.store.putMutation(mutation);
2049
2062
  return mutation;
@@ -2383,10 +2396,15 @@ var SyncEngine = class {
2383
2396
  await this._applyRejected(mut, res, busChanges);
2384
2397
  }
2385
2398
  }
2399
+ const processedIds = new Set(result.results.map((r) => r.mutationId));
2400
+ const unprocessed = mutations.filter((m) => !processedIds.has(m.mutationId));
2401
+ for (const mut of unprocessed) {
2402
+ await this._queue.markRetry(mut.mutationId, "partial_push: muta\xE7\xE3o n\xE3o processada no lote");
2403
+ }
2386
2404
  if (busChanges.length > 0) {
2387
2405
  this._bus.emit(busChanges);
2388
2406
  }
2389
- return true;
2407
+ return unprocessed.length === 0;
2390
2408
  }
2391
2409
  /** Atualiza o cache após confirmação de escrita (outcome=confirmed). */
2392
2410
  async _applySyncedDoc(collection, docId, serverVersion, serverTimestamp2, mut, busChanges) {
@@ -3693,7 +3711,7 @@ var DbCollectionRefImpl = class {
3693
3711
  }]);
3694
3712
  return { ok: true };
3695
3713
  }
3696
- async update(id, data) {
3714
+ async update(id, data, opts) {
3697
3715
  assertValidId(id);
3698
3716
  const existing = await this._store.getDoc(this._collection, id);
3699
3717
  const rawData = data;
@@ -3706,7 +3724,9 @@ var DbCollectionRefImpl = class {
3706
3724
  op: "update",
3707
3725
  payload: rawData,
3708
3726
  baseVersion: existing?.meta.serverVersion ?? null,
3709
- batchId: null
3727
+ batchId: null,
3728
+ // bug_0273e4dd: propaga merge-upsert (setDoc({merge:true}) cria-se-ausente).
3729
+ upsert: opts?.upsert
3710
3730
  });
3711
3731
  await this._store.putDoc({
3712
3732
  collection: this._collection,
@@ -3830,7 +3850,9 @@ var DbCollectionRefImpl = class {
3830
3850
  op: "update",
3831
3851
  payload: data,
3832
3852
  baseVersion: existing?.meta.serverVersion ?? null,
3833
- batchId
3853
+ batchId,
3854
+ // bug_0273e4dd: propaga merge-upsert (writeBatch.set(merge) cria-se-ausente).
3855
+ upsert: op.upsert
3834
3856
  });
3835
3857
  await this._store.putDoc({
3836
3858
  collection,
@@ -4092,6 +4114,10 @@ async function createOfflineDocumentsNamespace(opts) {
4092
4114
  connectivity,
4093
4115
  periodicSyncIntervalMs: opts.periodicSyncIntervalMs ?? 5 * 60 * 1e3
4094
4116
  });
4117
+ connectivity.start();
4118
+ if (!opts.singleTab && tabCoordinatorLike instanceof TabCoordinator) {
4119
+ tabCoordinatorLike.start();
4120
+ }
4095
4121
  return new NeetruDbDocumentsImpl({
4096
4122
  store,
4097
4123
  queue,
@@ -4561,11 +4587,11 @@ function createRestSyncTransport(config) {
4561
4587
  let raw;
4562
4588
  if (m.op === "add") {
4563
4589
  raw = await httpRequest2(config, {
4564
- method: "POST",
4565
- path: DATASTORE_COLLECTION_ENDPOINT(m.collection),
4590
+ method: "PUT",
4591
+ path: DATASTORE_DOC_ENDPOINT(m.collection, m.docId),
4566
4592
  body: { data: m.payload ?? {} },
4567
4593
  requireAuth: true,
4568
- retries: 0,
4594
+ retries: 2,
4569
4595
  headers: tenantHeaders
4570
4596
  });
4571
4597
  } else if (m.op === "set") {
@@ -4581,7 +4607,7 @@ function createRestSyncTransport(config) {
4581
4607
  raw = await httpRequest2(config, {
4582
4608
  method: "PATCH",
4583
4609
  path: DATASTORE_DOC_ENDPOINT(m.collection, m.docId),
4584
- body: { data: m.payload ?? {} },
4610
+ body: { data: m.payload ?? {}, ...m.upsert ? { upsert: true } : {} },
4585
4611
  requireAuth: true,
4586
4612
  retries: 2,
4587
4613
  headers: tenantHeaders
@@ -4604,7 +4630,24 @@ function createRestSyncTransport(config) {
4604
4630
  });
4605
4631
  } catch (err) {
4606
4632
  const msg = err instanceof Error ? err.message : String(err);
4607
- throw new NeetruDbError("db_unavailable", `pushMutations falhou: ${msg}`);
4633
+ const code = err?.code;
4634
+ const status = err?.status;
4635
+ const isPermanent = code === "forbidden" || code === "unauthorized" || code === "not_found" || code === "conflict" || code === "validation_failed" || typeof status === "number" && status >= 400 && status < 500 && status !== 408 && status !== 429;
4636
+ if (isPermanent) {
4637
+ const reason = code === "forbidden" || code === "unauthorized" ? "rejected_permission" : "rejected_validation";
4638
+ results.push({
4639
+ mutationId: m.mutationId,
4640
+ outcome: "rejected",
4641
+ reason,
4642
+ serverVersion: null,
4643
+ serverData: null
4644
+ });
4645
+ continue;
4646
+ }
4647
+ if (results.length === 0) {
4648
+ throw new NeetruDbError("db_unavailable", `pushMutations falhou: ${msg}`);
4649
+ }
4650
+ break;
4608
4651
  }
4609
4652
  }
4610
4653
  return { results };
@@ -5551,6 +5594,235 @@ var MockNotifications = class {
5551
5594
  }
5552
5595
  };
5553
5596
 
5597
+ // src/ai.ts
5598
+ init_errors();
5599
+ init_http();
5600
+ var AI_CHAT_PATH = "/api/sdk/v1/ai/chat";
5601
+ var AI_NONSTREAM_TIMEOUT_MS = 12e4;
5602
+ function resolveAiBody(config, opts, stream) {
5603
+ if (!opts || typeof opts !== "object") {
5604
+ throw new NeetruError("validation_failed", "ai: options object is required");
5605
+ }
5606
+ if (typeof opts.model !== "string" || opts.model.length === 0) {
5607
+ throw new NeetruError("validation_failed", "ai: model is required");
5608
+ }
5609
+ if (!Array.isArray(opts.messages) || opts.messages.length === 0) {
5610
+ throw new NeetruError("validation_failed", "ai: messages must be a non-empty array");
5611
+ }
5612
+ for (const m of opts.messages) {
5613
+ if (!m || typeof m !== "object" || typeof m.role !== "string" || m.role.length === 0 || typeof m.content !== "string") {
5614
+ throw new NeetruError(
5615
+ "validation_failed",
5616
+ "ai: each message must be { role: string, content: string }"
5617
+ );
5618
+ }
5619
+ }
5620
+ const productId = opts.productId ?? config.productId;
5621
+ const tenantId = opts.tenantId ?? config.tenantId;
5622
+ if (!productId) {
5623
+ throw new NeetruError(
5624
+ "validation_failed",
5625
+ "ai: productId required (pass to options or set on createNeetruClient)"
5626
+ );
5627
+ }
5628
+ if (!tenantId) {
5629
+ throw new NeetruError(
5630
+ "validation_failed",
5631
+ "ai: tenantId required (pass to options or set on createNeetruClient)"
5632
+ );
5633
+ }
5634
+ assertValidSdkId("productId", productId);
5635
+ assertValidSdkId("tenantId", tenantId);
5636
+ const body = {
5637
+ productId,
5638
+ tenantId,
5639
+ model: opts.model,
5640
+ messages: opts.messages,
5641
+ stream
5642
+ };
5643
+ if (opts.maxTokens !== void 0) {
5644
+ if (!Number.isInteger(opts.maxTokens) || opts.maxTokens <= 0) {
5645
+ throw new NeetruError("validation_failed", "ai: maxTokens must be a positive integer");
5646
+ }
5647
+ body.maxTokens = opts.maxTokens;
5648
+ }
5649
+ if (opts.temperature !== void 0) {
5650
+ if (typeof opts.temperature !== "number" || !Number.isFinite(opts.temperature)) {
5651
+ throw new NeetruError("validation_failed", "ai: temperature must be a finite number");
5652
+ }
5653
+ body.temperature = opts.temperature;
5654
+ }
5655
+ return body;
5656
+ }
5657
+ async function safeJson2(res) {
5658
+ try {
5659
+ const text = await res.text();
5660
+ if (!text) return void 0;
5661
+ return JSON.parse(text);
5662
+ } catch {
5663
+ return void 0;
5664
+ }
5665
+ }
5666
+ function statusToCode2(status) {
5667
+ if (status === 401) return "unauthorized";
5668
+ if (status === 403) return "forbidden";
5669
+ if (status === 404) return "not_found";
5670
+ if (status === 409) return "conflict";
5671
+ if (status === 422 || status === 400) return "validation_failed";
5672
+ if (status === 429) return "rate_limited";
5673
+ if (status >= 500) return "server_error";
5674
+ return "unknown";
5675
+ }
5676
+ function joinUrl(baseUrl, path) {
5677
+ const base = baseUrl.replace(/\/+$/, "");
5678
+ const p = path.startsWith("/") ? path : `/${path}`;
5679
+ return `${base}${p}`;
5680
+ }
5681
+ async function aiFetch(config, body, signal, stream) {
5682
+ if (!config.apiKey) {
5683
+ throw new NeetruError(
5684
+ "missing_api_key",
5685
+ "ai requires an apiKey. Pass it to createNeetruClient({ apiKey }) or set NEETRU_API_KEY env var."
5686
+ );
5687
+ }
5688
+ const url = joinUrl(config.baseUrl, AI_CHAT_PATH);
5689
+ const headers = {
5690
+ "content-type": "application/json",
5691
+ accept: stream ? "text/event-stream" : "application/json",
5692
+ authorization: `Bearer ${config.apiKey}`
5693
+ };
5694
+ const init = { method: "POST", headers, body: JSON.stringify(body) };
5695
+ if (stream) {
5696
+ if (signal) init.signal = signal;
5697
+ } else {
5698
+ init.signal = signal ?? AbortSignal.timeout(AI_NONSTREAM_TIMEOUT_MS);
5699
+ }
5700
+ let res;
5701
+ try {
5702
+ res = await config.fetch(url, init);
5703
+ } catch (err) {
5704
+ const message = err instanceof DOMException && err.name === "TimeoutError" ? `Network error: timeout after ${AI_NONSTREAM_TIMEOUT_MS / 1e3}s` : err instanceof DOMException && err.name === "AbortError" ? "Network error: request aborted" : `Network error: ${err instanceof Error ? err.message : "fetch failed"}`;
5705
+ throw new NeetruError("network_error", message);
5706
+ }
5707
+ const requestId = res.headers.get("x-request-id") ?? res.headers.get("x-correlation-id") ?? void 0;
5708
+ if (!res.ok) {
5709
+ const parsed = await safeJson2(res);
5710
+ let code = statusToCode2(res.status);
5711
+ let message = `HTTP ${res.status}`;
5712
+ if (parsed && typeof parsed === "object" && "error" in parsed) {
5713
+ const errField = parsed.error;
5714
+ if (typeof errField === "string") {
5715
+ message = errField;
5716
+ } else if (errField && typeof errField === "object") {
5717
+ if (typeof errField.code === "string") code = errField.code;
5718
+ if (typeof errField.message === "string") message = errField.message;
5719
+ }
5720
+ }
5721
+ throw new NeetruError(code, message, res.status, requestId);
5722
+ }
5723
+ const coreVersion = res.headers.get("x-neetru-version");
5724
+ if (coreVersion) checkCoreVersionCompat(coreVersion);
5725
+ return res;
5726
+ }
5727
+ function readMeta(res) {
5728
+ return {
5729
+ provider: res.headers.get("x-neetru-provider") ?? "",
5730
+ usageWarning: res.headers.get("x-neetru-ai-usage-warning") === "1"
5731
+ };
5732
+ }
5733
+ function parseSseData(rawEvent) {
5734
+ const dataLines = [];
5735
+ for (const line of rawEvent.split("\n")) {
5736
+ const trimmed = line.replace(/\r$/, "");
5737
+ if (trimmed.startsWith("data:")) {
5738
+ let v = trimmed.slice(5);
5739
+ if (v.startsWith(" ")) v = v.slice(1);
5740
+ dataLines.push(v);
5741
+ }
5742
+ }
5743
+ if (dataLines.length === 0) return null;
5744
+ const payload = dataLines.join("\n");
5745
+ if (payload === "[DONE]") return { done: true };
5746
+ try {
5747
+ const obj = JSON.parse(payload);
5748
+ const content = obj?.choices?.[0]?.delta?.content;
5749
+ if (typeof content === "string" && content.length > 0) return { content };
5750
+ return null;
5751
+ } catch {
5752
+ return null;
5753
+ }
5754
+ }
5755
+ function createAiNamespace(config) {
5756
+ return {
5757
+ async chat(opts) {
5758
+ const body = resolveAiBody(config, opts, false);
5759
+ const res = await aiFetch(config, body, opts.signal, false);
5760
+ const { provider: providerHeader, usageWarning } = readMeta(res);
5761
+ const raw = await safeJson2(res);
5762
+ if (!raw || raw.ok !== true || !Array.isArray(raw.choices)) {
5763
+ throw new NeetruError("invalid_response", "ai.chat response missing ok/choices");
5764
+ }
5765
+ return {
5766
+ choices: raw.choices,
5767
+ usage: raw.usage ?? { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 },
5768
+ model: raw.model ?? opts.model,
5769
+ provider: raw.provider ?? providerHeader ?? "",
5770
+ usageWarning
5771
+ };
5772
+ },
5773
+ stream(opts) {
5774
+ const body = resolveAiBody(config, opts, true);
5775
+ const responsePromise = aiFetch(config, body, opts.signal, true);
5776
+ const meta = responsePromise.then(readMeta);
5777
+ meta.catch(() => {
5778
+ });
5779
+ async function* iterate() {
5780
+ const res = await responsePromise;
5781
+ const stream = res.body;
5782
+ if (!stream) {
5783
+ throw new NeetruError("invalid_response", "ai.stream: response has no body");
5784
+ }
5785
+ const reader = stream.getReader();
5786
+ const decoder = new TextDecoder();
5787
+ let buffer = "";
5788
+ try {
5789
+ while (true) {
5790
+ const { done, value } = await reader.read();
5791
+ if (done) break;
5792
+ buffer += decoder.decode(value, { stream: true });
5793
+ let sep;
5794
+ while ((sep = buffer.indexOf("\n\n")) !== -1) {
5795
+ const rawEvent = buffer.slice(0, sep);
5796
+ buffer = buffer.slice(sep + 2);
5797
+ const parsed = parseSseData(rawEvent);
5798
+ if (parsed && "done" in parsed) return;
5799
+ if (parsed && "content" in parsed) yield parsed.content;
5800
+ }
5801
+ }
5802
+ const tail = buffer.trim();
5803
+ if (tail) {
5804
+ const parsed = parseSseData(tail);
5805
+ if (parsed && "content" in parsed) yield parsed.content;
5806
+ }
5807
+ } finally {
5808
+ try {
5809
+ reader.releaseLock();
5810
+ } catch {
5811
+ }
5812
+ try {
5813
+ await stream.cancel();
5814
+ } catch {
5815
+ }
5816
+ }
5817
+ }
5818
+ return {
5819
+ meta,
5820
+ [Symbol.asyncIterator]: iterate
5821
+ };
5822
+ }
5823
+ };
5824
+ }
5825
+
5554
5826
  // src/mocks.ts
5555
5827
  init_db_errors();
5556
5828
  init_errors();
@@ -6151,6 +6423,57 @@ var MockDb = class {
6151
6423
  this._store.clear();
6152
6424
  }
6153
6425
  };
6426
+ var MockAi = class {
6427
+ _calls = [];
6428
+ _reply(opts) {
6429
+ const last = [...opts.messages].reverse().find((m) => m.role === "user") ?? opts.messages[opts.messages.length - 1];
6430
+ const content = last?.content ?? "";
6431
+ return content ? `mock-reply: ${content}` : "mock-reply";
6432
+ }
6433
+ async chat(opts) {
6434
+ if (!opts || typeof opts.model !== "string" || opts.model.length === 0) {
6435
+ throw new NeetruError("validation_failed", "ai: model is required");
6436
+ }
6437
+ if (!Array.isArray(opts.messages) || opts.messages.length === 0) {
6438
+ throw new NeetruError("validation_failed", "ai: messages must be a non-empty array");
6439
+ }
6440
+ this._calls.push({ model: opts.model, messages: opts.messages, stream: false });
6441
+ const text = this._reply(opts);
6442
+ const promptTokens = opts.messages.reduce((n, m) => n + (m.content?.length ?? 0), 0);
6443
+ const completionTokens = text.length;
6444
+ return {
6445
+ choices: [{ message: { role: "assistant", content: text }, finish_reason: "stop" }],
6446
+ usage: {
6447
+ prompt_tokens: promptTokens,
6448
+ completion_tokens: completionTokens,
6449
+ total_tokens: promptTokens + completionTokens
6450
+ },
6451
+ model: opts.model,
6452
+ provider: "mock",
6453
+ usageWarning: false
6454
+ };
6455
+ }
6456
+ stream(opts) {
6457
+ if (!opts || typeof opts.model !== "string" || opts.model.length === 0) {
6458
+ throw new NeetruError("validation_failed", "ai: model is required");
6459
+ }
6460
+ if (!Array.isArray(opts.messages) || opts.messages.length === 0) {
6461
+ throw new NeetruError("validation_failed", "ai: messages must be a non-empty array");
6462
+ }
6463
+ this._calls.push({ model: opts.model, messages: opts.messages, stream: true });
6464
+ const text = this._reply(opts);
6465
+ const parts = text.match(/\S+\s*/g) ?? [text];
6466
+ const meta = Promise.resolve({ provider: "mock", usageWarning: false });
6467
+ async function* iterate() {
6468
+ for (const p of parts) yield p;
6469
+ }
6470
+ return { meta, [Symbol.asyncIterator]: iterate };
6471
+ }
6472
+ /** Test helper — histórico de chamadas. */
6473
+ __getCalls() {
6474
+ return this._calls;
6475
+ }
6476
+ };
6154
6477
 
6155
6478
  // src/auth.ts
6156
6479
  function readEnvApiKey() {
@@ -6518,15 +6841,15 @@ function createOidcAuthNamespace(config) {
6518
6841
  const overrideAuthUrl = typeof globalThis.NEETRU_AUTH_URL === "string" ? globalThis.NEETRU_AUTH_URL : null;
6519
6842
  const idpOrigin = overrideAuthUrl ?? config.baseUrl.replace(/^https?:\/\/api\./, "https://auth.");
6520
6843
  const issuer = idpOrigin;
6521
- const jwksUrl = `${idpOrigin}/api/v1/oauth/.well-known/jwks.json`;
6844
+ const jwksUrl = `${idpOrigin}/api/v1/oauth/.well-known/jwks`;
6522
6845
  const cacheTtlMs = options?.jwksCacheTtlMs ?? DEFAULT_JWKS_TTL_MS;
6523
6846
  const audienceFromOptions = options?.audience;
6524
6847
  const parsedKey = tryParseApiKey(config.apiKey);
6525
- const audience = audienceFromOptions ?? parsedKey?.keyId;
6848
+ const audience = audienceFromOptions ?? config.oidcClientId ?? parsedKey?.keyId;
6526
6849
  if (!audience) {
6527
6850
  throw new NeetruError(
6528
6851
  "invalid_config",
6529
- "verifyToken requires an audience: provide apiKey on the client or pass options.audience. Calling jwtVerify without an audience allows tokens issued for other OIDC clients to pass."
6852
+ "verifyToken requires an audience: provide oidcClientId/apiKey on the client or pass options.audience. Calling jwtVerify without an audience allows tokens issued for other OIDC clients to pass."
6530
6853
  );
6531
6854
  }
6532
6855
  const jwks = _getJwks(jwksUrl, cacheTtlMs);
@@ -6589,6 +6912,7 @@ function createNeetruClient(config = {}) {
6589
6912
  const db = config.mocks?.db ?? createNeetruDb(resolved, config.db);
6590
6913
  const webhooks = config.mocks?.webhooks ?? (isDev ? new MockWebhooks() : createWebhooksNamespace(resolved));
6591
6914
  const notifications = config.mocks?.notifications ?? (isDev ? new MockNotifications() : createNotificationsNamespace(resolved));
6915
+ const ai = config.mocks?.ai ?? (isDev ? new MockAi() : createAiNamespace(resolved));
6592
6916
  const client = Object.freeze({
6593
6917
  config: resolved,
6594
6918
  auth,
@@ -6600,7 +6924,8 @@ function createNeetruClient(config = {}) {
6600
6924
  db,
6601
6925
  checkout: createCheckoutNamespace(resolved),
6602
6926
  webhooks,
6603
- notifications
6927
+ notifications,
6928
+ ai
6604
6929
  });
6605
6930
  return client;
6606
6931
  }
@@ -6609,8 +6934,8 @@ function createNeetruClient(config = {}) {
6609
6934
  init_errors();
6610
6935
  init_db_errors();
6611
6936
  init_http();
6612
- var VERSION = "3.1.7";
6937
+ var VERSION = "3.1.10";
6613
6938
 
6614
- export { DEFAULT_BASE_URL, DEV_FIXTURE_USER, MockAuth, MockCheckout, MockDb, MockEntitlements, MockNotifications, MockSupport, MockUsage, MockWebhooks, NEETRU_SENTINEL_KEY, NeetruDbError, NeetruError, VERSION, checkCoreVersionCompat, createCheckoutNamespace, createNeetruClient, createNeetruDb, createNotificationsNamespace, createWebhooksNamespace, increment, isFieldSentinel, isIncrementSentinel, isServerTimestampSentinel, serverTimestamp, verifyWebhookRequest, verifyWebhookSignature };
6939
+ export { DEFAULT_BASE_URL, DEV_FIXTURE_USER, MockAi, MockAuth, MockCheckout, MockDb, MockEntitlements, MockNotifications, MockSupport, MockUsage, MockWebhooks, NEETRU_SENTINEL_KEY, NeetruDbError, NeetruError, VERSION, checkCoreVersionCompat, createAiNamespace, createCheckoutNamespace, createNeetruClient, createNeetruDb, createNotificationsNamespace, createWebhooksNamespace, increment, isFieldSentinel, isIncrementSentinel, isServerTimestampSentinel, serverTimestamp, verifyWebhookRequest, verifyWebhookSignature };
6615
6940
  //# sourceMappingURL=index.mjs.map
6616
6941
  //# sourceMappingURL=index.mjs.map