@neetru/sdk 3.1.9 → 3.1.11

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 (51) 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 +283 -1
  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 +283 -1
  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.d.cts +1 -1
  20. package/dist/db.d.ts +1 -1
  21. package/dist/entitlements.d.cts +1 -1
  22. package/dist/entitlements.d.ts +1 -1
  23. package/dist/firestore-compat.d.cts +1 -1
  24. package/dist/firestore-compat.d.ts +1 -1
  25. package/dist/index.cjs +286 -2
  26. package/dist/index.cjs.map +1 -1
  27. package/dist/index.d.cts +3 -3
  28. package/dist/index.d.ts +3 -3
  29. package/dist/index.mjs +285 -3
  30. package/dist/index.mjs.map +1 -1
  31. package/dist/mocks.cjs +52 -0
  32. package/dist/mocks.cjs.map +1 -1
  33. package/dist/mocks.d.cts +20 -2
  34. package/dist/mocks.d.ts +20 -2
  35. package/dist/mocks.mjs +52 -1
  36. package/dist/mocks.mjs.map +1 -1
  37. package/dist/notifications.d.cts +1 -1
  38. package/dist/notifications.d.ts +1 -1
  39. package/dist/react.d.cts +1 -1
  40. package/dist/react.d.ts +1 -1
  41. package/dist/support.d.cts +1 -1
  42. package/dist/support.d.ts +1 -1
  43. package/dist/telemetry.d.cts +1 -1
  44. package/dist/telemetry.d.ts +1 -1
  45. package/dist/{types-DDiVzgCT.d.cts → types-Cog9S-tC.d.cts} +84 -1
  46. package/dist/{types-HWDOUmlC.d.ts → types-dagUxKYz.d.ts} +84 -1
  47. package/dist/usage.d.cts +1 -1
  48. package/dist/usage.d.ts +1 -1
  49. package/dist/webhooks.d.cts +1 -1
  50. package/dist/webhooks.d.ts +1 -1
  51. package/package.json +6 -1
package/dist/auth.cjs CHANGED
@@ -5499,6 +5499,235 @@ var MockNotifications = class {
5499
5499
  }
5500
5500
  };
5501
5501
 
5502
+ // src/ai.ts
5503
+ init_errors();
5504
+ init_http();
5505
+ var AI_CHAT_PATH = "/api/sdk/v1/ai/chat";
5506
+ var AI_NONSTREAM_TIMEOUT_MS = 12e4;
5507
+ function resolveAiBody(config, opts, stream) {
5508
+ if (!opts || typeof opts !== "object") {
5509
+ throw new NeetruError("validation_failed", "ai: options object is required");
5510
+ }
5511
+ if (typeof opts.model !== "string" || opts.model.length === 0) {
5512
+ throw new NeetruError("validation_failed", "ai: model is required");
5513
+ }
5514
+ if (!Array.isArray(opts.messages) || opts.messages.length === 0) {
5515
+ throw new NeetruError("validation_failed", "ai: messages must be a non-empty array");
5516
+ }
5517
+ for (const m of opts.messages) {
5518
+ if (!m || typeof m !== "object" || typeof m.role !== "string" || m.role.length === 0 || typeof m.content !== "string") {
5519
+ throw new NeetruError(
5520
+ "validation_failed",
5521
+ "ai: each message must be { role: string, content: string }"
5522
+ );
5523
+ }
5524
+ }
5525
+ const productId = opts.productId ?? config.productId;
5526
+ const tenantId = opts.tenantId ?? config.tenantId;
5527
+ if (!productId) {
5528
+ throw new NeetruError(
5529
+ "validation_failed",
5530
+ "ai: productId required (pass to options or set on createNeetruClient)"
5531
+ );
5532
+ }
5533
+ if (!tenantId) {
5534
+ throw new NeetruError(
5535
+ "validation_failed",
5536
+ "ai: tenantId required (pass to options or set on createNeetruClient)"
5537
+ );
5538
+ }
5539
+ assertValidSdkId("productId", productId);
5540
+ assertValidSdkId("tenantId", tenantId);
5541
+ const body = {
5542
+ productId,
5543
+ tenantId,
5544
+ model: opts.model,
5545
+ messages: opts.messages,
5546
+ stream
5547
+ };
5548
+ if (opts.maxTokens !== void 0) {
5549
+ if (!Number.isInteger(opts.maxTokens) || opts.maxTokens <= 0) {
5550
+ throw new NeetruError("validation_failed", "ai: maxTokens must be a positive integer");
5551
+ }
5552
+ body.maxTokens = opts.maxTokens;
5553
+ }
5554
+ if (opts.temperature !== void 0) {
5555
+ if (typeof opts.temperature !== "number" || !Number.isFinite(opts.temperature)) {
5556
+ throw new NeetruError("validation_failed", "ai: temperature must be a finite number");
5557
+ }
5558
+ body.temperature = opts.temperature;
5559
+ }
5560
+ return body;
5561
+ }
5562
+ async function safeJson2(res) {
5563
+ try {
5564
+ const text = await res.text();
5565
+ if (!text) return void 0;
5566
+ return JSON.parse(text);
5567
+ } catch {
5568
+ return void 0;
5569
+ }
5570
+ }
5571
+ function statusToCode2(status) {
5572
+ if (status === 401) return "unauthorized";
5573
+ if (status === 403) return "forbidden";
5574
+ if (status === 404) return "not_found";
5575
+ if (status === 409) return "conflict";
5576
+ if (status === 422 || status === 400) return "validation_failed";
5577
+ if (status === 429) return "rate_limited";
5578
+ if (status >= 500) return "server_error";
5579
+ return "unknown";
5580
+ }
5581
+ function joinUrl(baseUrl, path) {
5582
+ const base = baseUrl.replace(/\/+$/, "");
5583
+ const p = path.startsWith("/") ? path : `/${path}`;
5584
+ return `${base}${p}`;
5585
+ }
5586
+ async function aiFetch(config, body, signal, stream) {
5587
+ if (!config.apiKey) {
5588
+ throw new NeetruError(
5589
+ "missing_api_key",
5590
+ "ai requires an apiKey. Pass it to createNeetruClient({ apiKey }) or set NEETRU_API_KEY env var."
5591
+ );
5592
+ }
5593
+ const url = joinUrl(config.baseUrl, AI_CHAT_PATH);
5594
+ const headers = {
5595
+ "content-type": "application/json",
5596
+ accept: stream ? "text/event-stream" : "application/json",
5597
+ authorization: `Bearer ${config.apiKey}`
5598
+ };
5599
+ const init = { method: "POST", headers, body: JSON.stringify(body) };
5600
+ if (stream) {
5601
+ if (signal) init.signal = signal;
5602
+ } else {
5603
+ init.signal = signal ?? AbortSignal.timeout(AI_NONSTREAM_TIMEOUT_MS);
5604
+ }
5605
+ let res;
5606
+ try {
5607
+ res = await config.fetch(url, init);
5608
+ } catch (err) {
5609
+ 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"}`;
5610
+ throw new NeetruError("network_error", message);
5611
+ }
5612
+ const requestId = res.headers.get("x-request-id") ?? res.headers.get("x-correlation-id") ?? void 0;
5613
+ if (!res.ok) {
5614
+ const parsed = await safeJson2(res);
5615
+ let code = statusToCode2(res.status);
5616
+ let message = `HTTP ${res.status}`;
5617
+ if (parsed && typeof parsed === "object" && "error" in parsed) {
5618
+ const errField = parsed.error;
5619
+ if (typeof errField === "string") {
5620
+ message = errField;
5621
+ } else if (errField && typeof errField === "object") {
5622
+ if (typeof errField.code === "string") code = errField.code;
5623
+ if (typeof errField.message === "string") message = errField.message;
5624
+ }
5625
+ }
5626
+ throw new NeetruError(code, message, res.status, requestId);
5627
+ }
5628
+ const coreVersion = res.headers.get("x-neetru-version");
5629
+ if (coreVersion) checkCoreVersionCompat(coreVersion);
5630
+ return res;
5631
+ }
5632
+ function readMeta(res) {
5633
+ return {
5634
+ provider: res.headers.get("x-neetru-provider") ?? "",
5635
+ usageWarning: res.headers.get("x-neetru-ai-usage-warning") === "1"
5636
+ };
5637
+ }
5638
+ function parseSseData(rawEvent) {
5639
+ const dataLines = [];
5640
+ for (const line of rawEvent.split("\n")) {
5641
+ const trimmed = line.replace(/\r$/, "");
5642
+ if (trimmed.startsWith("data:")) {
5643
+ let v = trimmed.slice(5);
5644
+ if (v.startsWith(" ")) v = v.slice(1);
5645
+ dataLines.push(v);
5646
+ }
5647
+ }
5648
+ if (dataLines.length === 0) return null;
5649
+ const payload = dataLines.join("\n");
5650
+ if (payload === "[DONE]") return { done: true };
5651
+ try {
5652
+ const obj = JSON.parse(payload);
5653
+ const content = obj?.choices?.[0]?.delta?.content;
5654
+ if (typeof content === "string" && content.length > 0) return { content };
5655
+ return null;
5656
+ } catch {
5657
+ return null;
5658
+ }
5659
+ }
5660
+ function createAiNamespace(config) {
5661
+ return {
5662
+ async chat(opts) {
5663
+ const body = resolveAiBody(config, opts, false);
5664
+ const res = await aiFetch(config, body, opts.signal, false);
5665
+ const { provider: providerHeader, usageWarning } = readMeta(res);
5666
+ const raw = await safeJson2(res);
5667
+ if (!raw || raw.ok !== true || !Array.isArray(raw.choices)) {
5668
+ throw new NeetruError("invalid_response", "ai.chat response missing ok/choices");
5669
+ }
5670
+ return {
5671
+ choices: raw.choices,
5672
+ usage: raw.usage ?? { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 },
5673
+ model: raw.model ?? opts.model,
5674
+ provider: raw.provider ?? providerHeader ?? "",
5675
+ usageWarning
5676
+ };
5677
+ },
5678
+ stream(opts) {
5679
+ const body = resolveAiBody(config, opts, true);
5680
+ const responsePromise = aiFetch(config, body, opts.signal, true);
5681
+ const meta = responsePromise.then(readMeta);
5682
+ meta.catch(() => {
5683
+ });
5684
+ async function* iterate() {
5685
+ const res = await responsePromise;
5686
+ const stream = res.body;
5687
+ if (!stream) {
5688
+ throw new NeetruError("invalid_response", "ai.stream: response has no body");
5689
+ }
5690
+ const reader = stream.getReader();
5691
+ const decoder = new TextDecoder();
5692
+ let buffer = "";
5693
+ try {
5694
+ while (true) {
5695
+ const { done, value } = await reader.read();
5696
+ if (done) break;
5697
+ buffer += decoder.decode(value, { stream: true });
5698
+ let sep;
5699
+ while ((sep = buffer.indexOf("\n\n")) !== -1) {
5700
+ const rawEvent = buffer.slice(0, sep);
5701
+ buffer = buffer.slice(sep + 2);
5702
+ const parsed = parseSseData(rawEvent);
5703
+ if (parsed && "done" in parsed) return;
5704
+ if (parsed && "content" in parsed) yield parsed.content;
5705
+ }
5706
+ }
5707
+ const tail = buffer.trim();
5708
+ if (tail) {
5709
+ const parsed = parseSseData(tail);
5710
+ if (parsed && "content" in parsed) yield parsed.content;
5711
+ }
5712
+ } finally {
5713
+ try {
5714
+ reader.releaseLock();
5715
+ } catch {
5716
+ }
5717
+ try {
5718
+ await stream.cancel();
5719
+ } catch {
5720
+ }
5721
+ }
5722
+ }
5723
+ return {
5724
+ meta,
5725
+ [Symbol.asyncIterator]: iterate
5726
+ };
5727
+ }
5728
+ };
5729
+ }
5730
+
5502
5731
  // src/mocks.ts
5503
5732
  init_errors();
5504
5733
  var DEV_FIXTURE_USER = Object.freeze({
@@ -5784,6 +6013,57 @@ var MockEntitlements = class {
5784
6013
  this._denied.clear();
5785
6014
  }
5786
6015
  };
6016
+ var MockAi = class {
6017
+ _calls = [];
6018
+ _reply(opts) {
6019
+ const last = [...opts.messages].reverse().find((m) => m.role === "user") ?? opts.messages[opts.messages.length - 1];
6020
+ const content = last?.content ?? "";
6021
+ return content ? `mock-reply: ${content}` : "mock-reply";
6022
+ }
6023
+ async chat(opts) {
6024
+ if (!opts || typeof opts.model !== "string" || opts.model.length === 0) {
6025
+ throw new NeetruError("validation_failed", "ai: model is required");
6026
+ }
6027
+ if (!Array.isArray(opts.messages) || opts.messages.length === 0) {
6028
+ throw new NeetruError("validation_failed", "ai: messages must be a non-empty array");
6029
+ }
6030
+ this._calls.push({ model: opts.model, messages: opts.messages, stream: false });
6031
+ const text = this._reply(opts);
6032
+ const promptTokens = opts.messages.reduce((n, m) => n + (m.content?.length ?? 0), 0);
6033
+ const completionTokens = text.length;
6034
+ return {
6035
+ choices: [{ message: { role: "assistant", content: text }, finish_reason: "stop" }],
6036
+ usage: {
6037
+ prompt_tokens: promptTokens,
6038
+ completion_tokens: completionTokens,
6039
+ total_tokens: promptTokens + completionTokens
6040
+ },
6041
+ model: opts.model,
6042
+ provider: "mock",
6043
+ usageWarning: false
6044
+ };
6045
+ }
6046
+ stream(opts) {
6047
+ if (!opts || typeof opts.model !== "string" || opts.model.length === 0) {
6048
+ throw new NeetruError("validation_failed", "ai: model is required");
6049
+ }
6050
+ if (!Array.isArray(opts.messages) || opts.messages.length === 0) {
6051
+ throw new NeetruError("validation_failed", "ai: messages must be a non-empty array");
6052
+ }
6053
+ this._calls.push({ model: opts.model, messages: opts.messages, stream: true });
6054
+ const text = this._reply(opts);
6055
+ const parts = text.match(/\S+\s*/g) ?? [text];
6056
+ const meta = Promise.resolve({ provider: "mock", usageWarning: false });
6057
+ async function* iterate() {
6058
+ for (const p of parts) yield p;
6059
+ }
6060
+ return { meta, [Symbol.asyncIterator]: iterate };
6061
+ }
6062
+ /** Test helper — histórico de chamadas. */
6063
+ __getCalls() {
6064
+ return this._calls;
6065
+ }
6066
+ };
5787
6067
 
5788
6068
  // src/auth.ts
5789
6069
  function readEnvApiKey() {
@@ -6225,6 +6505,7 @@ function createNeetruClient(config = {}) {
6225
6505
  const db = config.mocks?.db ?? createNeetruDb(resolved, config.db);
6226
6506
  const webhooks = config.mocks?.webhooks ?? (isDev ? new MockWebhooks() : createWebhooksNamespace(resolved));
6227
6507
  const notifications = config.mocks?.notifications ?? (isDev ? new MockNotifications() : createNotificationsNamespace(resolved));
6508
+ const ai = config.mocks?.ai ?? (isDev ? new MockAi() : createAiNamespace(resolved));
6228
6509
  const client = Object.freeze({
6229
6510
  config: resolved,
6230
6511
  auth,
@@ -6236,7 +6517,8 @@ function createNeetruClient(config = {}) {
6236
6517
  db,
6237
6518
  checkout: createCheckoutNamespace(resolved),
6238
6519
  webhooks,
6239
- notifications
6520
+ notifications,
6521
+ ai
6240
6522
  });
6241
6523
  return client;
6242
6524
  }