@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,26 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/paperless/credentials.ts", "../src/paperless/errors.ts", "../src/paperless/constraint-guard.ts", "../src/paperless/constraints.ts", "../src/paperless/generated/constraints/paperless-custom_fields.ts", "../src/paperless/generated/constraints/paperless-document_types.ts", "../src/paperless/generated/constraints/paperless-storage_paths.ts", "../src/paperless/generated/constraints/paperless-tags.ts", "../src/paperless/generated/constraints/index.ts", "../src/paperless/tag.ts", "../src/paperless/matching.ts", "../src/paperless/client.ts", "../src/paperless/matching-locate.ts", "../src/paperless/document-type.ts", "../src/paperless/storage-path.ts", "../src/paperless/custom-field.ts", "../src/paperless/providers.ts"],
4
+ "sourcesContent": [
5
+ "/**\n * Where the Paperless URL and token come from — S24's shape (alchemy-provider-standard): a lazy\n * `Context.Service` whose VALUE is itself an `Effect`, resolved inside each operation with\n * `yield* yield* PaperlessCredentials`. Resolving twice, not once at layer build, is what keeps a\n * token out of any state the engine might persist and lets a consumer swap the source (an env var\n * today, an OpenBao mint tomorrow) without touching a resource file.\n *\n * ⛔ NO DEFAULT HOST, AND NO DEFAULT TOKEN. A published package that fell back to a loopback\n * address would let a consumer who forgot the variable watch every call fail against a machine\n * that is not theirs, instead of being told which variable is missing.\n * ⛔ THE TOKEN IS NEVER A PROP, NEVER AN ATTRIBUTE. Alchemy persists attributes unencrypted\n * (S25); nothing this family returns from `read`/`reconcile` may carry it.\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 class PaperlessCredentialsError extends Data.TaggedError('PaperlessCredentialsError')<{\n readonly message: string;\n}> {}\n\nexport interface PaperlessCredentialValue {\n /** Trailing slash stripped, e.g. `https://paperless.example.com`. */\n readonly url: string;\n readonly token: string;\n}\n\n/**\n * ★ `readEnvironment` NAMES EVERY VARIABLE IT CONSUMES (S24) — `PAPERLESS_URL`, `PAPERLESS_TOKEN`\n * — so a consumer's `alchemy provider check-env` (or an equivalent preflight) can list them.\n */\nconst readEnvironment: Effect.Effect<PaperlessCredentialValue, PaperlessCredentialsError> =\n Effect.suspend(() => {\n const url = process.env['PAPERLESS_URL'];\n const token = process.env['PAPERLESS_TOKEN'];\n if (url === undefined || url.trim() === '') {\n return Effect.fail(\n new PaperlessCredentialsError({\n message: 'PAPERLESS_URL is not set. Point it at your Paperless-ngx instance.',\n }),\n );\n }\n if (token === undefined || token.trim() === '') {\n return Effect.fail(\n new PaperlessCredentialsError({\n message:\n 'PAPERLESS_TOKEN is not set. Mint a scoped token and export it — never store it in Alchemy props.',\n }),\n );\n }\n return Effect.succeed({ token: token.trim(), url: url.trim().replace(/\\/$/, '') });\n });\n\n/**\n * The lazy service. ⚠️ `Layer.succeed` registers the EFFECT, not its result — `readEnvironment`\n * runs once per `yield* yield*`, so a call after the variables are exported sees them even if\n * the layer itself was built before that (true in `fake-paperless.test.ts`, which sets the env\n * vars right before each call).\n */\nexport class PaperlessCredentials extends Context.Service<\n PaperlessCredentials,\n Effect.Effect<PaperlessCredentialValue, PaperlessCredentialsError>\n>()('PaperlessCredentials') {}\n\nexport const environmentLayer: Layer.Layer<PaperlessCredentials> = Layer.succeed(\n PaperlessCredentials,\n readEnvironment,\n);\n",
6
+ "/**\n * Paperless-ngx's HTTP status codes, mapped to tagged errors exactly once — here, and never\n * again by a status check inside a resource (S21, alchemy-provider-standard).\n *\n * ⛔ A SINGLE `PaperlessError` CLASS WITH A `.status` FIELD WOULD WORK, AND IS WHAT `netbox/\n * client.ts` DOES. This family does it the S21 way instead: one tag per distinct MEANING, so a\n * caller writes `Effect.catchTag('PaperlessNotFound', …)` rather than `if (e.status === 404)`.\n * `matching.ts`'s \"a 403 is never read as absent\" rule is exactly what a shared tag buys —\n * `catchTag('PaperlessNotFound', …)` cannot accidentally also catch `PaperlessUnauthorized`.\n */\nimport * as Data from 'effect/Data';\n\n/** No object at that path. The only tag `matching.ts` folds to \"absent\". */\nexport class PaperlessNotFound extends Data.TaggedError('PaperlessNotFound')<{\n readonly method: string;\n readonly path: string;\n}> {}\n\n/** 401 (no/bad token) or 403 (token valid, forbidden) — NEVER read as \"the object is absent\". */\nexport class PaperlessUnauthorized extends Data.TaggedError('PaperlessUnauthorized')<{\n readonly status: 401 | 403;\n readonly method: string;\n readonly path: string;\n}> {}\n\n/** 400 — the vendor rejected the body. Carries it, so the message quotes Paperless's own words. */\nexport class PaperlessValidation extends Data.TaggedError('PaperlessValidation')<{\n readonly method: string;\n readonly path: string;\n readonly body: string;\n}> {}\n\n/** 5xx, or a status this family's endpoints do not otherwise document. */\nexport class PaperlessUnavailable extends Data.TaggedError('PaperlessUnavailable')<{\n readonly status: number;\n readonly method: string;\n readonly path: string;\n readonly detail: string;\n}> {}\n\nexport type PaperlessError =\n | PaperlessNotFound\n | PaperlessUnauthorized\n | PaperlessValidation\n | PaperlessUnavailable;\n\n/**\n * The one place a status code is read. ⚠️ Anything outside {400,401,403,404,5xx} — 405, 409, 429\n * — is not documented for these four endpoints, so it falls to `PaperlessValidation` rather than\n * inventing a fifth tag for a case that was never measured.\n */\nexport const statusToError = (\n status: number,\n method: string,\n path: string,\n body: string,\n): PaperlessError => {\n if (status === 404) return new PaperlessNotFound({ method, path });\n if (status === 401 || status === 403) return new PaperlessUnauthorized({ method, path, status });\n if (status >= 500) return new PaperlessUnavailable({ detail: body, method, path, status });\n return new PaperlessValidation({ body, method, path });\n};\n",
7
+ "/**\n * The generated tables, looked up by endpoint, and the refusal that stops a plan.\n *\n * ⛔ AN UNKNOWN KEY IS A DEFECT, NOT A REASON TO SKIP THE CHECK — the generator resolves every key\n * in its source scan against the vendor document, so a key absent here means the tables were\n * not regenerated, which the message says. Same posture as `../netbox/constraint-guard.ts`.\n */\nimport * as Effect from 'effect/Effect';\nimport {\n type EndpointConstraints,\n type EndpointKey,\n type PaperlessBody,\n refusal,\n violations,\n} from './constraints.ts';\nimport { PAPERLESS_CONSTRAINTS } from './generated/constraints/index.ts';\n\nexport { PAPERLESS_CONSTRAINTS };\n\nexport const constraintsFor = (key: EndpointKey): EndpointConstraints => {\n const table = PAPERLESS_CONSTRAINTS[key];\n if (table === undefined) {\n throw new Error(\n `no vendor constraint table for '${key}'. It is named in this package but not generated: ` +\n 'run `bun codegen/paperless.ts` (see codegen/README.md).',\n );\n }\n return table;\n};\n\nexport const bodyViolations = (\n key: EndpointKey,\n body: PaperlessBody,\n presence: boolean,\n): readonly string[] => violations(constraintsFor(key), body, { presence });\n\n/**\n * ⛔ `Effect.die`, NOT `Effect.fail`. A declaration the vendor would reject is a DEFECT in the\n * stack file, not a recoverable condition.\n */\nexport const guardBody = (\n key: EndpointKey | undefined,\n body: PaperlessBody,\n presence: boolean,\n): Effect.Effect<void> =>\n Effect.suspend(() => {\n if (key === undefined) return Effect.void;\n const found = bodyViolations(key, body, presence);\n return found.length === 0 ? Effect.void : Effect.die(new Error(refusal(key, found)));\n });\n",
8
+ "/**\n * Paperless-ngx's own rules, checked here, before the request is built.\n *\n * ★ THE SAME READER SHAPE AS `../netbox/constraints.ts`, AND FOR THE SAME REASON: both vendors\n * send a JSON body where a number stays a number and a boolean stays a boolean, so `String(v)`\n * at the door would be the exact mangling that incident (codegen/README.md) exists to prevent.\n * The table itself is generated by `bun codegen/paperless.ts` from the vendor's own OpenAPI\n * document — nothing here is hand-typed.\n *\n * ⚠️ WHAT THIS DELIBERATELY DOES NOT ENFORCE: a `format` name (a DRF validator run server-side); a\n * pattern the generator could not carry faithfully (codegen/py-pattern.ts), recorded verbatim as\n * `patternSource`; the `(name, owner)` uniqueness constraint (models.py) — that is `matching.ts`'s\n * job, not a per-field rule; and `CustomField.select_options`, which is not in the schema at all\n * (groundTruth: the serializer's own rule is unverified at 3.1.1 and this table cannot state a\n * rule the vendor document does not carry).\n */\n\nexport interface ParamConstraint {\n readonly type?: string;\n /** From the schema's `required` array. ⚠️ Checked on CREATE only. */\n readonly required?: boolean;\n readonly maxLength?: number;\n readonly minLength?: number;\n readonly minimum?: number;\n readonly maximum?: number;\n /** ⚠️ `(string | number)[]` — `matching_algorithm`'s enum is numeric (`DataTypeEnum`'s is not). */\n readonly enum?: readonly (string | number)[];\n readonly pattern?: string;\n readonly patternSource?: string;\n readonly format?: string;\n readonly default?: string;\n}\n\nexport type EndpointConstraints = Readonly<Record<string, ParamConstraint>>;\n\n/** `paperless:POST /api/tags/` — exactly how the generated tables key an endpoint. */\nexport type EndpointKey = string;\n\nexport type PaperlessBody = Record<string, unknown>;\n\nexport interface ViolationOptions {\n /** ⛔ CREATE ONLY. A PATCH is deliberately partial, so presence there refuses every ordinary edit. */\n readonly presence?: boolean;\n}\n\n/** ⚠️ `[...value].length`, NOT `value.length` — Django counts characters, JavaScript UTF-16 units. */\nconst lengthOf = (value: string): number => [...value].length;\n\nconst checkString = (name: string, value: string, rule: ParamConstraint): string[] => {\n const out: string[] = [];\n if (rule.maxLength !== undefined && lengthOf(value) > rule.maxLength) {\n out.push(`${name}: at most ${String(rule.maxLength)} characters`);\n }\n if (rule.minLength !== undefined && lengthOf(value) < rule.minLength) {\n out.push(`${name}: at least ${String(rule.minLength)} characters`);\n }\n if (rule.enum !== undefined && !rule.enum.includes(value)) {\n out.push(`${name}: must be one of ${rule.enum.join(', ')}`);\n }\n if (rule.pattern !== undefined && !new RegExp(rule.pattern).test(value)) {\n out.push(`${name}: must match ${rule.patternSource ?? rule.pattern}`);\n }\n return out;\n};\n\nconst checkNumber = (name: string, value: number, rule: ParamConstraint): string[] => {\n const out: string[] = [];\n if (!Number.isFinite(value)) return out;\n if (rule.minimum !== undefined && value < rule.minimum)\n out.push(`${name}: at least ${String(rule.minimum)}`);\n if (rule.maximum !== undefined && value > rule.maximum)\n out.push(`${name}: at most ${String(rule.maximum)}`);\n return out;\n};\n\nconst checkValue = (name: string, value: unknown, rule: ParamConstraint): readonly string[] => {\n if (typeof value === 'string') return checkString(name, value, rule);\n if (typeof value === 'number') return checkNumber(name, value, rule);\n return [];\n};\n\n/**\n * Every vendor rule this body breaks. Pure — no network, no clock, no environment.\n *\n * ⚠️ `null` IS A VALUE, NOT AN ABSENCE — Paperless uses explicit `null` to clear a nullable\n * foreign key (`owner`, `parent`), so it must not trip the presence check, and no string/number\n * rule applies to it.\n */\nexport const violations = (\n table: EndpointConstraints,\n body: PaperlessBody,\n options: ViolationOptions = {},\n): readonly string[] => {\n const out: string[] = [];\n for (const [name, rule] of Object.entries(table)) {\n if (!Object.hasOwn(body, name)) {\n if (options.presence === true && rule.required === true) out.push(`${name}: required`);\n continue;\n }\n const value = body[name];\n for (const item of Array.isArray(value) ? value : [value])\n out.push(...checkValue(name, item, rule));\n }\n return out;\n};\n\n/** The message a plan dies with. */\nexport const refusal = (endpoint: EndpointKey, found: readonly string[]): string =>\n `${endpoint} would be rejected by the vendor:\\n - ${found.join('\\n - ')}\\n` +\n \"These are Paperless-ngx's own limits, read from its served OpenAPI document \" +\n '(packages/alchemy/src/paperless/generated/constraints). Fix the declaration.';\n",
9
+ "/**\n * Generated Paperless-ngx REST API (OpenAPI 3.0.3) request-body constraints for `/api/custom_fields/` — DO NOT EDIT BY HAND.\n *\n * Run: bun codegen/paperless.ts\n * Manifest entry: `paperless-openapi` — Paperless-ngx REST API (OpenAPI 3.0.3) 3.1.1\n * sha256 d0fe550d2135e37b, Paperless-ngx web service, loopback\n * /api/schema/?format=json\n *\n * 8 of this product's 100 POST/PUT/PATCH/DELETE endpoints are tabled across all\n * areas: the ones this package writes to, named in its own source. Every other vendor write\n * endpoint is UNTABLED and therefore unchecked at plan time.\n *\n * ⚠️ A `patternSource` with no `pattern` beside it could NOT be carried into a JavaScript\n * RegExp faithfully — Django/DRF's `\\w` is Unicode and JavaScript's is ASCII\n * (codegen/py-pattern.ts). It is recorded and NOT enforced.\n * ⚠️ `data_type` on a custom field is CREATE-ONLY: enforced here as a value rule, never as a\n * reason to allow a PATCH — the refusal on change lives in the provider (custom-field.ts).\n */\nimport type { EndpointConstraints } from '../../constraints.ts';\n\nexport const PAPERLESS_CUSTOM_FIELDS_CONSTRAINTS: Readonly<Record<string, EndpointConstraints>> = {\n \"paperless:PATCH /api/custom_fields/{id}/\": {\n \"name\": {\"maxLength\":128,\"minLength\":1,\"type\":\"string\"},\n },\n \"paperless:POST /api/custom_fields/\": {\n \"data_type\": {\"required\":true},\n \"name\": {\"maxLength\":128,\"minLength\":1,\"required\":true,\"type\":\"string\"},\n },\n};\n",
10
+ "/**\n * Generated Paperless-ngx REST API (OpenAPI 3.0.3) request-body constraints for `/api/document_types/` — DO NOT EDIT BY HAND.\n *\n * Run: bun codegen/paperless.ts\n * Manifest entry: `paperless-openapi` — Paperless-ngx REST API (OpenAPI 3.0.3) 3.1.1\n * sha256 d0fe550d2135e37b, Paperless-ngx web service, loopback\n * /api/schema/?format=json\n *\n * 8 of this product's 100 POST/PUT/PATCH/DELETE endpoints are tabled across all\n * areas: the ones this package writes to, named in its own source. Every other vendor write\n * endpoint is UNTABLED and therefore unchecked at plan time.\n *\n * ⚠️ A `patternSource` with no `pattern` beside it could NOT be carried into a JavaScript\n * RegExp faithfully — Django/DRF's `\\w` is Unicode and JavaScript's is ASCII\n * (codegen/py-pattern.ts). It is recorded and NOT enforced.\n * ⚠️ `data_type` on a custom field is CREATE-ONLY: enforced here as a value rule, never as a\n * reason to allow a PATCH — the refusal on change lives in the provider (custom-field.ts).\n */\nimport type { EndpointConstraints } from '../../constraints.ts';\n\nexport const PAPERLESS_DOCUMENT_TYPES_CONSTRAINTS: Readonly<Record<string, EndpointConstraints>> = {\n \"paperless:PATCH /api/document_types/{id}/\": {\n \"match\": {\"maxLength\":256,\"type\":\"string\"},\n \"matching_algorithm\": {\"enum\":[0,1,2,3,4,5,6],\"type\":\"integer\"},\n \"name\": {\"maxLength\":128,\"minLength\":1,\"type\":\"string\"},\n },\n \"paperless:POST /api/document_types/\": {\n \"match\": {\"maxLength\":256,\"type\":\"string\"},\n \"matching_algorithm\": {\"enum\":[0,1,2,3,4,5,6],\"type\":\"integer\"},\n \"name\": {\"maxLength\":128,\"minLength\":1,\"required\":true,\"type\":\"string\"},\n },\n};\n",
11
+ "/**\n * Generated Paperless-ngx REST API (OpenAPI 3.0.3) request-body constraints for `/api/storage_paths/` — DO NOT EDIT BY HAND.\n *\n * Run: bun codegen/paperless.ts\n * Manifest entry: `paperless-openapi` — Paperless-ngx REST API (OpenAPI 3.0.3) 3.1.1\n * sha256 d0fe550d2135e37b, Paperless-ngx web service, loopback\n * /api/schema/?format=json\n *\n * 8 of this product's 100 POST/PUT/PATCH/DELETE endpoints are tabled across all\n * areas: the ones this package writes to, named in its own source. Every other vendor write\n * endpoint is UNTABLED and therefore unchecked at plan time.\n *\n * ⚠️ A `patternSource` with no `pattern` beside it could NOT be carried into a JavaScript\n * RegExp faithfully — Django/DRF's `\\w` is Unicode and JavaScript's is ASCII\n * (codegen/py-pattern.ts). It is recorded and NOT enforced.\n * ⚠️ `data_type` on a custom field is CREATE-ONLY: enforced here as a value rule, never as a\n * reason to allow a PATCH — the refusal on change lives in the provider (custom-field.ts).\n */\nimport type { EndpointConstraints } from '../../constraints.ts';\n\nexport const PAPERLESS_STORAGE_PATHS_CONSTRAINTS: Readonly<Record<string, EndpointConstraints>> = {\n \"paperless:PATCH /api/storage_paths/{id}/\": {\n \"match\": {\"maxLength\":256,\"type\":\"string\"},\n \"matching_algorithm\": {\"enum\":[0,1,2,3,4,5,6],\"type\":\"integer\"},\n \"name\": {\"maxLength\":128,\"minLength\":1,\"type\":\"string\"},\n \"path\": {\"minLength\":1,\"type\":\"string\"},\n },\n \"paperless:POST /api/storage_paths/\": {\n \"match\": {\"maxLength\":256,\"type\":\"string\"},\n \"matching_algorithm\": {\"enum\":[0,1,2,3,4,5,6],\"type\":\"integer\"},\n \"name\": {\"maxLength\":128,\"minLength\":1,\"required\":true,\"type\":\"string\"},\n \"path\": {\"minLength\":1,\"required\":true,\"type\":\"string\"},\n },\n};\n",
12
+ "/**\n * Generated Paperless-ngx REST API (OpenAPI 3.0.3) request-body constraints for `/api/tags/` — DO NOT EDIT BY HAND.\n *\n * Run: bun codegen/paperless.ts\n * Manifest entry: `paperless-openapi` — Paperless-ngx REST API (OpenAPI 3.0.3) 3.1.1\n * sha256 d0fe550d2135e37b, Paperless-ngx web service, loopback\n * /api/schema/?format=json\n *\n * 8 of this product's 100 POST/PUT/PATCH/DELETE endpoints are tabled across all\n * areas: the ones this package writes to, named in its own source. Every other vendor write\n * endpoint is UNTABLED and therefore unchecked at plan time.\n *\n * ⚠️ A `patternSource` with no `pattern` beside it could NOT be carried into a JavaScript\n * RegExp faithfully — Django/DRF's `\\w` is Unicode and JavaScript's is ASCII\n * (codegen/py-pattern.ts). It is recorded and NOT enforced.\n * ⚠️ `data_type` on a custom field is CREATE-ONLY: enforced here as a value rule, never as a\n * reason to allow a PATCH — the refusal on change lives in the provider (custom-field.ts).\n */\nimport type { EndpointConstraints } from '../../constraints.ts';\n\nexport const PAPERLESS_TAGS_CONSTRAINTS: Readonly<Record<string, EndpointConstraints>> = {\n \"paperless:PATCH /api/tags/{id}/\": {\n \"color\": {\"maxLength\":7,\"minLength\":1,\"type\":\"string\"},\n \"match\": {\"maxLength\":256,\"type\":\"string\"},\n \"matching_algorithm\": {\"enum\":[0,1,2,3,4,5,6],\"type\":\"integer\"},\n \"name\": {\"maxLength\":128,\"minLength\":1,\"type\":\"string\"},\n },\n \"paperless:POST /api/tags/\": {\n \"color\": {\"maxLength\":7,\"minLength\":1,\"type\":\"string\"},\n \"match\": {\"maxLength\":256,\"type\":\"string\"},\n \"matching_algorithm\": {\"enum\":[0,1,2,3,4,5,6],\"type\":\"integer\"},\n \"name\": {\"maxLength\":128,\"minLength\":1,\"required\":true,\"type\":\"string\"},\n },\n};\n",
13
+ "/**\n * Every generated Paperless constraint table, merged — DO NOT EDIT BY HAND.\n *\n * Run: bun codegen/paperless.ts\n *\n * ⚠️ EVERY KEY IS PREFIXED `paperless:`, so one estate running several vendors' readers cannot\n * collide on an unprefixed `POST /api/…`.\n */\nimport type { EndpointConstraints } from '../../constraints.ts';\nimport { PAPERLESS_CUSTOM_FIELDS_CONSTRAINTS } from './paperless-custom_fields.ts';\nimport { PAPERLESS_DOCUMENT_TYPES_CONSTRAINTS } from './paperless-document_types.ts';\nimport { PAPERLESS_STORAGE_PATHS_CONSTRAINTS } from './paperless-storage_paths.ts';\nimport { PAPERLESS_TAGS_CONSTRAINTS } from './paperless-tags.ts';\n\nexport const PAPERLESS_CONSTRAINTS: Readonly<Record<string, EndpointConstraints>> = {\n ...PAPERLESS_CUSTOM_FIELDS_CONSTRAINTS,\n ...PAPERLESS_DOCUMENT_TYPES_CONSTRAINTS,\n ...PAPERLESS_STORAGE_PATHS_CONSTRAINTS,\n ...PAPERLESS_TAGS_CONSTRAINTS,\n};\n\n/** sha256 of the merged table, truncated — a digest of the DATA, not the file text. */\nexport const PAPERLESS_CONSTRAINTS_DIGEST = '66e61dce8c099d7d';\n",
14
+ "/**\n * `Paperless.Tag` — a Paperless-ngx tag, create-or-update and never deleted by default.\n *\n * ★ `matchingAlgorithm` AND THE OTHER ENUM-SHAPED FIELDS ARE INDEXED OFF THE GENERATED REQUEST\n * TYPE (`PaperlessTagRequest['matching_algorithm']`), NEVER RETYPED. The literal union `0 | 1 |\n * … | 6` comes from `codegen/paperless.ts`'s reading of the vendor's own `MatchingAlgorithm`\n * enum — copying it by hand here would be exactly the guess `schema-provenance` doctrine\n * forbids, and a vendor that adds a seventh algorithm would leave this type silently stale.\n *\n * ⛔ NO CREDENTIAL IS A PROP. `PAPERLESS_URL`/`PAPERLESS_TOKEN` come from `credentials.ts`.\n * ⛔ `defaultRemovalPolicy: 'retain'` — the reconciler this replaces never deletes taxonomy, and\n * a tag's id is referenced by every document it is attached to.\n */\nimport { Resource } from 'alchemy';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport type { PaperlessTagRequest } from './generated/types/tags.ts';\nimport { type MatchingProps, type MatchingRequirements, matchingHandlers } from './matching.ts';\n\nexport interface TagProps extends MatchingProps {\n /** ⚠️ maxLength 128 (generated constraint table). The locate key — see matching.ts's header. */\n name: string;\n /** ⚠️ minLength 1, maxLength 7 (a CSS hex color, generated table doesn't enforce the `#`). */\n color?: string;\n /** ⚠️ maxLength 256. The text or pattern documents are matched against. */\n match?: string;\n matchingAlgorithm?: PaperlessTagRequest['matching_algorithm'];\n isInsensitive?: boolean;\n /** Marks this tag as an inbox tag: newly consumed documents are tagged with it. */\n isInboxTag?: boolean;\n /** Another `Paperless.Tag`'s id — Paperless allows one level of tag nesting. */\n parent?: number;\n}\n\nexport interface TagAttributes {\n id: number;\n name: string;\n color: string;\n match: string;\n matchingAlgorithm: number;\n isInsensitive: boolean;\n isInboxTag: boolean;\n owner: number | undefined;\n parent: number | undefined;\n}\n\nexport interface Tag extends Resource<\n 'Paperless.Tag',\n TagProps,\n TagAttributes,\n never,\n MatchingRequirements\n> {}\n\nexport const Tag = Resource<Tag>('Paperless.Tag', { defaultRemovalPolicy: 'retain' });\n\nconst handlers = matchingHandlers<TagProps, TagAttributes>({\n attributes: (live, props) => {\n const id = live['id'];\n if (typeof id !== 'number') return undefined;\n return {\n color: typeof live['color'] === 'string' ? live['color'] : '',\n id,\n isInboxTag: live['is_inbox_tag'] === true,\n isInsensitive: live['is_insensitive'] === true,\n match: typeof live['match'] === 'string' ? live['match'] : '',\n matchingAlgorithm:\n typeof live['matching_algorithm'] === 'number' ? live['matching_algorithm'] : 0,\n name: typeof live['name'] === 'string' ? live['name'] : props.name,\n owner: typeof live['owner'] === 'number' ? live['owner'] : undefined,\n parent: typeof live['parent'] === 'number' ? live['parent'] : undefined,\n };\n },\n collection: 'tags',\n describe: (props) => `tags ${props.name}`,\n endpoint: { create: 'paperless:POST /api/tags/', update: 'paperless:PATCH /api/tags/{id}/' },\n fields: [\n { key: 'color', live: (r) => r['color'], prop: (p) => p.color },\n { key: 'match', live: (r) => r['match'], prop: (p) => p.match },\n {\n key: 'matching_algorithm',\n live: (r) => r['matching_algorithm'],\n prop: (p) => p.matchingAlgorithm,\n },\n { key: 'is_insensitive', live: (r) => r['is_insensitive'], prop: (p) => p.isInsensitive },\n { key: 'is_inbox_tag', live: (r) => r['is_inbox_tag'], prop: (p) => p.isInboxTag },\n { key: 'parent', live: (r) => r['parent'], prop: (p) => p.parent },\n ],\n});\n\nexport const TagProvider = () => Provider.effect(Tag, Effect.succeed(Tag.Provider.of(handlers)));\n",
15
+ "/**\n * One shape for every Paperless taxonomy object (Tag, DocumentType, StoragePath, CustomField):\n * locate by `name__iexact` only while there is no prior state, identify the exact (name, owner),\n * create-or-update, never delete by default. Modelled on `../netbox/resource.ts`'s\n * `netboxOperations`, with the differences this unit's spec calls for.\n *\n * ⛔ THE CREATE BODY ALWAYS CARRIES `owner`, NULL WHEN UNDECLARED. `OwnedObjectSerializer.create`\n * sets the owner to the TOKEN USER when the field is absent from the body (Paperless-ngx\n * `documents/serialisers.py:495-502` at v3.1.1) — omitting it the way NetBox's `prefix-form.ts`\n * omits an undeclared foreign key would silently create an object owned by whichever credential\n * ran the deploy, invisible to every other user and a second row beside the estate's `owner:\n * null` one on the next apply. Sending `owner: null` explicitly is what keeps a create\n * idempotent under this vendor's default.\n *\n * ★ READ ANSWERS `Unowned(attrs)` ON EVERY MATCH (H1, alchemy-provider-standard). Paperless has no\n * ownership marker this package could check — `owner` here is the estate's OWN prop, not proof\n * of who created the row — so, per the house's 2026-09-21 decision, identical is not ours: a\n * live match never adopts silently. `--adopt` (or `adopt(true)`) resolves it, same as any other\n * marker-less API (upstream's `Snippet.ts#read`).\n *\n * ⛔ IDENTITY IS THE STORED `id`, NOT `name`/`owner`, ONCE A RESOURCE HAS STATE (PR 163, red-team\n * HIGH finding). `diff` and `reconcile` used to re-derive identity from `news` alone on EVERY\n * call — `fetchLive` located a live row by `(name, owner)` even when `output` (the engine's\n * record of what this declaration last wrote, `Provider.ts#reconcile`'s `output` parameter) was\n * right there holding the object's real id. Editing `name` (or `owner`, for the three owned\n * families) on an already-deployed resource is the single most ordinary edit anyone would make\n * — and it made `fetchLive(news)` miss the row it previously wrote, fall into the create branch,\n * POST a SECOND object under the new name/owner, and leave the original permanently orphaned\n * (`list()` always answers `[]` by design, so `nuke` can never find it either). `diff` reported\n * a plain `{action:'update'}` — nothing told the operator this was about to duplicate and\n * abandon an object.\n *\n * The fix: once `output` is defined, `fetchLive` locates by `output.id` (a direct\n * `GET {collection}/{id}/`), never by name — a changed `name`/`owner` becomes an ordinary PATCH\n * of that same id (Paperless allows PATCHing both on all four types; documents keep their\n * tag/type/path because the id never changes). `patchBody`/`matches` now compare `name` and\n * (when `spec.owned !== false`) `owner` against the live row too, so the rename/re-own actually\n * reaches the PATCH body — locating the right row was necessary but not sufficient. The\n * name/owner-based `fetchByName` locate survives ONLY for the no-state path: a genuine first\n * create, or the engine's own adoption probe (`provider.read`, called with `output: undefined`\n * per `Plan.ts`'s adopt block) — `read` is unchanged and still locates by name for exactly that\n * reason. If `output.id` no longer exists live (deleted out of band — by hand, or by another\n * tool), `fetchLive` refuses loudly (`Effect.die`) rather than silently falling through to a\n * create: a plan that would otherwise read as \"ordinary update\" instead names the id and tells\n * the operator to adopt or clean up state, the same posture `immutable` already uses below for\n * `CustomField.dataType`.\n */\nimport { Unowned } from 'alchemy/AdoptPolicy';\nimport { isResolved } from 'alchemy/Diff';\nimport type { Input } from 'alchemy/Input';\nimport * as Effect from 'effect/Effect';\nimport type { PaperlessRow } from './client.ts';\nimport { paperless } from './client.ts';\nimport { guardBody } from './constraint-guard.ts';\nimport type { PaperlessBody } from './constraints.ts';\nimport { absentOn404, matchingLocate, ownerOf } from './matching-locate.ts';\nimport type { MatchingProps, MatchingSpec } from './matching-types.ts';\n\n/** Re-exported so `tag.ts`/`document-type.ts`/`storage-path.ts`/`custom-field.ts` keep importing the contract from `./matching.ts` — only the shape itself lives in `matching-types.ts`, split out to hold the file-size cap. */\nexport type {\n FieldSpec,\n MatchingError,\n MatchingProps,\n MatchingRequirements,\n MatchingSpec,\n} from './matching-types.ts';\n\n/** Object equality good enough for this family's scalars, nullable ints and `extra_data` JSON. */\nconst sameValue = (a: unknown, b: unknown): boolean =>\n typeof a === 'object' && a !== null ? JSON.stringify(a) === JSON.stringify(b) : Object.is(a, b);\n\nexport const matchingOperations = <\n Props extends MatchingProps,\n Attributes extends { readonly id: number },\n>(\n spec: MatchingSpec<Props, Attributes>,\n) => {\n // ★ IDENTITY RESOLUTION LIVES IN `matching-locate.ts` (file-size cap) — `fetchByName` for the\n // no-state path, `fetchLive` for the `output.id`-first switch this file's header describes.\n const { fetchByName, fetchById, fetchLive, objectPath } = matchingLocate<Props>(spec);\n\n /** ⚠️ ALWAYS NAME-BASED — this is `read`'s own contract (S7): find the object from props alone, with no `output` to consult. Unaffected by the identity switch above. */\n const read = (props: Props) =>\n fetchByName(props).pipe(\n Effect.map((live) => (live === undefined ? undefined : spec.attributes(live, props))),\n );\n\n const readById = (props: Props, id: number) =>\n fetchById(id).pipe(\n Effect.map((live) => (live === undefined ? undefined : spec.attributes(live, props))),\n );\n\n const createBody = (props: Props): PaperlessBody => {\n const out: PaperlessBody =\n spec.owned === false\n ? { name: props.name }\n : { name: props.name, owner: props.owner ?? null };\n for (const field of [...spec.fields, ...(spec.createOnly ?? [])]) {\n const value = field.prop(props);\n if (value !== undefined) out[field.key] = value;\n }\n return out;\n };\n\n /**\n * ⚠️ Only fields that DIFFER from the live row — a PATCH that repeats an unchanged value is\n * still a write the audit log carries for nothing.\n * ⛔ `name` AND (WHEN OWNED) `owner` ARE COMPARED TOO, NOT JUST `spec.fields`. Locating the\n * right row by `id` fixed WHERE the write lands; without this, a rename/re-own would still\n * locate correctly and then send an empty-for-those-keys PATCH, so the live object's name or\n * owner would silently never change (PR 163).\n */\n const patchBody = (props: Props, live: PaperlessRow): PaperlessBody => {\n const out: PaperlessBody = {};\n if (props.name !== live['name']) out['name'] = props.name;\n if (spec.owned !== false && ownerOf(live) !== (props.owner ?? undefined)) {\n out['owner'] = props.owner ?? null;\n }\n for (const field of spec.fields) {\n const value = field.prop(props);\n if (value !== undefined && !sameValue(value, field.live(live))) out[field.key] = value;\n }\n return out;\n };\n\n const matches = (props: Props, live: PaperlessRow): boolean => {\n if (props.name !== live['name']) return false;\n if (spec.owned !== false && ownerOf(live) !== (props.owner ?? undefined)) return false;\n return spec.fields.every((field) => {\n const value = field.prop(props);\n return value === undefined || sameValue(value, field.live(live));\n });\n };\n\n return {\n read,\n\n diff: (news: Input<Props>, output: Attributes | undefined) =>\n Effect.gen(function* () {\n if (output === undefined || !isResolved(news)) return undefined;\n const live = yield* fetchLive(news, output);\n if (live === undefined) return { action: 'update' } as const;\n const refusal = spec.immutable?.(live, news);\n if (refusal !== undefined) return yield* Effect.die(new Error(refusal));\n if (matches(news, live)) return { action: 'noop' } as const;\n return spec.endpoint.update === undefined\n ? ({ action: 'replace' } as const)\n : ({ action: 'update' } as const);\n }),\n\n reconcile: (news: Props, output: Attributes | undefined) =>\n Effect.gen(function* () {\n const before = yield* fetchLive(news, output);\n let id: number | undefined;\n if (before === undefined) {\n const body = createBody(news);\n yield* guardBody(spec.endpoint.create, body, true);\n yield* paperless('POST', `${spec.collection}/`, body);\n } else {\n id = typeof before['id'] === 'number' ? before['id'] : undefined;\n // ⚠️ RE-CHECKED HERE, NOT ONLY IN `diff` — an adoption's first reconcile can run with\n // no prior `diff` at all, and `immutable` guards a write that would destroy data.\n const refusal = spec.immutable?.(before, news);\n if (refusal !== undefined) return yield* Effect.die(new Error(refusal));\n if (spec.endpoint.update !== undefined) {\n const body = patchBody(news, before);\n if (Object.keys(body).length > 0) {\n yield* guardBody(spec.endpoint.update, body, false);\n yield* paperless('PATCH', `${objectPath(before)}/`, body);\n }\n }\n }\n // ★ READ BACK BY id WHEN WE HAVE ONE — a just-PATCHed row is read back by the id we\n // wrote to, not by (possibly just-changed) name, so a rename's own read-back cannot\n // fail the way the write it follows no longer can either. A fresh create has no id yet\n // to read back by, so that branch still reads back by the name it just created.\n const after = yield* id === undefined ? read(news) : readById(news, id);\n if (after === undefined) {\n return yield* Effect.die(\n new Error(\n `${spec.describe(news)}: the write returned no error but the object is still absent.`,\n ),\n );\n }\n return after;\n }),\n\n /**\n * ⛔ `output.id` FIRST, SAME SWITCH AS `fetchLive` (PR 163 re-review). The engine always\n * passes `output` here too (`Apply.ts`'s delete call sites all carry `output: attr`) —\n * `olds` is only the PROPS as last persisted, which drifts from the live object exactly\n * the way `news` did pre-fix: rename it out of band (by hand, or by another tool) after\n * the last successful reconcile, and a name-only locate silently finds nothing, `destroy`\n * returns as if there were nothing to delete, and the engine drops the state row anyway —\n * reporting \"deleted\" while the live object survives, permanently orphaned (the same\n * consequence `matching.ts`'s header describes for the original bug, on the delete path\n * the original fix didn't touch). A 404 on the `output.id` itself is NOT an error here,\n * unlike `fetchLive`'s die — it means the object is already gone, which is exactly the\n * idempotent success `delete` is supposed to report (S11, alchemy-provider-standard).\n */\n destroy: (olds: Props, output: Attributes | undefined) =>\n Effect.gen(function* () {\n const live = output === undefined ? yield* fetchByName(olds) : yield* fetchById(output.id);\n if (live === undefined) return;\n yield* absentOn404(paperless('DELETE', `${objectPath(live)}/`));\n }),\n };\n};\n\n/** The five provider handlers for a spec'd Paperless taxonomy object, wired once. */\nexport const matchingHandlers = <\n Props extends MatchingProps,\n Attributes extends { readonly id: number },\n>(\n spec: MatchingSpec<Props, Attributes>,\n) => {\n const ops = matchingOperations(spec);\n return {\n /** ⚠️ `[]` — Paperless index endpoints return the whole estate; adoption stays explicit. */\n list: () => Effect.succeed([]),\n read: ({ olds }: { olds: Props }) =>\n ops\n .read(olds)\n .pipe(Effect.map((attrs) => (attrs === undefined ? undefined : Unowned(attrs)))),\n diff: ({ news, output }: { news: Input<Props>; output: Attributes | undefined }) =>\n ops.diff(news, output),\n // ⛔ `output` IS WHAT MAKES THE IDENTITY FIX WORK (PR 163) — the engine always passes it\n // (`Provider.ts#reconcile`), so it is optional here only so a test can still call\n // `reconcile({ news })` for a from-nothing create without spelling out `output: undefined`.\n // ⚠️ `| undefined` on the optional type itself, not just `?` — `exactOptionalPropertyTypes`\n // otherwise refuses the engine's own `output: Attributes | undefined` at the call site.\n reconcile: ({ news, output }: { news: Props; output?: Attributes | undefined }) =>\n ops.reconcile(news, output),\n // ⚠️ `output` PASSED THROUGH, NOT DROPPED — see `destroy`'s own header above (PR 163\n // re-review). Same `| undefined` reasoning as `reconcile` just above.\n delete: ({ olds, output }: { olds: Props; output?: Attributes | undefined }) =>\n ops.destroy(olds, output),\n };\n};\n",
16
+ "/**\n * One authenticated call to the Paperless-ngx REST API at `/api`.\n *\n * ⚠️ `Authorization: Token <value>`, NOT `Bearer`. Paperless-ngx's `securitySchemes.tokenAuth`\n * (the v3.1.1 served schema) states the \"Token\" prefix explicitly — the same DRF\n * `TokenAuthentication` NetBox uses (netbox/client.ts), a different vendor, same convention.\n *\n * ⛔ `Accept: application/json; version=10` ON EVERY CALL. Paperless-ngx versions its API with\n * DRF's `AcceptHeaderVersioning` (`REST_FRAMEWORK.DEFAULT_VERSION = '10'`, measured against the\n * v3.1.1 source). Omitting the header still answers — DRF falls back to `DEFAULT_VERSION` — but\n * pinning it here means an estate upgrade that moves the default cannot silently change what\n * this package receives without a version bump of its own noticing.\n *\n * ⚠️ A LIST IS WRAPPED, A DETAIL IS NOT: `{count, next, previous, results: […]}` vs. the bare\n * object — the same DRF pagination shape NetBox uses.\n */\nimport * as Effect from 'effect/Effect';\nimport * as HttpClient from 'effect/unstable/http/HttpClient';\nimport * as HttpClientRequest from 'effect/unstable/http/HttpClientRequest';\nimport { PaperlessCredentials, type PaperlessCredentialsError } from './credentials.ts';\nimport { type PaperlessError, PaperlessUnavailable, statusToError } from './errors.ts';\n\nconst PATH_PREFIX = '/api';\nconst API_VERSION = '10';\n\nexport interface PaperlessList<T> {\n readonly count: number;\n readonly next: string | null;\n readonly previous: string | null;\n readonly results: readonly T[];\n}\n\nexport type PaperlessRow = Record<string, unknown>;\n\n/**\n * ⚠️ `PaperlessCredentials` IS PART OF THIS, NOT JUST `HttpClient`. `paperless()` reads the lazy\n * credentials service (`yield* yield* PaperlessCredentials`), so a stack that wires the\n * providers without also providing a credentials layer should fail to TYPECHECK, not fail at\n * the first call. `providers.ts` supplies `environmentLayer` by default for exactly this.\n */\nexport type PaperlessRequirements = HttpClient.HttpClient | PaperlessCredentials;\n\n/** One JSON call. Returns the parsed body, or `undefined` for an empty response (DELETE). */\nexport const paperless = <T>(\n method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE',\n path: string,\n body?: Record<string, unknown>,\n): Effect.Effect<T, PaperlessError | PaperlessCredentialsError, PaperlessRequirements> =>\n Effect.gen(function* () {\n const credentials = yield* yield* PaperlessCredentials;\n const url = `${credentials.url}${PATH_PREFIX}/${path.replace(/^\\//, '')}`;\n const client = yield* HttpClient.HttpClient;\n const request = HttpClientRequest.make(method)(url).pipe(\n HttpClientRequest.setHeaders({\n Accept: `application/json; version=${API_VERSION}`,\n Authorization: `Token ${credentials.token}`,\n }),\n // ⛔ Content-Type on `bodyText`, not the header map — `bodyText(s)` with no second argument\n // sets `text/plain` and overwrites a `Content-Type` set earlier via `setHeaders`, because\n // it is piped afterwards (measured against `@homeflare/alchemy/forgejo`, netbox/client.ts).\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 PaperlessUnavailable({ detail: String(cause), method, path, status: 0 }),\n ),\n );\n if (response.status < 200 || response.status >= 300) {\n const detail = yield* response.text.pipe(Effect.orElseSucceed(() => ''));\n return yield* Effect.fail(statusToError(response.status, method, path, detail.slice(0, 500)));\n }\n const text = yield* response.text.pipe(\n Effect.mapError(\n () =>\n new PaperlessUnavailable({\n detail: 'body read failed',\n method,\n path,\n status: response.status,\n }),\n ),\n );\n return (text === '' ? undefined : JSON.parse(text)) as T;\n });\n",
17
+ "/**\n * The identity-resolution slice of `matching.ts`'s lifecycle, split into its own file so\n * `matching.ts` stays under the house's 250-line-per-file cap. Owns exactly one question — which\n * live Paperless row IS this declaration — by name while there is no state, by the stored\n * `output.id` once there is one. See `matching.ts`'s header for the PR 163 red-team HIGH finding\n * this identity switch fixes.\n */\nimport * as Effect from 'effect/Effect';\nimport { PaperlessNotFound } from './errors.ts';\nimport { type PaperlessList, type PaperlessRow, paperless } from './client.ts';\nimport type { MatchingError, MatchingProps, MatchingRequirements } from './matching-types.ts';\n\n/**\n * ⚠️ Only 404 folds to \"absent\" — a 401/403 is never read as absent (acceptance criteria).\n * Exported — `matching.ts`'s `destroy` uses it directly around its own DELETE call.\n */\nexport const absentOn404 = <A>(io: Effect.Effect<A, MatchingError, MatchingRequirements>) =>\n io.pipe(\n Effect.catchIf(\n (cause): cause is PaperlessNotFound => cause instanceof PaperlessNotFound,\n () => Effect.succeed(undefined as A),\n ),\n );\n\n/** ★ Wide enough to see every case-variant candidate `name__iexact` returns; a natural key past it is not one. */\nconst LOCATE_PAGE = 20;\n\n/** ⚠️ Exported — `matching.ts`'s `patchBody`/`matches` compare the live `owner` too (PR 163). */\nexport const ownerOf = (row: PaperlessRow): number | undefined =>\n typeof row['owner'] === 'number' ? row['owner'] : undefined;\n\n/**\n * The name/id lookups every `matching.ts` operation shares, built once per family spec.\n * `collection` and `describe` are all this slice needs out of a full `MatchingSpec`.\n */\nexport const matchingLocate = <Props extends MatchingProps>(spec: {\n readonly collection: string;\n readonly describe: (props: Props) => string;\n}) => {\n /**\n * ★ THE NO-STATE LOCATE. Used only when there is no `output` yet: a genuine first create, or\n * the engine's own adoption probe (`provider.read`, always called with `output: undefined`).\n */\n const fetchByName = (props: Props) =>\n absentOn404(\n paperless<PaperlessList<PaperlessRow>>(\n 'GET',\n `${spec.collection}/?name__iexact=${encodeURIComponent(props.name)}&page_size=${String(LOCATE_PAGE)}`,\n ),\n ).pipe(\n Effect.map((list) => {\n if (list !== undefined && list.count > LOCATE_PAGE) {\n throw new Error(\n `${spec.describe(props)}: name__iexact matched ${String(list.count)} rows, more than ` +\n `the ${String(LOCATE_PAGE)}-row page this reads. Narrow the name rather than paging.`,\n );\n }\n const rows = (list?.results ?? []).filter(\n (row) => row['name'] === props.name && ownerOf(row) === (props.owner ?? undefined),\n );\n if (rows.length > 1) {\n throw new Error(\n `${spec.describe(props)}: matched ${String(rows.length)} Paperless objects with the same (name, owner).`,\n );\n }\n return rows[0];\n }),\n );\n\n /** The stored id itself, once this declaration has one. A 404 here means the row is gone. */\n const fetchById = (id: number) =>\n absentOn404(paperless<PaperlessRow>('GET', `${spec.collection}/${String(id)}/`));\n\n /**\n * ⛔ THE IDENTITY SWITCH (PR 163, red-team HIGH finding — see `matching.ts`'s header). `output`\n * defined means this declaration has already written something: identity is `output.id`, full\n * stop, and a changed `name`/`owner` in `props` is drift to PATCH, never a reason to relocate.\n * `output` undefined means there is nothing to be wrong about yet — locate by name.\n */\n const fetchLive = <Attributes extends { readonly id: number }>(\n props: Props,\n output: Attributes | undefined,\n ) =>\n output === undefined\n ? fetchByName(props)\n : fetchById(output.id).pipe(\n Effect.flatMap((live) =>\n live === undefined\n ? Effect.die(\n new Error(\n `${spec.describe(props)}: id ${String(output.id)} is recorded in state but no ` +\n 'longer exists live (deleted out of band). Refusing to create a replacement ' +\n '— adopt the live object back under this declaration, or remove it from ' +\n 'state, before deploying again.',\n ),\n )\n : Effect.succeed(live),\n ),\n );\n\n const objectPath = (live: PaperlessRow): string => `${spec.collection}/${String(live['id'])}`;\n\n return { fetchByName, fetchById, fetchLive, objectPath };\n};\n",
18
+ "/**\n * `Paperless.DocumentType` — a Paperless-ngx document type, create-or-update and never deleted.\n * Same shape as `tag.ts` minus `color`/`isInboxTag`/`parent` — see that file's header for why\n * `matchingAlgorithm`'s type comes off the generated request type rather than being retyped.\n */\nimport { Resource } from 'alchemy';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport type { PaperlessDocumentTypeRequest } from './generated/types/document-types.ts';\nimport { type MatchingProps, type MatchingRequirements, matchingHandlers } from './matching.ts';\n\nexport interface DocumentTypeProps extends MatchingProps {\n /** ⚠️ maxLength 128 (generated constraint table). The locate key. */\n name: string;\n /** ⚠️ maxLength 256. */\n match?: string;\n matchingAlgorithm?: PaperlessDocumentTypeRequest['matching_algorithm'];\n isInsensitive?: boolean;\n}\n\nexport interface DocumentTypeAttributes {\n id: number;\n name: string;\n match: string;\n matchingAlgorithm: number;\n isInsensitive: boolean;\n owner: number | undefined;\n}\n\nexport interface DocumentType extends Resource<\n 'Paperless.DocumentType',\n DocumentTypeProps,\n DocumentTypeAttributes,\n never,\n MatchingRequirements\n> {}\n\nexport const DocumentType = Resource<DocumentType>('Paperless.DocumentType', {\n defaultRemovalPolicy: 'retain',\n});\n\nconst handlers = matchingHandlers<DocumentTypeProps, DocumentTypeAttributes>({\n attributes: (live, props) => {\n const id = live['id'];\n if (typeof id !== 'number') return undefined;\n return {\n id,\n isInsensitive: live['is_insensitive'] === true,\n match: typeof live['match'] === 'string' ? live['match'] : '',\n matchingAlgorithm:\n typeof live['matching_algorithm'] === 'number' ? live['matching_algorithm'] : 0,\n name: typeof live['name'] === 'string' ? live['name'] : props.name,\n owner: typeof live['owner'] === 'number' ? live['owner'] : undefined,\n };\n },\n collection: 'document_types',\n describe: (props) => `document_types ${props.name}`,\n endpoint: {\n create: 'paperless:POST /api/document_types/',\n update: 'paperless:PATCH /api/document_types/{id}/',\n },\n fields: [\n { key: 'match', live: (r) => r['match'], prop: (p) => p.match },\n {\n key: 'matching_algorithm',\n live: (r) => r['matching_algorithm'],\n prop: (p) => p.matchingAlgorithm,\n },\n { key: 'is_insensitive', live: (r) => r['is_insensitive'], prop: (p) => p.isInsensitive },\n ],\n});\n\nexport const DocumentTypeProvider = () =>\n Provider.effect(DocumentType, Effect.succeed(DocumentType.Provider.of(handlers)));\n",
19
+ "/**\n * `Paperless.StoragePath` — a Paperless-ngx storage path template, create-or-update and never\n * deleted. Same shape as `document-type.ts` plus the vendor-required `path` template string.\n */\nimport { Resource } from 'alchemy';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport type { PaperlessStoragePathRequest } from './generated/types/storage-paths.ts';\nimport { type MatchingProps, type MatchingRequirements, matchingHandlers } from './matching.ts';\n\nexport interface StoragePathProps extends MatchingProps {\n /** ⚠️ maxLength 128 (generated constraint table). The locate key. */\n name: string;\n /** ⚠️ minLength 1 (generated table). Paperless's storage-path template syntax, e.g. `{correspondent}/{title}`. */\n path: string;\n /** ⚠️ maxLength 256. */\n match?: string;\n matchingAlgorithm?: PaperlessStoragePathRequest['matching_algorithm'];\n isInsensitive?: boolean;\n}\n\nexport interface StoragePathAttributes {\n id: number;\n name: string;\n path: string;\n match: string;\n matchingAlgorithm: number;\n isInsensitive: boolean;\n owner: number | undefined;\n}\n\nexport interface StoragePath extends Resource<\n 'Paperless.StoragePath',\n StoragePathProps,\n StoragePathAttributes,\n never,\n MatchingRequirements\n> {}\n\nexport const StoragePath = Resource<StoragePath>('Paperless.StoragePath', {\n defaultRemovalPolicy: 'retain',\n});\n\nconst handlers = matchingHandlers<StoragePathProps, StoragePathAttributes>({\n attributes: (live, props) => {\n const id = live['id'];\n if (typeof id !== 'number') return undefined;\n return {\n id,\n isInsensitive: live['is_insensitive'] === true,\n match: typeof live['match'] === 'string' ? live['match'] : '',\n matchingAlgorithm:\n typeof live['matching_algorithm'] === 'number' ? live['matching_algorithm'] : 0,\n name: typeof live['name'] === 'string' ? live['name'] : props.name,\n owner: typeof live['owner'] === 'number' ? live['owner'] : undefined,\n path: typeof live['path'] === 'string' ? live['path'] : props.path,\n };\n },\n collection: 'storage_paths',\n describe: (props) => `storage_paths ${props.name}`,\n endpoint: {\n create: 'paperless:POST /api/storage_paths/',\n update: 'paperless:PATCH /api/storage_paths/{id}/',\n },\n fields: [\n { key: 'path', live: (r) => r['path'], prop: (p) => p.path },\n { key: 'match', live: (r) => r['match'], prop: (p) => p.match },\n {\n key: 'matching_algorithm',\n live: (r) => r['matching_algorithm'],\n prop: (p) => p.matchingAlgorithm,\n },\n { key: 'is_insensitive', live: (r) => r['is_insensitive'], prop: (p) => p.isInsensitive },\n ],\n});\n\nexport const StoragePathProvider = () =>\n Provider.effect(StoragePath, Effect.succeed(StoragePath.Provider.of(handlers)));\n",
20
+ "/**\n * `Paperless.CustomField` — a Paperless-ngx custom field, create-or-update and never deleted.\n *\n * ⛔ `dataType` IS CREATE-ONLY, ENFORCED AT PLAN TIME, NEVER A PATCH AND NEVER A REPLACE. A\n * replace would delete this id and create a new one — and Paperless deletes the field's VALUE\n * on every document when its custom field is deleted. `matching.ts`'s `immutable` hook carries\n * this refusal generically; `dataType` is simply never listed in `fields`, so nothing in\n * `matching.ts` ever builds a PATCH body that includes it.\n *\n * ⛔ NO `owner` FIELD. Unlike `Tag`/`DocumentType`/`StoragePath`, `CustomField` does not extend\n * Paperless-ngx's `OwnedObjectSerializer` — its wire body is exactly `{name, data_type,\n * extra_data}` (measured against the v3.1.1 schema). `owned: false` stops `matching.ts`\n * sending a field the vendor's serializer does not declare.\n *\n * ⚠️ `CustomField.select_options` (the vendor's own rule for a `select` field's option list) is\n * not in the served schema at all, so it is recorded here and NOT enforced (groundTruth).\n */\nimport { Resource } from 'alchemy';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport type { PaperlessCustomFieldRequest } from './generated/types/custom-fields.ts';\nimport type { PaperlessRow } from './client.ts';\nimport { type MatchingRequirements, matchingHandlers } from './matching.ts';\n\nexport interface CustomFieldProps {\n /** ⚠️ maxLength 128 (generated constraint table). The locate key — CustomField's name is globally unique (models.py:1169), with no owner to further scope it. */\n name: string;\n /**\n * ⛔ CREATE-ONLY — see this file's header. `select`'s own option list\n * (`select_options`) is not in the vendor schema and is unenforced here.\n */\n dataType: PaperlessCustomFieldRequest['data_type'];\n /** Arbitrary JSON the vendor attaches no schema to (e.g. a `select` field's option list). */\n extraData?: unknown;\n}\n\nexport interface CustomFieldAttributes {\n id: number;\n name: string;\n dataType: string;\n extraData: unknown;\n}\n\nexport interface CustomField extends Resource<\n 'Paperless.CustomField',\n CustomFieldProps,\n CustomFieldAttributes,\n never,\n MatchingRequirements\n> {}\n\nexport const CustomField = Resource<CustomField>('Paperless.CustomField', {\n defaultRemovalPolicy: 'retain',\n});\n\n/**\n * ⛔ EXTRACTED SO IT CAN BE TESTED WITHOUT A SERVER (custom-field.test.ts) — the same reason\n * `../netbox/prefix-form.ts` pulls its body/matches functions out of `prefix.ts`.\n */\nexport const dataTypeImmutable = (\n live: PaperlessRow,\n props: CustomFieldProps,\n): string | undefined =>\n typeof live['data_type'] === 'string' && live['data_type'] !== props.dataType\n ? `Paperless.CustomField \"${props.name}\": data_type is create-only (live: ${live['data_type']}, ` +\n `declared: ${props.dataType}). Changing it would delete this field's value on every ` +\n 'document. Create a new custom field instead, and retire this one by hand.'\n : undefined;\n\n/** ⚠️ Exported (not through index.ts) so custom-field.test.ts can drive the real lifecycle. */\nexport const customFieldHandlers = matchingHandlers<CustomFieldProps, CustomFieldAttributes>({\n attributes: (live, props) => {\n const id = live['id'];\n if (typeof id !== 'number') return undefined;\n return {\n dataType: typeof live['data_type'] === 'string' ? live['data_type'] : props.dataType,\n extraData: live['extra_data'],\n id,\n name: typeof live['name'] === 'string' ? live['name'] : props.name,\n };\n },\n collection: 'custom_fields',\n describe: (props) => `custom_fields ${props.name}`,\n endpoint: {\n create: 'paperless:POST /api/custom_fields/',\n update: 'paperless:PATCH /api/custom_fields/{id}/',\n },\n createOnly: [{ key: 'data_type', live: (r) => r['data_type'], prop: (p) => p.dataType }],\n fields: [{ key: 'extra_data', live: (r) => r['extra_data'], prop: (p) => p.extraData }],\n immutable: dataTypeImmutable,\n owned: false,\n});\n\nexport const CustomFieldProvider = () =>\n Provider.effect(CustomField, Effect.succeed(CustomField.Provider.of(customFieldHandlers)));\n",
21
+ "/**\n * All four Paperless taxonomy providers, one credentials layer shared between them.\n *\n * Layer.mergeAll(paperlessProviders(), …the stack's other providers)\n *\n * ⚠️ `HttpClient.HttpClient` IS NOT BUNDLED HERE, and that follows `../netbox/index.ts` and\n * `../forgejo/client.ts`'s own precedent (its header: \"the idiom Alchemy's own providers\n * follow — `Hetzner/Providers.ts` builds on `FetchHttpClient.layer`\") rather than diverging\n * from it: the transport is the consuming STACK's choice (`FetchHttpClient.layer` in\n * production, a fake `HttpClient` in a test), not a house default a provider file should pin.\n */\nimport * as Layer from 'effect/Layer';\nimport { CustomFieldProvider } from './custom-field.ts';\nimport { DocumentTypeProvider } from './document-type.ts';\nimport { type PaperlessCredentials, environmentLayer } from './credentials.ts';\nimport { StoragePathProvider } from './storage-path.ts';\nimport { TagProvider } from './tag.ts';\n\nexport const paperlessProviders = (\n credentials: Layer.Layer<PaperlessCredentials> = environmentLayer,\n) =>\n Layer.mergeAll(\n TagProvider(),\n DocumentTypeProvider(),\n StoragePathProvider(),\n CustomFieldProvider(),\n ).pipe(Layer.provide(credentials));\n"
22
+ ],
23
+ "mappings": "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",
24
+ "debugId": "EE28611444A13F1E64756E2164756E21",
25
+ "names": []
26
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * The identity-resolution slice of `matching.ts`'s lifecycle, split into its own file so
3
+ * `matching.ts` stays under the house's 250-line-per-file cap. Owns exactly one question — which
4
+ * live Paperless row IS this declaration — by name while there is no state, by the stored
5
+ * `output.id` once there is one. See `matching.ts`'s header for the PR 163 red-team HIGH finding
6
+ * this identity switch fixes.
7
+ */
8
+ import * as Effect from 'effect/Effect';
9
+ import { type PaperlessRow } from './client.ts';
10
+ import type { MatchingError, MatchingProps, MatchingRequirements } from './matching-types.ts';
11
+ /**
12
+ * ⚠️ Only 404 folds to "absent" — a 401/403 is never read as absent (acceptance criteria).
13
+ * Exported — `matching.ts`'s `destroy` uses it directly around its own DELETE call.
14
+ */
15
+ export declare const absentOn404: <A>(io: Effect.Effect<A, MatchingError, MatchingRequirements>) => Effect.Effect<A, import("./credentials.ts").PaperlessCredentialsError | import("./errors.ts").PaperlessUnauthorized | import("./errors.ts").PaperlessUnavailable | import("./errors.ts").PaperlessValidation, import("./client.ts").PaperlessRequirements>;
16
+ /** ⚠️ Exported — `matching.ts`'s `patchBody`/`matches` compare the live `owner` too (PR 163). */
17
+ export declare const ownerOf: (row: PaperlessRow) => number | undefined;
18
+ /**
19
+ * The name/id lookups every `matching.ts` operation shares, built once per family spec.
20
+ * `collection` and `describe` are all this slice needs out of a full `MatchingSpec`.
21
+ */
22
+ export declare const matchingLocate: <Props extends MatchingProps>(spec: {
23
+ readonly collection: string;
24
+ readonly describe: (props: Props) => string;
25
+ }) => {
26
+ fetchByName: (props: Props) => Effect.Effect<PaperlessRow | undefined, import("./credentials.ts").PaperlessCredentialsError | import("./errors.ts").PaperlessUnauthorized | import("./errors.ts").PaperlessUnavailable | import("./errors.ts").PaperlessValidation, import("./client.ts").PaperlessRequirements>;
27
+ fetchById: (id: number) => Effect.Effect<PaperlessRow, import("./credentials.ts").PaperlessCredentialsError | import("./errors.ts").PaperlessUnauthorized | import("./errors.ts").PaperlessUnavailable | import("./errors.ts").PaperlessValidation, import("./client.ts").PaperlessRequirements>;
28
+ fetchLive: <Attributes extends {
29
+ readonly id: number;
30
+ }>(props: Props, output: Attributes | undefined) => Effect.Effect<PaperlessRow | undefined, import("./credentials.ts").PaperlessCredentialsError | import("./errors.ts").PaperlessUnauthorized | import("./errors.ts").PaperlessUnavailable | import("./errors.ts").PaperlessValidation, import("./client.ts").PaperlessRequirements>;
31
+ objectPath: (live: PaperlessRow) => string;
32
+ };
33
+ //# sourceMappingURL=matching-locate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"matching-locate.d.ts","sourceRoot":"","sources":["../../src/paperless/matching-locate.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,OAAO,EAAsB,KAAK,YAAY,EAAa,MAAM,aAAa,CAAC;AAC/E,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAE9F;;;GAGG;AACH,eAAO,MAAM,WAAW,GAAI,CAAC,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,aAAa,EAAE,oBAAoB,CAAC,+PAMrF,CAAC;AAKJ,iGAAiG;AACjG,eAAO,MAAM,OAAO,QAAS,YAAY,KAAG,MAAM,GAAG,SACQ,CAAC;AAE9D;;;GAGG;AACH,eAAO,MAAM,cAAc,GAAI,KAAK,SAAS,aAAa,QAAQ;IAChE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,MAAM,CAAC;CAC7C;yBAK6B,KAAK;oBA2BV,MAAM;gBASV,UAAU,SAAS;QAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;KAAE,SACpD,KAAK,UACJ,UAAU,GAAG,SAAS;uBAmBN,YAAY,KAAG,MAAM;CAGhD,CAAC"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * The contract every Paperless taxonomy object (Tag, DocumentType, StoragePath, CustomField)
3
+ * declares against `matching.ts`'s generic lifecycle — split into its own file so `matching.ts`
4
+ * itself (the identity/locate/create/patch logic the PR 163 red-team HIGH finding is about) stays
5
+ * under the house's 250-line-per-file cap. See `matching.ts`'s header for the identity design
6
+ * (`output.id` once a resource has state) this `MatchingSpec` shape supports.
7
+ */
8
+ import type { PaperlessCredentialsError } from './credentials.ts';
9
+ import type { PaperlessError } from './errors.ts';
10
+ import type { PaperlessRequirements, PaperlessRow } from './client.ts';
11
+ import type { EndpointKey } from './constraints.ts';
12
+ /** = `client.ts`'s `PaperlessRequirements` — `HttpClient` and the credentials service both. */
13
+ export type MatchingRequirements = PaperlessRequirements;
14
+ export type MatchingError = PaperlessError | PaperlessCredentialsError;
15
+ /** A wire field beyond `name`/`owner`: how to read it from props, and how to read it back. */
16
+ export interface FieldSpec<Props> {
17
+ readonly key: string;
18
+ readonly prop: (props: Props) => unknown;
19
+ readonly live: (row: PaperlessRow) => unknown;
20
+ }
21
+ export interface MatchingProps {
22
+ readonly name: string;
23
+ /** `undefined` means undeclared — the create body still sends `owner: null` (see matching.ts's header). */
24
+ readonly owner?: number;
25
+ }
26
+ export interface MatchingSpec<Props extends MatchingProps, Attributes extends {
27
+ readonly id: number;
28
+ }> {
29
+ /** `tags`, `document_types`, `storage_paths`, `custom_fields`. */
30
+ readonly collection: string;
31
+ /**
32
+ * ⚠️ DEFAULT `true`. `Tag`, `DocumentType` and `StoragePath` extend Paperless-ngx's
33
+ * `OwnedObjectSerializer`; `CustomField` does not — its wire body has no `owner` key at all
34
+ * (measured against the v3.1.1 schema: `CustomFieldRequest` is `{name, data_type,
35
+ * extra_data}`). `false` stops `createBody` sending a field the vendor would silently ignore.
36
+ */
37
+ readonly owned?: boolean;
38
+ /** Compared, PATCHed when they differ, and sent on create. */
39
+ readonly fields: readonly FieldSpec<Props>[];
40
+ /**
41
+ * Sent on CREATE only — never compared, never PATCHed. `CustomField.dataType` (custom-field.ts)
42
+ * is the one user: it must reach the create body, but `immutable` is what refuses a later
43
+ * change, and `matches`/`patchBody` must never see it as ordinary drift.
44
+ */
45
+ readonly createOnly?: readonly FieldSpec<Props>[];
46
+ readonly attributes: (live: PaperlessRow, props: Props) => Attributes | undefined;
47
+ readonly endpoint: {
48
+ readonly create: EndpointKey;
49
+ readonly update?: EndpointKey;
50
+ };
51
+ readonly describe: (props: Props) => string;
52
+ /**
53
+ * A field this object refuses to change once set — `CustomField.dataType` (custom-field.ts):
54
+ * a replace would delete that field's value on every document, so neither a PATCH nor a
55
+ * replace is acceptable. Return the refusal message, or `undefined` when unchanged. Checked in
56
+ * `diff` (plan time) and again in `reconcile` (an adoption `diff` never saw).
57
+ */
58
+ readonly immutable?: (live: PaperlessRow, props: Props) => string | undefined;
59
+ }
60
+ //# sourceMappingURL=matching-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"matching-types.d.ts","sourceRoot":"","sources":["../../src/paperless/matching-types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,kBAAkB,CAAC;AAClE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACvE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,+FAA+F;AAC/F,MAAM,MAAM,oBAAoB,GAAG,qBAAqB,CAAC;AACzD,MAAM,MAAM,aAAa,GAAG,cAAc,GAAG,yBAAyB,CAAC;AAEvE,8FAA8F;AAC9F,MAAM,WAAW,SAAS,CAAC,KAAK;IAC9B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC;IACzC,QAAQ,CAAC,IAAI,EAAE,CAAC,GAAG,EAAE,YAAY,KAAK,OAAO,CAAC;CAC/C;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,2GAA2G;IAC3G,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,YAAY,CAC3B,KAAK,SAAS,aAAa,EAC3B,UAAU,SAAS;IAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE;IAE1C,kEAAkE;IAClE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,8DAA8D;IAC9D,QAAQ,CAAC,MAAM,EAAE,SAAS,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;IAC7C;;;;OAIG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;IAClD,QAAQ,CAAC,UAAU,EAAE,CAAC,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,KAAK,KAAK,UAAU,GAAG,SAAS,CAAC;IAClF,QAAQ,CAAC,QAAQ,EAAE;QAAE,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;QAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,CAAC;IACnF,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,MAAM,CAAC;IAC5C;;;;;OAKG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,KAAK,KAAK,MAAM,GAAG,SAAS,CAAC;CAC/E"}
@@ -0,0 +1,61 @@
1
+ import type { Input } from 'alchemy/Input';
2
+ import * as Effect from 'effect/Effect';
3
+ import type { MatchingProps, MatchingSpec } from './matching-types.ts';
4
+ /** Re-exported so `tag.ts`/`document-type.ts`/`storage-path.ts`/`custom-field.ts` keep importing the contract from `./matching.ts` — only the shape itself lives in `matching-types.ts`, split out to hold the file-size cap. */
5
+ export type { FieldSpec, MatchingError, MatchingProps, MatchingRequirements, MatchingSpec, } from './matching-types.ts';
6
+ export declare const matchingOperations: <Props extends MatchingProps, Attributes extends {
7
+ readonly id: number;
8
+ }>(spec: MatchingSpec<Props, Attributes>) => {
9
+ read: (props: Props) => Effect.Effect<Attributes | undefined, import("./credentials.ts").PaperlessCredentialsError | import("./errors.ts").PaperlessUnauthorized | import("./errors.ts").PaperlessUnavailable | import("./errors.ts").PaperlessValidation, import("./client.ts").PaperlessRequirements>;
10
+ diff: (news: Input<Props>, output: Attributes | undefined) => Effect.Effect<{
11
+ readonly action: 'update';
12
+ } | {
13
+ readonly action: 'noop';
14
+ } | {
15
+ readonly action: 'replace';
16
+ } | undefined, import("./credentials.ts").PaperlessCredentialsError | import("./errors.ts").PaperlessUnauthorized | import("./errors.ts").PaperlessUnavailable | import("./errors.ts").PaperlessValidation, import("./client.ts").PaperlessRequirements>;
17
+ reconcile: (news: Props, output: Attributes | undefined) => Effect.Effect<Attributes, import("./credentials.ts").PaperlessCredentialsError | import("./errors.ts").PaperlessError, import("./client.ts").PaperlessRequirements>;
18
+ /**
19
+ * ⛔ `output.id` FIRST, SAME SWITCH AS `fetchLive` (PR 163 re-review). The engine always
20
+ * passes `output` here too (`Apply.ts`'s delete call sites all carry `output: attr`) —
21
+ * `olds` is only the PROPS as last persisted, which drifts from the live object exactly
22
+ * the way `news` did pre-fix: rename it out of band (by hand, or by another tool) after
23
+ * the last successful reconcile, and a name-only locate silently finds nothing, `destroy`
24
+ * returns as if there were nothing to delete, and the engine drops the state row anyway —
25
+ * reporting "deleted" while the live object survives, permanently orphaned (the same
26
+ * consequence `matching.ts`'s header describes for the original bug, on the delete path
27
+ * the original fix didn't touch). A 404 on the `output.id` itself is NOT an error here,
28
+ * unlike `fetchLive`'s die — it means the object is already gone, which is exactly the
29
+ * idempotent success `delete` is supposed to report (S11, alchemy-provider-standard).
30
+ */
31
+ destroy: (olds: Props, output: Attributes | undefined) => Effect.Effect<void, import("./credentials.ts").PaperlessCredentialsError | import("./errors.ts").PaperlessUnauthorized | import("./errors.ts").PaperlessUnavailable | import("./errors.ts").PaperlessValidation, import("./client.ts").PaperlessRequirements>;
32
+ };
33
+ /** The five provider handlers for a spec'd Paperless taxonomy object, wired once. */
34
+ export declare const matchingHandlers: <Props extends MatchingProps, Attributes extends {
35
+ readonly id: number;
36
+ }>(spec: MatchingSpec<Props, Attributes>) => {
37
+ /** ⚠️ `[]` — Paperless index endpoints return the whole estate; adoption stays explicit. */
38
+ list: () => Effect.Effect<never[], never, never>;
39
+ read: ({ olds }: {
40
+ olds: Props;
41
+ }) => Effect.Effect<Attributes | undefined, import("./credentials.ts").PaperlessCredentialsError | import("./errors.ts").PaperlessUnauthorized | import("./errors.ts").PaperlessUnavailable | import("./errors.ts").PaperlessValidation, import("./client.ts").PaperlessRequirements>;
42
+ diff: ({ news, output }: {
43
+ news: Input<Props>;
44
+ output: Attributes | undefined;
45
+ }) => Effect.Effect<{
46
+ readonly action: 'update';
47
+ } | {
48
+ readonly action: 'noop';
49
+ } | {
50
+ readonly action: 'replace';
51
+ } | undefined, import("./credentials.ts").PaperlessCredentialsError | import("./errors.ts").PaperlessUnauthorized | import("./errors.ts").PaperlessUnavailable | import("./errors.ts").PaperlessValidation, import("./client.ts").PaperlessRequirements>;
52
+ reconcile: ({ news, output }: {
53
+ news: Props;
54
+ output?: Attributes | undefined;
55
+ }) => Effect.Effect<Attributes, import("./credentials.ts").PaperlessCredentialsError | import("./errors.ts").PaperlessError, import("./client.ts").PaperlessRequirements>;
56
+ delete: ({ olds, output }: {
57
+ olds: Props;
58
+ output?: Attributes | undefined;
59
+ }) => Effect.Effect<void, import("./credentials.ts").PaperlessCredentialsError | import("./errors.ts").PaperlessUnauthorized | import("./errors.ts").PaperlessUnavailable | import("./errors.ts").PaperlessValidation, import("./client.ts").PaperlessRequirements>;
60
+ };
61
+ //# sourceMappingURL=matching.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"matching.d.ts","sourceRoot":"","sources":["../../src/paperless/matching.ts"],"names":[],"mappings":"AAiDA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAMxC,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEvE,iOAAiO;AACjO,YAAY,EACV,SAAS,EACT,aAAa,EACb,aAAa,EACb,oBAAoB,EACpB,YAAY,GACb,MAAM,qBAAqB,CAAC;AAM7B,eAAO,MAAM,kBAAkB,GAC7B,KAAK,SAAS,aAAa,EAC3B,UAAU,SAAS;IAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,QAEpC,YAAY,CAAC,KAAK,EAAE,UAAU,CAAC;kBAOhB,KAAK;iBAuDX,KAAK,CAAC,KAAK,CAAC,UAAU,UAAU,GAAG,SAAS;yBAIZ,QAAQ;;yBAGP,MAAM;;yBAEjC,SAAS;;sBAIV,KAAK,UAAU,UAAU,GAAG,SAAS;IAqCvD;;;;;;;;;;;;OAYG;oBACa,KAAK,UAAU,UAAU,GAAG,SAAS;CAOxD,CAAC;AAEF,qFAAqF;AACrF,eAAO,MAAM,gBAAgB,GAC3B,KAAK,SAAS,aAAa,EAC3B,UAAU,SAAS;IAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,QAEpC,YAAY,CAAC,KAAK,EAAE,UAAU,CAAC;IAInC,4FAA4F;IAC5F,IAAI;IACJ,IAAI,aAAa;QAAE,IAAI,EAAE,KAAK,CAAA;KAAE;IAIhC,IAAI,qBAAqB;QAAE,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;QAAC,MAAM,EAAE,UAAU,GAAG,SAAS,CAAA;KAAE;yBAnFlC,QAAQ;;yBAGP,MAAM;;yBAEjC,SAAS;;IAqF5B,SAAS,qBAAqB;QAAE,IAAI,EAAE,KAAK,CAAC;QAAC,MAAM,CAAC,EAAE,UAAU,GAAG,SAAS,CAAA;KAAE;IAI9E,MAAM,qBAAqB;QAAE,IAAI,EAAE,KAAK,CAAC;QAAC,MAAM,CAAC,EAAE,UAAU,GAAG,SAAS,CAAA;KAAE;CAG9E,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * All four Paperless taxonomy providers, one credentials layer shared between them.
3
+ *
4
+ * Layer.mergeAll(paperlessProviders(), …the stack's other providers)
5
+ *
6
+ * ⚠️ `HttpClient.HttpClient` IS NOT BUNDLED HERE, and that follows `../netbox/index.ts` and
7
+ * `../forgejo/client.ts`'s own precedent (its header: "the idiom Alchemy's own providers
8
+ * follow — `Hetzner/Providers.ts` builds on `FetchHttpClient.layer`") rather than diverging
9
+ * from it: the transport is the consuming STACK's choice (`FetchHttpClient.layer` in
10
+ * production, a fake `HttpClient` in a test), not a house default a provider file should pin.
11
+ */
12
+ import * as Layer from 'effect/Layer';
13
+ import { type PaperlessCredentials } from './credentials.ts';
14
+ export declare const paperlessProviders: (credentials?: Layer.Layer<PaperlessCredentials>) => Layer.Layer<import("alchemy").Provider<import("./custom-field.ts").CustomField> | import("alchemy").Provider<import("./document-type.ts").DocumentType> | import("alchemy").Provider<import("./storage-path.ts").StoragePath> | import("alchemy").Provider<import("./tag.ts").Tag>, never, import("effect/unstable/http/HttpClient").HttpClient>;
15
+ //# sourceMappingURL=providers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"providers.d.ts","sourceRoot":"","sources":["../../src/paperless/providers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AAGtC,OAAO,EAAE,KAAK,oBAAoB,EAAoB,MAAM,kBAAkB,CAAC;AAI/E,eAAO,MAAM,kBAAkB,iBAChB,KAAK,CAAC,KAAK,CAAC,oBAAoB,CAAC,qVAOZ,CAAC"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * `Paperless.StoragePath` — a Paperless-ngx storage path template, create-or-update and never
3
+ * deleted. Same shape as `document-type.ts` plus the vendor-required `path` template string.
4
+ */
5
+ import { Resource } from 'alchemy';
6
+ import * as Provider from 'alchemy/Provider';
7
+ import type { PaperlessStoragePathRequest } from './generated/types/storage-paths.ts';
8
+ import { type MatchingProps, type MatchingRequirements } from './matching.ts';
9
+ export interface StoragePathProps extends MatchingProps {
10
+ /** ⚠️ maxLength 128 (generated constraint table). The locate key. */
11
+ name: string;
12
+ /** ⚠️ minLength 1 (generated table). Paperless's storage-path template syntax, e.g. `{correspondent}/{title}`. */
13
+ path: string;
14
+ /** ⚠️ maxLength 256. */
15
+ match?: string;
16
+ matchingAlgorithm?: PaperlessStoragePathRequest['matching_algorithm'];
17
+ isInsensitive?: boolean;
18
+ }
19
+ export interface StoragePathAttributes {
20
+ id: number;
21
+ name: string;
22
+ path: string;
23
+ match: string;
24
+ matchingAlgorithm: number;
25
+ isInsensitive: boolean;
26
+ owner: number | undefined;
27
+ }
28
+ export interface StoragePath extends Resource<'Paperless.StoragePath', StoragePathProps, StoragePathAttributes, never, MatchingRequirements> {
29
+ }
30
+ export declare const StoragePath: import("alchemy").ResourceClass<StoragePath>;
31
+ export declare const StoragePathProvider: () => import("effect/Layer").Layer<Provider.Provider<StoragePath>, never, import("./client.ts").PaperlessRequirements>;
32
+ //# sourceMappingURL=storage-path.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage-path.d.ts","sourceRoot":"","sources":["../../src/paperless/storage-path.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAE7C,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,oCAAoC,CAAC;AACtF,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,oBAAoB,EAAoB,MAAM,eAAe,CAAC;AAEhG,MAAM,WAAW,gBAAiB,SAAQ,aAAa;IACrD,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC;IACb,kHAAkH;IAClH,IAAI,EAAE,MAAM,CAAC;IACb,wBAAwB;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iBAAiB,CAAC,EAAE,2BAA2B,CAAC,oBAAoB,CAAC,CAAC;IACtE,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,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,WAAY,SAAQ,QAAQ,CAC3C,uBAAuB,EACvB,gBAAgB,EAChB,qBAAqB,EACrB,KAAK,EACL,oBAAoB,CACrB;CAAG;AAEJ,eAAO,MAAM,WAAW,8CAEtB,CAAC;AAmCH,eAAO,MAAM,mBAAmB,wHACiD,CAAC"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * `Paperless.Tag` — a Paperless-ngx tag, create-or-update and never deleted by default.
3
+ *
4
+ * ★ `matchingAlgorithm` AND THE OTHER ENUM-SHAPED FIELDS ARE INDEXED OFF THE GENERATED REQUEST
5
+ * TYPE (`PaperlessTagRequest['matching_algorithm']`), NEVER RETYPED. The literal union `0 | 1 |
6
+ * … | 6` comes from `codegen/paperless.ts`'s reading of the vendor's own `MatchingAlgorithm`
7
+ * enum — copying it by hand here would be exactly the guess `schema-provenance` doctrine
8
+ * forbids, and a vendor that adds a seventh algorithm would leave this type silently stale.
9
+ *
10
+ * ⛔ NO CREDENTIAL IS A PROP. `PAPERLESS_URL`/`PAPERLESS_TOKEN` come from `credentials.ts`.
11
+ * ⛔ `defaultRemovalPolicy: 'retain'` — the reconciler this replaces never deletes taxonomy, and
12
+ * a tag's id is referenced by every document it is attached to.
13
+ */
14
+ import { Resource } from 'alchemy';
15
+ import * as Provider from 'alchemy/Provider';
16
+ import type { PaperlessTagRequest } from './generated/types/tags.ts';
17
+ import { type MatchingProps, type MatchingRequirements } from './matching.ts';
18
+ export interface TagProps extends MatchingProps {
19
+ /** ⚠️ maxLength 128 (generated constraint table). The locate key — see matching.ts's header. */
20
+ name: string;
21
+ /** ⚠️ minLength 1, maxLength 7 (a CSS hex color, generated table doesn't enforce the `#`). */
22
+ color?: string;
23
+ /** ⚠️ maxLength 256. The text or pattern documents are matched against. */
24
+ match?: string;
25
+ matchingAlgorithm?: PaperlessTagRequest['matching_algorithm'];
26
+ isInsensitive?: boolean;
27
+ /** Marks this tag as an inbox tag: newly consumed documents are tagged with it. */
28
+ isInboxTag?: boolean;
29
+ /** Another `Paperless.Tag`'s id — Paperless allows one level of tag nesting. */
30
+ parent?: number;
31
+ }
32
+ export interface TagAttributes {
33
+ id: number;
34
+ name: string;
35
+ color: string;
36
+ match: string;
37
+ matchingAlgorithm: number;
38
+ isInsensitive: boolean;
39
+ isInboxTag: boolean;
40
+ owner: number | undefined;
41
+ parent: number | undefined;
42
+ }
43
+ export interface Tag extends Resource<'Paperless.Tag', TagProps, TagAttributes, never, MatchingRequirements> {
44
+ }
45
+ export declare const Tag: import("alchemy").ResourceClass<Tag>;
46
+ export declare const TagProvider: () => import("effect/Layer").Layer<Provider.Provider<Tag>, never, import("./client.ts").PaperlessRequirements>;
47
+ //# sourceMappingURL=tag.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tag.d.ts","sourceRoot":"","sources":["../../src/paperless/tag.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAE7C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,oBAAoB,EAAoB,MAAM,eAAe,CAAC;AAEhG,MAAM,WAAW,QAAS,SAAQ,aAAa;IAC7C,gGAAgG;IAChG,IAAI,EAAE,MAAM,CAAC;IACb,8FAA8F;IAC9F,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2EAA2E;IAC3E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iBAAiB,CAAC,EAAE,mBAAmB,CAAC,oBAAoB,CAAC,CAAC;IAC9D,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,mFAAmF;IACnF,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,gFAAgF;IAChF,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,iBAAiB,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,OAAO,CAAC;IACvB,UAAU,EAAE,OAAO,CAAC;IACpB,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5B;AAED,MAAM,WAAW,GAAI,SAAQ,QAAQ,CACnC,eAAe,EACf,QAAQ,EACR,aAAa,EACb,KAAK,EACL,oBAAoB,CACrB;CAAG;AAEJ,eAAO,MAAM,GAAG,sCAAqE,CAAC;AAoCtF,eAAO,MAAM,WAAW,gHAAwE,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"}