@homeflare/alchemy 0.22.0 → 0.24.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/README.md +15 -1
- package/dist/litellm/client.d.ts +76 -0
- package/dist/litellm/client.d.ts.map +1 -0
- package/dist/litellm/credentials.d.ts +48 -0
- package/dist/litellm/credentials.d.ts.map +1 -0
- package/dist/litellm/fake-litellm.d.ts +36 -0
- package/dist/litellm/fake-litellm.d.ts.map +1 -0
- package/dist/litellm/fake-stack.d.ts +30 -0
- package/dist/litellm/fake-stack.d.ts.map +1 -0
- package/dist/litellm/generated/pass-through.d.ts +71 -0
- package/dist/litellm/generated/pass-through.d.ts.map +1 -0
- package/dist/litellm/index.d.ts +13 -0
- package/dist/litellm/index.d.ts.map +1 -0
- package/dist/litellm/index.js +270 -0
- package/dist/litellm/index.js.map +14 -0
- package/dist/litellm/pass-through-endpoint.d.ts +94 -0
- package/dist/litellm/pass-through-endpoint.d.ts.map +1 -0
- package/dist/litellm/pass-through-form.d.ts +40 -0
- package/dist/litellm/pass-through-form.d.ts.map +1 -0
- package/dist/litellm/providers.d.ts +16 -0
- package/dist/litellm/providers.d.ts.map +1 -0
- package/dist/postgres/connection.d.ts +50 -0
- package/dist/postgres/connection.d.ts.map +1 -0
- package/dist/postgres/database-attrs.d.ts +79 -0
- package/dist/postgres/database-attrs.d.ts.map +1 -0
- package/dist/postgres/database-sql.d.ts +73 -0
- package/dist/postgres/database-sql.d.ts.map +1 -0
- package/dist/postgres/database.d.ts +63 -0
- package/dist/postgres/database.d.ts.map +1 -0
- package/dist/postgres/errors.d.ts +86 -0
- package/dist/postgres/errors.d.ts.map +1 -0
- package/dist/postgres/fake-sql.d.ts +22 -0
- package/dist/postgres/fake-sql.d.ts.map +1 -0
- package/dist/postgres/index.d.ts +16 -0
- package/dist/postgres/index.d.ts.map +1 -0
- package/dist/postgres/index.js +238 -0
- package/dist/postgres/index.js.map +15 -0
- package/dist/postgres/providers.d.ts +14 -0
- package/dist/postgres/providers.d.ts.map +1 -0
- package/dist/proxmox/api-token-form.d.ts +12 -1
- package/dist/proxmox/api-token-form.d.ts.map +1 -1
- package/dist/proxmox/api-token.d.ts +8 -3
- package/dist/proxmox/api-token.d.ts.map +1 -1
- package/dist/proxmox/generated/pve/cluster-sdn-fabrics.d.ts +13 -13
- package/dist/proxmox/generated/pve/cluster-sdn-fabrics.d.ts.map +1 -1
- package/dist/proxmox/index.d.ts +9 -2
- package/dist/proxmox/index.d.ts.map +1 -1
- package/dist/proxmox/index.js +17 -9
- package/dist/proxmox/index.js.map +8 -7
- package/dist/proxmox/zfs-pool-spec.d.ts +27 -0
- package/dist/proxmox/zfs-pool-spec.d.ts.map +1 -0
- package/dist/proxmox/zfs-pool-write.d.ts +48 -7
- package/dist/proxmox/zfs-pool-write.d.ts.map +1 -1
- package/dist/proxmox/zfs-pool.d.ts +52 -22
- package/dist/proxmox/zfs-pool.d.ts.map +1 -1
- package/dist/release/archive.d.ts +6 -2
- package/dist/release/archive.d.ts.map +1 -1
- package/dist/release/binary-form.d.ts +14 -9
- package/dist/release/binary-form.d.ts.map +1 -1
- package/dist/release/binary-pins.d.ts +17 -0
- package/dist/release/binary-pins.d.ts.map +1 -0
- package/dist/release/catalog.d.ts.map +1 -1
- package/dist/release/index.js +90 -40
- package/dist/release/index.js.map +8 -7
- package/dist/release/tar.d.ts +9 -2
- package/dist/release/tar.d.ts.map +1 -1
- package/docs/adopted-deploys.md +14 -0
- package/docs/litellm.md +110 -0
- package/docs/peers.md +8 -1
- package/docs/postgres.md +125 -0
- package/docs/release-binary-catalogs.md +40 -4
- package/docs/release-binary-refusals.md +22 -8
- package/docs/release-binary-upstream.md +33 -15
- package/package.json +10 -1
- package/src/litellm/client.ts +195 -0
- package/src/litellm/credentials.ts +73 -0
- package/src/litellm/fake-litellm.ts +114 -0
- package/src/litellm/fake-stack.ts +100 -0
- package/src/litellm/generated/pass-through.ts +73 -0
- package/src/litellm/index.ts +42 -0
- package/src/litellm/pass-through-endpoint-delete-swallow.test.ts +66 -0
- package/src/litellm/pass-through-endpoint-replace.test.ts +84 -0
- package/src/litellm/pass-through-endpoint.test.ts +208 -0
- package/src/litellm/pass-through-endpoint.ts +258 -0
- package/src/litellm/pass-through-form.test.ts +128 -0
- package/src/litellm/pass-through-form.ts +136 -0
- package/src/litellm/providers.ts +19 -0
- package/src/postgres/connection.ts +62 -0
- package/src/postgres/database-attrs.ts +87 -0
- package/src/postgres/database-bigint-serialization.test.ts +140 -0
- package/src/postgres/database-lifecycle.test.ts +113 -0
- package/src/postgres/database-provenance.test.ts +118 -0
- package/src/postgres/database-refusals.test.ts +135 -0
- package/src/postgres/database-sql.ts +174 -0
- package/src/postgres/database.ts +193 -0
- package/src/postgres/errors.ts +131 -0
- package/src/postgres/fake-sql.ts +123 -0
- package/src/postgres/fixtures/pg-REL_18_6-create-database-synopsis.txt +26 -0
- package/src/postgres/fixtures/pg-REL_18_6-pg_database.h.txt +67 -0
- package/src/postgres/index.ts +30 -0
- package/src/postgres/providers.ts +16 -0
- package/src/proxmox/api-token-adopt.test.ts +138 -0
- package/src/proxmox/api-token-form.ts +19 -2
- package/src/proxmox/api-token.ts +8 -3
- package/src/proxmox/generated/pve/cluster-sdn-fabrics.ts +23 -13
- package/src/proxmox/index.test.ts +5 -2
- package/src/proxmox/index.ts +9 -2
- package/src/proxmox/zfs-pool-adopt.test.ts +141 -0
- package/src/proxmox/zfs-pool-spec.ts +76 -0
- package/src/proxmox/zfs-pool-write.ts +63 -9
- package/src/proxmox/zfs-pool.ts +55 -97
- package/src/release/archive.ts +10 -4
- package/src/release/binary-form.ts +24 -76
- package/src/release/binary-pins.ts +95 -0
- package/src/release/binary-root.test.ts +122 -0
- package/src/release/catalog.ts +10 -2
- package/src/release/tar-root.test.ts +114 -0
- package/src/release/tar.ts +78 -12
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/litellm/credentials.ts", "../src/litellm/client.ts", "../src/litellm/pass-through-endpoint.ts", "../src/litellm/pass-through-form.ts", "../src/litellm/providers.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"/**\n * The lazy Auth service every LiteLLM call reads from, and nothing else reads the environment.\n *\n * ★ LITELLM'S OWN VARIABLE NAMES, NOT A HOUSE CHOICE. `LITELLM_PROXY_URL` and\n * `LITELLM_PROXY_API_KEY` are what LiteLLM's own CLI client reads\n * (litellm/proxy/client/cli/main.py:70,78 at tag v1.100.0) — using the vendor's own names means\n * a consumer who already has a `.env` for the `litellm` CLI needs nothing new for this package.\n * ⛔ NO DEFAULT URL. A published package that fell back to a loopback address would let a\n * consumer who forgot the variable watch every call fail against a proxy that is not theirs,\n * instead of being told which variable is missing (S24/S25 — the house rule, not a vendor one).\n * ⛔ THE KEY IS NEVER A PROP. Alchemy persists attributes unencrypted (S25); the master key that\n * `update_config_general_settings` requires (PROXY_ADMIN only, proxy_server.py:16411 at the\n * tag) is read fresh from the environment on every call, never stored in state.\n * ★ LAZY: the service's VALUE is an `Effect` that resolves the credential, not the credential\n * itself — so a layer built once still reads the environment inside each operation\n * (`yield* yield* LitellmCredentials`), the idiom `alchemy/Auth`'s custom-auth-provider guide\n * describes for a credential a layer must not capture at build time.\n */\nimport * as Context from 'effect/Context';\nimport * as Data from 'effect/Data';\nimport * as Effect from 'effect/Effect';\nimport * as Layer from 'effect/Layer';\n\nexport const LITELLM_PROXY_URL_ENV = 'LITELLM_PROXY_URL';\nexport const LITELLM_PROXY_API_KEY_ENV = 'LITELLM_PROXY_API_KEY';\n\nexport interface LitellmCreds {\n /** Trailing slash stripped. No default — see the header. */\n readonly baseUrl: string;\n /** LiteLLM's master (or a PROXY_ADMIN) key. Never logged, never an attribute. */\n readonly apiKey: string;\n}\n\nexport class LitellmCredentialsError extends Data.TaggedError('LitellmCredentialsError')<{\n readonly message: string;\n}> {}\n\nconst missing = (name: string): LitellmCredentialsError =>\n new LitellmCredentialsError({\n message:\n `${name} is not set. Export it — the same variable LiteLLM's own \\`litellm\\` CLI ` +\n 'reads — before running a stack that declares a LiteLLM.PassThroughEndpoint.',\n });\n\n/** Read at call time. Exported so a test can call it directly against `process.env`. */\nexport const resolveCreds: Effect.Effect<LitellmCreds, LitellmCredentialsError> = Effect.gen(\n function* () {\n const rawUrl = process.env[LITELLM_PROXY_URL_ENV];\n if (rawUrl === undefined || rawUrl.trim() === '')\n return yield* Effect.fail(missing(LITELLM_PROXY_URL_ENV));\n const rawKey = process.env[LITELLM_PROXY_API_KEY_ENV];\n if (rawKey === undefined || rawKey.trim() === '') {\n return yield* Effect.fail(missing(LITELLM_PROXY_API_KEY_ENV));\n }\n return { baseUrl: rawUrl.trim().replace(/\\/$/, ''), apiKey: rawKey.trim() };\n },\n);\n\n/** The Effect service a LiteLLM operation reads: `yield* yield* LitellmCredentials`. */\nexport class LitellmCredentials extends Context.Service<\n LitellmCredentials,\n Effect.Effect<LitellmCreds, LitellmCredentialsError>\n>()('homeflare/litellm/Credentials') {}\n\n/** The default layer: resolves from the environment, fresh, on every `yield*`. */\nexport const litellmCredentialsLayer: Layer.Layer<LitellmCredentials> = Layer.succeed(\n LitellmCredentials,\n resolveCreds,\n);\n\n/** For a test: a fixed credential, never touching the environment. */\nexport const litellmCredentialsLayerFor = (creds: LitellmCreds): Layer.Layer<LitellmCredentials> =>\n Layer.succeed(LitellmCredentials, Effect.succeed(creds));\n",
|
|
6
|
+
"/**\n * The four LiteLLM proxy calls a `LiteLLM.PassThroughEndpoint` needs, and nothing else reaches\n * LiteLLM's `/config/pass_through_endpoint` routes.\n *\n * ⛔ WRAPPED IN A PER-BASE-URL SEMAPHORE. LiteLLM stores every pass-through endpoint in ONE\n * `general_settings.pass_through_endpoints` list (pass_through_endpoints.py at tag v1.100.0),\n * so create/update/delete are each a read-modify-write of the WHOLE list through\n * `update_config_general_settings`. Two of this package's own calls racing each other — two\n * endpoints created in the same deploy — would each read the list, add their own row and write\n * it back, and the second write would silently drop the first row. One permit per proxy\n * serialises this package's own writes; it cannot serialise against the LiteLLM UI or a second\n * deploy process, which is why `reconcile` reads back after writing rather than trusting the\n * call that just returned (docs/litellm.md).\n * ★ `Authorization: Bearer <key>` — LiteLLM's own client uses it (litellm/proxy/client/cli, and\n * every `/config/*` route decorator at the tag takes `user_api_key_dict = Depends(user_api_key_auth)`,\n * which accepts a bearer token). Every mutation additionally requires the caller be\n * PROXY_ADMIN (`update_config_general_settings`, proxy_server.py:16411 at the tag) — an\n * ordinary virtual key gets `LitellmUnauthorizedError`, not a clearer 403 body naming the role.\n * ⛔ NO MESSAGE SNIFFING (S21). Every status this file maps becomes a typed tag; nothing here or\n * above it matches an error body's text. Deleting an id LiteLLM does not have answers 400\n * (measured against the tag's `pass_through_endpoint_delete` — the id is looked up in the list\n * and a miss raises `HTTPException(400)`, not 404) — but so does a genuinely-live row when the\n * caller isn't PROXY_ADMIN, or the DB is disconnected (`update_config_general_settings`,\n * proxy_server.py:16389-16416 at the tag): the SAME status, for a delete that did NOT happen.\n * `deletePassThroughEndpoint` tells them apart by re-listing (a GET, which needs no PROXY_ADMIN —\n * proxy_server.py:3245-3251) rather than by the 400's body text — see its own comment.\n */\nimport * as Data from 'effect/Data';\nimport * as Effect from 'effect/Effect';\nimport * as HttpClient from 'effect/unstable/http/HttpClient';\nimport * as HttpClientRequest from 'effect/unstable/http/HttpClientRequest';\nimport * as Semaphore from 'effect/Semaphore';\nimport { LitellmCredentials, type LitellmCredentialsError } from './credentials.ts';\nimport type {\n PassThroughEndpointResponse,\n PassThroughGenericEndpoint,\n} from './generated/pass-through.ts';\n\nexport type LitellmRequirements = HttpClient.HttpClient | LitellmCredentials;\n\n/**\n * The `litellm:<METHOD> <path>` operations this package writes to and reads from.\n *\n * ★ `codegen/litellm.ts` scans this FILE's text for these exact literals to pick which of\n * LiteLLM 1.100.0's 696 paths to generate types from (codegen/litellm.ts header). Keep each one\n * a literal string on one line; the generator matches text, it does not evaluate the object.\n */\nexport const LITELLM_OPERATIONS = {\n create: 'litellm:POST /config/pass_through_endpoint',\n delete: 'litellm:DELETE /config/pass_through_endpoint',\n list: 'litellm:GET /config/pass_through_endpoint',\n update: 'litellm:POST /config/pass_through_endpoint/{endpoint_id}',\n} as const;\n\nexport class LitellmUnauthorizedError extends Data.TaggedError('LitellmUnauthorizedError')<{\n readonly status: number;\n readonly method: string;\n readonly path: string;\n}> {}\nexport class LitellmBadRequestError extends Data.TaggedError('LitellmBadRequestError')<{\n readonly method: string;\n readonly path: string;\n readonly detail: string;\n}> {}\nexport class LitellmHttpError extends Data.TaggedError('LitellmHttpError')<{\n readonly status: number;\n readonly method: string;\n readonly path: string;\n readonly detail: string;\n}> {}\nexport class LitellmTransportError extends Data.TaggedError('LitellmTransportError')<{\n readonly method: string;\n readonly path: string;\n readonly cause: string;\n}> {}\n\nexport type LitellmError =\n | LitellmUnauthorizedError\n | LitellmBadRequestError\n | LitellmHttpError\n | LitellmTransportError\n | LitellmCredentialsError;\n\nconst semaphores = new Map<string, Semaphore.Semaphore>();\n\n/** One semaphore per base URL, created on first use. Never `Hash.structure` (S18) — a plain Map key. */\nconst semaphoreFor = (baseUrl: string): Effect.Effect<Semaphore.Semaphore> =>\n Effect.sync(() => {\n const existing = semaphores.get(baseUrl);\n if (existing !== undefined) return existing;\n const created = Semaphore.makeUnsafe(1);\n semaphores.set(baseUrl, created);\n return created;\n });\n\nconst call = <T>(method: 'GET' | 'POST' | 'DELETE', path: string, body?: unknown) =>\n Effect.gen(function* () {\n const creds = yield* yield* LitellmCredentials;\n const client = yield* HttpClient.HttpClient;\n const url = `${creds.baseUrl}${path}`;\n const request = HttpClientRequest.make(method)(url).pipe(\n HttpClientRequest.setHeaders({\n Accept: 'application/json',\n Authorization: `Bearer ${creds.apiKey}`,\n }),\n body === undefined\n ? (self) => self\n : HttpClientRequest.bodyText(JSON.stringify(body), 'application/json'),\n );\n const response = yield* client\n .execute(request)\n .pipe(\n Effect.mapError(\n (cause) => new LitellmTransportError({ cause: String(cause), method, path }),\n ),\n );\n const text = yield* response.text.pipe(\n Effect.mapError((cause) => new LitellmTransportError({ cause: String(cause), method, path })),\n );\n if (response.status === 401 || response.status === 403) {\n return yield* Effect.fail(\n new LitellmUnauthorizedError({ method, path, status: response.status }),\n );\n }\n if (response.status === 400) {\n return yield* Effect.fail(\n new LitellmBadRequestError({ detail: text.slice(0, 300), method, path }),\n );\n }\n if (response.status < 200 || response.status >= 300) {\n return yield* Effect.fail(\n new LitellmHttpError({ detail: text.slice(0, 300), method, path, status: response.status }),\n );\n }\n return (text === '' ? undefined : JSON.parse(text)) as T;\n });\n\n/** GET /config/pass_through_endpoint — every row, config-file and DB alike. Not under the semaphore: reads don't race the field. */\nexport const listPassThroughEndpoints = (): Effect.Effect<\n readonly PassThroughGenericEndpoint[],\n LitellmError,\n LitellmRequirements\n> =>\n call<PassThroughEndpointResponse>('GET', '/config/pass_through_endpoint').pipe(\n Effect.map((body) => body.endpoints),\n );\n\nconst mutate = <T>(io: Effect.Effect<T, LitellmError, LitellmRequirements>) =>\n Effect.gen(function* () {\n const creds = yield* yield* LitellmCredentials;\n const semaphore = yield* semaphoreFor(creds.baseUrl);\n return yield* semaphore.withPermits(1)(io);\n });\n\n/** POST /config/pass_through_endpoint. `body` carries `id` — see pass-through-endpoint.ts. */\nexport const createPassThroughEndpoint = (\n body: PassThroughGenericEndpoint,\n): Effect.Effect<void, LitellmError, LitellmRequirements> =>\n mutate(call('POST', '/config/pass_through_endpoint', body));\n\n/** POST /config/pass_through_endpoint/{endpoint_id}. Merges with `exclude_none` — see pass-through-form.ts. */\nexport const updatePassThroughEndpoint = (\n endpointId: string,\n body: Partial<PassThroughGenericEndpoint>,\n): Effect.Effect<void, LitellmError, LitellmRequirements> =>\n mutate(call('POST', `/config/pass_through_endpoint/${encodeURIComponent(endpointId)}`, body));\n\n/**\n * DELETE /config/pass_through_endpoint?endpoint_id=… — idempotent BY THIS FUNCTION, not by the\n * vendor. ⚠️ Measured at v1.100.0: deleting an id the list does not have raises `HTTPException(400)`\n * from `pass_through_endpoint_delete` — but the SAME route also 400s `not_allowed_access` for a\n * non-PROXY_ADMIN caller or a disconnected DB (`update_config_general_settings`,\n * proxy_server.py:16389-16416), on a row that never got deleted. A 400 alone cannot tell the two\n * apart, so THIS FUNCTION DOESN'T GUESS FROM THE STATUS ALONE: on 400 it re-lists (a GET — no\n * PROXY_ADMIN required, proxy_server.py:3245-3251) and only treats the delete as already-done if\n * `endpointId` is genuinely absent; otherwise the delete failed and this re-fails with the ORIGINAL\n * error, not a message match (S21 — a status-and-a-real-read decision, never body text).\n */\nexport const deletePassThroughEndpoint = (\n endpointId: string,\n): Effect.Effect<void, LitellmError, LitellmRequirements> =>\n mutate(\n call(\n 'DELETE',\n `/config/pass_through_endpoint?endpoint_id=${encodeURIComponent(endpointId)}`,\n ).pipe(\n Effect.catchTag('LitellmBadRequestError', (original) =>\n Effect.gen(function* () {\n const rows = yield* listPassThroughEndpoints();\n if (rows.some((row) => row.id === endpointId)) return yield* Effect.fail(original);\n }),\n ),\n Effect.asVoid,\n ),\n );\n",
|
|
7
|
+
"/**\n * `LiteLLM.PassThroughEndpoint` — one row in LiteLLM's `general_settings.pass_through_endpoints`.\n *\n * ★ ADOPT-FRIENDLY BY id, NOT BY PATH. `output?.id` is used as the object's live id once the\n * engine has one — from our own prior create (the deterministic physical name) or from an\n * adopted foreign row — and `read` computes a FRESH physical name only for a resource the\n * engine has never resolved before. This is S7/S9's \"output is a cache of stable identifiers,\n * not proof the object exists\" and S13's \"an adopted object keeps its live name as an explicit\n * prop\", applied to an id LiteLLM lets a client choose (`_types.py:2192` at v1.100.0: create\n * takes a caller-supplied `id`, else `uuid4()`).\n *\n * ⛔ A PATH HELD BY AN `is_from_config` ROW IS REFUSED, NOT ADOPTED. A DB row on the same path\n * overrides it at runtime (`pass_through_endpoints.py` merges DB after config, at the tag) — so\n * creating one would silently take over a route the operator declared in `config.yaml`, the\n * exact \"DB row silently overrides the file\" the task names. `is_from_config` rows are also\n * never updated or deleted by this provider, for the same reason.\n * ⛔ A DB ROW WITH NO `id` (path-only, `_types.py`'s \"backwards compatibility\" case) CANNOT BE\n * ADDRESSED by the id-keyed update/delete calls this provider uses, so it is refused rather than\n * silently adopted into an id-based lifecycle it cannot actually manage — see docs/litellm.md.\n * ⛔ NO CREDENTIAL IS A PROP (S25). `LITELLM_PROXY_URL`/`LITELLM_PROXY_API_KEY` are read at call\n * time by credentials.ts.\n */\nimport { Resource } from 'alchemy';\nimport { Unowned } from 'alchemy/AdoptPolicy';\nimport { isResolved } from 'alchemy/Diff';\nimport type { Input } from 'alchemy/Input';\nimport { createPhysicalName } from 'alchemy/PhysicalName';\nimport * as Provider from 'alchemy/Provider';\nimport type { Stack } from 'alchemy/Stack';\nimport type { Stage } from 'alchemy/Stage';\nimport * as Data from 'effect/Data';\nimport * as Effect from 'effect/Effect';\nimport * as Predicate from 'effect/Predicate';\nimport {\n type LitellmError,\n type LitellmRequirements,\n createPassThroughEndpoint,\n deletePassThroughEndpoint,\n listPassThroughEndpoints,\n updatePassThroughEndpoint,\n} from './client.ts';\nimport type { PassThroughGenericEndpoint } from './generated/pass-through.ts';\nimport {\n type PassThroughEndpointAttributes,\n type PassThroughEndpointProps,\n createBody,\n literalSecretHeaders,\n matches,\n needsReplace,\n updateBody,\n} from './pass-through-form.ts';\n\nexport type { PassThroughEndpointAttributes, PassThroughEndpointProps };\n\nexport class LitellmConfigPathConflictError extends Data.TaggedError(\n 'LitellmConfigPathConflictError',\n)<{\n readonly path: string;\n}> {}\nexport class LitellmUnaddressableRowError extends Data.TaggedError('LitellmUnaddressableRowError')<{\n readonly path: string;\n}> {}\nexport class LitellmLiteralSecretHeaderError extends Data.TaggedError(\n 'LitellmLiteralSecretHeaderError',\n)<{\n readonly headers: readonly string[];\n}> {}\n\nexport type PassThroughEndpointError =\n | LitellmError\n | LitellmConfigPathConflictError\n | LitellmUnaddressableRowError\n | LitellmLiteralSecretHeaderError;\n\nexport interface LiteLLMPassThroughEndpoint extends Resource<\n 'LiteLLM.PassThroughEndpoint',\n PassThroughEndpointProps,\n PassThroughEndpointAttributes,\n never,\n LitellmRequirements | Stack | Stage\n> {}\n\nexport const LiteLLMPassThroughEndpoint = Resource<LiteLLMPassThroughEndpoint>(\n 'LiteLLM.PassThroughEndpoint',\n);\n\nexport const isLiteLLMPassThroughEndpoint = (value: unknown): value is LiteLLMPassThroughEndpoint =>\n Predicate.hasProperty(value, 'Type') && value.Type === 'LiteLLM.PassThroughEndpoint';\n\nconst toAttributes = (\n row: PassThroughGenericEndpoint,\n fallbackId: string,\n): PassThroughEndpointAttributes => ({\n ...row,\n id: row.id ?? fallbackId,\n});\n\nconst physicalIdOf = (logicalId: string, instanceId: string) =>\n createPhysicalName({ id: logicalId, instanceId, lowercase: true, maxLength: 64 });\n\n/**\n * Find this resource's live row (by id) and, when absent, the row that already holds its `path`\n * (a config-file row to refuse, or a DB row LiteLLM did not give an id — also refused, see the\n * file header — or a genuine foreign DB row to hand back `Unowned`).\n */\nconst locate = (\n rows: readonly PassThroughGenericEndpoint[],\n objectId: string,\n path: string,\n): {\n readonly mine: PassThroughGenericEndpoint | undefined;\n readonly conflict: PassThroughGenericEndpoint | undefined;\n} => {\n const mine = rows.find((row) => row.id === objectId);\n const conflict = mine === undefined ? rows.find((row) => row.path === path) : undefined;\n return { conflict, mine };\n};\n\n/** The typed refusal for a `path` conflict that cannot ever be adopted (config row, or no id). */\nconst conflictRefusal = (conflict: PassThroughGenericEndpoint, path: string) =>\n conflict.is_from_config === true\n ? new LitellmConfigPathConflictError({ path })\n : new LitellmUnaddressableRowError({ path });\n\n/** `read`'s answer for a path conflict: `Unowned` when it is a real, addressable DB row — a typed refusal otherwise. */\nconst readConflict = (conflict: PassThroughGenericEndpoint, path: string) =>\n conflict.is_from_config !== true && conflict.id !== undefined && conflict.id !== null\n ? Effect.succeed(Unowned(toAttributes(conflict, conflict.id)))\n : Effect.fail(conflictRefusal(conflict, path));\n\nconst refuseLiteralSecrets = (headers: Readonly<Record<string, unknown>> | undefined) => {\n const bad = literalSecretHeaders(headers);\n return bad.length === 0\n ? Effect.void\n : Effect.fail(new LitellmLiteralSecretHeaderError({ headers: bad }));\n};\n\n/** ⛔ TEST-ONLY EXPORT. Not on index.ts's barrel — a lifecycle test calls these directly against the fake. */\nexport const passThroughHandlers = {\n read: ({\n id,\n instanceId,\n olds,\n output,\n }: {\n id: string;\n instanceId: string;\n olds: PassThroughEndpointProps;\n output: PassThroughEndpointAttributes | undefined;\n }) =>\n Effect.gen(function* () {\n const objectId = output?.id ?? (yield* physicalIdOf(id, instanceId));\n const rows = yield* listPassThroughEndpoints();\n const { conflict, mine } = locate(rows, objectId, olds.path);\n if (mine !== undefined) return toAttributes(mine, objectId);\n if (conflict === undefined) return undefined;\n return yield* readConflict(conflict, olds.path);\n }),\n\n diff: ({\n news,\n output,\n }: {\n news: Input<PassThroughEndpointProps>;\n output: PassThroughEndpointAttributes | undefined;\n }) =>\n Effect.gen(function* () {\n if (!isResolved(news)) return undefined;\n yield* refuseLiteralSecrets(news.headers);\n if (output === undefined) return undefined;\n // ⛔ deleteFirst: TRUE — a create-first replace mints a new instanceId/id before the old row\n // on `path` is gone, so the new generation's `reconcile` would refuse it as a foreign\n // conflict (Apply.ts). Same fix this kit's other unique-identity providers use.\n if (needsReplace(output, news)) return { action: 'replace', deleteFirst: true } as const;\n return matches(output, news)\n ? ({ action: 'noop' } as const)\n : ({ action: 'update' } as const);\n }),\n\n reconcile: ({\n id,\n instanceId,\n news,\n output,\n }: {\n id: string;\n instanceId: string;\n news: PassThroughEndpointProps;\n output: PassThroughEndpointAttributes | undefined;\n }) =>\n Effect.gen(function* () {\n yield* refuseLiteralSecrets(news.headers);\n const objectId = output?.id ?? (yield* physicalIdOf(id, instanceId));\n const rows = yield* listPassThroughEndpoints();\n const { conflict, mine } = locate(rows, objectId, news.path);\n\n if (mine === undefined) {\n // ⚠️ A CONFLICT HERE — reconcile, not read — means ownership shifted between plan and\n // apply (a config row or a foreign DB row landed on this path after `read` cleared it).\n // `read` gates a genuine adopt by returning `Unowned`; reconcile itself never adopts,\n // so ANY conflict here fails rather than risking a second row on one path.\n if (conflict !== undefined) return yield* Effect.fail(conflictRefusal(conflict, news.path));\n yield* createPassThroughEndpoint(createBody(news, objectId));\n } else if (mine.is_from_config === true) {\n // ⛔ NEVER UPDATE OR DELETE AN `is_from_config` ROW, even one this resource's own\n // deterministic id happens to match (a config.yaml author could in principle choose the\n // same id `createPhysicalName` would). Refuse rather than silently editing the file's row.\n return yield* Effect.fail(new LitellmConfigPathConflictError({ path: news.path }));\n } else if (needsReplace(toAttributes(mine, objectId), news)) {\n yield* deletePassThroughEndpoint(objectId);\n yield* createPassThroughEndpoint(createBody(news, objectId));\n } else if (!matches(toAttributes(mine, objectId), news)) {\n const body = updateBody(news);\n if (Object.keys(body).length > 0) yield* updatePassThroughEndpoint(objectId, body);\n }\n\n const after = (yield* listPassThroughEndpoints()).find((row) => row.id === objectId);\n if (after === undefined) {\n return yield* Effect.die(\n new Error(\n `LiteLLM.PassThroughEndpoint ${news.path}: the write returned no error but id ` +\n `${objectId} is still absent from GET /config/pass_through_endpoint — read back ` +\n 'rather than trusting the call that just returned.',\n ),\n );\n }\n return toAttributes(after, objectId);\n }),\n\n delete: ({\n id,\n instanceId,\n olds,\n output,\n }: {\n id: string;\n instanceId: string;\n olds: PassThroughEndpointProps;\n output: PassThroughEndpointAttributes;\n }) =>\n Effect.gen(function* () {\n const objectId = output.id ?? (yield* physicalIdOf(id, instanceId));\n const rows = yield* listPassThroughEndpoints();\n const live = rows.find((row) => row.id === objectId);\n // ⛔ NEVER DELETE AN `is_from_config` ROW, and a DB row this resource never created (a path\n // match with a different id) is not this resource's to remove either.\n if (live === undefined || live.is_from_config === true || live.path !== olds.path) return;\n yield* deletePassThroughEndpoint(objectId);\n }),\n\n list: () => Effect.succeed([]),\n};\n\nexport const LiteLLMPassThroughEndpointProvider = () =>\n Provider.effect(\n LiteLLMPassThroughEndpoint,\n Effect.succeed(LiteLLMPassThroughEndpoint.Provider.of(passThroughHandlers)),\n );\n",
|
|
8
|
+
"/**\n * The wire body for a `LiteLLM.PassThroughEndpoint`, the comparison that decides whether it needs\n * writing, and the refusals that stop a plan before it writes anything unsafe.\n *\n * ★ EXTRACTED SO IT CAN BE TESTED WITHOUT A SERVER — the same reason netbox keeps `prefix-form.ts`\n * beside `prefix.ts` (S32/testing-and-docs.md).\n *\n * ⛔ PROPS ARE THE GENERATED TYPE, MINUS THE TWO FIELDS THIS RESOURCE OWNS ITSELF: `id` (a\n * deterministic physical name — pass-through-endpoint.ts) and `is_from_config` (response-only;\n * the vendor sets it, a declaration never does). Deriving `Props` with `Omit<>` rather than\n * hand-typing it means a vendor schema change that adds, removes or narrows a field is felt here\n * as a compiler error, not silently ignored.\n */\nimport { deepEqual } from 'alchemy/Diff';\nimport type * as Generated from './generated/pass-through.ts';\n\nexport type PassThroughEndpointProps = Omit<\n Generated.PassThroughGenericEndpoint,\n 'id' | 'is_from_config'\n>;\n\n/** What `read` returns: the props plus the two vendor-owned fields, `id` now guaranteed present. */\nexport type PassThroughEndpointAttributes = Omit<Generated.PassThroughGenericEndpoint, 'id'> & {\n readonly id: string;\n};\n\n/** ⚠️ NetBox `settled()`'s pattern: only fields the SCHEMA gives a vendor default for, copied from it (not assumed). */\nexport const settled = (props: PassThroughEndpointProps) => ({\n auth: props.auth ?? true,\n cost_per_request: props.cost_per_request ?? 0,\n default_query_params: props.default_query_params ?? {},\n headers: props.headers ?? {},\n include_subpath: props.include_subpath ?? false,\n});\n\n/** The three fields whose absence the vendor's `exclude_none` update can never write (see client.ts). */\nexport const NULLABLE_FIELDS = ['timeout', 'methods', 'guardrails'] as const;\n\nconst isUnset = (value: unknown): boolean => value === undefined || value === null;\n\n/**\n * ⚠️ BUILT AS A WIDER RECORD AND PRUNED, NOT AS THE GENERATED TYPE DIRECTLY.\n * `exactOptionalPropertyTypes` is on (packages/config/tsconfig.base.json), so writing\n * `timeout: undefined` into a `{ timeout?: number | null }` is a type error rather than an\n * absent key — the same reason `codegen/openapi.ts`'s `prune` exists, and the same fix.\n */\nconst prune = (value: Record<string, unknown>): Record<string, unknown> =>\n Object.fromEntries(Object.entries(value).filter(([, v]) => v !== undefined));\n\n/**\n * Full body for CREATE. `id` is the deterministic physical name (pass-through-endpoint.ts), sent\n * so a failed create is recoverable by `read` rather than orphaning a second row on retry.\n */\nexport const createBody = (\n props: PassThroughEndpointProps,\n id: string,\n): Generated.PassThroughGenericEndpoint =>\n prune({\n ...settled(props),\n guardrails: isUnset(props.guardrails) ? undefined : props.guardrails,\n id,\n methods: isUnset(props.methods) ? undefined : props.methods,\n path: props.path,\n target: props.target,\n timeout: isUnset(props.timeout) ? undefined : props.timeout,\n }) as unknown as Generated.PassThroughGenericEndpoint;\n\n/**\n * Partial body for UPDATE (`POST /{id}`). ⛔ ONLY FIELDS THIS DECLARATION ACTUALLY SETS — the\n * vendor merges the parsed body with `model_dump(exclude_none=True)` (measured at v1.100.0), so an\n * `undefined` OR an explicit `null` here would be dropped by the server anyway; sending neither is\n * the same outcome with no request made when nothing changed (`Object.keys(body).length === 0`\n * checked by the caller in pass-through-endpoint.ts).\n */\nexport const updateBody = (\n props: PassThroughEndpointProps,\n): Partial<Generated.PassThroughGenericEndpoint> =>\n prune({\n ...settled(props),\n guardrails: isUnset(props.guardrails) ? undefined : props.guardrails,\n methods: isUnset(props.methods) ? undefined : props.methods,\n path: props.path,\n target: props.target,\n timeout: isUnset(props.timeout) ? undefined : props.timeout,\n }) as Partial<Generated.PassThroughGenericEndpoint>;\n\n/**\n * Whether ANY of the three nullable fields went from set (in `live`) to unset (in `props`) — the\n * one case `updateBody` cannot express, because the vendor's `exclude_none` merge silently keeps\n * the old value instead of clearing it. Detected here so `pass-through-endpoint.ts` can plan a\n * `replace` (delete-then-create) instead of an update that would look like it worked.\n */\nexport const needsReplace = (\n live: PassThroughEndpointAttributes,\n props: PassThroughEndpointProps,\n): boolean => NULLABLE_FIELDS.some((field) => !isUnset(live[field]) && isUnset(props[field]));\n\n/** Whether the live object already says what the declaration says — the `diff` \"noop\" test. */\nexport const matches = (\n live: PassThroughEndpointAttributes,\n props: PassThroughEndpointProps,\n): boolean => {\n const fixed = settled(props);\n return (\n live.target === props.target &&\n live.auth === fixed.auth &&\n live.cost_per_request === fixed.cost_per_request &&\n live.include_subpath === fixed.include_subpath &&\n deepEqual(live.headers, fixed.headers) &&\n deepEqual(live.default_query_params, fixed.default_query_params) &&\n (isUnset(props.timeout) ? isUnset(live.timeout) : live.timeout === props.timeout) &&\n (isUnset(props.methods) ? isUnset(live.methods) : deepEqual(live.methods, props.methods)) &&\n (isUnset(props.guardrails)\n ? isUnset(live.guardrails)\n : deepEqual(live.guardrails, props.guardrails))\n );\n};\n\n/**\n * A literal secret in a forwarded header (S25, house rule — not a vendor one; see\n * pass-through-endpoint.ts). Case-insensitive on the header name; the reference form is LiteLLM's\n * own, `os.environ/NAME` (`set_env_variables_in_header`, pass_through_endpoints.py:117-160 at the\n * tag) — a missing variable on the proxy host leaves that literal string in the header, which\n * nothing in the API can detect, so this is enforced here rather than trusted to the vendor.\n */\nconst SECRET_HEADER_NAMES = new Set(['authorization', 'x-api-key', 'cf-aig-authorization']);\n\nexport const literalSecretHeaders = (\n headers: Readonly<Record<string, unknown>> | undefined,\n): readonly string[] =>\n Object.entries(headers ?? {})\n .filter(\n ([name, value]) =>\n SECRET_HEADER_NAMES.has(name.toLowerCase()) && !String(value).includes('os.environ/'),\n )\n .map(([name]) => name);\n",
|
|
9
|
+
"/**\n * The LiteLLM provider, with its credential source, as one layer for a stack.\n *\n * Layer.mergeAll(litellmProviders(), …the stack's other providers)\n *\n * ★ THE DEFAULT READS `LITELLM_PROXY_URL`/`LITELLM_PROXY_API_KEY` AT CALL TIME (credentials.ts),\n * never at layer build — pass another `Layer.Layer<LitellmCredentials>` (see\n * `litellmCredentialsLayerFor` in a test) to source them another way.\n * ⚠️ `HttpClient` is NOT provided here. It is ambient in every Alchemy runtime the same way it is\n * for `netboxProviders`-style families in this kit; a stack that already deploys anything over\n * HTTP already has one.\n */\nimport * as Layer from 'effect/Layer';\nimport { type LitellmCredentials, litellmCredentialsLayer } from './credentials.ts';\nimport { LiteLLMPassThroughEndpointProvider } from './pass-through-endpoint.ts';\n\nexport const litellmProviders = (\n creds: Layer.Layer<LitellmCredentials> = litellmCredentialsLayer,\n) => LiteLLMPassThroughEndpointProvider().pipe(Layer.provide(creds));\n"
|
|
10
|
+
],
|
|
11
|
+
"mappings": "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",
|
|
12
|
+
"debugId": "9216FE33E5E3721264756E2164756E21",
|
|
13
|
+
"names": []
|
|
14
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `LiteLLM.PassThroughEndpoint` — one row in LiteLLM's `general_settings.pass_through_endpoints`.
|
|
3
|
+
*
|
|
4
|
+
* ★ ADOPT-FRIENDLY BY id, NOT BY PATH. `output?.id` is used as the object's live id once the
|
|
5
|
+
* engine has one — from our own prior create (the deterministic physical name) or from an
|
|
6
|
+
* adopted foreign row — and `read` computes a FRESH physical name only for a resource the
|
|
7
|
+
* engine has never resolved before. This is S7/S9's "output is a cache of stable identifiers,
|
|
8
|
+
* not proof the object exists" and S13's "an adopted object keeps its live name as an explicit
|
|
9
|
+
* prop", applied to an id LiteLLM lets a client choose (`_types.py:2192` at v1.100.0: create
|
|
10
|
+
* takes a caller-supplied `id`, else `uuid4()`).
|
|
11
|
+
*
|
|
12
|
+
* ⛔ A PATH HELD BY AN `is_from_config` ROW IS REFUSED, NOT ADOPTED. A DB row on the same path
|
|
13
|
+
* overrides it at runtime (`pass_through_endpoints.py` merges DB after config, at the tag) — so
|
|
14
|
+
* creating one would silently take over a route the operator declared in `config.yaml`, the
|
|
15
|
+
* exact "DB row silently overrides the file" the task names. `is_from_config` rows are also
|
|
16
|
+
* never updated or deleted by this provider, for the same reason.
|
|
17
|
+
* ⛔ A DB ROW WITH NO `id` (path-only, `_types.py`'s "backwards compatibility" case) CANNOT BE
|
|
18
|
+
* ADDRESSED by the id-keyed update/delete calls this provider uses, so it is refused rather than
|
|
19
|
+
* silently adopted into an id-based lifecycle it cannot actually manage — see docs/litellm.md.
|
|
20
|
+
* ⛔ NO CREDENTIAL IS A PROP (S25). `LITELLM_PROXY_URL`/`LITELLM_PROXY_API_KEY` are read at call
|
|
21
|
+
* time by credentials.ts.
|
|
22
|
+
*/
|
|
23
|
+
import { Resource } from 'alchemy';
|
|
24
|
+
import type { Input } from 'alchemy/Input';
|
|
25
|
+
import * as Provider from 'alchemy/Provider';
|
|
26
|
+
import type { Stack } from 'alchemy/Stack';
|
|
27
|
+
import type { Stage } from 'alchemy/Stage';
|
|
28
|
+
import * as Effect from 'effect/Effect';
|
|
29
|
+
import { type LitellmError, type LitellmRequirements } from './client.ts';
|
|
30
|
+
import { type PassThroughEndpointAttributes, type PassThroughEndpointProps } from './pass-through-form.ts';
|
|
31
|
+
export type { PassThroughEndpointAttributes, PassThroughEndpointProps };
|
|
32
|
+
declare const LitellmConfigPathConflictError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
33
|
+
readonly _tag: "LitellmConfigPathConflictError";
|
|
34
|
+
} & Readonly<A>;
|
|
35
|
+
export declare class LitellmConfigPathConflictError extends LitellmConfigPathConflictError_base<{
|
|
36
|
+
readonly path: string;
|
|
37
|
+
}> {
|
|
38
|
+
}
|
|
39
|
+
declare const LitellmUnaddressableRowError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
40
|
+
readonly _tag: "LitellmUnaddressableRowError";
|
|
41
|
+
} & Readonly<A>;
|
|
42
|
+
export declare class LitellmUnaddressableRowError extends LitellmUnaddressableRowError_base<{
|
|
43
|
+
readonly path: string;
|
|
44
|
+
}> {
|
|
45
|
+
}
|
|
46
|
+
declare const LitellmLiteralSecretHeaderError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
47
|
+
readonly _tag: "LitellmLiteralSecretHeaderError";
|
|
48
|
+
} & Readonly<A>;
|
|
49
|
+
export declare class LitellmLiteralSecretHeaderError extends LitellmLiteralSecretHeaderError_base<{
|
|
50
|
+
readonly headers: readonly string[];
|
|
51
|
+
}> {
|
|
52
|
+
}
|
|
53
|
+
export type PassThroughEndpointError = LitellmError | LitellmConfigPathConflictError | LitellmUnaddressableRowError | LitellmLiteralSecretHeaderError;
|
|
54
|
+
export interface LiteLLMPassThroughEndpoint extends Resource<'LiteLLM.PassThroughEndpoint', PassThroughEndpointProps, PassThroughEndpointAttributes, never, LitellmRequirements | Stack | Stage> {
|
|
55
|
+
}
|
|
56
|
+
export declare const LiteLLMPassThroughEndpoint: import("alchemy").ResourceClass<LiteLLMPassThroughEndpoint>;
|
|
57
|
+
export declare const isLiteLLMPassThroughEndpoint: (value: unknown) => value is LiteLLMPassThroughEndpoint;
|
|
58
|
+
/** ⛔ TEST-ONLY EXPORT. Not on index.ts's barrel — a lifecycle test calls these directly against the fake. */
|
|
59
|
+
export declare const passThroughHandlers: {
|
|
60
|
+
read: ({ id, instanceId, olds, output, }: {
|
|
61
|
+
id: string;
|
|
62
|
+
instanceId: string;
|
|
63
|
+
olds: PassThroughEndpointProps;
|
|
64
|
+
output: PassThroughEndpointAttributes | undefined;
|
|
65
|
+
}) => Effect.Effect<PassThroughEndpointAttributes | undefined, LitellmConfigPathConflictError | LitellmUnaddressableRowError | LitellmError, import("alchemy").InstanceId | Stack | Stage | LitellmRequirements>;
|
|
66
|
+
diff: ({ news, output, }: {
|
|
67
|
+
news: Input<PassThroughEndpointProps>;
|
|
68
|
+
output: PassThroughEndpointAttributes | undefined;
|
|
69
|
+
}) => Effect.Effect<{
|
|
70
|
+
readonly action: 'replace';
|
|
71
|
+
readonly deleteFirst: true;
|
|
72
|
+
} | {
|
|
73
|
+
readonly deleteFirst?: never;
|
|
74
|
+
readonly action: 'noop';
|
|
75
|
+
} | {
|
|
76
|
+
readonly deleteFirst?: never;
|
|
77
|
+
readonly action: 'update';
|
|
78
|
+
} | undefined, LitellmLiteralSecretHeaderError, never>;
|
|
79
|
+
reconcile: ({ id, instanceId, news, output, }: {
|
|
80
|
+
id: string;
|
|
81
|
+
instanceId: string;
|
|
82
|
+
news: PassThroughEndpointProps;
|
|
83
|
+
output: PassThroughEndpointAttributes | undefined;
|
|
84
|
+
}) => Effect.Effect<PassThroughEndpointAttributes, LitellmConfigPathConflictError | LitellmLiteralSecretHeaderError | LitellmUnaddressableRowError | LitellmError, import("alchemy").InstanceId | Stack | Stage | LitellmRequirements>;
|
|
85
|
+
delete: ({ id, instanceId, olds, output, }: {
|
|
86
|
+
id: string;
|
|
87
|
+
instanceId: string;
|
|
88
|
+
olds: PassThroughEndpointProps;
|
|
89
|
+
output: PassThroughEndpointAttributes;
|
|
90
|
+
}) => Effect.Effect<void, LitellmError, import("alchemy").InstanceId | Stack | Stage | LitellmRequirements>;
|
|
91
|
+
list: () => Effect.Effect<never[], never, never>;
|
|
92
|
+
};
|
|
93
|
+
export declare const LiteLLMPassThroughEndpointProvider: () => import("effect/Layer").Layer<Provider.Provider<LiteLLMPassThroughEndpoint>, never, Stack | Stage | LitellmRequirements>;
|
|
94
|
+
//# sourceMappingURL=pass-through-endpoint.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pass-through-endpoint.d.ts","sourceRoot":"","sources":["../../src/litellm/pass-through-endpoint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAGnC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAE3C,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAE3C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,mBAAmB,EAKzB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,KAAK,6BAA6B,EAClC,KAAK,wBAAwB,EAM9B,MAAM,wBAAwB,CAAC;AAEhC,YAAY,EAAE,6BAA6B,EAAE,wBAAwB,EAAE,CAAC;;;;AAExE,qBAAa,8BAA+B,SAAQ,oCAElD;IACA,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB,CAAC;CAAG;;;;AACL,qBAAa,4BAA6B,SAAQ,kCAAiD;IACjG,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB,CAAC;CAAG;;;;AACL,qBAAa,+BAAgC,SAAQ,qCAEnD;IACA,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC,CAAC;CAAG;AAEL,MAAM,MAAM,wBAAwB,GAChC,YAAY,GACZ,8BAA8B,GAC9B,4BAA4B,GAC5B,+BAA+B,CAAC;AAEpC,MAAM,WAAW,0BAA2B,SAAQ,QAAQ,CAC1D,6BAA6B,EAC7B,wBAAwB,EACxB,6BAA6B,EAC7B,KAAK,EACL,mBAAmB,GAAG,KAAK,GAAG,KAAK,CACpC;CAAG;AAEJ,eAAO,MAAM,0BAA0B,6DAEtC,CAAC;AAEF,eAAO,MAAM,4BAA4B,UAAW,OAAO,KAAG,KAAK,IAAI,0BACe,CAAC;AAkDvF,6GAA6G;AAC7G,eAAO,MAAM,mBAAmB;IAC9B,IAAI,sCAKD;QACD,EAAE,EAAE,MAAM,CAAC;QACX,UAAU,EAAE,MAAM,CAAC;QACnB,IAAI,EAAE,wBAAwB,CAAC;QAC/B,MAAM,EAAE,6BAA6B,GAAG,SAAS,CAAC;KACnD;IAUD,IAAI,sBAGD;QACD,IAAI,EAAE,KAAK,CAAC,wBAAwB,CAAC,CAAC;QACtC,MAAM,EAAE,6BAA6B,GAAG,SAAS,CAAC;KACnD;yBAQoD,SAAS;;;;yBAE3C,MAAM;;;yBACN,QAAQ;;IAG3B,SAAS,sCAKN;QACD,EAAE,EAAE,MAAM,CAAC;QACX,UAAU,EAAE,MAAM,CAAC;QACnB,IAAI,EAAE,wBAAwB,CAAC;QAC/B,MAAM,EAAE,6BAA6B,GAAG,SAAS,CAAC;KACnD;IAwCD,MAAM,sCAKH;QACD,EAAE,EAAE,MAAM,CAAC;QACX,UAAU,EAAE,MAAM,CAAC;QACnB,IAAI,EAAE,wBAAwB,CAAC;QAC/B,MAAM,EAAE,6BAA6B,CAAC;KACvC;IAWD,IAAI;CACL,CAAC;AAEF,eAAO,MAAM,kCAAkC,+HAI5C,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type * as Generated from './generated/pass-through.ts';
|
|
2
|
+
export type PassThroughEndpointProps = Omit<Generated.PassThroughGenericEndpoint, 'id' | 'is_from_config'>;
|
|
3
|
+
/** What `read` returns: the props plus the two vendor-owned fields, `id` now guaranteed present. */
|
|
4
|
+
export type PassThroughEndpointAttributes = Omit<Generated.PassThroughGenericEndpoint, 'id'> & {
|
|
5
|
+
readonly id: string;
|
|
6
|
+
};
|
|
7
|
+
/** ⚠️ NetBox `settled()`'s pattern: only fields the SCHEMA gives a vendor default for, copied from it (not assumed). */
|
|
8
|
+
export declare const settled: (props: PassThroughEndpointProps) => {
|
|
9
|
+
auth: boolean;
|
|
10
|
+
cost_per_request: number;
|
|
11
|
+
default_query_params: Record<string, unknown>;
|
|
12
|
+
headers: Record<string, unknown>;
|
|
13
|
+
include_subpath: boolean;
|
|
14
|
+
};
|
|
15
|
+
/** The three fields whose absence the vendor's `exclude_none` update can never write (see client.ts). */
|
|
16
|
+
export declare const NULLABLE_FIELDS: readonly ['timeout', 'methods', 'guardrails'];
|
|
17
|
+
/**
|
|
18
|
+
* Full body for CREATE. `id` is the deterministic physical name (pass-through-endpoint.ts), sent
|
|
19
|
+
* so a failed create is recoverable by `read` rather than orphaning a second row on retry.
|
|
20
|
+
*/
|
|
21
|
+
export declare const createBody: (props: PassThroughEndpointProps, id: string) => Generated.PassThroughGenericEndpoint;
|
|
22
|
+
/**
|
|
23
|
+
* Partial body for UPDATE (`POST /{id}`). ⛔ ONLY FIELDS THIS DECLARATION ACTUALLY SETS — the
|
|
24
|
+
* vendor merges the parsed body with `model_dump(exclude_none=True)` (measured at v1.100.0), so an
|
|
25
|
+
* `undefined` OR an explicit `null` here would be dropped by the server anyway; sending neither is
|
|
26
|
+
* the same outcome with no request made when nothing changed (`Object.keys(body).length === 0`
|
|
27
|
+
* checked by the caller in pass-through-endpoint.ts).
|
|
28
|
+
*/
|
|
29
|
+
export declare const updateBody: (props: PassThroughEndpointProps) => Partial<Generated.PassThroughGenericEndpoint>;
|
|
30
|
+
/**
|
|
31
|
+
* Whether ANY of the three nullable fields went from set (in `live`) to unset (in `props`) — the
|
|
32
|
+
* one case `updateBody` cannot express, because the vendor's `exclude_none` merge silently keeps
|
|
33
|
+
* the old value instead of clearing it. Detected here so `pass-through-endpoint.ts` can plan a
|
|
34
|
+
* `replace` (delete-then-create) instead of an update that would look like it worked.
|
|
35
|
+
*/
|
|
36
|
+
export declare const needsReplace: (live: PassThroughEndpointAttributes, props: PassThroughEndpointProps) => boolean;
|
|
37
|
+
/** Whether the live object already says what the declaration says — the `diff` "noop" test. */
|
|
38
|
+
export declare const matches: (live: PassThroughEndpointAttributes, props: PassThroughEndpointProps) => boolean;
|
|
39
|
+
export declare const literalSecretHeaders: (headers: Readonly<Record<string, unknown>> | undefined) => readonly string[];
|
|
40
|
+
//# sourceMappingURL=pass-through-form.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pass-through-form.d.ts","sourceRoot":"","sources":["../../src/litellm/pass-through-form.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,KAAK,SAAS,MAAM,6BAA6B,CAAC;AAE9D,MAAM,MAAM,wBAAwB,GAAG,IAAI,CACzC,SAAS,CAAC,0BAA0B,EACpC,IAAI,GAAG,gBAAgB,CACxB,CAAC;AAEF,oGAAoG;AACpG,MAAM,MAAM,6BAA6B,GAAG,IAAI,CAAC,SAAS,CAAC,0BAA0B,EAAE,IAAI,CAAC,GAAG;IAC7F,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,wHAAwH;AACxH,eAAO,MAAM,OAAO,UAAW,wBAAwB;IACrD,IAAI;IACJ,gBAAgB;IAChB,oBAAoB;IACpB,OAAO;IACP,eAAe;CACf,CAAC;AAEH,yGAAyG;AACzG,eAAO,MAAM,eAAe,YAAI,SAAS,EAAE,SAAS,EAAE,YAAY,CAAU,CAAC;AAa7E;;;GAGG;AACH,eAAO,MAAM,UAAU,UACd,wBAAwB,MAC3B,MAAM,KACT,SAAS,CAAC,0BAS0C,CAAC;AAExD;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,UACd,wBAAwB,KAC9B,OAAO,CAAC,SAAS,CAAC,0BAA0B,CAQM,CAAC;AAEtD;;;;;GAKG;AACH,eAAO,MAAM,YAAY,SACjB,6BAA6B,SAC5B,wBAAwB,KAC9B,OAA0F,CAAC;AAE9F,+FAA+F;AAC/F,eAAO,MAAM,OAAO,SACZ,6BAA6B,SAC5B,wBAAwB,KAC9B,OAeF,CAAC;AAWF,eAAO,MAAM,oBAAoB,YACtB,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,SAAS,KACrD,SAAS,MAAM,EAMQ,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The LiteLLM provider, with its credential source, as one layer for a stack.
|
|
3
|
+
*
|
|
4
|
+
* Layer.mergeAll(litellmProviders(), …the stack's other providers)
|
|
5
|
+
*
|
|
6
|
+
* ★ THE DEFAULT READS `LITELLM_PROXY_URL`/`LITELLM_PROXY_API_KEY` AT CALL TIME (credentials.ts),
|
|
7
|
+
* never at layer build — pass another `Layer.Layer<LitellmCredentials>` (see
|
|
8
|
+
* `litellmCredentialsLayerFor` in a test) to source them another way.
|
|
9
|
+
* ⚠️ `HttpClient` is NOT provided here. It is ambient in every Alchemy runtime the same way it is
|
|
10
|
+
* for `netboxProviders`-style families in this kit; a stack that already deploys anything over
|
|
11
|
+
* HTTP already has one.
|
|
12
|
+
*/
|
|
13
|
+
import * as Layer from 'effect/Layer';
|
|
14
|
+
import { type LitellmCredentials } from './credentials.ts';
|
|
15
|
+
export declare const litellmProviders: (creds?: Layer.Layer<LitellmCredentials>) => Layer.Layer<import("alchemy").Provider<import("./pass-through-endpoint.ts").LiteLLMPassThroughEndpoint>, never, import("effect/unstable/http/HttpClient").HttpClient | import("alchemy").Stack | import("alchemy").Stage>;
|
|
16
|
+
//# sourceMappingURL=providers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"providers.d.ts","sourceRoot":"","sources":["../../src/litellm/providers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,KAAK,kBAAkB,EAA2B,MAAM,kBAAkB,CAAC;AAGpF,eAAO,MAAM,gBAAgB,WACpB,KAAK,CAAC,KAAK,CAAC,kBAAkB,CAAC,8NAC4B,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a database's cluster comes from, and how each lifecycle operation gets a client.
|
|
3
|
+
*
|
|
4
|
+
* ⛔ NO ESTATE VALUE LIVES HERE. The kit is PUBLIC; the mini's socket path, its user and its
|
|
5
|
+
* database names are the CONSUMER's props, passed to `postgresConnection(...)` in a house
|
|
6
|
+
* stack — never a default, never a fallback (`docs/postgres.md#path`).
|
|
7
|
+
* ★ A LAZY `Context.Service` WHOSE VALUE IS AN `Effect` (S24), THE SAME SHAPE THE HOUSE USES FOR
|
|
8
|
+
* CREDENTIALS: `yield* yield* PostgresConnection` resolves the config INSIDE each operation,
|
|
9
|
+
* never at layer build. Nothing here holds a secret today (`PostgresConnectionConfig` has no
|
|
10
|
+
* `password` — S25, and CREATE DATABASE has none to assert either), but the shape is the one a
|
|
11
|
+
* later TLS path (`Config.redacted` from the environment) drops into without a rewrite.
|
|
12
|
+
* ★ THE POOL IS BUILT IN A SCOPE AND CLOSED PER OPERATION. `Effect.provide(op, PgClient.layer(config))`
|
|
13
|
+
* already opens a scope for the layer's build and closes it when `op` finishes — one pool per
|
|
14
|
+
* `withPg` call, never held across a whole `reconcile`.
|
|
15
|
+
*/
|
|
16
|
+
import * as PgClient from '@effect/sql-pg/PgClient';
|
|
17
|
+
import * as Context from 'effect/Context';
|
|
18
|
+
import * as Effect from 'effect/Effect';
|
|
19
|
+
import * as Layer from 'effect/Layer';
|
|
20
|
+
import type { SqlError } from 'effect/unstable/sql/SqlError';
|
|
21
|
+
/** What a stack passes to reach one cluster. `host` may be a socket directory (one beginning
|
|
22
|
+
* with `/` expands to `${host}/.s.PGSQL.${port}` — measured in
|
|
23
|
+
* `node_modules/@effect/sql-pg/src/PgConnection.ts`, the "Details" doc on `Config`) or a TCP
|
|
24
|
+
* host name; `port` defaults to `@effect/sql-pg`'s own default (5432) when omitted. */
|
|
25
|
+
export interface PostgresConnectionConfig {
|
|
26
|
+
readonly host: string;
|
|
27
|
+
readonly port?: number;
|
|
28
|
+
readonly database: string;
|
|
29
|
+
readonly username: string;
|
|
30
|
+
readonly ssl?: boolean;
|
|
31
|
+
}
|
|
32
|
+
declare const PostgresConnection_base: Context.ServiceClass<PostgresConnection, "Postgres.Connection", Effect.Effect<PostgresConnectionConfig, never, never>>;
|
|
33
|
+
/** The lazy connection service. Its value is an `Effect` of the config, per S24 — see the file
|
|
34
|
+
* header for why. */
|
|
35
|
+
export declare class PostgresConnection extends PostgresConnection_base {
|
|
36
|
+
}
|
|
37
|
+
/** A stack's one-line way to provide a cluster: `Layer.provide(postgresConnection({ … }))` on
|
|
38
|
+
* `PostgresDatabaseProvider()`, or merged into the stack's own layer tree. */
|
|
39
|
+
export declare const postgresConnection: (config: PostgresConnectionConfig) => Layer.Layer<PostgresConnection>;
|
|
40
|
+
/**
|
|
41
|
+
* Run one operation against a freshly built, freshly closed connection pool.
|
|
42
|
+
*
|
|
43
|
+
* Every `read`, `reconcile` step and `delete` in this family calls this once for the whole
|
|
44
|
+
* operation — never once per statement — so a reconcile that runs a `SELECT`, then a `CREATE`,
|
|
45
|
+
* then a re-read shares one pool and closes it once, while a plan-only `read` still opens and
|
|
46
|
+
* closes its own.
|
|
47
|
+
*/
|
|
48
|
+
export declare const withPg: <A, E>(build: (pg: PgClient.PgClient) => Effect.Effect<A, E>) => Effect.Effect<A, E | SqlError, PostgresConnection>;
|
|
49
|
+
export {};
|
|
50
|
+
//# sourceMappingURL=connection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/postgres/connection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,QAAQ,MAAM,yBAAyB,CAAC;AACpD,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAC;AAC1C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAE7D;;;uFAGuF;AACvF,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC;CACxB;;AAED;qBACqB;AACrB,qBAAa,kBAAmB,SAAQ,uBAGd;CAAG;AAE7B;8EAC8E;AAC9E,eAAO,MAAM,kBAAkB,WACrB,wBAAwB,KAC/B,KAAK,CAAC,KAAK,CAAC,kBAAkB,CAA8D,CAAC;AAEhG;;;;;;;GAOG;AACH,eAAO,MAAM,MAAM,GAAI,CAAC,EAAE,CAAC,SAClB,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,KACpD,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,GAAG,QAAQ,EAAE,kBAAkB,CAKhD,CAAC"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Postgres.Database` props and attributes, and the one prop-level check that runs before any
|
|
3
|
+
* statement: the name's byte length against `NAMEDATALEN`.
|
|
4
|
+
*
|
|
5
|
+
* ⛔ EVERY ASSERTED PROP HERE HAS A CITED COLUMN. `docs/postgres.md#mapping` and
|
|
6
|
+
* `database-provenance.test.ts` are what make that true rather than asserted: the test parses
|
|
7
|
+
* the two committed fixtures (`create_database.sgml`'s synopsis, `pg_database.h`'s struct) and
|
|
8
|
+
* fails if a prop here has no synopsis option or no catalog column behind it.
|
|
9
|
+
* ⛔ NO PASSWORD, EVER. A database has none in `pg_database` (S25) — `CREATE DATABASE` has no
|
|
10
|
+
* password option at all (`gram.y@REL_18_6#createdb_opt_name`, measured: `IDENT | CONNECTION
|
|
11
|
+
* LIMIT | ENCODING | LOCATION | OWNER | TABLESPACE | TEMPLATE`).
|
|
12
|
+
*/
|
|
13
|
+
/** NAMEDATALEN (`pg_config_manual.h@REL_18_6` line 29) minus the C string terminator: the
|
|
14
|
+
* longest name the server stores without truncating. */
|
|
15
|
+
export declare const POSTGRES_NAME_MAX_BYTES = 63;
|
|
16
|
+
/** UTF-8 byte length, the same unit `NAMEDATALEN` counts in (`scansup.c@REL_18_6#truncate_identifier`
|
|
17
|
+
* clips by `pg_mbcliplen`, a byte-oriented, multibyte-aware clip — never a JS `.length`, which
|
|
18
|
+
* counts UTF-16 code units and would pass a 63-byte multibyte name that is really 70+ bytes, or
|
|
19
|
+
* refuse a 63-byte name whose `.length` looks smaller). */
|
|
20
|
+
export declare const utf8ByteLength: (value: string) => number;
|
|
21
|
+
/**
|
|
22
|
+
* Declared, at-most-once-asserted properties of one database.
|
|
23
|
+
*
|
|
24
|
+
* Every prop here maps to a `CREATE DATABASE` `WITH` option `create_database.sgml` documents
|
|
25
|
+
* AND a `pg_database` column that stores it (`database-provenance.test.ts`). `TEMPLATE`,
|
|
26
|
+
* `STRATEGY` and `OID` are excluded because nothing in `pg_database` reads them back once the
|
|
27
|
+
* database exists; `ICU_RULES` and `COLLATION_VERSION` are excluded for having no consumer in
|
|
28
|
+
* this family today. See `docs/postgres.md#excluded`.
|
|
29
|
+
*/
|
|
30
|
+
export interface PostgresDatabaseProps {
|
|
31
|
+
/** At most 63 UTF-8 bytes — refused at plan past that (`PostgresDatabaseNameRefused`). The
|
|
32
|
+
* identity: a name change is a plan-time refusal, never a replace. */
|
|
33
|
+
readonly name: string;
|
|
34
|
+
/** A role name in `pg_roles`, asserted before create (`PostgresDatabaseOwnerMissing`) and
|
|
35
|
+
* compared against `pg_get_userbyid(datdba)` on every plan thereafter. */
|
|
36
|
+
readonly owner: string;
|
|
37
|
+
/** `pg_encoding_to_char(datdatabase.encoding)`, e.g. `"UTF8"`. @default the template's. */
|
|
38
|
+
readonly encoding?: string;
|
|
39
|
+
/** `'builtin' | 'icu' | 'libc'` — `pg_database.datlocprovider` decoded
|
|
40
|
+
* (`pg_collation.h@REL_18_6#collprovider_name`). @default the template's. */
|
|
41
|
+
readonly localeProvider?: 'builtin' | 'icu' | 'libc';
|
|
42
|
+
/** `pg_database.datcollate`. @default the template's. */
|
|
43
|
+
readonly lcCollate?: string;
|
|
44
|
+
/** `pg_database.datctype`. @default the template's. */
|
|
45
|
+
readonly lcCtype?: string;
|
|
46
|
+
/** `pg_database.datallowconn`. @default `true`. */
|
|
47
|
+
readonly allowConnections?: boolean;
|
|
48
|
+
/** `pg_database.datconnlimit`; `-1` means unlimited. @default `-1`. */
|
|
49
|
+
readonly connectionLimit?: number;
|
|
50
|
+
/** `pg_database.datistemplate`. @default `false`. */
|
|
51
|
+
readonly isTemplate?: boolean;
|
|
52
|
+
/** A tablespace name in `pg_tablespace`. @default the template's (usually `pg_default`). */
|
|
53
|
+
readonly tablespace?: string;
|
|
54
|
+
}
|
|
55
|
+
/** Live attributes, read back after every reconcile. No `password` — see the file header. */
|
|
56
|
+
export interface PostgresDatabaseAttributes {
|
|
57
|
+
readonly name: string;
|
|
58
|
+
readonly oid: number;
|
|
59
|
+
readonly owner: string;
|
|
60
|
+
readonly encoding: string;
|
|
61
|
+
readonly localeProvider: 'builtin' | 'icu' | 'libc';
|
|
62
|
+
readonly collate: string;
|
|
63
|
+
readonly ctype: string;
|
|
64
|
+
readonly allowConnections: boolean;
|
|
65
|
+
readonly connectionLimit: number;
|
|
66
|
+
readonly isTemplate: boolean;
|
|
67
|
+
readonly tablespace: string;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Refuse a name the server would truncate, before any statement is built.
|
|
71
|
+
*
|
|
72
|
+
* ★ CALLED FROM `diff` (so a plan fails, never a deploy) AND FROM `reconcile` (so a fake or a
|
|
73
|
+
* direct call gets the same refusal, never just a broken statement).
|
|
74
|
+
*/
|
|
75
|
+
export declare const nameByteRefusal: (name: string) => {
|
|
76
|
+
readonly byteLength: number;
|
|
77
|
+
readonly limit: number;
|
|
78
|
+
} | undefined;
|
|
79
|
+
//# sourceMappingURL=database-attrs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"database-attrs.d.ts","sourceRoot":"","sources":["../../src/postgres/database-attrs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH;wDACwD;AACxD,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAE1C;;;2DAG2D;AAC3D,eAAO,MAAM,cAAc,UAAW,MAAM,KAAG,MAAoD,CAAC;AAEpG;;;;;;;;GAQG;AACH,MAAM,WAAW,qBAAqB;IACpC;0EACsE;IACtE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;8EAC0E;IAC1E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,2FAA2F;IAC3F,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B;iFAC6E;IAC7E,QAAQ,CAAC,cAAc,CAAC,EAAE,SAAS,GAAG,KAAK,GAAG,MAAM,CAAC;IACrD,yDAAyD;IACzD,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,uDAAuD;IACvD,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,mDAAmD;IACnD,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IACpC,uEAAuE;IACvE,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,qDAAqD;IACrD,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAC9B,4FAA4F;IAC5F,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,6FAA6F;AAC7F,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,cAAc,EAAE,SAAS,GAAG,KAAK,GAAG,MAAM,CAAC;IACpD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAC;IACnC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;GAKG;AACH,eAAO,MAAM,eAAe,SACpB,MAAM,KACX;IAAE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,SAK5D,CAAC"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The four statements `Postgres.Database` ever issues, and the quoting each one needs.
|
|
3
|
+
*
|
|
4
|
+
* ⛔ `CREATE DATABASE` TAKES NO BIND PARAMETERS FOR ANY OF ITS VALUES, INCLUDING `OWNER`.
|
|
5
|
+
* Measured at `gram.y@REL_18_6`: `CreatedbStmt: CREATE DATABASE name opt_with
|
|
6
|
+
* createdb_opt_list`, and every `createdb_opt_item` value is `NumericOnly`,
|
|
7
|
+
* `opt_boolean_or_string` or the bare keyword `DEFAULT` — none of those productions reaches
|
|
8
|
+
* `PARAM` (`$n`). `sql\`…\`` cannot help here; every free-form value in this statement is
|
|
9
|
+
* escaped by hand, once, in this file, and the assembled text runs through `.unsafe()`.
|
|
10
|
+
* ⚠️ THE DATABASE NAME AND EFFECT'S OWN `sql(value)` IDENTIFIER HELPER DO NOT MIX. `name` is a
|
|
11
|
+
* `ColId` (`gram.y@REL_18_6` line 17327), a single token — but `Statement.defaultEscape('"')`
|
|
12
|
+
* (what `sql(value)` compiles to) also rewrites every `.` in the value to `"."`, because it is
|
|
13
|
+
* built for a *qualified* name like `schema.table`. A database named `"my.app"` would come out
|
|
14
|
+
* as `"my"."app"` — two tokens where `CreatedbStmt` parses exactly one — a syntax error the
|
|
15
|
+
* engine would blame on this provider. `quoteIdent` below is the single-token form: it quotes
|
|
16
|
+
* and doubles embedded `"`, and nothing else.
|
|
17
|
+
* ★ EVERY OTHER `WITH` VALUE (`OWNER`, `TABLESPACE`, `ENCODING`, `LOCALE_PROVIDER`,
|
|
18
|
+
* `LC_COLLATE`, `LC_CTYPE`) IS A STRING LITERAL, NOT AN IDENTIFIER. Measured at the same tag:
|
|
19
|
+
* `createdb_opt_item`'s string branch is `opt_boolean_or_string → NonReservedWord_or_Sconst →
|
|
20
|
+
* NonReservedWord | Sconst`. A locale like `en_US.UTF-8` would hit the same dot-splitting
|
|
21
|
+
* `quoteIdent` avoids if it were identifier-quoted; a plain `Sconst` (`quoteStringLiteral`) has
|
|
22
|
+
* no such rule. This rests on `standard_conforming_strings = on` (the PG18 default, and
|
|
23
|
+
* measured live 2026-09-23 — `docs/postgres.md#measured`); the escaper only ever doubles `'`
|
|
24
|
+
* and never emits a backslash, so it never depends on which way that setting points.
|
|
25
|
+
*/
|
|
26
|
+
import type { SqlError } from 'effect/unstable/sql/SqlError';
|
|
27
|
+
import * as Effect from 'effect/Effect';
|
|
28
|
+
import type { PostgresDatabaseAttributes, PostgresDatabaseProps } from './database-attrs.ts';
|
|
29
|
+
/** What every function in this file needs from a client — real (`PgClient.PgClient`) or fake
|
|
30
|
+
* (`fake-sql.ts`). Deliberately smaller than `SqlClient`: nothing here ever needs the tagged
|
|
31
|
+
* template, transactions or streaming, because every statement is either bound params through
|
|
32
|
+
* `.unsafe(text, params)` or a hand-escaped literal through `.unsafe(text)`. */
|
|
33
|
+
export interface PgExecutor {
|
|
34
|
+
readonly unsafe: <A extends object>(sql: string, params?: ReadonlyArray<unknown>) => Effect.Effect<ReadonlyArray<A>, SqlError>;
|
|
35
|
+
}
|
|
36
|
+
/** Single-token identifier quoting: wrap in `"`, double any embedded `"`. No dot-splitting —
|
|
37
|
+
* see the file header. Used for the database name and, since a role or tablespace name is a
|
|
38
|
+
* `NonReservedWord` too, for `OWNER` and `TABLESPACE`. */
|
|
39
|
+
export declare const quoteIdent: (value: string) => string;
|
|
40
|
+
/** SQL string-literal quoting: wrap in `'`, double any embedded `'`. Used for every `WITH`
|
|
41
|
+
* value that is locale or encoding text, never for an identifier (see the file header). */
|
|
42
|
+
export declare const quoteStringLiteral: (value: string) => string;
|
|
43
|
+
/**
|
|
44
|
+
* Build the exact `CREATE DATABASE` text — a pure function, unit-tested on its own with no
|
|
45
|
+
* client at all. `CONNECTION LIMIT` and the two booleans are never string-escaped: an integer is
|
|
46
|
+
* `Math.trunc`ed and stringified, and a boolean is one of exactly two literal words this file
|
|
47
|
+
* writes, so no declared value reaches the statement through string interpolation unescaped.
|
|
48
|
+
*/
|
|
49
|
+
export declare const buildCreateDatabaseSql: (props: PostgresDatabaseProps) => string;
|
|
50
|
+
/** `42P04` (`duplicate_database`) is a race: something else created the name between our read
|
|
51
|
+
* and our create. Measured at `internal/sqlError.ts@effect/sql-pg 4.0.0-rc.115`: every SQLSTATE
|
|
52
|
+
* starting `42` — this one included — classifies as `SqlSyntaxError`, the same tag a genuine
|
|
53
|
+
* syntax error would raise; there is no more specific tag to `catchTag` on. So this reads the
|
|
54
|
+
* raw code PostgreSQL sent, carried on `reason.cause.code` (`PgConnection.ts#errorFromFields`,
|
|
55
|
+
* `Object.assign(new Error(...), fields)`), rather than trusting the tag alone. */
|
|
56
|
+
export declare const isDuplicateDatabaseRace: (error: SqlError) => boolean;
|
|
57
|
+
/** `OWNER` is checked against `pg_roles` before any `CREATE`, so a missing role is
|
|
58
|
+
* `PostgresDatabaseOwnerMissing` at reconcile time rather than the server's own
|
|
59
|
+
* `ERROR: role "…" does not exist` surfacing as an unclassified statement failure. */
|
|
60
|
+
export declare const roleExists: (pg: PgExecutor, role: string) => Effect.Effect<boolean, SqlError>;
|
|
61
|
+
/** The read: one bound `SELECT` on `pg_database`, joined to the two functions that turn its raw
|
|
62
|
+
* owner and encoding columns into names (`docs/postgres.md#mapping`). `undefined` when the row
|
|
63
|
+
* is absent — ownership branding happens one layer up, in `database.ts`, not here. */
|
|
64
|
+
export declare const selectDatabase: (pg: PgExecutor, name: string) => Effect.Effect<PostgresDatabaseAttributes | undefined, SqlError>;
|
|
65
|
+
/** Compare every declared, asserted prop against the live row. Returns the name of the first
|
|
66
|
+
* prop that drifted, or `undefined` when every declared prop matches. `name` and `owner` are
|
|
67
|
+
* compared by their callers (identity and the create-time assertion), never here. */
|
|
68
|
+
export declare const firstDrift: (props: PostgresDatabaseProps, live: PostgresDatabaseAttributes) => {
|
|
69
|
+
readonly prop: string;
|
|
70
|
+
readonly declared: unknown;
|
|
71
|
+
readonly live: unknown;
|
|
72
|
+
} | undefined;
|
|
73
|
+
//# sourceMappingURL=database-sql.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"database-sql.d.ts","sourceRoot":"","sources":["../../src/postgres/database-sql.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,EAAE,0BAA0B,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAE7F;;;gFAGgF;AAChF,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,SAAS,MAAM,EAChC,GAAG,EAAE,MAAM,EACX,MAAM,CAAC,EAAE,aAAa,CAAC,OAAO,CAAC,KAC5B,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;CAChD;AAED;;0DAE0D;AAC1D,eAAO,MAAM,UAAU,UAAW,MAAM,KAAG,MAA0C,CAAC;AAEtF;2FAC2F;AAC3F,eAAO,MAAM,kBAAkB,UAAW,MAAM,KAAG,MAA0C,CAAC;AAuB9F;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,UAAW,qBAAqB,KAAG,MACE,CAAC;AAEzE;;;;;mFAKmF;AACnF,eAAO,MAAM,uBAAuB,UAAW,QAAQ,KAAG,OAMzD,CAAC;AAIF;;sFAEsF;AACtF,eAAO,MAAM,UAAU,OAAQ,UAAU,QAAQ,MAAM,KAAG,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,QAAQ,CAItF,CAAC;AAwCJ;;sFAEsF;AACtF,eAAO,MAAM,cAAc,OACrB,UAAU,QACR,MAAM,KACX,MAAM,CAAC,MAAM,CAAC,0BAA0B,GAAG,SAAS,EAAE,QAAQ,CACkC,CAAC;AAEpG;;qFAEqF;AACrF,eAAO,MAAM,UAAU,UACd,qBAAqB,QACtB,0BAA0B,KAC/B;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAAG,SAgBlF,CAAC"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Postgres.Database` — create-and-assert over a self-hosted PostgreSQL 18 cluster.
|
|
3
|
+
*
|
|
4
|
+
* ⛔ THIS IS NOT AN ALTER-CAPABLE RESOURCE. Every optional prop is asserted ONCE, at create, and
|
|
5
|
+
* compared against the live row on every later plan; a mismatch is a typed
|
|
6
|
+
* `PostgresDatabaseDrift` refusal, never an `ALTER DATABASE`. `docs/postgres.md` says why:
|
|
7
|
+
* several of these props (`LC_COLLATE`, `LC_CTYPE`, `LOCALE_PROVIDER`) cannot be altered on a
|
|
8
|
+
* live database at all without `ALTER … REFRESH COLLATION VERSION` semantics this family does
|
|
9
|
+
* not implement, and the rest are left alone on principle rather than by accident.
|
|
10
|
+
* ⛔ `delete` NEVER DROPS. See `errors.ts#PostgresDatabaseDropRefused` and
|
|
11
|
+
* `defaultRemovalPolicy: 'retain'` below — two independent reasons the engine never issues
|
|
12
|
+
* `DROP DATABASE` through this provider.
|
|
13
|
+
* ★ A DATABASE CARRIES NO OWNERSHIP MARK (H1). `read` always answers `Unowned` for a match, the
|
|
14
|
+
* same rule `Cloudflare.Snippets.Snippet` uses for a marker-less API — so every one of the
|
|
15
|
+
* mini's 19 already-live databases needs `adopt(true)` in the stack that declares it.
|
|
16
|
+
*/
|
|
17
|
+
import { Resource } from 'alchemy';
|
|
18
|
+
import type { Input } from 'alchemy/Input';
|
|
19
|
+
import * as Provider from 'alchemy/Provider';
|
|
20
|
+
import * as Effect from 'effect/Effect';
|
|
21
|
+
import type { SqlError } from 'effect/unstable/sql/SqlError';
|
|
22
|
+
import type { PostgresDatabaseAttributes, PostgresDatabaseProps } from './database-attrs.ts';
|
|
23
|
+
import { type PostgresConnection } from './connection.ts';
|
|
24
|
+
import type { PgExecutor } from './database-sql.ts';
|
|
25
|
+
import { PostgresDatabaseCreateVanished, PostgresDatabaseDrift, PostgresDatabaseNameRefused, PostgresDatabaseOwnerMissing, PostgresDatabaseRenameRefused } from './errors.ts';
|
|
26
|
+
export interface PostgresDatabase extends Resource<'Postgres.Database', PostgresDatabaseProps, PostgresDatabaseAttributes, never, PostgresConnection> {
|
|
27
|
+
}
|
|
28
|
+
export declare const PostgresDatabase: import("alchemy").ResourceClass<PostgresDatabase>;
|
|
29
|
+
/** Mirrors `alchemy/Resource`'s own `isResourceOfType`: a resource constructor is a callable
|
|
30
|
+
* `Object.assign`ed function, not a plain object, so `typeof` must accept both — a bare
|
|
31
|
+
* `'object'` check (measured live in `scripts/smoke.ts`: it rejects `PostgresDatabase` itself,
|
|
32
|
+
* matching only an instance) is the regression this comment exists to prevent. */
|
|
33
|
+
export declare const isPostgresDatabase: (value: unknown) => value is PostgresDatabase;
|
|
34
|
+
/**
|
|
35
|
+
* The core of `reconcile`, against any {@link PgExecutor} — the real pooled client through
|
|
36
|
+
* `withPg`, or `fake-sql.ts`'s recording fake in tests. Kept apart from the provider wiring
|
|
37
|
+
* below so a lifecycle test drives this exact function, not a re-implementation of it.
|
|
38
|
+
*/
|
|
39
|
+
export declare const reconcileWithClient: (pg: PgExecutor, props: PostgresDatabaseProps) => Effect.Effect<PostgresDatabaseAttributes, PostgresDatabaseOwnerMissing | PostgresDatabaseDrift | PostgresDatabaseCreateVanished | SqlError>;
|
|
40
|
+
/** The core of `read`: one bound `SELECT`, no ownership branding — the caller (the provider's
|
|
41
|
+
* `read` below, or a test) decides whether to wrap a match in `Unowned`. */
|
|
42
|
+
export declare const readWithClient: (pg: PgExecutor, name: string) => Effect.Effect<PostgresDatabaseAttributes | undefined, SqlError, never>;
|
|
43
|
+
/**
|
|
44
|
+
* The core of `diff`, entirely offline: `news` (declared) against `output` (last-applied
|
|
45
|
+
* attributes) — never a live query (that authoritative check is `reconcileWithClient`'s job, per
|
|
46
|
+
* S10). A rename is a plan-time refusal; over-long names are too, so a bad name never even reaches
|
|
47
|
+
* `reconcile`; every other change answers `update`, which `reconcileWithClient` then either
|
|
48
|
+
* applies (create) or refuses (drift on an already-live database).
|
|
49
|
+
*/
|
|
50
|
+
export declare const diffPostgresDatabase: (news: Input<PostgresDatabaseProps>, output: PostgresDatabaseAttributes | undefined) => Effect.Effect<{
|
|
51
|
+
readonly action: 'update';
|
|
52
|
+
} | {
|
|
53
|
+
readonly action: 'noop';
|
|
54
|
+
} | undefined, PostgresDatabaseNameRefused | PostgresDatabaseRenameRefused, never>;
|
|
55
|
+
/**
|
|
56
|
+
* The five lifecycle handlers, wired to `withPg` for anything that touches a client. Exported
|
|
57
|
+
* on its own — not only through {@link PostgresDatabaseProvider}'s `Layer` — so a test can call
|
|
58
|
+
* `postgresDatabaseHandlers.delete(...)` directly, against the real implementation, without
|
|
59
|
+
* standing up the engine's provider machinery.
|
|
60
|
+
*/
|
|
61
|
+
export declare const postgresDatabaseHandlers: Provider.ProviderService<PostgresDatabase, PostgresConnection, never, never, PostgresConnection, never, never, never, never>;
|
|
62
|
+
export declare const PostgresDatabaseProvider: () => import("effect/Layer").Layer<Provider.Provider<PostgresDatabase>, never, PostgresConnection>;
|
|
63
|
+
//# sourceMappingURL=database.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/postgres/database.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAGnC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,KAAK,EAAE,0BAA0B,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAS7F,OAAO,EAAE,KAAK,kBAAkB,EAAU,MAAM,iBAAiB,CAAC;AAClE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EACL,8BAA8B,EAC9B,qBAAqB,EAErB,2BAA2B,EAC3B,4BAA4B,EAC5B,6BAA6B,EAC9B,MAAM,aAAa,CAAC;AAErB,MAAM,WAAW,gBAAiB,SAAQ,QAAQ,CAChD,mBAAmB,EACnB,qBAAqB,EACrB,0BAA0B,EAC1B,KAAK,EACL,kBAAkB,CACnB;CAAG;AAEJ,eAAO,MAAM,gBAAgB,mDAE3B,CAAC;AAEH;;;kFAGkF;AAClF,eAAO,MAAM,kBAAkB,UAAW,OAAO,KAAG,KAAK,IAAI,gBAGD,CAAC;AAY7D;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,OAC1B,UAAU,SACP,qBAAqB,KAC3B,MAAM,CAAC,MAAM,CACd,0BAA0B,EAC1B,4BAA4B,GAAG,qBAAqB,GAAG,8BAA8B,GAAG,QAAQ,CAqC9F,CAAC;AAEL;4EAC4E;AAC5E,eAAO,MAAM,cAAc,OAAQ,UAAU,QAAQ,MAAM,2EAA6B,CAAC;AAEzF;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,SACzB,KAAK,CAAC,qBAAqB,CAAC,UAC1B,0BAA0B,GAAG,SAAS;qBAgBhB,QAAQ;;qBAA0B,MAAM;kFACpE,CAAC;AAEL;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,8HA4BnC,CAAC;AAEH,eAAO,MAAM,wBAAwB,oGACyB,CAAC"}
|