@homeflare/alchemy 0.23.0 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. package/README.md +25 -1
  2. package/dist/litellm/client.d.ts +76 -0
  3. package/dist/litellm/client.d.ts.map +1 -0
  4. package/dist/litellm/credentials.d.ts +48 -0
  5. package/dist/litellm/credentials.d.ts.map +1 -0
  6. package/dist/litellm/fake-litellm.d.ts +36 -0
  7. package/dist/litellm/fake-litellm.d.ts.map +1 -0
  8. package/dist/litellm/fake-stack.d.ts +30 -0
  9. package/dist/litellm/fake-stack.d.ts.map +1 -0
  10. package/dist/litellm/generated/pass-through.d.ts +71 -0
  11. package/dist/litellm/generated/pass-through.d.ts.map +1 -0
  12. package/dist/litellm/index.d.ts +13 -0
  13. package/dist/litellm/index.d.ts.map +1 -0
  14. package/dist/litellm/index.js +270 -0
  15. package/dist/litellm/index.js.map +14 -0
  16. package/dist/litellm/pass-through-endpoint.d.ts +94 -0
  17. package/dist/litellm/pass-through-endpoint.d.ts.map +1 -0
  18. package/dist/litellm/pass-through-form.d.ts +40 -0
  19. package/dist/litellm/pass-through-form.d.ts.map +1 -0
  20. package/dist/litellm/providers.d.ts +16 -0
  21. package/dist/litellm/providers.d.ts.map +1 -0
  22. package/dist/paperless/client.d.ts +37 -0
  23. package/dist/paperless/client.d.ts.map +1 -0
  24. package/dist/paperless/constraint-guard.d.ts +19 -0
  25. package/dist/paperless/constraint-guard.d.ts.map +1 -0
  26. package/dist/paperless/constraints.d.ts +50 -0
  27. package/dist/paperless/constraints.d.ts.map +1 -0
  28. package/dist/paperless/credentials.d.ts +40 -0
  29. package/dist/paperless/credentials.d.ts.map +1 -0
  30. package/dist/paperless/custom-field.d.ts +75 -0
  31. package/dist/paperless/custom-field.d.ts.map +1 -0
  32. package/dist/paperless/document-type.d.ts +30 -0
  33. package/dist/paperless/document-type.d.ts.map +1 -0
  34. package/dist/paperless/errors.d.ts +49 -0
  35. package/dist/paperless/errors.d.ts.map +1 -0
  36. package/dist/paperless/fake-paperless.d.ts +39 -0
  37. package/dist/paperless/fake-paperless.d.ts.map +1 -0
  38. package/dist/paperless/generated/constraints/index.d.ts +13 -0
  39. package/dist/paperless/generated/constraints/index.d.ts.map +1 -0
  40. package/dist/paperless/generated/constraints/paperless-custom_fields.d.ts +21 -0
  41. package/dist/paperless/generated/constraints/paperless-custom_fields.d.ts.map +1 -0
  42. package/dist/paperless/generated/constraints/paperless-document_types.d.ts +21 -0
  43. package/dist/paperless/generated/constraints/paperless-document_types.d.ts.map +1 -0
  44. package/dist/paperless/generated/constraints/paperless-storage_paths.d.ts +21 -0
  45. package/dist/paperless/generated/constraints/paperless-storage_paths.d.ts.map +1 -0
  46. package/dist/paperless/generated/constraints/paperless-tags.d.ts +21 -0
  47. package/dist/paperless/generated/constraints/paperless-tags.d.ts.map +1 -0
  48. package/dist/paperless/generated/types/custom-fields.d.ts +23 -0
  49. package/dist/paperless/generated/types/custom-fields.d.ts.map +1 -0
  50. package/dist/paperless/generated/types/document-types.d.ts +39 -0
  51. package/dist/paperless/generated/types/document-types.d.ts.map +1 -0
  52. package/dist/paperless/generated/types/index.d.ts +10 -0
  53. package/dist/paperless/generated/types/index.d.ts.map +1 -0
  54. package/dist/paperless/generated/types/storage-paths.d.ts +31 -0
  55. package/dist/paperless/generated/types/storage-paths.d.ts.map +1 -0
  56. package/dist/paperless/generated/types/tags.d.ts +37 -0
  57. package/dist/paperless/generated/types/tags.d.ts.map +1 -0
  58. package/dist/paperless/index.d.ts +19 -0
  59. package/dist/paperless/index.d.ts.map +1 -0
  60. package/dist/paperless/index.js +524 -0
  61. package/dist/paperless/index.js.map +26 -0
  62. package/dist/paperless/matching-locate.d.ts +33 -0
  63. package/dist/paperless/matching-locate.d.ts.map +1 -0
  64. package/dist/paperless/matching-types.d.ts +60 -0
  65. package/dist/paperless/matching-types.d.ts.map +1 -0
  66. package/dist/paperless/matching.d.ts +61 -0
  67. package/dist/paperless/matching.d.ts.map +1 -0
  68. package/dist/paperless/providers.d.ts +15 -0
  69. package/dist/paperless/providers.d.ts.map +1 -0
  70. package/dist/paperless/storage-path.d.ts +32 -0
  71. package/dist/paperless/storage-path.d.ts.map +1 -0
  72. package/dist/paperless/tag.d.ts +47 -0
  73. package/dist/paperless/tag.d.ts.map +1 -0
  74. package/dist/postgres/connection.d.ts +50 -0
  75. package/dist/postgres/connection.d.ts.map +1 -0
  76. package/dist/postgres/database-attrs.d.ts +79 -0
  77. package/dist/postgres/database-attrs.d.ts.map +1 -0
  78. package/dist/postgres/database-sql.d.ts +73 -0
  79. package/dist/postgres/database-sql.d.ts.map +1 -0
  80. package/dist/postgres/database.d.ts +63 -0
  81. package/dist/postgres/database.d.ts.map +1 -0
  82. package/dist/postgres/errors.d.ts +86 -0
  83. package/dist/postgres/errors.d.ts.map +1 -0
  84. package/dist/postgres/fake-sql.d.ts +22 -0
  85. package/dist/postgres/fake-sql.d.ts.map +1 -0
  86. package/dist/postgres/index.d.ts +16 -0
  87. package/dist/postgres/index.d.ts.map +1 -0
  88. package/dist/postgres/index.js +238 -0
  89. package/dist/postgres/index.js.map +15 -0
  90. package/dist/postgres/providers.d.ts +14 -0
  91. package/dist/postgres/providers.d.ts.map +1 -0
  92. package/dist/proxmox/index.js.map +2 -2
  93. package/dist/proxmox/zfs-pool.d.ts +10 -2
  94. package/dist/proxmox/zfs-pool.d.ts.map +1 -1
  95. package/dist/release/archive.d.ts +6 -2
  96. package/dist/release/archive.d.ts.map +1 -1
  97. package/dist/release/binary-form.d.ts +14 -9
  98. package/dist/release/binary-form.d.ts.map +1 -1
  99. package/dist/release/binary-pins.d.ts +17 -0
  100. package/dist/release/binary-pins.d.ts.map +1 -0
  101. package/dist/release/catalog.d.ts.map +1 -1
  102. package/dist/release/index.js +90 -40
  103. package/dist/release/index.js.map +8 -7
  104. package/dist/release/tar.d.ts +9 -2
  105. package/dist/release/tar.d.ts.map +1 -1
  106. package/docs/litellm.md +110 -0
  107. package/docs/paperless.md +115 -0
  108. package/docs/peers.md +8 -1
  109. package/docs/postgres.md +125 -0
  110. package/docs/release-binary-catalogs.md +40 -4
  111. package/docs/release-binary-refusals.md +22 -8
  112. package/docs/release-binary-upstream.md +33 -15
  113. package/package.json +14 -1
  114. package/src/litellm/client.ts +195 -0
  115. package/src/litellm/credentials.ts +73 -0
  116. package/src/litellm/fake-litellm.ts +114 -0
  117. package/src/litellm/fake-stack.ts +100 -0
  118. package/src/litellm/generated/pass-through.ts +73 -0
  119. package/src/litellm/index.ts +42 -0
  120. package/src/litellm/pass-through-endpoint-delete-swallow.test.ts +66 -0
  121. package/src/litellm/pass-through-endpoint-replace.test.ts +84 -0
  122. package/src/litellm/pass-through-endpoint.test.ts +208 -0
  123. package/src/litellm/pass-through-endpoint.ts +258 -0
  124. package/src/litellm/pass-through-form.test.ts +128 -0
  125. package/src/litellm/pass-through-form.ts +136 -0
  126. package/src/litellm/providers.ts +19 -0
  127. package/src/paperless/client.ts +88 -0
  128. package/src/paperless/constraint-guard.ts +50 -0
  129. package/src/paperless/constraints.test.ts +74 -0
  130. package/src/paperless/constraints.ts +111 -0
  131. package/src/paperless/credentials.ts +69 -0
  132. package/src/paperless/custom-field.test.ts +74 -0
  133. package/src/paperless/custom-field.ts +95 -0
  134. package/src/paperless/document-type.ts +74 -0
  135. package/src/paperless/errors.ts +62 -0
  136. package/src/paperless/fake-paperless.ts +156 -0
  137. package/src/paperless/generated/constraints/index.ts +23 -0
  138. package/src/paperless/generated/constraints/paperless-custom_fields.ts +29 -0
  139. package/src/paperless/generated/constraints/paperless-document_types.ts +32 -0
  140. package/src/paperless/generated/constraints/paperless-storage_paths.ts +34 -0
  141. package/src/paperless/generated/constraints/paperless-tags.ts +34 -0
  142. package/src/paperless/generated/types/custom-fields.ts +23 -0
  143. package/src/paperless/generated/types/document-types.ts +30 -0
  144. package/src/paperless/generated/types/index.ts +9 -0
  145. package/src/paperless/generated/types/storage-paths.ts +31 -0
  146. package/src/paperless/generated/types/tags.ts +37 -0
  147. package/src/paperless/index.ts +56 -0
  148. package/src/paperless/matching-identity.test.ts +194 -0
  149. package/src/paperless/matching-locate.ts +104 -0
  150. package/src/paperless/matching-types.ts +61 -0
  151. package/src/paperless/matching.test.ts +128 -0
  152. package/src/paperless/matching.ts +239 -0
  153. package/src/paperless/providers.ts +27 -0
  154. package/src/paperless/storage-path.ts +78 -0
  155. package/src/paperless/tag.ts +91 -0
  156. package/src/postgres/connection.ts +62 -0
  157. package/src/postgres/database-attrs.ts +87 -0
  158. package/src/postgres/database-bigint-serialization.test.ts +140 -0
  159. package/src/postgres/database-lifecycle.test.ts +113 -0
  160. package/src/postgres/database-provenance.test.ts +118 -0
  161. package/src/postgres/database-refusals.test.ts +135 -0
  162. package/src/postgres/database-sql.ts +174 -0
  163. package/src/postgres/database.ts +193 -0
  164. package/src/postgres/errors.ts +131 -0
  165. package/src/postgres/fake-sql.ts +123 -0
  166. package/src/postgres/fixtures/pg-REL_18_6-create-database-synopsis.txt +26 -0
  167. package/src/postgres/fixtures/pg-REL_18_6-pg_database.h.txt +67 -0
  168. package/src/postgres/index.ts +30 -0
  169. package/src/postgres/providers.ts +16 -0
  170. package/src/proxmox/zfs-pool.ts +10 -2
  171. package/src/release/archive.ts +10 -4
  172. package/src/release/binary-form.ts +24 -76
  173. package/src/release/binary-pins.ts +95 -0
  174. package/src/release/binary-root.test.ts +122 -0
  175. package/src/release/catalog.ts +10 -2
  176. package/src/release/tar-root.test.ts +114 -0
  177. 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,37 @@
1
+ /**
2
+ * One authenticated call to the Paperless-ngx REST API at `/api`.
3
+ *
4
+ * ⚠️ `Authorization: Token <value>`, NOT `Bearer`. Paperless-ngx's `securitySchemes.tokenAuth`
5
+ * (the v3.1.1 served schema) states the "Token" prefix explicitly — the same DRF
6
+ * `TokenAuthentication` NetBox uses (netbox/client.ts), a different vendor, same convention.
7
+ *
8
+ * ⛔ `Accept: application/json; version=10` ON EVERY CALL. Paperless-ngx versions its API with
9
+ * DRF's `AcceptHeaderVersioning` (`REST_FRAMEWORK.DEFAULT_VERSION = '10'`, measured against the
10
+ * v3.1.1 source). Omitting the header still answers — DRF falls back to `DEFAULT_VERSION` — but
11
+ * pinning it here means an estate upgrade that moves the default cannot silently change what
12
+ * this package receives without a version bump of its own noticing.
13
+ *
14
+ * ⚠️ A LIST IS WRAPPED, A DETAIL IS NOT: `{count, next, previous, results: […]}` vs. the bare
15
+ * object — the same DRF pagination shape NetBox uses.
16
+ */
17
+ import * as Effect from 'effect/Effect';
18
+ import * as HttpClient from 'effect/unstable/http/HttpClient';
19
+ import { PaperlessCredentials, type PaperlessCredentialsError } from './credentials.ts';
20
+ import { type PaperlessError } from './errors.ts';
21
+ export interface PaperlessList<T> {
22
+ readonly count: number;
23
+ readonly next: string | null;
24
+ readonly previous: string | null;
25
+ readonly results: readonly T[];
26
+ }
27
+ export type PaperlessRow = Record<string, unknown>;
28
+ /**
29
+ * ⚠️ `PaperlessCredentials` IS PART OF THIS, NOT JUST `HttpClient`. `paperless()` reads the lazy
30
+ * credentials service (`yield* yield* PaperlessCredentials`), so a stack that wires the
31
+ * providers without also providing a credentials layer should fail to TYPECHECK, not fail at
32
+ * the first call. `providers.ts` supplies `environmentLayer` by default for exactly this.
33
+ */
34
+ export type PaperlessRequirements = HttpClient.HttpClient | PaperlessCredentials;
35
+ /** One JSON call. Returns the parsed body, or `undefined` for an empty response (DELETE). */
36
+ export declare const paperless: <T>(method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE', path: string, body?: Record<string, unknown>) => Effect.Effect<T, PaperlessError | PaperlessCredentialsError, PaperlessRequirements>;
37
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/paperless/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,UAAU,MAAM,iCAAiC,CAAC;AAE9D,OAAO,EAAE,oBAAoB,EAAE,KAAK,yBAAyB,EAAE,MAAM,kBAAkB,CAAC;AACxF,OAAO,EAAE,KAAK,cAAc,EAAuC,MAAM,aAAa,CAAC;AAKvF,MAAM,WAAW,aAAa,CAAC,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC,EAAE,CAAC;CAChC;AAED,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEnD;;;;;GAKG;AACH,MAAM,MAAM,qBAAqB,GAAG,UAAU,CAAC,UAAU,GAAG,oBAAoB,CAAC;AAEjF,6FAA6F;AAC7F,eAAO,MAAM,SAAS,GAAI,CAAC,UACjB,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,QAC7C,MAAM,SACL,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC7B,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,cAAc,GAAG,yBAAyB,EAAE,qBAAqB,CAwCjF,CAAC"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * The generated tables, looked up by endpoint, and the refusal that stops a plan.
3
+ *
4
+ * ⛔ AN UNKNOWN KEY IS A DEFECT, NOT A REASON TO SKIP THE CHECK — the generator resolves every key
5
+ * in its source scan against the vendor document, so a key absent here means the tables were
6
+ * not regenerated, which the message says. Same posture as `../netbox/constraint-guard.ts`.
7
+ */
8
+ import * as Effect from 'effect/Effect';
9
+ import { type EndpointConstraints, type EndpointKey, type PaperlessBody } from './constraints.ts';
10
+ import { PAPERLESS_CONSTRAINTS } from './generated/constraints/index.ts';
11
+ export { PAPERLESS_CONSTRAINTS };
12
+ export declare const constraintsFor: (key: EndpointKey) => EndpointConstraints;
13
+ export declare const bodyViolations: (key: EndpointKey, body: PaperlessBody, presence: boolean) => readonly string[];
14
+ /**
15
+ * ⛔ `Effect.die`, NOT `Effect.fail`. A declaration the vendor would reject is a DEFECT in the
16
+ * stack file, not a recoverable condition.
17
+ */
18
+ export declare const guardBody: (key: EndpointKey | undefined, body: PaperlessBody, presence: boolean) => Effect.Effect<void>;
19
+ //# sourceMappingURL=constraint-guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constraint-guard.d.ts","sourceRoot":"","sources":["../../src/paperless/constraint-guard.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,WAAW,EAChB,KAAK,aAAa,EAGnB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC;AAEzE,OAAO,EAAE,qBAAqB,EAAE,CAAC;AAEjC,eAAO,MAAM,cAAc,QAAS,WAAW,KAAG,mBASjD,CAAC;AAEF,eAAO,MAAM,cAAc,QACpB,WAAW,QACV,aAAa,YACT,OAAO,KAChB,SAAS,MAAM,EAAyD,CAAC;AAE5E;;;GAGG;AACH,eAAO,MAAM,SAAS,QACf,WAAW,GAAG,SAAS,QACtB,aAAa,YACT,OAAO,KAChB,MAAM,CAAC,MAAM,CAAC,IAAI,CAKjB,CAAC"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Paperless-ngx's own rules, checked here, before the request is built.
3
+ *
4
+ * ★ THE SAME READER SHAPE AS `../netbox/constraints.ts`, AND FOR THE SAME REASON: both vendors
5
+ * send a JSON body where a number stays a number and a boolean stays a boolean, so `String(v)`
6
+ * at the door would be the exact mangling that incident (codegen/README.md) exists to prevent.
7
+ * The table itself is generated by `bun codegen/paperless.ts` from the vendor's own OpenAPI
8
+ * document — nothing here is hand-typed.
9
+ *
10
+ * ⚠️ WHAT THIS DELIBERATELY DOES NOT ENFORCE: a `format` name (a DRF validator run server-side); a
11
+ * pattern the generator could not carry faithfully (codegen/py-pattern.ts), recorded verbatim as
12
+ * `patternSource`; the `(name, owner)` uniqueness constraint (models.py) — that is `matching.ts`'s
13
+ * job, not a per-field rule; and `CustomField.select_options`, which is not in the schema at all
14
+ * (groundTruth: the serializer's own rule is unverified at 3.1.1 and this table cannot state a
15
+ * rule the vendor document does not carry).
16
+ */
17
+ export interface ParamConstraint {
18
+ readonly type?: string;
19
+ /** From the schema's `required` array. ⚠️ Checked on CREATE only. */
20
+ readonly required?: boolean;
21
+ readonly maxLength?: number;
22
+ readonly minLength?: number;
23
+ readonly minimum?: number;
24
+ readonly maximum?: number;
25
+ /** ⚠️ `(string | number)[]` — `matching_algorithm`'s enum is numeric (`DataTypeEnum`'s is not). */
26
+ readonly enum?: readonly (string | number)[];
27
+ readonly pattern?: string;
28
+ readonly patternSource?: string;
29
+ readonly format?: string;
30
+ readonly default?: string;
31
+ }
32
+ export type EndpointConstraints = Readonly<Record<string, ParamConstraint>>;
33
+ /** `paperless:POST /api/tags/` — exactly how the generated tables key an endpoint. */
34
+ export type EndpointKey = string;
35
+ export type PaperlessBody = Record<string, unknown>;
36
+ export interface ViolationOptions {
37
+ /** ⛔ CREATE ONLY. A PATCH is deliberately partial, so presence there refuses every ordinary edit. */
38
+ readonly presence?: boolean;
39
+ }
40
+ /**
41
+ * Every vendor rule this body breaks. Pure — no network, no clock, no environment.
42
+ *
43
+ * ⚠️ `null` IS A VALUE, NOT AN ABSENCE — Paperless uses explicit `null` to clear a nullable
44
+ * foreign key (`owner`, `parent`), so it must not trip the presence check, and no string/number
45
+ * rule applies to it.
46
+ */
47
+ export declare const violations: (table: EndpointConstraints, body: PaperlessBody, options?: ViolationOptions) => readonly string[];
48
+ /** The message a plan dies with. */
49
+ export declare const refusal: (endpoint: EndpointKey, found: readonly string[]) => string;
50
+ //# sourceMappingURL=constraints.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constraints.d.ts","sourceRoot":"","sources":["../../src/paperless/constraints.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,qEAAqE;IACrE,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,mGAAmG;IACnG,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;IAC7C,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,MAAM,mBAAmB,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC,CAAC;AAE5E,sFAAsF;AACtF,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC;AAEjC,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEpD,MAAM,WAAW,gBAAgB;IAC/B,qGAAqG;IACrG,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;CAC7B;AAsCD;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,UACd,mBAAmB,QACpB,aAAa,YACV,gBAAgB,KACxB,SAAS,MAAM,EAYjB,CAAC;AAEF,oCAAoC;AACpC,eAAO,MAAM,OAAO,aAAc,WAAW,SAAS,SAAS,MAAM,EAAE,KAAG,MAGM,CAAC"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Where the Paperless URL and token come from — S24's shape (alchemy-provider-standard): a lazy
3
+ * `Context.Service` whose VALUE is itself an `Effect`, resolved inside each operation with
4
+ * `yield* yield* PaperlessCredentials`. Resolving twice, not once at layer build, is what keeps a
5
+ * token out of any state the engine might persist and lets a consumer swap the source (an env var
6
+ * today, an OpenBao mint tomorrow) without touching a resource file.
7
+ *
8
+ * ⛔ NO DEFAULT HOST, AND NO DEFAULT TOKEN. A published package that fell back to a loopback
9
+ * address would let a consumer who forgot the variable watch every call fail against a machine
10
+ * that is not theirs, instead of being told which variable is missing.
11
+ * ⛔ THE TOKEN IS NEVER A PROP, NEVER AN ATTRIBUTE. Alchemy persists attributes unencrypted
12
+ * (S25); nothing this family returns from `read`/`reconcile` may carry it.
13
+ */
14
+ import * as Context from 'effect/Context';
15
+ import * as Effect from 'effect/Effect';
16
+ import * as Layer from 'effect/Layer';
17
+ declare const PaperlessCredentialsError_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 & {
18
+ readonly _tag: "PaperlessCredentialsError";
19
+ } & Readonly<A>;
20
+ export declare class PaperlessCredentialsError extends PaperlessCredentialsError_base<{
21
+ readonly message: string;
22
+ }> {
23
+ }
24
+ export interface PaperlessCredentialValue {
25
+ /** Trailing slash stripped, e.g. `https://paperless.example.com`. */
26
+ readonly url: string;
27
+ readonly token: string;
28
+ }
29
+ declare const PaperlessCredentials_base: Context.ServiceClass<PaperlessCredentials, "PaperlessCredentials", Effect.Effect<PaperlessCredentialValue, PaperlessCredentialsError, never>>;
30
+ /**
31
+ * The lazy service. ⚠️ `Layer.succeed` registers the EFFECT, not its result — `readEnvironment`
32
+ * runs once per `yield* yield*`, so a call after the variables are exported sees them even if
33
+ * the layer itself was built before that (true in `fake-paperless.test.ts`, which sets the env
34
+ * vars right before each call).
35
+ */
36
+ export declare class PaperlessCredentials extends PaperlessCredentials_base {
37
+ }
38
+ export declare const environmentLayer: Layer.Layer<PaperlessCredentials>;
39
+ export {};
40
+ //# sourceMappingURL=credentials.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../../src/paperless/credentials.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAC;AAE1C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;;;;AAEtC,qBAAa,yBAA0B,SAAQ,+BAA8C;IAC3F,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B,CAAC;CAAG;AAEL,MAAM,WAAW,wBAAwB;IACvC,qEAAqE;IACrE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;;AA4BD;;;;;GAKG;AACH,qBAAa,oBAAqB,SAAQ,yBAGf;CAAG;AAE9B,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,KAAK,CAAC,oBAAoB,CAG9D,CAAC"}
@@ -0,0 +1,75 @@
1
+ /**
2
+ * `Paperless.CustomField` — a Paperless-ngx custom field, create-or-update and never deleted.
3
+ *
4
+ * ⛔ `dataType` IS CREATE-ONLY, ENFORCED AT PLAN TIME, NEVER A PATCH AND NEVER A REPLACE. A
5
+ * replace would delete this id and create a new one — and Paperless deletes the field's VALUE
6
+ * on every document when its custom field is deleted. `matching.ts`'s `immutable` hook carries
7
+ * this refusal generically; `dataType` is simply never listed in `fields`, so nothing in
8
+ * `matching.ts` ever builds a PATCH body that includes it.
9
+ *
10
+ * ⛔ NO `owner` FIELD. Unlike `Tag`/`DocumentType`/`StoragePath`, `CustomField` does not extend
11
+ * Paperless-ngx's `OwnedObjectSerializer` — its wire body is exactly `{name, data_type,
12
+ * extra_data}` (measured against the v3.1.1 schema). `owned: false` stops `matching.ts`
13
+ * sending a field the vendor's serializer does not declare.
14
+ *
15
+ * ⚠️ `CustomField.select_options` (the vendor's own rule for a `select` field's option list) is
16
+ * not in the served schema at all, so it is recorded here and NOT enforced (groundTruth).
17
+ */
18
+ import { Resource } from 'alchemy';
19
+ import * as Provider from 'alchemy/Provider';
20
+ import * as Effect from 'effect/Effect';
21
+ import type { PaperlessCustomFieldRequest } from './generated/types/custom-fields.ts';
22
+ import type { PaperlessRow } from './client.ts';
23
+ import { type MatchingRequirements } from './matching.ts';
24
+ export interface CustomFieldProps {
25
+ /** ⚠️ maxLength 128 (generated constraint table). The locate key — CustomField's name is globally unique (models.py:1169), with no owner to further scope it. */
26
+ name: string;
27
+ /**
28
+ * ⛔ CREATE-ONLY — see this file's header. `select`'s own option list
29
+ * (`select_options`) is not in the vendor schema and is unenforced here.
30
+ */
31
+ dataType: PaperlessCustomFieldRequest['data_type'];
32
+ /** Arbitrary JSON the vendor attaches no schema to (e.g. a `select` field's option list). */
33
+ extraData?: unknown;
34
+ }
35
+ export interface CustomFieldAttributes {
36
+ id: number;
37
+ name: string;
38
+ dataType: string;
39
+ extraData: unknown;
40
+ }
41
+ export interface CustomField extends Resource<'Paperless.CustomField', CustomFieldProps, CustomFieldAttributes, never, MatchingRequirements> {
42
+ }
43
+ export declare const CustomField: import("alchemy").ResourceClass<CustomField>;
44
+ /**
45
+ * ⛔ EXTRACTED SO IT CAN BE TESTED WITHOUT A SERVER (custom-field.test.ts) — the same reason
46
+ * `../netbox/prefix-form.ts` pulls its body/matches functions out of `prefix.ts`.
47
+ */
48
+ export declare const dataTypeImmutable: (live: PaperlessRow, props: CustomFieldProps) => string | undefined;
49
+ /** ⚠️ Exported (not through index.ts) so custom-field.test.ts can drive the real lifecycle. */
50
+ export declare const customFieldHandlers: {
51
+ list: () => Effect.Effect<never[], never, never>;
52
+ read: ({ olds }: {
53
+ olds: CustomFieldProps;
54
+ }) => Effect.Effect<CustomFieldAttributes | undefined, import("./credentials.ts").PaperlessCredentialsError | import("./errors.ts").PaperlessUnauthorized | import("./errors.ts").PaperlessUnavailable | import("./errors.ts").PaperlessValidation, import("./client.ts").PaperlessRequirements>;
55
+ diff: ({ news, output }: {
56
+ news: import("alchemy").Input<CustomFieldProps>;
57
+ output: CustomFieldAttributes | undefined;
58
+ }) => Effect.Effect<{
59
+ readonly action: 'update';
60
+ } | {
61
+ readonly action: 'noop';
62
+ } | {
63
+ readonly action: 'replace';
64
+ } | undefined, import("./credentials.ts").PaperlessCredentialsError | import("./errors.ts").PaperlessUnauthorized | import("./errors.ts").PaperlessUnavailable | import("./errors.ts").PaperlessValidation, import("./client.ts").PaperlessRequirements>;
65
+ reconcile: ({ news, output }: {
66
+ news: CustomFieldProps;
67
+ output?: CustomFieldAttributes | undefined;
68
+ }) => Effect.Effect<CustomFieldAttributes, import("./credentials.ts").PaperlessCredentialsError | import("./errors.ts").PaperlessError, import("./client.ts").PaperlessRequirements>;
69
+ delete: ({ olds, output }: {
70
+ olds: CustomFieldProps;
71
+ output?: CustomFieldAttributes | undefined;
72
+ }) => Effect.Effect<void, import("./credentials.ts").PaperlessCredentialsError | import("./errors.ts").PaperlessUnauthorized | import("./errors.ts").PaperlessUnavailable | import("./errors.ts").PaperlessValidation, import("./client.ts").PaperlessRequirements>;
73
+ };
74
+ export declare const CustomFieldProvider: () => import("effect/Layer").Layer<Provider.Provider<CustomField>, never, import("./client.ts").PaperlessRequirements>;
75
+ //# sourceMappingURL=custom-field.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom-field.d.ts","sourceRoot":"","sources":["../../src/paperless/custom-field.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,oCAAoC,CAAC;AACtF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,KAAK,oBAAoB,EAAoB,MAAM,eAAe,CAAC;AAE5E,MAAM,WAAW,gBAAgB;IAC/B,iKAAiK;IACjK,IAAI,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,QAAQ,EAAE,2BAA2B,CAAC,WAAW,CAAC,CAAC;IACnD,6FAA6F;IAC7F,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,WAAY,SAAQ,QAAQ,CAC3C,uBAAuB,EACvB,gBAAgB,EAChB,qBAAqB,EACrB,KAAK,EACL,oBAAoB,CACrB;CAAG;AAEJ,eAAO,MAAM,WAAW,8CAEtB,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,iBAAiB,SACtB,YAAY,SACX,gBAAgB,KACtB,MAAM,GAAG,SAKG,CAAC;AAEhB,+FAA+F;AAC/F,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;CAqB9B,CAAC;AAEH,eAAO,MAAM,mBAAmB,wHAC4D,CAAC"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * `Paperless.DocumentType` — a Paperless-ngx document type, create-or-update and never deleted.
3
+ * Same shape as `tag.ts` minus `color`/`isInboxTag`/`parent` — see that file's header for why
4
+ * `matchingAlgorithm`'s type comes off the generated request type rather than being retyped.
5
+ */
6
+ import { Resource } from 'alchemy';
7
+ import * as Provider from 'alchemy/Provider';
8
+ import type { PaperlessDocumentTypeRequest } from './generated/types/document-types.ts';
9
+ import { type MatchingProps, type MatchingRequirements } from './matching.ts';
10
+ export interface DocumentTypeProps extends MatchingProps {
11
+ /** ⚠️ maxLength 128 (generated constraint table). The locate key. */
12
+ name: string;
13
+ /** ⚠️ maxLength 256. */
14
+ match?: string;
15
+ matchingAlgorithm?: PaperlessDocumentTypeRequest['matching_algorithm'];
16
+ isInsensitive?: boolean;
17
+ }
18
+ export interface DocumentTypeAttributes {
19
+ id: number;
20
+ name: string;
21
+ match: string;
22
+ matchingAlgorithm: number;
23
+ isInsensitive: boolean;
24
+ owner: number | undefined;
25
+ }
26
+ export interface DocumentType extends Resource<'Paperless.DocumentType', DocumentTypeProps, DocumentTypeAttributes, never, MatchingRequirements> {
27
+ }
28
+ export declare const DocumentType: import("alchemy").ResourceClass<DocumentType>;
29
+ export declare const DocumentTypeProvider: () => import("effect/Layer").Layer<Provider.Provider<DocumentType>, never, import("./client.ts").PaperlessRequirements>;
30
+ //# sourceMappingURL=document-type.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document-type.d.ts","sourceRoot":"","sources":["../../src/paperless/document-type.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAE7C,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,qCAAqC,CAAC;AACxF,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,oBAAoB,EAAoB,MAAM,eAAe,CAAC;AAEhG,MAAM,WAAW,iBAAkB,SAAQ,aAAa;IACtD,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC;IACb,wBAAwB;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iBAAiB,CAAC,EAAE,4BAA4B,CAAC,oBAAoB,CAAC,CAAC;IACvE,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,sBAAsB;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,iBAAiB,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,OAAO,CAAC;IACvB,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3B;AAED,MAAM,WAAW,YAAa,SAAQ,QAAQ,CAC5C,wBAAwB,EACxB,iBAAiB,EACjB,sBAAsB,EACtB,KAAK,EACL,oBAAoB,CACrB;CAAG;AAEJ,eAAO,MAAM,YAAY,+CAEvB,CAAC;AAiCH,eAAO,MAAM,oBAAoB,yHACkD,CAAC"}
@@ -0,0 +1,49 @@
1
+ declare const PaperlessNotFound_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 & {
2
+ readonly _tag: "PaperlessNotFound";
3
+ } & Readonly<A>;
4
+ /** No object at that path. The only tag `matching.ts` folds to "absent". */
5
+ export declare class PaperlessNotFound extends PaperlessNotFound_base<{
6
+ readonly method: string;
7
+ readonly path: string;
8
+ }> {
9
+ }
10
+ declare const PaperlessUnauthorized_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 & {
11
+ readonly _tag: "PaperlessUnauthorized";
12
+ } & Readonly<A>;
13
+ /** 401 (no/bad token) or 403 (token valid, forbidden) — NEVER read as "the object is absent". */
14
+ export declare class PaperlessUnauthorized extends PaperlessUnauthorized_base<{
15
+ readonly status: 401 | 403;
16
+ readonly method: string;
17
+ readonly path: string;
18
+ }> {
19
+ }
20
+ declare const PaperlessValidation_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 & {
21
+ readonly _tag: "PaperlessValidation";
22
+ } & Readonly<A>;
23
+ /** 400 — the vendor rejected the body. Carries it, so the message quotes Paperless's own words. */
24
+ export declare class PaperlessValidation extends PaperlessValidation_base<{
25
+ readonly method: string;
26
+ readonly path: string;
27
+ readonly body: string;
28
+ }> {
29
+ }
30
+ declare const PaperlessUnavailable_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 & {
31
+ readonly _tag: "PaperlessUnavailable";
32
+ } & Readonly<A>;
33
+ /** 5xx, or a status this family's endpoints do not otherwise document. */
34
+ export declare class PaperlessUnavailable extends PaperlessUnavailable_base<{
35
+ readonly status: number;
36
+ readonly method: string;
37
+ readonly path: string;
38
+ readonly detail: string;
39
+ }> {
40
+ }
41
+ export type PaperlessError = PaperlessNotFound | PaperlessUnauthorized | PaperlessValidation | PaperlessUnavailable;
42
+ /**
43
+ * The one place a status code is read. ⚠️ Anything outside {400,401,403,404,5xx} — 405, 409, 429
44
+ * — is not documented for these four endpoints, so it falls to `PaperlessValidation` rather than
45
+ * inventing a fifth tag for a case that was never measured.
46
+ */
47
+ export declare const statusToError: (status: number, method: string, path: string, body: string) => PaperlessError;
48
+ export {};
49
+ //# sourceMappingURL=errors.d.ts.map