@neetru/sdk 3.2.2 → 3.3.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.
- package/CHANGELOG.md +36 -0
- package/dist/ai.cjs +229 -7
- package/dist/ai.cjs.map +1 -1
- package/dist/ai.d.cts +1 -1
- package/dist/ai.d.ts +1 -1
- package/dist/ai.mjs +229 -7
- package/dist/ai.mjs.map +1 -1
- package/dist/auth.cjs +556 -20
- 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 +556 -20
- package/dist/auth.mjs.map +1 -1
- package/dist/catalog.cjs +19 -3
- package/dist/catalog.cjs.map +1 -1
- package/dist/catalog.d.cts +1 -1
- package/dist/catalog.d.ts +1 -1
- package/dist/catalog.mjs +19 -3
- package/dist/catalog.mjs.map +1 -1
- package/dist/checkout.cjs +19 -3
- package/dist/checkout.cjs.map +1 -1
- package/dist/checkout.d.cts +1 -1
- package/dist/checkout.d.ts +1 -1
- package/dist/checkout.mjs +19 -3
- package/dist/checkout.mjs.map +1 -1
- package/dist/db-react.d.cts +1 -1
- package/dist/db-react.d.ts +1 -1
- package/dist/db.cjs +330 -12
- 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 +330 -12
- package/dist/db.mjs.map +1 -1
- package/dist/entitlements.cjs +19 -3
- package/dist/entitlements.cjs.map +1 -1
- package/dist/entitlements.d.cts +1 -1
- package/dist/entitlements.d.ts +1 -1
- package/dist/entitlements.mjs +19 -3
- package/dist/entitlements.mjs.map +1 -1
- package/dist/errors.cjs +11 -1
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.cts +10 -1
- package/dist/errors.d.ts +10 -1
- package/dist/errors.mjs +11 -1
- package/dist/errors.mjs.map +1 -1
- package/dist/firestore-compat.d.cts +1 -1
- package/dist/firestore-compat.d.ts +1 -1
- package/dist/index.cjs +557 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.mjs +557 -21
- package/dist/index.mjs.map +1 -1
- package/dist/mocks.cjs +19 -3
- package/dist/mocks.cjs.map +1 -1
- package/dist/mocks.d.cts +3 -1
- package/dist/mocks.d.ts +3 -1
- package/dist/mocks.mjs +19 -3
- package/dist/mocks.mjs.map +1 -1
- package/dist/notifications.cjs +19 -3
- package/dist/notifications.cjs.map +1 -1
- package/dist/notifications.d.cts +1 -1
- package/dist/notifications.d.ts +1 -1
- package/dist/notifications.mjs +19 -3
- package/dist/notifications.mjs.map +1 -1
- package/dist/react.d.cts +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/support.cjs +19 -3
- package/dist/support.cjs.map +1 -1
- package/dist/support.d.cts +1 -1
- package/dist/support.d.ts +1 -1
- package/dist/support.mjs +19 -3
- package/dist/support.mjs.map +1 -1
- package/dist/telemetry.cjs +19 -3
- 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 +19 -3
- package/dist/telemetry.mjs.map +1 -1
- package/dist/{types-PzHvWMbV.d.cts → types-DQplLXIk.d.cts} +69 -5
- package/dist/{types-iLDjiqqk.d.ts → types-DslphKIM.d.ts} +69 -5
- package/dist/usage.cjs +19 -3
- 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 +19 -3
- package/dist/usage.mjs.map +1 -1
- package/dist/webhooks.cjs +19 -3
- package/dist/webhooks.cjs.map +1 -1
- package/dist/webhooks.d.cts +1 -1
- package/dist/webhooks.d.ts +1 -1
- package/dist/webhooks.mjs +19 -3
- package/dist/webhooks.mjs.map +1 -1
- package/package.json +1 -1
package/dist/db.cjs
CHANGED
|
@@ -20,12 +20,22 @@ var init_errors = __esm({
|
|
|
20
20
|
code;
|
|
21
21
|
status;
|
|
22
22
|
requestId;
|
|
23
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Motivo semântico opcional propagado pelo Core no corpo do erro (campo
|
|
25
|
+
* `reason` no TOPO do body, fora de `error`). Usado principalmente pelo 503
|
|
26
|
+
* de lease de DB, onde `reason` discrimina o estado do ambiente
|
|
27
|
+
* (`suspended` | `archived` | `revoked` | `provisioning` | `degraded` |
|
|
28
|
+
* `no_route` | `expired`). Camadas superiores (ex.: `db/sql`) mapeiam isso
|
|
29
|
+
* para um `NeetruDbErrorCode` mais específico (recuperável vs não).
|
|
30
|
+
*/
|
|
31
|
+
reason;
|
|
32
|
+
constructor(code, message, status, requestId, reason) {
|
|
24
33
|
super(message);
|
|
25
34
|
this.name = "NeetruError";
|
|
26
35
|
this.code = code;
|
|
27
36
|
this.status = status;
|
|
28
37
|
this.requestId = requestId;
|
|
38
|
+
this.reason = reason;
|
|
29
39
|
Object.setPrototypeOf(this, _NeetruError.prototype);
|
|
30
40
|
}
|
|
31
41
|
};
|
|
@@ -231,16 +241,22 @@ async function httpRequest(config, opts) {
|
|
|
231
241
|
const body = await safeJson(res);
|
|
232
242
|
let code = statusToCode(res.status);
|
|
233
243
|
let message = `HTTP ${res.status}`;
|
|
234
|
-
|
|
244
|
+
let reason;
|
|
245
|
+
if (body && typeof body === "object") {
|
|
235
246
|
const errField = body.error;
|
|
236
247
|
if (typeof errField === "string") {
|
|
237
248
|
message = errField;
|
|
238
249
|
} else if (errField && typeof errField === "object") {
|
|
239
250
|
if (typeof errField.code === "string") code = errField.code;
|
|
240
251
|
if (typeof errField.message === "string") message = errField.message;
|
|
252
|
+
if (typeof errField.reason === "string") reason = errField.reason;
|
|
253
|
+
} else {
|
|
254
|
+
if (typeof body.code === "string") code = body.code;
|
|
255
|
+
if (typeof body.message === "string") message = body.message;
|
|
241
256
|
}
|
|
257
|
+
if (typeof body.reason === "string") reason = body.reason;
|
|
242
258
|
}
|
|
243
|
-
const err = new NeetruError(code, message, res.status, requestId);
|
|
259
|
+
const err = new NeetruError(code, message, res.status, requestId, reason);
|
|
244
260
|
lastError = err;
|
|
245
261
|
const isRetryable = RETRYABLE_CODES2.has(code);
|
|
246
262
|
if (isRetryable && attempt < maxRetries) {
|
|
@@ -276,8 +292,98 @@ var init_http = __esm({
|
|
|
276
292
|
}
|
|
277
293
|
});
|
|
278
294
|
|
|
295
|
+
// src/db/sql/resilient-pool.ts
|
|
296
|
+
function isResilientPool(x) {
|
|
297
|
+
return typeof x === "object" && x !== null && x[RESILIENT_POOL_BRAND] === true;
|
|
298
|
+
}
|
|
299
|
+
function isConnectionEstablishmentError(err) {
|
|
300
|
+
const code = err && typeof err === "object" && "code" in err ? err.code : void 0;
|
|
301
|
+
if (code === "ECONNREFUSED" || code === "ETIMEDOUT" || code === "ENOTFOUND" || code === "EHOSTUNREACH" || code === "EAI_AGAIN") {
|
|
302
|
+
return true;
|
|
303
|
+
}
|
|
304
|
+
if (typeof code === "string") {
|
|
305
|
+
const upper = code.toUpperCase();
|
|
306
|
+
if (upper === "CERT_HAS_EXPIRED" || upper.startsWith("ERR_SSL") || upper.startsWith("ERR_TLS") || upper.includes("CERTIFICATE_EXPIRED")) {
|
|
307
|
+
return true;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
const rawMessage = err instanceof Error ? err.message : typeof err === "string" ? err : "";
|
|
311
|
+
const message = rawMessage.toLowerCase();
|
|
312
|
+
if (message.length === 0) return false;
|
|
313
|
+
if (message.includes("certificate expired") || message.includes("certificate has expired") || message.includes("cert_has_expired") || message.includes("alert certificate expired") || message.includes("err_ssl") || message.includes("sslv3 alert") || message.includes("tlsv1 alert")) {
|
|
314
|
+
return true;
|
|
315
|
+
}
|
|
316
|
+
if (message.includes("econnrefused") || message.includes("etimedout") || message.includes("enotfound") || message.includes("ehostunreach") || message.includes("getaddrinfo")) {
|
|
317
|
+
return true;
|
|
318
|
+
}
|
|
319
|
+
return false;
|
|
320
|
+
}
|
|
321
|
+
function createResilientPool(initialInner) {
|
|
322
|
+
const state = { current: initialInner };
|
|
323
|
+
let reLease = null;
|
|
324
|
+
const controls = {
|
|
325
|
+
__neetruSwapInner(newInner) {
|
|
326
|
+
const old = state.current;
|
|
327
|
+
state.current = newInner;
|
|
328
|
+
return old;
|
|
329
|
+
},
|
|
330
|
+
__neetruGetInner() {
|
|
331
|
+
return state.current;
|
|
332
|
+
},
|
|
333
|
+
__neetruSetReLease(fn) {
|
|
334
|
+
reLease = fn;
|
|
335
|
+
}
|
|
336
|
+
};
|
|
337
|
+
const invokeQuery = (pool, args) => pool.query(...args);
|
|
338
|
+
const wrappedQuery = (...args) => {
|
|
339
|
+
if (args.length > 0 && typeof args[args.length - 1] === "function") {
|
|
340
|
+
return invokeQuery(state.current, args);
|
|
341
|
+
}
|
|
342
|
+
return (async () => {
|
|
343
|
+
try {
|
|
344
|
+
return await invokeQuery(state.current, args);
|
|
345
|
+
} catch (err) {
|
|
346
|
+
const fn = reLease;
|
|
347
|
+
if (fn === null || !isConnectionEstablishmentError(err)) {
|
|
348
|
+
throw err;
|
|
349
|
+
}
|
|
350
|
+
try {
|
|
351
|
+
await fn();
|
|
352
|
+
} catch {
|
|
353
|
+
throw err;
|
|
354
|
+
}
|
|
355
|
+
return await invokeQuery(state.current, args);
|
|
356
|
+
}
|
|
357
|
+
})();
|
|
358
|
+
};
|
|
359
|
+
const target = Object.create(Object.getPrototypeOf(initialInner));
|
|
360
|
+
const proxy = new Proxy(target, {
|
|
361
|
+
get(_t, prop) {
|
|
362
|
+
if (prop === RESILIENT_POOL_BRAND) return true;
|
|
363
|
+
if (prop === "__neetruSwapInner") return controls.__neetruSwapInner;
|
|
364
|
+
if (prop === "__neetruGetInner") return controls.__neetruGetInner;
|
|
365
|
+
if (prop === "__neetruSetReLease") return controls.__neetruSetReLease;
|
|
366
|
+
if (prop === "query") return wrappedQuery;
|
|
367
|
+
if (prop === "constructor") {
|
|
368
|
+
return state.current.constructor;
|
|
369
|
+
}
|
|
370
|
+
const current = state.current;
|
|
371
|
+
const value = Reflect.get(current, prop, current);
|
|
372
|
+
return typeof value === "function" ? value.bind(current) : value;
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
return proxy;
|
|
376
|
+
}
|
|
377
|
+
var RESILIENT_POOL_BRAND;
|
|
378
|
+
var init_resilient_pool = __esm({
|
|
379
|
+
"src/db/sql/resilient-pool.ts"() {
|
|
380
|
+
RESILIENT_POOL_BRAND = /* @__PURE__ */ Symbol.for("neetru.sdk.resilientPool");
|
|
381
|
+
}
|
|
382
|
+
});
|
|
383
|
+
|
|
279
384
|
// src/db/sql/lease.ts
|
|
280
385
|
function hasConnectionChanged(oldLease, newLease) {
|
|
386
|
+
if (!oldLease) return true;
|
|
281
387
|
return oldLease.leaseId !== newLease.leaseId || oldLease.credentialVersion !== newLease.credentialVersion || oldLease.host !== newLease.host || oldLease.port !== newLease.port || oldLease.dbName !== newLease.dbName || oldLease.user !== newLease.user || oldLease.password !== newLease.password || oldLease.sslca !== newLease.sslca || oldLease.clientCert !== newLease.clientCert || oldLease.clientKey !== newLease.clientKey;
|
|
282
388
|
}
|
|
283
389
|
function mapPoolError(err) {
|
|
@@ -305,14 +411,31 @@ var RENEWAL_THRESHOLD, MIN_RENEWAL_DELAY_MS, SqlLeaseManager;
|
|
|
305
411
|
var init_lease = __esm({
|
|
306
412
|
"src/db/sql/lease.ts"() {
|
|
307
413
|
init_db_errors();
|
|
414
|
+
init_resilient_pool();
|
|
308
415
|
RENEWAL_THRESHOLD = 0.8;
|
|
309
416
|
MIN_RENEWAL_DELAY_MS = 3e4;
|
|
310
417
|
SqlLeaseManager = class {
|
|
418
|
+
/**
|
|
419
|
+
* Lease atual. `null` no modo LAZY antes da 1ª query (o 1º lease é adiado —
|
|
420
|
+
* ver `_ensureInitialLease`). Após a 1ª aquisição, sempre não-nulo.
|
|
421
|
+
*/
|
|
311
422
|
_lease;
|
|
312
423
|
_pool;
|
|
313
424
|
_orm;
|
|
314
425
|
_renewalTimer = null;
|
|
315
426
|
_closed = false;
|
|
427
|
+
/**
|
|
428
|
+
* Promessa de re-lease REATIVO em voo — coalesce chamadas concorrentes
|
|
429
|
+
* (N queries falhando ao mesmo tempo disparam UM único re-lease). Ver
|
|
430
|
+
* `_reactiveReLease`.
|
|
431
|
+
*/
|
|
432
|
+
_reLeaseInFlight = null;
|
|
433
|
+
/**
|
|
434
|
+
* Promessa da aquisição do LEASE INICIAL em voo (modo lazy) — coalesce as
|
|
435
|
+
* N primeiras queries concorrentes num único fetch. `null` quando não há
|
|
436
|
+
* aquisição inicial pendente. Ver `_ensureInitialLease`.
|
|
437
|
+
*/
|
|
438
|
+
_initInFlight = null;
|
|
316
439
|
_deps;
|
|
317
440
|
constructor(lease, pool, orm, deps) {
|
|
318
441
|
this._lease = lease;
|
|
@@ -322,7 +445,7 @@ var init_lease = __esm({
|
|
|
322
445
|
...deps,
|
|
323
446
|
now: deps.now ?? (() => Date.now())
|
|
324
447
|
};
|
|
325
|
-
this._scheduleRenewal();
|
|
448
|
+
if (this._lease) this._scheduleRenewal();
|
|
326
449
|
}
|
|
327
450
|
// ─── Superfície pública (NeetruSqlClient) ──────────────────────────────────
|
|
328
451
|
get orm() {
|
|
@@ -360,6 +483,7 @@ var init_lease = __esm({
|
|
|
360
483
|
* usa `MIN_RENEWAL_DELAY_MS` como fallback seguro.
|
|
361
484
|
*/
|
|
362
485
|
_calcRenewalDelayMs() {
|
|
486
|
+
if (!this._lease) return MIN_RENEWAL_DELAY_MS;
|
|
363
487
|
const expiresAtMs = Date.parse(this._lease.expiresAt);
|
|
364
488
|
if (!Number.isFinite(expiresAtMs)) return MIN_RENEWAL_DELAY_MS;
|
|
365
489
|
const now = this._deps.now();
|
|
@@ -383,6 +507,8 @@ var init_lease = __esm({
|
|
|
383
507
|
}
|
|
384
508
|
async _doRenew() {
|
|
385
509
|
if (this._closed) return;
|
|
510
|
+
const currentLease = this._lease;
|
|
511
|
+
if (!currentLease) return;
|
|
386
512
|
try {
|
|
387
513
|
const withDb = (o) => {
|
|
388
514
|
if (this._deps.database !== void 0) o.database = this._deps.database;
|
|
@@ -390,7 +516,7 @@ var init_lease = __esm({
|
|
|
390
516
|
};
|
|
391
517
|
let newLease;
|
|
392
518
|
try {
|
|
393
|
-
newLease = await this._deps.fetchLease(withDb({ leaseId:
|
|
519
|
+
newLease = await this._deps.fetchLease(withDb({ leaseId: currentLease.leaseId }));
|
|
394
520
|
} catch (renewErr) {
|
|
395
521
|
newLease = await this._deps.fetchLease(withDb({}));
|
|
396
522
|
}
|
|
@@ -407,6 +533,114 @@ var init_lease = __esm({
|
|
|
407
533
|
}
|
|
408
534
|
this._scheduleRenewal();
|
|
409
535
|
}
|
|
536
|
+
// ─── Re-lease REATIVO (P1.5) ───────────────────────────────────────────────
|
|
537
|
+
/**
|
|
538
|
+
* Recuperação REATIVA: chamado pelo wrapper resiliente (`resilient-pool.ts`)
|
|
539
|
+
* quando uma query plana falha num erro de ESTABELECIMENTO de conexão (cert
|
|
540
|
+
* mTLS expirado no meio de uma request, ou timer de renovação que não
|
|
541
|
+
* disparou). Emite um lease NOVO e faz o swap do pool interno do wrapper com
|
|
542
|
+
* o certificado fresco; o wrapper então re-tenta a query 1x no pool novo.
|
|
543
|
+
*
|
|
544
|
+
* Vai DIRETO ao lease novo (sem `leaseId`) — a conexão falhou porque o
|
|
545
|
+
* certificado do lease atual está ruim, então `renew` (que depende do lease
|
|
546
|
+
* antigo estar vivo) não é o caminho mais robusto. Emitir do zero é o que já
|
|
547
|
+
* cura o death-spiral no caminho proativo (ver `_doRenew`).
|
|
548
|
+
*
|
|
549
|
+
* Coalesce concorrência: N queries falhando ao mesmo tempo compartilham UM
|
|
550
|
+
* único re-lease (não N leases). Após o swap, cada query re-tenta no pool
|
|
551
|
+
* vigente.
|
|
552
|
+
*
|
|
553
|
+
* NÃO engole erro: se o re-lease falhar, propaga — o wrapper então relança o
|
|
554
|
+
* erro ORIGINAL da query pro consumer.
|
|
555
|
+
*/
|
|
556
|
+
async _reactiveReLease() {
|
|
557
|
+
if (this._closed) {
|
|
558
|
+
throw new exports.NeetruDbError(
|
|
559
|
+
"db_unavailable",
|
|
560
|
+
"SqlLeaseManager foi fechado \u2014 re-lease reativo abortado."
|
|
561
|
+
);
|
|
562
|
+
}
|
|
563
|
+
if (this._reLeaseInFlight !== null) return this._reLeaseInFlight;
|
|
564
|
+
const run = (async () => {
|
|
565
|
+
const withDb = (o) => {
|
|
566
|
+
if (this._deps.database !== void 0) o.database = this._deps.database;
|
|
567
|
+
return o;
|
|
568
|
+
};
|
|
569
|
+
const fresh = await this._deps.fetchLease(withDb({}));
|
|
570
|
+
await this._swapPool(fresh);
|
|
571
|
+
})();
|
|
572
|
+
this._reLeaseInFlight = run;
|
|
573
|
+
try {
|
|
574
|
+
await run;
|
|
575
|
+
} finally {
|
|
576
|
+
this._reLeaseInFlight = null;
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
// ─── Lease INICIAL lazy (motor de ambientes suspenso) ──────────────────────
|
|
580
|
+
/**
|
|
581
|
+
* Adquire o LEASE INICIAL sob demanda (modo lazy). Chamado pelo pool "adiado"
|
|
582
|
+
* (`createDeferredPool`) na 1ª query/transação — nunca no boot do módulo.
|
|
583
|
+
*
|
|
584
|
+
* Fluxo:
|
|
585
|
+
* 1. Se já há lease (`_lease != null`), devolve o pool REAL interno vigente
|
|
586
|
+
* (idempotente — chamadas após a 1ª aquisição são no-op de fetch).
|
|
587
|
+
* 2. Busca o 1º lease no Core (com `database` se houver — contrato C-H).
|
|
588
|
+
* 3. Cria o pool REAL e faz o swap no wrapper resiliente (`__neetruSwapInner`)
|
|
589
|
+
* pra que as queries seguintes passem direto pelo pool real.
|
|
590
|
+
* 4. Agenda a renovação proativa (só a partir daqui — antes não havia lease).
|
|
591
|
+
*
|
|
592
|
+
* Coalesce concorrência: N primeiras queries compartilham UM único fetch
|
|
593
|
+
* (`_initInFlight`). Se a aquisição FALHA (ex.: ambiente suspenso →
|
|
594
|
+
* `db_suspended`, ou Core fora → `db_unavailable`), o erro propaga pra query
|
|
595
|
+
* que a disparou e `_initInFlight` é limpo — a PRÓXIMA query re-tenta (o
|
|
596
|
+
* ambiente pode ter voltado). NÃO derruba o processo nem o import do módulo.
|
|
597
|
+
*/
|
|
598
|
+
async _ensureInitialLease() {
|
|
599
|
+
if (this._closed) {
|
|
600
|
+
throw new exports.NeetruDbError(
|
|
601
|
+
"db_unavailable",
|
|
602
|
+
"SqlLeaseManager foi fechado \u2014 aquisi\xE7\xE3o de lease inicial abortada."
|
|
603
|
+
);
|
|
604
|
+
}
|
|
605
|
+
if (this._lease) {
|
|
606
|
+
return isResilientPool(this._pool) ? this._pool.__neetruGetInner() : this._pool;
|
|
607
|
+
}
|
|
608
|
+
if (this._initInFlight !== null) return this._initInFlight;
|
|
609
|
+
const run = (async () => {
|
|
610
|
+
let lease;
|
|
611
|
+
try {
|
|
612
|
+
lease = this._deps.database !== void 0 ? await this._deps.fetchLease({ database: this._deps.database }) : await this._deps.fetchLease();
|
|
613
|
+
} catch (err) {
|
|
614
|
+
if (err instanceof exports.NeetruDbError) throw err;
|
|
615
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
616
|
+
throw new exports.NeetruDbError("db_unavailable", `Falha ao obter lease inicial: ${msg}`);
|
|
617
|
+
}
|
|
618
|
+
let realPool;
|
|
619
|
+
try {
|
|
620
|
+
realPool = this._deps.createPool(lease);
|
|
621
|
+
} catch (err) {
|
|
622
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
623
|
+
throw new exports.NeetruDbError("db_unavailable", `Falha ao abrir pool: ${msg}`);
|
|
624
|
+
}
|
|
625
|
+
if (isResilientPool(this._pool)) {
|
|
626
|
+
const old = this._pool.__neetruSwapInner(realPool);
|
|
627
|
+
void old.end().catch(() => {
|
|
628
|
+
});
|
|
629
|
+
} else {
|
|
630
|
+
this._pool = realPool;
|
|
631
|
+
}
|
|
632
|
+
this._lease = lease;
|
|
633
|
+
this._scheduleRenewal();
|
|
634
|
+
return realPool;
|
|
635
|
+
})();
|
|
636
|
+
this._initInFlight = run;
|
|
637
|
+
try {
|
|
638
|
+
return await run;
|
|
639
|
+
} catch (err) {
|
|
640
|
+
this._initInFlight = null;
|
|
641
|
+
throw err;
|
|
642
|
+
}
|
|
643
|
+
}
|
|
410
644
|
/**
|
|
411
645
|
* Hot-swap do pool: cria pool novo, atualiza ponteiros, drena pool antigo.
|
|
412
646
|
*
|
|
@@ -418,6 +652,13 @@ var init_lease = __esm({
|
|
|
418
652
|
return;
|
|
419
653
|
}
|
|
420
654
|
const newPool = this._deps.createPool(newLease);
|
|
655
|
+
if (isResilientPool(this._pool)) {
|
|
656
|
+
const oldInner = this._pool.__neetruSwapInner(newPool);
|
|
657
|
+
this._lease = newLease;
|
|
658
|
+
void oldInner.end().catch(() => {
|
|
659
|
+
});
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
421
662
|
const newOrm = this._deps.createDrizzle(newPool, this._deps.schema);
|
|
422
663
|
const oldPool = this._pool;
|
|
423
664
|
this._lease = newLease;
|
|
@@ -483,10 +724,19 @@ function createHttpLeaseFetcher(config) {
|
|
|
483
724
|
retries: 2
|
|
484
725
|
});
|
|
485
726
|
} catch (err) {
|
|
727
|
+
const verb = isRenewal ? "renovar" : "obter";
|
|
486
728
|
const msg = err instanceof Error ? err.message : String(err);
|
|
729
|
+
const reason = err instanceof NeetruError ? err.reason : void 0;
|
|
730
|
+
if (reason === "suspended" || reason === "archived" || reason === "revoked") {
|
|
731
|
+
const human = reason === "suspended" ? "ambiente suspenso \u2014 a m\xE1quina que o servia parou; ser\xE1 religado quando o ambiente voltar" : reason === "archived" ? "ambiente arquivado \u2014 este banco foi arquivado e n\xE3o aceita mais conex\xF5es" : "lease revogado \u2014 as credenciais deste ambiente foram revogadas pelo Core";
|
|
732
|
+
throw new exports.NeetruDbError(
|
|
733
|
+
"db_suspended",
|
|
734
|
+
`Falha ao ${verb} lease SQL: ${human} (reason=${reason}).`
|
|
735
|
+
);
|
|
736
|
+
}
|
|
487
737
|
throw new exports.NeetruDbError(
|
|
488
738
|
"db_unavailable",
|
|
489
|
-
`Falha ao ${
|
|
739
|
+
`Falha ao ${verb} lease SQL: ${msg}`
|
|
490
740
|
);
|
|
491
741
|
}
|
|
492
742
|
return parseLease(raw);
|
|
@@ -558,8 +808,64 @@ async function createDrizzleHandle(pool, schema) {
|
|
|
558
808
|
);
|
|
559
809
|
return drizzle(pool, { schema });
|
|
560
810
|
}
|
|
811
|
+
function createDeferredPool(poolPrototype, ensureRealPool) {
|
|
812
|
+
let real = null;
|
|
813
|
+
let inflight = null;
|
|
814
|
+
const getReal = () => {
|
|
815
|
+
if (real) return Promise.resolve(real);
|
|
816
|
+
if (!inflight) {
|
|
817
|
+
inflight = ensureRealPool().then((p) => {
|
|
818
|
+
real = p;
|
|
819
|
+
inflight = null;
|
|
820
|
+
return p;
|
|
821
|
+
}).catch((e) => {
|
|
822
|
+
inflight = null;
|
|
823
|
+
throw e;
|
|
824
|
+
});
|
|
825
|
+
}
|
|
826
|
+
return inflight;
|
|
827
|
+
};
|
|
828
|
+
const deferred = {
|
|
829
|
+
async query(...args) {
|
|
830
|
+
const p = await getReal();
|
|
831
|
+
return p.query(...args);
|
|
832
|
+
},
|
|
833
|
+
async connect(...args) {
|
|
834
|
+
const p = await getReal();
|
|
835
|
+
return p.connect(...args);
|
|
836
|
+
},
|
|
837
|
+
async end() {
|
|
838
|
+
if (real) await real.end();
|
|
839
|
+
}
|
|
840
|
+
};
|
|
841
|
+
if (poolPrototype) {
|
|
842
|
+
Object.setPrototypeOf(deferred, poolPrototype);
|
|
843
|
+
}
|
|
844
|
+
return deferred;
|
|
845
|
+
}
|
|
561
846
|
async function createSqlClientWithDeps(deps, options) {
|
|
562
847
|
const depsWithDb = options?.database !== void 0 ? { ...deps, database: options.database } : deps;
|
|
848
|
+
if (depsWithDb.lazy) {
|
|
849
|
+
let managerRef = null;
|
|
850
|
+
const deferred = createDeferredPool(depsWithDb.poolPrototype ?? null, () => {
|
|
851
|
+
if (!managerRef) {
|
|
852
|
+
throw new exports.NeetruDbError("db_unavailable", "SQL client n\xE3o inicializado (manager ausente).");
|
|
853
|
+
}
|
|
854
|
+
return managerRef._ensureInitialLease();
|
|
855
|
+
});
|
|
856
|
+
const pool2 = createResilientPool(deferred);
|
|
857
|
+
let orm2;
|
|
858
|
+
try {
|
|
859
|
+
orm2 = depsWithDb.createDrizzle(pool2, depsWithDb.schema);
|
|
860
|
+
} catch (err) {
|
|
861
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
862
|
+
throw new exports.NeetruDbError("db_unavailable", `Falha ao criar handle Drizzle: ${msg}`);
|
|
863
|
+
}
|
|
864
|
+
const manager2 = new SqlLeaseManager(null, pool2, orm2, depsWithDb);
|
|
865
|
+
managerRef = manager2;
|
|
866
|
+
pool2.__neetruSetReLease(() => manager2._reactiveReLease());
|
|
867
|
+
return manager2;
|
|
868
|
+
}
|
|
563
869
|
let lease;
|
|
564
870
|
try {
|
|
565
871
|
if (options?.database !== void 0) {
|
|
@@ -572,23 +878,26 @@ async function createSqlClientWithDeps(deps, options) {
|
|
|
572
878
|
const msg = err instanceof Error ? err.message : String(err);
|
|
573
879
|
throw new exports.NeetruDbError("db_unavailable", `Falha ao obter lease inicial: ${msg}`);
|
|
574
880
|
}
|
|
575
|
-
let
|
|
881
|
+
let innerPool;
|
|
576
882
|
try {
|
|
577
|
-
|
|
883
|
+
innerPool = depsWithDb.createPool(lease);
|
|
578
884
|
} catch (err) {
|
|
579
885
|
const msg = err instanceof Error ? err.message : String(err);
|
|
580
886
|
throw new exports.NeetruDbError("db_unavailable", `Falha ao abrir pool: ${msg}`);
|
|
581
887
|
}
|
|
888
|
+
const pool = createResilientPool(innerPool);
|
|
582
889
|
let orm;
|
|
583
890
|
try {
|
|
584
891
|
orm = depsWithDb.createDrizzle(pool, depsWithDb.schema);
|
|
585
892
|
} catch (err) {
|
|
586
|
-
await
|
|
893
|
+
await innerPool.end().catch(() => {
|
|
587
894
|
});
|
|
588
895
|
const msg = err instanceof Error ? err.message : String(err);
|
|
589
896
|
throw new exports.NeetruDbError("db_unavailable", `Falha ao criar handle Drizzle: ${msg}`);
|
|
590
897
|
}
|
|
591
|
-
|
|
898
|
+
const manager = new SqlLeaseManager(lease, pool, orm, depsWithDb);
|
|
899
|
+
pool.__neetruSetReLease(() => manager._reactiveReLease());
|
|
900
|
+
return manager;
|
|
592
901
|
}
|
|
593
902
|
async function createSqlClientFromConfig(config, schema, options) {
|
|
594
903
|
const fetchLease = createHttpLeaseFetcher(config);
|
|
@@ -609,8 +918,8 @@ async function createSqlClientFromConfig(config, schema, options) {
|
|
|
609
918
|
`[client.db.sql] requer pg + drizzle-orm como peerDependencies do servidor. Instale: npm install pg drizzle-orm. (${err.message})`
|
|
610
919
|
);
|
|
611
920
|
}
|
|
921
|
+
const Pool = pgMod.Pool ?? pgMod.default?.Pool;
|
|
612
922
|
const createPool = (lease) => {
|
|
613
|
-
const Pool = pgMod.Pool ?? pgMod.default?.Pool;
|
|
614
923
|
const ssl = lease.sslca ? {
|
|
615
924
|
ca: lease.sslca,
|
|
616
925
|
cert: lease.clientCert ?? void 0,
|
|
@@ -637,7 +946,14 @@ async function createSqlClientFromConfig(config, schema, options) {
|
|
|
637
946
|
fetchLease,
|
|
638
947
|
createPool,
|
|
639
948
|
createDrizzle,
|
|
640
|
-
schema
|
|
949
|
+
schema,
|
|
950
|
+
// Lease inicial LAZY (motor de ambientes suspenso): `client.db.sql()` não
|
|
951
|
+
// pode explodir o `import` do módulo do produto quando o ambiente está
|
|
952
|
+
// suspenso/arquivado no boot. O 1º lease é adiado pra 1ª query.
|
|
953
|
+
lazy: true,
|
|
954
|
+
// Placeholder pré-1ª-query herda `pg.Pool.prototype` pra `instanceof Pool`
|
|
955
|
+
// valer no wrapper resiliente (transações Drizzle PASS-THROUGH seguras).
|
|
956
|
+
poolPrototype: Pool?.prototype ?? null
|
|
641
957
|
},
|
|
642
958
|
options
|
|
643
959
|
);
|
|
@@ -646,7 +962,9 @@ var LEASE_ENDPOINT, LEASE_RENEW_ENDPOINT;
|
|
|
646
962
|
var init_sql_client = __esm({
|
|
647
963
|
"src/db/sql/sql-client.ts"() {
|
|
648
964
|
init_db_errors();
|
|
965
|
+
init_errors();
|
|
649
966
|
init_lease();
|
|
967
|
+
init_resilient_pool();
|
|
650
968
|
LEASE_ENDPOINT = "/api/sdk/v1/db/lease";
|
|
651
969
|
LEASE_RENEW_ENDPOINT = "/api/sdk/v1/db/lease/renew";
|
|
652
970
|
}
|