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