@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,79 @@
1
+ /**
2
+ * `Postgres.Database` props and attributes, and the one prop-level check that runs before any
3
+ * statement: the name's byte length against `NAMEDATALEN`.
4
+ *
5
+ * ⛔ EVERY ASSERTED PROP HERE HAS A CITED COLUMN. `docs/postgres.md#mapping` and
6
+ * `database-provenance.test.ts` are what make that true rather than asserted: the test parses
7
+ * the two committed fixtures (`create_database.sgml`'s synopsis, `pg_database.h`'s struct) and
8
+ * fails if a prop here has no synopsis option or no catalog column behind it.
9
+ * ⛔ NO PASSWORD, EVER. A database has none in `pg_database` (S25) — `CREATE DATABASE` has no
10
+ * password option at all (`gram.y@REL_18_6#createdb_opt_name`, measured: `IDENT | CONNECTION
11
+ * LIMIT | ENCODING | LOCATION | OWNER | TABLESPACE | TEMPLATE`).
12
+ */
13
+ /** NAMEDATALEN (`pg_config_manual.h@REL_18_6` line 29) minus the C string terminator: the
14
+ * longest name the server stores without truncating. */
15
+ export declare const POSTGRES_NAME_MAX_BYTES = 63;
16
+ /** UTF-8 byte length, the same unit `NAMEDATALEN` counts in (`scansup.c@REL_18_6#truncate_identifier`
17
+ * clips by `pg_mbcliplen`, a byte-oriented, multibyte-aware clip — never a JS `.length`, which
18
+ * counts UTF-16 code units and would pass a 63-byte multibyte name that is really 70+ bytes, or
19
+ * refuse a 63-byte name whose `.length` looks smaller). */
20
+ export declare const utf8ByteLength: (value: string) => number;
21
+ /**
22
+ * Declared, at-most-once-asserted properties of one database.
23
+ *
24
+ * Every prop here maps to a `CREATE DATABASE` `WITH` option `create_database.sgml` documents
25
+ * AND a `pg_database` column that stores it (`database-provenance.test.ts`). `TEMPLATE`,
26
+ * `STRATEGY` and `OID` are excluded because nothing in `pg_database` reads them back once the
27
+ * database exists; `ICU_RULES` and `COLLATION_VERSION` are excluded for having no consumer in
28
+ * this family today. See `docs/postgres.md#excluded`.
29
+ */
30
+ export interface PostgresDatabaseProps {
31
+ /** At most 63 UTF-8 bytes — refused at plan past that (`PostgresDatabaseNameRefused`). The
32
+ * identity: a name change is a plan-time refusal, never a replace. */
33
+ readonly name: string;
34
+ /** A role name in `pg_roles`, asserted before create (`PostgresDatabaseOwnerMissing`) and
35
+ * compared against `pg_get_userbyid(datdba)` on every plan thereafter. */
36
+ readonly owner: string;
37
+ /** `pg_encoding_to_char(datdatabase.encoding)`, e.g. `"UTF8"`. @default the template's. */
38
+ readonly encoding?: string;
39
+ /** `'builtin' | 'icu' | 'libc'` — `pg_database.datlocprovider` decoded
40
+ * (`pg_collation.h@REL_18_6#collprovider_name`). @default the template's. */
41
+ readonly localeProvider?: 'builtin' | 'icu' | 'libc';
42
+ /** `pg_database.datcollate`. @default the template's. */
43
+ readonly lcCollate?: string;
44
+ /** `pg_database.datctype`. @default the template's. */
45
+ readonly lcCtype?: string;
46
+ /** `pg_database.datallowconn`. @default `true`. */
47
+ readonly allowConnections?: boolean;
48
+ /** `pg_database.datconnlimit`; `-1` means unlimited. @default `-1`. */
49
+ readonly connectionLimit?: number;
50
+ /** `pg_database.datistemplate`. @default `false`. */
51
+ readonly isTemplate?: boolean;
52
+ /** A tablespace name in `pg_tablespace`. @default the template's (usually `pg_default`). */
53
+ readonly tablespace?: string;
54
+ }
55
+ /** Live attributes, read back after every reconcile. No `password` — see the file header. */
56
+ export interface PostgresDatabaseAttributes {
57
+ readonly name: string;
58
+ readonly oid: number;
59
+ readonly owner: string;
60
+ readonly encoding: string;
61
+ readonly localeProvider: 'builtin' | 'icu' | 'libc';
62
+ readonly collate: string;
63
+ readonly ctype: string;
64
+ readonly allowConnections: boolean;
65
+ readonly connectionLimit: number;
66
+ readonly isTemplate: boolean;
67
+ readonly tablespace: string;
68
+ }
69
+ /**
70
+ * Refuse a name the server would truncate, before any statement is built.
71
+ *
72
+ * ★ CALLED FROM `diff` (so a plan fails, never a deploy) AND FROM `reconcile` (so a fake or a
73
+ * direct call gets the same refusal, never just a broken statement).
74
+ */
75
+ export declare const nameByteRefusal: (name: string) => {
76
+ readonly byteLength: number;
77
+ readonly limit: number;
78
+ } | undefined;
79
+ //# sourceMappingURL=database-attrs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database-attrs.d.ts","sourceRoot":"","sources":["../../src/postgres/database-attrs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH;wDACwD;AACxD,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAE1C;;;2DAG2D;AAC3D,eAAO,MAAM,cAAc,UAAW,MAAM,KAAG,MAAoD,CAAC;AAEpG;;;;;;;;GAQG;AACH,MAAM,WAAW,qBAAqB;IACpC;0EACsE;IACtE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;8EAC0E;IAC1E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,2FAA2F;IAC3F,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B;iFAC6E;IAC7E,QAAQ,CAAC,cAAc,CAAC,EAAE,SAAS,GAAG,KAAK,GAAG,MAAM,CAAC;IACrD,yDAAyD;IACzD,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,uDAAuD;IACvD,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,mDAAmD;IACnD,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IACpC,uEAAuE;IACvE,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,qDAAqD;IACrD,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAC9B,4FAA4F;IAC5F,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,6FAA6F;AAC7F,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,cAAc,EAAE,SAAS,GAAG,KAAK,GAAG,MAAM,CAAC;IACpD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAC;IACnC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;GAKG;AACH,eAAO,MAAM,eAAe,SACpB,MAAM,KACX;IAAE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,SAK5D,CAAC"}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * The four statements `Postgres.Database` ever issues, and the quoting each one needs.
3
+ *
4
+ * ⛔ `CREATE DATABASE` TAKES NO BIND PARAMETERS FOR ANY OF ITS VALUES, INCLUDING `OWNER`.
5
+ * Measured at `gram.y@REL_18_6`: `CreatedbStmt: CREATE DATABASE name opt_with
6
+ * createdb_opt_list`, and every `createdb_opt_item` value is `NumericOnly`,
7
+ * `opt_boolean_or_string` or the bare keyword `DEFAULT` — none of those productions reaches
8
+ * `PARAM` (`$n`). `sql\`…\`` cannot help here; every free-form value in this statement is
9
+ * escaped by hand, once, in this file, and the assembled text runs through `.unsafe()`.
10
+ * ⚠️ THE DATABASE NAME AND EFFECT'S OWN `sql(value)` IDENTIFIER HELPER DO NOT MIX. `name` is a
11
+ * `ColId` (`gram.y@REL_18_6` line 17327), a single token — but `Statement.defaultEscape('"')`
12
+ * (what `sql(value)` compiles to) also rewrites every `.` in the value to `"."`, because it is
13
+ * built for a *qualified* name like `schema.table`. A database named `"my.app"` would come out
14
+ * as `"my"."app"` — two tokens where `CreatedbStmt` parses exactly one — a syntax error the
15
+ * engine would blame on this provider. `quoteIdent` below is the single-token form: it quotes
16
+ * and doubles embedded `"`, and nothing else.
17
+ * ★ EVERY OTHER `WITH` VALUE (`OWNER`, `TABLESPACE`, `ENCODING`, `LOCALE_PROVIDER`,
18
+ * `LC_COLLATE`, `LC_CTYPE`) IS A STRING LITERAL, NOT AN IDENTIFIER. Measured at the same tag:
19
+ * `createdb_opt_item`'s string branch is `opt_boolean_or_string → NonReservedWord_or_Sconst →
20
+ * NonReservedWord | Sconst`. A locale like `en_US.UTF-8` would hit the same dot-splitting
21
+ * `quoteIdent` avoids if it were identifier-quoted; a plain `Sconst` (`quoteStringLiteral`) has
22
+ * no such rule. This rests on `standard_conforming_strings = on` (the PG18 default, and
23
+ * measured live 2026-09-23 — `docs/postgres.md#measured`); the escaper only ever doubles `'`
24
+ * and never emits a backslash, so it never depends on which way that setting points.
25
+ */
26
+ import type { SqlError } from 'effect/unstable/sql/SqlError';
27
+ import * as Effect from 'effect/Effect';
28
+ import type { PostgresDatabaseAttributes, PostgresDatabaseProps } from './database-attrs.ts';
29
+ /** What every function in this file needs from a client — real (`PgClient.PgClient`) or fake
30
+ * (`fake-sql.ts`). Deliberately smaller than `SqlClient`: nothing here ever needs the tagged
31
+ * template, transactions or streaming, because every statement is either bound params through
32
+ * `.unsafe(text, params)` or a hand-escaped literal through `.unsafe(text)`. */
33
+ export interface PgExecutor {
34
+ readonly unsafe: <A extends object>(sql: string, params?: ReadonlyArray<unknown>) => Effect.Effect<ReadonlyArray<A>, SqlError>;
35
+ }
36
+ /** Single-token identifier quoting: wrap in `"`, double any embedded `"`. No dot-splitting —
37
+ * see the file header. Used for the database name and, since a role or tablespace name is a
38
+ * `NonReservedWord` too, for `OWNER` and `TABLESPACE`. */
39
+ export declare const quoteIdent: (value: string) => string;
40
+ /** SQL string-literal quoting: wrap in `'`, double any embedded `'`. Used for every `WITH`
41
+ * value that is locale or encoding text, never for an identifier (see the file header). */
42
+ export declare const quoteStringLiteral: (value: string) => string;
43
+ /**
44
+ * Build the exact `CREATE DATABASE` text — a pure function, unit-tested on its own with no
45
+ * client at all. `CONNECTION LIMIT` and the two booleans are never string-escaped: an integer is
46
+ * `Math.trunc`ed and stringified, and a boolean is one of exactly two literal words this file
47
+ * writes, so no declared value reaches the statement through string interpolation unescaped.
48
+ */
49
+ export declare const buildCreateDatabaseSql: (props: PostgresDatabaseProps) => string;
50
+ /** `42P04` (`duplicate_database`) is a race: something else created the name between our read
51
+ * and our create. Measured at `internal/sqlError.ts@effect/sql-pg 4.0.0-rc.115`: every SQLSTATE
52
+ * starting `42` — this one included — classifies as `SqlSyntaxError`, the same tag a genuine
53
+ * syntax error would raise; there is no more specific tag to `catchTag` on. So this reads the
54
+ * raw code PostgreSQL sent, carried on `reason.cause.code` (`PgConnection.ts#errorFromFields`,
55
+ * `Object.assign(new Error(...), fields)`), rather than trusting the tag alone. */
56
+ export declare const isDuplicateDatabaseRace: (error: SqlError) => boolean;
57
+ /** `OWNER` is checked against `pg_roles` before any `CREATE`, so a missing role is
58
+ * `PostgresDatabaseOwnerMissing` at reconcile time rather than the server's own
59
+ * `ERROR: role "…" does not exist` surfacing as an unclassified statement failure. */
60
+ export declare const roleExists: (pg: PgExecutor, role: string) => Effect.Effect<boolean, SqlError>;
61
+ /** The read: one bound `SELECT` on `pg_database`, joined to the two functions that turn its raw
62
+ * owner and encoding columns into names (`docs/postgres.md#mapping`). `undefined` when the row
63
+ * is absent — ownership branding happens one layer up, in `database.ts`, not here. */
64
+ export declare const selectDatabase: (pg: PgExecutor, name: string) => Effect.Effect<PostgresDatabaseAttributes | undefined, SqlError>;
65
+ /** Compare every declared, asserted prop against the live row. Returns the name of the first
66
+ * prop that drifted, or `undefined` when every declared prop matches. `name` and `owner` are
67
+ * compared by their callers (identity and the create-time assertion), never here. */
68
+ export declare const firstDrift: (props: PostgresDatabaseProps, live: PostgresDatabaseAttributes) => {
69
+ readonly prop: string;
70
+ readonly declared: unknown;
71
+ readonly live: unknown;
72
+ } | undefined;
73
+ //# sourceMappingURL=database-sql.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database-sql.d.ts","sourceRoot":"","sources":["../../src/postgres/database-sql.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,EAAE,0BAA0B,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAE7F;;;gFAGgF;AAChF,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,SAAS,MAAM,EAChC,GAAG,EAAE,MAAM,EACX,MAAM,CAAC,EAAE,aAAa,CAAC,OAAO,CAAC,KAC5B,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;CAChD;AAED;;0DAE0D;AAC1D,eAAO,MAAM,UAAU,UAAW,MAAM,KAAG,MAA0C,CAAC;AAEtF;2FAC2F;AAC3F,eAAO,MAAM,kBAAkB,UAAW,MAAM,KAAG,MAA0C,CAAC;AAuB9F;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,UAAW,qBAAqB,KAAG,MACE,CAAC;AAEzE;;;;;mFAKmF;AACnF,eAAO,MAAM,uBAAuB,UAAW,QAAQ,KAAG,OAMzD,CAAC;AAIF;;sFAEsF;AACtF,eAAO,MAAM,UAAU,OAAQ,UAAU,QAAQ,MAAM,KAAG,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,QAAQ,CAItF,CAAC;AAwCJ;;sFAEsF;AACtF,eAAO,MAAM,cAAc,OACrB,UAAU,QACR,MAAM,KACX,MAAM,CAAC,MAAM,CAAC,0BAA0B,GAAG,SAAS,EAAE,QAAQ,CACkC,CAAC;AAEpG;;qFAEqF;AACrF,eAAO,MAAM,UAAU,UACd,qBAAqB,QACtB,0BAA0B,KAC/B;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAAG,SAgBlF,CAAC"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * `Postgres.Database` — create-and-assert over a self-hosted PostgreSQL 18 cluster.
3
+ *
4
+ * ⛔ THIS IS NOT AN ALTER-CAPABLE RESOURCE. Every optional prop is asserted ONCE, at create, and
5
+ * compared against the live row on every later plan; a mismatch is a typed
6
+ * `PostgresDatabaseDrift` refusal, never an `ALTER DATABASE`. `docs/postgres.md` says why:
7
+ * several of these props (`LC_COLLATE`, `LC_CTYPE`, `LOCALE_PROVIDER`) cannot be altered on a
8
+ * live database at all without `ALTER … REFRESH COLLATION VERSION` semantics this family does
9
+ * not implement, and the rest are left alone on principle rather than by accident.
10
+ * ⛔ `delete` NEVER DROPS. See `errors.ts#PostgresDatabaseDropRefused` and
11
+ * `defaultRemovalPolicy: 'retain'` below — two independent reasons the engine never issues
12
+ * `DROP DATABASE` through this provider.
13
+ * ★ A DATABASE CARRIES NO OWNERSHIP MARK (H1). `read` always answers `Unowned` for a match, the
14
+ * same rule `Cloudflare.Snippets.Snippet` uses for a marker-less API — so every one of the
15
+ * mini's 19 already-live databases needs `adopt(true)` in the stack that declares it.
16
+ */
17
+ import { Resource } from 'alchemy';
18
+ import type { Input } from 'alchemy/Input';
19
+ import * as Provider from 'alchemy/Provider';
20
+ import * as Effect from 'effect/Effect';
21
+ import type { SqlError } from 'effect/unstable/sql/SqlError';
22
+ import type { PostgresDatabaseAttributes, PostgresDatabaseProps } from './database-attrs.ts';
23
+ import { type PostgresConnection } from './connection.ts';
24
+ import type { PgExecutor } from './database-sql.ts';
25
+ import { PostgresDatabaseCreateVanished, PostgresDatabaseDrift, PostgresDatabaseNameRefused, PostgresDatabaseOwnerMissing, PostgresDatabaseRenameRefused } from './errors.ts';
26
+ export interface PostgresDatabase extends Resource<'Postgres.Database', PostgresDatabaseProps, PostgresDatabaseAttributes, never, PostgresConnection> {
27
+ }
28
+ export declare const PostgresDatabase: import("alchemy").ResourceClass<PostgresDatabase>;
29
+ /** Mirrors `alchemy/Resource`'s own `isResourceOfType`: a resource constructor is a callable
30
+ * `Object.assign`ed function, not a plain object, so `typeof` must accept both — a bare
31
+ * `'object'` check (measured live in `scripts/smoke.ts`: it rejects `PostgresDatabase` itself,
32
+ * matching only an instance) is the regression this comment exists to prevent. */
33
+ export declare const isPostgresDatabase: (value: unknown) => value is PostgresDatabase;
34
+ /**
35
+ * The core of `reconcile`, against any {@link PgExecutor} — the real pooled client through
36
+ * `withPg`, or `fake-sql.ts`'s recording fake in tests. Kept apart from the provider wiring
37
+ * below so a lifecycle test drives this exact function, not a re-implementation of it.
38
+ */
39
+ export declare const reconcileWithClient: (pg: PgExecutor, props: PostgresDatabaseProps) => Effect.Effect<PostgresDatabaseAttributes, PostgresDatabaseOwnerMissing | PostgresDatabaseDrift | PostgresDatabaseCreateVanished | SqlError>;
40
+ /** The core of `read`: one bound `SELECT`, no ownership branding — the caller (the provider's
41
+ * `read` below, or a test) decides whether to wrap a match in `Unowned`. */
42
+ export declare const readWithClient: (pg: PgExecutor, name: string) => Effect.Effect<PostgresDatabaseAttributes | undefined, SqlError, never>;
43
+ /**
44
+ * The core of `diff`, entirely offline: `news` (declared) against `output` (last-applied
45
+ * attributes) — never a live query (that authoritative check is `reconcileWithClient`'s job, per
46
+ * S10). A rename is a plan-time refusal; over-long names are too, so a bad name never even reaches
47
+ * `reconcile`; every other change answers `update`, which `reconcileWithClient` then either
48
+ * applies (create) or refuses (drift on an already-live database).
49
+ */
50
+ export declare const diffPostgresDatabase: (news: Input<PostgresDatabaseProps>, output: PostgresDatabaseAttributes | undefined) => Effect.Effect<{
51
+ readonly action: 'update';
52
+ } | {
53
+ readonly action: 'noop';
54
+ } | undefined, PostgresDatabaseNameRefused | PostgresDatabaseRenameRefused, never>;
55
+ /**
56
+ * The five lifecycle handlers, wired to `withPg` for anything that touches a client. Exported
57
+ * on its own — not only through {@link PostgresDatabaseProvider}'s `Layer` — so a test can call
58
+ * `postgresDatabaseHandlers.delete(...)` directly, against the real implementation, without
59
+ * standing up the engine's provider machinery.
60
+ */
61
+ export declare const postgresDatabaseHandlers: Provider.ProviderService<PostgresDatabase, PostgresConnection, never, never, PostgresConnection, never, never, never, never>;
62
+ export declare const PostgresDatabaseProvider: () => import("effect/Layer").Layer<Provider.Provider<PostgresDatabase>, never, PostgresConnection>;
63
+ //# sourceMappingURL=database.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/postgres/database.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAGnC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,KAAK,EAAE,0BAA0B,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAS7F,OAAO,EAAE,KAAK,kBAAkB,EAAU,MAAM,iBAAiB,CAAC;AAClE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EACL,8BAA8B,EAC9B,qBAAqB,EAErB,2BAA2B,EAC3B,4BAA4B,EAC5B,6BAA6B,EAC9B,MAAM,aAAa,CAAC;AAErB,MAAM,WAAW,gBAAiB,SAAQ,QAAQ,CAChD,mBAAmB,EACnB,qBAAqB,EACrB,0BAA0B,EAC1B,KAAK,EACL,kBAAkB,CACnB;CAAG;AAEJ,eAAO,MAAM,gBAAgB,mDAE3B,CAAC;AAEH;;;kFAGkF;AAClF,eAAO,MAAM,kBAAkB,UAAW,OAAO,KAAG,KAAK,IAAI,gBAGD,CAAC;AAY7D;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,OAC1B,UAAU,SACP,qBAAqB,KAC3B,MAAM,CAAC,MAAM,CACd,0BAA0B,EAC1B,4BAA4B,GAAG,qBAAqB,GAAG,8BAA8B,GAAG,QAAQ,CAqC9F,CAAC;AAEL;4EAC4E;AAC5E,eAAO,MAAM,cAAc,OAAQ,UAAU,QAAQ,MAAM,2EAA6B,CAAC;AAEzF;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,SACzB,KAAK,CAAC,qBAAqB,CAAC,UAC1B,0BAA0B,GAAG,SAAS;qBAgBhB,QAAQ;;qBAA0B,MAAM;kFACpE,CAAC;AAEL;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,8HA4BnC,CAAC;AAEH,eAAO,MAAM,wBAAwB,oGACyB,CAAC"}
@@ -0,0 +1,86 @@
1
+ declare const PostgresDatabaseNameRefused_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
2
+ readonly _tag: "PostgresDatabaseNameRefused";
3
+ } & Readonly<A>;
4
+ /**
5
+ * A declared name would be silently truncated by the server (`NAMEDATALEN` 64, so 63 UTF-8
6
+ * bytes is the last one that survives whole) — refused at plan, before any statement runs.
7
+ *
8
+ * ⛔ WHY THIS IS A PLAN-TIME REFUSAL AND NOT A SERVER ERROR. `truncate_identifier`
9
+ * (`scansup.c@REL_18_6` line 93) clips a long name to 63 bytes and raises only a `NOTICE`
10
+ * (`errcode(ERRCODE_NAME_TOO_LONG)`), never an error. A create would succeed against the
11
+ * TRUNCATED name, so the next plan's read (`WHERE datname = $1`, the name exactly as
12
+ * declared) would find nothing and try to create it again — forever. See
13
+ * `doc/postgres.md#name`.
14
+ */
15
+ export declare class PostgresDatabaseNameRefused extends PostgresDatabaseNameRefused_base<{
16
+ readonly name: string;
17
+ readonly byteLength: number;
18
+ readonly limit: number;
19
+ }> {
20
+ get message(): string;
21
+ }
22
+ declare const PostgresDatabaseRenameRefused_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
23
+ readonly _tag: "PostgresDatabaseRenameRefused";
24
+ } & Readonly<A>;
25
+ /** A logical id's `name` changed. A database's name is its identity; there is no `ALTER … RENAME`
26
+ * in this family's vocabulary (see `docs/postgres.md`), and `diff` never turns this into a
27
+ * `replace` — a replace here is `DROP DATABASE` before `CREATE DATABASE`, on data. */
28
+ export declare class PostgresDatabaseRenameRefused extends PostgresDatabaseRenameRefused_base<{
29
+ readonly from: string;
30
+ readonly to: string;
31
+ }> {
32
+ get message(): string;
33
+ }
34
+ declare const PostgresDatabaseOwnerMissing_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
35
+ readonly _tag: "PostgresDatabaseOwnerMissing";
36
+ } & Readonly<A>;
37
+ /** `OWNER` names a role `pg_roles` does not have, checked before the `CREATE DATABASE` that
38
+ * would otherwise fail on it. Postgres itself refuses the same case with `ERROR: role "…" does
39
+ * not exist` (`createdb()`, `dbcommands.c`) — this tag exists so the refusal is typed and the
40
+ * check runs before any statement, not caught from the driver's text. */
41
+ export declare class PostgresDatabaseOwnerMissing extends PostgresDatabaseOwnerMissing_base<{
42
+ readonly database: string;
43
+ readonly owner: string;
44
+ }> {
45
+ get message(): string;
46
+ }
47
+ declare const PostgresDatabaseDrift_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
48
+ readonly _tag: "PostgresDatabaseDrift";
49
+ } & Readonly<A>;
50
+ /** A live, asserted property no longer matches the declaration. `reconcile` never issues an
51
+ * `ALTER DATABASE` to fix this — see `docs/postgres.md` for why each asserted prop stops at
52
+ * create. */
53
+ export declare class PostgresDatabaseDrift extends PostgresDatabaseDrift_base<{
54
+ readonly database: string;
55
+ readonly prop: string;
56
+ readonly declared: unknown;
57
+ readonly live: unknown;
58
+ }> {
59
+ get message(): string;
60
+ }
61
+ declare const PostgresDatabaseDropRefused_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
62
+ readonly _tag: "PostgresDatabaseDropRefused";
63
+ } & Readonly<A>;
64
+ /** `delete` never drops a database. `DROP DATABASE` is destructive and irreversible on data
65
+ * this family did not create from nothing (most declared databases here are adopted, not
66
+ * created); the decision to remove one stays a human act on the host. */
67
+ export declare class PostgresDatabaseDropRefused extends PostgresDatabaseDropRefused_base<{
68
+ readonly database: string;
69
+ }> {
70
+ get message(): string;
71
+ }
72
+ declare const PostgresDatabaseCreateVanished_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
73
+ readonly _tag: "PostgresDatabaseCreateVanished";
74
+ } & Readonly<A>;
75
+ /** `CREATE DATABASE` raised no error, but the immediate re-read (S10: never trust the write's
76
+ * own report) still finds nothing. Measured to have no real trigger in a correctly speaking
77
+ * client, but S20 forbids `Effect.die` here regardless — an invariant violation is a typed
78
+ * failure the caller can see and retry, never a defect that crashes the whole engine. */
79
+ export declare class PostgresDatabaseCreateVanished extends PostgresDatabaseCreateVanished_base<{
80
+ readonly database: string;
81
+ }> {
82
+ get message(): string;
83
+ }
84
+ export type PostgresDatabaseError = PostgresDatabaseNameRefused | PostgresDatabaseRenameRefused | PostgresDatabaseOwnerMissing | PostgresDatabaseDrift | PostgresDatabaseDropRefused | PostgresDatabaseCreateVanished;
85
+ export {};
86
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/postgres/errors.ts"],"names":[],"mappings":";;;AAcA;;;;;;;;;;GAUG;AACH,qBAAa,2BAA4B,SAAQ,iCAAgD;IAC/F,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB,CAAC;IACA,IAAa,OAAO,IAAI,MAAM,CAO7B;CACF;;;;AAED;;sFAEsF;AACtF,qBAAa,6BAA8B,SAAQ,mCAEjD;IACA,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;CACrB,CAAC;IACA,IAAa,OAAO,IAAI,MAAM,CAO7B;CACF;;;;AAED;;;yEAGyE;AACzE,qBAAa,4BAA6B,SAAQ,kCAAiD;IACjG,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB,CAAC;IACA,IAAa,OAAO,IAAI,MAAM,CAE7B;CACF;;;;AAED;;aAEa;AACb,qBAAa,qBAAsB,SAAQ,2BAA0C;IACnF,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;CACxB,CAAC;IACA,IAAa,OAAO,IAAI,MAAM,CAO7B;CACF;;;;AAED;;yEAEyE;AACzE,qBAAa,2BAA4B,SAAQ,iCAAgD;IAC/F,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B,CAAC;IACA,IAAa,OAAO,IAAI,MAAM,CAM7B;CACF;;;;AAED;;;yFAGyF;AACzF,qBAAa,8BAA+B,SAAQ,oCAElD;IACA,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B,CAAC;IACA,IAAa,OAAO,IAAI,MAAM,CAM7B;CACF;AAED,MAAM,MAAM,qBAAqB,GAC7B,2BAA2B,GAC3B,6BAA6B,GAC7B,4BAA4B,GAC5B,qBAAqB,GACrB,2BAA2B,GAC3B,8BAA8B,CAAC"}
@@ -0,0 +1,22 @@
1
+ import type { PostgresDatabaseAttributes } from './database-attrs.ts';
2
+ import type { PgExecutor } from './database-sql.ts';
3
+ export interface RecordedStatement {
4
+ readonly text: string;
5
+ readonly params: ReadonlyArray<unknown>;
6
+ }
7
+ export interface FakeSql extends PgExecutor {
8
+ readonly statements: ReadonlyArray<RecordedStatement>;
9
+ /** Mutable on purpose: a test seeds a row a "competing" statement would have produced (the
10
+ * `42P04` race case) before the fake ever sees it. */
11
+ readonly databases: Map<string, PostgresDatabaseAttributes>;
12
+ }
13
+ export interface FakeSqlOptions {
14
+ readonly roles?: ReadonlyArray<string>;
15
+ readonly databases?: ReadonlyArray<PostgresDatabaseAttributes>;
16
+ /** Fail the NEXT `CREATE DATABASE` with SQLSTATE `42P04` (duplicate_database), once, the way a
17
+ * concurrent creator racing this reconcile would — classified exactly as
18
+ * `@effect/sql-pg`'s own driver classifies it (`database-sql.ts`'s header). */
19
+ readonly raceNextCreate?: boolean;
20
+ }
21
+ export declare const makeFakeSql: (options?: FakeSqlOptions) => FakeSql;
22
+ //# sourceMappingURL=fake-sql.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fake-sql.d.ts","sourceRoot":"","sources":["../../src/postgres/fake-sql.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;AACtE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;CACzC;AAED,MAAM,WAAW,OAAQ,SAAQ,UAAU;IACzC,QAAQ,CAAC,UAAU,EAAE,aAAa,CAAC,iBAAiB,CAAC,CAAC;IACtD;0DACsD;IACtD,QAAQ,CAAC,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,0BAA0B,CAAC,CAAC;CAC7D;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACvC,QAAQ,CAAC,SAAS,CAAC,EAAE,aAAa,CAAC,0BAA0B,CAAC,CAAC;IAC/D;;mFAE+E;IAC/E,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC;CACnC;AAoCD,eAAO,MAAM,WAAW,aAAa,cAAc,KAAQ,OAkD1D,CAAC"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * PostgreSQL providers for Alchemy — `Postgres.Database`, create-and-assert over a self-hosted
3
+ * cluster, walked against PostgreSQL 18.6. See `docs/postgres.md`.
4
+ *
5
+ * ⛔ THIS BARREL IS THE PUBLIC API, deliberately smaller than the directory: `fake-sql.ts` is a
6
+ * test double, not something a consuming stack should import.
7
+ */
8
+ export type { PostgresConnectionConfig } from './connection.ts';
9
+ export { PostgresConnection, postgresConnection, withPg } from './connection.ts';
10
+ export type { PostgresDatabaseAttributes, PostgresDatabaseProps } from './database-attrs.ts';
11
+ export { POSTGRES_NAME_MAX_BYTES, nameByteRefusal, utf8ByteLength } from './database-attrs.ts';
12
+ export { buildCreateDatabaseSql, firstDrift, isDuplicateDatabaseRace, quoteIdent, quoteStringLiteral, roleExists, selectDatabase, } from './database-sql.ts';
13
+ export { PostgresDatabase, PostgresDatabaseProvider, isPostgresDatabase } from './database.ts';
14
+ export { PostgresDatabaseDrift, PostgresDatabaseDropRefused, PostgresDatabaseNameRefused, PostgresDatabaseOwnerMissing, PostgresDatabaseRenameRefused, type PostgresDatabaseError, } from './errors.ts';
15
+ export { postgresProviders } from './providers.ts';
16
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/postgres/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,YAAY,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;AAChE,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACjF,YAAY,EAAE,0BAA0B,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC7F,OAAO,EAAE,uBAAuB,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC/F,OAAO,EACL,sBAAsB,EACtB,UAAU,EACV,uBAAuB,EACvB,UAAU,EACV,kBAAkB,EAClB,UAAU,EACV,cAAc,GACf,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAC/F,OAAO,EACL,qBAAqB,EACrB,2BAA2B,EAC3B,2BAA2B,EAC3B,4BAA4B,EAC5B,6BAA6B,EAC7B,KAAK,qBAAqB,GAC3B,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,238 @@
1
+ // src/postgres/connection.ts
2
+ import * as PgClient from "@effect/sql-pg/PgClient";
3
+ import * as Context from "effect/Context";
4
+ import * as Effect from "effect/Effect";
5
+ import * as Layer from "effect/Layer";
6
+
7
+ class PostgresConnection extends Context.Service()("Postgres.Connection") {
8
+ }
9
+ var postgresConnection = (config) => Layer.succeed(PostgresConnection, Effect.succeed(config));
10
+ var withPg = (build) => Effect.gen(function* () {
11
+ const resolveConfig = yield* PostgresConnection;
12
+ const config = yield* resolveConfig;
13
+ return yield* Effect.provide(Effect.flatMap(PgClient.PgClient, build), PgClient.layer(config));
14
+ });
15
+ // src/postgres/database-attrs.ts
16
+ var POSTGRES_NAME_MAX_BYTES = 63;
17
+ var utf8ByteLength = (value) => new TextEncoder().encode(value).byteLength;
18
+ var nameByteRefusal = (name) => {
19
+ const byteLength = utf8ByteLength(name);
20
+ return byteLength > POSTGRES_NAME_MAX_BYTES ? { byteLength, limit: POSTGRES_NAME_MAX_BYTES } : undefined;
21
+ };
22
+ // src/postgres/database-sql.ts
23
+ import * as Effect2 from "effect/Effect";
24
+ var quoteIdent = (value) => `"${value.replace(/"/g, '""')}"`;
25
+ var quoteStringLiteral = (value) => `'${value.replace(/'/g, "''")}'`;
26
+ var withOptions = (props) => {
27
+ const parts = [`OWNER ${quoteIdent(props.owner)}`];
28
+ if (props.encoding !== undefined)
29
+ parts.push(`ENCODING ${quoteStringLiteral(props.encoding)}`);
30
+ if (props.localeProvider !== undefined) {
31
+ parts.push(`LOCALE_PROVIDER ${quoteStringLiteral(props.localeProvider)}`);
32
+ }
33
+ if (props.lcCollate !== undefined)
34
+ parts.push(`LC_COLLATE ${quoteStringLiteral(props.lcCollate)}`);
35
+ if (props.lcCtype !== undefined)
36
+ parts.push(`LC_CTYPE ${quoteStringLiteral(props.lcCtype)}`);
37
+ if (props.tablespace !== undefined)
38
+ parts.push(`TABLESPACE ${quoteIdent(props.tablespace)}`);
39
+ if (props.allowConnections !== undefined) {
40
+ parts.push(`ALLOW_CONNECTIONS ${props.allowConnections ? "true" : "false"}`);
41
+ }
42
+ if (props.connectionLimit !== undefined) {
43
+ parts.push(`CONNECTION LIMIT ${String(Math.trunc(props.connectionLimit))}`);
44
+ }
45
+ if (props.isTemplate !== undefined)
46
+ parts.push(`IS_TEMPLATE ${props.isTemplate ? "true" : "false"}`);
47
+ return parts.join(" ");
48
+ };
49
+ var buildCreateDatabaseSql = (props) => `CREATE DATABASE ${quoteIdent(props.name)} WITH ${withOptions(props)}`;
50
+ var isDuplicateDatabaseRace = (error) => {
51
+ if (error.reason._tag !== "SqlSyntaxError")
52
+ return false;
53
+ const cause = error.reason.cause;
54
+ return typeof cause === "object" && cause !== null && cause.code === "42P04";
55
+ };
56
+ var ROLE_EXISTS_SQL = "SELECT 1 AS present FROM pg_roles WHERE rolname = $1";
57
+ var roleExists = (pg, role) => Effect2.map(pg.unsafe(ROLE_EXISTS_SQL, [role]), (rows) => rows.length > 0);
58
+ var SELECT_DATABASE_SQL = `SELECT
59
+ d.oid AS oid,
60
+ d.datname AS name,
61
+ pg_get_userbyid(d.datdba) AS owner,
62
+ pg_encoding_to_char(d.encoding) AS encoding,
63
+ CASE d.datlocprovider
64
+ WHEN 'b' THEN 'builtin' WHEN 'i' THEN 'icu' WHEN 'c' THEN 'libc'
65
+ ELSE 'libc'
66
+ END AS "localeProvider",
67
+ d.datcollate AS collate,
68
+ d.datctype AS ctype,
69
+ d.datallowconn AS "allowConnections",
70
+ d.datconnlimit AS "connectionLimit",
71
+ d.datistemplate AS "isTemplate",
72
+ t.spcname AS tablespace
73
+ FROM pg_database d
74
+ JOIN pg_tablespace t ON t.oid = d.dattablespace
75
+ WHERE d.datname = $1`;
76
+ var selectDatabase = (pg, name) => Effect2.map(pg.unsafe(SELECT_DATABASE_SQL, [name]), (rows) => rows[0]);
77
+ var firstDrift = (props, live) => {
78
+ const checks = [
79
+ ["encoding", props.encoding, live.encoding],
80
+ ["localeProvider", props.localeProvider, live.localeProvider],
81
+ ["lcCollate", props.lcCollate, live.collate],
82
+ ["lcCtype", props.lcCtype, live.ctype],
83
+ ["allowConnections", props.allowConnections, live.allowConnections],
84
+ ["connectionLimit", props.connectionLimit, live.connectionLimit],
85
+ ["isTemplate", props.isTemplate, live.isTemplate],
86
+ ["tablespace", props.tablespace, live.tablespace]
87
+ ];
88
+ for (const [prop, declared, liveValue] of checks) {
89
+ if (declared !== undefined && declared !== liveValue)
90
+ return { prop, declared, live: liveValue };
91
+ }
92
+ return;
93
+ };
94
+ // src/postgres/database.ts
95
+ import { Resource } from "alchemy";
96
+ import { Unowned } from "alchemy/AdoptPolicy";
97
+ import { isResolved } from "alchemy/Diff";
98
+ import * as Provider from "alchemy/Provider";
99
+ import * as Effect3 from "effect/Effect";
100
+
101
+ // src/postgres/errors.ts
102
+ import * as Data from "effect/Data";
103
+
104
+ class PostgresDatabaseNameRefused extends Data.TaggedError("PostgresDatabaseNameRefused") {
105
+ get message() {
106
+ return `Postgres.Database "${this.name}": ${String(this.byteLength)} UTF-8 bytes, over the ` + `${String(this.limit)}-byte limit (NAMEDATALEN 64, minus the terminator). PostgreSQL would ` + "accept the CREATE and silently truncate the name with only a NOTICE, so this plan refuses " + "it instead of creating a database no later read will ever find again.";
107
+ }
108
+ }
109
+
110
+ class PostgresDatabaseRenameRefused extends Data.TaggedError("PostgresDatabaseRenameRefused") {
111
+ get message() {
112
+ return `Postgres.Database: the declared name changed from "${this.from}" to "${this.to}". A ` + "rename is refused at plan — this provider never replaces a database (that is DROP then " + "CREATE) and never runs ALTER … RENAME. Declare a new logical id for the new name, and " + "remove the old one once its data has moved.";
113
+ }
114
+ }
115
+
116
+ class PostgresDatabaseOwnerMissing extends Data.TaggedError("PostgresDatabaseOwnerMissing") {
117
+ get message() {
118
+ return `Postgres.Database "${this.database}": OWNER "${this.owner}" is not a role in pg_roles.`;
119
+ }
120
+ }
121
+
122
+ class PostgresDatabaseDrift extends Data.TaggedError("PostgresDatabaseDrift") {
123
+ get message() {
124
+ return `Postgres.Database "${this.database}": ${this.prop} is declared as ` + `${JSON.stringify(this.declared)} but the live database has ${JSON.stringify(this.live)}. ` + "This provider never issues ALTER DATABASE — update the declaration to match the live " + "value, or change it by hand and re-plan.";
125
+ }
126
+ }
127
+
128
+ class PostgresDatabaseDropRefused extends Data.TaggedError("PostgresDatabaseDropRefused") {
129
+ get message() {
130
+ return `Postgres.Database "${this.database}": delete is refused. This provider never issues DROP ` + "DATABASE. Drop it by hand over the trust socket if you mean to remove it, then remove the " + "declaration.";
131
+ }
132
+ }
133
+
134
+ class PostgresDatabaseCreateVanished extends Data.TaggedError("PostgresDatabaseCreateVanished") {
135
+ get message() {
136
+ return `Postgres.Database "${this.database}": CREATE DATABASE raised no error, but the database ` + "is still absent on the immediate re-read. Re-plan — this is not a state this provider " + "expects to reach.";
137
+ }
138
+ }
139
+
140
+ // src/postgres/database.ts
141
+ var PostgresDatabase = Resource("Postgres.Database", {
142
+ defaultRemovalPolicy: "retain"
143
+ });
144
+ var isPostgresDatabase = (value) => (typeof value === "object" || typeof value === "function") && value !== null && value.Type === "Postgres.Database";
145
+ var liveDrift = (props, live) => {
146
+ if (props.owner !== live.owner) {
147
+ return { prop: "owner", declared: props.owner, live: live.owner };
148
+ }
149
+ return firstDrift(props, live);
150
+ };
151
+ var reconcileWithClient = (pg, props) => Effect3.gen(function* () {
152
+ const observed = yield* selectDatabase(pg, props.name);
153
+ if (observed === undefined) {
154
+ const ownerPresent = yield* roleExists(pg, props.owner);
155
+ if (!ownerPresent) {
156
+ return yield* Effect3.fail(new PostgresDatabaseOwnerMissing({ database: props.name, owner: props.owner }));
157
+ }
158
+ yield* pg.unsafe(buildCreateDatabaseSql(props)).pipe(Effect3.asVoid, Effect3.catchTag("SqlError", (error) => isDuplicateDatabaseRace(error) ? Effect3.succeed(undefined) : Effect3.fail(error)));
159
+ const created = yield* selectDatabase(pg, props.name);
160
+ if (created === undefined) {
161
+ return yield* Effect3.fail(new PostgresDatabaseCreateVanished({ database: props.name }));
162
+ }
163
+ return created;
164
+ }
165
+ const drift = liveDrift(props, observed);
166
+ if (drift !== undefined) {
167
+ return yield* Effect3.fail(new PostgresDatabaseDrift({
168
+ database: props.name,
169
+ prop: drift.prop,
170
+ declared: drift.declared,
171
+ live: drift.live
172
+ }));
173
+ }
174
+ return observed;
175
+ });
176
+ var readWithClient = (pg, name) => selectDatabase(pg, name);
177
+ var diffPostgresDatabase = (news, output) => Effect3.gen(function* () {
178
+ if (output === undefined || !isResolved(news))
179
+ return;
180
+ if (news.name !== output.name) {
181
+ return yield* Effect3.fail(new PostgresDatabaseRenameRefused({ from: output.name, to: news.name }));
182
+ }
183
+ const nameRefusal = nameByteRefusal(news.name);
184
+ if (nameRefusal !== undefined) {
185
+ return yield* Effect3.fail(new PostgresDatabaseNameRefused({ name: news.name, ...nameRefusal }));
186
+ }
187
+ const changed = news.owner !== output.owner || firstDrift(news, output) !== undefined;
188
+ return changed ? { action: "update" } : { action: "noop" };
189
+ });
190
+ var postgresDatabaseHandlers = PostgresDatabase.Provider.of({
191
+ list: () => Effect3.succeed([]),
192
+ nuke: { skip: true },
193
+ read: ({ olds, output }) => Effect3.gen(function* () {
194
+ const name = output?.name ?? olds.name;
195
+ const live = yield* withPg((pg) => readWithClient(pg, name));
196
+ return live === undefined ? undefined : Unowned(live);
197
+ }),
198
+ diff: ({ news, output }) => diffPostgresDatabase(news, output),
199
+ reconcile: ({ news }) => Effect3.gen(function* () {
200
+ const nameRefusal = nameByteRefusal(news.name);
201
+ if (nameRefusal !== undefined) {
202
+ return yield* Effect3.fail(new PostgresDatabaseNameRefused({ name: news.name, ...nameRefusal }));
203
+ }
204
+ return yield* withPg((pg) => reconcileWithClient(pg, news));
205
+ }),
206
+ delete: ({ olds }) => Effect3.fail(new PostgresDatabaseDropRefused({ database: olds.name }))
207
+ });
208
+ var PostgresDatabaseProvider = () => Provider.succeed(PostgresDatabase, postgresDatabaseHandlers);
209
+ // src/postgres/providers.ts
210
+ import * as Layer2 from "effect/Layer";
211
+ var postgresProviders = (config) => PostgresDatabaseProvider().pipe(Layer2.provide(postgresConnection(config)));
212
+ export {
213
+ POSTGRES_NAME_MAX_BYTES,
214
+ PostgresConnection,
215
+ PostgresDatabase,
216
+ PostgresDatabaseDrift,
217
+ PostgresDatabaseDropRefused,
218
+ PostgresDatabaseNameRefused,
219
+ PostgresDatabaseOwnerMissing,
220
+ PostgresDatabaseProvider,
221
+ PostgresDatabaseRenameRefused,
222
+ buildCreateDatabaseSql,
223
+ firstDrift,
224
+ isDuplicateDatabaseRace,
225
+ isPostgresDatabase,
226
+ nameByteRefusal,
227
+ postgresConnection,
228
+ postgresProviders,
229
+ quoteIdent,
230
+ quoteStringLiteral,
231
+ roleExists,
232
+ selectDatabase,
233
+ utf8ByteLength,
234
+ withPg
235
+ };
236
+
237
+ //# debugId=84B82CBA6D8F3F5F64756E2164756E21
238
+ //# sourceMappingURL=index.js.map