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