@palbase/backend 32.0.0 → 33.0.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 +79 -26
- package/dist/bin/palbase-backend.cjs.map +1 -1
- package/dist/bin/palbase-backend.js +5 -5
- package/dist/{chunk-IXAX5CON.js → chunk-C4ZA5AT2.js} +1 -1
- package/dist/{chunk-IXAX5CON.js.map → chunk-C4ZA5AT2.js.map} +1 -1
- package/dist/{chunk-AZJIKCOR.js → chunk-DCDHAKF3.js} +39 -23
- package/dist/{chunk-AZJIKCOR.js.map → chunk-DCDHAKF3.js.map} +1 -1
- package/dist/{chunk-Z4CZRMNF.js → chunk-GOPZPM2A.js} +2 -2
- package/dist/{chunk-5C5UCILO.js → chunk-N54QZER3.js} +3 -3
- package/dist/{chunk-XEGZ3S2Q.js → chunk-QMFOL2K6.js} +32 -7
- package/dist/chunk-QMFOL2K6.js.map +1 -0
- package/dist/{chunk-KGP6ALIU.js → chunk-XOX6RFPZ.js} +30 -8
- package/dist/chunk-XOX6RFPZ.js.map +1 -0
- package/dist/db/index.cjs +14 -4
- package/dist/db/index.cjs.map +1 -1
- package/dist/db/index.d.cts +1 -1
- package/dist/db/index.d.ts +1 -1
- package/dist/db/index.js +3 -3
- package/dist/engine/index.cjs +79 -26
- package/dist/engine/index.cjs.map +1 -1
- package/dist/engine/index.d.cts +3 -3
- package/dist/engine/index.d.ts +3 -3
- package/dist/engine/index.js +5 -5
- package/dist/{index-DgYkdNT9.d.cts → index-BWgnGj68.d.cts} +68 -20
- package/dist/{index-BILC9WwS.d.ts → index-Bi74dcOu.d.ts} +2 -2
- package/dist/{index-BgWnP07a.d.ts → index-Dg10RnZO.d.ts} +68 -20
- package/dist/{index-CWGiJ2Up.d.cts → index-g6iQyYci.d.cts} +2 -2
- package/dist/index.cjs +59 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -6
- package/dist/index.d.ts +6 -6
- package/dist/index.js +5 -5
- package/dist/openapi/index.d.cts +2 -2
- package/dist/openapi/index.d.ts +2 -2
- package/dist/{registry-BcRYIQ-R.d.cts → registry-6VT5RPeO.d.cts} +1 -1
- package/dist/{registry-ClzjxIWy.d.ts → registry-B-sxJJN0.d.ts} +1 -1
- package/dist/test/index.cjs +35 -8
- package/dist/test/index.cjs.map +1 -1
- package/dist/test/index.d.cts +1 -1
- package/dist/test/index.d.ts +1 -1
- package/dist/test/index.js +7 -5
- package/dist/test/index.js.map +1 -1
- package/docs/README.md +1 -1
- package/docs/auth.md +102 -20
- package/docs/llms-full.txt +129 -21
- package/docs/schema.md +26 -0
- package/package.json +1 -1
- package/template/db/public.ts +23 -0
- package/template/package.json +1 -1
- package/dist/chunk-KGP6ALIU.js.map +0 -1
- package/dist/chunk-XEGZ3S2Q.js.map +0 -1
- /package/dist/{chunk-Z4CZRMNF.js.map → chunk-GOPZPM2A.js.map} +0 -0
- /package/dist/{chunk-5C5UCILO.js.map → chunk-N54QZER3.js.map} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
qualifiedTableKey
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-C4ZA5AT2.js";
|
|
4
4
|
import {
|
|
5
5
|
__name
|
|
6
6
|
} from "./chunk-KATPXCJ5.js";
|
|
@@ -575,4 +575,4 @@ export {
|
|
|
575
575
|
raw,
|
|
576
576
|
openai
|
|
577
577
|
};
|
|
578
|
-
//# sourceMappingURL=chunk-
|
|
578
|
+
//# sourceMappingURL=chunk-GOPZPM2A.js.map
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CODECS
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-XOX6RFPZ.js";
|
|
4
4
|
import {
|
|
5
5
|
qualifiedTableKey
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-C4ZA5AT2.js";
|
|
7
7
|
import {
|
|
8
8
|
__name
|
|
9
9
|
} from "./chunk-KATPXCJ5.js";
|
|
@@ -697,4 +697,4 @@ export {
|
|
|
697
697
|
buildContainer,
|
|
698
698
|
assertNoOrphanEntryPoints
|
|
699
699
|
};
|
|
700
|
-
//# sourceMappingURL=chunk-
|
|
700
|
+
//# sourceMappingURL=chunk-N54QZER3.js.map
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
qualifiedTableKey,
|
|
4
4
|
runTxPlan,
|
|
5
5
|
withRetry
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-C4ZA5AT2.js";
|
|
7
7
|
import {
|
|
8
8
|
__name
|
|
9
9
|
} from "./chunk-KATPXCJ5.js";
|
|
@@ -26,9 +26,29 @@ function __runWithRuntime(services, fn) {
|
|
|
26
26
|
}, fn);
|
|
27
27
|
}
|
|
28
28
|
__name(__runWithRuntime, "__runWithRuntime");
|
|
29
|
+
var REQUIRED_SERVICES = [
|
|
30
|
+
"Database",
|
|
31
|
+
"Auth",
|
|
32
|
+
"Secrets",
|
|
33
|
+
"Documents",
|
|
34
|
+
"Storage",
|
|
35
|
+
"Cache",
|
|
36
|
+
"Log",
|
|
37
|
+
"Notifications",
|
|
38
|
+
"Flags",
|
|
39
|
+
"Realtime"
|
|
40
|
+
];
|
|
41
|
+
function refuseIncompleteBox(services) {
|
|
42
|
+
const missing = REQUIRED_SERVICES.filter((k) => services[k] === void 0);
|
|
43
|
+
if (missing.length > 0) {
|
|
44
|
+
throw new Error(`the request scope is missing ${missing.join(", ")} \u2014 this box was most likely written by a DIFFERENT @palbase/backend major sharing the same process (the request store is a process-wide well-known Symbol, deliberately, so two bundles of ONE version can share it). Align the versions, or pass every service when building the scope yourself.`);
|
|
45
|
+
}
|
|
46
|
+
return services;
|
|
47
|
+
}
|
|
48
|
+
__name(refuseIncompleteBox, "refuseIncompleteBox");
|
|
29
49
|
function __getRuntime() {
|
|
30
50
|
const scoped = __requestALS.getStore();
|
|
31
|
-
if (scoped) return scoped.runtime;
|
|
51
|
+
if (scoped) return refuseIncompleteBox(scoped.runtime);
|
|
32
52
|
if (runtime === null) {
|
|
33
53
|
throw new Error("Palbase services accessed outside a request scope. The Database/Documents/\u2026 singletons are only available inside an endpoint handler (or after the runtime has called __runWithRuntime / __setRuntime).");
|
|
34
54
|
}
|
|
@@ -170,7 +190,12 @@ function makeTypedSurface(raw) {
|
|
|
170
190
|
$findById: /* @__PURE__ */ __name((table, id) => raw.findById(table, id), "$findById"),
|
|
171
191
|
$findMany: /* @__PURE__ */ __name((table, query, opts) => raw.findMany(table, query, opts), "$findMany"),
|
|
172
192
|
$put: /* @__PURE__ */ __name((table, data, opts) => raw.put(table, data, opts), "$put"),
|
|
173
|
-
|
|
193
|
+
// `opts` İLETİLİR. Düşürüldüğü sürece `Database.$updateMany(t, w, s,
|
|
194
|
+
// { returning: false })` sessizce SATIRLARI döndürüyordu, sayıyı değil —
|
|
195
|
+
// ve `if (n === 0) throw new Conflict(...)` guard'ı HİÇ çalışmıyordu,
|
|
196
|
+
// çünkü `[] === 0` yanlıştır. Komşu forwarder'lar (`$findMany`, `$put`)
|
|
197
|
+
// kendi opsiyonlarını zaten iletiyordu; bu biri unutulmuştu.
|
|
198
|
+
$updateMany: /* @__PURE__ */ __name((table, where, set, opts) => raw.updateMany(table, where, set, opts), "$updateMany"),
|
|
174
199
|
$deleteMany: /* @__PURE__ */ __name((table, where) => raw.deleteMany(table, where), "$deleteMany"),
|
|
175
200
|
$count: /* @__PURE__ */ __name((table, where) => raw.count(table, where), "$count"),
|
|
176
201
|
$search: /* @__PURE__ */ __name((table, params) => raw.search(table, params), "$search"),
|
|
@@ -301,13 +326,13 @@ var Flags = Object.assign({
|
|
|
301
326
|
* Lazily resolve the runtime's cross-user sibling on each call. We do NOT
|
|
302
327
|
* cache it: `rawFlags.asService()` reads the CURRENT request scope through
|
|
303
328
|
* the runtime proxy, so caching would leak one request's sibling into
|
|
304
|
-
* another concurrent request. Mirrors `Database
|
|
329
|
+
* another concurrent request. Mirrors `Database.$asService()`.
|
|
305
330
|
*/
|
|
306
331
|
$asService() {
|
|
307
332
|
return rawFlags.asService();
|
|
308
333
|
},
|
|
309
334
|
/**
|
|
310
|
-
* The name this member carried before
|
|
335
|
+
* The name this member carried before 32.0.0.
|
|
311
336
|
*
|
|
312
337
|
* It does NOT work silently. A retired member that quietly keeps returning
|
|
313
338
|
* is how a rename becomes a mystery: the old call site goes on compiling,
|
|
@@ -317,7 +342,7 @@ var Flags = Object.assign({
|
|
|
317
342
|
* Database.$asService()" while the promise went unkept.
|
|
318
343
|
*/
|
|
319
344
|
asService() {
|
|
320
|
-
throw new Error("Flags.asService() was renamed to Flags.$asService() in
|
|
345
|
+
throw new Error("Flags.asService() was renamed to Flags.$asService() in 32.0.0 \u2014 system members carry the `$` prefix, like Database.$asService().");
|
|
321
346
|
}
|
|
322
347
|
});
|
|
323
348
|
var Realtime = makeServiceProxy("Realtime");
|
|
@@ -343,4 +368,4 @@ export {
|
|
|
343
368
|
Flags,
|
|
344
369
|
Realtime
|
|
345
370
|
};
|
|
346
|
-
//# sourceMappingURL=chunk-
|
|
371
|
+
//# sourceMappingURL=chunk-QMFOL2K6.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/runtime.ts"],"sourcesContent":["/**\n * runtime.ts — request-scoped service singletons.\n *\n * The backend SDK no longer threads a `ctx` god-object through every handler.\n * Instead, controller methods import PascalCase service singletons directly:\n *\n * import { Controller, Post, Body, Database } from \"@palbase/backend\";\n *\n * \\@Controller(\"/todos\")\n * export default class TodosController {\n * \\@Post(\"\") create(\\@Body(CreateTodoBody) body: CreateTodoBody): unknown {\n * return Database.public.todos.insert({ title: body.title });\n * }\n * }\n *\n * The singletons are thin Proxies. Every property access forwards to the live\n * client for the CURRENT request scope, resolved through {@link __getRuntime}.\n *\n * # Request-scope resolution (persistent app-server)\n *\n * The runtime is a long-running Node process that serves many concurrent\n * requests on one event loop (NOT a fresh subprocess per request). A single\n * module-global slot would let one in-flight request's services bleed into\n * another's. So the services are carried in an {@link AsyncLocalStorage} store\n * ({@link __requestALS}) that the runtime sets per request with\n * {@link __runWithRuntime}; every async continuation of that request reads its\n * own store. `__getRuntime` reads the ALS store first; the module-global slot\n * (set by {@link __setRuntime}) is only a fallback for callers that run OUTSIDE\n * an ALS scope (dev-server, unit tests, the legacy single-shot path). Because\n * each `br-<ref>` pod is single-tenant, there is no cross-tenant leakage; the\n * ALS store is what prevents cross-REQUEST leakage within the shared process.\n *\n * The seam that makes `import { Database } from \"@palbase/backend\"` resolve to\n * the runtime-injected client: `@palbase/backend` is marked esbuild-EXTERNAL\n * when the tenant bundle is built, and the package is installed globally in the\n * pod (NODE_PATH=/usr/local/lib/node_modules). So worker.js's\n * `require('@palbase/backend')` and the bundle's `import` resolve to ONE shared\n * module instance — the ALS store and `__setRuntime` slot on that instance are\n * visible to the singletons the bundle imported.\n */\n\nimport type { PalbaseFlagKey } from \"./stack.js\";\nimport type { Buckets, BucketTypes, Schemas } from \"./db/env.js\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\n\nimport type {\n DBClient,\n DBOps,\n CacheClient,\n Logger,\n PalbaseDocsClient,\n SecretsService,\n} from \"./endpoint.js\";\nimport type {\n PalbaseAuthAdminClient,\n PalbaseStorageClient,\n PalbaseBucketClient,\n PalbaseNotificationsClient,\n PalbaseFlagsClient,\n PalbaseFlagsServiceClient,\n PalbaseFlagContext,\n PalbaseFlagVariant,\n PalbaseFlag,\n PalbaseFlagValue,\n PalbaseSetOverrideResult,\n PalbaseRealtimeClient,\n} from \"./clients.js\";\nimport type { PalbaseResult } from \"./endpoint.js\";\nimport type {\n EnvTypedDatabase,\n EnvServiceDatabase,\n EnvTables,\n TxPlan,\n TxTables,\n} from \"./db/typed-db.js\";\nimport type { Materialized } from \"./db/tx-plan.js\";\nimport { TxPlanBuilder, runTxPlan } from \"./db/tx-plan.js\";\nimport { withRetry } from \"./db/typed-db.js\";\nimport { qualifiedTableKey } from \"./db/schema-json.js\";\nimport type { DollarOps } from \"./db/typed-db.js\";\n\n/** The set of live clients the runtime injects per request scope.\n *\n * Realtime is BROADCAST-ONLY here (a stateless handler can push an event but\n * cannot hold a subscription socket — `subscribe()` lives on the client SDK).\n *\n * EXCLUDED on purpose: Functions, Links, Analytics. They are not exposed as\n * backend handler singletons — out of scope for backend endpoints. `Auth` is\n * here in its ROLE-ASSIGNMENT shape only: signing in is the client SDK's job,\n * but granting a role is an operator verb the tenant's own handler needs\n * (FR-009). */\nexport interface RuntimeServices {\n Database: DBClient;\n Auth: PalbaseAuthAdminClient;\n Secrets: SecretsService;\n Documents: PalbaseDocsClient;\n Storage: PalbaseStorageClient;\n Cache: CacheClient;\n Log: Logger;\n Notifications: PalbaseNotificationsClient;\n Flags: PalbaseFlagsClient;\n Realtime: PalbaseRealtimeClient;\n}\n\n/**\n * The per-request ALS box.\n *\n * `runtime` is the service bundle. `userId` is the request's authenticated user\n * id, written by the runtime immediately after it assembles the request object\n * (worker.js: `requestALS.getStore().userId = pbReq.user?.id || null`) and\n * `null` on an anonymous request. It was already being written there for the\n * Flags client's auto-bind; declaring it here makes the existing contract typed\n * instead of implicit, which is what lets `currentSubjectId()` read a\n * server-owned identity rather than trusting anything the caller sent.\n */\nexport interface RequestStore {\n runtime: RuntimeServices;\n userId?: string | null;\n /** This request's id (`req_…`), written by the runtime. Used as the spend\n * idempotency scope when the caller supplied no `Idempotency-Key`. */\n requestId?: string;\n /** The caller's `Idempotency-Key` header, or `null` when absent. What makes a\n * client's retry replay its first result instead of charging twice. */\n idempotencyKey?: string | null;\n}\n\n/**\n * Per-request store. The persistent runtime runs each request inside\n * {@link __runWithRuntime}, so every async continuation of that request reads\n * its OWN `runtime` (and any other request-scoped fields the runtime adds).\n *\n * Exported with a `__` prefix so the runtime (worker.js) shares the SAME ALS\n * instance across the one module instance — two ALS instances would silently\n * not see each other's stores. NOT part of the public author-facing API.\n */\n/**\n * The per-request store — ON globalThis under a well-known Symbol, for the same\n * reason `lifecycleHooks` and the controller registry are.\n *\n * MEASURED, through the production package gate: `tsup` emits `dist/index.cjs`\n * and `dist/test/index.cjs` as SEPARATE bundles and each inlines this module.\n * With a module-local `AsyncLocalStorage`, `withServices` (which ships from the\n * `/test` subpath) opened a scope in ITS copy while the `Database` singleton\n * (which ships from the root) read the OTHER copy's — and the ambient service a\n * test had just installed was invisible:\n *\n * Error: Palbase services accessed outside a request scope.\n *\n * The unit tests could not see it: inside the package there is only ever one\n * copy. It took calling the built artifact the way a consumer does. Same class\n * as the defect `@Module`'s slot comment describes — two module-local values\n * where the two halves of one contract must agree.\n *\n * SÜRÜMSÜZ, VE BU BİLİNÇLİ. Anahtar `…requestALS@32` gibi sürümlenseydi iki\n * major aynı süreçte ALS'i paylaşmayı BIRAKIRDI — yani yukarıda anlatılan kusur\n * sürüm sınırında geri gelirdi. Bedeli de gerçek ve burada yazılı:\n * `RequestStore.runtime` zorunlu bir `RuntimeServices` ve o küme büyüyor\n * (30.0.0 `Auth` ile 9→10). Eski bir majorün yazdığı kutuyu yeni bir major\n * okursa yeni alan `undefined` gelir.\n *\n * Bu pakette YEDİ well-known Symbol var ve hepsi sürümsüz (`httpError`,\n * `engineRaised`, `channels`, `errorRegistry`, `declarationRefusal`,\n * `lifecycleHooks`, ve bu). Yalnız BİRİNİ sürümlemek tutarsızlıktan başka bir\n * şey üretmez: karar paket geneli olmalı ve `RequestStore`'un şekil\n * sözleşmesiyle birlikte alınmalı (sapma defteri D-05).\n */\nconst REQUEST_ALS: unique symbol = Symbol.for(\"palbase.backend.requestALS\") as never;\n\nexport const __requestALS: AsyncLocalStorage<RequestStore> = ((): AsyncLocalStorage<RequestStore> => {\n const g = globalThis as unknown as Record<symbol, AsyncLocalStorage<RequestStore> | undefined>;\n return (g[REQUEST_ALS] ??= new AsyncLocalStorage<RequestStore>());\n})();\n\n/** Process-global fallback slot. Used only OUTSIDE an ALS scope (dev-server,\n * unit tests, legacy single-shot worker). Inside the persistent server every\n * request runs in {@link __requestALS}, which takes precedence. */\nlet runtime: RuntimeServices | null = null;\n\n/** Install the live clients in the process-global fallback slot.\n *\n * Persistent-server requests should use {@link __runWithRuntime} instead; this\n * remains for dev-server / tests / the legacy single-shot path that run without\n * an ALS scope. NOT part of the public author-facing API. */\nexport function __setRuntime(services: RuntimeServices): void {\n runtime = services;\n}\n\n/** Run `fn` with `services` bound as the request-scoped runtime.\n *\n * The persistent worker calls this once per request so concurrent requests\n * never share a services slot. NOT part of the public author-facing API. */\nexport function __runWithRuntime<T>(services: RuntimeServices, fn: () => T): T {\n return __requestALS.run({ runtime: services }, fn);\n}\n\n/** Read the live clients, throwing if accessed outside a request scope.\n *\n * Resolves the ALS store first (persistent server, per-request), then the\n * process-global fallback (dev-server / tests). NOT part of the public\n * author-facing API — used by the runtime and the singleton Proxies. */\n/**\n * Bir kutunun TAŞIMASI GEREKEN servisler.\n *\n * `withServices`'in listesiyle aynı gerçeği söylüyor ama BURADA yaşamak\n * zorunda: `test/` yalnız test yüzeyinde, bu kontrol ise her isteğin yolunda.\n * İkisi de `satisfies` ile `RuntimeServices`'a pinli, yani biri eksik kalırsa\n * derleme durur.\n */\nconst REQUIRED_SERVICES = [\n \"Database\",\n \"Auth\",\n \"Secrets\",\n \"Documents\",\n \"Storage\",\n \"Cache\",\n \"Log\",\n \"Notifications\",\n \"Flags\",\n \"Realtime\",\n] as const satisfies readonly (keyof RuntimeServices)[];\n\n/**\n * ÇAPRAZ-MAJOR KUTUYU ADIYLA REDDET (sapma defteri D-05).\n *\n * `__requestALS` sürümSÜZ bir `Symbol.for` altında ve bu bilinçli: iki bundle\n * (`dist/index.cjs` ve `dist/test/index.cjs`) ambient kapsamı ancak öyle\n * paylaşır. Bedeli de gerçek — tek süreçte iki major varsa ESKİ olanın yazdığı\n * kutuyu YENİ olan okur, ve servis kümesi büyümüşse (30.0.0'da 9→10, `Auth`)\n * yeni alan `undefined` gelir.\n *\n * `undefined` bir servis, erişildiğinde `Reflect.get called on non-object`\n * verir: ne eksik olanın adı, ne sebebi. Bu kontrol o sessizliği bir cümleye\n * çevirir. Maliyeti istek başına on `in` kontrolü.\n */\nfunction refuseIncompleteBox(services: RuntimeServices): RuntimeServices {\n const missing = REQUIRED_SERVICES.filter((k) => services[k] === undefined);\n if (missing.length > 0) {\n throw new Error(\n `the request scope is missing ${missing.join(\", \")} — this box was most likely written by a ` +\n `DIFFERENT @palbase/backend major sharing the same process (the request store is a ` +\n `process-wide well-known Symbol, deliberately, so two bundles of ONE version can share it). ` +\n `Align the versions, or pass every service when building the scope yourself.`,\n );\n }\n return services;\n}\n\nexport function __getRuntime(): RuntimeServices {\n const scoped = __requestALS.getStore();\n if (scoped) return refuseIncompleteBox(scoped.runtime);\n if (runtime === null) {\n throw new Error(\n \"Palbase services accessed outside a request scope. The Database/Documents/… \" +\n \"singletons are only available inside an endpoint handler (or after the \" +\n \"runtime has called __runWithRuntime / __setRuntime).\",\n );\n }\n return runtime;\n}\n\n// ─── lifecycle: where a long-lived resource lives (FR-013) ─────────────────\n//\n// `Resource` was removed in 23.0.0 and nothing replaced the LIFECYCLE half of\n// it. What that left behind is measurable: a connection pool (the driver in\n// `docs/resources.md`'s own example was Neo4j) had no documented place to be\n// opened and NO WAY AT ALL to be closed, so every deploy left the pool it\n// opened behind. These two hooks are that half — and only that half. The\n// secret-distribution half does not come back: a handler reads `Secrets.get`,\n// and a start hook, which runs before any request scope exists, reads the\n// `process.env` the runtime mirrors the vault into at boot.\n\n/** A lifecycle hook. Sync or async; the runtime awaits what it returns. */\nexport type LifecycleHook = () => void | Promise<void>;\n\n/** Runs one release's shutdown hooks. Handed back by {@link __runStartHooks}\n * and called by the engine's `app.shutdown()`. Idempotent. */\nexport type ShutdownRunner = () => Promise<void>;\n\ninterface DeclaredHook {\n name: string;\n run: LifecycleHook;\n}\n\ninterface DeclaredLifecycle {\n start: DeclaredHook[];\n shutdown: DeclaredHook[];\n}\n\n/**\n * What has been DECLARED and not yet claimed by an app.\n *\n * On globalThis under a well-known Symbol for the reason the controller\n * registry is (`decorators/controller.ts`): a deployed bundle inlines its own\n * copy of this package, and the engine that has to RUN these hooks holds the\n * other copy. Two module-local arrays would mean the engine reads the empty one\n * and every declared hook is silently never run — which is exactly how\n * `Resource`'s `init(env)` died.\n */\nconst LIFECYCLE: unique symbol = Symbol.for(\"palbase.backend.lifecycleHooks\") as never;\n\nfunction declaredLifecycle(): DeclaredLifecycle {\n const g = globalThis as unknown as Record<symbol, DeclaredLifecycle | undefined>;\n return (g[LIFECYCLE] ??= { start: [], shutdown: [] });\n}\n\n/**\n * Run `hook` ONCE while the application comes up, before it serves anything.\n *\n * Call it at MODULE SCOPE in a file the application imports — the same rule\n * `defineDefaultAuth` and `@Controller` follow, and for the same reason: the\n * declaration is claimed when the app boots, which is after module loading and\n * before the first request. `name` is not decoration: a hook that throws is\n * reported by that name and the boot is REFUSED, so it is what tells an\n * operator which resource did not come up.\n *\n * There is no request scope yet, so the `Database`/`Secrets`/… singletons are\n * NOT available inside a start hook. A secret is read from `process.env` here\n * (the runtime mirrors the vault into it at boot).\n *\n * @example\n * // resources/graph.ts\n * import neo4j from \"neo4j-driver\";\n * import { onStart, onShutdown } from \"@palbase/backend\";\n *\n * export let graph: Driver;\n * onStart(\"graph\", () => {\n * graph = neo4j.driver(process.env.NEO4J_URL!, neo4j.auth.basic(\"neo4j\", process.env.NEO4J_PASSWORD!));\n * });\n * onShutdown(\"graph\", () => graph.close());\n */\nexport function onStart(name: string, hook: LifecycleHook): void {\n declaredLifecycle().start.push({ name, run: hook });\n}\n\n/**\n * Run `hook` while the application shuts down — the place a pool opened in\n * {@link onStart} is closed.\n *\n * Shutdown is BEST-EFFORT by design: a hook that throws is reported by name and\n * the rest still run. A drain that abandoned the remaining hooks on the first\n * failure would leak exactly what this exists to release, and the process is\n * leaving anyway.\n *\n * Hooks run in REVERSE declaration order, so a resource is released before what\n * it was built on.\n */\nexport function onShutdown(name: string, hook: LifecycleHook): void {\n declaredLifecycle().shutdown.push({ name, run: hook });\n}\n\nfunction reason(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\n/** Best-effort drain: every hook runs, a failure is reported, none is silent. */\nasync function drain(hooks: DeclaredHook[]): Promise<void> {\n for (const h of [...hooks].reverse()) {\n try {\n await h.run();\n } catch (err) {\n console.error(`[palbase] shutdown hook \"${h.name}\" failed: ${reason(err)}`, err);\n }\n }\n}\n\n/**\n * CLAIM what has been declared, run the start hooks, and hand back the runner\n * for this release's shutdown hooks. Called by the engine's `createApp`; the\n * `App.shutdown()` it builds calls what comes back. NOT part of the public\n * author-facing API.\n *\n * IT CLAIMS RATHER THAN READS, which is what makes it correct in this runtime:\n * a candidate release is loaded BESIDE the live one in one process\n * (`v2/runtime/src/registry-scope.ts`), and both bundles append to the one\n * shared slot above. If each app read the whole list, the live app's shutdown\n * would close the candidate's pool and the candidate's would close the live\n * app's. Taking the declarations leaves each app holding exactly its own.\n *\n * A start hook that throws REFUSES THE BOOT — with the hook's name in the\n * message — after releasing whatever the earlier hooks already opened. Serving\n * from a half-initialised app is the silence this whole surface replaces, and a\n * boot that dies holding an open pool is the leak it replaces.\n */\nexport async function __runStartHooks(): Promise<ShutdownRunner> {\n const slot = declaredLifecycle();\n const start = slot.start.splice(0);\n const shutdown = slot.shutdown.splice(0);\n\n for (const h of start) {\n try {\n await h.run();\n } catch (err) {\n await drain(shutdown);\n throw new Error(`[palbase] start hook \"${h.name}\" failed: ${reason(err)}`, { cause: err });\n }\n }\n\n let drained = false;\n return async () => {\n // SIGTERM racing a redeploy asks twice; a pool is closed once.\n if (drained) return;\n drained = true;\n await drain(shutdown);\n };\n}\n\n/** Drop every declaration. For tests, which declare repeatedly in one process.\n * NOT part of the public author-facing API. */\nexport function __resetLifecycleHooks(): void {\n const g = globalThis as unknown as Record<symbol, DeclaredLifecycle | undefined>;\n delete g[LIFECYCLE];\n}\n\n/**\n * Build a Proxy singleton that forwards every property access to the live\n * client named `key` on the current runtime.\n *\n * The single `as RuntimeServices[K]` is the only contained cast in the surface:\n * `Reflect.get` on a typed object returns `unknown` for a `string | symbol`\n * key, but `prop` is constrained to keys of the client interface at the call\n * sites (the exported singletons are typed below), so the forward is sound.\n */\nfunction makeServiceProxy<K extends keyof RuntimeServices>(key: K): RuntimeServices[K] {\n const handler: ProxyHandler<RuntimeServices[K]> = {\n get(_target, prop, receiver) {\n const client = __getRuntime()[key];\n const value = Reflect.get(client as object, prop, receiver) as unknown;\n // Bind methods to their owning client so `this` stays correct when the\n // author destructures or calls `Database.query(...)`.\n return typeof value === \"function\" ? value.bind(client) : value;\n },\n };\n // The Proxy target is irrelevant (all access goes through `get`); the cast\n // names the surface type the singleton presents to authors.\n return new Proxy({} as RuntimeServices[K], handler);\n}\n\n/**\n * Build the `.tables` accessor for the top-level `Database`. Each\n * `tables.<name>` access returns a small object that forwards the five CRUD ops\n * to the underlying client using `name` as the string table identifier. The\n * shapes are typed against the generated `palbase-env.d.ts` (`EnvTables`); at\n * runtime they are plain string-keyed calls, so no schema value is needed here.\n *\n * Returns `EnvTables` — TS cannot infer the mapped type through the Proxy, so\n * a single structural narrowing names the surface (the proxy returns a\n * correctly-shaped accessor for whatever string member is read).\n */\n/** T018 (C-8): similar/recommend'in string-keyed yüzü. DBOps'a (endpoint.ts)\n * BİLEREK eklenmedi — search-param imza üçlüsü (engine/db + typed-db +\n * endpoint) büyümesin: proxy dispatch runtime'da engine ops'una zaten ulaşır,\n * derleme güvenliğini typed yüzey (EnvTypedTable) verir. */\nexport interface RecoOps {\n similar(table: string, id: string, params?: Record<string, unknown>): Promise<Record<string, unknown>[]>;\n recommend(table: string, params: Record<string, unknown>): Promise<Record<string, unknown>[]>;\n}\n\n/**\n * The Proxy behind EVERY `.tables` map — public's and every other schema's.\n *\n * `prefix` is what the wire name is built from: `\"\"` for `public`, so its tables\n * stay BARE, and `\"<schema>.\"` for any other, so `schema(\"billing\").tables\n * .invoices` reaches the broker as `billing.invoices` (D-10 — the same\n * schema-qualified key `toSchemaJSON` and the generated `relations` use).\n *\n * One trap for both surfaces: two copies would be two op lists that can drift,\n * and the one that forgets an op does not complain — it answers `undefined`.\n */\nfunction makeTableProxy(ops: () => DBOps & RecoOps, prefix: string): object {\n return new Proxy(\n {},\n {\n get(_t, prop: string | symbol) {\n if (typeof prop !== \"string\") return undefined;\n const name = `${prefix}${prop}`;\n return {\n insert: (data: Record<string, unknown>) => ops().insert(name, data),\n // DÖRDÜNCÜ FİİL, aynı gerekçeyle: tip söz veriyor, ops katmanı\n // uyguluyor, ve bir handler'ın gerçekten dokunduğu yer BURASI.\n // `runtime-table-verbs` kapısı bunu adıyla saydı.\n aggregate: (q: Parameters<DBOps[\"aggregate\"]>[1]) => ops().aggregate(name, q),\n insertMany: (\n rows: readonly Record<string, unknown>[],\n opts?: { onConflict: readonly string[]; action?: \"ignore\" | \"update\" },\n ) => ops().insertMany(name, rows, opts),\n update: (q: { where: { id: string }; set: Record<string, unknown> }) =>\n ops().update(name, q.where.id, q.set),\n delete: (id: string) => ops().delete(name, id),\n findById: (id: string) => ops().findById(name, id),\n findMany: (q?: Record<string, unknown>) => {\n // `where` AYIKLANIR; kalan alanlar (orderBy/limit/offset) ham op'un\n // ikinci parametresine gider. Tümünü geçirmek `where`'i tel üstünde\n // ikinci kez gönderirdi — `typed-db.test.ts` bunu yakalıyor.\n const { where, ...opts } = q ?? {};\n return ops().findMany(\n name,\n where as Record<string, unknown> | undefined,\n opts as Parameters<DBOps[\"findMany\"]>[2],\n );\n },\n put: (q: { data: Record<string, unknown>; onConflict: readonly string[] }) =>\n ops().put(name, q.data, { onConflict: q.onConflict }),\n // THREE VERBS THE TYPE PROMISED AND THIS PROXY DID NOT EMIT.\n //\n // `EnvTypedTableBase` declares `updateMany`, `deleteMany` and `count`\n // (typed-db.ts) and the ops layer implements all three — only this\n // proxy, which is what a handler actually touches, left them out. So\n // the type said the verb exists, autocomplete offered it, and the call\n // answered `undefined is not a function`.\n //\n // Older than this run, but the run rewrote this proxy for\n // `Database.schema(name).tables.*` and would have carried the gap onto\n // the new surface too.\n updateMany: (q: { where: Record<string, unknown>; set: Record<string, unknown> }) =>\n ops().updateMany(name, q.where, q.set),\n deleteMany: (q: { where: Record<string, unknown> }) => ops().deleteMany(name, q.where),\n count: (q?: { where?: Record<string, unknown> }) => ops().count(name, q?.where),\n search: (params?: Record<string, unknown>) => ops().search(name, params),\n similar: (id: string, params?: Record<string, unknown>) => ops().similar(name, id, params),\n recommend: (params: Record<string, unknown>) => ops().recommend(name, params),\n facets: (params: { facets: string[] } & Record<string, unknown>) => ops().facets(name, params),\n supersede: (id: string, row: Record<string, unknown>) => ops().supersede(name, id, row),\n claim: (unique: Record<string, unknown>, extra?: Record<string, unknown>) =>\n ops().claim(name, unique, extra),\n };\n },\n },\n );\n}\n\n\n/** The raw string-keyed `DBClient` for the current request scope. */\nconst rawDatabase: DBClient = makeServiceProxy(\"Database\");\n\n/**\n * Wrap a raw `DBClient` into the typed `{ ...ops, tables, transaction }`\n * surface. The five string ops forward straight through; `tables` is the\n * env-typed accessor; `transaction` yields typed tables. Reused for both the\n * default (RLS-enforced) `Database` and the `asService()` sibling — each is\n * fed its own raw client (the default proxy vs `rawDatabase.asService()`).\n *\n * The `satisfies` pins the op surface so a missing/renamed op is a compile\n * error; the assembled object carries `tables`/`transaction` alongside.\n */\n/**\n * PACKAGE-INTERNAL, and deliberately NOT re-exported from `index.ts`.\n *\n * `test/fake-db.ts` builds the fake's surface with the SAME constructor\n * production uses, so the two cannot drift: the day a `$op` is added here, the\n * fake grows it in the same commit. Exporting it from the public index instead\n * would put a runtime-assembly detail on the author-facing API (FR-006).\n */\nexport function makeTypedSurface(raw: Omit<DBClient, \"asService\">): EnvServiceDatabase {\n // Proxy dispatch her üyeyi taşır; RecoOps tipi DBClient'a eklenmediğinden\n // (yukarıdaki karar) similar/recommend erişimi bu daraltmadan geçer.\n const reco = raw as Omit<DBClient, \"asService\"> & RecoOps;\n // `$` ÖNEKİ AÇIKÇA YAZILIR, dinamik üretilmez.\n //\n // Bir tur `Object.fromEntries(Object.entries(ops).map(…))` ile üretilmişti ve\n // `database.test.ts`'in sayımı onu göremedi: sayım DEKLARASYONLARI okuyor,\n // string literal'leri değil. Görünmeyen bir yüzey denetlenemez — ve o testin\n // varlık sebebi tam olarak budur (FR-044: yüzeyde bağlantı bilgisi olmadığını\n // kanıtlamak, ama önce yüzeye gerçekten ULAŞTIĞINI kanıtlamak).\n const ops = {\n $query: (sql: string, params?: unknown[]) => raw.query(sql, params),\n $insert: (table: string, data: Record<string, unknown>) => raw.insert(table, data),\n $update: (table: string, id: string, data: Record<string, unknown>) =>\n raw.update(table, id, data),\n $delete: (table: string, id: string) => raw.delete(table, id),\n $findById: (table: string, id: string) => raw.findById(table, id),\n $findMany: (table: string, query?: Record<string, unknown>, opts?: Parameters<DBOps[\"findMany\"]>[2]) =>\n raw.findMany(table, query, opts),\n $put: (table: string, data: Record<string, unknown>, opts: { onConflict: readonly string[] }) =>\n raw.put(table, data, opts),\n // `opts` İLETİLİR. Düşürüldüğü sürece `Database.$updateMany(t, w, s,\n // { returning: false })` sessizce SATIRLARI döndürüyordu, sayıyı değil —\n // ve `if (n === 0) throw new Conflict(...)` guard'ı HİÇ çalışmıyordu,\n // çünkü `[] === 0` yanlıştır. Komşu forwarder'lar (`$findMany`, `$put`)\n // kendi opsiyonlarını zaten iletiyordu; bu biri unutulmuştu.\n $updateMany: (\n table: string,\n where: Record<string, unknown>,\n set: Record<string, unknown>,\n opts?: Parameters<DBOps[\"updateMany\"]>[3],\n ) => raw.updateMany(table, where, set, opts),\n $deleteMany: (table: string, where: Record<string, unknown>) => raw.deleteMany(table, where),\n $count: (table: string, where?: Record<string, unknown>) => raw.count(table, where),\n $search: (table: string, params?: Record<string, unknown>) => raw.search(table, params),\n $similar: (table: string, id: string, params?: Record<string, unknown>) =>\n reco.similar(table, id, params),\n $recommend: (table: string, params: Record<string, unknown>) => reco.recommend(table, params),\n $facets: (table: string, params: { facets: string[] } & Record<string, unknown>) => reco.facets(table, params),\n $claim: (table: string, unique: Record<string, unknown>, extra?: Record<string, unknown>) =>\n reco.claim(table, unique, extra),\n $lockRows: (table: string, ids: readonly string[]) => reco.lockRows(table, ids),\n $advisoryXactLock: (key: string) => reco.advisoryXactLock(key),\n $aggregate: (table: string, q: Parameters<DBOps[\"aggregate\"]>[1]) => raw.aggregate(table, q),\n $insertMany: (\n table: string,\n rows: readonly Record<string, unknown>[],\n opts?: { onConflict: readonly string[]; action?: \"ignore\" | \"update\" },\n ) => raw.insertMany(table, rows, opts),\n $supersede: (table: string, id: string, row: Record<string, unknown>) =>\n raw.supersede(table, id, row),\n } satisfies DollarOps<Omit<DBOps & RecoOps, \"attempt\">>;\n // `ops` DOĞRUDAN verilir, spread edilmez: sayım (`database.test.ts`) nesneyi\n // deklarasyonundan takip ediyor ve bir spread onu kaybettiriyor. Görünmeyen\n // yüzey denetlenemez.\n const base = Object.assign(ops as unknown as Record<string, unknown>, {\n // Both surfaces get it: a savepoint on the service transaction is as useful\n // as one on the request's, and each is bound to its own connection.\n $attempt: <T,>(fn: (tx: DBOps) => Promise<T>) => raw.attempt(fn),\n $transaction<T>(\n fn: (tx: TxPlan) => T extends Promise<unknown> ? never : T,\n opts?: { retry?: number },\n ): Promise<Materialized<T>> {\n // A FRESH builder per call: it holds this transaction's ops and its error\n // slot table, and the runtime serves concurrent requests on one event loop.\n // A shared builder would splice one request's writes into another's plan.\n //\n // Retry de HER DENEMEDE taze bir builder istiyor (FR-037): önceki denemenin\n // op'ları planda kalsaydı ikinci deneme birincinin yazmalarını TEKRAR\n // gönderirdi. Bu yüzden builder döngünün İÇİNDE kuruluyor.\n return withRetry(() => {\n const builder = new TxPlanBuilder();\n return runTxPlan(raw, makeTxPlanHandle(builder), builder, fn) as Promise<\n Materialized<T>\n >;\n }, opts ?? {}) as Promise<Materialized<T>>;\n },\n });\n // ŞEMA ERİŞİMİ BİR PROXY'DİR, çünkü hangi şemaların bildirildiğini yalnız TİP\n // bilir — runtime'da `Database.billing` diye bir üye yoktur, o ada dokunulduğu\n // anda üretilir. `$`'la başlamayan her ad bir ŞEMA adıdır; ayrım tam olarak\n // budur ve tip tarafındaki DollarOps ile aynı kuralı uygular.\n return new Proxy(base, {\n get(target, prop, receiver) {\n // `tables` DOĞRUDAN YÜZEYDE DE public'in takma adı.\n //\n // Plan tutamağı `tx.tables.todos`'u öğretiyor (göç notu da öyle), ama\n // `Database.public.todos` aynı kelimeyi ADI `tables` OLAN BİR ŞEMA sanıp\n // tele `tables.todos` yazıyordu. Tip onu reddettiği için derlenen kodda\n // erişilemezdi — ama `as any` ya da düz JS ile geçen biri sessizce\n // olmayan bir şemaya gidiyordu, ve iki yüzeyin aynı kelimeye zıt cevap\n // vermesi bu run'ın kapattığı sınıfın kendisi (gözcü M-6).\n if (prop === \"tables\") return makeTableProxy(() => reco, \"\");\n if (typeof prop === \"string\" && !prop.startsWith(\"$\") && !(prop in target)) {\n // Nitelikli tablo anahtarının kuralı BURADA TEKRARLANMAZ (FR-058): tek\n // yazıcı `qualifiedTableKey` ve `table-key-single-source.test.ts` ikinci\n // bir yazıcıyı reddediyor. Prefix ondan türetilir — boş tablo adıyla\n // çağrıldığında geriye ya \"\" (public) ya \"<şema>.\" kalır.\n return makeTableProxy(() => reco, qualifiedTableKey(prop, \"\"));\n }\n return Reflect.get(target, prop, receiver);\n },\n }) as unknown as EnvServiceDatabase;\n}\n\n/**\n * `makeTableProxy`'nin plan ikizi: `tables.<name>` yields the\n * table's PLAN operations, recorded into `builder` instead of sent one by one.\n *\n * Same Proxy shape and same reason for the single narrowing — TS cannot infer a\n * mapped type through a Proxy, so the cast names what the trap returns.\n */\nfunction makeTxTablesAccessor(builder: TxPlanBuilder, prefix = \"\"): TxTables {\n const tablesProxy = new Proxy(\n {},\n {\n get(_t, prop: string | symbol) {\n if (typeof prop !== \"string\") return undefined;\n return builder.table(prefix + prop);\n },\n },\n );\n return tablesProxy as TxTables;\n}\n\n/**\n * Plan tutamağı — `Database` ile AYNI şekil: `tx.public.x`, `tx.<şema>.x`, ve\n * geriye dönük `tx.tables.x`.\n *\n * Şema adı tablo adının ÖNÜNE geçiyor (`billing.invoices`), tıpkı doğrudan\n * yüzeyin `makeTypedSurface` proxy'sinin yaptığı gibi — ve motor artık onu\n * `quoteTable` ile İKİ parça hâlinde tırnaklıyor. Bu ikisi olmadan `billing`\n * şemasındaki iki tabloyu tek atomik planda yazmak imkânsızdı.\n *\n * `tables` ve `public` DIŞINDAKİ HER ad şema kabul edilir ve altındaki tablolar\n * `<ad>.<tablo>` diye adlanır. Yanlış bir şema adı TİPTE yakalanıyor\n * (`keyof Schemas`) — `tx.constructor.x` ve `tx.toString.x` dahil, ölçüldü.\n *\n * TİPTEN KAÇAN bir ad için savunma `quoteTable`'ın KAÇIŞIDIR, başka bir şey\n * değil: `runPlanOp` `op.table`'ı doğrulamadan ona veriyor ve `quoteTable`\n * tırnak ikizleyerek tek bir tanımlayıcı üretiyor. Ölçüldü: `a\"; DROP TABLE t; --`\n * → `\"a\"\"; DROP TABLE t; --\"`, yani enjeksiyon değil, `relation does not exist`.\n * (Bu yorum bir zamanlar `validateSchemaIdentifier`'a atıf yapıyordu — o\n * fonksiyon Go tarafında yaşıyor ve BU yolu hiç görmüyor; gözcü yakaladı.)\n */\nfunction makeTxPlanHandle(builder: TxPlanBuilder): TxPlan {\n const publicTables = makeTxTablesAccessor(builder);\n return new Proxy(\n {},\n {\n get(_t, prop: string | symbol) {\n if (typeof prop !== \"string\") return undefined;\n if (prop === \"tables\") return publicTables;\n // ÖNEK TEK YAZICIDAN (FR-058): `qualifiedTableKey`. Bu kuralı burada\n // elle yazmıştım — public'i çıplak bırakıp diğerine nokta ekleyen bir\n // if/return çifti — ve FR-058 kapısı onu GÖRMEDİ, çünkü kapı yalnız\n // ternary arıyordu. (Kural burada KELİMEYLE anlatılıyor, kod biçiminde\n // DEĞİL: kapı metni tarıyor ve bir yorumdaki kopya da onu tetikler.)\n // İkinci bir yazıcı, kapının var olma sebebi olan sınıfın kendisi:\n // `env-gen.ts`'in kendi kopyası bir public FK'yi başka bir şemanın\n // tablosuna etiketlemişti ve hiçbir şey bunu söylememişti.\n // Boş tablo adıyla çağrılınca geriye ya \"\" (public) ya \"<şema>.\" kalır.\n return makeTxTablesAccessor(builder, qualifiedTableKey(prop, \"\"));\n },\n },\n ) as TxPlan;\n}\n\n/**\n * The project's own Postgres (pgx, schema `env_<envId>`).\n *\n * Typed by default: `Database.public.<name>.insert({...})` is typed against\n * the project's generated `palbase-env.d.ts` with NO import and NO generic.\n * The raw string ops (`Database.$query`/`$insert`/`$update`/`$delete`/\n * `$findById`/`$findMany`) are also available for read-only SQL and for a table\n * name only known at runtime — which the typed surface cannot express past\n * seven tables (see `db/tx-union-key.test-d.ts`).\n *\n * RLS is enforced by default (the runtime runs each op as `authenticated` with\n * the verified user's claims). To bypass RLS, call `Database.$asService()` —\n * explicit and greppable — which runs as the `service_role` (BYPASSRLS).\n *\n * @example\n * import { Database } from \"@palbase/backend\";\n *\n * const todo = await Database.public.todos.insert({ title: req.input.title });\n * todo.id; // string ✓\n * const rows = await Database.$query(\"SELECT id FROM todos WHERE done = $1\", [false]);\n * const all = await Database.$asService().public.todos.findMany({}); // RLS bypass\n */\nexport const Database: EnvTypedDatabase = Object.assign(makeTypedSurface(rawDatabase), {\n /**\n * Lazily resolve the runtime's service-role sibling on each call. We do NOT\n * cache it: `rawDatabase.asService()` reads the CURRENT request scope through\n * the runtime proxy, and the per-request runtime injects a service client\n * bound to that request's identity headers — caching would leak one request's\n * sibling into another concurrent request.\n */\n $asService(): EnvServiceDatabase {\n return makeTypedSurface(rawDatabase.asService());\n },\n}) as unknown as EnvTypedDatabase;\n\n/** Firestore-like document client (PalDocs). */\nexport const Documents: PalbaseDocsClient = makeServiceProxy(\"Documents\");\n\n/**\n * `buckets.<name>` — the storage twin of `Database.public.<name>`, and the same\n * mechanism: `config/storage.ts` generates a `Buckets` augmentation into\n * `palbase-env.d.ts`, so a bucket name is a property with no import and no\n * generic, and a typo is a compile error.\n *\n * The intermediate `.buckets` is there for the reason `.tables` is: bucket\n * names must not share a namespace with the client's own methods, or the day\n * somebody declares a bucket called `bucket` the surface breaks.\n */\nexport type EnvBuckets = {\n [K in keyof Buckets]: TypedBucketClient<Buckets[K] extends BucketTypes ? Buckets[K] : BucketTypes>;\n};\n\n/** One bucket, with its declared variant names in the type. */\nexport interface TypedBucketClient<B extends BucketTypes> extends Omit<PalbaseBucketClient, \"getPublicUrl\"> {\n /** The public URL for the object, or for one of THIS bucket's declared\n * renditions. A name the bucket does not declare is a compile error. */\n getPublicUrl(path: string, options?: { variant?: B[\"variants\"] }): string;\n}\n\nfunction makeBucketsAccessor(storage: () => PalbaseStorageClient): EnvBuckets {\n return new Proxy(\n {},\n {\n get(_t, prop: string | symbol) {\n if (typeof prop !== \"string\") return undefined;\n return storage().bucket(prop);\n },\n },\n ) as EnvBuckets;\n}\n\nconst rawStorage: PalbaseStorageClient = makeServiceProxy(\"Storage\");\n\n/**\n * Object storage: buckets, objects, renditions and signed URLs.\n *\n * `Storage.buckets.posts.upload(...)` is the typed path. `Storage.bucket(name)`\n * remains for a name computed at runtime — rare, and it gives up the typing,\n * which is the honest trade rather than a second blessed way to do it.\n */\nexport const Storage: PalbaseStorageClient & { buckets: EnvBuckets } = Object.assign(\n {\n // FORWARDED explicitly, not assigned onto the service proxy.\n //\n // `Object.assign(rawStorage, {buckets})` writes onto the proxy's TARGET, and\n // the proxy's only trap is `get`, which forwards every read to the module\n // client — so the property landed somewhere nothing reads and\n // `Storage.buckets.docs` was `undefined` in a deployed handler. It\n // typechecked, every test passed, and the live call answered\n // \"TypeError: undefined is not an object\". `Database` never had the bug\n // because it builds a plain surface the same way this now does.\n bucket: (name: string) => rawStorage.bucket(name),\n },\n { buckets: makeBucketsAccessor(() => rawStorage) },\n);\n\n/** JSON-typed cache (get/set/incr/getOrSet). */\nexport const Cache: CacheClient = makeServiceProxy(\"Cache\");\n\n\n/**\n * This tenant's secrets — API keys, provider credentials, signing material the\n * BACKEND owns.\n *\n * `await Secrets.get(\"STRIPE_KEY\")` in a handler. There is no `.env` to read\n * and no file to mount: a secret is written through the vault's authenticated\n * API (or the CLI/MCP that calls it), and this is how the deployed code reads\n * it back. A name this tenant has not set answers null — as does every name\n * the STACK holds, because no route returns a platform secret's value at all.\n */\nexport const Secrets: SecretsService = makeServiceProxy(\"Secrets\");\n\n/**\n * Role assignment, as an operator — `Auth.assignRole(userId, \"agent\")`.\n *\n * The half of auth a SERVER owns. Signing in, MFA and device attestation are a\n * person acting on their own account and live on the client SDK; granting a\n * role is the tenant's product doing something to somebody else, which is\n * exactly what a handler is for. It writes with the service-role credential,\n * because an end user who could write their own assignment would make every\n * permission underneath it meaningless.\n *\n * The write is visible to the very next request: authority is read from the\n * table on each call, never carried on a token.\n */\nexport const Auth: PalbaseAuthAdminClient = makeServiceProxy(\"Auth\");\n\n/** Structured logger. */\nexport const Log: Logger = makeServiceProxy(\"Log\");\n\n/** Push / email / SMS / in-app notifications. */\nexport const Notifications: PalbaseNotificationsClient = makeServiceProxy(\"Notifications\");\n\n\n/**\n * The raw runtime Flags client for the current request scope. Carries the\n * default-surface reads + `setOverride` AND the runtime's `asService()` sibling\n * (the br-pod's `buildFlagsClient` returns both). The default `Flags` singleton\n * below forwards reads + `setOverride` through here; `Flags.$asService()`\n * forwards to this client's own `asService()`.\n */\nconst rawFlags: PalbaseFlagsClient = makeServiceProxy(\"Flags\");\n\n/**\n * Feature flags.\n *\n * Mirrors the `Database` / `Database.$asService()` model. The default surface is\n * RLS-equivalent for flags: reads resolve against the CURRENT request user and\n * `Flags.setOverride(key, value)` writes an override for that same signed-in\n * user (no userId argument, no admin power). Cross-user admin writes\n * (`setOverrideForUser`, …) live behind `Flags.$asService()` — explicit and\n * greppable, just like `Database.$asService()`.\n *\n * @example\n * import { Flags } from \"@palbase/backend\";\n *\n * if (await Flags.isEnabled(\"new_checkout\")) { ... } // current user\n * await Flags.setOverride(\"new_checkout\", true); // current user\n * await Flags.$asService().setOverrideForUser(\"u_9\", \"x\", true); // cross-user\n */\n/**\n * The ambient `Flags` singleton's own surface — NOT the raw client's.\n *\n * MEASURED, as a consumer, against the built tarball: annotating the singleton\n * as `PalbaseFlagsClient` ERASED the `$asService` that `Object.assign` adds, so\n * `Flags.$asService()` did not exist for anyone outside this package —\n *\n * TS2551: Property '$asService' does not exist on type 'PalbaseFlagsClient'.\n * Did you mean 'asService'?\n *\n * — while the only path that DID compile (`asService()`) throws by design. The\n * feature was written and unreachable, which is the defect class this surface\n * exists to remove.\n *\n * `Database` never had the problem because its singleton carries its OWN type\n * (`EnvTypedDatabase`) rather than the raw client's (`DBClient`). This is that,\n * for `Flags`. The raw `PalbaseFlagsClient.asService()` is untouched (FR-023) —\n * it is the seam this forwards to.\n */\nexport type PalbaseFlagsAmbient = Omit<PalbaseFlagsClient, \"asService\"> & {\n /** RLS'i aşan, kullanıcılar arası yazma yüzeyi. */\n $asService(): PalbaseFlagsServiceClient;\n /** 31.0.0 öncesinin adı — SESSİZCE çalışmaz, yerini söyleyerek fırlatır. */\n asService(): never;\n};\n\nexport const Flags: PalbaseFlagsAmbient = Object.assign(\n {\n isEnabled(\n flagName: PalbaseFlagKey,\n context?: PalbaseFlagContext,\n ): Promise<PalbaseResult<boolean>> {\n return rawFlags.isEnabled(flagName, context);\n },\n getVariant(\n flagName: PalbaseFlagKey,\n context?: PalbaseFlagContext,\n ): Promise<PalbaseResult<PalbaseFlagVariant>> {\n return rawFlags.getVariant(flagName, context);\n },\n getAll(context?: PalbaseFlagContext): Promise<PalbaseResult<PalbaseFlag[]>> {\n return rawFlags.getAll(context);\n },\n /**\n * Resolve a flag's value, with an optional fallback.\n *\n * FORWARDED as of 2026-08-15. This surface is written out by hand, method\n * by method, and `get` was missing from it — so the client implemented it,\n * thirty assertions covered it, and `Flags.get(\"x\")` was `undefined` in a\n * deployed handler. Exactly the shape of the `Storage.buckets` defect found\n * the same day: a hand-maintained forwarding list is a list somebody has to\n * remember to update.\n */\n get(\n flagName: PalbaseFlagKey,\n defaultOrContext?: PalbaseFlagValue | PalbaseFlagContext,\n maybeContext?: PalbaseFlagContext,\n ): Promise<PalbaseResult<PalbaseFlagValue>> {\n return rawFlags.get(flagName, defaultOrContext, maybeContext);\n },\n setOverride(\n key: PalbaseFlagKey,\n value: PalbaseFlagValue,\n ): Promise<PalbaseResult<PalbaseSetOverrideResult>> {\n return rawFlags.setOverride(key, value);\n },\n },\n {\n /**\n * Lazily resolve the runtime's cross-user sibling on each call. We do NOT\n * cache it: `rawFlags.asService()` reads the CURRENT request scope through\n * the runtime proxy, so caching would leak one request's sibling into\n * another concurrent request. Mirrors `Database.$asService()`.\n */\n $asService(): PalbaseFlagsServiceClient {\n return rawFlags.asService();\n },\n /**\n * The name this member carried before 32.0.0.\n *\n * It does NOT work silently. A retired member that quietly keeps returning\n * is how a rename becomes a mystery: the old call site goes on compiling,\n * the new name never spreads, and the two live side by side until somebody\n * greps for one and misses half the codebase. `Database` made this split\n * first (`$asService`), and this JSDoc promised \"exactly like\n * Database.$asService()\" while the promise went unkept.\n */\n asService(): never {\n throw new Error(\n \"Flags.asService() was renamed to Flags.$asService() in 32.0.0 — system members carry the `$` prefix, like Database.$asService().\",\n );\n },\n },\n);\n\n/**\n * The Realtime broadcast singleton for the current request scope. Backend-side\n * Realtime is BROADCAST-ONLY (a stateless handler can push but not subscribe —\n * `subscribe()` lives on the client SDK's `pb.realtime`). Fire-and-forget:\n * `broadcast` resolves once accepted (or with an `error`), never blocking the\n * handler on subscribers.\n *\n * @example\n * import { Realtime } from \"@palbase/backend\";\n *\n * await Realtime.broadcast(\"room:42\", \"message\", { text, from: user.id });\n */\nexport const Realtime: PalbaseRealtimeClient = makeServiceProxy(\"Realtime\");\n"],"mappings":"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","names":["AsyncLocalStorage","REQUEST_ALS","Symbol","for","__requestALS","g","globalThis","AsyncLocalStorage","runtime","__setRuntime","services","__runWithRuntime","fn","run","REQUIRED_SERVICES","refuseIncompleteBox","missing","filter","k","undefined","length","Error","join","__getRuntime","scoped","getStore","LIFECYCLE","declaredLifecycle","start","shutdown","onStart","name","hook","push","onShutdown","reason","err","message","String","drain","hooks","h","reverse","console","error","__runStartHooks","slot","splice","cause","drained","__resetLifecycleHooks","makeServiceProxy","key","handler","get","_target","prop","receiver","client","value","Reflect","bind","Proxy","makeTableProxy","ops","prefix","_t","insert","data","aggregate","q","insertMany","rows","opts","update","where","id","set","delete","findById","findMany","put","onConflict","updateMany","deleteMany","count","search","params","similar","recommend","facets","supersede","row","claim","unique","extra","rawDatabase","makeTypedSurface","raw","reco","$query","sql","query","$insert","table","$update","$delete","$findById","$findMany","$put","$updateMany","$deleteMany","$count","$search","$similar","$recommend","$facets","$claim","$lockRows","ids","lockRows","$advisoryXactLock","advisoryXactLock","$aggregate","$insertMany","$supersede","base","Object","assign","$attempt","attempt","$transaction","withRetry","builder","TxPlanBuilder","runTxPlan","makeTxPlanHandle","target","startsWith","qualifiedTableKey","makeTxTablesAccessor","tablesProxy","publicTables","Database","$asService","asService","Documents","makeBucketsAccessor","storage","bucket","rawStorage","Storage","buckets","Cache","Secrets","Auth","Log","Notifications","rawFlags","Flags","isEnabled","flagName","context","getVariant","getAll","defaultOrContext","maybeContext","setOverride","Realtime"]}
|
|
@@ -8,15 +8,27 @@ var CODECS = {
|
|
|
8
8
|
// not representable and the author wants `asDecimal()` instead.
|
|
9
9
|
number: {
|
|
10
10
|
fromDb: /* @__PURE__ */ __name((v) => Number(v), "fromDb"),
|
|
11
|
-
|
|
11
|
+
// YAZMA YOLUNDA SESSİZ BOZULMA YOK. `toDb(9007199254740993)` bir zamanlar
|
|
12
|
+
// "9007199254740992" yazıyordu: uygulamanın tuttuğundan BAŞKA bir sayı
|
|
13
|
+
// kolona gidiyordu ve hiçbir şey söylemiyordu. `asNumber()`'ın JSDoc'u
|
|
14
|
+
// "2^53'ün altında güvenli" diyordu ama bunu uygulayan bir şey yoktu.
|
|
15
|
+
toDb: /* @__PURE__ */ __name((v) => {
|
|
16
|
+
if (typeof v === "number" && !Number.isSafeInteger(v)) {
|
|
17
|
+
throw new Error(`asNumber() cannot write ${v}: it is outside the safe integer range and would be stored as a different number. Declare .asDecimal() and hand this column a string.`);
|
|
18
|
+
}
|
|
19
|
+
return String(v);
|
|
20
|
+
}, "toDb"),
|
|
12
21
|
tsType: "number"
|
|
13
22
|
},
|
|
14
23
|
// Exact-precision column → the string Postgres sent. It exists so the
|
|
15
24
|
// DECLARATION is explicit: "this column is a decimal I handle as text",
|
|
16
25
|
// rather than the ABSENCE of a declaration meaning the same thing by default.
|
|
26
|
+
// NULL güvencesi ÇİFTİN KENDİSİNDE. Motor NULL'ı zaten atlıyor (üç ayrı
|
|
27
|
+
// yerde), ama bir değişmez, onu koruyan üç `if`'in DIŞINDA yazılmalı: biri
|
|
28
|
+
// düşerse `String(null)` kolonu "null" dizesine çevirirdi.
|
|
17
29
|
decimal: {
|
|
18
|
-
fromDb: /* @__PURE__ */ __name((v) => String(v), "fromDb"),
|
|
19
|
-
toDb: /* @__PURE__ */ __name((v) => String(v), "toDb"),
|
|
30
|
+
fromDb: /* @__PURE__ */ __name((v) => v === null || v === void 0 ? v : String(v), "fromDb"),
|
|
31
|
+
toDb: /* @__PURE__ */ __name((v) => v === null || v === void 0 ? v : String(v), "toDb"),
|
|
20
32
|
tsType: "string"
|
|
21
33
|
}
|
|
22
34
|
};
|
|
@@ -249,12 +261,22 @@ var ColumnBuilder = class _ColumnBuilder {
|
|
|
249
261
|
if (this._def.transform !== void 0) {
|
|
250
262
|
throw new Error(`.${method}() and .transform() are two ways to say the same thing on one column \u2014 keep .${method}().`);
|
|
251
263
|
}
|
|
252
|
-
this._def.codec
|
|
253
|
-
|
|
264
|
+
if (this._def.codec !== void 0) {
|
|
265
|
+
throw new Error(`column already declares .as${this._def.codec === "number" ? "Number" : "Decimal"}(); a column has ONE codec.`);
|
|
266
|
+
}
|
|
267
|
+
return new _ColumnBuilder(this._def.type, {
|
|
268
|
+
...this._def,
|
|
269
|
+
codec
|
|
270
|
+
});
|
|
254
271
|
}
|
|
255
272
|
transform(fns) {
|
|
256
|
-
this._def.
|
|
257
|
-
|
|
273
|
+
if (this._def.codec !== void 0) {
|
|
274
|
+
throw new Error(`column already declares a named codec; .transform() and .as*() are two ways to say the same thing \u2014 keep the codec.`);
|
|
275
|
+
}
|
|
276
|
+
return new _ColumnBuilder(this._def.type, {
|
|
277
|
+
...this._def,
|
|
278
|
+
transform: fns
|
|
279
|
+
});
|
|
258
280
|
}
|
|
259
281
|
};
|
|
260
282
|
function uuid() {
|
|
@@ -358,4 +380,4 @@ export {
|
|
|
358
380
|
userRef,
|
|
359
381
|
installationRef
|
|
360
382
|
};
|
|
361
|
-
//# sourceMappingURL=chunk-
|
|
383
|
+
//# sourceMappingURL=chunk-XOX6RFPZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/db/columns.ts"],"sourcesContent":["/** On delete action for foreign key references. */\nexport type OnDeleteAction = 'cascade' | 'set null' | 'restrict' | 'no action';\n\n/**\n * The ON DELETE actions permitted on a foreign key to the built-in auth users\n * (`auth.users`). Both let a user's rows be removed (`cascade`) or detached\n * (`set null`) when the account is erased; `restrict` / `no action` would BLOCK\n * erasure and are therefore excluded. This is the CLIENT-SIDE mirror of the\n * server's auth-FK deletion policy — the server (validateAuthUserFK) is the real\n * boundary, this narrows the type so the common mistake is caught at compile time.\n */\nexport type AuthUserOnDelete = Extract<OnDeleteAction, 'cascade' | 'set null'>;\n\n/** Column type identifiers. */\nexport type ColumnType =\n | 'uuid'\n | 'text'\n | 'integer'\n | 'bigint'\n | 'numeric'\n | 'boolean'\n | 'timestamp'\n | 'jsonb'\n | 'enum'\n | 'vector';\n\n/** Base column definition shared by all column types. */\nexport interface ColumnDef {\n type: ColumnType;\n nullable: boolean;\n primaryKey: boolean;\n defaultValue?: unknown;\n defaultRandom?: boolean;\n defaultNow?: boolean;\n references?: { table: string; column: string };\n /** Pending FK target, resolved by `defineSchema` once every binding exists. */\n referencesThunk?: () => ColumnBuilder;\n /** FK onto THIS table — no thunk needed, the target is the declaring table. */\n selfRefColumn?: string;\n /** This column OWNS the row: erasure, RLS and the `owner` relation read it. */\n owns?: true;\n /** Explicit name for the FORWARD relation (child → parent), when the one\n * derived from the column would be ambiguous or unclear. */\n refAs?: string;\n /**\n * Explicit name for the REVERSE relation (parent → children).\n *\n * Separate from {@link refAs} because the two directions are different facts:\n * the forward name describes the parent this row points at (`author`), the\n * reverse one describes the rows hanging off the parent (`posts`). One option\n * naming both made an ordinary schema undeclarable — `posts.author_id` and\n * `comments.author_id` both named `{ as: \"author\" }` collided on `users`, and\n * the refusal asked for the `{ as }` they had both already written.\n */\n reverseAs?: string;\n /** The table this column belongs to; set by `defineTable`. */\n ownerTable?: { name: string; columns: Record<string, ColumnBuilder> };\n /**\n * The name this column used to have. A diff cannot tell a rename from a drop and\n * an add — both leave one name gone and another present — so the intent has to be\n * declared. Without it, renaming a column loses its data.\n */\n renamedFrom?: string;\n /**\n * This release's PROMISE that it does not reference this column — neither\n * reads it nor writes it, and never names it in a filter, a sort or a SET.\n *\n * The contraction gate reads it and nothing else does: dropping a column is\n * refused while the RUNNING release's declaration still lacks this mark, so\n * removing a column is two deploys — mark it, ship, then drop it.\n *\n * The word is `ignored` and not `deprecated` deliberately: RFC 9745 defines\n * deprecation as changing NO behaviour, and this changes what a deploy will\n * accept. Rails calls the same thing `ignored_columns`.\n */\n ignored?: boolean;\n onDeleteAction?: OnDeleteAction;\n /** FR-044: yalnız AÇIKÇA `false` bildirilince var. Türev FK index'ini kapatır. */\n index?: boolean;\n /** FR-049: kolon `increment()` ile güncelleniyor. Plan HOT çakışmasını uyarır. */\n counter?: boolean;\n enumName?: string;\n enumValues?: string[];\n unique?: boolean;\n /**\n * The value is written by the DATABASE — a trigger, a rule, an identity — not by\n * the author and not by a DEFAULT this schema declares. It makes the column\n * optional on INSERT without putting a DEFAULT in the DDL.\n *\n * Before this existed the only way to keep a trigger-filled column off the\n * INSERT type was to give it a fake `default()`: a value the schema claimed to\n * write and the trigger immediately overwrote. That made the schema lie about\n * its own data.\n */\n dbAssigned?: boolean;\n /** vector(n): the declared dimension count — part of the TYPE (typmod), read\n * by the wire serializer and the deploy's auto-index (FR-001). */\n dimensions?: number;\n /**\n * How the stored value is projected in and out of this process (FR-009).\n *\n * NOT part of the DDL: the column's Postgres type is unchanged and this pair\n * is never serialized into a migration. It exists so the row surface can hand\n * back the type the application actually works with.\n */\n /**\n * The named codec this column declares (FR-001). Unlike `transform`, the NAME\n * survives into the runtime object, so `makeEnvDts` can emit the matching\n * TypeScript type instead of falling back to the storage type. Not part of\n * the DDL: the column's Postgres type is unchanged.\n */\n codec?: CodecName;\n transform?: ColumnTransform;\n}\n\n/**\n * The read/write pair a column may declare (FR-009).\n *\n * `fromDb` takes whatever the driver produced for this column and returns the\n * value the application sees; `toDb` is its inverse on the way out. Kept\n * deliberately unexported — a column declares one inline, nobody needs to name\n * the shape.\n */\ninterface ColumnTransform<T = unknown> {\n fromDb: (value: unknown) => T;\n toDb: (value: T) => unknown;\n}\n\n/**\n * The closed set of named column codecs (FR-001).\n *\n * WHY A CLOSED SET rather than `.transform<T>()`. A transform's target type is\n * a TYPE parameter — erased at runtime — so the generator that reads the\n * bundled schema object cannot learn it. `palbase-env.d.ts` therefore said\n * `string` while the engine handed the application a number, and a consumer\n * project wrote a hand-rolled codec module plus 141 call sites to compensate.\n *\n * A codec is NAMED instead of typed: the name lands in `_def.codec`, travels in\n * the runtime object to every reader, and BOTH the conversion and the emitted\n * TypeScript type are derived from it. One declaration, one truth, and no way\n * for the two to disagree.\n */\nexport type CodecName = 'number' | 'decimal';\n\nexport const CODECS: Record<\n CodecName,\n { fromDb: (value: unknown) => unknown; toDb: (value: unknown) => unknown; tsType: 'number' | 'string' }\n> = {\n // Exact-precision column → JS number. Safe below 2^53; a value above it is\n // not representable and the author wants `asDecimal()` instead.\n number: {\n fromDb: (v) => Number(v),\n // YAZMA YOLUNDA SESSİZ BOZULMA YOK. `toDb(9007199254740993)` bir zamanlar\n // \"9007199254740992\" yazıyordu: uygulamanın tuttuğundan BAŞKA bir sayı\n // kolona gidiyordu ve hiçbir şey söylemiyordu. `asNumber()`'ın JSDoc'u\n // \"2^53'ün altında güvenli\" diyordu ama bunu uygulayan bir şey yoktu.\n toDb: (v) => {\n if (typeof v === 'number' && !Number.isSafeInteger(v)) {\n throw new Error(\n `asNumber() cannot write ${v}: it is outside the safe integer range and would be stored as a different number. Declare .asDecimal() and hand this column a string.`,\n );\n }\n return String(v);\n },\n tsType: 'number',\n },\n // Exact-precision column → the string Postgres sent. It exists so the\n // DECLARATION is explicit: \"this column is a decimal I handle as text\",\n // rather than the ABSENCE of a declaration meaning the same thing by default.\n // NULL güvencesi ÇİFTİN KENDİSİNDE. Motor NULL'ı zaten atlıyor (üç ayrı\n // yerde), ama bir değişmez, onu koruyan üç `if`'in DIŞINDA yazılmalı: biri\n // düşerse `String(null)` kolonu \"null\" dizesine çevirirdi.\n decimal: {\n fromDb: (v) => (v === null || v === undefined ? v : String(v)),\n toDb: (v) => (v === null || v === undefined ? v : String(v)),\n tsType: 'string',\n },\n};\n\n/** FR-002: a vector column cannot carry keys/defaults/references — the modifier\n * is named in the error so the author fixes the right line. */\nfunction refuseOnVector(def: ColumnDef, modifier: string): void {\n if (def.type === 'vector') {\n throw new Error(`vector column: .${modifier}() is not supported (FR-002 — allowed: nullable()/notNull())`);\n }\n}\n\n// Phantom brand symbols — never have runtime values; exist only to force\n// TypeScript's structural type system to distinguish ColumnBuilder instances\n// with different type-param combinations. Without these, TS sees all\n// ColumnBuilder<K,...> as structurally identical and the first branch of\n// ColValue matches everything.\ndeclare const __colKind: unique symbol;\ndeclare const __colNullable: unique symbol;\ndeclare const __colHasDefault: unique symbol;\ndeclare const __colEnumValues: unique symbol;\ndeclare const __colPayload: unique symbol;\ndeclare const __colTransform: unique symbol;\n\n/**\n * Fluent column builder with phantom type params:\n * K — ColumnType literal (e.g. \"text\", \"integer\")\n * N — boolean: true when nullable() has been called last (false = NOT NULL)\n * D — boolean: true when a default has been set\n * E — enum value union (never for non-enum columns)\n * P — jsonb payload shape (unknown unless jsonb<T>() supplied one)\n * T — transform target type (`never` when the column declares no transform;\n * `never` is the sentinel because it is the only type that survives\n * `[T] extends [never]` and never collides with a real target type)\n *\n * All six params have defaults so bare `ColumnBuilder` (no args) still\n * satisfies `Record<string, ColumnBuilder>` in schema.ts without modification.\n *\n * The six `declare readonly` brand fields carry the phantom types into the\n * structural shape so that conditional types like ColValue<C> can discriminate\n * on K without requiring runtime values on those fields.\n */\nexport class ColumnBuilder<\n K extends ColumnType = ColumnType,\n N extends boolean = boolean,\n D extends boolean = boolean,\n E = unknown,\n P = unknown,\n // `unknown`, not `never`: the schema's own constraint is a BARE\n // `ColumnBuilder`, whose T lands on this default. With `never` there, a\n // column that declares `.transform<number>()` is not assignable to the\n // constraint at all — `number` does not extend `never` — so a transform\n // could not appear in a schema and the whole table's `RowShape` collapsed.\n // Measured: TS2322 on `defineSchema`.\n T = unknown,\n> {\n // These fields exist only in the type layer (declared, never initialised at\n // runtime — TypeScript allows declared class members without an initializer\n // in strict mode as long as they're never read at runtime).\n declare readonly [__colKind]: K;\n declare readonly [__colNullable]: N;\n declare readonly [__colHasDefault]: D;\n declare readonly [__colEnumValues]: E;\n declare readonly [__colPayload]: P;\n declare readonly [__colTransform]: T;\n\n readonly _def: ColumnDef;\n\n constructor(type: K, existingDef?: ColumnDef) {\n this._def = existingDef ?? {\n type,\n nullable: false,\n primaryKey: false,\n };\n }\n\n /** Mark this column as the primary key. */\n primaryKey(): ColumnBuilder<K, N, D, E, P, T> {\n refuseOnVector(this._def, 'primaryKey');\n this._def.primaryKey = true;\n return new ColumnBuilder<K, N, D, E, P, T>(this._def.type as K, this._def);\n }\n\n /** Mark this column as NOT NULL (default). */\n notNull(): ColumnBuilder<K, false, D, E, P, T> {\n this._def.nullable = false;\n return new ColumnBuilder<K, false, D, E, P, T>(this._def.type as K, this._def);\n }\n\n /** Allow NULL values. */\n nullable(): ColumnBuilder<K, true, D, E, P, T> {\n this._def.nullable = true;\n return new ColumnBuilder<K, true, D, E, P, T>(this._def.type as K, this._def);\n }\n\n /** Set a default value. */\n default(value: unknown): ColumnBuilder<K, N, true, E, P, T> {\n refuseOnVector(this._def, 'default');\n this._def.defaultValue = value;\n return new ColumnBuilder<K, N, true, E, P, T>(this._def.type as K, this._def);\n }\n\n /** UUID: generate a random default (gen_random_uuid()). */\n defaultRandom(): ColumnBuilder<K, N, true, E, P, T> {\n refuseOnVector(this._def, 'defaultRandom');\n this._def.defaultRandom = true;\n return new ColumnBuilder<K, N, true, E, P, T>(this._def.type as K, this._def);\n }\n\n /** Timestamp: default to now(). */\n defaultNow(): ColumnBuilder<K, N, true, E, P, T> {\n refuseOnVector(this._def, 'defaultNow');\n this._def.defaultNow = true;\n return new ColumnBuilder<K, N, true, E, P, T>(this._def.type as K, this._def);\n }\n\n /**\n * The DATABASE assigns this column's value — a trigger, a rule, an identity.\n *\n * The column becomes optional on INSERT (the author has nothing to send) while\n * the DDL stays free of a DEFAULT this schema would not honour. It is NOT\n * `default()`: that declares a value the schema promises to write.\n *\n * Naming: deliberately not `generated()`. Postgres has GENERATED columns and\n * they are a different thing; borrowing the word would send a reader — or a\n * model writing a schema — to the wrong feature.\n */\n dbAssigned(): ColumnBuilder<K, N, true, E, P, T> {\n this._def.dbAssigned = true;\n return new ColumnBuilder<K, N, true, E, P, T>(this._def.type as K, this._def);\n }\n\n /** Add a foreign key reference. */\n /**\n * Declares that this column used to be called `previous`.\n *\n * A schema diff sees one name gone and another present; it cannot know whether\n * you renamed a column or dropped one and added another, and the two are very\n * different — the second loses every value. Saying so here turns the plan into\n * `ALTER TABLE … RENAME COLUMN` instead.\n *\n * Once the rename has been applied the annotation is inert (the old name is no\n * longer there to rename), so it can be deleted at your leisure.\n */\n renamedFrom(previous: string): ColumnBuilder<K, N, D, E, P, T> {\n this._def.renamedFrom = previous;\n return this as unknown as ColumnBuilder<K, N, D, E, P, T>;\n }\n\n /**\n * See {@link ColumnDef.ignored}.\n *\n * COPIES the def rather than mutating it. The constructor takes an existing\n * def BY REFERENCE, so every builder derived from another shares one object —\n * `const a = slug.unique()` leaves `a._def === slug._def`. An in-place\n * `ignored = true` therefore marks every column sharing that def, including\n * one another table actively reads, and the gate would let THAT column be\n * dropped. Measured before this copy existed.\n *\n * The aliasing is older than this method and other fields leak through it too.\n * The reason this one cannot wait: every other leak produces a VISIBLE schema\n * difference — the plan shows it, the DDL shows it. This one is invisible by\n * design (no DDL, no diff, no plan line), so its only effect is to disarm a\n * safety gate in silence.\n */\n ignored(): ColumnBuilder<K, N, D, E, P, T> {\n return new ColumnBuilder<K, N, D, E, P, T>(this._def.type as K, {\n ...this._def,\n ignored: true,\n });\n }\n\n /**\n * Foreign key onto another table's column.\n *\n * The target is a THUNK, not a direct reference. In a cycle (`x → y`, `y → x`)\n * the second table does not exist yet when the first is built; a direct\n * reference makes TypeScript chase its own tail (TS7022 — measured, and making\n * the return type independent of the target does NOT help). The thunk is\n * invoked in `defineSchema`, where every binding exists and every table\n * already knows its name.\n *\n * In a cycle, ONE side needs an explicit return type:\n * `references((): AnyColumn => y.id)`. One side is enough — measured.\n * For a self-reference use `selfReferences(column)`: no thunk, no annotation.\n *\n * `as` names the FORWARD relation (`author_id` → `author` by default);\n * `reverseAs` names the REVERSE one on the parent, whose default is this\n * table's own name (`users.posts`). Two foreign keys from one table onto one\n * parent therefore need a `reverseAs` on at least one of them — the reverse\n * names would otherwise both be this table's name.\n */\n references(\n target: () => AnyColumn,\n opts?: {\n as?: string;\n reverseAs?: string;\n onDelete?: OnDeleteAction;\n /**\n * Bu FK kolonu için TÜREVİ index üretilsin mi (FR-044). Varsayılan açık.\n *\n * Postgres bir foreign key'i otomatik indekslemez — yalnız hedef\n * taraftaki unique kısıt vardır. Bedeli FK üzerinden her JOIN'de ve her\n * `ON DELETE CASCADE`'de ödenir: bir parent silinirken child tablo tam\n * taranır.\n *\n * `false` demek gerçek bir ihtiyaç, nezaket değil (D-028): FK kolonu aynı\n * zamanda SIK GÜNCELLENEN bir kolonsa index HOT güncellemeyi kırar — ve\n * HOT, DEĞİŞEN kolon indeksliyse kırılır, tablo indeksli diye değil.\n */\n index?: boolean;\n },\n ): ColumnBuilder<K, N, D, E, P, T> {\n refuseOnVector(this._def, 'references');\n if (typeof target !== 'function') {\n // Fail where the mistake is. Storing a non-thunk here surfaces three\n // layers later as \"def.referencesThunk is not a function\", inside\n // defineSchema, naming neither the column nor the call that was wrong.\n throw new Error(\n `references(...) takes a callback: write references(() => otherTable.column). ` +\n `The two-string form references(\"table\", \"column\") is gone — a string cannot be type-checked ` +\n `and cannot point at a table that does not exist yet.`,\n );\n }\n this._def.referencesThunk = target;\n if (opts?.as !== undefined) this._def.refAs = opts.as;\n if (opts?.reverseAs !== undefined) this._def.reverseAs = opts.reverseAs;\n if (opts?.onDelete !== undefined) this._def.onDeleteAction = opts.onDelete;\n // Yalnız AÇIKÇA false bildirilince taşınıyor: `undefined` \"bildirilmedi,\n // türet\" demek ve wire'da hiç görünmemeli (bayt-aynılık).\n if (opts?.index === false) this._def.index = false;\n return new ColumnBuilder<K, N, D, E, P, T>(this._def.type as K, this._def);\n }\n\n /**\n * Bu kolon `increment()` / `decrement()` ile güncelleniyor (FR-049).\n *\n * Deploy kontrolcü kodunu OKUMAZ, o yüzden bildirimin söylemesi gerekiyor —\n * ve söylediği anda plan bir şeyi görebiliyor: aynı kolon hem sayaç hem\n * indeksliyse her güncelleme HOT'u kaybeder.\n *\n * ZİNCİR (D-028+D-030): HOT kaybı → ölü tuple → autovacuum yükü → ve\n * autovacuum worker'ları KÜME GENELİNDE bir kaynak, yani bedeli başka\n * kiracıların tabloları da öder.\n *\n * Sektörde çare \"dokümana uyarı yaz\"dır, çünkü index'i ekleyen kişi\n * `increment()`'i yazan kişi değildir. Bu bildirimde ikisi de YAN YANA\n * duruyor.\n *\n * Uyarı, HATA DEĞİL: sayaç kolonunu indekslemek bazen doğru karardır.\n */\n counter(): ColumnBuilder<K, N, D, E, P, T> {\n this._def.counter = true;\n return new ColumnBuilder<K, N, D, E, P, T>(this._def.type as K, this._def);\n }\n\n /**\n * Foreign key onto THIS table (`parent_id → id`) — category trees, comment\n * replies, org charts.\n *\n * No thunk and no type annotation: the target table is the one being declared,\n * so there is nothing to defer and nothing for TypeScript to chase in a circle.\n * Drizzle forces an explicit `(): AnyPgColumn =>` here because its reference\n * always goes through a callback; measured, we do not need one.\n */\n selfReferences(\n column: string,\n opts?: { as?: string; onDelete?: OnDeleteAction },\n ): ColumnBuilder<K, N, D, E, P, T> {\n refuseOnVector(this._def, 'selfReferences');\n this._def.selfRefColumn = column;\n if (opts?.as !== undefined) this._def.refAs = opts.as;\n if (opts?.onDelete !== undefined) this._def.onDeleteAction = opts.onDelete;\n return new ColumnBuilder<K, N, D, E, P, T>(this._def.type as K, this._def);\n }\n\n\n\n /** Set the ON DELETE action for a foreign key reference. */\n onDelete(action: OnDeleteAction): ColumnBuilder<K, N, D, E, P, T> {\n this._def.onDeleteAction = action;\n return new ColumnBuilder<K, N, D, E, P, T>(this._def.type as K, this._def);\n }\n\n /** Add a single-column UNIQUE constraint. */\n unique(): ColumnBuilder<K, N, D, E, P, T> {\n refuseOnVector(this._def, 'unique');\n this._def.unique = true;\n return new ColumnBuilder<K, N, D, E, P, T>(this._def.type as K, this._def);\n }\n\n /**\n * Declare how this column's value is projected in and out of the process.\n *\n * The DDL does not move: `numeric` stays `numeric`, and the driver still hands\n * back what Postgres sent. What changes is the type the row surface exposes —\n * it becomes `Target`:\n *\n * amount: numeric().transform<number>({ fromDb: Number, toDb: String })\n *\n * `numeric` surfacing as `string` is CORRECT (a JS number cannot hold\n * arbitrary precision), and that is exactly why this exists: application code\n * that does arithmetic on the column otherwise rewrites the same\n * `Number(row.amount)` / `String(x)` pair in every controller that touches it,\n * and each rewrite is a place the two directions can drift apart.\n *\n * A transform is a PROJECTION, never a constraint: it lives only in this\n * process, so it can neither validate nor migrate what is stored.\n */\n /**\n * Surface this exact-precision column as a JS `number` (FR-001).\n *\n * `numeric`/`bigint` arrive as strings because a JS number cannot hold their\n * full range — correct, and exactly why this exists: application code that\n * does arithmetic on the column otherwise rewrites the same `Number(row.x)` /\n * `String(v)` pair in every caller, and each rewrite is a place the two\n * directions can drift apart.\n */\n asNumber(): ColumnBuilder<K, N, D, E, P, number> {\n return this.withCodec<number>('number', 'asNumber');\n }\n\n /**\n * Surface this exact-precision column as a `string` — the value Postgres\n * sent, DECLARED rather than defaulted (FR-001).\n */\n asDecimal(): ColumnBuilder<K, N, D, E, P, string> {\n return this.withCodec<string>('decimal', 'asDecimal');\n }\n\n private withCodec<Target>(codec: CodecName, method: string): ColumnBuilder<K, N, D, E, P, Target> {\n if (this._def.type !== 'bigint' && this._def.type !== 'numeric') {\n throw new Error(\n `${this._def.type} column: .${method}() is only available on bigint()/numeric() — ` +\n `those are the exact-precision types that surface as string.`,\n );\n }\n if (this._def.transform !== undefined) {\n throw new Error(\n `.${method}() and .transform() are two ways to say the same thing on one column — keep .${method}().`,\n );\n }\n if (this._def.codec !== undefined) {\n throw new Error(\n `column already declares .as${this._def.codec === 'number' ? 'Number' : 'Decimal'}(); a column has ONE codec.`,\n );\n }\n // KOPYA, MUTASYON DEĞİL — `ignored()` gibi.\n //\n // `this._def.codec = codec` yazmak, ORTAK bir temelden çatallanan iki\n // kolonun ikisine de SON codec'i verirdi. Ölçüldü:\n // const money = numeric().notNull();\n // money.asNumber(); money.asDecimal();\n // → ikisinin de _def.codec === 'decimal', ve AYNI nesne.\n // Birincinin TİPİ `number`, üretecin bastığı tip `string` olurdu — yani bu\n // özelliğin kapatmak için var olduğu ayrışmanın ta kendisi.\n return new ColumnBuilder<K, N, D, E, P, Target>(this._def.type as K, {\n ...this._def,\n codec,\n });\n }\n\n transform<Target>(fns: ColumnTransform<Target>): ColumnBuilder<K, N, D, E, P, Target> {\n // AYNA KONTROLÜ. `asNumber()` mevcut bir transform'u reddediyor; burası da\n // mevcut bir codec'i reddetmeli, yoksa kapı TEK YÖNLÜ olur:\n // `numeric().asNumber().transform<Date>(…)` üç ayrı cevap üretirdi —\n // builder tipi `Date`, üretilen tip `number`, motor `Number()` uygular —\n // ve hiçbir şey adını söyleyerek reddetmezdi.\n if (this._def.codec !== undefined) {\n throw new Error(\n `column already declares a named codec; .transform() and .as*() are two ways to say the same thing — keep the codec.`,\n );\n }\n // The cast is the variance, not a shortcut: `toDb` takes `Target`, and a\n // `ColumnTransform<unknown>` would have to accept anything. The stored pair\n // is only ever called with this column's own values.\n return new ColumnBuilder<K, N, D, E, P, Target>(this._def.type as K, {\n ...this._def,\n transform: fns as ColumnTransform,\n });\n }\n}\n\n// ---------------------------------------------------------------------------\n// Type extractors — imported by Task 2 to derive insert/row shapes.\n// ---------------------------------------------------------------------------\n\n/**\n * Extracts the TypeScript value type for a column, respecting nullability.\n * - \"uuid\" | \"text\" | \"timestamp\" | \"bigint\" | \"numeric\" → string (or string | null when N = true)\n * Note: bigint/numeric surface as string — JS number loses precision past 2^53,\n * and pgx/PostgREST serialize int8/numeric as strings. App code uses\n * BigInt(row.amount) for bigint, or a decimal lib for numeric.\n * - \"integer\" → number\n * - \"boolean\" → boolean\n * - \"jsonb\" → P (the dev-supplied payload shape from jsonb<T>(), else unknown)\n * - \"enum\" → E (the union of literal values)\n *\n * A declared `.transform<T>()` OVERRIDES the table above: the column then\n * surfaces as T (or T | null when nullable), because that is the value the\n * application is handed. Nullability is still the column's, not the\n * transform's — `fromDb` is not called for a NULL.\n */\nexport type ColValue<C> =\n C extends ColumnBuilder<ColumnType, infer N, boolean, unknown, unknown, infer T>\n ? [unknown] extends [T]\n ? ColStoredValue<C>\n : N extends true\n ? T | null\n : T\n : never;\n\n/** The value as the DATABASE hands it over — the branch table above, before any\n * transform. This is what a column's `fromDb` receives. */\ntype ColStoredValue<C> =\n C extends ColumnBuilder<'uuid' | 'text' | 'timestamp' | 'bigint' | 'numeric', infer N, infer _D, infer _E, infer _P>\n ? N extends true\n ? string | null\n : string\n : C extends ColumnBuilder<'integer', infer N, infer _D, infer _E, infer _P>\n ? N extends true\n ? number | null\n : number\n : C extends ColumnBuilder<'boolean', infer N, infer _D, infer _E, infer _P>\n ? N extends true\n ? boolean | null\n : boolean\n : C extends ColumnBuilder<'jsonb', infer N, infer _D, infer _E, infer P>\n ? N extends true\n ? P | null\n : P\n : C extends ColumnBuilder<'vector', infer N, infer _D, infer _E, infer _P>\n ? N extends true\n ? number[] | null\n : number[]\n : C extends ColumnBuilder<'enum', infer N, infer _D, infer E, infer _P>\n ? N extends true\n ? E | null\n : E\n : never;\n\n/**\n * True when a column is optional on INSERT:\n * - nullable columns (N = true) — the DB allows NULL so the field may be omitted\n * - columns with a default (D = true) — the DB fills in the value when absent\n */\nexport type ColIsOptionalOnInsert<C> =\n C extends ColumnBuilder<ColumnType, true, boolean, unknown, unknown, unknown>\n ? true\n : C extends ColumnBuilder<ColumnType, boolean, true, unknown, unknown, unknown>\n ? true\n : false;\n\n// ---------------------------------------------------------------------------\n// Factory functions\n// ---------------------------------------------------------------------------\n\n/** Create a UUID column. */\nexport function uuid(): ColumnBuilder<'uuid', false, false, never> {\n return new ColumnBuilder('uuid');\n}\n\n/** Create a TEXT column. */\nexport function text(): ColumnBuilder<'text', false, false, never> {\n return new ColumnBuilder('text');\n}\n\n/** Create an INTEGER column. Emits int4 (max ~2.1B). */\nexport function integer(): ColumnBuilder<'integer', false, false, never> {\n return new ColumnBuilder('integer');\n}\n\n/**\n * Create a BIGINT column (Postgres int8, max ~9.2×10^18).\n * Surfaces as `string` in row/insert types — JS number loses precision past 2^53\n * and pgx/PostgREST serialize int8 as a JSON string. Use BigInt(row.column) in app code.\n */\nexport function bigint(): ColumnBuilder<'bigint', false, false, never> {\n return new ColumnBuilder('bigint');\n}\n\n/**\n * Create a NUMERIC column (Postgres `numeric`/`decimal`, arbitrary precision).\n * For exact fractional values (money with cents as a decimal, rates, weights)\n * where int4/int8 don't fit. Surfaces as `string` in row/insert types — JS\n * number can't hold arbitrary-precision decimals without rounding, and\n * pgx/PostgREST serialize numeric as a JSON string. Parse with a decimal lib\n * (or BigInt for scaled integers) in app code.\n */\nexport function numeric(): ColumnBuilder<'numeric', false, false, never> {\n return new ColumnBuilder('numeric');\n}\n\n/** Create a BOOLEAN column. */\nexport function boolean(): ColumnBuilder<'boolean', false, false, never> {\n return new ColumnBuilder('boolean');\n}\n\n/** Create a TIMESTAMP column. */\nexport function timestamp(): ColumnBuilder<'timestamp', false, false, never> {\n return new ColumnBuilder('timestamp');\n}\n\n/**\n * Create a JSONB column. Pass a payload type to make the generated row/insert\n * type concrete instead of `unknown`:\n *\n * tags: jsonb<string[]>() // row.tags: string[]\n * meta: jsonb<{ tier: string }>() // row.meta: { tier: string }\n * raw: jsonb() // row.raw: unknown (back-compat)\n *\n * The runtime accepts a plain JS object/array directly (no JSON.stringify); the\n * generic only refines the TYPE the env codegen emits.\n */\nexport function jsonb<T = unknown>(): ColumnBuilder<'jsonb', false, false, never, T> {\n return new ColumnBuilder('jsonb');\n}\n\n/**\n * Create an ENUM column.\n * @param name The PostgreSQL enum type name (used in DDL).\n * @param values A readonly tuple of valid string values — kept `const` so the\n * union `V[number]` is as narrow as possible.\n */\nexport function enumType<const V extends readonly string[]>(\n name: string,\n values: V,\n): ColumnBuilder<'enum', false, false, V[number]> {\n const builder = new ColumnBuilder<'enum', false, false, V[number]>('enum');\n builder._def.enumName = name;\n builder._def.enumValues = [...values];\n return builder;\n}\n\n/** vector(n) — pgvector kolonu. n TİPİN parçasıdır (typmod) ve [1, 2000] —\n * 2000 = pgvector'ün HNSW-indekslenebilir tavanı; auto-index bu beyanla bağlı\n * (spec FR-001, D-3). */\nexport function vector(dimensions: number): ColumnBuilder<'vector', false, false, unknown, number[]> {\n if (!Number.isInteger(dimensions) || dimensions < 1 || dimensions > 2000) {\n throw new Error(`vector(): dimensions must be an integer in [1, 2000], got ${String(dimensions)}`);\n }\n const b = new ColumnBuilder('vector') as ColumnBuilder<'vector', false, false, unknown, number[]>;\n (b._def as { dimensions?: number }).dimensions = dimensions;\n return b;\n}\n\n/**\n * Any column, whatever its type parameters.\n *\n * Exported so a cycle can be broken from ONE side:\n * `references((): AnyColumn => y.id)`.\n */\nexport type AnyColumn = ColumnBuilder;\n\n/**\n * The column that OWNS this row: a `text` FK onto `auth.users(id)`, NOT NULL,\n * ON DELETE CASCADE.\n *\n * Ownership drives account erasure, so cascade is the only correct action and\n * takes no argument. The referencing column must be `text` (palauth ids are\n * `usr_<uuid>`) and NOT NULL — both are implied here rather than left to the\n * caller, so three rules the type could not express before become UNWRITABLE.\n *\n * At most ONE per table. The old shape let several columns reference\n * `auth.users` and picked the FIRST IN DECLARATION ORDER as the owner — moving a\n * `created_by` above a `user_id` silently changed which rows an account deletion\n * took with it. A second one is now rejected at push.\n *\n * For a column that merely POINTS at a user without owning the row\n * (`created_by`, `edited_by`), use `userRef({ onDelete })`.\n */\nexport function ownedByUser(): ColumnBuilder<'text', false, false, never> {\n const b = new ColumnBuilder<'text', false, false, never>('text');\n b._def.nullable = false;\n b._def.references = { table: 'auth.users', column: 'id' };\n b._def.onDeleteAction = 'cascade';\n b._def.owns = true;\n return b;\n}\n\n/**\n * A plain FK onto `auth.users(id)` that does NOT own the row.\n *\n * `created_by` / `edited_by`: deleting that user must not delete the row. ON\n * DELETE is required and limited to `cascade | set null` so an erasure request\n * is never blocked by a lingering FK; `set null` needs a nullable column.\n */\nexport function userRef(opts: { onDelete: AuthUserOnDelete; as?: string }): ColumnBuilder<'text', boolean, false, never> {\n const b = new ColumnBuilder<'text', boolean, false, never>('text');\n b._def.references = { table: 'auth.users', column: 'id' };\n b._def.onDeleteAction = opts.onDelete;\n if (opts.as !== undefined) b._def.refAs = opts.as;\n return b;\n}\n\n/**\n * A plain FK onto `auth.installations(id)` — the app-scoped verified-device\n * anchor.\n *\n * An installation is an APP INSTALL, not a user: this is NOT ownership. A\n * user-owned row still needs its own `ownedByUser()` so account erasure removes\n * it; an installation reference alone does not tie a row to a user's deletion.\n */\nexport function installationRef(opts: { onDelete: AuthUserOnDelete; as?: string }): ColumnBuilder<'text', boolean, false, never> {\n const b = new ColumnBuilder<'text', boolean, false, never>('text');\n b._def.references = { table: 'auth.installations', column: 'id' };\n b._def.onDeleteAction = opts.onDelete;\n if (opts.as !== undefined) b._def.refAs = opts.as;\n return b;\n}\n"],"mappings":"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","names":["CODECS","number","fromDb","v","Number","toDb","isSafeInteger","Error","String","tsType","decimal","undefined","refuseOnVector","def","modifier","type","ColumnBuilder","_def","existingDef","nullable","primaryKey","notNull","default","value","defaultValue","defaultRandom","defaultNow","dbAssigned","renamedFrom","previous","ignored","references","target","opts","referencesThunk","as","refAs","reverseAs","onDelete","onDeleteAction","index","counter","selfReferences","column","selfRefColumn","action","unique","asNumber","withCodec","asDecimal","codec","method","transform","fns","uuid","text","integer","bigint","numeric","boolean","timestamp","jsonb","enumType","name","values","builder","enumName","enumValues","vector","dimensions","isInteger","b","ownedByUser","table","owns","userRef","installationRef"]}
|
package/dist/db/index.cjs
CHANGED
|
@@ -803,12 +803,22 @@ var ColumnBuilder = class _ColumnBuilder {
|
|
|
803
803
|
if (this._def.transform !== void 0) {
|
|
804
804
|
throw new Error(`.${method}() and .transform() are two ways to say the same thing on one column \u2014 keep .${method}().`);
|
|
805
805
|
}
|
|
806
|
-
this._def.codec
|
|
807
|
-
|
|
806
|
+
if (this._def.codec !== void 0) {
|
|
807
|
+
throw new Error(`column already declares .as${this._def.codec === "number" ? "Number" : "Decimal"}(); a column has ONE codec.`);
|
|
808
|
+
}
|
|
809
|
+
return new _ColumnBuilder(this._def.type, {
|
|
810
|
+
...this._def,
|
|
811
|
+
codec
|
|
812
|
+
});
|
|
808
813
|
}
|
|
809
814
|
transform(fns) {
|
|
810
|
-
this._def.
|
|
811
|
-
|
|
815
|
+
if (this._def.codec !== void 0) {
|
|
816
|
+
throw new Error(`column already declares a named codec; .transform() and .as*() are two ways to say the same thing \u2014 keep the codec.`);
|
|
817
|
+
}
|
|
818
|
+
return new _ColumnBuilder(this._def.type, {
|
|
819
|
+
...this._def,
|
|
820
|
+
transform: fns
|
|
821
|
+
});
|
|
812
822
|
}
|
|
813
823
|
};
|
|
814
824
|
function uuid() {
|