@palbase/backend 27.0.0 → 28.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.
Files changed (52) hide show
  1. package/dist/bin/palbase-backend.cjs +165 -39
  2. package/dist/bin/palbase-backend.cjs.map +1 -1
  3. package/dist/bin/palbase-backend.js +4 -4
  4. package/dist/{chunk-GYK6QYS4.js → chunk-3TUJWHC2.js} +28 -3
  5. package/dist/chunk-3TUJWHC2.js.map +1 -0
  6. package/dist/{chunk-OO7R25AI.js → chunk-75YROPRZ.js} +118 -39
  7. package/dist/chunk-75YROPRZ.js.map +1 -0
  8. package/dist/{chunk-TS4U7NBD.js → chunk-IVZERLTM.js} +2 -2
  9. package/dist/{chunk-I3C4PFIW.js → chunk-RVP6BTEZ.js} +63 -6
  10. package/dist/chunk-RVP6BTEZ.js.map +1 -0
  11. package/dist/{chunk-DRZFQRJI.js → chunk-SNDXY565.js} +3 -2
  12. package/dist/chunk-SNDXY565.js.map +1 -0
  13. package/dist/db/index.cjs +30 -1
  14. package/dist/db/index.cjs.map +1 -1
  15. package/dist/db/index.d.cts +1 -1
  16. package/dist/db/index.d.ts +1 -1
  17. package/dist/db/index.js +3 -3
  18. package/dist/engine/index.cjs +175 -39
  19. package/dist/engine/index.cjs.map +1 -1
  20. package/dist/engine/index.d.cts +3 -3
  21. package/dist/engine/index.d.ts +3 -3
  22. package/dist/engine/index.js +11 -4
  23. package/dist/{index-VtToZmUm.d.cts → index-9C3JHxg-.d.cts} +112 -5
  24. package/dist/{index-NuzRCuxe.d.ts → index-BbvOoZFr.d.ts} +112 -5
  25. package/dist/{index-Bve7BBTL.d.cts → index-DtISj9QX.d.cts} +125 -22
  26. package/dist/{index-BrvvxSpn.d.ts → index-dTTLlHIn.d.ts} +125 -22
  27. package/dist/index.cjs +93 -4
  28. package/dist/index.cjs.map +1 -1
  29. package/dist/index.d.cts +6 -22
  30. package/dist/index.d.ts +6 -22
  31. package/dist/index.js +10 -4
  32. package/dist/index.js.map +1 -1
  33. package/dist/openapi/index.d.cts +2 -2
  34. package/dist/openapi/index.d.ts +2 -2
  35. package/dist/{registry-B0eyOF9x.d.ts → registry-JQNIX-eA.d.ts} +1 -1
  36. package/dist/{registry-Bk9_rbNd.d.cts → registry-qIPM5BQe.d.cts} +1 -1
  37. package/dist/test/index.cjs +100 -6
  38. package/dist/test/index.cjs.map +1 -1
  39. package/dist/test/index.d.cts +1 -1
  40. package/dist/test/index.d.ts +1 -1
  41. package/dist/test/index.js +88 -6
  42. package/dist/test/index.js.map +1 -1
  43. package/docs/README.md +1 -1
  44. package/docs/database.md +103 -1
  45. package/docs/llms-full.txt +104 -2
  46. package/package.json +1 -1
  47. package/template/package.json +1 -1
  48. package/dist/chunk-DRZFQRJI.js.map +0 -1
  49. package/dist/chunk-GYK6QYS4.js.map +0 -1
  50. package/dist/chunk-I3C4PFIW.js.map +0 -1
  51. package/dist/chunk-OO7R25AI.js.map +0 -1
  52. /package/dist/{chunk-TS4U7NBD.js.map → chunk-IVZERLTM.js.map} +0 -0
@@ -1,8 +1,8 @@
1
- import '../index-Bve7BBTL.cjs';
2
- export { A as App, e as AuthVerifier, B as BootRefused, f as CreateAppOptions, E as EgressPolicy, g as EngineConfig, M as ModuleClients, R as RateLimiter, h as RequestDatabase, i as RouteEntry, j as RuntimeHooks, S as ScrubResult, k as SqlDriver, l as SqlTx, m as buildRouteTable, n as createApp, o as createLazyTransaction, p as createOps, q as createRequestDatabase, r as effectiveAuth, s as hostAllowed, t as installEgressFence, u as loadConfig, v as makeMemoryCache, w as matchRoute, x as quoteIdent, y as scrubSecrets, z as withTables } from '../index-VtToZmUm.cjs';
1
+ import '../index-DtISj9QX.cjs';
2
+ export { A as App, g as AuthVerifier, B as BootRefused, h as CreateAppOptions, D as DECLARATION_REFUSAL, a as DeclarationRefused, E as EgressPolicy, j as EngineConfig, M as ModuleClients, R as RateLimiter, k as RequestDatabase, l as RouteEntry, m as RuntimeHooks, S as ScrubResult, n as SqlDriver, o as SqlTx, p as buildRouteTable, q as createApp, r as createLazyTransaction, s as createOps, t as createRequestDatabase, u as effectiveAuth, v as hostAllowed, w as installEgressFence, i as isDeclarationRefused, x as loadConfig, y as makeMemoryCache, z as matchRoute, F as quoteIdent, G as scrubSecrets, H as withTables } from '../index-9C3JHxg-.cjs';
3
3
  import '../db/env.cjs';
4
4
  import '../stack.cjs';
5
5
  import 'zod';
6
6
  import 'node:async_hooks';
7
7
  import '../module-Dl1KFVtc.cjs';
8
- import '../registry-Bk9_rbNd.cjs';
8
+ import '../registry-qIPM5BQe.cjs';
@@ -1,8 +1,8 @@
1
- import '../index-BrvvxSpn.js';
2
- export { A as App, e as AuthVerifier, B as BootRefused, f as CreateAppOptions, E as EgressPolicy, g as EngineConfig, M as ModuleClients, R as RateLimiter, h as RequestDatabase, i as RouteEntry, j as RuntimeHooks, S as ScrubResult, k as SqlDriver, l as SqlTx, m as buildRouteTable, n as createApp, o as createLazyTransaction, p as createOps, q as createRequestDatabase, r as effectiveAuth, s as hostAllowed, t as installEgressFence, u as loadConfig, v as makeMemoryCache, w as matchRoute, x as quoteIdent, y as scrubSecrets, z as withTables } from '../index-NuzRCuxe.js';
1
+ import '../index-dTTLlHIn.js';
2
+ export { A as App, g as AuthVerifier, B as BootRefused, h as CreateAppOptions, D as DECLARATION_REFUSAL, a as DeclarationRefused, E as EgressPolicy, j as EngineConfig, M as ModuleClients, R as RateLimiter, k as RequestDatabase, l as RouteEntry, m as RuntimeHooks, S as ScrubResult, n as SqlDriver, o as SqlTx, p as buildRouteTable, q as createApp, r as createLazyTransaction, s as createOps, t as createRequestDatabase, u as effectiveAuth, v as hostAllowed, w as installEgressFence, i as isDeclarationRefused, x as loadConfig, y as makeMemoryCache, z as matchRoute, F as quoteIdent, G as scrubSecrets, H as withTables } from '../index-BbvOoZFr.js';
3
3
  import '../db/env.js';
4
4
  import '../stack.js';
5
5
  import 'zod';
6
6
  import 'node:async_hooks';
7
7
  import '../module-Dl1KFVtc.js';
8
- import '../registry-B0eyOF9x.js';
8
+ import '../registry-JQNIX-eA.js';
@@ -16,16 +16,22 @@ import {
16
16
  quoteIdent,
17
17
  scrubSecrets,
18
18
  withTables
19
- } from "../chunk-OO7R25AI.js";
20
- import "../chunk-I3C4PFIW.js";
21
- import "../chunk-DRZFQRJI.js";
22
- import "../chunk-GYK6QYS4.js";
19
+ } from "../chunk-75YROPRZ.js";
20
+ import {
21
+ DECLARATION_REFUSAL,
22
+ DeclarationRefused,
23
+ isDeclarationRefused
24
+ } from "../chunk-RVP6BTEZ.js";
25
+ import "../chunk-SNDXY565.js";
26
+ import "../chunk-3TUJWHC2.js";
23
27
  import "../chunk-VVMJEVQP.js";
24
28
  import "../chunk-CGNN2PUH.js";
25
29
  import "../chunk-VXPNPVAG.js";
26
30
  export {
27
31
  AuthVerifier,
28
32
  BootRefused,
33
+ DECLARATION_REFUSAL,
34
+ DeclarationRefused,
29
35
  RateLimiter,
30
36
  buildRouteTable,
31
37
  createApp,
@@ -35,6 +41,7 @@ export {
35
41
  effectiveAuth,
36
42
  hostAllowed,
37
43
  installEgressFence,
44
+ isDeclarationRefused,
38
45
  loadConfig,
39
46
  makeMemoryCache,
40
47
  matchRoute,
@@ -1,6 +1,6 @@
1
- import { D as DBClient, a as DBOps, T as TxPlanBody, b as TxPlanResponse, A as AuthSpec, C as CacheClient, c as RuntimeServices, _ as __runWithRuntime, d as __requestALS } from './index-Bve7BBTL.cjs';
1
+ import { D as DBClient, a as DBOps, T as TxPlanBody, b as TxPlanResponse, A as AuthSpec, C as CacheClient, c as RuntimeServices, _ as __runWithRuntime, d as __requestALS } from './index-DtISj9QX.cjs';
2
2
  import { T as Token } from './module-Dl1KFVtc.cjs';
3
- import { R as RouteMeta } from './registry-Bk9_rbNd.cjs';
3
+ import { R as RouteMeta } from './registry-qIPM5BQe.cjs';
4
4
 
5
5
  /**
6
6
  * engine/config.ts — settings from the environment, and the gate that refuses
@@ -66,7 +66,15 @@ interface EngineConfig {
66
66
  dbServiceRole: string;
67
67
  poolMax: number;
68
68
  }
69
- /** Thrown when a mandatory module is unconfigured. Carries the missing names. */
69
+ /**
70
+ * THE ENVIRONMENT THIS STACK WAS HANDED IS UNUSABLE. Carries the missing names.
71
+ *
72
+ * ONLY the environment. A refusal about the author's own declarations is
73
+ * `DeclarationRefused` (see `src/refusals.ts`) and the difference is not
74
+ * severity, it is the CURE: no push fixes a missing DATABASE_URL, so the
75
+ * runtime must keep treating this one as fatal rather than waiting for an
76
+ * artifact that would change nothing.
77
+ */
70
78
  declare class BootRefused extends Error {
71
79
  readonly missing: readonly string[];
72
80
  constructor(missing: readonly string[], message: string);
@@ -171,10 +179,27 @@ interface FindManyOptions {
171
179
  * refused without it — see `offsetClause`. */
172
180
  offset?: number;
173
181
  }
174
- /** The six string-keyed operations, plus an interactive `transaction`. */
175
182
  declare function createOps(tx: TxLike): {
176
183
  query<T = unknown>(sql: string, params?: unknown[]): Promise<T[]>;
177
184
  insert(table: string, data: Row): Promise<Row>;
185
+ /**
186
+ * Çok satırı TEK statement'la ekle.
187
+ *
188
+ * Motor bunu zaten yapıyordu ve yalnız PLAN yolundan erişilebiliyordu
189
+ * (`tx.public.x.insertMany`). Dışarıda tek kapı `insert(row)` olduğu için
190
+ * "şu 200 satırı yaz" ya 200 gidiş-dönüş ya da ham SQL oluyordu — ölçüldü:
191
+ * bir müşteri projesinde bir seed rutini satırları tek tek siliyor
192
+ * (`deleteMany` VARKEN) ve tek tek ekliyor.
193
+ *
194
+ * SATIRLARIN ANAHTAR KÜMESİ AYNI OLMAK ZORUNDA. Kolon listesi tek bir
195
+ * statement'ta paylaşıldığı için ilk satırdan türer; farklı anahtar taşıyan
196
+ * bir satırın fazla kolonu SESSİZCE YAZILMAZDI. Eksik kolon için `null`
197
+ * yazın ya da varsayılanı istiyorsanız o satırları ayrı çağrıda ekleyin.
198
+ */
199
+ insertMany(table: string, rows: readonly Row[], opts?: {
200
+ onConflict: readonly string[];
201
+ action?: "ignore" | "update";
202
+ }): Promise<Row[]>;
178
203
  /**
179
204
  * Bir ADI kilitle — satırı değil (FR-054).
180
205
  *
@@ -527,6 +552,80 @@ declare function createRequestDatabase(sql: SqlDriver, identity: {
527
552
  claimsJson: string;
528
553
  }): RequestDatabase;
529
554
 
555
+ /**
556
+ * The two ways a stack refuses to boot — and they are two because their CURES
557
+ * are two.
558
+ *
559
+ * A LEAF MODULE ON PURPOSE. `db/` does not import from `engine/` and must not
560
+ * start: the schema layer is below the engine, and a refusal both of them raise
561
+ * cannot live in either. Anything imported here would invert that.
562
+ *
563
+ * WHY THE SPLIT EXISTS AT ALL — measured live on 2026-09-04.
564
+ *
565
+ * `8qitbtucm` ran for days on an image whose `setSchema` did not build
566
+ * relations. Moved onto one that does, the same artifact hit
567
+ * `buildRelations` at boot, threw a bare `Error`, and the runtime — which waits
568
+ * only on its own `ArtifactRefused` — treated it as fatal. The supervisor took
569
+ * the pod down, palsvc with it. CrashLoopBackOff, back-off 5m, and palsvc is
570
+ * THE PROCESS THAT ACCEPTS THE PUSH the message asks for.
571
+ *
572
+ * That is verbatim the lesson `v2/runtime/src/loader.ts` already carries for
573
+ * ABI refusals: *"the cure was named in the error and made impossible by the
574
+ * exit that carried it."* It cost a tenant a third time because the second
575
+ * refusal had no type to be recognised by.
576
+ *
577
+ * SO THE RULE IS THE CURE, NOT THE SEVERITY:
578
+ *
579
+ * {@link DeclarationRefused} — what was PUSHED cannot be built. Restarting
580
+ * re-reads the same bytes and fails identically; only a new artifact
581
+ * changes the answer. The runtime WAITS on this, keeps palsvc alive, and
582
+ * says the reason out loud.
583
+ *
584
+ * {@link BootRefused} — the ENVIRONMENT this stack was handed is unusable
585
+ * (no DATABASE_URL, an unparseable PORT, no SQL driver). No push fixes it;
586
+ * waiting for one would be a lie told in a log line forever. Stays fatal.
587
+ *
588
+ * NOT A MESSAGE PATTERN. The runtime's loader already says why: *"The
589
+ * distinction is the loader's TYPE, not a pattern over its wording."* A gate
590
+ * that reads wording drifts from the thing it claims to measure the first time
591
+ * someone improves an error message.
592
+ */
593
+ /**
594
+ * The tag that survives a realm boundary.
595
+ *
596
+ * `instanceof` compares constructor identity, and identity is per module
597
+ * instance. In the pod today the runtime and the bundle share one install
598
+ * (measured: both frames resolve to `/app/node_modules/@palbase/backend`), so
599
+ * `instanceof` would work — but it works by a coincidence of packaging, and a
600
+ * bundle that ever carries its own copy would make every declaration refusal
601
+ * silently fatal again. `Symbol.for` reads from the process-wide registry, so
602
+ * the tag means the same thing in every copy.
603
+ *
604
+ * Exported so the runtime can recognise the refusal without importing the
605
+ * class — see {@link isDeclarationRefused}.
606
+ */
607
+ declare const DECLARATION_REFUSAL: unique symbol;
608
+ /**
609
+ * What was pushed cannot be built. The cure is a NEW ARTIFACT.
610
+ *
611
+ * Thrown by the declaration layer — relation naming, ownership, entry points —
612
+ * wherever a fact about the author's code makes the app impossible to
613
+ * construct. Never thrown for anything the environment could change.
614
+ */
615
+ declare class DeclarationRefused extends Error {
616
+ /** @see DECLARATION_REFUSAL — realm-safe, unlike `instanceof`. */
617
+ readonly [DECLARATION_REFUSAL]: true;
618
+ constructor(message: string);
619
+ }
620
+ /**
621
+ * True for a refusal whose only cure is a new artifact — across realms.
622
+ *
623
+ * Takes `unknown` because every caller is a `catch`. A non-object, a null, a
624
+ * plain `Error`: all false, and false is the safe answer — it means "keep
625
+ * treating this as fatal", which is what the code did before this type existed.
626
+ */
627
+ declare function isDeclarationRefused(e: unknown): e is DeclarationRefused;
628
+
530
629
  /**
531
630
  * How a container refuses.
532
631
  *
@@ -550,6 +649,14 @@ declare class DiError extends Error {
550
649
  readonly path: string[];
551
650
  readonly at: string;
552
651
  readonly fixes: string[];
652
+ /**
653
+ * EVERY `DiKind` IS A FACT ABOUT THE AUTHOR'S DECLARATIONS — an unresolvable
654
+ * dependency, a cycle, a class no module owns. Not one of them is something
655
+ * the environment could change, so restarting re-reads the same bytes and
656
+ * fails identically. The cure is a new artifact, and the tag is how the
657
+ * runtime learns that without matching on wording.
658
+ */
659
+ readonly [DECLARATION_REFUSAL]: true;
553
660
  constructor(kind: DiKind, path: string[], at: string, detail: string, fixes: string[]);
554
661
  }
555
662
  /** How often a module appears in the OTHERS' `imports`. */
@@ -927,4 +1034,4 @@ interface App {
927
1034
  */
928
1035
  declare function createApp(opts: CreateAppOptions): Promise<App>;
929
1036
 
930
- export { type App as A, BootRefused as B, type Container as C, DiError as D, type EgressPolicy as E, type ModuleClients as M, RateLimiter as R, type ScrubResult as S, type DiKind as a, type ModulePressure as b, assertNoOrphanEntryPoints as c, buildContainer as d, AuthVerifier as e, type CreateAppOptions as f, type EngineConfig as g, type RequestDatabase as h, type RouteEntry as i, type RuntimeHooks as j, type SqlDriver as k, type SqlTx as l, buildRouteTable as m, createApp as n, createLazyTransaction as o, createOps as p, createRequestDatabase as q, effectiveAuth as r, hostAllowed as s, installEgressFence as t, loadConfig as u, makeMemoryCache as v, matchRoute as w, quoteIdent as x, scrubSecrets as y, withTables as z };
1037
+ export { type App as A, BootRefused as B, type Container as C, DECLARATION_REFUSAL as D, type EgressPolicy as E, quoteIdent as F, scrubSecrets as G, withTables as H, type ModuleClients as M, RateLimiter as R, type ScrubResult as S, DeclarationRefused as a, DiError as b, type DiKind as c, type ModulePressure as d, assertNoOrphanEntryPoints as e, buildContainer as f, AuthVerifier as g, type CreateAppOptions as h, isDeclarationRefused as i, type EngineConfig as j, type RequestDatabase as k, type RouteEntry as l, type RuntimeHooks as m, type SqlDriver as n, type SqlTx as o, buildRouteTable as p, createApp as q, createLazyTransaction as r, createOps as s, createRequestDatabase as t, effectiveAuth as u, hostAllowed as v, installEgressFence as w, loadConfig as x, makeMemoryCache as y, matchRoute as z };
@@ -1,6 +1,6 @@
1
- import { D as DBClient, a as DBOps, T as TxPlanBody, b as TxPlanResponse, A as AuthSpec, C as CacheClient, c as RuntimeServices, _ as __runWithRuntime, d as __requestALS } from './index-BrvvxSpn.js';
1
+ import { D as DBClient, a as DBOps, T as TxPlanBody, b as TxPlanResponse, A as AuthSpec, C as CacheClient, c as RuntimeServices, _ as __runWithRuntime, d as __requestALS } from './index-dTTLlHIn.js';
2
2
  import { T as Token } from './module-Dl1KFVtc.js';
3
- import { R as RouteMeta } from './registry-B0eyOF9x.js';
3
+ import { R as RouteMeta } from './registry-JQNIX-eA.js';
4
4
 
5
5
  /**
6
6
  * engine/config.ts — settings from the environment, and the gate that refuses
@@ -66,7 +66,15 @@ interface EngineConfig {
66
66
  dbServiceRole: string;
67
67
  poolMax: number;
68
68
  }
69
- /** Thrown when a mandatory module is unconfigured. Carries the missing names. */
69
+ /**
70
+ * THE ENVIRONMENT THIS STACK WAS HANDED IS UNUSABLE. Carries the missing names.
71
+ *
72
+ * ONLY the environment. A refusal about the author's own declarations is
73
+ * `DeclarationRefused` (see `src/refusals.ts`) and the difference is not
74
+ * severity, it is the CURE: no push fixes a missing DATABASE_URL, so the
75
+ * runtime must keep treating this one as fatal rather than waiting for an
76
+ * artifact that would change nothing.
77
+ */
70
78
  declare class BootRefused extends Error {
71
79
  readonly missing: readonly string[];
72
80
  constructor(missing: readonly string[], message: string);
@@ -171,10 +179,27 @@ interface FindManyOptions {
171
179
  * refused without it — see `offsetClause`. */
172
180
  offset?: number;
173
181
  }
174
- /** The six string-keyed operations, plus an interactive `transaction`. */
175
182
  declare function createOps(tx: TxLike): {
176
183
  query<T = unknown>(sql: string, params?: unknown[]): Promise<T[]>;
177
184
  insert(table: string, data: Row): Promise<Row>;
185
+ /**
186
+ * Çok satırı TEK statement'la ekle.
187
+ *
188
+ * Motor bunu zaten yapıyordu ve yalnız PLAN yolundan erişilebiliyordu
189
+ * (`tx.public.x.insertMany`). Dışarıda tek kapı `insert(row)` olduğu için
190
+ * "şu 200 satırı yaz" ya 200 gidiş-dönüş ya da ham SQL oluyordu — ölçüldü:
191
+ * bir müşteri projesinde bir seed rutini satırları tek tek siliyor
192
+ * (`deleteMany` VARKEN) ve tek tek ekliyor.
193
+ *
194
+ * SATIRLARIN ANAHTAR KÜMESİ AYNI OLMAK ZORUNDA. Kolon listesi tek bir
195
+ * statement'ta paylaşıldığı için ilk satırdan türer; farklı anahtar taşıyan
196
+ * bir satırın fazla kolonu SESSİZCE YAZILMAZDI. Eksik kolon için `null`
197
+ * yazın ya da varsayılanı istiyorsanız o satırları ayrı çağrıda ekleyin.
198
+ */
199
+ insertMany(table: string, rows: readonly Row[], opts?: {
200
+ onConflict: readonly string[];
201
+ action?: "ignore" | "update";
202
+ }): Promise<Row[]>;
178
203
  /**
179
204
  * Bir ADI kilitle — satırı değil (FR-054).
180
205
  *
@@ -527,6 +552,80 @@ declare function createRequestDatabase(sql: SqlDriver, identity: {
527
552
  claimsJson: string;
528
553
  }): RequestDatabase;
529
554
 
555
+ /**
556
+ * The two ways a stack refuses to boot — and they are two because their CURES
557
+ * are two.
558
+ *
559
+ * A LEAF MODULE ON PURPOSE. `db/` does not import from `engine/` and must not
560
+ * start: the schema layer is below the engine, and a refusal both of them raise
561
+ * cannot live in either. Anything imported here would invert that.
562
+ *
563
+ * WHY THE SPLIT EXISTS AT ALL — measured live on 2026-09-04.
564
+ *
565
+ * `8qitbtucm` ran for days on an image whose `setSchema` did not build
566
+ * relations. Moved onto one that does, the same artifact hit
567
+ * `buildRelations` at boot, threw a bare `Error`, and the runtime — which waits
568
+ * only on its own `ArtifactRefused` — treated it as fatal. The supervisor took
569
+ * the pod down, palsvc with it. CrashLoopBackOff, back-off 5m, and palsvc is
570
+ * THE PROCESS THAT ACCEPTS THE PUSH the message asks for.
571
+ *
572
+ * That is verbatim the lesson `v2/runtime/src/loader.ts` already carries for
573
+ * ABI refusals: *"the cure was named in the error and made impossible by the
574
+ * exit that carried it."* It cost a tenant a third time because the second
575
+ * refusal had no type to be recognised by.
576
+ *
577
+ * SO THE RULE IS THE CURE, NOT THE SEVERITY:
578
+ *
579
+ * {@link DeclarationRefused} — what was PUSHED cannot be built. Restarting
580
+ * re-reads the same bytes and fails identically; only a new artifact
581
+ * changes the answer. The runtime WAITS on this, keeps palsvc alive, and
582
+ * says the reason out loud.
583
+ *
584
+ * {@link BootRefused} — the ENVIRONMENT this stack was handed is unusable
585
+ * (no DATABASE_URL, an unparseable PORT, no SQL driver). No push fixes it;
586
+ * waiting for one would be a lie told in a log line forever. Stays fatal.
587
+ *
588
+ * NOT A MESSAGE PATTERN. The runtime's loader already says why: *"The
589
+ * distinction is the loader's TYPE, not a pattern over its wording."* A gate
590
+ * that reads wording drifts from the thing it claims to measure the first time
591
+ * someone improves an error message.
592
+ */
593
+ /**
594
+ * The tag that survives a realm boundary.
595
+ *
596
+ * `instanceof` compares constructor identity, and identity is per module
597
+ * instance. In the pod today the runtime and the bundle share one install
598
+ * (measured: both frames resolve to `/app/node_modules/@palbase/backend`), so
599
+ * `instanceof` would work — but it works by a coincidence of packaging, and a
600
+ * bundle that ever carries its own copy would make every declaration refusal
601
+ * silently fatal again. `Symbol.for` reads from the process-wide registry, so
602
+ * the tag means the same thing in every copy.
603
+ *
604
+ * Exported so the runtime can recognise the refusal without importing the
605
+ * class — see {@link isDeclarationRefused}.
606
+ */
607
+ declare const DECLARATION_REFUSAL: unique symbol;
608
+ /**
609
+ * What was pushed cannot be built. The cure is a NEW ARTIFACT.
610
+ *
611
+ * Thrown by the declaration layer — relation naming, ownership, entry points —
612
+ * wherever a fact about the author's code makes the app impossible to
613
+ * construct. Never thrown for anything the environment could change.
614
+ */
615
+ declare class DeclarationRefused extends Error {
616
+ /** @see DECLARATION_REFUSAL — realm-safe, unlike `instanceof`. */
617
+ readonly [DECLARATION_REFUSAL]: true;
618
+ constructor(message: string);
619
+ }
620
+ /**
621
+ * True for a refusal whose only cure is a new artifact — across realms.
622
+ *
623
+ * Takes `unknown` because every caller is a `catch`. A non-object, a null, a
624
+ * plain `Error`: all false, and false is the safe answer — it means "keep
625
+ * treating this as fatal", which is what the code did before this type existed.
626
+ */
627
+ declare function isDeclarationRefused(e: unknown): e is DeclarationRefused;
628
+
530
629
  /**
531
630
  * How a container refuses.
532
631
  *
@@ -550,6 +649,14 @@ declare class DiError extends Error {
550
649
  readonly path: string[];
551
650
  readonly at: string;
552
651
  readonly fixes: string[];
652
+ /**
653
+ * EVERY `DiKind` IS A FACT ABOUT THE AUTHOR'S DECLARATIONS — an unresolvable
654
+ * dependency, a cycle, a class no module owns. Not one of them is something
655
+ * the environment could change, so restarting re-reads the same bytes and
656
+ * fails identically. The cure is a new artifact, and the tag is how the
657
+ * runtime learns that without matching on wording.
658
+ */
659
+ readonly [DECLARATION_REFUSAL]: true;
553
660
  constructor(kind: DiKind, path: string[], at: string, detail: string, fixes: string[]);
554
661
  }
555
662
  /** How often a module appears in the OTHERS' `imports`. */
@@ -927,4 +1034,4 @@ interface App {
927
1034
  */
928
1035
  declare function createApp(opts: CreateAppOptions): Promise<App>;
929
1036
 
930
- export { type App as A, BootRefused as B, type Container as C, DiError as D, type EgressPolicy as E, type ModuleClients as M, RateLimiter as R, type ScrubResult as S, type DiKind as a, type ModulePressure as b, assertNoOrphanEntryPoints as c, buildContainer as d, AuthVerifier as e, type CreateAppOptions as f, type EngineConfig as g, type RequestDatabase as h, type RouteEntry as i, type RuntimeHooks as j, type SqlDriver as k, type SqlTx as l, buildRouteTable as m, createApp as n, createLazyTransaction as o, createOps as p, createRequestDatabase as q, effectiveAuth as r, hostAllowed as s, installEgressFence as t, loadConfig as u, makeMemoryCache as v, matchRoute as w, quoteIdent as x, scrubSecrets as y, withTables as z };
1037
+ export { type App as A, BootRefused as B, type Container as C, DECLARATION_REFUSAL as D, type EgressPolicy as E, quoteIdent as F, scrubSecrets as G, withTables as H, type ModuleClients as M, RateLimiter as R, type ScrubResult as S, DeclarationRefused as a, DiError as b, type DiKind as c, type ModulePressure as d, assertNoOrphanEntryPoints as e, buildContainer as f, AuthVerifier as g, type CreateAppOptions as h, isDeclarationRefused as i, type EngineConfig as j, type RequestDatabase as k, type RouteEntry as l, type RuntimeHooks as m, type SqlDriver as n, type SqlTx as o, buildRouteTable as p, createApp as q, createLazyTransaction as r, createOps as s, createRequestDatabase as t, effectiveAuth as u, hostAllowed as v, installEgressFence as w, loadConfig as x, makeMemoryCache as y, matchRoute as z };
@@ -2825,7 +2825,12 @@ type RowShape<T extends TableDef> = {
2825
2825
  };
2826
2826
  /** A typed table accessor that mirrors the runtime DBClient surface. */
2827
2827
  interface TypedTable<T extends TableDef> {
2828
- insert(data: InsertShape<T>): Promise<RowShape<T>>;
2828
+ insert(data: InsertValues<InsertShape<T>>): Promise<RowShape<T>>;
2829
+ /** Çok satır, TEK statement. Satırların anahtar kümesi aynı olmalı. */
2830
+ insertMany(rows: readonly InsertValues<InsertShape<T>>[], opts?: {
2831
+ onConflict: readonly string[];
2832
+ action?: "ignore" | "update";
2833
+ }): Promise<RowShape<T>[]>;
2829
2834
  put(q: {
2830
2835
  data: InsertShape<T>;
2831
2836
  onConflict: readonly string[];
@@ -2837,7 +2842,7 @@ interface TypedTable<T extends TableDef> {
2837
2842
  where: {
2838
2843
  id: string;
2839
2844
  };
2840
- set: Partial<InsertShape<T>>;
2845
+ set: SetShape<InsertShape<T>>;
2841
2846
  }): Promise<RowShape<T> | null>;
2842
2847
  delete(id: string): Promise<void>;
2843
2848
  findById(id: string): Promise<RowShape<T> | null>;
@@ -2974,9 +2979,39 @@ declare function col<const N extends string>(name: N): ColRef<N>;
2974
2979
  * kabul, `numeric ↔ text` ret olur ve ikisi de DOĞRU olur. Defterde teklif.
2975
2980
  */
2976
2981
  type PgFamily<V> = [NonNullable<V>] extends [boolean] ? "bool" : [NonNullable<V>] extends [readonly (number | string)[]] ? "array" : [NonNullable<V>] extends [Date] ? "date" : [NonNullable<V>] extends [string | number] ? "scalar" : "opaque";
2977
- /** `Row`'un `V` ile karşılaştırılabilir kolonları — aynı aileden olanlar. */
2982
+ /**
2983
+ * Kolonun BİLDİRİLMİŞ Postgres tipi — `env-gen`'in bastığı markadan.
2984
+ *
2985
+ * Marka yoksa `"?"`: elle yazılmış bir `Tables` (testler) ya da yeniden
2986
+ * üretilmemiş bir `palbase-env.d.ts`. O durumda eski TS-tipi sezgiseline
2987
+ * düşülüyor, çünkü marka olmadan söylenebilecek daha doğru bir şey yok.
2988
+ */
2989
+ type PgTag<V> = [NonNullable<V>] extends [{
2990
+ readonly __pg?: infer N;
2991
+ }] ? [N] extends [string] ? N : "?" : "?";
2992
+ /**
2993
+ * Postgres'te birbirleriyle karşılaştırılabilen tipler tek "cins".
2994
+ *
2995
+ * `integer`, `bigint` ve `numeric` örtük sayısal dönüşümle karşılaştırılır;
2996
+ * geri kalan her tip yalnız kendisiyle. `uuid ↔ text` ve `enum ↔ text`
2997
+ * Postgres'te `operator does not exist` verir — D-021'de CANLI ölçülmüştü ve
2998
+ * tip bunu göremediği için kabul ediyordu.
2999
+ */
3000
+ type PgKind<P> = P extends "integer" | "bigint" | "numeric" ? "num" : P;
3001
+ /** İki tarafın da markası var mı — yoksa sezgisele düşülür. */
3002
+ type BothBranded<A, B> = "?" extends PgTag<A> ? false : "?" extends PgTag<B> ? false : true;
3003
+ type PgSameKind<A, B> = [PgKind<PgTag<A>>] extends [PgKind<PgTag<B>>] ? [PgKind<PgTag<B>>] extends [PgKind<PgTag<A>>] ? true : false : false;
3004
+ /**
3005
+ * `A` kolonu `B` ile karşılaştırılabilir mi.
3006
+ *
3007
+ * Marka varsa CEVAP KESİN: ne yanlış ret ne yanlış kabul. D-021 bu kuralı
3008
+ * "doğru olamaz" diye kaydetmişti çünkü tipte ayırt edecek bilgi yoktu; bilgi
3009
+ * artık tipte.
3010
+ */
3011
+ type ColComparable<A, B> = BothBranded<A, B> extends true ? PgSameKind<A, B> : [PgFamily<A>] extends [PgFamily<B>] ? true : false;
3012
+ /** `Row`'un `V` ile karşılaştırılabilir kolonları. */
2978
3013
  type ColumnsComparableTo<Row, V> = {
2979
- [K in keyof Row]-?: PgFamily<Row[K]> extends PgFamily<V> ? K : never;
3014
+ [K in keyof Row]-?: ColComparable<Row[K], V> extends true ? K : never;
2980
3015
  }[keyof Row];
2981
3016
  /**
2982
3017
  * Bu satırın kolonlarından birine referans. `Row` bilinmiyorsa hiçbiri.
@@ -3052,12 +3087,24 @@ type TextOps<V> = V extends string ? {
3052
3087
  * `C` bir parametre, çünkü `ColRefOf<Row, V>` aksi hâlde altı operatör
3053
3088
  * konumunda AYRI AYRI instantiate edilirdi — kolon başına altı mapped type.
3054
3089
  * Böyle kolon başına BİR tane (N-2). */
3090
+ /**
3091
+ * `now()` HANGİ kolonlarda karşılaştırma değeri olabilir.
3092
+ *
3093
+ * `timestamp` TypeScript'e `string` olarak geliyor — `text` ile aynı tip. Yani
3094
+ * "yalnız zaman kolonları" tip düzeyinde İFADE EDİLEMEZ (D-021'in tam olarak
3095
+ * kaydettiği sınır: TS numeric/bigint/text/uuid/timestamp'ı `string`'e
3096
+ * çöktürüyor). İki seçenekten yanlış KABUL, yanlış RET'e tercih ediliyor:
3097
+ * `now()`'ı bir metin kolonunda yazmak Postgres hatası verir, ama zaman
3098
+ * kolonunda YASAKLAMAK yazarı bu yüzden `sqlFragment`e düşürürdü — ve bu
3099
+ * özelliğin var olma nedeni tam olarak o düşüşü kaldırmak.
3100
+ */
3101
+ type NowComparable<V> = "?" extends PgTag<V> ? [NonNullable<V>] extends [string | Date] ? TxNow : never : PgTag<V> extends "timestamp" ? TxNow : never;
3055
3102
  type WhereOpBody<V, C> = {
3056
- gt?: V | C;
3057
- gte?: V | C;
3058
- lt?: V | C;
3059
- lte?: V | C;
3060
- neq?: V | C;
3103
+ gt?: V | C | NowComparable<V>;
3104
+ gte?: V | C | NowComparable<V>;
3105
+ lt?: V | C | NowComparable<V>;
3106
+ lte?: V | C | NowComparable<V>;
3107
+ neq?: V | C | NowComparable<V>;
3061
3108
  in?: V[];
3062
3109
  /** `IS NULL` / `IS NOT NULL` (FR-006) — `= NULL` SQL'de her zaman UNKNOWN'dır. */
3063
3110
  isNull?: boolean;
@@ -3072,7 +3119,7 @@ type WhereOp<V, Row = unknown> = WhereOpWith<V, ColRefOf<Row, V>>;
3072
3119
  * `TextOps<V>` `V extends string` diye soruyor ve `string | Ref<string>` o
3073
3120
  * soruya HAYIR der, yani `contains`/`startsWith` sessizce KAYBOLURDU.
3074
3121
  */
3075
- type WhereOpWith<V, C> = V | C | WhereOpBody<V, C>;
3122
+ type WhereOpWith<V, C> = V | C | NowComparable<V> | WhereOpBody<V, C>;
3076
3123
  /**
3077
3124
  * A filter over a row: every field optional, each one a plain value (equality)
3078
3125
  * or an operator object. THE filter language — `findMany`, `updateMany`,
@@ -3089,10 +3136,18 @@ type WhereFilter<Row> = {
3089
3136
  * Kysely'nin TS7'de 9,8M instantiation üreten vakası derin generic
3090
3137
  * özyinelemeydi. Burada derinlik yazarın filtresi kadardır ve pratikte
3091
3138
  * bir-iki seviyedir; tip maliyeti T001'in tavanına karşı ölçülür.
3139
+ *
3140
+ * Dal bir `sqlFragment` DE olabilir. Motor bunu zaten derliyordu — parantezli
3141
+ * ve parametreli (`… AND (((expires_at > now())) AND (t."city" = $2))`) —
3142
+ * ama tip yalnız üst düzey `where`'de fragment'e izin veriyordu. Yani
3143
+ * "fragment sorgudan KAÇMAK için değil, sorgunun İÇİNE girmek için" sözü tam
3144
+ * da bileşim anında kırılıyordu: bir koşulu bir fragment'le birleştirmek
3145
+ * isteyen yazar SORGUNUN TAMAMINI ham SQL'e taşımak zorunda kalıyordu.
3146
+ * Ölçüldü: bir müşteri sorgusunda tam olarak bu oldu.
3092
3147
  */
3093
- OR?: WhereFilter<Row>[];
3094
- AND?: WhereFilter<Row>[];
3095
- NOT?: WhereFilter<Row>;
3148
+ OR?: (WhereFilter<Row> | SqlFragment)[];
3149
+ AND?: (WhereFilter<Row> | SqlFragment)[];
3150
+ NOT?: WhereFilter<Row> | SqlFragment;
3096
3151
  };
3097
3152
  /**
3098
3153
  * Ordering and paging for a read.
@@ -3237,7 +3292,25 @@ type MutateInput<Row, Insert, Rels = unknown> = {
3237
3292
  * string de sayısal sayılır; ayrımı burada yapamayız, ama D-007 zaten miktarın
3238
3293
  * JS `number`'a hiç uğramamasını istiyor.
3239
3294
  */
3240
- type SetValue<V> = V | (NonNullable<V> extends number | string ? TxColumnExpr : never) | (NonNullable<V> extends Date ? TxNow : never);
3295
+ /** `increment()`/`decrement()` HANGİ kolonlarda anlamlı bildirilmiş PG tipine
3296
+ * göre. Marka yoksa eski sezgisel (`number | string`), ki o `text` kolonda da
3297
+ * izin veriyordu: Postgres `text + 1` demez. */
3298
+ type NumericCol<V> = "?" extends PgTag<V> ? NonNullable<V> extends number | string ? TxColumnExpr : never : PgKind<PgTag<V>> extends "num" ? TxColumnExpr : never;
3299
+ /**
3300
+ * Bir EKLEME yolunda yazılabilecek değer.
3301
+ *
3302
+ * Kolonun kendi tipi ya da — zaman kolonlarında — sunucu saati. `increment()`
3303
+ * burada YOK ve olamaz: satır henüz yokken "kolonun şu anki değeri" diye bir
3304
+ * şey yok, motor da bunu adıyla reddediyor.
3305
+ *
3306
+ * Var olma nedeni ölçülmüş: introspect edilmiş şemalarda zaman kolonlarının
3307
+ * çoğu varsayılansız (`timestamp()`), yani "eklerken sunucu saatini yaz"ın tek
3308
+ * karşılığı istemci saatiydi — 33 tablolu bir projede 12 yerde.
3309
+ */
3310
+ type InsertValues<Insert> = {
3311
+ [K in keyof Insert]: Insert[K] | NowComparable<Insert[K]>;
3312
+ };
3313
+ type SetValue<V> = V | NumericCol<V> | NowComparable<V>;
3241
3314
  /** Bir update'in `set`'i: insert şeklinin herhangi bir alt kümesi, ifadelerle. */
3242
3315
  type SetShape<Insert> = {
3243
3316
  [K in keyof Insert]?: SetValue<Insert[K]>;
@@ -3320,7 +3393,30 @@ type RelsOf<T> = T extends {
3320
3393
  } ? R : unknown;
3321
3394
  /** Temel tablo erişimcisi — search'süz beş op. */
3322
3395
  interface EnvTypedTableBase<T extends TableTypes> {
3323
- insert(data: T["insert"]): Promise<T["row"]>;
3396
+ insert(data: InsertValues<T["insert"]>): Promise<T["row"]>;
3397
+ /**
3398
+ * Çok satırı TEK statement'la ekle (tek gidiş-dönüş).
3399
+ *
3400
+ * ```ts
3401
+ * await Database.public.journey_stops.insertMany(
3402
+ * stops.map((s, i) => ({ journey_id, name: s.name, sort_order: i })),
3403
+ * );
3404
+ * ```
3405
+ *
3406
+ * Satırların anahtar kümesi AYNI olmalı: tek statement kolon listesini
3407
+ * paylaşır, yani farklı şekilli bir satırın fazla kolonu sessizce yazılmaz
3408
+ * ve eksiği yanlış sütuna kayardı. Eksik alan için `null` yazın.
3409
+ *
3410
+ * Boş dizi boş sonuç döner — hata değil: "eklenecek bir şey yoktu".
3411
+ */
3412
+ insertMany(rows: readonly InsertValues<T["insert"]>[], opts?: {
3413
+ /** Çakışmanın ARANDIĞI kolonlar — benzersiz bir kısıt ya da index taşımalı. */
3414
+ onConflict: readonly Extract<keyof T["row"], string>[];
3415
+ /** `"ignore"` (VARSAYILAN, plan yoluyla aynı) çakışanı atlar ve o satır
3416
+ * SONUÇTA DÖNMEZ — Postgres yalnız yazdıklarını döndürür; `"update"`
3417
+ * üzerine yazar. */
3418
+ action?: "ignore" | "update";
3419
+ }): Promise<T["row"][]>;
3324
3420
  /**
3325
3421
  * Bir idempotency anahtarını sahiplen (FR-033).
3326
3422
  *
@@ -3336,7 +3432,7 @@ interface EnvTypedTableBase<T extends TableTypes> {
3336
3432
  * İlk argüman satırı BULAN alanlar, ikincisi yalnız yazılanlar — ikinci çağrı
3337
3433
  * farklı bir yük gönderse bile satır anahtarla bulunur.
3338
3434
  */
3339
- claim(unique: Partial<T["insert"]>, extra?: Partial<T["insert"]>): Promise<{
3435
+ claim(unique: Partial<T["insert"]>, extra?: Partial<InsertValues<T["insert"]>>): Promise<{
3340
3436
  inserted: boolean;
3341
3437
  row: T["row"];
3342
3438
  }>;
@@ -3361,7 +3457,7 @@ interface EnvTypedTableBase<T extends TableTypes> {
3361
3457
  * seçmeyi kolay yapıyordu.
3362
3458
  */
3363
3459
  put(q: {
3364
- data: T["insert"];
3460
+ data: InsertValues<T["insert"]>;
3365
3461
  onConflict: readonly Extract<keyof T["row"], string>[];
3366
3462
  }): Promise<T["row"]>;
3367
3463
  /** Update the row by id; resolves to the updated row, or `null` if no row
@@ -3371,7 +3467,7 @@ interface EnvTypedTableBase<T extends TableTypes> {
3371
3467
  where: {
3372
3468
  id: string;
3373
3469
  };
3374
- set: Partial<T["insert"]>;
3470
+ set: SetShape<T["insert"]>;
3375
3471
  }): Promise<T["row"] | null>;
3376
3472
  delete(id: string): Promise<void>;
3377
3473
  findById(id: string): Promise<T["row"] | null>;
@@ -3406,7 +3502,7 @@ interface EnvTypedTableBase<T extends TableTypes> {
3406
3502
  * kapanır (valid_to/superseded_by), yenisi TEK savepoint'te eklenir; dönüş
3407
3503
  * yeni satır. Validity beyanı olmayan tabloda adlandırılmış çalışma-zamanı
3408
3504
  * hatası — tip düzeyinde ayrım env `Tables` bayrağı taşımadığından yapılamaz. */
3409
- supersede(id: string, row: T["insert"]): Promise<T["row"]>;
3505
+ supersede(id: string, row: InsertValues<T["insert"]>): Promise<T["row"]>;
3410
3506
  }
3411
3507
  /** Tablo erişimcisi: env girdisi `searchable: true` taşıyorsa (vector kolonu ya da
3412
3508
  * search beyanı — env-gen üretir) `search()` üyesi VARDIR; yoksa üye hiç yoktur ve
@@ -3960,9 +4056,9 @@ type TxWhereField<Row, K extends keyof Row> = WhereOpWith<Row[K], ColRefOf<Row,
3960
4056
  type TxWhere<Row, Rels = unknown> = {
3961
4057
  [K in keyof Row]?: TxWhereField<Row, K>;
3962
4058
  } & {
3963
- OR?: TxWhere<Row, Rels>[];
3964
- AND?: TxWhere<Row, Rels>[];
3965
- NOT?: TxWhere<Row, Rels>;
4059
+ OR?: (TxWhere<Row, Rels> | SqlFragment)[];
4060
+ AND?: (TxWhere<Row, Rels> | SqlFragment)[];
4061
+ NOT?: TxWhere<Row, Rels> | SqlFragment;
3966
4062
  } & HasOnly<Rels>;
3967
4063
  /** Options for a plan `select`. */
3968
4064
  interface TxSelectOptions {
@@ -4342,6 +4438,13 @@ interface DBOps {
4342
4438
  */
4343
4439
  query<T = unknown>(sql: string, params?: unknown[]): Promise<T[]>;
4344
4440
  insert(table: string, data: Record<string, unknown>): Promise<Record<string, unknown>>;
4441
+ /** Çok satır, TEK statement. Satırların anahtar kümesi aynı olmalı — kolon
4442
+ * listesi paylaşıldığı için farklı şekilli bir satırın kolonu sessizce
4443
+ * yazılmazdı. */
4444
+ insertMany(table: string, rows: readonly Record<string, unknown>[], opts?: {
4445
+ onConflict: readonly string[];
4446
+ action?: "ignore" | "update";
4447
+ }): Promise<Record<string, unknown>[]>;
4345
4448
  /**
4346
4449
  * Insert the row, or update it when it collides on `onConflict` — one
4347
4450
  * statement, so two concurrent callers cannot both lose.