@palbase/backend 38.0.16 → 39.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 +46 -1
- package/dist/bin/palbase-backend.cjs.map +1 -1
- package/dist/bin/palbase-backend.js +5 -5
- package/dist/{chunk-LFUNRWUE.js → chunk-4J6I4KVQ.js} +67 -3
- package/dist/{chunk-LFUNRWUE.js.map → chunk-4J6I4KVQ.js.map} +1 -1
- package/dist/{chunk-7WAGQ3VR.js → chunk-AR2I4M7I.js} +497 -17
- package/dist/chunk-AR2I4M7I.js.map +1 -0
- package/dist/{chunk-YANGWAIM.js → chunk-H7EKL6HC.js} +2 -2
- package/dist/{chunk-EHKEMHB4.js → chunk-JJSR4BUX.js} +49 -5
- package/dist/chunk-JJSR4BUX.js.map +1 -0
- package/dist/{chunk-CS6NQ6PO.js → chunk-JPTFYHP3.js} +2 -2
- package/dist/{chunk-CS6NQ6PO.js.map → chunk-JPTFYHP3.js.map} +1 -1
- package/dist/{chunk-X7UR3VXA.js → chunk-XZWOMPD3.js} +3 -2
- package/dist/chunk-XZWOMPD3.js.map +1 -0
- package/dist/{chunk-PDD55QWN.js → chunk-ZNQDL466.js} +2 -2
- package/dist/db/index.cjs +378 -14
- 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 +46 -1
- 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-CI7S0Wqv.d.cts → index-B0mjUnxy.d.cts} +25 -2
- package/dist/{index-CUy50OLU.d.ts → index-eN4KzGa5.d.ts} +555 -29
- package/dist/{index-DRx880KY.d.cts → index-pK2At5Yc.d.cts} +555 -29
- package/dist/{index-Ogi30dbt.d.ts → index-yDno9Ju9.d.ts} +25 -2
- package/dist/index.cjs +628 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +183 -8
- package/dist/index.d.ts +183 -8
- package/dist/index.js +74 -6
- package/dist/index.js.map +1 -1
- package/dist/openapi/index.cjs.map +1 -1
- package/dist/openapi/index.d.cts +2 -2
- package/dist/openapi/index.d.ts +2 -2
- package/dist/openapi/index.js +2 -2
- package/dist/{registry-C7UCkQf0.d.cts → registry-9dNeVN5d.d.cts} +1 -1
- package/dist/{registry-DJcvbomD.d.ts → registry-B8ddZiMY.d.ts} +1 -1
- package/dist/test/index.cjs +19 -0
- 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 +20 -2
- package/dist/test/index.js.map +1 -1
- package/docs/README.md +1 -1
- package/docs/database.md +79 -0
- package/docs/llms-full.txt +462 -33
- package/docs/schema.md +382 -32
- package/package.json +1 -1
- package/template/package.json +1 -1
- package/dist/chunk-7WAGQ3VR.js.map +0 -1
- package/dist/chunk-EHKEMHB4.js.map +0 -1
- package/dist/chunk-X7UR3VXA.js.map +0 -1
- /package/dist/{chunk-YANGWAIM.js.map → chunk-H7EKL6HC.js.map} +0 -0
- /package/dist/{chunk-PDD55QWN.js.map → chunk-ZNQDL466.js.map} +0 -0
|
@@ -191,7 +191,7 @@ var DeadlockDetected = class extends Conflict {
|
|
|
191
191
|
return typeof e === "object" && e !== null && e.name === "DeadlockDetected";
|
|
192
192
|
}
|
|
193
193
|
constructor(sqlstate = SQLSTATE.deadlockDetected, message, data) {
|
|
194
|
-
super(message ?? "deadlock: iki i\u015Flem birbirinin kilidini bekledi ve bu i\u015Flem iptal edildi \u2014 kilit s\u0131ras\u0131n\u0131
|
|
194
|
+
super(message ?? "deadlock: iki i\u015Flem birbirinin kilidini bekledi ve bu i\u015Flem iptal edildi \u2014 kilit s\u0131ras\u0131n\u0131 sabitleyin: id'ler elinizdeyse Database.$lockRows(tablo, id'ler), elinizde bir filtre varsa Database.$lockRowsWhere(tablo, filtre) kullan\u0131n", "deadlock_detected", data);
|
|
195
195
|
this.name = "DeadlockDetected";
|
|
196
196
|
this.sqlstate = sqlstate;
|
|
197
197
|
}
|
|
@@ -223,4 +223,4 @@ export {
|
|
|
223
223
|
DeadlockDetected,
|
|
224
224
|
TooManyRequests
|
|
225
225
|
};
|
|
226
|
-
//# sourceMappingURL=chunk-
|
|
226
|
+
//# sourceMappingURL=chunk-JPTFYHP3.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/db/sqlstate.ts","../src/errors.ts"],"sourcesContent":["/** PostgreSQL protocol identifiers, shared by error mapping and retry policy. */\nexport const SQLSTATE = Object.freeze({\n serializationFailure: \"40001\",\n deadlockDetected: \"40P01\",\n queryCancelled: \"57014\",\n commandGuardFailed: \"PBC01\",\n} as const);\n\n/** These errors confirm that PostgreSQL aborted the transaction. A connection\n * failure does not: the COMMIT may already have succeeded. */\nexport function isTransactionRollbackState(state: unknown): boolean {\n return state === SQLSTATE.serializationFailure || state === SQLSTATE.deadlockDetected;\n}\n","import { SQLSTATE, isTransactionRollbackState } from \"./db/sqlstate.js\";\n\n/**\n * The brand that identifies an HttpError ACROSS SDK instances.\n *\n * A process legitimately holds more than one copy of this SDK — the runtime\n * loads the engine from its own node_modules while the tenant's bundle carries\n * an inlined copy, which is why the controller registry and the error registry\n * are both anchored on `Symbol.for`. The one place that did not follow the\n * pattern was the engine's catch: `err instanceof HttpError` compares CLASS\n * IDENTITY, so a `throw new NotFound()` from the bundle's copy did not match\n * the engine's copy and every typed error in every deployed backend degraded to\n * `500 internal_error`. Measured through the edge on a real deploy: a route\n * throwing `NotFound` answered 500 while the runtime's own log printed the\n * error object with `status: 404` right beside it.\n *\n * `Symbol.for` puts this in the cross-realm registry, so every copy of the SDK\n * agrees on it by VALUE rather than by identity.\n */\nexport const HTTP_ERROR_BRAND: unique symbol = Symbol.for(\"palbase.backend.httpError\");\n\n/**\n * Set on an `HttpError` the ENGINE built out of a driver failure, as opposed to\n * one the author constructed to ANSWER a request.\n *\n * The distinction cannot be read off the status, and 409 is why. The scaffold\n * teaches `throw new Conflict(\"title already taken\")` as the way to answer\n * (template/AGENTS.md), and the engine raises `UniqueViolation` — also a 409 —\n * when a write hits a unique index. Logging by status therefore either loses the\n * engine's event or writes an \"unhandled\" line every time an author takes the\n * documented path. Measured: it did the second.\n *\n * `Symbol.for` so the mark survives the bundle/runtime SDK split, the same way\n * {@link HTTP_ERROR_BRAND} does.\n */\nexport const ENGINE_RAISED: unique symbol = Symbol.for(\"palbase.backend.engineRaised\") as never;\n\n/** Mark `e` as engine-raised and return it, so a conversion site reads as one expression. */\nexport function markEngineRaised<E extends object>(e: E): E {\n (e as Record<symbol, unknown>)[ENGINE_RAISED] = true;\n return e;\n}\n\n/** Whether the engine built this error, rather than the author throwing it to answer. */\nexport function isEngineRaised(e: unknown): boolean {\n return typeof e === \"object\" && e !== null && (e as Record<symbol, unknown>)[ENGINE_RAISED] === true;\n}\n\n/**\n * Whether a thrown value is an HttpError from ANY copy of this SDK.\n *\n * The shape is checked as well as the brand: the brand says \"this claims to be\n * one of ours\", the fields say the envelope can actually be built from it, and\n * a half-formed object must fall through to the 500 path rather than produce a\n * malformed response.\n */\nexport function isHttpError(err: unknown): err is HttpError {\n if (typeof err !== \"object\" || err === null) return false;\n const e = err as Record<PropertyKey, unknown>;\n return (\n e[HTTP_ERROR_BRAND] === true &&\n typeof e.status === \"number\" &&\n typeof e.error === \"string\" &&\n typeof e.errorDescription === \"string\"\n );\n}\n\n/** HTTP error with structured error response format.\n *\n * The base class for the throwable error classes (`PalError`, `Conflict`,\n * `NotFound`, …). Construct one directly with `throw new HttpError(404,\n * \"todo_not_found\", \"No such todo\")`, or throw a named subclass\n * (`throw new NotFound(\"todo not found\")`). The runtime catches any `HttpError`\n * and emits the standard envelope; on the wire (and to iOS) it surfaces as\n * `BackendError.server(code, status, message, requestId)`.\n *\n * The optional `data` field carries a structured payload alongside the\n * standard envelope — for errors that need to ship extra context\n * (e.g. `new Conflict(\"locked\", \"title_locked\", { retryAfter: 30 })`). It rides\n * through to the iOS typed enum's associated value.\n */\nexport class HttpError extends Error {\n public readonly status: number;\n public readonly error: string;\n public readonly errorDescription: string;\n public readonly data?: unknown;\n /** See {@link HTTP_ERROR_BRAND} — how the engine recognises this across SDK copies. */\n public readonly [HTTP_ERROR_BRAND] = true;\n\n constructor(status: number, error: string, errorDescription: string, data?: unknown) {\n super(errorDescription);\n this.name = \"HttpError\";\n this.status = status;\n this.error = error;\n this.errorDescription = errorDescription;\n if (data !== undefined) {\n this.data = data;\n }\n }\n\n /**\n * Serialize to the standard Palbase error response format.\n * The `requestId` is injected by the runtime layer from the request context.\n * When called without arguments (e.g. JSON.stringify), request_id is omitted.\n * When `data` is set, it is appended as a strict-superset field.\n */\n toJSON(requestId?: string): {\n error: string;\n error_description: string;\n status: number;\n request_id?: string;\n data?: unknown;\n } {\n const result: {\n error: string;\n error_description: string;\n status: number;\n request_id?: string;\n data?: unknown;\n } = {\n error: this.error,\n error_description: this.errorDescription,\n status: this.status,\n };\n if (requestId) {\n result.request_id = requestId;\n }\n if (this.data !== undefined) {\n result.data = this.data;\n }\n return result;\n }\n}\n\n/**\n * Throw with a custom HTTP status + wire code. The general-purpose escape hatch\n * when none of the named classes (`Conflict`/`NotFound`/…) fits.\n *\n * @example\n * throw new PalError(418, \"teapot\", \"I'm a teapot\");\n */\nexport class PalError extends HttpError {\n constructor(status: number, code: string, description: string, data?: unknown) {\n super(status, code, description, data);\n this.name = \"PalError\";\n }\n}\n\n/** Base for the named status classes. Each subclass fixes its HTTP status; the\n * `code` defaults to the class's canonical wire code (overridable), and the\n * `message` defaults to a human-readable label (overridable). */\nabstract class NamedHttpError extends HttpError {\n protected constructor(\n status: number,\n defaultCode: string,\n name: string,\n message?: string,\n code?: string,\n data?: unknown,\n ) {\n super(status, code ?? defaultCode, message ?? defaultMessage(name), data);\n this.name = name;\n }\n}\n\n/** Derive a default human-readable message from a class name\n * (\"NotFound\" → \"Not found\", \"TooManyRequests\" → \"Too many requests\"). */\nfunction defaultMessage(name: string): string {\n const spaced = name.replace(/([a-z0-9])([A-Z])/g, \"$1 $2\");\n return spaced.charAt(0).toUpperCase() + spaced.slice(1).toLowerCase();\n}\n\n/**\n * 400 — the request was malformed or failed validation. Carries a fixed typed\n * payload: `new BadRequest({ fields: [{ field: \"email\", message: \"invalid\" }] })`.\n * The shape is declared once in the SDK so codegen surfaces `error.data.fields`\n * typed on the client.\n */\nexport class BadRequest extends NamedHttpError {\n public declare readonly data: BadRequestData;\n constructor(data: BadRequestData, message?: string) {\n super(400, \"bad_request\", \"BadRequest\", message, undefined, data);\n }\n}\n\n/** 401 — the caller is not authenticated. */\nexport class Unauthorized extends NamedHttpError {\n constructor(message?: string, code?: string, data?: unknown) {\n super(401, \"unauthorized\", \"Unauthorized\", message, code, data);\n }\n}\n\n/** 403 — the caller is authenticated but not allowed. */\nexport class Forbidden extends NamedHttpError {\n constructor(message?: string, code?: string, data?: unknown) {\n super(403, \"forbidden\", \"Forbidden\", message, code, data);\n }\n}\n\n/** 404 — the requested resource does not exist. */\nexport class NotFound extends NamedHttpError {\n constructor(message?: string, code?: string, data?: unknown) {\n super(404, \"not_found\", \"NotFound\", message, code, data);\n }\n}\n\n/** 409 — the request conflicts with the current state. */\nexport class Conflict extends NamedHttpError {\n constructor(message?: string, code?: string, data?: unknown) {\n super(409, \"conflict\", \"Conflict\", message, code, data);\n }\n}\n\n/**\n * 409 — a write was refused because it would duplicate an existing row.\n * Carries the NAME of the unique constraint Postgres named (`users_email_key`).\n *\n * The engine produces it: a statement rejected with SQLSTATE `23505` is\n * converted here rather than surfacing as an opaque driver error (see\n * `engine/db.ts`, `diagnosingDriver`). What that removes is the string match —\n * before this, the only way to act on a duplicate was to test the driver\n * message for \"duplicate key value violates unique constraint\", a contract\n * nobody signed that breaks on a Postgres upgrade, a locale, or a constraint\n * rename, silently and in production.\n *\n * THE NAME IS A FIELD AND STAYS OUT OF THE DEFAULT MESSAGE. The two are not\n * the same audience. `constraint` is read by the code that catches this — the\n * developer, who already knows the schema. `errorDescription` is the HTTP\n * response body, and an UNCAUGHT duplicate puts it in front of the\n * application's end user: `users_email_key` there discloses how the schema is\n * built to whoever sent the request. The platform's own data API took the same\n * decision one surface over and wrote down why —\n * `v2/internal/modules/database/internal/handler/pgerror.go:83-87` collapses\n * every 23xxx to a generic conflict, \"never disclose the constraint/column\n * name\". A thrower who WANTS the name on the wire passes it deliberately\n * (`new UniqueViolation(c, \\`\\${c} already exists\\`)`, or through `data`).\n *\n * @example\n * try {\n * await Database.public.users.insert({ email });\n * } catch (e) {\n * if (UniqueViolation.is(e) && e.constraint === \"users_email_key\") {\n * throw new Conflict(\"That email is taken\", \"email_taken\");\n * }\n * throw e;\n * }\n */\nexport class UniqueViolation extends Conflict {\n /**\n * Whether `e` is a unique violation — REGARDLESS of which copy of this SDK\n * constructed it.\n *\n * Use this instead of `instanceof`. Measured on a live stack: a controller\n * bundle INLINES its own copy of `@palbase/backend`, and the engine that\n * raises this error is the runtime's copy. Two copies, two class identities,\n * and `e instanceof UniqueViolation` is false in the one place a caller\n * writes it — a check that reads as correct and silently never matches.\n */\n static is(e: unknown): e is UniqueViolation {\n return (\n typeof e === \"object\" &&\n e !== null &&\n (e as { name?: unknown }).name === \"UniqueViolation\" &&\n typeof (e as { constraint?: unknown }).constraint === \"string\"\n );\n }\n\n /** The unique constraint the statement violated, as Postgres named it.\n * `\"\"` when the driver did not say which — see `engine/db.ts`. */\n public readonly constraint: string;\n\n constructor(constraint: string, message?: string, code?: string, data?: unknown) {\n super(message ?? \"Unique constraint violated\", code ?? \"unique_violation\", data);\n this.name = \"UniqueViolation\";\n this.constraint = constraint;\n }\n}\n\n/**\n * Eşzamanlılık yüzünden BAŞARISIZ OLAN, ama TEKRAR DENENİRSE başarılı olabilecek\n * hataların ortak yüzeyi (FR-036).\n *\n * `retryable: true` bir tahmin değil, Postgres'in söylediği şey: `40001` ve\n * `40P01` \"bu işlemi baştan çalıştır\" anlamına gelir. Kümenin DAR tutulması\n * kasıtlı — `23505` buraya girseydi, tekrar denemek aynı cevabı verir ve\n * döngü sonsuza kadar dönerdi.\n */\nexport interface RetryableError extends Error {\n readonly retryable: true;\n /** Beş karakterlik SQLSTATE — hangi eşzamanlılık hatası olduğunu SÖYLER. */\n readonly sqlstate: string;\n}\n\n/**\n * `e` tekrar denenebilir mi?\n *\n * `instanceof` DEĞİL şekil kontrolü — ve sebebi ÖLÇÜLMÜŞ (bkz. {@link\n * UniqueViolation.is}): bir controller bundle'ı kendi `@palbase/backend`\n * kopyasını inline'lıyor, hatayı fırlatan motor ise runtime'ın kopyası. İki\n * sınıf kimliği, ve `e instanceof SerializationFailure` yazan tek yerde\n * sessizce hiç eşleşmiyor.\n */\nexport function isRetryable(e: unknown): e is RetryableError {\n return (\n typeof e === \"object\" &&\n e !== null &&\n (e as { retryable?: unknown }).retryable === true &&\n isTransactionRollbackState((e as { sqlstate?: unknown }).sqlstate)\n );\n}\n\n/**\n * `40001 serialization_failure` — SERIALIZABLE ya da REPEATABLE READ altında\n * eşzamanlı bir işlem bu işlemi geçersiz kıldı.\n *\n * D-016: bu hata BLOKLANMIYOR, FIRLATILIYOR. Retry'sız bir izolasyon\n * yükseltmesi sunmak defect'tir — kullanıcıya çalışmayan bir düğme vermektir:\n * SERIALIZABLE'a çıkıp `40001`'i yakalamayan kod, önceden çalışan işlemleri\n * rastgele kaybetmeye başlar.\n */\nexport class SerializationFailure extends Conflict implements RetryableError {\n public readonly retryable = true as const;\n public readonly sqlstate: string;\n\n static is(e: unknown): e is SerializationFailure {\n return (\n typeof e === \"object\" && e !== null &&\n (e as { name?: unknown }).name === \"SerializationFailure\"\n );\n }\n\n constructor(sqlstate: string = SQLSTATE.serializationFailure, message?: string, data?: unknown) {\n super(\n message ??\n \"işlem eşzamanlı bir işlemle serileştirilemedi — aynı işlemi baştan çalıştırın \" +\n \"(karar okumaları ve yazmaları $atomic içine alıp { retry: n } verin)\",\n \"serialization_failure\",\n data,\n );\n this.name = \"SerializationFailure\";\n this.sqlstate = sqlstate;\n }\n}\n\n/**\n * `40P01 deadlock_detected` — iki işlem birbirinin kilidini bekliyordu;\n * Postgres birini seçip iptal etti.\n *\n * Bu hatayı GÖRMEK bir tasarım sinyali: kilit sırası deterministik değilse\n * tekrar denemek yalnız maliyeti öteler. `Database.$lockRows(...)` (FR-035)\n * ya da çok satırlı yazmanın CTE'si (FR-015) sırayı sabitler.\n */\nexport class DeadlockDetected extends Conflict implements RetryableError {\n public readonly retryable = true as const;\n public readonly sqlstate: string;\n\n static is(e: unknown): e is DeadlockDetected {\n return (\n typeof e === \"object\" && e !== null &&\n (e as { name?: unknown }).name === \"DeadlockDetected\"\n );\n }\n\n constructor(sqlstate: string = SQLSTATE.deadlockDetected, message?: string, data?: unknown) {\n super(\n message ??\n \"deadlock: iki işlem birbirinin kilidini bekledi ve bu işlem iptal edildi — \" +\n \"kilit sırasını sabitlemek için Database.$lockRows(tablo, id'ler) kullanın\",\n \"deadlock_detected\",\n data,\n );\n this.name = \"DeadlockDetected\";\n this.sqlstate = sqlstate;\n }\n}\n\n/** A single field-level validation failure carried by {@link BadRequest}. */\nexport interface FieldError {\n /** The offending field's name (dotted path for nested fields). */\n field: string;\n /** Human-readable reason the field failed. */\n message: string;\n}\n\n/** The fixed, typed payload {@link BadRequest} ships. */\nexport interface BadRequestData {\n /** The fields that failed validation. */\n fields: FieldError[];\n}\n\n/** The fixed, typed payload {@link TooManyRequests} ships. */\nexport interface TooManyRequestsData {\n /** Seconds the caller should wait before retrying. */\n retryAfter: number;\n}\n\n/**\n * 429 — the caller has exceeded the rate limit. Carries a fixed typed payload:\n * `new TooManyRequests({ retryAfter: 30 })`. The shape is declared once in the\n * SDK (error-registry pre-seed) so codegen surfaces `error.data.retryAfter`\n * typed on the client — no per-project definition needed.\n */\nexport class TooManyRequests extends NamedHttpError {\n public declare readonly data: TooManyRequestsData;\n constructor(data: TooManyRequestsData, message?: string) {\n super(429, \"too_many_requests\", \"TooManyRequests\", message, undefined, data);\n }\n}\n"],"mappings":"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","names":["SQLSTATE","Object","freeze","serializationFailure","deadlockDetected","queryCancelled","commandGuardFailed","isTransactionRollbackState","state","HTTP_ERROR_BRAND","Symbol","for","ENGINE_RAISED","markEngineRaised","e","isEngineRaised","isHttpError","err","status","error","errorDescription","HttpError","Error","data","name","undefined","toJSON","requestId","result","error_description","request_id","PalError","code","description","NamedHttpError","defaultCode","message","defaultMessage","spaced","replace","charAt","toUpperCase","slice","toLowerCase","BadRequest","Unauthorized","Forbidden","NotFound","Conflict","UniqueViolation","is","constraint","isRetryable","retryable","isTransactionRollbackState","sqlstate","SerializationFailure","SQLSTATE","serializationFailure","DeadlockDetected","deadlockDetected","TooManyRequests"]}
|
|
1
|
+
{"version":3,"sources":["../src/db/sqlstate.ts","../src/errors.ts"],"sourcesContent":["/** PostgreSQL protocol identifiers, shared by error mapping and retry policy. */\nexport const SQLSTATE = Object.freeze({\n serializationFailure: \"40001\",\n deadlockDetected: \"40P01\",\n queryCancelled: \"57014\",\n commandGuardFailed: \"PBC01\",\n} as const);\n\n/** These errors confirm that PostgreSQL aborted the transaction. A connection\n * failure does not: the COMMIT may already have succeeded. */\nexport function isTransactionRollbackState(state: unknown): boolean {\n return state === SQLSTATE.serializationFailure || state === SQLSTATE.deadlockDetected;\n}\n","import { SQLSTATE, isTransactionRollbackState } from \"./db/sqlstate.js\";\n\n/**\n * The brand that identifies an HttpError ACROSS SDK instances.\n *\n * A process legitimately holds more than one copy of this SDK — the runtime\n * loads the engine from its own node_modules while the tenant's bundle carries\n * an inlined copy, which is why the controller registry and the error registry\n * are both anchored on `Symbol.for`. The one place that did not follow the\n * pattern was the engine's catch: `err instanceof HttpError` compares CLASS\n * IDENTITY, so a `throw new NotFound()` from the bundle's copy did not match\n * the engine's copy and every typed error in every deployed backend degraded to\n * `500 internal_error`. Measured through the edge on a real deploy: a route\n * throwing `NotFound` answered 500 while the runtime's own log printed the\n * error object with `status: 404` right beside it.\n *\n * `Symbol.for` puts this in the cross-realm registry, so every copy of the SDK\n * agrees on it by VALUE rather than by identity.\n */\nexport const HTTP_ERROR_BRAND: unique symbol = Symbol.for(\"palbase.backend.httpError\");\n\n/**\n * Set on an `HttpError` the ENGINE built out of a driver failure, as opposed to\n * one the author constructed to ANSWER a request.\n *\n * The distinction cannot be read off the status, and 409 is why. The scaffold\n * teaches `throw new Conflict(\"title already taken\")` as the way to answer\n * (template/AGENTS.md), and the engine raises `UniqueViolation` — also a 409 —\n * when a write hits a unique index. Logging by status therefore either loses the\n * engine's event or writes an \"unhandled\" line every time an author takes the\n * documented path. Measured: it did the second.\n *\n * `Symbol.for` so the mark survives the bundle/runtime SDK split, the same way\n * {@link HTTP_ERROR_BRAND} does.\n */\nexport const ENGINE_RAISED: unique symbol = Symbol.for(\"palbase.backend.engineRaised\") as never;\n\n/** Mark `e` as engine-raised and return it, so a conversion site reads as one expression. */\nexport function markEngineRaised<E extends object>(e: E): E {\n (e as Record<symbol, unknown>)[ENGINE_RAISED] = true;\n return e;\n}\n\n/** Whether the engine built this error, rather than the author throwing it to answer. */\nexport function isEngineRaised(e: unknown): boolean {\n return typeof e === \"object\" && e !== null && (e as Record<symbol, unknown>)[ENGINE_RAISED] === true;\n}\n\n/**\n * Whether a thrown value is an HttpError from ANY copy of this SDK.\n *\n * The shape is checked as well as the brand: the brand says \"this claims to be\n * one of ours\", the fields say the envelope can actually be built from it, and\n * a half-formed object must fall through to the 500 path rather than produce a\n * malformed response.\n */\nexport function isHttpError(err: unknown): err is HttpError {\n if (typeof err !== \"object\" || err === null) return false;\n const e = err as Record<PropertyKey, unknown>;\n return (\n e[HTTP_ERROR_BRAND] === true &&\n typeof e.status === \"number\" &&\n typeof e.error === \"string\" &&\n typeof e.errorDescription === \"string\"\n );\n}\n\n/** HTTP error with structured error response format.\n *\n * The base class for the throwable error classes (`PalError`, `Conflict`,\n * `NotFound`, …). Construct one directly with `throw new HttpError(404,\n * \"todo_not_found\", \"No such todo\")`, or throw a named subclass\n * (`throw new NotFound(\"todo not found\")`). The runtime catches any `HttpError`\n * and emits the standard envelope; on the wire (and to iOS) it surfaces as\n * `BackendError.server(code, status, message, requestId)`.\n *\n * The optional `data` field carries a structured payload alongside the\n * standard envelope — for errors that need to ship extra context\n * (e.g. `new Conflict(\"locked\", \"title_locked\", { retryAfter: 30 })`). It rides\n * through to the iOS typed enum's associated value.\n */\nexport class HttpError extends Error {\n public readonly status: number;\n public readonly error: string;\n public readonly errorDescription: string;\n public readonly data?: unknown;\n /** See {@link HTTP_ERROR_BRAND} — how the engine recognises this across SDK copies. */\n public readonly [HTTP_ERROR_BRAND] = true;\n\n constructor(status: number, error: string, errorDescription: string, data?: unknown) {\n super(errorDescription);\n this.name = \"HttpError\";\n this.status = status;\n this.error = error;\n this.errorDescription = errorDescription;\n if (data !== undefined) {\n this.data = data;\n }\n }\n\n /**\n * Serialize to the standard Palbase error response format.\n * The `requestId` is injected by the runtime layer from the request context.\n * When called without arguments (e.g. JSON.stringify), request_id is omitted.\n * When `data` is set, it is appended as a strict-superset field.\n */\n toJSON(requestId?: string): {\n error: string;\n error_description: string;\n status: number;\n request_id?: string;\n data?: unknown;\n } {\n const result: {\n error: string;\n error_description: string;\n status: number;\n request_id?: string;\n data?: unknown;\n } = {\n error: this.error,\n error_description: this.errorDescription,\n status: this.status,\n };\n if (requestId) {\n result.request_id = requestId;\n }\n if (this.data !== undefined) {\n result.data = this.data;\n }\n return result;\n }\n}\n\n/**\n * Throw with a custom HTTP status + wire code. The general-purpose escape hatch\n * when none of the named classes (`Conflict`/`NotFound`/…) fits.\n *\n * @example\n * throw new PalError(418, \"teapot\", \"I'm a teapot\");\n */\nexport class PalError extends HttpError {\n constructor(status: number, code: string, description: string, data?: unknown) {\n super(status, code, description, data);\n this.name = \"PalError\";\n }\n}\n\n/** Base for the named status classes. Each subclass fixes its HTTP status; the\n * `code` defaults to the class's canonical wire code (overridable), and the\n * `message` defaults to a human-readable label (overridable). */\nabstract class NamedHttpError extends HttpError {\n protected constructor(\n status: number,\n defaultCode: string,\n name: string,\n message?: string,\n code?: string,\n data?: unknown,\n ) {\n super(status, code ?? defaultCode, message ?? defaultMessage(name), data);\n this.name = name;\n }\n}\n\n/** Derive a default human-readable message from a class name\n * (\"NotFound\" → \"Not found\", \"TooManyRequests\" → \"Too many requests\"). */\nfunction defaultMessage(name: string): string {\n const spaced = name.replace(/([a-z0-9])([A-Z])/g, \"$1 $2\");\n return spaced.charAt(0).toUpperCase() + spaced.slice(1).toLowerCase();\n}\n\n/**\n * 400 — the request was malformed or failed validation. Carries a fixed typed\n * payload: `new BadRequest({ fields: [{ field: \"email\", message: \"invalid\" }] })`.\n * The shape is declared once in the SDK so codegen surfaces `error.data.fields`\n * typed on the client.\n */\nexport class BadRequest extends NamedHttpError {\n public declare readonly data: BadRequestData;\n constructor(data: BadRequestData, message?: string) {\n super(400, \"bad_request\", \"BadRequest\", message, undefined, data);\n }\n}\n\n/** 401 — the caller is not authenticated. */\nexport class Unauthorized extends NamedHttpError {\n constructor(message?: string, code?: string, data?: unknown) {\n super(401, \"unauthorized\", \"Unauthorized\", message, code, data);\n }\n}\n\n/** 403 — the caller is authenticated but not allowed. */\nexport class Forbidden extends NamedHttpError {\n constructor(message?: string, code?: string, data?: unknown) {\n super(403, \"forbidden\", \"Forbidden\", message, code, data);\n }\n}\n\n/** 404 — the requested resource does not exist. */\nexport class NotFound extends NamedHttpError {\n constructor(message?: string, code?: string, data?: unknown) {\n super(404, \"not_found\", \"NotFound\", message, code, data);\n }\n}\n\n/** 409 — the request conflicts with the current state. */\nexport class Conflict extends NamedHttpError {\n constructor(message?: string, code?: string, data?: unknown) {\n super(409, \"conflict\", \"Conflict\", message, code, data);\n }\n}\n\n/**\n * 409 — a write was refused because it would duplicate an existing row.\n * Carries the NAME of the unique constraint Postgres named (`users_email_key`).\n *\n * The engine produces it: a statement rejected with SQLSTATE `23505` is\n * converted here rather than surfacing as an opaque driver error (see\n * `engine/db.ts`, `diagnosingDriver`). What that removes is the string match —\n * before this, the only way to act on a duplicate was to test the driver\n * message for \"duplicate key value violates unique constraint\", a contract\n * nobody signed that breaks on a Postgres upgrade, a locale, or a constraint\n * rename, silently and in production.\n *\n * THE NAME IS A FIELD AND STAYS OUT OF THE DEFAULT MESSAGE. The two are not\n * the same audience. `constraint` is read by the code that catches this — the\n * developer, who already knows the schema. `errorDescription` is the HTTP\n * response body, and an UNCAUGHT duplicate puts it in front of the\n * application's end user: `users_email_key` there discloses how the schema is\n * built to whoever sent the request. The platform's own data API took the same\n * decision one surface over and wrote down why —\n * `v2/internal/modules/database/internal/handler/pgerror.go:83-87` collapses\n * every 23xxx to a generic conflict, \"never disclose the constraint/column\n * name\". A thrower who WANTS the name on the wire passes it deliberately\n * (`new UniqueViolation(c, \\`\\${c} already exists\\`)`, or through `data`).\n *\n * @example\n * try {\n * await Database.public.users.insert({ email });\n * } catch (e) {\n * if (UniqueViolation.is(e) && e.constraint === \"users_email_key\") {\n * throw new Conflict(\"That email is taken\", \"email_taken\");\n * }\n * throw e;\n * }\n */\nexport class UniqueViolation extends Conflict {\n /**\n * Whether `e` is a unique violation — REGARDLESS of which copy of this SDK\n * constructed it.\n *\n * Use this instead of `instanceof`. Measured on a live stack: a controller\n * bundle INLINES its own copy of `@palbase/backend`, and the engine that\n * raises this error is the runtime's copy. Two copies, two class identities,\n * and `e instanceof UniqueViolation` is false in the one place a caller\n * writes it — a check that reads as correct and silently never matches.\n */\n static is(e: unknown): e is UniqueViolation {\n return (\n typeof e === \"object\" &&\n e !== null &&\n (e as { name?: unknown }).name === \"UniqueViolation\" &&\n typeof (e as { constraint?: unknown }).constraint === \"string\"\n );\n }\n\n /** The unique constraint the statement violated, as Postgres named it.\n * `\"\"` when the driver did not say which — see `engine/db.ts`. */\n public readonly constraint: string;\n\n constructor(constraint: string, message?: string, code?: string, data?: unknown) {\n super(message ?? \"Unique constraint violated\", code ?? \"unique_violation\", data);\n this.name = \"UniqueViolation\";\n this.constraint = constraint;\n }\n}\n\n/**\n * Eşzamanlılık yüzünden BAŞARISIZ OLAN, ama TEKRAR DENENİRSE başarılı olabilecek\n * hataların ortak yüzeyi (FR-036).\n *\n * `retryable: true` bir tahmin değil, Postgres'in söylediği şey: `40001` ve\n * `40P01` \"bu işlemi baştan çalıştır\" anlamına gelir. Kümenin DAR tutulması\n * kasıtlı — `23505` buraya girseydi, tekrar denemek aynı cevabı verir ve\n * döngü sonsuza kadar dönerdi.\n */\nexport interface RetryableError extends Error {\n readonly retryable: true;\n /** Beş karakterlik SQLSTATE — hangi eşzamanlılık hatası olduğunu SÖYLER. */\n readonly sqlstate: string;\n}\n\n/**\n * `e` tekrar denenebilir mi?\n *\n * `instanceof` DEĞİL şekil kontrolü — ve sebebi ÖLÇÜLMÜŞ (bkz. {@link\n * UniqueViolation.is}): bir controller bundle'ı kendi `@palbase/backend`\n * kopyasını inline'lıyor, hatayı fırlatan motor ise runtime'ın kopyası. İki\n * sınıf kimliği, ve `e instanceof SerializationFailure` yazan tek yerde\n * sessizce hiç eşleşmiyor.\n */\nexport function isRetryable(e: unknown): e is RetryableError {\n return (\n typeof e === \"object\" &&\n e !== null &&\n (e as { retryable?: unknown }).retryable === true &&\n isTransactionRollbackState((e as { sqlstate?: unknown }).sqlstate)\n );\n}\n\n/**\n * `40001 serialization_failure` — SERIALIZABLE ya da REPEATABLE READ altında\n * eşzamanlı bir işlem bu işlemi geçersiz kıldı.\n *\n * D-016: bu hata BLOKLANMIYOR, FIRLATILIYOR. Retry'sız bir izolasyon\n * yükseltmesi sunmak defect'tir — kullanıcıya çalışmayan bir düğme vermektir:\n * SERIALIZABLE'a çıkıp `40001`'i yakalamayan kod, önceden çalışan işlemleri\n * rastgele kaybetmeye başlar.\n */\nexport class SerializationFailure extends Conflict implements RetryableError {\n public readonly retryable = true as const;\n public readonly sqlstate: string;\n\n static is(e: unknown): e is SerializationFailure {\n return (\n typeof e === \"object\" && e !== null &&\n (e as { name?: unknown }).name === \"SerializationFailure\"\n );\n }\n\n constructor(sqlstate: string = SQLSTATE.serializationFailure, message?: string, data?: unknown) {\n super(\n message ??\n \"işlem eşzamanlı bir işlemle serileştirilemedi — aynı işlemi baştan çalıştırın \" +\n \"(karar okumaları ve yazmaları $atomic içine alıp { retry: n } verin)\",\n \"serialization_failure\",\n data,\n );\n this.name = \"SerializationFailure\";\n this.sqlstate = sqlstate;\n }\n}\n\n/**\n * `40P01 deadlock_detected` — iki işlem birbirinin kilidini bekliyordu;\n * Postgres birini seçip iptal etti.\n *\n * Bu hatayı GÖRMEK bir tasarım sinyali: kilit sırası deterministik değilse\n * tekrar denemek yalnız maliyeti öteler. Sırayı sabitleyen üç şey var ve\n * hangisinin geçerli olduğunu ELİNDEKİ belirler: satırların id'leri elindeyse\n * `Database.$lockRows(tablo, id'ler)`, elinde bir FİLTRE varsa\n * `Database.$lockRowsWhere(tablo, filtre)` (FR-037 — birincil anahtara göre\n * sıralayarak kilitler, yani `updateMany` ile aynı sırayı paylaşır), ya da çok\n * satırlı yazmanın CTE'si (FR-015).\n */\nexport class DeadlockDetected extends Conflict implements RetryableError {\n public readonly retryable = true as const;\n public readonly sqlstate: string;\n\n static is(e: unknown): e is DeadlockDetected {\n return (\n typeof e === \"object\" && e !== null &&\n (e as { name?: unknown }).name === \"DeadlockDetected\"\n );\n }\n\n constructor(sqlstate: string = SQLSTATE.deadlockDetected, message?: string, data?: unknown) {\n super(\n message ??\n \"deadlock: iki işlem birbirinin kilidini bekledi ve bu işlem iptal edildi — \" +\n \"kilit sırasını sabitleyin: id'ler elinizdeyse Database.$lockRows(tablo, id'ler), \" +\n \"elinizde bir filtre varsa Database.$lockRowsWhere(tablo, filtre) kullanın\",\n \"deadlock_detected\",\n data,\n );\n this.name = \"DeadlockDetected\";\n this.sqlstate = sqlstate;\n }\n}\n\n/** A single field-level validation failure carried by {@link BadRequest}. */\nexport interface FieldError {\n /** The offending field's name (dotted path for nested fields). */\n field: string;\n /** Human-readable reason the field failed. */\n message: string;\n}\n\n/** The fixed, typed payload {@link BadRequest} ships. */\nexport interface BadRequestData {\n /** The fields that failed validation. */\n fields: FieldError[];\n}\n\n/** The fixed, typed payload {@link TooManyRequests} ships. */\nexport interface TooManyRequestsData {\n /** Seconds the caller should wait before retrying. */\n retryAfter: number;\n}\n\n/**\n * 429 — the caller has exceeded the rate limit. Carries a fixed typed payload:\n * `new TooManyRequests({ retryAfter: 30 })`. The shape is declared once in the\n * SDK (error-registry pre-seed) so codegen surfaces `error.data.retryAfter`\n * typed on the client — no per-project definition needed.\n */\nexport class TooManyRequests extends NamedHttpError {\n public declare readonly data: TooManyRequestsData;\n constructor(data: TooManyRequestsData, message?: string) {\n super(429, \"too_many_requests\", \"TooManyRequests\", message, undefined, data);\n }\n}\n"],"mappings":"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","names":["SQLSTATE","Object","freeze","serializationFailure","deadlockDetected","queryCancelled","commandGuardFailed","isTransactionRollbackState","state","HTTP_ERROR_BRAND","Symbol","for","ENGINE_RAISED","markEngineRaised","e","isEngineRaised","isHttpError","err","status","error","errorDescription","HttpError","Error","data","name","undefined","toJSON","requestId","result","error_description","request_id","PalError","code","description","NamedHttpError","defaultCode","message","defaultMessage","spaced","replace","charAt","toUpperCase","slice","toLowerCase","BadRequest","Unauthorized","Forbidden","NotFound","Conflict","UniqueViolation","is","constraint","isRetryable","retryable","isTransactionRollbackState","sqlstate","SerializationFailure","SQLSTATE","serializationFailure","DeadlockDetected","deadlockDetected","TooManyRequests"]}
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
assertPlanRetry,
|
|
4
4
|
qualifiedTableKey,
|
|
5
5
|
runTxPlan
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-4J6I4KVQ.js";
|
|
7
7
|
import {
|
|
8
8
|
__name
|
|
9
9
|
} from "./chunk-VXPNPVAG.js";
|
|
@@ -217,6 +217,7 @@ function makeTypedSurface(raw) {
|
|
|
217
217
|
$facets: /* @__PURE__ */ __name((table, params) => reco.facets(table, params), "$facets"),
|
|
218
218
|
$claim: /* @__PURE__ */ __name((table, unique, extra) => reco.claim(table, unique, extra), "$claim"),
|
|
219
219
|
$lockRows: /* @__PURE__ */ __name((table, ids) => reco.lockRows(table, ids), "$lockRows"),
|
|
220
|
+
$lockRowsWhere: /* @__PURE__ */ __name((table, where, opts) => reco.lockRowsWhere(table, where, opts), "$lockRowsWhere"),
|
|
220
221
|
$advisoryXactLock: /* @__PURE__ */ __name((key) => reco.advisoryXactLock(key), "$advisoryXactLock"),
|
|
221
222
|
$aggregate: /* @__PURE__ */ __name((table, q) => raw.aggregate(table, q), "$aggregate"),
|
|
222
223
|
$insertMany: /* @__PURE__ */ __name((table, rows, opts) => raw.insertMany(table, rows, opts), "$insertMany"),
|
|
@@ -416,4 +417,4 @@ export {
|
|
|
416
417
|
Flags,
|
|
417
418
|
Realtime
|
|
418
419
|
};
|
|
419
|
-
//# sourceMappingURL=chunk-
|
|
420
|
+
//# sourceMappingURL=chunk-XZWOMPD3.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 AtomicClient,\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 AtomicDatabase,\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 { assertPlanRetry, type AtomicOptions } from \"./db/transaction-options.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?: import(\"./db/bulk.js\").InsertManyOptions,\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 findUnique: (q: { where: Record<string, unknown>; select?: readonly string[]; with?: Record<string, unknown> }) =>\n ops().findUnique(name, q.where, { select: q.select, with: q.with }),\n page: (q?: Record<string, unknown>) => {\n const { where, ...opts } = q ?? {};\n return ops().page(name, where as Record<string, unknown> | undefined, opts as import(\"./db/page.js\").RawPageOptions);\n },\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>; returning?: boolean }) =>\n ops().updateMany(name, q.where, q.set, q.returning === undefined ? undefined : { returning: q.returning }),\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: AtomicClient & Partial<Pick<DBClient, \"atomic\">>): 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 $diagnostics: () => raw.diagnostics(),\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 $findUnique: (table: string, where: Record<string, unknown>, opts?: Parameters<DBOps[\"findUnique\"]>[2]) => raw.findUnique(table, where, opts),\n $page: (table: string, query?: Record<string, unknown>, opts?: import(\"./db/page.js\").RawPageOptions) => raw.page(table, query, opts),\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 $lockRowsWhere: (\n table: string,\n where: Record<string, unknown>,\n opts?: Parameters<DBOps[\"lockRowsWhere\"]>[2],\n ) => reco.lockRowsWhere(table, where, opts),\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?: import(\"./db/bulk.js\").InsertManyOptions,\n ) => raw.insertMany(table, rows, opts)) as DBOps[\"insertMany\"],\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 $atomic<T>(fn: (tx: AtomicDatabase) => Promise<T>, options?: AtomicOptions): Promise<T> {\n if (typeof raw.atomic !== \"function\") {\n throw new Error(\"This Database handle cannot open a root transaction; nested $atomic is not supported\");\n }\n // ALS.run scopes only this callback and its continuations, never another\n // Promise.all branch. Calls made by ordinary services through Database\n // therefore share the callback's physical transaction and identity.\n const parent = __requestALS.getStore();\n let runtime: RuntimeServices | undefined;\n try { runtime = __getRuntime(); } catch { /* Direct typed engine handles need no ambient runtime. */ }\n return raw.atomic(async (tx) => {\n const typed = makeTypedSurface(tx);\n const invoke = () => fn(typed);\n if (!runtime) return invoke();\n const refuse = (): never => { throw new Error(\"Database.$atomic owns one transaction and identity; nested roots and $asService are not allowed\"); };\n const ambient = Object.assign({}, tx, { atomic: refuse, asService: refuse });\n return __requestALS.run({ ...parent, runtime: { ...runtime, Database: ambient } }, invoke);\n }, options);\n },\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 assertPlanRetry(opts);\n const builder = new TxPlanBuilder();\n return runTxPlan(raw, makeTxPlanHandle(builder), builder, fn) as Promise<Materialized<T>>;\n },\n $command<T>(fn: (tx: TxPlan) => T extends Promise<unknown> ? never : T, options?: import(\"./db/command.js\").CommandOptions): Promise<Materialized<T>> {\n const builder = new TxPlanBuilder();\n return runTxPlan({ txPlan: plan => raw.command(plan, options) }, makeTxPlanHandle(builder), builder, fn) 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","findUnique","select","with","page","findMany","put","onConflict","updateMany","returning","deleteMany","count","search","params","similar","recommend","facets","supersede","row","claim","unique","extra","rawDatabase","makeTypedSurface","raw","reco","$query","sql","query","$diagnostics","diagnostics","$insert","table","$update","$delete","$findById","$findUnique","$page","$findMany","$put","$updateMany","$deleteMany","$count","$search","$similar","$recommend","$facets","$claim","$lockRows","ids","lockRows","$lockRowsWhere","lockRowsWhere","$advisoryXactLock","advisoryXactLock","$aggregate","$insertMany","$supersede","base","Object","assign","$atomic","options","atomic","parent","tx","typed","invoke","refuse","ambient","asService","Database","$attempt","attempt","$transaction","assertPlanRetry","builder","TxPlanBuilder","runTxPlan","makeTxPlanHandle","$command","txPlan","plan","command","target","startsWith","qualifiedTableKey","makeTxTablesAccessor","tablesProxy","publicTables","$asService","Documents","makeBucketsAccessor","storage","bucket","rawStorage","Storage","buckets","Cache","Secrets","Auth","Log","Notifications","rawFlags","Flags","isEnabled","flagName","context","getVariant","getAll","defaultOrContext","maybeContext","setOverride","Realtime"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
HttpError
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-JPTFYHP3.js";
|
|
4
4
|
import {
|
|
5
5
|
__name
|
|
6
6
|
} from "./chunk-VXPNPVAG.js";
|
|
@@ -161,4 +161,4 @@ export {
|
|
|
161
161
|
getErrorRegistry,
|
|
162
162
|
defineError
|
|
163
163
|
};
|
|
164
|
-
//# sourceMappingURL=chunk-
|
|
164
|
+
//# sourceMappingURL=chunk-ZNQDL466.js.map
|