@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.
Files changed (53) hide show
  1. package/dist/bin/palbase-backend.cjs +79 -26
  2. package/dist/bin/palbase-backend.cjs.map +1 -1
  3. package/dist/bin/palbase-backend.js +5 -5
  4. package/dist/{chunk-IXAX5CON.js → chunk-C4ZA5AT2.js} +1 -1
  5. package/dist/{chunk-IXAX5CON.js.map → chunk-C4ZA5AT2.js.map} +1 -1
  6. package/dist/{chunk-AZJIKCOR.js → chunk-DCDHAKF3.js} +39 -23
  7. package/dist/{chunk-AZJIKCOR.js.map → chunk-DCDHAKF3.js.map} +1 -1
  8. package/dist/{chunk-Z4CZRMNF.js → chunk-GOPZPM2A.js} +2 -2
  9. package/dist/{chunk-5C5UCILO.js → chunk-N54QZER3.js} +3 -3
  10. package/dist/{chunk-XEGZ3S2Q.js → chunk-QMFOL2K6.js} +32 -7
  11. package/dist/chunk-QMFOL2K6.js.map +1 -0
  12. package/dist/{chunk-KGP6ALIU.js → chunk-XOX6RFPZ.js} +30 -8
  13. package/dist/chunk-XOX6RFPZ.js.map +1 -0
  14. package/dist/db/index.cjs +14 -4
  15. package/dist/db/index.cjs.map +1 -1
  16. package/dist/db/index.d.cts +1 -1
  17. package/dist/db/index.d.ts +1 -1
  18. package/dist/db/index.js +3 -3
  19. package/dist/engine/index.cjs +79 -26
  20. package/dist/engine/index.cjs.map +1 -1
  21. package/dist/engine/index.d.cts +3 -3
  22. package/dist/engine/index.d.ts +3 -3
  23. package/dist/engine/index.js +5 -5
  24. package/dist/{index-DgYkdNT9.d.cts → index-BWgnGj68.d.cts} +68 -20
  25. package/dist/{index-BILC9WwS.d.ts → index-Bi74dcOu.d.ts} +2 -2
  26. package/dist/{index-BgWnP07a.d.ts → index-Dg10RnZO.d.ts} +68 -20
  27. package/dist/{index-CWGiJ2Up.d.cts → index-g6iQyYci.d.cts} +2 -2
  28. package/dist/index.cjs +59 -12
  29. package/dist/index.cjs.map +1 -1
  30. package/dist/index.d.cts +6 -6
  31. package/dist/index.d.ts +6 -6
  32. package/dist/index.js +5 -5
  33. package/dist/openapi/index.d.cts +2 -2
  34. package/dist/openapi/index.d.ts +2 -2
  35. package/dist/{registry-BcRYIQ-R.d.cts → registry-6VT5RPeO.d.cts} +1 -1
  36. package/dist/{registry-ClzjxIWy.d.ts → registry-B-sxJJN0.d.ts} +1 -1
  37. package/dist/test/index.cjs +35 -8
  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 +7 -5
  42. package/dist/test/index.js.map +1 -1
  43. package/docs/README.md +1 -1
  44. package/docs/auth.md +102 -20
  45. package/docs/llms-full.txt +129 -21
  46. package/docs/schema.md +26 -0
  47. package/package.json +1 -1
  48. package/template/db/public.ts +23 -0
  49. package/template/package.json +1 -1
  50. package/dist/chunk-KGP6ALIU.js.map +0 -1
  51. package/dist/chunk-XEGZ3S2Q.js.map +0 -1
  52. /package/dist/{chunk-Z4CZRMNF.js.map → chunk-GOPZPM2A.js.map} +0 -0
  53. /package/dist/{chunk-5C5UCILO.js.map → chunk-N54QZER3.js.map} +0 -0
@@ -1,8 +1,8 @@
1
- import '../index-DgYkdNT9.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-CWGiJ2Up.cjs';
1
+ import '../index-BWgnGj68.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-g6iQyYci.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-BcRYIQ-R.cjs';
8
+ import '../registry-6VT5RPeO.cjs';
@@ -1,8 +1,8 @@
1
- import '../index-BgWnP07a.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-BILC9WwS.js';
1
+ import '../index-Dg10RnZO.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-Bi74dcOu.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-ClzjxIWy.js';
8
+ import '../registry-B-sxJJN0.js';
@@ -16,15 +16,15 @@ import {
16
16
  quoteIdent,
17
17
  scrubSecrets,
18
18
  withTables
19
- } from "../chunk-AZJIKCOR.js";
19
+ } from "../chunk-DCDHAKF3.js";
20
20
  import {
21
21
  DECLARATION_REFUSAL,
22
22
  DeclarationRefused,
23
23
  isDeclarationRefused
24
- } from "../chunk-5C5UCILO.js";
25
- import "../chunk-XEGZ3S2Q.js";
26
- import "../chunk-KGP6ALIU.js";
27
- import "../chunk-IXAX5CON.js";
24
+ } from "../chunk-N54QZER3.js";
25
+ import "../chunk-QMFOL2K6.js";
26
+ import "../chunk-XOX6RFPZ.js";
27
+ import "../chunk-C4ZA5AT2.js";
28
28
  import "../chunk-BRLJOXWS.js";
29
29
  import "../chunk-KATPXCJ5.js";
30
30
  export {
@@ -28,16 +28,32 @@ interface User {
28
28
  /**
29
29
  * Whether that email address has been confirmed.
30
30
  *
31
- * Server-resolved from the verified user profile, not read from the JWT: a
32
- * token claim is only true as of when the token was minted, so a user who
33
- * verifies mid-session would keep reporting `false` until it expired.
31
+ * READ FROM THE VERIFIED TOKEN (`email_verified`), so it says what was true
32
+ * when that token was minted: a user who confirms their address mid-session
33
+ * keeps reporting `false` until their next one. {@link User.roles} is the
34
+ * deliberate exception — it is read from the table on every request.
35
+ *
36
+ * This paragraph used to claim the opposite ("server-resolved from the
37
+ * verified profile, not read from the JWT") and to describe a ~30-second
38
+ * profile cache. Measured 2026-09-04: the engine fills this field from
39
+ * `claims.email_verified` and no such cache exists anywhere in the runtime.
40
+ * Whether the claim or a profile read is the RIGHT source is a separate
41
+ * question and an open one — but the type may not answer it wrongly while it
42
+ * is open.
34
43
  *
35
44
  * `false` for a phone-only user (no address to confirm) and for any user who
36
- * has not clicked through yet. Before this field existed, answering "is this
37
- * address confirmed" in a handler cost an extra network round-trip PER
38
- * REQUEST for one boolean the runtime already had.
45
+ * has not clicked through yet.
39
46
  */
40
47
  emailVerified: boolean;
48
+ /**
49
+ * The caller's DATABASE role — the string RLS reads, not an application role.
50
+ *
51
+ * It is the literal `"authenticated"` for every signed-in caller and
52
+ * `"anon"` for every unauthenticated one, so `user.role === "admin"` is
53
+ * ALWAYS false and reads like a working check. Application roles are
54
+ * {@link User.roles}; this field says which Postgres role the request runs
55
+ * as, and that is the only question it answers.
56
+ */
41
57
  role: string;
42
58
  /**
43
59
  * The caller's APPLICATION roles, as assigned in `auth.user_roles`.
@@ -2603,7 +2619,7 @@ interface PalbaseLinksClient {
2603
2619
  * \@Controller("/todos")
2604
2620
  * export default class TodosController {
2605
2621
  * \@Post("") create(\@Body(CreateTodoBody) body: CreateTodoBody): unknown {
2606
- * return Database.insert("todos", { title: body.title });
2622
+ * return Database.public.todos.insert({ title: body.title });
2607
2623
  * }
2608
2624
  * }
2609
2625
  *
@@ -2688,11 +2704,6 @@ declare function __setRuntime(services: RuntimeServices): void;
2688
2704
  * The persistent worker calls this once per request so concurrent requests
2689
2705
  * never share a services slot. NOT part of the public author-facing API. */
2690
2706
  declare function __runWithRuntime<T>(services: RuntimeServices, fn: () => T): T;
2691
- /** Read the live clients, throwing if accessed outside a request scope.
2692
- *
2693
- * Resolves the ALS store first (persistent server, per-request), then the
2694
- * process-global fallback (dev-server / tests). NOT part of the public
2695
- * author-facing API — used by the runtime and the singleton Proxies. */
2696
2707
  declare function __getRuntime(): RuntimeServices;
2697
2708
  /** A lifecycle hook. Sync or async; the runtime awaits what it returns. */
2698
2709
  type LifecycleHook = () => void | Promise<void>;
@@ -2805,7 +2816,7 @@ declare const Database: EnvTypedDatabase;
2805
2816
  /** Firestore-like document client (PalDocs). */
2806
2817
  declare const Documents: PalbaseDocsClient;
2807
2818
  /**
2808
- * `buckets.<name>` — the storage twin of `Database.tables.<name>`, and the same
2819
+ * `buckets.<name>` — the storage twin of `Database.public.<name>`, and the same
2809
2820
  * mechanism: `config/storage.ts` generates a `Buckets` augmentation into
2810
2821
  * `palbase-env.d.ts`, so a bucket name is a property with no import and no
2811
2822
  * generic, and a typo is a compile error.
@@ -2869,21 +2880,46 @@ declare const Notifications: PalbaseNotificationsClient;
2869
2880
  /**
2870
2881
  * Feature flags.
2871
2882
  *
2872
- * Mirrors the `Database` / `Database.asService()` model. The default surface is
2883
+ * Mirrors the `Database` / `Database.$asService()` model. The default surface is
2873
2884
  * RLS-equivalent for flags: reads resolve against the CURRENT request user and
2874
2885
  * `Flags.setOverride(key, value)` writes an override for that same signed-in
2875
2886
  * user (no userId argument, no admin power). Cross-user admin writes
2876
- * (`setOverrideForUser`, …) live behind `Flags.asService()` — explicit and
2877
- * greppable, just like `Database.asService()`.
2887
+ * (`setOverrideForUser`, …) live behind `Flags.$asService()` — explicit and
2888
+ * greppable, just like `Database.$asService()`.
2878
2889
  *
2879
2890
  * @example
2880
2891
  * import { Flags } from "@palbase/backend";
2881
2892
  *
2882
2893
  * if (await Flags.isEnabled("new_checkout")) { ... } // current user
2883
2894
  * await Flags.setOverride("new_checkout", true); // current user
2884
- * await Flags.asService().setOverrideForUser("u_9", "x", true); // cross-user
2895
+ * await Flags.$asService().setOverrideForUser("u_9", "x", true); // cross-user
2896
+ */
2897
+ /**
2898
+ * The ambient `Flags` singleton's own surface — NOT the raw client's.
2899
+ *
2900
+ * MEASURED, as a consumer, against the built tarball: annotating the singleton
2901
+ * as `PalbaseFlagsClient` ERASED the `$asService` that `Object.assign` adds, so
2902
+ * `Flags.$asService()` did not exist for anyone outside this package —
2903
+ *
2904
+ * TS2551: Property '$asService' does not exist on type 'PalbaseFlagsClient'.
2905
+ * Did you mean 'asService'?
2906
+ *
2907
+ * — while the only path that DID compile (`asService()`) throws by design. The
2908
+ * feature was written and unreachable, which is the defect class this surface
2909
+ * exists to remove.
2910
+ *
2911
+ * `Database` never had the problem because its singleton carries its OWN type
2912
+ * (`EnvTypedDatabase`) rather than the raw client's (`DBClient`). This is that,
2913
+ * for `Flags`. The raw `PalbaseFlagsClient.asService()` is untouched (FR-023) —
2914
+ * it is the seam this forwards to.
2885
2915
  */
2886
- declare const Flags: PalbaseFlagsClient;
2916
+ type PalbaseFlagsAmbient = Omit<PalbaseFlagsClient, "asService"> & {
2917
+ /** RLS'i aşan, kullanıcılar arası yazma yüzeyi. */
2918
+ $asService(): PalbaseFlagsServiceClient;
2919
+ /** 31.0.0 öncesinin adı — SESSİZCE çalışmaz, yerini söyleyerek fırlatır. */
2920
+ asService(): never;
2921
+ };
2922
+ declare const Flags: PalbaseFlagsAmbient;
2887
2923
  /**
2888
2924
  * The Realtime broadcast singleton for the current request scope. Backend-side
2889
2925
  * Realtime is BROADCAST-ONLY (a stateless handler can push but not subscribe —
@@ -3454,8 +3490,20 @@ type WithRowOf<Rels, R extends keyof Rels, Q> = Rels[R] extends {
3454
3490
  * özdeş değil" hâle getirirdi ve tam eşitlik iddiaları kırılırdı. Ölçüldü:
3455
3491
  * `toEqualTypeOf` dört yerde düştü.
3456
3492
  */
3457
- type WithResult<Rels, W> = [keyof W] extends [never] ? unknown : {
3458
- [R in keyof W & keyof Rels]: WithRowOf<Rels, R, W[R]>;
3493
+ /**
3494
+ * Yalnız GERÇEKTEN istenen dallar. `undefined extends W[R]` olan anahtarlar
3495
+ * düşüyor — ve bu, `ReturnType<typeof t.findMany>` yazan herkes için kritik:
3496
+ * generic bir imzada `ReturnType` tip parametrelerini VARSAYILANLA değil
3497
+ * KISITLA örnekliyor, yani `W = WithInput<Rels>` oluyor ve her ilişki
3498
+ * (`?:` ile, dolayısıyla `| undefined`) zorunlu bir alana dönüşüyordu.
3499
+ * Gerçek projede ölçüldü: `type MemberRow = Awaited<ReturnType<…findMany>>[number]`
3500
+ * 32 ilişkiyi birden ister hâle geldi ve 4 yerde kırıldı.
3501
+ */
3502
+ type RequestedWith<W> = {
3503
+ [R in keyof W]-?: undefined extends W[R] ? never : R;
3504
+ }[keyof W];
3505
+ type WithResult<Rels, W> = [RequestedWith<W>] extends [never] ? unknown : {
3506
+ [R in RequestedWith<W> & keyof Rels]: WithRowOf<Rels, R, W[R]>;
3459
3507
  };
3460
3508
  type QueryInput<Row, K extends keyof Row = keyof Row, Rels = unknown> = {
3461
3509
  /**
@@ -1,6 +1,6 @@
1
- import { D as DBClient, b as DBOps, T as TxPlanBody, c as TxPlanResponse, A as AuthSpec, C as CacheClient, a as RuntimeServices, _ as __runWithRuntime, d as __requestALS } from './index-BgWnP07a.js';
1
+ import { D as DBClient, b as DBOps, T as TxPlanBody, c as TxPlanResponse, A as AuthSpec, C as CacheClient, a as RuntimeServices, _ as __runWithRuntime, d as __requestALS } from './index-Dg10RnZO.js';
2
2
  import { T as Token } from './module-Dl1KFVtc.js';
3
- import { R as RouteMeta } from './registry-ClzjxIWy.js';
3
+ import { R as RouteMeta } from './registry-B-sxJJN0.js';
4
4
 
5
5
  /**
6
6
  * engine/config.ts — settings from the environment, and the gate that refuses
@@ -28,16 +28,32 @@ interface User {
28
28
  /**
29
29
  * Whether that email address has been confirmed.
30
30
  *
31
- * Server-resolved from the verified user profile, not read from the JWT: a
32
- * token claim is only true as of when the token was minted, so a user who
33
- * verifies mid-session would keep reporting `false` until it expired.
31
+ * READ FROM THE VERIFIED TOKEN (`email_verified`), so it says what was true
32
+ * when that token was minted: a user who confirms their address mid-session
33
+ * keeps reporting `false` until their next one. {@link User.roles} is the
34
+ * deliberate exception — it is read from the table on every request.
35
+ *
36
+ * This paragraph used to claim the opposite ("server-resolved from the
37
+ * verified profile, not read from the JWT") and to describe a ~30-second
38
+ * profile cache. Measured 2026-09-04: the engine fills this field from
39
+ * `claims.email_verified` and no such cache exists anywhere in the runtime.
40
+ * Whether the claim or a profile read is the RIGHT source is a separate
41
+ * question and an open one — but the type may not answer it wrongly while it
42
+ * is open.
34
43
  *
35
44
  * `false` for a phone-only user (no address to confirm) and for any user who
36
- * has not clicked through yet. Before this field existed, answering "is this
37
- * address confirmed" in a handler cost an extra network round-trip PER
38
- * REQUEST for one boolean the runtime already had.
45
+ * has not clicked through yet.
39
46
  */
40
47
  emailVerified: boolean;
48
+ /**
49
+ * The caller's DATABASE role — the string RLS reads, not an application role.
50
+ *
51
+ * It is the literal `"authenticated"` for every signed-in caller and
52
+ * `"anon"` for every unauthenticated one, so `user.role === "admin"` is
53
+ * ALWAYS false and reads like a working check. Application roles are
54
+ * {@link User.roles}; this field says which Postgres role the request runs
55
+ * as, and that is the only question it answers.
56
+ */
41
57
  role: string;
42
58
  /**
43
59
  * The caller's APPLICATION roles, as assigned in `auth.user_roles`.
@@ -2603,7 +2619,7 @@ interface PalbaseLinksClient {
2603
2619
  * \@Controller("/todos")
2604
2620
  * export default class TodosController {
2605
2621
  * \@Post("") create(\@Body(CreateTodoBody) body: CreateTodoBody): unknown {
2606
- * return Database.insert("todos", { title: body.title });
2622
+ * return Database.public.todos.insert({ title: body.title });
2607
2623
  * }
2608
2624
  * }
2609
2625
  *
@@ -2688,11 +2704,6 @@ declare function __setRuntime(services: RuntimeServices): void;
2688
2704
  * The persistent worker calls this once per request so concurrent requests
2689
2705
  * never share a services slot. NOT part of the public author-facing API. */
2690
2706
  declare function __runWithRuntime<T>(services: RuntimeServices, fn: () => T): T;
2691
- /** Read the live clients, throwing if accessed outside a request scope.
2692
- *
2693
- * Resolves the ALS store first (persistent server, per-request), then the
2694
- * process-global fallback (dev-server / tests). NOT part of the public
2695
- * author-facing API — used by the runtime and the singleton Proxies. */
2696
2707
  declare function __getRuntime(): RuntimeServices;
2697
2708
  /** A lifecycle hook. Sync or async; the runtime awaits what it returns. */
2698
2709
  type LifecycleHook = () => void | Promise<void>;
@@ -2805,7 +2816,7 @@ declare const Database: EnvTypedDatabase;
2805
2816
  /** Firestore-like document client (PalDocs). */
2806
2817
  declare const Documents: PalbaseDocsClient;
2807
2818
  /**
2808
- * `buckets.<name>` — the storage twin of `Database.tables.<name>`, and the same
2819
+ * `buckets.<name>` — the storage twin of `Database.public.<name>`, and the same
2809
2820
  * mechanism: `config/storage.ts` generates a `Buckets` augmentation into
2810
2821
  * `palbase-env.d.ts`, so a bucket name is a property with no import and no
2811
2822
  * generic, and a typo is a compile error.
@@ -2869,21 +2880,46 @@ declare const Notifications: PalbaseNotificationsClient;
2869
2880
  /**
2870
2881
  * Feature flags.
2871
2882
  *
2872
- * Mirrors the `Database` / `Database.asService()` model. The default surface is
2883
+ * Mirrors the `Database` / `Database.$asService()` model. The default surface is
2873
2884
  * RLS-equivalent for flags: reads resolve against the CURRENT request user and
2874
2885
  * `Flags.setOverride(key, value)` writes an override for that same signed-in
2875
2886
  * user (no userId argument, no admin power). Cross-user admin writes
2876
- * (`setOverrideForUser`, …) live behind `Flags.asService()` — explicit and
2877
- * greppable, just like `Database.asService()`.
2887
+ * (`setOverrideForUser`, …) live behind `Flags.$asService()` — explicit and
2888
+ * greppable, just like `Database.$asService()`.
2878
2889
  *
2879
2890
  * @example
2880
2891
  * import { Flags } from "@palbase/backend";
2881
2892
  *
2882
2893
  * if (await Flags.isEnabled("new_checkout")) { ... } // current user
2883
2894
  * await Flags.setOverride("new_checkout", true); // current user
2884
- * await Flags.asService().setOverrideForUser("u_9", "x", true); // cross-user
2895
+ * await Flags.$asService().setOverrideForUser("u_9", "x", true); // cross-user
2896
+ */
2897
+ /**
2898
+ * The ambient `Flags` singleton's own surface — NOT the raw client's.
2899
+ *
2900
+ * MEASURED, as a consumer, against the built tarball: annotating the singleton
2901
+ * as `PalbaseFlagsClient` ERASED the `$asService` that `Object.assign` adds, so
2902
+ * `Flags.$asService()` did not exist for anyone outside this package —
2903
+ *
2904
+ * TS2551: Property '$asService' does not exist on type 'PalbaseFlagsClient'.
2905
+ * Did you mean 'asService'?
2906
+ *
2907
+ * — while the only path that DID compile (`asService()`) throws by design. The
2908
+ * feature was written and unreachable, which is the defect class this surface
2909
+ * exists to remove.
2910
+ *
2911
+ * `Database` never had the problem because its singleton carries its OWN type
2912
+ * (`EnvTypedDatabase`) rather than the raw client's (`DBClient`). This is that,
2913
+ * for `Flags`. The raw `PalbaseFlagsClient.asService()` is untouched (FR-023) —
2914
+ * it is the seam this forwards to.
2885
2915
  */
2886
- declare const Flags: PalbaseFlagsClient;
2916
+ type PalbaseFlagsAmbient = Omit<PalbaseFlagsClient, "asService"> & {
2917
+ /** RLS'i aşan, kullanıcılar arası yazma yüzeyi. */
2918
+ $asService(): PalbaseFlagsServiceClient;
2919
+ /** 31.0.0 öncesinin adı — SESSİZCE çalışmaz, yerini söyleyerek fırlatır. */
2920
+ asService(): never;
2921
+ };
2922
+ declare const Flags: PalbaseFlagsAmbient;
2887
2923
  /**
2888
2924
  * The Realtime broadcast singleton for the current request scope. Backend-side
2889
2925
  * Realtime is BROADCAST-ONLY (a stateless handler can push but not subscribe —
@@ -3454,8 +3490,20 @@ type WithRowOf<Rels, R extends keyof Rels, Q> = Rels[R] extends {
3454
3490
  * özdeş değil" hâle getirirdi ve tam eşitlik iddiaları kırılırdı. Ölçüldü:
3455
3491
  * `toEqualTypeOf` dört yerde düştü.
3456
3492
  */
3457
- type WithResult<Rels, W> = [keyof W] extends [never] ? unknown : {
3458
- [R in keyof W & keyof Rels]: WithRowOf<Rels, R, W[R]>;
3493
+ /**
3494
+ * Yalnız GERÇEKTEN istenen dallar. `undefined extends W[R]` olan anahtarlar
3495
+ * düşüyor — ve bu, `ReturnType<typeof t.findMany>` yazan herkes için kritik:
3496
+ * generic bir imzada `ReturnType` tip parametrelerini VARSAYILANLA değil
3497
+ * KISITLA örnekliyor, yani `W = WithInput<Rels>` oluyor ve her ilişki
3498
+ * (`?:` ile, dolayısıyla `| undefined`) zorunlu bir alana dönüşüyordu.
3499
+ * Gerçek projede ölçüldü: `type MemberRow = Awaited<ReturnType<…findMany>>[number]`
3500
+ * 32 ilişkiyi birden ister hâle geldi ve 4 yerde kırıldı.
3501
+ */
3502
+ type RequestedWith<W> = {
3503
+ [R in keyof W]-?: undefined extends W[R] ? never : R;
3504
+ }[keyof W];
3505
+ type WithResult<Rels, W> = [RequestedWith<W>] extends [never] ? unknown : {
3506
+ [R in RequestedWith<W> & keyof Rels]: WithRowOf<Rels, R, W[R]>;
3459
3507
  };
3460
3508
  type QueryInput<Row, K extends keyof Row = keyof Row, Rels = unknown> = {
3461
3509
  /**
@@ -1,6 +1,6 @@
1
- import { D as DBClient, b as DBOps, T as TxPlanBody, c as TxPlanResponse, A as AuthSpec, C as CacheClient, a as RuntimeServices, _ as __runWithRuntime, d as __requestALS } from './index-DgYkdNT9.cjs';
1
+ import { D as DBClient, b as DBOps, T as TxPlanBody, c as TxPlanResponse, A as AuthSpec, C as CacheClient, a as RuntimeServices, _ as __runWithRuntime, d as __requestALS } from './index-BWgnGj68.cjs';
2
2
  import { T as Token } from './module-Dl1KFVtc.cjs';
3
- import { R as RouteMeta } from './registry-BcRYIQ-R.cjs';
3
+ import { R as RouteMeta } from './registry-6VT5RPeO.cjs';
4
4
 
5
5
  /**
6
6
  * engine/config.ts — settings from the environment, and the gate that refuses
package/dist/index.cjs CHANGED
@@ -1286,9 +1286,29 @@ function __runWithRuntime(services, fn) {
1286
1286
  }, fn);
1287
1287
  }
1288
1288
  __name(__runWithRuntime, "__runWithRuntime");
1289
+ var REQUIRED_SERVICES = [
1290
+ "Database",
1291
+ "Auth",
1292
+ "Secrets",
1293
+ "Documents",
1294
+ "Storage",
1295
+ "Cache",
1296
+ "Log",
1297
+ "Notifications",
1298
+ "Flags",
1299
+ "Realtime"
1300
+ ];
1301
+ function refuseIncompleteBox(services) {
1302
+ const missing = REQUIRED_SERVICES.filter((k) => services[k] === void 0);
1303
+ if (missing.length > 0) {
1304
+ 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.`);
1305
+ }
1306
+ return services;
1307
+ }
1308
+ __name(refuseIncompleteBox, "refuseIncompleteBox");
1289
1309
  function __getRuntime() {
1290
1310
  const scoped = __requestALS.getStore();
1291
- if (scoped) return scoped.runtime;
1311
+ if (scoped) return refuseIncompleteBox(scoped.runtime);
1292
1312
  if (runtime === null) {
1293
1313
  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).");
1294
1314
  }
@@ -1430,7 +1450,12 @@ function makeTypedSurface(raw2) {
1430
1450
  $findById: /* @__PURE__ */ __name((table, id) => raw2.findById(table, id), "$findById"),
1431
1451
  $findMany: /* @__PURE__ */ __name((table, query, opts) => raw2.findMany(table, query, opts), "$findMany"),
1432
1452
  $put: /* @__PURE__ */ __name((table, data, opts) => raw2.put(table, data, opts), "$put"),
1433
- $updateMany: /* @__PURE__ */ __name((table, where, set) => raw2.updateMany(table, where, set), "$updateMany"),
1453
+ // `opts` İLETİLİR. Düşürüldüğü sürece `Database.$updateMany(t, w, s,
1454
+ // { returning: false })` sessizce SATIRLARI döndürüyordu, sayıyı değil —
1455
+ // ve `if (n === 0) throw new Conflict(...)` guard'ı HİÇ çalışmıyordu,
1456
+ // çünkü `[] === 0` yanlıştır. Komşu forwarder'lar (`$findMany`, `$put`)
1457
+ // kendi opsiyonlarını zaten iletiyordu; bu biri unutulmuştu.
1458
+ $updateMany: /* @__PURE__ */ __name((table, where, set, opts) => raw2.updateMany(table, where, set, opts), "$updateMany"),
1434
1459
  $deleteMany: /* @__PURE__ */ __name((table, where) => raw2.deleteMany(table, where), "$deleteMany"),
1435
1460
  $count: /* @__PURE__ */ __name((table, where) => raw2.count(table, where), "$count"),
1436
1461
  $search: /* @__PURE__ */ __name((table, params) => raw2.search(table, params), "$search"),
@@ -1561,13 +1586,13 @@ var Flags = Object.assign({
1561
1586
  * Lazily resolve the runtime's cross-user sibling on each call. We do NOT
1562
1587
  * cache it: `rawFlags.asService()` reads the CURRENT request scope through
1563
1588
  * the runtime proxy, so caching would leak one request's sibling into
1564
- * another concurrent request. Mirrors `Database.asService()`.
1589
+ * another concurrent request. Mirrors `Database.$asService()`.
1565
1590
  */
1566
1591
  $asService() {
1567
1592
  return rawFlags.asService();
1568
1593
  },
1569
1594
  /**
1570
- * The name this member carried before 31.0.0.
1595
+ * The name this member carried before 32.0.0.
1571
1596
  *
1572
1597
  * It does NOT work silently. A retired member that quietly keeps returning
1573
1598
  * is how a rename becomes a mystery: the old call site goes on compiling,
@@ -1577,7 +1602,7 @@ var Flags = Object.assign({
1577
1602
  * Database.$asService()" while the promise went unkept.
1578
1603
  */
1579
1604
  asService() {
1580
- throw new Error("Flags.asService() was renamed to Flags.$asService() in 31.0.0 \u2014 system members carry the `$` prefix, like Database.$asService().");
1605
+ throw new Error("Flags.asService() was renamed to Flags.$asService() in 32.0.0 \u2014 system members carry the `$` prefix, like Database.$asService().");
1581
1606
  }
1582
1607
  });
1583
1608
  var Realtime = makeServiceProxy("Realtime");
@@ -3317,15 +3342,27 @@ var CODECS = {
3317
3342
  // not representable and the author wants `asDecimal()` instead.
3318
3343
  number: {
3319
3344
  fromDb: /* @__PURE__ */ __name((v) => Number(v), "fromDb"),
3320
- toDb: /* @__PURE__ */ __name((v) => String(v), "toDb"),
3345
+ // YAZMA YOLUNDA SESSİZ BOZULMA YOK. `toDb(9007199254740993)` bir zamanlar
3346
+ // "9007199254740992" yazıyordu: uygulamanın tuttuğundan BAŞKA bir sayı
3347
+ // kolona gidiyordu ve hiçbir şey söylemiyordu. `asNumber()`'ın JSDoc'u
3348
+ // "2^53'ün altında güvenli" diyordu ama bunu uygulayan bir şey yoktu.
3349
+ toDb: /* @__PURE__ */ __name((v) => {
3350
+ if (typeof v === "number" && !Number.isSafeInteger(v)) {
3351
+ 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.`);
3352
+ }
3353
+ return String(v);
3354
+ }, "toDb"),
3321
3355
  tsType: "number"
3322
3356
  },
3323
3357
  // Exact-precision column → the string Postgres sent. It exists so the
3324
3358
  // DECLARATION is explicit: "this column is a decimal I handle as text",
3325
3359
  // rather than the ABSENCE of a declaration meaning the same thing by default.
3360
+ // NULL güvencesi ÇİFTİN KENDİSİNDE. Motor NULL'ı zaten atlıyor (üç ayrı
3361
+ // yerde), ama bir değişmez, onu koruyan üç `if`'in DIŞINDA yazılmalı: biri
3362
+ // düşerse `String(null)` kolonu "null" dizesine çevirirdi.
3326
3363
  decimal: {
3327
- fromDb: /* @__PURE__ */ __name((v) => String(v), "fromDb"),
3328
- toDb: /* @__PURE__ */ __name((v) => String(v), "toDb"),
3364
+ fromDb: /* @__PURE__ */ __name((v) => v === null || v === void 0 ? v : String(v), "fromDb"),
3365
+ toDb: /* @__PURE__ */ __name((v) => v === null || v === void 0 ? v : String(v), "toDb"),
3329
3366
  tsType: "string"
3330
3367
  }
3331
3368
  };
@@ -3558,12 +3595,22 @@ var ColumnBuilder = class _ColumnBuilder {
3558
3595
  if (this._def.transform !== void 0) {
3559
3596
  throw new Error(`.${method}() and .transform() are two ways to say the same thing on one column \u2014 keep .${method}().`);
3560
3597
  }
3561
- this._def.codec = codec;
3562
- return new _ColumnBuilder(this._def.type, this._def);
3598
+ if (this._def.codec !== void 0) {
3599
+ throw new Error(`column already declares .as${this._def.codec === "number" ? "Number" : "Decimal"}(); a column has ONE codec.`);
3600
+ }
3601
+ return new _ColumnBuilder(this._def.type, {
3602
+ ...this._def,
3603
+ codec
3604
+ });
3563
3605
  }
3564
3606
  transform(fns) {
3565
- this._def.transform = fns;
3566
- return new _ColumnBuilder(this._def.type, this._def);
3607
+ if (this._def.codec !== void 0) {
3608
+ throw new Error(`column already declares a named codec; .transform() and .as*() are two ways to say the same thing \u2014 keep the codec.`);
3609
+ }
3610
+ return new _ColumnBuilder(this._def.type, {
3611
+ ...this._def,
3612
+ transform: fns
3613
+ });
3567
3614
  }
3568
3615
  };
3569
3616
  function uuid() {