@palbase/backend 22.1.0 → 23.1.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/dist/bin/palbase-backend.cjs +750 -40
- package/dist/bin/palbase-backend.cjs.map +1 -1
- package/dist/bin/palbase-backend.js +5 -5
- package/dist/{chunk-YL4C5NRY.js → chunk-HQRJDARQ.js} +2 -2
- package/dist/{chunk-74XDEF5J.js → chunk-M5MCBWJI.js} +723 -37
- package/dist/chunk-M5MCBWJI.js.map +1 -0
- package/dist/{chunk-W5ODXPY3.js → chunk-NS5V43YQ.js} +14 -1
- package/dist/chunk-NS5V43YQ.js.map +1 -0
- package/dist/{chunk-SQC5EIWY.js → chunk-OHALWEOG.js} +19 -9
- package/dist/chunk-OHALWEOG.js.map +1 -0
- package/dist/{chunk-I3ON7MYF.js → chunk-PY7YJDCT.js} +129 -18
- package/dist/chunk-PY7YJDCT.js.map +1 -0
- package/dist/{chunk-N32VDWKH.js → chunk-R3KN6RHD.js} +4 -59
- package/dist/chunk-R3KN6RHD.js.map +1 -0
- package/dist/{chunk-QMVK4X3V.js → chunk-RCLNBJCM.js} +98 -98
- package/dist/chunk-RCLNBJCM.js.map +1 -0
- package/dist/db/env.cjs.map +1 -1
- package/dist/db/env.d.cts +3 -21
- package/dist/db/env.d.ts +3 -21
- package/dist/db/index.cjs +140 -16
- package/dist/db/index.cjs.map +1 -1
- package/dist/db/index.d.cts +3 -2
- package/dist/db/index.d.ts +3 -2
- package/dist/db/index.js +2 -2
- package/dist/{endpoint-BVT6jcVW.d.cts → endpoint-CVWXh6oG.d.ts} +147 -15
- package/dist/{endpoint-BVT6jcVW.d.ts → endpoint-c9h5jriX.d.cts} +147 -15
- package/dist/engine/index.cjs +750 -40
- package/dist/engine/index.cjs.map +1 -1
- package/dist/engine/index.d.cts +6 -5
- package/dist/engine/index.d.ts +6 -5
- package/dist/engine/index.js +4 -4
- package/dist/{index-BS1gW4nV.d.cts → index-BZrJXnVh.d.ts} +142 -72
- package/dist/{index-BqCiHao8.d.cts → index-By8Dle5U.d.cts} +196 -21
- package/dist/{index-vwHoS0l2.d.ts → index-CwAJ7HEe.d.ts} +196 -21
- package/dist/{index-CCZqzych.d.ts → index-CxeQSfJP.d.cts} +142 -72
- package/dist/index.cjs +388 -1101
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +89 -1134
- package/dist/index.d.ts +89 -1134
- package/dist/index.js +133 -902
- package/dist/index.js.map +1 -1
- package/dist/openapi/index.cjs +32 -61
- package/dist/openapi/index.cjs.map +1 -1
- package/dist/openapi/index.d.cts +6 -2
- package/dist/openapi/index.d.ts +6 -2
- package/dist/openapi/index.js +34 -26
- package/dist/openapi/index.js.map +1 -1
- package/dist/{registry-Bsuf-orT.d.ts → registry-B3niOVYp.d.ts} +108 -170
- package/dist/{registry-BWttGlaT.d.cts → registry-CqPK2Qby.d.cts} +108 -170
- package/dist/{purchases/keys.cjs → stack.cjs} +4 -4
- package/dist/stack.cjs.map +1 -0
- package/dist/stack.d.cts +76 -0
- package/dist/stack.d.ts +76 -0
- package/dist/stack.js +1 -0
- package/dist/test/index.cjs +482 -9
- package/dist/test/index.cjs.map +1 -1
- package/dist/test/index.d.cts +35 -3
- package/dist/test/index.d.ts +35 -3
- package/dist/test/index.js +480 -8
- package/dist/test/index.js.map +1 -1
- package/docs/README.md +7 -6
- package/docs/llms-full.txt +7 -260
- package/docs/llms.txt +0 -2
- package/package.json +9 -8
- package/stager/return_types.js +23 -0
- package/template/package.json +1 -1
- package/dist/chunk-74XDEF5J.js.map +0 -1
- package/dist/chunk-I3ON7MYF.js.map +0 -1
- package/dist/chunk-N32VDWKH.js.map +0 -1
- package/dist/chunk-QMVK4X3V.js.map +0 -1
- package/dist/chunk-SQC5EIWY.js.map +0 -1
- package/dist/chunk-W5ODXPY3.js.map +0 -1
- package/dist/purchases/keys.cjs.map +0 -1
- package/dist/purchases/keys.d.cts +0 -42
- package/dist/purchases/keys.d.ts +0 -42
- package/dist/purchases/keys.js +0 -1
- package/docs/config.md +0 -147
- package/docs/resources.md +0 -97
- package/template/config/secrets.ts +0 -24
- /package/dist/{chunk-YL4C5NRY.js.map → chunk-HQRJDARQ.js.map} +0 -0
- /package/dist/{purchases/keys.js.map → stack.js.map} +0 -0
package/dist/engine/index.d.cts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
export { A as App,
|
|
2
|
-
import '../endpoint-
|
|
3
|
-
import '../
|
|
1
|
+
export { A as App, h as AuthVerifier, B as BootRefused, i as CreateAppOptions, E as EgressPolicy, j as EngineConfig, M as ModuleClients, k as RateLimiter, l as RequestDatabase, m as RouteEntry, n as RuntimeHooks, o as ScrubResult, p as SqlDriver, q as SqlTx, r as buildRouteTable, s as createApp, t as createLazyTransaction, u as createOps, v as createRequestDatabase, w as effectiveAuth, x as hostAllowed, y as installEgressFence, z as loadConfig, G as makeMemoryCache, H as matchRoute, I as quoteIdent, J as scrubSecrets, K as withTables } from '../index-CxeQSfJP.cjs';
|
|
2
|
+
import '../endpoint-c9h5jriX.cjs';
|
|
3
|
+
import '../stack.cjs';
|
|
4
4
|
import 'node:async_hooks';
|
|
5
|
-
import '../index-
|
|
6
|
-
import '../
|
|
5
|
+
import '../index-By8Dle5U.cjs';
|
|
6
|
+
import '../db/env.cjs';
|
|
7
|
+
import '../registry-CqPK2Qby.cjs';
|
|
7
8
|
import 'zod';
|
package/dist/engine/index.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
export { A as App,
|
|
2
|
-
import '../endpoint-
|
|
3
|
-
import '../
|
|
1
|
+
export { A as App, h as AuthVerifier, B as BootRefused, i as CreateAppOptions, E as EgressPolicy, j as EngineConfig, M as ModuleClients, k as RateLimiter, l as RequestDatabase, m as RouteEntry, n as RuntimeHooks, o as ScrubResult, p as SqlDriver, q as SqlTx, r as buildRouteTable, s as createApp, t as createLazyTransaction, u as createOps, v as createRequestDatabase, w as effectiveAuth, x as hostAllowed, y as installEgressFence, z as loadConfig, G as makeMemoryCache, H as matchRoute, I as quoteIdent, J as scrubSecrets, K as withTables } from '../index-BZrJXnVh.js';
|
|
2
|
+
import '../endpoint-CVWXh6oG.js';
|
|
3
|
+
import '../stack.js';
|
|
4
4
|
import 'node:async_hooks';
|
|
5
|
-
import '../index-
|
|
6
|
-
import '../
|
|
5
|
+
import '../index-CwAJ7HEe.js';
|
|
6
|
+
import '../db/env.js';
|
|
7
|
+
import '../registry-B3niOVYp.js';
|
|
7
8
|
import 'zod';
|
package/dist/engine/index.js
CHANGED
|
@@ -16,10 +16,10 @@ import {
|
|
|
16
16
|
quoteIdent,
|
|
17
17
|
scrubSecrets,
|
|
18
18
|
withTables
|
|
19
|
-
} from "../chunk-
|
|
20
|
-
import "../chunk-
|
|
21
|
-
import "../chunk-
|
|
22
|
-
import "../chunk-
|
|
19
|
+
} from "../chunk-M5MCBWJI.js";
|
|
20
|
+
import "../chunk-OHALWEOG.js";
|
|
21
|
+
import "../chunk-NS5V43YQ.js";
|
|
22
|
+
import "../chunk-RCLNBJCM.js";
|
|
23
23
|
import "../chunk-7D4SUZUM.js";
|
|
24
24
|
export {
|
|
25
25
|
AuthVerifier,
|
|
@@ -1,63 +1,8 @@
|
|
|
1
|
-
import { Buckets, BucketTypes } from './
|
|
1
|
+
import { Buckets, BucketTypes } from './stack.js';
|
|
2
2
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
3
|
-
import { C as CacheClient, P as PalbaseDocsClient, a as PalbaseFlagsClient, L as Logger, b as PalbaseNotificationsClient, c as PalbaseRealtimeClient, D as DBClient, S as SecretsService, d as PalbaseStorageClient, e as PalbaseBucketClient, T as TxPlanBody,
|
|
4
|
-
import { E as EnvTypedDatabase } from './index-
|
|
5
|
-
import { R as RouteMeta } from './registry-
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* The purchases surface the decorators need, declared as a NARROW STRUCTURAL
|
|
9
|
-
* interface rather than an import of `@palstore/purchases`.
|
|
10
|
-
*
|
|
11
|
-
* `PurchasesClient` (palstore's `sk_` backend SDK) satisfies this shape as-is,
|
|
12
|
-
* so the runtime injects the real client with no adapter — but `@palbase/backend`
|
|
13
|
-
* itself gains no dependency on it. That matters: this package is published
|
|
14
|
-
* public and baked into the br-pod image from a tarball, so a dependency on an
|
|
15
|
-
* unpublished sibling would break `npm install palbase-backend.tgz`. It is also
|
|
16
|
-
* the pattern `withSpend` already uses for the same reason (`SpendCapableClient`
|
|
17
|
-
* in palstore's own spend.ts: "a narrow structural interface … so this file has
|
|
18
|
-
* no dependency on client.ts").
|
|
19
|
-
*
|
|
20
|
-
* The methods here are a SUBSET of `PurchasesClient` — only what the two
|
|
21
|
-
* decorators call. Grants, refunds, credits and customer-info reads stay off
|
|
22
|
-
* this interface: a tenant that wants them imports the palstore SDK directly.
|
|
23
|
-
*/
|
|
24
|
-
/** Store environment a subject is fixed to. Mirrors `StoreEnv` in `@palstore/purchases`. */
|
|
25
|
-
type StoreEnv = "production" | "sandbox";
|
|
26
|
-
/** Quota/credit state carried by a 429. Mirrors `LimitState` in `@palstore/purchases`
|
|
27
|
-
* (SPEC-purchases-v1 §11) — re-declared, not imported, for the reason above. */
|
|
28
|
-
interface LimitState {
|
|
29
|
-
key: string;
|
|
30
|
-
scope: string;
|
|
31
|
-
window: string;
|
|
32
|
-
used: number;
|
|
33
|
-
reserved: number;
|
|
34
|
-
max: number;
|
|
35
|
-
remaining: number;
|
|
36
|
-
resetAt: string;
|
|
37
|
-
}
|
|
38
|
-
/** Options for one spend. `idempotencyKey` is required by the server (§9). */
|
|
39
|
-
interface SpendOptions {
|
|
40
|
-
/** Defaults to 1. */
|
|
41
|
-
count?: number;
|
|
42
|
-
idempotencyKey: string;
|
|
43
|
-
}
|
|
44
|
-
interface PurchasesService {
|
|
45
|
-
/** Map a tenant-side user reference to its palstore subject, creating one on
|
|
46
|
-
* first sight. Server-authoritative — the caller never names a subject. */
|
|
47
|
-
resolveSubject(input: {
|
|
48
|
-
userRef: string;
|
|
49
|
-
storeEnv: StoreEnv;
|
|
50
|
-
}): Promise<{
|
|
51
|
-
subjectId: string;
|
|
52
|
-
}>;
|
|
53
|
-
/** Resolve silently when `entitlementKey` is active for `subjectId`; throw
|
|
54
|
-
* `EntitlementRequiredError` otherwise. Consumes nothing. */
|
|
55
|
-
require(subjectId: string, entitlementKey: string): Promise<void>;
|
|
56
|
-
/** Reserve → run `handler` → commit on success, cancel on throw, always
|
|
57
|
-
* rethrowing the handler's own error. The whole reason the decorators are a
|
|
58
|
-
* thin layer: this lifecycle is already written and tested in palstore's SDK. */
|
|
59
|
-
withSpend<T>(subjectId: string, key: string, opts: SpendOptions, handler: () => Promise<T>): Promise<T>;
|
|
60
|
-
}
|
|
3
|
+
import { C as CacheClient, P as PalbaseDocsClient, a as PalbaseFlagsClient, L as Logger, b as PalbaseNotificationsClient, c as PalbaseRealtimeClient, D as DBClient, S as SecretsService, d as PalbaseStorageClient, e as PalbaseBucketClient, f as DBOps, T as TxPlanBody, g as TxPlanResponse } from './endpoint-CVWXh6oG.js';
|
|
4
|
+
import { E as EnvTypedDatabase } from './index-CwAJ7HEe.js';
|
|
5
|
+
import { R as RouteMeta } from './registry-B3niOVYp.js';
|
|
61
6
|
|
|
62
7
|
/**
|
|
63
8
|
* runtime.ts — request-scoped service singletons.
|
|
@@ -118,7 +63,6 @@ interface RuntimeServices {
|
|
|
118
63
|
Notifications: PalbaseNotificationsClient;
|
|
119
64
|
Flags: PalbaseFlagsClient;
|
|
120
65
|
Realtime: PalbaseRealtimeClient;
|
|
121
|
-
Purchases: PurchasesService;
|
|
122
66
|
}
|
|
123
67
|
/**
|
|
124
68
|
* The per-request ALS box.
|
|
@@ -239,15 +183,6 @@ declare const Secrets: SecretsService;
|
|
|
239
183
|
declare const Log: Logger;
|
|
240
184
|
/** Push / email / SMS / in-app notifications. */
|
|
241
185
|
declare const Notifications: PalbaseNotificationsClient;
|
|
242
|
-
/**
|
|
243
|
-
* Palstore purchases (entitlements + quota/credit spend).
|
|
244
|
-
*
|
|
245
|
-
* Reached by handlers through the `@RequireEntitlement` / `@Spend` decorators
|
|
246
|
-
* rather than called directly in the common case; exposed as a singleton for
|
|
247
|
-
* the cases the decorators deliberately do not cover (a dynamic spend count,
|
|
248
|
-
* which must run BEFORE the billable side-effect).
|
|
249
|
-
*/
|
|
250
|
-
declare const Purchases: PurchasesService;
|
|
251
186
|
/**
|
|
252
187
|
* Feature flags.
|
|
253
188
|
*
|
|
@@ -426,14 +361,41 @@ declare function createLazyTransaction(sql: SqlDriver, role: string, claimsJson:
|
|
|
426
361
|
type LazyTransaction = ReturnType<typeof createLazyTransaction>;
|
|
427
362
|
/** Either a live driver transaction or the lazy holder above. */
|
|
428
363
|
type TxLike = SqlTx | LazyTransaction;
|
|
364
|
+
/** What `findMany` accepts beside its filter: an ordering and a row ceiling.
|
|
365
|
+
* Both used to require dropping to raw SQL, and the docs said so — which is how
|
|
366
|
+
* a tenant's controllers filled up with hand-written SELECTs. */
|
|
367
|
+
interface FindManyOptions {
|
|
368
|
+
orderBy?: {
|
|
369
|
+
column: string;
|
|
370
|
+
direction?: "asc" | "desc";
|
|
371
|
+
};
|
|
372
|
+
limit?: number;
|
|
373
|
+
}
|
|
429
374
|
/** The six string-keyed operations, plus an interactive `transaction`. */
|
|
430
375
|
declare function createOps(tx: TxLike): {
|
|
431
376
|
query(sql: string, params?: unknown[]): Promise<Row[]>;
|
|
432
377
|
insert(table: string, data: Row): Promise<Row>;
|
|
378
|
+
/**
|
|
379
|
+
* INSERT the row, or UPDATE it when it collides on `onConflict`.
|
|
380
|
+
*
|
|
381
|
+
* WHY IT IS AN OPERATION rather than a recipe. "Try the insert, catch the
|
|
382
|
+
* unique violation, update instead" does not work here: a request runs in ONE
|
|
383
|
+
* Postgres transaction, so the failed insert aborts it and every later
|
|
384
|
+
* statement answers `current transaction is aborted`. A tenant measured that
|
|
385
|
+
* as 7 of 8 concurrent requests returning 500, gave up on upsert, and had a
|
|
386
|
+
* trigger create the row instead — a workaround that needs a new trigger for
|
|
387
|
+
* every table with a unique row.
|
|
388
|
+
*
|
|
389
|
+
* The conflict columns are excluded from the SET list: they are what MATCHED,
|
|
390
|
+
* so writing them back is at best a no-op and at worst a surprise.
|
|
391
|
+
*/
|
|
392
|
+
upsert(table: string, data: Row, opts: {
|
|
393
|
+
onConflict: readonly string[];
|
|
394
|
+
}): Promise<Row>;
|
|
433
395
|
update(table: string, id: string, data: Row): Promise<Row | null>;
|
|
434
396
|
delete(table: string, id: string): Promise<void>;
|
|
435
397
|
findById(table: string, id: string): Promise<Row | null>;
|
|
436
|
-
findMany(table: string, query?: Row): Promise<Row[]>;
|
|
398
|
+
findMany(table: string, query?: Row, opts?: FindManyOptions): Promise<Row[]>;
|
|
437
399
|
/**
|
|
438
400
|
* Tek-SQL hibrit arama (FR-014): iki kol CTE + FULL OUTER JOIN + RRF
|
|
439
401
|
* (1/(50+rank), CLAIM-N4). Operatör şema-nitelikli (C-10, M-1); GUC
|
|
@@ -448,9 +410,117 @@ declare function createOps(tx: TxLike): {
|
|
|
448
410
|
limit?: number;
|
|
449
411
|
using?: string;
|
|
450
412
|
mode?: "hybrid" | "text" | "vector";
|
|
413
|
+
/** Nihai (RRF-sonrası) skor alt eşiği — süzme LIMIT'ten ÖNCE (FR-001). */
|
|
414
|
+
minScore?: number;
|
|
415
|
+
/** RRF-sonrası üstel tazelik çürümesi: _score * exp(-ln(2)*yaş/halfLife) (FR-004). */
|
|
416
|
+
recency?: {
|
|
417
|
+
field: string;
|
|
418
|
+
halfLife: string;
|
|
419
|
+
};
|
|
420
|
+
/** Chunk-modunda satır başına dönen en iyi blok sayısı (1..10, vars. 3; FR-015). */
|
|
421
|
+
blocksPerRow?: number;
|
|
422
|
+
/** Filtrelenmiş küme üzerinde kolon başına top-20 değer sayacı —
|
|
423
|
+
* dönüş dizisinin `_facets` özelliği (FR-027). */
|
|
424
|
+
facets?: string[];
|
|
425
|
+
/** Satır-modunda FTS eşleşme vurgusu: ts_headline ile `_highlight`
|
|
426
|
+
* alanı; chunk-modda no-op — bloklar zaten eşleşen kesittir (FR-025). */
|
|
427
|
+
highlight?: boolean;
|
|
428
|
+
/** Validity'li tabloda zaman penceresi: varsayılan yalnız güncel;
|
|
429
|
+
* "all" tüm versiyonlar; {asOf} o andaki geçerli versiyon (FR-029). */
|
|
430
|
+
validity?: "all" | {
|
|
431
|
+
asOf: string;
|
|
432
|
+
};
|
|
433
|
+
/** Alan-boost (FR-030): skor * (1 + w·x/(1+x)) — sınırlı, dış servissiz. */
|
|
434
|
+
boost?: {
|
|
435
|
+
field: string;
|
|
436
|
+
weight: number;
|
|
437
|
+
};
|
|
438
|
+
}): Promise<Row[]>;
|
|
439
|
+
/**
|
|
440
|
+
* similar (T018, FR-022): "bu satıra benzeyenler". Hedef vektör DB'den
|
|
441
|
+
* okunur — satır-modu satırın kendi kolonu, chunk-modu parent chunk'larının
|
|
442
|
+
* şema-nitelikli avg'ı — ve ana arama search'ün vector yoluyla koşar;
|
|
443
|
+
* kaynak satır sonuçtan düşer. İKİ tur BİLİNÇLİ (plan T018): target-CTE'li
|
|
444
|
+
* tek SQL'de "id yok" ile "0 komşu" ayrılamazdı; +1 küçük turla id-yokluğu
|
|
445
|
+
* adlandırılmış hataya çevrilir. Sonuç şekli search ile aynı (FR-015).
|
|
446
|
+
*/
|
|
447
|
+
similar(table: string, id: string, opts?: {
|
|
448
|
+
where?: Record<string, unknown>;
|
|
449
|
+
limit?: number;
|
|
450
|
+
using?: string;
|
|
451
|
+
minScore?: number;
|
|
452
|
+
recency?: {
|
|
453
|
+
field: string;
|
|
454
|
+
halfLife: string;
|
|
455
|
+
};
|
|
456
|
+
blocksPerRow?: number;
|
|
457
|
+
validity?: "all" | {
|
|
458
|
+
asOf: string;
|
|
459
|
+
};
|
|
460
|
+
boost?: {
|
|
461
|
+
field: string;
|
|
462
|
+
weight: number;
|
|
463
|
+
};
|
|
451
464
|
}): Promise<Row[]>;
|
|
465
|
+
/**
|
|
466
|
+
* recommend (T018, FR-023): positive/negative id kümelerinden öneri.
|
|
467
|
+
* Hedef vektör DB-İÇİ CTE'lerle türer: pos = avg(embedding of positives),
|
|
468
|
+
* negative varsa hedef = pos.v + (pos.v - neg.v) — pgvector avg agregası
|
|
469
|
+
* ve +/- operatörleri ŞEMA-NİTELİKLİ (M-1: search_path'e güvenilmez).
|
|
470
|
+
* Vektör matematiği istemciye inmez; bulunamayan positive/negative
|
|
471
|
+
* adlandırılmış hatadır ve kaynak id'ler sonuçtan düşer.
|
|
472
|
+
*/
|
|
473
|
+
recommend(table: string, opts: {
|
|
474
|
+
positive: unknown[];
|
|
475
|
+
negative?: unknown[];
|
|
476
|
+
where?: Record<string, unknown>;
|
|
477
|
+
limit?: number;
|
|
478
|
+
using?: string;
|
|
479
|
+
minScore?: number;
|
|
480
|
+
recency?: {
|
|
481
|
+
field: string;
|
|
482
|
+
halfLife: string;
|
|
483
|
+
};
|
|
484
|
+
blocksPerRow?: number;
|
|
485
|
+
validity?: "all" | {
|
|
486
|
+
asOf: string;
|
|
487
|
+
};
|
|
488
|
+
boost?: {
|
|
489
|
+
field: string;
|
|
490
|
+
weight: number;
|
|
491
|
+
};
|
|
492
|
+
}): Promise<Row[]>;
|
|
493
|
+
/**
|
|
494
|
+
* supersede (T021, FR-029, C-9): validity'li tabloda satırın YENİ
|
|
495
|
+
* versiyonunu TEK savepoint'te yazar — eski satır kapatılır
|
|
496
|
+
* (valid_to = now(), superseded_by = yeni pk), yeni satır eklenir, dönüş
|
|
497
|
+
* yeni satırdır. Yeni pk CLIENT'ta üretilir (row'da verilmemişse
|
|
498
|
+
* randomUUID — declared şemaların uuid().defaultRandom() standardı) ki
|
|
499
|
+
* kapatma UPDATE'i INSERT'ten ÖNCE koşabilsin: 0 satır = zaten superseded
|
|
500
|
+
* ya da yok → INSERT hiç denenmez; INSERT hatası ise savepoint'le
|
|
501
|
+
* kapatmayı da geri sarar — yarım supersede diye bir durum yoktur.
|
|
502
|
+
*/
|
|
503
|
+
supersede(table: string, id: string, row: Row): Promise<Row>;
|
|
452
504
|
/** A real SAVEPOINT inside the request's transaction. */
|
|
453
505
|
transaction<T>(cb: (t: unknown) => Promise<T>): Promise<T>;
|
|
506
|
+
/**
|
|
507
|
+
* Run `fn` against a handle bound to a SAVEPOINT, so a failure inside it
|
|
508
|
+
* rolls back only what that handle wrote and the request can keep writing.
|
|
509
|
+
*
|
|
510
|
+
* WHY THE HANDLE IS AN ARGUMENT. The obvious shape — `attempt(async () => {
|
|
511
|
+
* ... Database.insert(...) ... })`, with no parameter — would have to point
|
|
512
|
+
* the ambient `Database` at the savepoint for the duration, and a request is
|
|
513
|
+
* concurrent with itself: `Promise.all([Database.insert(a),
|
|
514
|
+
* Database.attempt(...)])` would put `a` inside the savepoint and roll it
|
|
515
|
+
* back with it. Silent data loss, and the same interleaving this file already
|
|
516
|
+
* refuses for `asService()`. Passing the handle makes the boundary something
|
|
517
|
+
* you can see in the code that crosses it.
|
|
518
|
+
*
|
|
519
|
+
* Postgres, not us: the savepoint is released on success and rolled back on
|
|
520
|
+
* failure by the driver, so an aborted statement inside `fn` does not poison
|
|
521
|
+
* the surrounding transaction.
|
|
522
|
+
*/
|
|
523
|
+
attempt<T>(fn: (tx: DBOps) => Promise<T>): Promise<T>;
|
|
454
524
|
/**
|
|
455
525
|
* Execute a whole transaction plan — what `Database.transaction(fn)` builds.
|
|
456
526
|
*
|
|
@@ -800,7 +870,7 @@ interface RuntimeHooks {
|
|
|
800
870
|
__requestALS: typeof __requestALS;
|
|
801
871
|
}
|
|
802
872
|
/** The module singletons the engine injects, minus the two it owns itself. */
|
|
803
|
-
type ModuleClients = Partial<Pick<RuntimeServices, "Documents" | "Storage" | "Notifications" | "Flags" | "Realtime" | "
|
|
873
|
+
type ModuleClients = Partial<Pick<RuntimeServices, "Documents" | "Storage" | "Notifications" | "Flags" | "Realtime" | "Secrets">>;
|
|
804
874
|
interface CreateAppOptions {
|
|
805
875
|
/**
|
|
806
876
|
* Vault'tan TEK secret okuma (FR-025): sorgu-anı embedding'in anahtarı
|
|
@@ -854,4 +924,4 @@ interface App {
|
|
|
854
924
|
*/
|
|
855
925
|
declare function createApp(opts: CreateAppOptions): Promise<App>;
|
|
856
926
|
|
|
857
|
-
export { type App as A, BootRefused as B, Cache as C, Database as D, type EgressPolicy as E, Flags as F,
|
|
927
|
+
export { type App as A, BootRefused as B, Cache as C, Database as D, type EgressPolicy as E, Flags as F, makeMemoryCache as G, matchRoute as H, quoteIdent as I, scrubSecrets as J, withTables as K, Log as L, type ModuleClients as M, Notifications as N, Realtime as R, Secrets as S, __getRuntime as _, Documents as a, type RequestStore as b, type RuntimeServices as c, Storage as d, __requestALS as e, __runWithRuntime as f, __setRuntime as g, AuthVerifier as h, type CreateAppOptions as i, type EngineConfig as j, RateLimiter as k, type RequestDatabase as l, type RouteEntry as m, type RuntimeHooks as n, type ScrubResult as o, type SqlDriver as p, type SqlTx as q, buildRouteTable as r, createApp as s, createLazyTransaction as t, createOps as u, createRequestDatabase as v, effectiveAuth as w, hostAllowed as x, installEgressFence as y, loadConfig as z };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Tables, TableTypes } from './db/env.cjs';
|
|
2
|
-
import { D as DBClient, b8 as TxPlanHandle, bh as TxTable, M as Materialized } from './endpoint-
|
|
2
|
+
import { D as DBClient, b8 as TxPlanHandle, bh as TxTable, M as Materialized } from './endpoint-c9h5jriX.cjs';
|
|
3
3
|
|
|
4
4
|
/** On delete action for foreign key references. */
|
|
5
5
|
type OnDeleteAction = 'cascade' | 'set null' | 'restrict' | 'no action';
|
|
@@ -36,6 +36,17 @@ interface ColumnDef {
|
|
|
36
36
|
enumName?: string;
|
|
37
37
|
enumValues?: string[];
|
|
38
38
|
unique?: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* The value is written by the DATABASE — a trigger, a rule, an identity — not by
|
|
41
|
+
* the author and not by a DEFAULT this schema declares. It makes the column
|
|
42
|
+
* optional on INSERT without putting a DEFAULT in the DDL.
|
|
43
|
+
*
|
|
44
|
+
* Before this existed the only way to keep a trigger-filled column off the
|
|
45
|
+
* INSERT type was to give it a fake `default()`: a value the schema claimed to
|
|
46
|
+
* write and the trigger immediately overwrote. That made the schema lie about
|
|
47
|
+
* its own data.
|
|
48
|
+
*/
|
|
49
|
+
dbAssigned?: boolean;
|
|
39
50
|
/** vector(n): the declared dimension count — part of the TYPE (typmod), read
|
|
40
51
|
* by the wire serializer and the deploy's auto-index (FR-001). */
|
|
41
52
|
dimensions?: number;
|
|
@@ -80,6 +91,18 @@ declare class ColumnBuilder<K extends ColumnType = ColumnType, N extends boolean
|
|
|
80
91
|
defaultRandom(): ColumnBuilder<K, N, true, E, P>;
|
|
81
92
|
/** Timestamp: default to now(). */
|
|
82
93
|
defaultNow(): ColumnBuilder<K, N, true, E, P>;
|
|
94
|
+
/**
|
|
95
|
+
* The DATABASE assigns this column's value — a trigger, a rule, an identity.
|
|
96
|
+
*
|
|
97
|
+
* The column becomes optional on INSERT (the author has nothing to send) while
|
|
98
|
+
* the DDL stays free of a DEFAULT this schema would not honour. It is NOT
|
|
99
|
+
* `default()`: that declares a value the schema promises to write.
|
|
100
|
+
*
|
|
101
|
+
* Naming: deliberately not `generated()`. Postgres has GENERATED columns and
|
|
102
|
+
* they are a different thing; borrowing the word would send a reader — or a
|
|
103
|
+
* model writing a schema — to the wrong feature.
|
|
104
|
+
*/
|
|
105
|
+
dbAssigned(): ColumnBuilder<K, N, true, E, P>;
|
|
83
106
|
/** Add a foreign key reference. */
|
|
84
107
|
/**
|
|
85
108
|
* Declares that this column used to be called `previous`.
|
|
@@ -247,22 +270,6 @@ interface PolicyDef {
|
|
|
247
270
|
withCheck: string | null;
|
|
248
271
|
permissive: boolean;
|
|
249
272
|
}
|
|
250
|
-
/**
|
|
251
|
-
* Fluent RLS policy builder.
|
|
252
|
-
*
|
|
253
|
-
* Defaults (documented, applied at construction):
|
|
254
|
-
* - `command`: `"all"` — applies to every SQL command unless `.for(...)` narrows it.
|
|
255
|
-
* - `roles`: `["authenticated"]` — the common case is "rule applies to signed-in
|
|
256
|
-
* users". Call `.to(...)` to override; pass `.to()` with no roles (or never
|
|
257
|
-
* call it after a reset) to target PUBLIC.
|
|
258
|
-
* - `using` / `withCheck`: `null` — no row filter / write check until set.
|
|
259
|
-
* - `permissive`: `true` — `AS PERMISSIVE` (policies OR together).
|
|
260
|
-
*
|
|
261
|
-
* Each method mutates `_def` in place and returns `this`, so the chain is a
|
|
262
|
-
* single builder instance (no per-call allocation, like a tagged-template
|
|
263
|
-
* compile target). The terminal `PolicyDef` is read directly off `_def` by
|
|
264
|
-
* `schema_extract.js`.
|
|
265
|
-
*/
|
|
266
273
|
declare class PolicyBuilder {
|
|
267
274
|
readonly _def: PolicyDef;
|
|
268
275
|
constructor(name: string);
|
|
@@ -280,6 +287,34 @@ declare class PolicyBuilder {
|
|
|
280
287
|
to(...roles: string[]): this;
|
|
281
288
|
/** Set the `USING (...)` row-visibility expression (raw SQL). */
|
|
282
289
|
using(sqlExpr: string): this;
|
|
290
|
+
/**
|
|
291
|
+
* "Rows of THIS table whose owner the caller is a member of" — the membership
|
|
292
|
+
* pattern, written so it cannot recurse.
|
|
293
|
+
*
|
|
294
|
+
* THE TRAP IT EXISTS FOR. Written by hand, membership policies point at each
|
|
295
|
+
* other: `channels` is visible to members, so its policy reads
|
|
296
|
+
* `channel_members`; `channel_members` is visible to members, so its policy
|
|
297
|
+
* reads `channels`. Postgres refuses the pair at query time with `infinite
|
|
298
|
+
* recursion detected in policy for relation ...`, and the error names the
|
|
299
|
+
* relation but not the cycle. The way out is asymmetry — the MEMBERSHIP table
|
|
300
|
+
* is protected by `user_id = auth.uid()` and nothing else, and every other
|
|
301
|
+
* table subqueries INTO it. That shape was in the platform's own schema and
|
|
302
|
+
* written down nowhere; a customer recovered it by reading that schema.
|
|
303
|
+
*
|
|
304
|
+
* `(select auth.uid())` rather than a bare call: the scalar subquery is
|
|
305
|
+
* evaluated ONCE per statement instead of per row.
|
|
306
|
+
*
|
|
307
|
+
* @example
|
|
308
|
+
* // channels: visible to members. The membership table gets the simple one.
|
|
309
|
+
* policy("member_read").for("select").to("authenticated")
|
|
310
|
+
* .memberOf("channel_members", "channel_id")
|
|
311
|
+
* // → id IN (SELECT "channel_id" FROM "channel_members"
|
|
312
|
+
* // WHERE "user_id" = (select auth.uid()))
|
|
313
|
+
*/
|
|
314
|
+
memberOf(membershipTable: string, foreignKey: string, options?: {
|
|
315
|
+
column?: string;
|
|
316
|
+
userColumn?: string;
|
|
317
|
+
}): this;
|
|
283
318
|
/** Set the `WITH CHECK (...)` write-validation expression (raw SQL). */
|
|
284
319
|
withCheck(sqlExpr: string): this;
|
|
285
320
|
/** Set the policy mode: `"permissive"` (default, OR-combined) or
|
|
@@ -347,12 +382,20 @@ interface EmbeddingModelRef {
|
|
|
347
382
|
apiKeyName?: string;
|
|
348
383
|
baseURL?: string;
|
|
349
384
|
}
|
|
385
|
+
/** Chat/damıtma modeli DESKRIPTORU (C-11, D-019) — memory beyanının extract'i.
|
|
386
|
+
* Embedding gibi düz veridir; çağrıyı worker yapar, anahtar vault'taki
|
|
387
|
+
* OPENAI_API_KEY'dir (D-017: aynı sağlayıcı, yeni dış sistem yok). */
|
|
388
|
+
interface ChatModelRef {
|
|
389
|
+
provider: "openai";
|
|
390
|
+
model: string;
|
|
391
|
+
}
|
|
350
392
|
declare const openai: {
|
|
351
393
|
embedding(model: string, opts?: {
|
|
352
394
|
dimensions?: number;
|
|
353
395
|
apiKeyName?: string;
|
|
354
396
|
baseURL?: string;
|
|
355
397
|
}): EmbeddingModelRef;
|
|
398
|
+
chat(model: string): ChatModelRef;
|
|
356
399
|
};
|
|
357
400
|
|
|
358
401
|
/**
|
|
@@ -400,12 +443,44 @@ interface VectorSearchDecl {
|
|
|
400
443
|
* eşleşme riski (Confluence-tipi sync yükleri için). */
|
|
401
444
|
staleness?: "null" | "keep";
|
|
402
445
|
}
|
|
403
|
-
/** Tablonun arama
|
|
404
|
-
*
|
|
405
|
-
*
|
|
446
|
+
/** Tablonun arama beyanı — İKİ biçim (D-007, tek yüzey):
|
|
447
|
+
*
|
|
448
|
+
* YENİ (önerilen): `{ from, model, ... }` — `from` kolonları hem FTS'e hem
|
|
449
|
+
* embed'e girer. Tabloda vector kolonu declare edilmişse SATIR-modu; yoksa
|
|
450
|
+
* CHUNK-modu otomatiktir (D-010): vektörler türev `__palbase_chunks`
|
|
451
|
+
* tablosunda yaşar, içerik otomatik bölünür. `text: false` FTS'i kapatır,
|
|
452
|
+
* `text: [..]` FTS kolonlarını from'dan ayırır. `chunks` yalnız ince ayar.
|
|
453
|
+
*
|
|
454
|
+
* ESKİ: `text: string[]` + `vector: {...}` — aynen çalışır, wire çıktısı
|
|
455
|
+
* bayt-aynı kalır (NFR-B1). İki biçim KARIŞTIRILAMAZ. */
|
|
406
456
|
interface SearchDecl {
|
|
407
|
-
text?: string[];
|
|
457
|
+
text?: string[] | boolean;
|
|
408
458
|
vector?: VectorSearchDecl | VectorSearchDecl[];
|
|
459
|
+
/** Yeni biçim: arama kaynağı kolonlar (FTS + embed). Varlığı yeni biçimi seçer. */
|
|
460
|
+
from?: string[];
|
|
461
|
+
/** Yeni biçim: auto-embed modeli (zorunlu — BYO için eski biçimi kullanın). */
|
|
462
|
+
model?: EmbeddingModelRef;
|
|
463
|
+
metric?: SearchMetric;
|
|
464
|
+
staleness?: "null" | "keep";
|
|
465
|
+
/** Chunk-modu ince ayarı (yalnız vector kolonsuz tabloda anlamlı). */
|
|
466
|
+
chunks?: {
|
|
467
|
+
size?: number;
|
|
468
|
+
overlap?: number;
|
|
469
|
+
};
|
|
470
|
+
/** Sorgu-yeniden-yazımı: tek yönlü eş anlamlı haritası (FR-026). */
|
|
471
|
+
synonyms?: Record<string, string[]>;
|
|
472
|
+
/** Geçerlilik kolonları türetilir; arama varsayılan yalnız günceli tarar (FR-029). */
|
|
473
|
+
validity?: boolean;
|
|
474
|
+
}
|
|
475
|
+
/** Hafıza beyanı (FR-032, D-019): kaynak tablonun yazımlarından platform
|
|
476
|
+
* fact damıtır ve `into` tablosuna yazar. Hedef NORMAL declared tablodur —
|
|
477
|
+
* kendi search/unique/validity beyanlarıyla. Okuma = Database.search(into).
|
|
478
|
+
* subject default "owner": fact'in kime ait olduğu kolonu (iki tabloda da). */
|
|
479
|
+
interface MemoryDecl {
|
|
480
|
+
from: string[];
|
|
481
|
+
into: string;
|
|
482
|
+
extract: ChatModelRef;
|
|
483
|
+
subject?: string;
|
|
409
484
|
}
|
|
410
485
|
interface TableInput<C extends ColumnMap = ColumnMap> {
|
|
411
486
|
columns: C;
|
|
@@ -448,6 +523,8 @@ interface TableInput<C extends ColumnMap = ColumnMap> {
|
|
|
448
523
|
}[];
|
|
449
524
|
/** Arama beyanı — bkz. SearchDecl. */
|
|
450
525
|
search?: SearchDecl;
|
|
526
|
+
/** Hafıza beyanı — bkz. MemoryDecl (FR-032). */
|
|
527
|
+
memory?: MemoryDecl;
|
|
451
528
|
}
|
|
452
529
|
/**
|
|
453
530
|
* A table definition — the runtime value the Go runtime's `schema_extract.js`
|
|
@@ -485,6 +562,8 @@ interface TableDef<C extends ColumnMap = ColumnMap> {
|
|
|
485
562
|
}[];
|
|
486
563
|
/** Arama beyanı, doğrulanmış ve taşınmış hali. */
|
|
487
564
|
search?: SearchDecl;
|
|
565
|
+
/** Hafıza beyanı, doğrulanmış ve taşınmış hali (FR-032). */
|
|
566
|
+
memory?: MemoryDecl;
|
|
488
567
|
}
|
|
489
568
|
/**
|
|
490
569
|
* A schema definition containing multiple tables, keyed by table name.
|
|
@@ -603,6 +682,9 @@ type RowShape<T extends TableDef> = {
|
|
|
603
682
|
/** A typed table accessor that mirrors the runtime DBClient surface. */
|
|
604
683
|
interface TypedTable<T extends TableDef> {
|
|
605
684
|
insert(data: InsertShape<T>): Promise<RowShape<T>>;
|
|
685
|
+
upsert(data: InsertShape<T>, opts: {
|
|
686
|
+
onConflict: readonly string[];
|
|
687
|
+
}): Promise<RowShape<T>>;
|
|
606
688
|
/** Update the row by id; resolves to the updated row, or `null` if no row
|
|
607
689
|
* matched (absent or RLS-hidden) — an idempotent outcome, mirroring
|
|
608
690
|
* `findById`. The runtime returns a null row rather than throwing. */
|
|
@@ -662,10 +744,65 @@ interface SearchParamsTyped<T extends TableTypes> {
|
|
|
662
744
|
/** Birden çok vektör kolonunda hedef seçimi (model geçişi, FR-013/using). */
|
|
663
745
|
using?: string;
|
|
664
746
|
mode?: "hybrid" | "text" | "vector";
|
|
747
|
+
/** Nihai (RRF-sonrası) skor alt eşiği — süzme LIMIT'ten önce uygulanır (FR-001). */
|
|
748
|
+
minScore?: number;
|
|
749
|
+
/** Chunk-modunda satır başına en iyi blok sayısı (1..10, vars. 3; FR-015). */
|
|
750
|
+
blocksPerRow?: number;
|
|
751
|
+
/** Tazelik çürümesi: nihai skor RRF-sonrası exp(-ln(2)*yaş/halfLife) ile çarpılır;
|
|
752
|
+
* field bir timestamp kolonu, halfLife "90s" | "15m" | "12h" | "30d" biçiminde (FR-004). */
|
|
753
|
+
recency?: {
|
|
754
|
+
field: Extract<keyof T["row"], string>;
|
|
755
|
+
halfLife: string;
|
|
756
|
+
};
|
|
757
|
+
/** Filtrelenmiş küme üzerinde kolon başına top-20 değer sayacı — dönüş
|
|
758
|
+
* dizisinin `_facets` özelliği (FR-027). */
|
|
759
|
+
facets?: Extract<keyof T["row"], string>[];
|
|
760
|
+
/** Satır-modunda FTS eşleşme vurgusu: sonuç satırına `_highlight` ekler;
|
|
761
|
+
* chunk-modda no-op — bloklar zaten eşleşen kesittir (FR-025). */
|
|
762
|
+
highlight?: boolean;
|
|
763
|
+
/** Validity'li tabloda zaman penceresi: varsayılan yalnız güncel versiyon;
|
|
764
|
+
* "all" tüm versiyonlar; {asOf} o anda geçerli olan (FR-029). */
|
|
765
|
+
validity?: "all" | {
|
|
766
|
+
asOf: string;
|
|
767
|
+
};
|
|
768
|
+
/** Alan-boost (FR-030): skor * (1 + w·x/(1+x)) — sayısal kolonla sınırlı
|
|
769
|
+
* çarpan, dış servissiz; bileşim RRF → boost → recency → minScore. */
|
|
770
|
+
boost?: {
|
|
771
|
+
field: Extract<keyof T["row"], string>;
|
|
772
|
+
weight: number;
|
|
773
|
+
};
|
|
665
774
|
}
|
|
775
|
+
/** search() dönüş dizisinin sorgu-düzeyi ekleri (FR-027): `_facets` dizinin
|
|
776
|
+
* ÖZELLİĞİDİR, satırlara kopyalanmaz (JSON'a satır başına şişme olmasın). */
|
|
777
|
+
type SearchFacets = Record<string, {
|
|
778
|
+
value: string | null;
|
|
779
|
+
count: number;
|
|
780
|
+
}[]>;
|
|
781
|
+
/** similar()/recommend() taşıyıcı opsiyonları (T018, FR-022): search'ün
|
|
782
|
+
* paramlarından query/vector/mode düşer — hedef vektörü metodun kendisi
|
|
783
|
+
* DB'den kurar; facets/highlight de düşer (T020) — engine bu ikisini
|
|
784
|
+
* similar/recommend'e geçirmez, tip vaadi gerçekle aynı kalır. */
|
|
785
|
+
type SimilarParamsTyped<T extends TableTypes> = Omit<SearchParamsTyped<T>, "query" | "vector" | "mode" | "facets" | "highlight">;
|
|
786
|
+
/** recommend() parametreleri (T018, FR-023). */
|
|
787
|
+
type RecommendParamsTyped<T extends TableTypes> = SimilarParamsTyped<T> & {
|
|
788
|
+
/** Kaynak beğeniler — hedef vektör bunların DB-içi avg'ı; boş olamaz. */
|
|
789
|
+
positive: string[];
|
|
790
|
+
/** İtilen örnekler — hedef pos.v + (pos.v - neg.v) ile yönlenir. */
|
|
791
|
+
negative?: string[];
|
|
792
|
+
};
|
|
666
793
|
/** Temel tablo erişimcisi — search'süz beş op. */
|
|
667
794
|
interface EnvTypedTableBase<T extends TableTypes> {
|
|
668
795
|
insert(data: T["insert"]): Promise<T["row"]>;
|
|
796
|
+
/**
|
|
797
|
+
* Insert the row, or update it when it collides on `onConflict`.
|
|
798
|
+
*
|
|
799
|
+
* The conflict columns must carry a unique constraint or index — that is what
|
|
800
|
+
* Postgres matches on — and they are excluded from the update, since they are
|
|
801
|
+
* what matched.
|
|
802
|
+
*/
|
|
803
|
+
upsert(data: T["insert"], opts: {
|
|
804
|
+
onConflict: readonly Extract<keyof T["row"], string>[];
|
|
805
|
+
}): Promise<T["row"]>;
|
|
669
806
|
/** Update the row by id; resolves to the updated row, or `null` if no row
|
|
670
807
|
* matched (absent or RLS-hidden) — an idempotent outcome, mirroring
|
|
671
808
|
* `findById`. The runtime returns a null row rather than throwing. */
|
|
@@ -673,6 +810,11 @@ interface EnvTypedTableBase<T extends TableTypes> {
|
|
|
673
810
|
delete(id: string): Promise<void>;
|
|
674
811
|
findById(id: string): Promise<T["row"] | null>;
|
|
675
812
|
findMany(query?: Partial<T["row"]>): Promise<T["row"][]>;
|
|
813
|
+
/** Validity'li tabloda satırın yeni versiyonu (FR-029, C-9): eski satır
|
|
814
|
+
* kapanır (valid_to/superseded_by), yenisi TEK savepoint'te eklenir; dönüş
|
|
815
|
+
* yeni satır. Validity beyanı olmayan tabloda adlandırılmış çalışma-zamanı
|
|
816
|
+
* hatası — tip düzeyinde ayrım env `Tables` bayrağı taşımadığından yapılamaz. */
|
|
817
|
+
supersede(id: string, row: T["insert"]): Promise<T["row"]>;
|
|
676
818
|
}
|
|
677
819
|
/** Tablo erişimcisi: env girdisi `searchable: true` taşıyorsa (vector kolonu ya da
|
|
678
820
|
* search beyanı — env-gen üretir) `search()` üyesi VARDIR; yoksa üye hiç yoktur ve
|
|
@@ -682,6 +824,18 @@ type EnvTypedTable<T extends TableTypes> = EnvTypedTableBase<T> & (T extends {
|
|
|
682
824
|
} ? {
|
|
683
825
|
search(params: SearchParamsTyped<T>): Promise<Array<T["row"] & {
|
|
684
826
|
_score: number;
|
|
827
|
+
}> & {
|
|
828
|
+
_facets?: SearchFacets;
|
|
829
|
+
}>;
|
|
830
|
+
/** "Bu satıra benzeyenler" (FR-022): hedef vektör DB'den okunur,
|
|
831
|
+
* kaynak satır sonuçta yoktur; id yoksa adlandırılmış hata. */
|
|
832
|
+
similar(id: string, params?: SimilarParamsTyped<T>): Promise<Array<T["row"] & {
|
|
833
|
+
_score: number;
|
|
834
|
+
}>>;
|
|
835
|
+
/** positive/negative beğenilerden öneri (FR-023): hedef vektör DB-içi
|
|
836
|
+
* avg CTE'leriyle; kaynak id'ler sonuçta yoktur. */
|
|
837
|
+
recommend(params: RecommendParamsTyped<T>): Promise<Array<T["row"] & {
|
|
838
|
+
_score: number;
|
|
685
839
|
}>>;
|
|
686
840
|
} : Record<never, never>);
|
|
687
841
|
/** The `tables` map exposed on `Database`/`tx`, keyed by the env `Tables`
|
|
@@ -750,6 +904,27 @@ interface EnvTypedDatabase extends Omit<DBClient, "txPlan" | "asService"> {
|
|
|
750
904
|
* });
|
|
751
905
|
*/
|
|
752
906
|
transaction<T>(fn: (tx: TxPlan) => T extends Promise<unknown> ? never : T): Promise<Materialized<T>>;
|
|
907
|
+
/**
|
|
908
|
+
* Run `fn` inside a SAVEPOINT, so a write that fails in it does not poison the
|
|
909
|
+
* rest of the request.
|
|
910
|
+
*
|
|
911
|
+
* A request is ONE Postgres transaction: a failed statement aborts it and
|
|
912
|
+
* every later one answers `current transaction is aborted`. That is why
|
|
913
|
+
* "insert, catch the unique violation, update instead" cannot be written
|
|
914
|
+
* directly — and why {@link EnvTypedTableBase.upsert} exists for the common
|
|
915
|
+
* case. Reach for `attempt` when the recovery is not an upsert.
|
|
916
|
+
*
|
|
917
|
+
* The handle is a parameter, not the ambient `Database`: only what `tx` writes
|
|
918
|
+
* is inside the boundary, so a concurrent branch of the same request cannot be
|
|
919
|
+
* rolled back by someone else's failure.
|
|
920
|
+
*
|
|
921
|
+
* @example
|
|
922
|
+
* const claimed = await Database.attempt(async (tx) => {
|
|
923
|
+
* await tx.insert("seats", { row: 4, seat: 12, user_id: user.id });
|
|
924
|
+
* return true;
|
|
925
|
+
* }).catch(() => false);
|
|
926
|
+
*/
|
|
927
|
+
attempt<T>(fn: (tx: Omit<DBClient, "attempt" | "txPlan" | "asService">) => Promise<T>): Promise<T>;
|
|
753
928
|
/**
|
|
754
929
|
* Return a sibling that bypasses RLS by running as the `service_role`. Use
|
|
755
930
|
* sparingly and explicitly — the default `Database.*` path is RLS-enforced.
|