@neetru/sdk 3.6.0 → 3.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/dist/ai.cjs +254 -391
  3. package/dist/ai.d.cts +1 -1
  4. package/dist/ai.d.ts +1 -1
  5. package/dist/ai.mjs +254 -391
  6. package/dist/auth.cjs +3547 -4998
  7. package/dist/auth.d.cts +1 -1
  8. package/dist/auth.d.ts +1 -1
  9. package/dist/auth.mjs +3544 -4995
  10. package/dist/build-guard.cjs +101 -119
  11. package/dist/build-guard.mjs +91 -109
  12. package/dist/catalog.cjs +152 -224
  13. package/dist/catalog.d.cts +1 -1
  14. package/dist/catalog.d.ts +1 -1
  15. package/dist/catalog.mjs +152 -224
  16. package/dist/checkout.cjs +237 -320
  17. package/dist/checkout.d.cts +1 -1
  18. package/dist/checkout.d.ts +1 -1
  19. package/dist/checkout.mjs +236 -319
  20. package/dist/db-react.cjs +59 -87
  21. package/dist/db-react.d.cts +1 -1
  22. package/dist/db-react.d.ts +1 -1
  23. package/dist/db-react.mjs +56 -84
  24. package/dist/db.cjs +2055 -3013
  25. package/dist/db.d.cts +1 -1
  26. package/dist/db.d.ts +1 -1
  27. package/dist/db.mjs +2068 -3026
  28. package/dist/entitlements.cjs +12 -20
  29. package/dist/entitlements.d.cts +1 -1
  30. package/dist/entitlements.d.ts +1 -1
  31. package/dist/entitlements.mjs +12 -20
  32. package/dist/errors.cjs +9 -12
  33. package/dist/errors.d.cts +37 -2
  34. package/dist/errors.d.ts +37 -2
  35. package/dist/errors.mjs +8 -12
  36. package/dist/firestore-compat.cjs +241 -297
  37. package/dist/firestore-compat.d.cts +1 -1
  38. package/dist/firestore-compat.d.ts +1 -1
  39. package/dist/firestore-compat.mjs +215 -271
  40. package/dist/index.cjs +3605 -5131
  41. package/dist/index.d.cts +6 -4
  42. package/dist/index.d.ts +6 -4
  43. package/dist/index.mjs +3710 -5236
  44. package/dist/mobile.cjs +238 -342
  45. package/dist/mobile.d.cts +1 -1
  46. package/dist/mobile.d.ts +1 -1
  47. package/dist/mobile.mjs +238 -342
  48. package/dist/mocks.cjs +312 -447
  49. package/dist/mocks.d.cts +1 -1
  50. package/dist/mocks.d.ts +1 -1
  51. package/dist/mocks.mjs +306 -441
  52. package/dist/notifications.cjs +226 -314
  53. package/dist/notifications.d.cts +1 -1
  54. package/dist/notifications.d.ts +1 -1
  55. package/dist/notifications.mjs +225 -313
  56. package/dist/react.cjs +522 -693
  57. package/dist/react.d.cts +1 -1
  58. package/dist/react.d.ts +1 -1
  59. package/dist/react.mjs +512 -683
  60. package/dist/support.cjs +329 -325
  61. package/dist/support.d.cts +25 -3
  62. package/dist/support.d.ts +25 -3
  63. package/dist/support.mjs +328 -325
  64. package/dist/telemetry.cjs +197 -301
  65. package/dist/telemetry.d.cts +1 -1
  66. package/dist/telemetry.d.ts +1 -1
  67. package/dist/telemetry.mjs +197 -301
  68. package/dist/{types-Du_iioJ7.d.cts → types-Bx4kTLmk.d.cts} +26 -0
  69. package/dist/{types-BPATkXYu.d.ts → types-Dx0IrOSF.d.ts} +26 -0
  70. package/dist/usage.cjs +163 -227
  71. package/dist/usage.d.cts +1 -1
  72. package/dist/usage.d.ts +1 -1
  73. package/dist/usage.mjs +163 -227
  74. package/dist/webhooks.cjs +258 -359
  75. package/dist/webhooks.d.cts +1 -1
  76. package/dist/webhooks.d.ts +1 -1
  77. package/dist/webhooks.mjs +255 -356
  78. package/package.json +4 -3
  79. package/dist/ai.cjs.map +0 -1
  80. package/dist/ai.mjs.map +0 -1
  81. package/dist/auth.cjs.map +0 -1
  82. package/dist/auth.mjs.map +0 -1
  83. package/dist/build-guard.cjs.map +0 -1
  84. package/dist/build-guard.mjs.map +0 -1
  85. package/dist/catalog.cjs.map +0 -1
  86. package/dist/catalog.mjs.map +0 -1
  87. package/dist/checkout.cjs.map +0 -1
  88. package/dist/checkout.mjs.map +0 -1
  89. package/dist/db-react.cjs.map +0 -1
  90. package/dist/db-react.mjs.map +0 -1
  91. package/dist/db.cjs.map +0 -1
  92. package/dist/db.mjs.map +0 -1
  93. package/dist/entitlements.cjs.map +0 -1
  94. package/dist/entitlements.mjs.map +0 -1
  95. package/dist/errors.cjs.map +0 -1
  96. package/dist/errors.mjs.map +0 -1
  97. package/dist/firestore-compat.cjs.map +0 -1
  98. package/dist/firestore-compat.mjs.map +0 -1
  99. package/dist/index.cjs.map +0 -1
  100. package/dist/index.mjs.map +0 -1
  101. package/dist/mobile.cjs.map +0 -1
  102. package/dist/mobile.mjs.map +0 -1
  103. package/dist/mocks.cjs.map +0 -1
  104. package/dist/mocks.mjs.map +0 -1
  105. package/dist/notifications.cjs.map +0 -1
  106. package/dist/notifications.mjs.map +0 -1
  107. package/dist/react.cjs.map +0 -1
  108. package/dist/react.mjs.map +0 -1
  109. package/dist/support.cjs.map +0 -1
  110. package/dist/support.mjs.map +0 -1
  111. package/dist/telemetry.cjs.map +0 -1
  112. package/dist/telemetry.mjs.map +0 -1
  113. package/dist/usage.cjs.map +0 -1
  114. package/dist/usage.mjs.map +0 -1
  115. package/dist/webhooks.cjs.map +0 -1
  116. package/dist/webhooks.mjs.map +0 -1
package/dist/ai.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  // src/errors.ts
4
- var NeetruError = class _NeetruError extends Error {
4
+ var u = class t extends Error {
5
5
  code;
6
6
  status;
7
7
  requestId;
@@ -14,517 +14,380 @@ var NeetruError = class _NeetruError extends Error {
14
14
  * para um `NeetruDbErrorCode` mais específico (recuperável vs não).
15
15
  */
16
16
  reason;
17
- constructor(code, message, status, requestId, reason) {
18
- super(message);
19
- this.name = "NeetruError";
20
- this.code = code;
21
- this.status = status;
22
- this.requestId = requestId;
23
- this.reason = reason;
24
- Object.setPrototypeOf(this, _NeetruError.prototype);
17
+ constructor(e, r, s, n, i) {
18
+ super(r), this.name = "NeetruError", this.code = e, this.status = s, this.requestId = n, this.reason = i, Object.setPrototypeOf(this, t.prototype);
25
19
  }
26
20
  };
27
21
 
28
22
  // src/http.ts
29
- var COMPAT_MATRIX = {
23
+ var z = {
30
24
  // SDK major 3.x requer Core ≥ 2026.05.1 (suporte a X-Neetru-Version header,
31
25
  // runTransaction write-after-read, verifyWebhookRequest, webhooks 3.0+)
32
26
  3: "2026.05.1"
33
27
  };
34
- function stripRevision(v) {
35
- return v.split("+")[0] ?? v;
28
+ function b(t) {
29
+ return t.split("+")[0] ?? t;
36
30
  }
37
- function compareCalVer(a, b) {
38
- const partsA = stripRevision(a).split(".").map(Number);
39
- const partsB = stripRevision(b).split(".").map(Number);
40
- const len = Math.max(partsA.length, partsB.length);
41
- for (let i = 0; i < len; i++) {
42
- const pa = partsA[i] ?? 0;
43
- const pb = partsB[i] ?? 0;
44
- if (pa !== pb) return pa - pb;
31
+ function B(t, e) {
32
+ let r = b(t).split(".").map(Number), s = b(e).split(".").map(Number), n = Math.max(r.length, s.length);
33
+ for (let i = 0; i < n; i++) {
34
+ let a = r[i] ?? 0, o = s[i] ?? 0;
35
+ if (a !== o) return a - o;
45
36
  }
46
37
  return 0;
47
38
  }
48
- function readInjectedSdkVersion() {
39
+ function G() {
49
40
  try {
50
- return "3.6.0";
41
+ return "3.7.0";
51
42
  } catch {
52
- return void 0;
43
+ return;
53
44
  }
54
45
  }
55
- function getSdkMajor() {
56
- const ver = readInjectedSdkVersion();
57
- if (ver) return parseInt(ver.split(".")[0] ?? "3", 10);
58
- return 3;
46
+ function J() {
47
+ let t = G();
48
+ return t ? parseInt(t.split(".")[0] ?? "3", 10) : 3;
59
49
  }
60
- var _warnedCoreTooOld = /* @__PURE__ */ new Set();
61
- function checkCoreVersionCompat(coreVersion) {
62
- if (!coreVersion) return;
63
- const sdkMajor = getSdkMajor();
64
- const minCore = COMPAT_MATRIX[sdkMajor];
65
- if (!minCore) return;
66
- const stripped = stripRevision(coreVersion);
67
- if (compareCalVer(stripped, minCore) < 0) {
68
- const key = `${sdkMajor}:${stripped}`;
69
- if (!_warnedCoreTooOld.has(key)) {
70
- _warnedCoreTooOld.add(key);
71
- console.warn(
72
- `[Neetru SDK] Aten\xE7\xE3o: Core vers\xE3o "${coreVersion}" \xE9 anterior ao m\xEDnimo recomendado "${minCore}" para SDK major ${sdkMajor}. Alguns recursos podem n\xE3o funcionar corretamente. Atualize o Core para a vers\xE3o mais recente.`
73
- );
74
- }
50
+ var w = /* @__PURE__ */ new Set();
51
+ function E(t) {
52
+ if (!t) return;
53
+ let e = J(), r = z[e];
54
+ if (!r) return;
55
+ let s = b(t);
56
+ if (B(s, r) < 0) {
57
+ let n = `${e}:${s}`;
58
+ w.has(n) || (w.add(n), console.warn(
59
+ `[Neetru SDK] Aten\xE7\xE3o: Core vers\xE3o "${t}" \xE9 anterior ao m\xEDnimo recomendado "${r}" para SDK major ${e}. Alguns recursos podem n\xE3o funcionar corretamente. Atualize o Core para a vers\xE3o mais recente.`
60
+ ));
75
61
  }
76
62
  }
77
- function statusToCode(status) {
78
- if (status === 401) return "unauthorized";
79
- if (status === 403) return "forbidden";
80
- if (status === 404) return "not_found";
81
- if (status === 409) return "conflict";
82
- if (status === 422 || status === 400) return "validation_failed";
83
- if (status === 429) return "rate_limited";
84
- if (status >= 500) return "server_error";
85
- return "unknown";
63
+ function x(t) {
64
+ return t === 401 ? "unauthorized" : t === 403 ? "forbidden" : t === 404 ? "not_found" : t === 409 ? "conflict" : t === 410 ? "gone" : t === 422 || t === 400 ? "validation_failed" : t === 429 ? "rate_limited" : t >= 500 ? "server_error" : "unknown";
86
65
  }
87
66
 
88
67
  // src/validators.ts
89
- var SDK_ID_RE = /^[a-z0-9][a-z0-9_-]{0,62}$/i;
90
- function assertValidSdkId(field, value) {
91
- if (typeof value !== "string" || !SDK_ID_RE.test(value)) {
92
- throw new NeetruError(
68
+ var k = /^[a-z0-9][a-z0-9_-]{0,62}$/i;
69
+ function A(t, e) {
70
+ if (typeof e != "string" || !k.test(e))
71
+ throw new u(
93
72
  "validation_failed",
94
- `${field} must match ${SDK_ID_RE.source} (lowercase alphanumeric, _, -; first char must be alphanumeric; max 63 chars)`
73
+ `${t} must match ${k.source} (lowercase alphanumeric, _, -; first char must be alphanumeric; max 63 chars)`
95
74
  );
96
- }
97
75
  }
98
76
 
99
77
  // src/ai.ts
100
- var AI_CHAT_PATH = "/api/sdk/v1/ai/chat";
101
- var AI_NONSTREAM_TIMEOUT_MS = 12e4;
102
- var MAX_TEXT_PART_LEN = 32768;
103
- var MAX_CONTENT_PARTS = 32;
104
- var MAX_DATA_URL_LEN = 4194304;
105
- var MAX_HTTPS_URL_LEN = 2048;
106
- var DATA_IMAGE_URL_RE = /^data:image\/(?:png|jpe?g|gif|webp);base64,[A-Za-z0-9+/]+={0,2}$/;
107
- var HTTPS_URL_RE = /^https:\/\/[^\s]+$/;
108
- var IMAGE_DETAIL_VALUES = /* @__PURE__ */ new Set(["low", "high", "auto"]);
109
- var AI_ROLES = /* @__PURE__ */ new Set(["system", "user", "assistant"]);
110
- var MAX_MESSAGES = 100;
111
- var MAX_MODEL_LEN = 128;
112
- var MAX_TOKENS_CAP = 32768;
113
- var MIN_TEMPERATURE = 0;
114
- var MAX_TEMPERATURE = 2;
115
- var MAX_TOTAL_CONTENT_PARTS = 200;
116
- var MAX_TOTAL_IMAGES = 20;
117
- var MAX_TOTAL_IMAGE_BYTES = 12 * 1024 * 1024;
118
- var MAX_SCHEMA_JSON_LEN = 5e4;
119
- var MAX_SCHEMA_DEPTH = 16;
120
- var MAX_SCHEMA_KEYS = 500;
121
- var FORBIDDEN_SCHEMA_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
122
- var RESPONSE_FORMAT_NAME_RE = /^[a-zA-Z0-9_-]+$/;
123
- function fail(msg) {
124
- throw new NeetruError("validation_failed", msg);
78
+ var V = "/api/sdk/v1/ai/chat", T = 12e4, _ = 32768, R = 32, v = 4194304, S = 2048, Y = /^data:image\/(?:png|jpe?g|gif|webp);base64,[A-Za-z0-9+/]+={0,2}$/, Z = /^https:\/\/[^\s]+$/, Q = /* @__PURE__ */ new Set(["low", "high", "auto"]), ee = /* @__PURE__ */ new Set(["system", "user", "assistant"]), M = 100, j = 128, C = 32768, N = 0, I = 2, O = 200, P = 20, $ = 12 * 1024 * 1024, q = 5e4, F = 16, D = 500, te = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]), ne = /^[a-zA-Z0-9_-]+$/;
79
+ function c(t) {
80
+ throw new u("validation_failed", t);
125
81
  }
126
- function isStrictBase64Payload(b64) {
127
- if (b64.length === 0 || b64.length % 4 !== 0) return false;
128
- const pad = b64.endsWith("==") ? 2 : b64.endsWith("=") ? 1 : 0;
129
- const body = pad > 0 ? b64.slice(0, b64.length - pad) : b64;
130
- return !body.includes("=");
82
+ function re(t) {
83
+ if (t.length === 0 || t.length % 4 !== 0) return false;
84
+ let e = t.endsWith("==") ? 2 : t.endsWith("=") ? 1 : 0;
85
+ return !(e > 0 ? t.slice(0, t.length - e) : t).includes("=");
131
86
  }
132
- function dataUrlDecodedBytes(url) {
133
- const comma = url.indexOf(",");
134
- if (comma === -1) return 0;
135
- const b64 = url.slice(comma + 1);
136
- const pad = b64.endsWith("==") ? 2 : b64.endsWith("=") ? 1 : 0;
137
- return Math.max(0, Math.floor(b64.length * 3 / 4) - pad);
87
+ function se(t) {
88
+ let e = t.indexOf(",");
89
+ if (e === -1) return 0;
90
+ let r = t.slice(e + 1), s = r.endsWith("==") ? 2 : r.endsWith("=") ? 1 : 0;
91
+ return Math.max(0, Math.floor(r.length * 3 / 4) - s);
138
92
  }
139
- function checkAggregateMultimodalLimits(messages) {
140
- let totalParts = 0;
141
- let totalImages = 0;
142
- let totalImageBytes = 0;
143
- for (const m of messages) {
144
- const content = m?.content;
145
- if (!Array.isArray(content)) continue;
146
- for (const part of content) {
147
- totalParts++;
148
- if (totalParts > MAX_TOTAL_CONTENT_PARTS) {
149
- return `ai: total content parts exceeds ${MAX_TOTAL_CONTENT_PARTS} per request`;
150
- }
151
- if (part && typeof part === "object" && part.type === "image_url") {
152
- totalImages++;
153
- if (totalImages > MAX_TOTAL_IMAGES) {
154
- return `ai: total images exceeds ${MAX_TOTAL_IMAGES} per request`;
155
- }
156
- const url = part.image_url?.url;
157
- if (typeof url === "string" && url.startsWith("data:")) {
158
- totalImageBytes += dataUrlDecodedBytes(url);
159
- if (totalImageBytes > MAX_TOTAL_IMAGE_BYTES) {
160
- return `ai: aggregate image bytes exceeds ${MAX_TOTAL_IMAGE_BYTES} per request`;
161
- }
93
+ function ie(t) {
94
+ let e = 0, r = 0, s = 0;
95
+ for (let n of t) {
96
+ let i = n?.content;
97
+ if (Array.isArray(i))
98
+ for (let a of i) {
99
+ if (e++, e > O)
100
+ return `ai: total content parts exceeds ${O} per request`;
101
+ if (a && typeof a == "object" && a.type === "image_url") {
102
+ if (r++, r > P)
103
+ return `ai: total images exceeds ${P} per request`;
104
+ let o = a.image_url?.url;
105
+ if (typeof o == "string" && o.startsWith("data:") && (s += se(o), s > $))
106
+ return `ai: aggregate image bytes exceeds ${$} per request`;
162
107
  }
163
108
  }
164
- }
165
109
  }
166
110
  return null;
167
111
  }
168
- function validateStructuredSchema(schema) {
169
- if (schema === void 0) return null;
170
- if (schema === null || typeof schema !== "object") {
112
+ function oe(t) {
113
+ if (t === void 0) return null;
114
+ if (t === null || typeof t != "object")
171
115
  return "ai: responseFormat json_schema.schema must be an object";
172
- }
173
- let serialized;
116
+ let e;
174
117
  try {
175
- serialized = JSON.stringify(schema);
118
+ e = JSON.stringify(t);
176
119
  } catch {
177
120
  return "ai: responseFormat json_schema.schema is not serializable";
178
121
  }
179
- if (serialized === void 0) return "ai: responseFormat json_schema.schema is invalid";
180
- if (serialized.length > MAX_SCHEMA_JSON_LEN) {
181
- return `ai: responseFormat json_schema.schema exceeds ${MAX_SCHEMA_JSON_LEN} serialized chars`;
182
- }
183
- let keyCount = 0;
184
- const walk = (node, depth) => {
185
- if (depth > MAX_SCHEMA_DEPTH) {
186
- return `ai: responseFormat json_schema.schema exceeds max depth ${MAX_SCHEMA_DEPTH}`;
187
- }
188
- if (Array.isArray(node)) {
189
- for (const item of node) {
190
- const e = walk(item, depth + 1);
191
- if (e) return e;
122
+ if (e === void 0) return "ai: responseFormat json_schema.schema is invalid";
123
+ if (e.length > q)
124
+ return `ai: responseFormat json_schema.schema exceeds ${q} serialized chars`;
125
+ let r = 0, s = (n, i) => {
126
+ if (i > F)
127
+ return `ai: responseFormat json_schema.schema exceeds max depth ${F}`;
128
+ if (Array.isArray(n)) {
129
+ for (let a of n) {
130
+ let o = s(a, i + 1);
131
+ if (o) return o;
192
132
  }
193
133
  return null;
194
134
  }
195
- if (node && typeof node === "object") {
196
- for (const key of Object.getOwnPropertyNames(node)) {
197
- if (FORBIDDEN_SCHEMA_KEYS.has(key)) {
198
- return `ai: responseFormat json_schema.schema has forbidden key: ${key}`;
199
- }
200
- keyCount++;
201
- if (keyCount > MAX_SCHEMA_KEYS) {
202
- return `ai: responseFormat json_schema.schema exceeds ${MAX_SCHEMA_KEYS} keys`;
203
- }
204
- const e = walk(node[key], depth + 1);
205
- if (e) return e;
135
+ if (n && typeof n == "object")
136
+ for (let a of Object.getOwnPropertyNames(n)) {
137
+ if (te.has(a))
138
+ return `ai: responseFormat json_schema.schema has forbidden key: ${a}`;
139
+ if (r++, r > D)
140
+ return `ai: responseFormat json_schema.schema exceeds ${D} keys`;
141
+ let o = s(n[a], i + 1);
142
+ if (o) return o;
206
143
  }
207
- }
208
144
  return null;
209
145
  };
210
- return walk(schema, 0);
146
+ return s(t, 0);
211
147
  }
212
- function validateImageUrl(url) {
213
- if (typeof url !== "string" || url.length === 0) {
214
- fail("ai: image_url.url must be a non-empty string");
215
- }
216
- const u = url;
217
- if (u.startsWith("data:")) {
218
- if (u.length > MAX_DATA_URL_LEN) fail(`ai: image_url.url (data:) exceeds ${MAX_DATA_URL_LEN} chars`);
219
- if (!DATA_IMAGE_URL_RE.test(u)) {
220
- fail("ai: image_url.url data: must be data:image/<png|jpeg|jpg|gif|webp>;base64,<...>");
221
- }
222
- const b64 = u.slice(u.indexOf(",") + 1);
223
- if (!isStrictBase64Payload(b64)) {
224
- fail("ai: image_url.url data: has invalid base64 (length/padding)");
225
- }
148
+ function ae(t) {
149
+ (typeof t != "string" || t.length === 0) && c("ai: image_url.url must be a non-empty string");
150
+ let e = t;
151
+ if (e.startsWith("data:")) {
152
+ e.length > v && c(`ai: image_url.url (data:) exceeds ${v} chars`), Y.test(e) || c("ai: image_url.url data: must be data:image/<png|jpeg|jpg|gif|webp>;base64,<...>");
153
+ let r = e.slice(e.indexOf(",") + 1);
154
+ re(r) || c("ai: image_url.url data: has invalid base64 (length/padding)");
226
155
  return;
227
156
  }
228
- if (u.startsWith("https://")) {
229
- if (u.length > MAX_HTTPS_URL_LEN) fail(`ai: image_url.url (https:) exceeds ${MAX_HTTPS_URL_LEN} chars`);
230
- if (!HTTPS_URL_RE.test(u)) fail("ai: image_url.url https: is invalid");
157
+ if (e.startsWith("https://")) {
158
+ e.length > S && c(`ai: image_url.url (https:) exceeds ${S} chars`), Z.test(e) || c("ai: image_url.url https: is invalid");
231
159
  return;
232
160
  }
233
- fail("ai: image_url.url must be a data:image/...;base64 or https:// URL (other schemes are rejected)");
161
+ c("ai: image_url.url must be a data:image/...;base64 or https:// URL (other schemes are rejected)");
234
162
  }
235
- function validateAiContent(content, role) {
236
- if (typeof content === "string") {
237
- if (content.length === 0) fail("ai: message content string must be non-empty");
238
- if (content.length > MAX_TEXT_PART_LEN) fail(`ai: message content exceeds ${MAX_TEXT_PART_LEN} chars`);
163
+ function ce(t, e) {
164
+ if (typeof t == "string") {
165
+ t.length === 0 && c("ai: message content string must be non-empty"), t.length > _ && c(`ai: message content exceeds ${_} chars`);
239
166
  return;
240
167
  }
241
- if (!Array.isArray(content) || content.length === 0) {
242
- fail("ai: message content must be a non-empty string or array of parts");
243
- }
244
- if (content.length > MAX_CONTENT_PARTS) fail(`ai: message content has more than ${MAX_CONTENT_PARTS} parts`);
245
- for (const part of content) {
246
- if (!part || typeof part !== "object") fail("ai: each content part must be an object");
247
- const type = part.type;
248
- if (type === "text") {
249
- const text = part.text;
250
- if (typeof text !== "string" || text.length === 0) fail("ai: text part must have non-empty { text: string }");
251
- if (text.length > MAX_TEXT_PART_LEN) fail(`ai: text part exceeds ${MAX_TEXT_PART_LEN} chars`);
252
- } else if (type === "image_url") {
253
- if (role !== "user") {
254
- fail(`ai: image_url is only allowed in role 'user' messages (got '${role}')`);
255
- }
256
- const iu = part.image_url;
257
- if (!iu || typeof iu !== "object") fail("ai: image_url part must have { image_url: { url } }");
258
- validateImageUrl(iu.url);
259
- const detail = iu.detail;
260
- if (detail !== void 0 && (typeof detail !== "string" || !IMAGE_DETAIL_VALUES.has(detail))) {
261
- fail("ai: image_url.detail must be 'low' | 'high' | 'auto'");
262
- }
263
- } else {
264
- fail("ai: content part type must be 'text' or 'image_url'");
265
- }
168
+ (!Array.isArray(t) || t.length === 0) && c("ai: message content must be a non-empty string or array of parts"), t.length > R && c(`ai: message content has more than ${R} parts`);
169
+ for (let r of t) {
170
+ (!r || typeof r != "object") && c("ai: each content part must be an object");
171
+ let s = r.type;
172
+ if (s === "text") {
173
+ let n = r.text;
174
+ (typeof n != "string" || n.length === 0) && c("ai: text part must have non-empty { text: string }"), n.length > _ && c(`ai: text part exceeds ${_} chars`);
175
+ } else if (s === "image_url") {
176
+ e !== "user" && c(`ai: image_url is only allowed in role 'user' messages (got '${e}')`);
177
+ let n = r.image_url;
178
+ (!n || typeof n != "object") && c("ai: image_url part must have { image_url: { url } }"), ae(n.url);
179
+ let i = n.detail;
180
+ i !== void 0 && (typeof i != "string" || !Q.has(i)) && c("ai: image_url.detail must be 'low' | 'high' | 'auto'");
181
+ } else
182
+ c("ai: content part type must be 'text' or 'image_url'");
266
183
  }
267
184
  }
268
- function validateResponseFormat(rf) {
269
- if (!rf || typeof rf !== "object") fail("ai: responseFormat must be an object");
270
- const type = rf.type;
271
- if (type === "text" || type === "json_object") return;
272
- if (type === "json_schema") {
273
- const js = rf.json_schema;
274
- if (!js || typeof js !== "object") fail("ai: responseFormat json_schema requires { json_schema: { name } }");
275
- const jso = js;
276
- const name = jso.name;
277
- if (typeof name !== "string" || name.length === 0) fail("ai: responseFormat json_schema.name is required");
278
- if (name.length > 64) fail("ai: responseFormat json_schema.name exceeds 64 chars");
279
- if (!RESPONSE_FORMAT_NAME_RE.test(name)) fail("ai: responseFormat json_schema.name must match a-zA-Z0-9_-");
280
- if (jso.description !== void 0) {
281
- if (typeof jso.description !== "string") fail("ai: responseFormat json_schema.description must be a string");
282
- if (jso.description.length > 1e3) fail("ai: responseFormat json_schema.description exceeds 1000 chars");
283
- }
284
- if (jso.strict !== void 0 && typeof jso.strict !== "boolean") {
285
- fail("ai: responseFormat json_schema.strict must be a boolean");
286
- }
287
- if (jso.schema !== void 0) {
288
- const err = validateStructuredSchema(jso.schema);
289
- if (err) fail(err);
185
+ function ue(t) {
186
+ (!t || typeof t != "object") && c("ai: responseFormat must be an object");
187
+ let e = t.type;
188
+ if (!(e === "text" || e === "json_object")) {
189
+ if (e === "json_schema") {
190
+ let r = t.json_schema;
191
+ (!r || typeof r != "object") && c("ai: responseFormat json_schema requires { json_schema: { name } }");
192
+ let s = r, n = s.name;
193
+ if ((typeof n != "string" || n.length === 0) && c("ai: responseFormat json_schema.name is required"), n.length > 64 && c("ai: responseFormat json_schema.name exceeds 64 chars"), ne.test(n) || c("ai: responseFormat json_schema.name must match a-zA-Z0-9_-"), s.description !== void 0 && (typeof s.description != "string" && c("ai: responseFormat json_schema.description must be a string"), s.description.length > 1e3 && c("ai: responseFormat json_schema.description exceeds 1000 chars")), s.strict !== void 0 && typeof s.strict != "boolean" && c("ai: responseFormat json_schema.strict must be a boolean"), s.schema !== void 0) {
194
+ let i = oe(s.schema);
195
+ i && c(i);
196
+ }
197
+ return;
290
198
  }
291
- return;
199
+ c("ai: responseFormat.type must be 'text' | 'json_object' | 'json_schema'");
292
200
  }
293
- fail("ai: responseFormat.type must be 'text' | 'json_object' | 'json_schema'");
294
201
  }
295
- function resolveAiBody(config, opts, stream) {
296
- if (!opts || typeof opts !== "object") {
297
- throw new NeetruError("validation_failed", "ai: options object is required");
298
- }
299
- if (typeof opts.model !== "string" || opts.model.length === 0) {
300
- throw new NeetruError("validation_failed", "ai: model is required");
301
- }
302
- if (opts.model.length > MAX_MODEL_LEN) {
303
- throw new NeetruError("validation_failed", `ai: model exceeds ${MAX_MODEL_LEN} chars`);
304
- }
305
- if (!Array.isArray(opts.messages) || opts.messages.length === 0) {
306
- throw new NeetruError("validation_failed", "ai: messages must be a non-empty array");
307
- }
308
- if (opts.messages.length > MAX_MESSAGES) {
309
- throw new NeetruError("validation_failed", `ai: messages exceeds ${MAX_MESSAGES} per request`);
310
- }
311
- for (const m of opts.messages) {
312
- if (!m || typeof m !== "object" || typeof m.role !== "string" || !AI_ROLES.has(m.role)) {
313
- throw new NeetruError(
202
+ function U(t, e, r) {
203
+ if (!e || typeof e != "object")
204
+ throw new u("validation_failed", "ai: options object is required");
205
+ if (typeof e.model != "string" || e.model.length === 0)
206
+ throw new u("validation_failed", "ai: model is required");
207
+ if (e.model.length > j)
208
+ throw new u("validation_failed", `ai: model exceeds ${j} chars`);
209
+ if (!Array.isArray(e.messages) || e.messages.length === 0)
210
+ throw new u("validation_failed", "ai: messages must be a non-empty array");
211
+ if (e.messages.length > M)
212
+ throw new u("validation_failed", `ai: messages exceeds ${M} per request`);
213
+ for (let o of e.messages) {
214
+ if (!o || typeof o != "object" || typeof o.role != "string" || !ee.has(o.role))
215
+ throw new u(
314
216
  "validation_failed",
315
217
  "ai: each message role must be 'system' | 'user' | 'assistant'"
316
218
  );
317
- }
318
- validateAiContent(m.content, m.role);
219
+ ce(o.content, o.role);
319
220
  }
320
- const aggErr = checkAggregateMultimodalLimits(opts.messages);
321
- if (aggErr) throw new NeetruError("validation_failed", aggErr);
322
- const productId = opts.productId ?? config.productId;
323
- const tenantId = opts.tenantId ?? config.tenantId;
324
- if (!productId) {
325
- throw new NeetruError(
221
+ let s = ie(e.messages);
222
+ if (s) throw new u("validation_failed", s);
223
+ let n = e.productId ?? t.productId, i = e.tenantId ?? t.tenantId;
224
+ if (!n)
225
+ throw new u(
326
226
  "validation_failed",
327
227
  "ai: productId required (pass to options or set on createNeetruClient)"
328
228
  );
329
- }
330
- if (!tenantId) {
331
- throw new NeetruError(
229
+ if (!i)
230
+ throw new u(
332
231
  "validation_failed",
333
232
  "ai: tenantId required (pass to options or set on createNeetruClient)"
334
233
  );
335
- }
336
- assertValidSdkId("productId", productId);
337
- assertValidSdkId("tenantId", tenantId);
338
- const body = {
339
- productId,
340
- tenantId,
341
- model: opts.model,
342
- messages: opts.messages,
343
- stream
234
+ A("productId", n), A("tenantId", i);
235
+ let a = {
236
+ productId: n,
237
+ tenantId: i,
238
+ model: e.model,
239
+ messages: e.messages,
240
+ stream: r
344
241
  };
345
- if (opts.maxTokens !== void 0) {
346
- if (!Number.isInteger(opts.maxTokens) || opts.maxTokens <= 0 || opts.maxTokens > MAX_TOKENS_CAP) {
347
- throw new NeetruError(
242
+ if (e.maxTokens !== void 0) {
243
+ if (!Number.isInteger(e.maxTokens) || e.maxTokens <= 0 || e.maxTokens > C)
244
+ throw new u(
348
245
  "validation_failed",
349
- `ai: maxTokens must be a positive integer <= ${MAX_TOKENS_CAP}`
246
+ `ai: maxTokens must be a positive integer <= ${C}`
350
247
  );
351
- }
352
- body.maxTokens = opts.maxTokens;
248
+ a.maxTokens = e.maxTokens;
353
249
  }
354
- if (opts.temperature !== void 0) {
355
- if (typeof opts.temperature !== "number" || !Number.isFinite(opts.temperature) || opts.temperature < MIN_TEMPERATURE || opts.temperature > MAX_TEMPERATURE) {
356
- throw new NeetruError(
250
+ if (e.temperature !== void 0) {
251
+ if (typeof e.temperature != "number" || !Number.isFinite(e.temperature) || e.temperature < N || e.temperature > I)
252
+ throw new u(
357
253
  "validation_failed",
358
- `ai: temperature must be a finite number between ${MIN_TEMPERATURE} and ${MAX_TEMPERATURE}`
254
+ `ai: temperature must be a finite number between ${N} and ${I}`
359
255
  );
360
- }
361
- body.temperature = opts.temperature;
256
+ a.temperature = e.temperature;
362
257
  }
363
- if (opts.responseFormat !== void 0) {
364
- validateResponseFormat(opts.responseFormat);
365
- body.responseFormat = opts.responseFormat;
366
- }
367
- return body;
258
+ return e.responseFormat !== void 0 && (ue(e.responseFormat), a.responseFormat = e.responseFormat), a;
368
259
  }
369
- async function safeJson(res) {
260
+ async function X(t) {
370
261
  try {
371
- const text = await res.text();
372
- if (!text) return void 0;
373
- return JSON.parse(text);
262
+ let e = await t.text();
263
+ return e ? JSON.parse(e) : void 0;
374
264
  } catch {
375
- return void 0;
265
+ return;
376
266
  }
377
267
  }
378
- function joinUrl(baseUrl, path) {
379
- const base = baseUrl.replace(/\/+$/, "");
380
- const p = path.startsWith("/") ? path : `/${path}`;
381
- return `${base}${p}`;
268
+ function de(t, e) {
269
+ let r = t.replace(/\/+$/, ""), s = e.startsWith("/") ? e : `/${e}`;
270
+ return `${r}${s}`;
382
271
  }
383
- async function aiFetch(config, body, signal, stream) {
384
- if (!config.apiKey) {
385
- throw new NeetruError(
272
+ async function L(t, e, r, s) {
273
+ if (!t.apiKey)
274
+ throw new u(
386
275
  "missing_api_key",
387
276
  "ai requires an apiKey. Pass it to createNeetruClient({ apiKey }) or set NEETRU_API_KEY env var."
388
277
  );
389
- }
390
- const url = joinUrl(config.baseUrl, AI_CHAT_PATH);
391
- const headers = {
278
+ let n = de(t.baseUrl, V), a = { method: "POST", headers: {
392
279
  "content-type": "application/json",
393
- accept: stream ? "text/event-stream" : "application/json",
394
- authorization: `Bearer ${config.apiKey}`
395
- };
396
- const init = { method: "POST", headers, body: JSON.stringify(body) };
397
- if (stream) {
398
- if (signal) init.signal = signal;
399
- } else {
400
- init.signal = signal ?? AbortSignal.timeout(AI_NONSTREAM_TIMEOUT_MS);
401
- }
402
- let res;
280
+ accept: s ? "text/event-stream" : "application/json",
281
+ authorization: `Bearer ${t.apiKey}`
282
+ }, body: JSON.stringify(e) };
283
+ s ? r && (a.signal = r) : a.signal = r ?? AbortSignal.timeout(T);
284
+ let o;
403
285
  try {
404
- res = await config.fetch(url, init);
405
- } catch (err) {
406
- 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"}`;
407
- throw new NeetruError("network_error", message);
408
- }
409
- const requestId = res.headers.get("x-request-id") ?? res.headers.get("x-correlation-id") ?? void 0;
410
- if (!res.ok) {
411
- const parsed = await safeJson(res);
412
- let code = statusToCode(res.status);
413
- let message = `HTTP ${res.status}`;
414
- if (parsed && typeof parsed === "object" && "error" in parsed) {
415
- const errField = parsed.error;
416
- if (typeof errField === "string") {
417
- message = errField;
418
- } else if (errField && typeof errField === "object") {
419
- if (typeof errField.code === "string") code = errField.code;
420
- if (typeof errField.message === "string") message = errField.message;
421
- }
286
+ o = await t.fetch(n, a);
287
+ } catch (d) {
288
+ let f = d instanceof DOMException && d.name === "TimeoutError" ? `Network error: timeout after ${T / 1e3}s` : d instanceof DOMException && d.name === "AbortError" ? "Network error: request aborted" : `Network error: ${d instanceof Error ? d.message : "fetch failed"}`;
289
+ throw new u("network_error", f);
290
+ }
291
+ let g = o.headers.get("x-request-id") ?? o.headers.get("x-correlation-id") ?? void 0;
292
+ if (!o.ok) {
293
+ let d = await X(o), f = x(o.status), m = `HTTP ${o.status}`;
294
+ if (d && typeof d == "object" && "error" in d) {
295
+ let l = d.error;
296
+ typeof l == "string" ? m = l : l && typeof l == "object" && (typeof l.code == "string" && (f = l.code), typeof l.message == "string" && (m = l.message));
422
297
  }
423
- throw new NeetruError(code, message, res.status, requestId);
298
+ throw new u(f, m, o.status, g);
424
299
  }
425
- const coreVersion = res.headers.get("x-neetru-version");
426
- if (coreVersion) checkCoreVersionCompat(coreVersion);
427
- return res;
300
+ let h = o.headers.get("x-neetru-version");
301
+ return h && E(h), o;
428
302
  }
429
- function readMeta(res) {
303
+ function H(t) {
430
304
  return {
431
- provider: res.headers.get("x-neetru-provider") ?? "",
432
- usageWarning: res.headers.get("x-neetru-ai-usage-warning") === "1"
305
+ provider: t.headers.get("x-neetru-provider") ?? "",
306
+ usageWarning: t.headers.get("x-neetru-ai-usage-warning") === "1"
433
307
  };
434
308
  }
435
- function parseSseData(rawEvent) {
436
- const dataLines = [];
437
- for (const line of rawEvent.split("\n")) {
438
- const trimmed = line.replace(/\r$/, "");
439
- if (trimmed.startsWith("data:")) {
440
- let v = trimmed.slice(5);
441
- if (v.startsWith(" ")) v = v.slice(1);
442
- dataLines.push(v);
309
+ function K(t) {
310
+ let e = [];
311
+ for (let s of t.split(`
312
+ `)) {
313
+ let n = s.replace(/\r$/, "");
314
+ if (n.startsWith("data:")) {
315
+ let i = n.slice(5);
316
+ i.startsWith(" ") && (i = i.slice(1)), e.push(i);
443
317
  }
444
318
  }
445
- if (dataLines.length === 0) return null;
446
- const payload = dataLines.join("\n");
447
- if (payload === "[DONE]") return { done: true };
319
+ if (e.length === 0) return null;
320
+ let r = e.join(`
321
+ `);
322
+ if (r === "[DONE]") return { done: true };
448
323
  try {
449
- const obj = JSON.parse(payload);
450
- const content = obj?.choices?.[0]?.delta?.content;
451
- if (typeof content === "string" && content.length > 0) return { content };
452
- return null;
324
+ let n = JSON.parse(r)?.choices?.[0]?.delta?.content;
325
+ return typeof n == "string" && n.length > 0 ? { content: n } : null;
453
326
  } catch {
454
327
  return null;
455
328
  }
456
329
  }
457
- function createAiNamespace(config) {
330
+ function ye(t) {
458
331
  return {
459
- async chat(opts) {
460
- const body = resolveAiBody(config, opts, false);
461
- const res = await aiFetch(config, body, opts.signal, false);
462
- const { provider: providerHeader, usageWarning } = readMeta(res);
463
- const raw = await safeJson(res);
464
- if (!raw || raw.ok !== true || !Array.isArray(raw.choices)) {
465
- throw new NeetruError("invalid_response", "ai.chat response missing ok/choices");
466
- }
332
+ async chat(e) {
333
+ let r = U(t, e, false), s = await L(t, r, e.signal, false), { provider: n, usageWarning: i } = H(s), a = await X(s);
334
+ if (!a || a.ok !== true || !Array.isArray(a.choices))
335
+ throw new u("invalid_response", "ai.chat response missing ok/choices");
467
336
  return {
468
- choices: raw.choices,
469
- usage: raw.usage ?? { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 },
470
- model: raw.model ?? opts.model,
471
- provider: raw.provider ?? providerHeader ?? "",
472
- usageWarning
337
+ choices: a.choices,
338
+ usage: a.usage ?? { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 },
339
+ model: a.model ?? e.model,
340
+ provider: a.provider ?? n ?? "",
341
+ usageWarning: i
473
342
  };
474
343
  },
475
- stream(opts) {
476
- const body = resolveAiBody(config, opts, true);
477
- const responsePromise = aiFetch(config, body, opts.signal, true);
478
- const meta = responsePromise.then(readMeta);
479
- meta.catch(() => {
344
+ stream(e) {
345
+ let r = U(t, e, true), s = L(t, r, e.signal, true), n = s.then(H);
346
+ n.catch(() => {
480
347
  });
481
- async function* iterate() {
482
- const res = await responsePromise;
483
- const stream = res.body;
484
- if (!stream) {
485
- throw new NeetruError("invalid_response", "ai.stream: response has no body");
486
- }
487
- const reader = stream.getReader();
488
- const decoder = new TextDecoder();
489
- let buffer = "";
348
+ async function* i() {
349
+ let o = (await s).body;
350
+ if (!o)
351
+ throw new u("invalid_response", "ai.stream: response has no body");
352
+ let g = o.getReader(), h = new TextDecoder(), d = "";
490
353
  try {
491
- while (true) {
492
- const { done, value } = await reader.read();
493
- if (done) break;
494
- buffer += decoder.decode(value, { stream: true });
495
- let sep;
496
- while ((sep = buffer.indexOf("\n\n")) !== -1) {
497
- const rawEvent = buffer.slice(0, sep);
498
- buffer = buffer.slice(sep + 2);
499
- const parsed = parseSseData(rawEvent);
500
- if (parsed && "done" in parsed) return;
501
- if (parsed && "content" in parsed) yield parsed.content;
354
+ for (; ; ) {
355
+ let { done: m, value: l } = await g.read();
356
+ if (m) break;
357
+ d += h.decode(l, { stream: !0 });
358
+ let y;
359
+ for (; (y = d.indexOf(`
360
+
361
+ `)) !== -1; ) {
362
+ let W = d.slice(0, y);
363
+ d = d.slice(y + 2);
364
+ let p = K(W);
365
+ if (p && "done" in p) return;
366
+ p && "content" in p && (yield p.content);
502
367
  }
503
368
  }
504
- const tail = buffer.trim();
505
- if (tail) {
506
- const parsed = parseSseData(tail);
507
- if (parsed && "content" in parsed) yield parsed.content;
369
+ let f = d.trim();
370
+ if (f) {
371
+ let m = K(f);
372
+ m && "content" in m && (yield m.content);
508
373
  }
509
374
  } finally {
510
375
  try {
511
- reader.releaseLock();
376
+ g.releaseLock();
512
377
  } catch {
513
378
  }
514
379
  try {
515
- await stream.cancel();
380
+ await o.cancel();
516
381
  } catch {
517
382
  }
518
383
  }
519
384
  }
520
385
  return {
521
- meta,
522
- [Symbol.asyncIterator]: iterate
386
+ meta: n,
387
+ [Symbol.asyncIterator]: i
523
388
  };
524
389
  }
525
390
  };
526
391
  }
527
392
 
528
- exports.createAiNamespace = createAiNamespace;
529
- //# sourceMappingURL=ai.cjs.map
530
- //# sourceMappingURL=ai.cjs.map
393
+ exports.createAiNamespace = ye;