@homeflare/alchemy 0.45.0 → 0.46.1

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 (148) hide show
  1. package/dist/postgres/connection.d.ts.map +1 -1
  2. package/dist/postgres/fake-schema-drop.d.ts +13 -0
  3. package/dist/postgres/fake-schema-drop.d.ts.map +1 -0
  4. package/dist/postgres/fake-schema-sql.d.ts +5 -0
  5. package/dist/postgres/fake-schema-sql.d.ts.map +1 -1
  6. package/dist/postgres/fake-sql.d.ts +4 -0
  7. package/dist/postgres/fake-sql.d.ts.map +1 -1
  8. package/dist/postgres/index.d.ts +2 -1
  9. package/dist/postgres/index.d.ts.map +1 -1
  10. package/dist/postgres/index.js +461 -278
  11. package/dist/postgres/index.js.map +19 -15
  12. package/dist/postgres/psql-executor.d.ts.map +1 -1
  13. package/dist/postgres/schema-closure-sql.d.ts +45 -0
  14. package/dist/postgres/schema-closure-sql.d.ts.map +1 -0
  15. package/dist/postgres/schema-delete.d.ts +17 -8
  16. package/dist/postgres/schema-delete.d.ts.map +1 -1
  17. package/dist/postgres/schema-drop-sql.d.ts +66 -0
  18. package/dist/postgres/schema-drop-sql.d.ts.map +1 -0
  19. package/dist/postgres/schema-errors.d.ts +19 -1
  20. package/dist/postgres/schema-errors.d.ts.map +1 -1
  21. package/dist/postgres/schema-sequence-error.d.ts +29 -0
  22. package/dist/postgres/schema-sequence-error.d.ts.map +1 -0
  23. package/dist/postgres/schema-sql.d.ts +16 -0
  24. package/dist/postgres/schema-sql.d.ts.map +1 -1
  25. package/dist/postgres/schema-test-kit.d.ts +3 -1
  26. package/dist/postgres/schema-test-kit.d.ts.map +1 -1
  27. package/dist/postgres/search-path.d.ts +38 -0
  28. package/dist/postgres/search-path.d.ts.map +1 -0
  29. package/dist/proxmox/generated/constraints/index.d.ts +1 -1
  30. package/dist/proxmox/generated/constraints/pve-access.d.ts +1 -1
  31. package/dist/proxmox/generated/constraints/pve-cluster-backup.d.ts +1 -1
  32. package/dist/proxmox/generated/constraints/pve-cluster-ceph.d.ts +1 -1
  33. package/dist/proxmox/generated/constraints/pve-cluster-firewall.d.ts +1 -1
  34. package/dist/proxmox/generated/constraints/pve-cluster-ha.d.ts +1 -1
  35. package/dist/proxmox/generated/constraints/pve-cluster-metrics.d.ts +1 -1
  36. package/dist/proxmox/generated/constraints/pve-cluster-notifications.d.ts +1 -1
  37. package/dist/proxmox/generated/constraints/pve-cluster-replication.d.ts +1 -1
  38. package/dist/proxmox/generated/constraints/pve-cluster-sdn.d.ts +1 -1
  39. package/dist/proxmox/generated/constraints/pve-nodes-ceph.d.ts +1 -1
  40. package/dist/proxmox/generated/constraints/pve-nodes-disks.d.ts +1 -1
  41. package/dist/proxmox/generated/constraints/pve-nodes-lxc.d.ts +1 -1
  42. package/dist/proxmox/generated/constraints/pve-nodes-network.d.ts +1 -1
  43. package/dist/proxmox/generated/constraints/pve-nodes-qemu.d.ts +1 -1
  44. package/dist/proxmox/generated/constraints/pve-nodes-qemu.d.ts.map +1 -1
  45. package/dist/proxmox/generated/constraints/pve-nodes-storage.d.ts +1 -1
  46. package/dist/proxmox/generated/constraints/pve-pools.d.ts +1 -1
  47. package/dist/proxmox/generated/constraints/pve-storage.d.ts +1 -1
  48. package/dist/proxmox/index.d.ts +1 -0
  49. package/dist/proxmox/index.d.ts.map +1 -1
  50. package/dist/proxmox/index.js +283 -130
  51. package/dist/proxmox/index.js.map +31 -27
  52. package/dist/proxmox/qemu-dns.d.ts +2 -0
  53. package/dist/proxmox/qemu-dns.d.ts.map +1 -0
  54. package/dist/proxmox/qemu-errors.d.ts +1 -0
  55. package/dist/proxmox/qemu-errors.d.ts.map +1 -1
  56. package/dist/proxmox/qemu-form.d.ts.map +1 -1
  57. package/dist/proxmox/qemu-judge.d.ts.map +1 -1
  58. package/dist/proxmox/qemu-lifecycle.d.ts +15 -10
  59. package/dist/proxmox/qemu-lifecycle.d.ts.map +1 -1
  60. package/dist/proxmox/qemu-props.d.ts +10 -0
  61. package/dist/proxmox/qemu-props.d.ts.map +1 -1
  62. package/dist/proxmox/qemu-resize.d.ts +10 -0
  63. package/dist/proxmox/qemu-resize.d.ts.map +1 -0
  64. package/dist/proxmox/qemu-size.d.ts +20 -0
  65. package/dist/proxmox/qemu-size.d.ts.map +1 -0
  66. package/dist/proxmox/qemu-spec.d.ts +25 -0
  67. package/dist/proxmox/qemu-spec.d.ts.map +1 -0
  68. package/dist/verify/fake-engine.d.ts +4 -2
  69. package/dist/verify/fake-engine.d.ts.map +1 -1
  70. package/docs/postgres-schema.md +22 -1
  71. package/docs/proxmox-qemu-resize.md +62 -0
  72. package/package.json +1 -1
  73. package/src/postgres/connection.ts +10 -14
  74. package/src/postgres/database-bigint-serialization.test.ts +2 -2
  75. package/src/postgres/database-sql.ts +6 -6
  76. package/src/postgres/fake-grants-read.ts +3 -3
  77. package/src/postgres/fake-role-sql.ts +2 -2
  78. package/src/postgres/fake-schema-drop.ts +75 -0
  79. package/src/postgres/fake-schema-sql.ts +10 -1
  80. package/src/postgres/fake-sql.ts +10 -3
  81. package/src/postgres/grants-database.test.ts +5 -5
  82. package/src/postgres/grants-existence.ts +7 -7
  83. package/src/postgres/grants-ownership.ts +5 -5
  84. package/src/postgres/grants-psql-lifecycle.test.ts +7 -3
  85. package/src/postgres/grants-read-defaults.ts +5 -5
  86. package/src/postgres/grants-read.ts +16 -16
  87. package/src/postgres/index.ts +2 -0
  88. package/src/postgres/psql-executor-escaping.test.ts +3 -1
  89. package/src/postgres/psql-executor.test.ts +11 -5
  90. package/src/postgres/psql-executor.ts +4 -1
  91. package/src/postgres/role-adopt.test.ts +3 -1
  92. package/src/postgres/role-membership-options.test.ts +3 -1
  93. package/src/postgres/role-membership-repair.test.ts +3 -3
  94. package/src/postgres/role-membership-sql.ts +4 -4
  95. package/src/postgres/role-password-transports.test.ts +4 -2
  96. package/src/postgres/role-sql.ts +1 -1
  97. package/src/postgres/schema-closure-sql.ts +108 -0
  98. package/src/postgres/schema-delete-proofs.test.ts +7 -7
  99. package/src/postgres/schema-delete.ts +88 -46
  100. package/src/postgres/schema-drop-2bp01.test.ts +29 -17
  101. package/src/postgres/schema-drop-real.test.ts +190 -0
  102. package/src/postgres/schema-drop-sql.ts +135 -0
  103. package/src/postgres/schema-drop.test.ts +241 -0
  104. package/src/postgres/schema-errors.ts +26 -0
  105. package/src/postgres/schema-handlers.test.ts +21 -14
  106. package/src/postgres/schema-lifecycle.test.ts +6 -60
  107. package/src/postgres/schema-sequence-error.ts +38 -0
  108. package/src/postgres/schema-socket.test.ts +20 -6
  109. package/src/postgres/schema-sql.ts +16 -11
  110. package/src/postgres/schema-takeover.test.ts +7 -4
  111. package/src/postgres/schema-test-kit.ts +18 -4
  112. package/src/postgres/search-path.test.ts +100 -0
  113. package/src/postgres/search-path.ts +62 -0
  114. package/src/proxmox/generated/constraints/index.ts +1 -1
  115. package/src/proxmox/generated/constraints/pve-access.ts +1 -1
  116. package/src/proxmox/generated/constraints/pve-cluster-backup.ts +1 -1
  117. package/src/proxmox/generated/constraints/pve-cluster-ceph.ts +1 -1
  118. package/src/proxmox/generated/constraints/pve-cluster-firewall.ts +1 -1
  119. package/src/proxmox/generated/constraints/pve-cluster-ha.ts +1 -1
  120. package/src/proxmox/generated/constraints/pve-cluster-metrics.ts +1 -1
  121. package/src/proxmox/generated/constraints/pve-cluster-notifications.ts +1 -1
  122. package/src/proxmox/generated/constraints/pve-cluster-replication.ts +1 -1
  123. package/src/proxmox/generated/constraints/pve-cluster-sdn.ts +1 -1
  124. package/src/proxmox/generated/constraints/pve-nodes-ceph.ts +1 -1
  125. package/src/proxmox/generated/constraints/pve-nodes-disks.ts +1 -1
  126. package/src/proxmox/generated/constraints/pve-nodes-lxc.ts +1 -1
  127. package/src/proxmox/generated/constraints/pve-nodes-network.ts +1 -1
  128. package/src/proxmox/generated/constraints/pve-nodes-qemu.ts +6 -1
  129. package/src/proxmox/generated/constraints/pve-nodes-storage.ts +1 -1
  130. package/src/proxmox/generated/constraints/pve-pools.ts +1 -1
  131. package/src/proxmox/generated/constraints/pve-storage.ts +1 -1
  132. package/src/proxmox/index.ts +1 -0
  133. package/src/proxmox/qemu-dns.test.ts +73 -0
  134. package/src/proxmox/qemu-dns.ts +32 -0
  135. package/src/proxmox/qemu-errors.ts +1 -0
  136. package/src/proxmox/qemu-form.ts +6 -3
  137. package/src/proxmox/qemu-judge.ts +2 -1
  138. package/src/proxmox/qemu-lifecycle.ts +28 -47
  139. package/src/proxmox/qemu-props.ts +14 -2
  140. package/src/proxmox/qemu-resize-lock.test.ts +121 -0
  141. package/src/proxmox/qemu-resize-recovery.test.ts +81 -0
  142. package/src/proxmox/qemu-resize.test.ts +199 -0
  143. package/src/proxmox/qemu-resize.ts +86 -0
  144. package/src/proxmox/qemu-size.test.ts +64 -0
  145. package/src/proxmox/qemu-size.ts +95 -0
  146. package/src/proxmox/qemu-spec.ts +44 -0
  147. package/src/proxmox/qemu-volume.ts +5 -5
  148. package/src/verify/fake-engine.ts +6 -3
@@ -0,0 +1,2 @@
1
+ export declare const dnsRefusals: (nameserver: unknown) => string[];
2
+ //# sourceMappingURL=qemu-dns.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qemu-dns.d.ts","sourceRoot":"","sources":["../../src/proxmox/qemu-dns.ts"],"names":[],"mappings":"AAuBA,eAAO,MAAM,WAAW,eAAgB,OAAO,KAAG,MAAM,EAQvD,CAAC"}
@@ -1,5 +1,6 @@
1
1
  /** A QEMU write or observation this provider will not turn into a create or a delete. */
2
2
  export declare class QemuRefusedError extends Error {
3
+ readonly _tag = "QemuRefusedError";
3
4
  constructor(message: string);
4
5
  }
5
6
  //# sourceMappingURL=qemu-errors.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"qemu-errors.d.ts","sourceRoot":"","sources":["../../src/proxmox/qemu-errors.ts"],"names":[],"mappings":"AAAA,yFAAyF;AACzF,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,YAAY,OAAO,EAAE,MAAM,EAG1B;CACF"}
1
+ {"version":3,"file":"qemu-errors.d.ts","sourceRoot":"","sources":["../../src/proxmox/qemu-errors.ts"],"names":[],"mappings":"AAAA,yFAAyF;AACzF,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,QAAQ,CAAC,IAAI,sBAAsB;IACnC,YAAY,OAAO,EAAE,MAAM,EAG1B;CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"qemu-form.d.ts","sourceRoot":"","sources":["../../src/proxmox/qemu-form.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EACL,KAAK,OAAO,EAKb,MAAM,iBAAiB,CAAC;AAEzB;;;;GAIG;AACH,eAAO,MAAM,YAAY,UAAW,OAAO,KAAG,MAAM,EAG6C,CAAC;AAElG,2FAA2F;AAC3F,eAAO,MAAM,IAAI,UAAW,OAAO,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAO1D,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,UAAU,UAAW,OAAO;;CAGvC,CAAC;AAEH,qGAAqG;AACrG,eAAO,MAAM,UAAU,aAAO,CAAC"}
1
+ {"version":3,"file":"qemu-form.d.ts","sourceRoot":"","sources":["../../src/proxmox/qemu-form.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EACL,KAAK,OAAO,EAKb,MAAM,iBAAiB,CAAC;AAGzB;;;;GAIG;AACH,eAAO,MAAM,YAAY,UAAW,OAAO,KAAG,MAAM,EAKnD,CAAC;AAEF,2FAA2F;AAC3F,eAAO,MAAM,IAAI,UAAW,OAAO,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAO1D,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,UAAU,UAAW,OAAO;;CAGvC,CAAC;AAEH,qGAAqG;AACrG,eAAO,MAAM,UAAU,aAAO,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"qemu-judge.d.ts","sourceRoot":"","sources":["../../src/proxmox/qemu-judge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,KAAK,OAAO,EAAsD,MAAM,iBAAiB,CAAC;AAInG,MAAM,MAAM,QAAQ,GAAG;IACrB,mFAAmF;IACnF,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,oFAAoF;IACpF,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,iEAAiE;IACjE,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CACnC,CAAC;AAEF,eAAO,MAAM,KAAK,UAAW,OAAO,UAAU,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAG,QA8CtE,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,eAAe,SAAU,MAAM,OAAO,MAAM,GAAG,SAAS,KAAG,MAAM,GAAG,SAI1B,CAAC"}
1
+ {"version":3,"file":"qemu-judge.d.ts","sourceRoot":"","sources":["../../src/proxmox/qemu-judge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,KAAK,OAAO,EAAsD,MAAM,iBAAiB,CAAC;AAKnG,MAAM,MAAM,QAAQ,GAAG;IACrB,mFAAmF;IACnF,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,oFAAoF;IACpF,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,iEAAiE;IACjE,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CACnC,CAAC;AAEF,eAAO,MAAM,KAAK,UAAW,OAAO,UAAU,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAG,QA8CtE,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,eAAe,SAAU,MAAM,OAAO,MAAM,GAAG,SAAS,KAAG,MAAM,GAAG,SAI1B,CAAC"}
@@ -1,40 +1,45 @@
1
1
  import type { Input } from 'alchemy/Input';
2
2
  import * as nodes from '@distilled.cloud/proxmox/nodes';
3
3
  import * as Effect from 'effect/Effect';
4
- import { type VmAttributes, type VmProps } from './qemu-props.ts';
5
- export declare const readQemu: (props: VmProps) => Effect.Effect<VmAttributes | undefined, Error | import("effect/unstable/http/HttpClientError").HttpClientError | import("./credential-errors.ts").PveCredentialDenied, import("effect/unstable/http/HttpClient").HttpClient>;
4
+ import type * as HttpClient from 'effect/unstable/http/HttpClient';
5
+ import { QemuRefusedError } from './qemu-errors.ts';
6
+ import type { VmAttributes, VmProps } from './qemu-props.ts';
7
+ import { type QemuDiskResizeRefused } from './qemu-size.ts';
8
+ type QemuLifecycleError = Effect.Error<ReturnType<typeof readQemu>> | nodes.PutNodeQemuResizeError | QemuRefusedError | QemuDiskResizeRefused;
9
+ export declare const readQemu: (props: VmProps) => Effect.Effect<VmAttributes | undefined, Error | import("effect/unstable/http/HttpClientError").HttpClientError | import("./credential-errors.ts").PveCredentialDenied, HttpClient.HttpClient>;
6
10
  /** No purge, no skiplock, no destroy-unreferenced-disks. A missing config is already gone. */
7
- export declare const deleteQemu: (props: VmProps) => Effect.Effect<void | undefined, Error | import("./credential-errors.ts").PveCredentialDenied | nodes.ProxmoxOpError, import("effect/unstable/http/HttpClient").HttpClient>;
11
+ export declare const deleteQemu: (props: VmProps) => Effect.Effect<void | undefined, Error | import("./credential-errors.ts").PveCredentialDenied | nodes.ProxmoxOpError, HttpClient.HttpClient>;
8
12
  export declare const qemuHandlers: {
9
13
  list: () => Effect.Effect<never[], never, never>;
10
14
  /**
11
15
  * ⛔ `Unowned` UNLESS STATE ALREADY VOUCHES FOR IT (C2). With no attributes this is Alchemy's
12
16
  * adoption probe or the recovery read for an interrupted create (ownership/probe.ts); `settled`
13
- * is the same `judge` reconcile uses, so "proven ours" and "reconcile would write nothing" are
14
- * the same question asked once.
17
+ * uses the same config comparison as reconcile. Disk growth is a post-create step, so its
18
+ * drift cannot disown a VM after an interrupted/failed resize (measured 2026-10-05).
15
19
  */
16
20
  read: (args_0: {
17
21
  fqn: string;
18
22
  instanceId: string;
19
23
  olds: VmProps;
20
24
  output: VmAttributes | undefined;
21
- }) => Effect.Effect<VmAttributes | undefined, Error | import("effect/unstable/http/HttpClientError").HttpClientError | import("./credential-errors.ts").PveCredentialDenied, import("effect/unstable/http/HttpClient").HttpClient>;
25
+ }) => Effect.Effect<VmAttributes | undefined, Error | import("effect/unstable/http/HttpClientError").HttpClientError | import("./credential-errors.ts").PveCredentialDenied, HttpClient.HttpClient>;
22
26
  diff: (args_0: {
23
27
  news: Input<VmProps>;
24
28
  olds: VmProps;
25
29
  output: VmAttributes | undefined;
26
30
  }) => Effect.Effect<{
27
- readonly action: "noop" | "update";
28
- } | undefined, Error | import("effect/unstable/http/HttpClientError").HttpClientError | import("./credential-errors.ts").PveCredentialDenied, import("effect/unstable/http/HttpClient").HttpClient>;
31
+ readonly action: 'noop' | 'update';
32
+ } | undefined, QemuLifecycleError, HttpClient.HttpClient>;
29
33
  reconcile: (args_0: {
30
34
  fqn: string;
31
35
  instanceId: string;
32
36
  news: VmProps;
33
37
  olds: VmProps | undefined;
34
38
  output: VmAttributes | undefined;
35
- }) => Effect.Effect<VmAttributes, Error | import("effect/unstable/http/HttpClientError").HttpClientError | import("./credential-errors.ts").PveCredentialDenied, import("effect/unstable/http/HttpClient").HttpClient>;
39
+ }) => Effect.Effect<VmAttributes, QemuLifecycleError, HttpClient.HttpClient>;
36
40
  delete: ({ olds }: {
37
41
  olds: VmProps;
38
- }) => Effect.Effect<void | undefined, Error | import("./credential-errors.ts").PveCredentialDenied | nodes.ProxmoxOpError, import("effect/unstable/http/HttpClient").HttpClient>;
42
+ }) => Effect.Effect<void | undefined, Error | import("./credential-errors.ts").PveCredentialDenied | nodes.ProxmoxOpError, HttpClient.HttpClient>;
39
43
  };
44
+ export {};
40
45
  //# sourceMappingURL=qemu-lifecycle.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"qemu-lifecycle.d.ts","sourceRoot":"","sources":["../../src/proxmox/qemu-lifecycle.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,KAAK,KAAK,MAAM,gCAAgC,CAAC;AACxD,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAOxC,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,OAAO,EAMb,MAAM,iBAAiB,CAAC;AAwCzB,eAAO,MAAM,QAAQ,UAAW,OAAO,iOAapC,CAAC;AAEJ,8FAA8F;AAC9F,eAAO,MAAM,UAAU,UAAW,OAAO,+KAMyB,CAAC;AAmBnE,eAAO,MAAM,YAAY;IACvB,IAAI;IACJ;;;;;OAKG;IACH,IAAI;aAMG,MAAM;oBACC,MAAM;cACZ,OAAO;gBACL,YAAY,GAAG,SAAS;;IAQlC,IAAI;cAKI,KAAK,CAAC,OAAO,CAAC;cACd,OAAO;gBACL,YAAY,GAAG,SAAS;;;;IAwBlC,SAAS;aAOF,MAAM;oBACC,MAAM;cACZ,OAAO;cACP,OAAO,GAAG,SAAS;gBACjB,YAAY,GAAG,SAAS;;IAoDlC,MAAM,aAAa;QAAE,IAAI,EAAE,OAAO,CAAA;KAAE;CACrC,CAAC"}
1
+ {"version":3,"file":"qemu-lifecycle.d.ts","sourceRoot":"","sources":["../../src/proxmox/qemu-lifecycle.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,KAAK,KAAK,MAAM,gCAAgC,CAAC;AACxD,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,KAAK,UAAU,MAAM,iCAAiC,CAAC;AAKnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEpD,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAI7D,OAAO,EAAE,KAAK,qBAAqB,EAAyC,MAAM,gBAAgB,CAAC;AAOnG,KAAK,kBAAkB,GACnB,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC,GACzC,KAAK,CAAC,sBAAsB,GAC5B,gBAAgB,GAChB,qBAAqB,CAAC;AAE1B,eAAO,MAAM,QAAQ,UAAW,OAAO,kMAapC,CAAC;AAEJ,8FAA8F;AAC9F,eAAO,MAAM,UAAU,UAAW,OAAO,gJAMyB,CAAC;AAmBnE,eAAO,MAAM,YAAY;IACvB,IAAI;IACJ;;;;;OAKG;IACH,IAAI;aAMG,MAAM;oBACC,MAAM;cACZ,OAAO;gBACL,YAAY,GAAG,SAAS;;IAUlC,IAAI;cAKI,KAAK,CAAC,OAAO,CAAC;cACd,OAAO;gBACL,YAAY,GAAG,SAAS;;yBAEb,MAAM,GAAG,QAAQ;;IA4BtC,SAAS;aAOF,MAAM;oBACC,MAAM;cACZ,OAAO;cACP,OAAO,GAAG,SAAS;gBACjB,YAAY,GAAG,SAAS;;IAuDlC,MAAM,aAAa;QAAE,IAAI,EAAE,OAAO,CAAA;KAAE;CACrC,CAAC"}
@@ -46,6 +46,16 @@ export interface VmProps extends WithTarget {
46
46
  boot?: string;
47
47
  /** e.g. `virtio-scsi-single`. */
48
48
  scsihw?: string;
49
+ /** Cloud-init DNS servers: PVE's `address-list` (IP addresses or DNS names). */
50
+ nameserver?: string;
51
+ /** Cloud-init DNS search domains, in PVE's wire spelling. */
52
+ searchdomain?: string;
53
+ /**
54
+ * Minimum disk sizes in GiB, e.g. `{ scsi0: 64 }`, grown after import completes.
55
+ * Existing larger disks are left alone. Lowering a previously declared target is refused.
56
+ * These are resize targets, never config fields; declare the corresponding disk slot too.
57
+ */
58
+ diskSizesGiB?: Partial<Record<`scsi${number}` | `ide${number}`, number>>;
49
59
  /**
50
60
  * `local-zfs:32` to create a new volume, or the live volume string as adopted —
51
61
  * `cephtb4:vm-101-disk-0,size=32G` (n is 0 to 30, pve-qemu-server's own `$MAX_SCSI_DISKS`).
@@ -1 +1 @@
1
- {"version":3,"file":"qemu-props.d.ts","sourceRoot":"","sources":["../../src/proxmox/qemu-props.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD,kGAAkG;AAClG,MAAM,MAAM,OAAO,GAAG,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC;AAEtC,MAAM,WAAW,OAAQ,SAAQ,UAAU;IACzC,uFAAuF;IACvF,IAAI,EAAE,MAAM,CAAC;IACb,iEAAiE;IACjE,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW;IACX,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sCAAsC;IACtC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,yFAAyF;IACzF,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,+BAA+B;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iCAAiC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,CAAC,IAAI,EAAE,OAAO,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,CAAC;IAC5C;;;OAGG;IACH,CAAC,GAAG,EAAE,MAAM,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,CAAC;IAC1C;;;;OAIG;IACH,CAAC,SAAS,EAAE,MAAM,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,CAAC;IAChD,mFAAmF;IACnF,CAAC,QAAQ,EAAE,WAAW,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,CAAC;IACpD,gFAAgF;IAChF,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,CAAC;IAChD,kCAAkC;IAClC,UAAU,CAAC,EAAE,KAAK,CAAC;IACnB,wCAAwC;IACxC,OAAO,CAAC,EAAE,KAAK,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,2FAA2F;IAC3F,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAKD,8FAA8F;AAC9F,eAAO,MAAM,OAAO;aAClB,GAAG,EAAE,CAAC;aACN,QAAQ,EAAE,EAAE;aACZ,GAAG,EAAE,EAAE;aACP,IAAI,EAAE,EAAE;aACR,MAAM,EAAE,CAAC;CACD,CAAC;AACX,MAAM,MAAM,aAAa,GAAG,MAAM,OAAO,OAAO,CAAC;AAEjD,4FAA4F;AAC5F,eAAO,MAAM,UAAU,QAAS,MAAM,KAAG,SAAS,CAAC,aAAa,EAAE,MAAM,CAAC,GAAG,SAM3E,CAAC;AAeF;;;;;GAKG;AACH,eAAO,MAAM,YAAY,QAAS,MAAM,KAAG,OACY,CAAC;AAExD,6GAA6G;AAC7G,eAAO,MAAM,aAAa,UAAW,OAAO,OAAO,MAAM,KAAG,OACR,CAAC;AAErD,wFAAwF;AACxF,eAAO,MAAM,YAAY,UAAW,OAAO,KAAG,MAAM,EAGzC,CAAC;AAEZ,iGAAiG;AACjG,eAAO,MAAM,SAAS,UAAW,OAAO,KAAG,MACmB,CAAC;AAE/D;;;;;;GAMG;AACH,eAAO,MAAM,YAAY,SAAU,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAQjF,CAAC"}
1
+ {"version":3,"file":"qemu-props.d.ts","sourceRoot":"","sources":["../../src/proxmox/qemu-props.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD,kGAAkG;AAClG,MAAM,MAAM,OAAO,GAAG,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC;AAEtC,MAAM,WAAW,OAAQ,SAAQ,UAAU;IACzC,uFAAuF;IACvF,IAAI,EAAE,MAAM,CAAC;IACb,iEAAiE;IACjE,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW;IACX,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sCAAsC;IACtC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,yFAAyF;IACzF,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,+BAA+B;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iCAAiC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gFAAgF;IAChF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6DAA6D;IAC7D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,OAAO,MAAM,EAAE,GAAG,MAAM,MAAM,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC;IACzE;;;OAGG;IACH,CAAC,IAAI,EAAE,OAAO,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,CAAC;IAC5C;;;OAGG;IACH,CAAC,GAAG,EAAE,MAAM,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,CAAC;IAC1C;;;;OAIG;IACH,CAAC,SAAS,EAAE,MAAM,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,CAAC;IAChD,mFAAmF;IACnF,CAAC,QAAQ,EAAE,WAAW,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,CAAC;IACpD,gFAAgF;IAChF,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,CAAC;IAChD,kCAAkC;IAClC,UAAU,CAAC,EAAE,KAAK,CAAC;IACnB,wCAAwC;IACxC,OAAO,CAAC,EAAE,KAAK,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,2FAA2F;IAC3F,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAKD,8FAA8F;AAC9F,eAAO,MAAM,OAAO;aAClB,GAAG,EAAE,CAAC;aACN,QAAQ,EAAE,EAAE;aACZ,GAAG,EAAE,EAAE;aACP,IAAI,EAAE,EAAE;aACR,MAAM,EAAE,CAAC;CACD,CAAC;AACX,MAAM,MAAM,aAAa,GAAG,MAAM,OAAO,OAAO,CAAC;AAEjD,4FAA4F;AAC5F,eAAO,MAAM,UAAU,QAAS,MAAM,KAAG,SAAS,CAAC,aAAa,EAAE,MAAM,CAAC,GAAG,SAM3E,CAAC;AAiBF;;;;;GAKG;AACH,eAAO,MAAM,YAAY,QAAS,MAAM,KAAG,OACY,CAAC;AAExD,6GAA6G;AAC7G,eAAO,MAAM,aAAa,UAAW,OAAO,OAAO,MAAM,KAAG,OACR,CAAC;AAErD,wFAAwF;AACxF,eAAO,MAAM,YAAY,UAAW,OAAO,KAAG,MAAM,EAGzC,CAAC;AAEZ,iGAAiG;AACjG,eAAO,MAAM,SAAS,UAAW,OAAO,KAAG,MACmB,CAAC;AAE/D;;;;;;GAMG;AACH,eAAO,MAAM,YAAY,SAAU,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAQjF,CAAC"}
@@ -0,0 +1,10 @@
1
+ import * as Effect from 'effect/Effect';
2
+ import { type VmProps } from './qemu-props.ts';
3
+ /**
4
+ * ⛔ Observe the unlocked, allocated disk before each resize, including recovery after import.
5
+ * Absolute targets make retries safe; `+60G` would grow again after interrupted state writes.
6
+ * This resource never starts a guest. Its caller can power on only after reconcile returns.
7
+ * SDK failures retain their generated tags; provider refusals are QemuDiskResizeRefused.
8
+ */
9
+ export declare const resizeDisks: (props: VmProps) => Effect.Effect<undefined, Error | import("effect/unstable/http/HttpClientError").HttpClientError | import("./credential-errors.ts").PveCredentialDenied, import("effect/unstable/http/HttpClient").HttpClient>;
10
+ //# sourceMappingURL=qemu-resize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qemu-resize.d.ts","sourceRoot":"","sources":["../../src/proxmox/qemu-resize.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,KAAK,OAAO,EAAgB,MAAM,iBAAiB,CAAC;AAqC7D;;;;;GAKG;AACH,eAAO,MAAM,WAAW,UAAW,OAAO,kNAuCtC,CAAC"}
@@ -0,0 +1,20 @@
1
+ import * as Effect from 'effect/Effect';
2
+ import { type VmProps } from './qemu-props.ts';
3
+ declare const QemuDiskResizeRefused_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 & {
4
+ readonly _tag: "QemuDiskResizeRefused";
5
+ } & Readonly<A>;
6
+ export declare class QemuDiskResizeRefused extends QemuDiskResizeRefused_base<{
7
+ readonly disk: string;
8
+ readonly message: string;
9
+ }> {
10
+ }
11
+ /** PVE config reports binary sizes, including fractional G and integer M/K for imported images. */
12
+ export declare const diskBytes: (config: string | undefined) => number | undefined;
13
+ /** Validate the whole declaration before any write, including a later attempted target decrease. */
14
+ export declare const validateDiskSizes: (props: VmProps, olds?: VmProps) => Effect.Effect<undefined, QemuDiskResizeRefused, never>;
15
+ /** Capacity drift drives reconcile; recovery ownership ignores it because resize follows create. */
16
+ export declare const diskSizeDrift: (props: VmProps, config: Record<string, string>) => string[];
17
+ /** Missing slots may be imported by the upcoming config write; unreadable existing sizes fail closed. */
18
+ export declare const checkLiveDiskSizes: (props: VmProps, config: Record<string, string>, allowMissing?: boolean) => Effect.Effect<undefined, QemuDiskResizeRefused, never>;
19
+ export {};
20
+ //# sourceMappingURL=qemu-size.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qemu-size.d.ts","sourceRoot":"","sources":["../../src/proxmox/qemu-size.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,KAAK,OAAO,EAA6B,MAAM,iBAAiB,CAAC;;;;AAG1E,qBAAa,qBAAsB,SAAQ,2BAA0C;IACnF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B,CAAC;CAAG;AAEL,mGAAmG;AACnG,eAAO,MAAM,SAAS,WAAY,MAAM,GAAG,SAAS,KAAG,MAAM,GAAG,SAO/D,CAAC;AAEF,oGAAoG;AACpG,eAAO,MAAM,iBAAiB,UAAW,OAAO,SAAS,OAAO,2DAwC5D,CAAC;AAEL,oGAAoG;AACpG,eAAO,MAAM,aAAa,UAAW,OAAO,UAAU,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAG,MAAM,EAKzC,CAAC;AAE7C,yGAAyG;AACzG,eAAO,MAAM,kBAAkB,UACtB,OAAO,UACN,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,mFAmB5B,CAAC"}
@@ -0,0 +1,25 @@
1
+ /** QEMU config forms and vendor guards, separated from lifecycle to keep both reviewable. */
2
+ import { createForm } from './qemu-form.ts';
3
+ import { type VmAttributes, type VmProps } from './qemu-props.ts';
4
+ export declare const attributesOf: (live: Record<string, unknown>, props: VmProps) => VmAttributes;
5
+ /**
6
+ * ⚠️ KEPT ONLY TO SATISFY `PveSpec`'S REQUIRED FIELD — `qemu-judge.ts`'s `judge` is what diff and
7
+ * reconcile actually decide by, since a plain string compare is exactly the bug C1 found (it
8
+ * never matches the "new disk" or MAC-less spellings a declaration may use).
9
+ */
10
+ declare const matches: (attributes: VmAttributes, props: VmProps) => boolean;
11
+ export declare const spec: {
12
+ attributes: typeof attributesOf;
13
+ collection: (props: VmProps) => string;
14
+ createForm: typeof createForm;
15
+ endpoint: {
16
+ create: string;
17
+ update: string;
18
+ };
19
+ matches: typeof matches;
20
+ path: (props: VmProps) => string;
21
+ updateForm: typeof import("./qemu-form.ts").form;
22
+ };
23
+ export declare const guardCreate: (props: VmProps, presence: boolean) => import("effect/Effect").Effect<void>, guardUpdate: (props: VmProps) => import("effect/Effect").Effect<void>;
24
+ export {};
25
+ //# sourceMappingURL=qemu-spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qemu-spec.d.ts","sourceRoot":"","sources":["../../src/proxmox/qemu-spec.ts"],"names":[],"mappings":"AAAA,6FAA6F;AAC7F,OAAO,EAAE,UAAU,EAAc,MAAM,gBAAgB,CAAC;AACxD,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,OAAO,EAMb,MAAM,iBAAiB,CAAC;AAIzB,eAAO,MAAM,YAAY,SAAU,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,SAAS,OAAO,KAAG,YAI3E,CAAC;AAEH;;;;GAIG;AACH,QAAA,MAAM,OAAO,eAAgB,YAAY,SAAS,OAAO,YAG2B,CAAC;AAErF,eAAO,MAAM,IAAI;;wBAEK,OAAO;;;QAGzB,MAAM;QACN,MAAM;;;kBAGM,OAAO;;CAEmB,CAAC;AAE3C,eAAO,MAAQ,WAAW,+EAAE,WAAW,0DAAqB,CAAC"}
@@ -53,7 +53,7 @@ export type DriftRow = {
53
53
  readonly action: string;
54
54
  };
55
55
  export interface FakeEngine {
56
- /** Plan and apply `body`, as `alchemy deploy --adopt` would; resolves to the planned actions. */
56
+ /** Plan and apply `body` with the configured adoption policy; resolves to planned actions. */
57
57
  readonly deploy: (body: Body) => Promise<Record<string, string>>;
58
58
  /** Verify `body` against what the earlier deploys recorded. */
59
59
  readonly verify: (body: Body, options?: VerifyOptions) => Promise<AdoptReport>;
@@ -78,7 +78,9 @@ export interface FakeEngine {
78
78
  * hands in its real provider over a fake transport (proxmox/ceph-pool-adopt.test.ts).
79
79
  * ⚠️ THE CASTS ARE AT THE ENGINE'S TYPED BOUNDARY — openbao/fake-stack.ts has the reasoning.
80
80
  */
81
- export declare const engineOver: <ROut, E, RIn>(layer: Layer.Layer<ROut, E, RIn>) => FakeEngine;
81
+ export declare const engineOver: <ROut, E, RIn>(layer: Layer.Layer<ROut, E, RIn>, options?: {
82
+ readonly adopt?: boolean;
83
+ }) => FakeEngine;
82
84
  /** The fake family over `cloud`. */
83
85
  export declare const fakeEngine: (cloud: Cloud) => FakeEngine;
84
86
  //# sourceMappingURL=fake-engine.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fake-engine.d.ts","sourceRoot":"","sources":["../../src/verify/fake-engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,OAAO,MAAM,SAAS,CAAC;AAKnC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,aAAa,EAAiB,MAAM,aAAa,CAAC;AAElF,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AACD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,KAAM,SAAQ,OAAO,CAAC,QAAQ,CAAC,YAAY,EAAE,UAAU,EAAE,eAAe,CAAC;CAAG;AAC7F,eAAO,MAAM,KAAK,8BAAwC,CAAC;AAE3D,4EAA4E;AAC5E,MAAM,WAAW,KAAM,SAAQ,OAAO,CAAC,QAAQ,CAAC,YAAY,EAAE,UAAU,EAAE,eAAe,CAAC;CAAG;AAC7F,eAAO,MAAM,KAAK,8BAAwC,CAAC;AAE3D;;;;GAIG;AACH,MAAM,WAAW,QAAS,SAAQ,OAAO,CAAC,QAAQ,CAAC,eAAe,EAAE,UAAU,EAAE,eAAe,CAAC;CAAG;AACnG,eAAO,MAAM,QAAQ,iCAA8C,CAAC;AAEpE,gGAAgG;AAChG,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,QAAQ,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED,eAAO,MAAM,OAAO,UAAU,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAQ,KAI1D,CAAC;AAEH,8CAA8C;AAC9C,eAAO,MAAM,QAAQ,UAAW,KAAK,KAAG,MAAM,EAC2C,CAAC;AAyB1F,4FAA4F;AAC5F,eAAO,MAAM,cAAc,UAAW,KAAK,8GAmCxC,CAAC;AAEJ,oFAAoF;AACpF,MAAM,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;AAK5D,gGAAgG;AAChG,MAAM,MAAM,QAAQ,GAAG;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnD,MAAM,WAAW,UAAU;IACzB,iGAAiG;IACjG,QAAQ,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACjE,+DAA+D;IAC/D,QAAQ,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,EAAE,aAAa,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;IAC/E;;;;;;;;OAQG;IACH,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;IAClE;;;OAGG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,MAAM,CAAC;CAC/B;AAED;;;;GAIG;AACH,eAAO,MAAM,UAAU,GAAI,IAAI,EAAE,CAAC,EAAE,GAAG,SAAS,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,KAAG,UAmD3E,CAAC;AAEF,oCAAoC;AACpC,eAAO,MAAM,UAAU,UAAW,KAAK,KAAG,UAA+C,CAAC"}
1
+ {"version":3,"file":"fake-engine.d.ts","sourceRoot":"","sources":["../../src/verify/fake-engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,OAAO,MAAM,SAAS,CAAC;AAKnC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,aAAa,EAAiB,MAAM,aAAa,CAAC;AAElF,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AACD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,KAAM,SAAQ,OAAO,CAAC,QAAQ,CAAC,YAAY,EAAE,UAAU,EAAE,eAAe,CAAC;CAAG;AAC7F,eAAO,MAAM,KAAK,8BAAwC,CAAC;AAE3D,4EAA4E;AAC5E,MAAM,WAAW,KAAM,SAAQ,OAAO,CAAC,QAAQ,CAAC,YAAY,EAAE,UAAU,EAAE,eAAe,CAAC;CAAG;AAC7F,eAAO,MAAM,KAAK,8BAAwC,CAAC;AAE3D;;;;GAIG;AACH,MAAM,WAAW,QAAS,SAAQ,OAAO,CAAC,QAAQ,CAAC,eAAe,EAAE,UAAU,EAAE,eAAe,CAAC;CAAG;AACnG,eAAO,MAAM,QAAQ,iCAA8C,CAAC;AAEpE,gGAAgG;AAChG,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,QAAQ,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED,eAAO,MAAM,OAAO,UAAU,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAQ,KAI1D,CAAC;AAEH,8CAA8C;AAC9C,eAAO,MAAM,QAAQ,UAAW,KAAK,KAAG,MAAM,EAC2C,CAAC;AAyB1F,4FAA4F;AAC5F,eAAO,MAAM,cAAc,UAAW,KAAK,8GAmCxC,CAAC;AAEJ,oFAAoF;AACpF,MAAM,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;AAK5D,gGAAgG;AAChG,MAAM,MAAM,QAAQ,GAAG;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnD,MAAM,WAAW,UAAU;IACzB,8FAA8F;IAC9F,QAAQ,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACjE,+DAA+D;IAC/D,QAAQ,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,EAAE,aAAa,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;IAC/E;;;;;;;;OAQG;IACH,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;IAClE;;;OAGG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,MAAM,CAAC;CAC/B;AAED;;;;GAIG;AACH,eAAO,MAAM,UAAU,GAAI,IAAI,EAAE,CAAC,EAAE,GAAG,SAC9B,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,YACvB;IAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,KACpC,UAmDF,CAAC;AAEF,oCAAoC;AACpC,eAAO,MAAM,UAAU,UAAW,KAAK,KAAG,UAA+C,CAAC"}
@@ -97,7 +97,28 @@ empty, or when `cascade: true` is declared.
97
97
  as the same typed tag. `2BP01` is class `2B`, so both transports wrap it as `UnknownError`
98
98
  with the raw code on `reason.cause.code` — the classifier reads that code, not the tag.
99
99
  - With `cascade: true` the drop is `DROP SCHEMA IF EXISTS … CASCADE` and removes the schema's
100
- objects too. The `IF EXISTS` makes delete idempotent.
100
+ objects too — and, because Postgres cascades through `pg_depend`, anything in ANOTHER schema
101
+ that depends on them (a view, a foreign key, a trigger another stack owns). So before a
102
+ cascade drop the atomic block counts those dependents and refuses with
103
+ `PostgresSchemaCascadeCrossSchemaRefused`, naming the COUNT only (never another owner's
104
+ object names). A dependent whose catalog class is not recognised counts as foreign: the check
105
+ fails closed. The `IF EXISTS` makes delete idempotent.
106
+ - Known limit: `cascade: true` over a schema that holds a sequence (every serial or identity
107
+ column owns one) is refused with `PostgresSchemaCascadeSequencesRefused` (`HF004`, count only).
108
+ A sequence cannot be locked, so a concurrent view over it could escape the dependency proof;
109
+ drop the schema's contents first, then declare `cascade: false`.
110
+ - The proof and the drop are ONE statement. The re-read, the emptiness (or dependents) check and
111
+ the `DROP` run in a single `DO` block, so on the runner transport it is one `psql`. The block
112
+ takes `pg_advisory_xact_lock(<schema oid>)`, re-verifies the persisted `oid` + `owner` inside
113
+ the transaction, and only then drops; a schema replaced after the read is
114
+ `PostgresSchemaDeleteForeignRefused`. The lock is cooperative (it serialises two runs of this
115
+ provider, not an arbitrary `CREATE TABLE`); a creator that slips in after the check is still
116
+ caught by the server's own `2BP01` on a plain drop.
117
+ - Every session pins `search_path = pg_catalog, pg_temp` (`search-path.ts`) and every SQL
118
+ builder writes `pg_catalog.<name>`, so a role that can create objects in a schema on its
119
+ path cannot forge `pg_namespace` and fake the delete proof. Runner path: the script is
120
+ prefixed with the `SET`; socket path: the one reserved connection an operation uses is pinned
121
+ first (`search-path.test.ts` greps the builders for an unqualified catalog name).
101
122
  - A `cascade` change is a real change: it answers `update` (compared against the previous
102
123
  declaration's persisted props), so the flipped value reaches state and the eventual drop
103
124
  uses it — a schema first declared `cascade: true` stops dropping with CASCADE once the
@@ -0,0 +1,62 @@
1
+ # Cloud-init DNS and imported disk growth
2
+
3
+ ```ts
4
+ Proxmox.Vm('talos', {
5
+ target,
6
+ node: 'n2',
7
+ vmid: 10000,
8
+ scsi0: 'cephtb4:0,import-from=cephfs-tb4:import/talos.raw',
9
+ diskSizesGiB: { scsi0: 64 },
10
+ ide2: 'cephtb4:cloudinit',
11
+ ipconfig0: 'ip=10.20.16.20/24,gw=10.20.16.254',
12
+ nameserver: '10.20.16.254',
13
+ searchdomain: 'example.internal',
14
+ });
15
+ ```
16
+
17
+ `diskSizesGiB` declares minimum capacity in positive integer GiB. Declare the disk
18
+ slot alongside its target. Equal or larger live disks need no resize; lowering a
19
+ previously declared target is refused with `QemuDiskResizeRefused`, which callers
20
+ can handle with `Effect.catchTag`. Removing a target leaves capacity unmanaged.
21
+ Unsupported slots, CD-ROM/cloud-init drives and unreadable live sizes are refused.
22
+
23
+ The provider waits for import, reads the allocated disk, sends an absolute target
24
+ through the generated `putNodeQemuResize` operation, waits for its task, and verifies
25
+ the resulting capacity. An interrupted create resumes without `--adopt` when its
26
+ config matches, even if disk growth remains unfinished. Each resize checks the raw
27
+ VM lock first; lock waits and resize task polling share a 60-observation budget per
28
+ disk. A persistent lock raises `QemuDiskResizeRefused` without a resize write.
29
+ Absolute targets let retries finish growth without adding capacity twice.
30
+ `size=` in a volume config string is still observed metadata: omit it when
31
+ using a resize target. An unrelated update never resends the import source.
32
+
33
+ This resource never starts a VM. Complete its reconciliation before the separate
34
+ power-on step. Resizing an existing guest does not grow its partitions/filesystems.
35
+
36
+ DNS keys use the same declared-key comparison as `ipconfig0`: omitted keys remain
37
+ unmanaged. `nameserver` enforces the generated `address-list` constraint; PVE permits
38
+ IP addresses or DNS names, separated by whitespace, commas or semicolons. The provider
39
+ preserves the supplied spelling. `searchdomain` has no extra schema constraint.
40
+
41
+ Schema provenance: `codegen/manifest.json`, pve-manager **9.2.11/f6997e698c7933ea**,
42
+ SHA-256 `9def8f13611184ee1c7d0399713130dfc4a065701d0d91a69b9c03df929344e9`.
43
+ QEMU resize already exists in both generated surfaces: distilled's `nodes.ts` and
44
+ the legacy generator's packed `nodes-node-qemu-vmid-mtunnel.ts`. A filename search
45
+ for `qemu-vmid-resize.ts` misses the latter; neither output needs hand editing.
46
+ Both caches were recovered byte-for-byte from public vendor sources, then
47
+ `bun codegen/constraints.ts` generated the resize guard. The existing type generator
48
+ already supports the operation. No generated file was edited by hand.
49
+
50
+ Recovery used the vendor Makefile's concatenation order: `api-viewer/apidata.js`
51
+ and `api-viewer/PVEAPI.js` from proxmox/pve-docs
52
+ `4e7ff22c70eb35c63e65b0ba65c98980cb4d7bf5`, then `src/api-viewer/APIViewer.js`
53
+ from proxmox/proxmox-widget-toolkit `9204a77880eb31aff6ffc8bcf65ca48539685ab1`.
54
+ The result is exactly 4,337,847 bytes and the manifest SHA-256 above. PBS's 1,508,860
55
+ bytes were recovered from the public `proxmox-backup-docs_4.2.3-1_all.deb`, matching
56
+ the manifest's full SHA-256 (the recorded server version is 4.2.6-1, running 4.2.3).
57
+ No host was contacted. Tests cover generated constraints/types and fake SDK transport.
58
+
59
+ The named format's semantics come from
60
+ [PVE::JSONSchema](https://github.com/proxmox/pve-common/blob/defd246f31f327463f901a2daaf8dc52efcc5a97/src/PVE/JSONSchema.pm)
61
+ and `PVE::ParseUtils` at the same commit, inspected 2026-10-05. This is vendor-source
62
+ and fake-transport evidence; no live PVE call or Talos boot was performed.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@homeflare/alchemy",
3
- "version": "0.45.0",
3
+ "version": "0.46.1",
4
4
  "description": "Custom Alchemy providers for Cloudflare, Proxmox, OpenBao, Forgejo, Talos, launchd and Caddy, and a verifier that proves adoptions are no-ops.",
5
5
  "homepage": "https://github.com/taslabs-net/homeflare-kit/tree/main/packages/alchemy#readme",
6
6
  "bugs": "https://github.com/taslabs-net/homeflare-kit/issues",
@@ -20,6 +20,7 @@ import * as Layer from 'effect/Layer';
20
20
  import type { SqlError } from 'effect/unstable/sql/SqlError';
21
21
  import type { PgExecutor } from './database-sql.ts';
22
22
  import { type PsqlRunner, makePsqlExecutor } from './psql-executor.ts';
23
+ import { pinnedSocketExecutor } from './search-path.ts';
23
24
 
24
25
  /** What a stack passes to reach one cluster. `host` may be a socket directory (one beginning
25
26
  * with `/` expands to `${host}/.s.PGSQL.${port}` — measured in
@@ -109,21 +110,16 @@ export const withPg = <A, E>(
109
110
  });
110
111
  }
111
112
  const pool = yield* PostgresPool;
113
+ // ⛔ ONE RESERVED, PINNED CONNECTION FOR THE WHOLE OPERATION (`search-path.ts`): the pin is a
114
+ // session setting, so a pool checkout per statement would run most statements unpinned — and
115
+ // would autocommit CREATE before GRANT. `Effect.scoped` releases the reservation; the pool
116
+ // layer below closes the pool after.
112
117
  return yield* Effect.provide(
113
- Effect.flatMap(PgClient.PgClient, (pg) =>
114
- build(
115
- {
116
- unsafe: (sql, params) => pg.unsafe(sql, params),
117
- // One reserved connection (`SqlClient.withTransaction`). A pool checkout per
118
- // statement would autocommit CREATE before GRANT.
119
- transaction: (statements) =>
120
- pg.withTransaction(
121
- Effect.gen(function* () {
122
- for (const sql of statements) yield* pg.unsafe(sql).pipe(Effect.asVoid);
123
- }),
124
- ),
125
- },
126
- { database: target.database },
118
+ Effect.scoped(
119
+ Effect.flatMap(PgClient.PgClient, (pg) =>
120
+ Effect.flatMap(pinnedSocketExecutor(pg), (executor) =>
121
+ build(executor, { database: target.database }),
122
+ ),
127
123
  ),
128
124
  ),
129
125
  pool(target),
@@ -76,10 +76,10 @@ const wireAccuratePg = (seed?: {
76
76
  return {
77
77
  unsafe: <A extends object>(text: string, params: ReadonlyArray<unknown> = []) =>
78
78
  Effect.suspend(() => {
79
- if (text.startsWith('SELECT 1 AS present FROM pg_roles')) {
79
+ if (text.startsWith('SELECT 1 AS present FROM pg_catalog.pg_roles')) {
80
80
  return Effect.succeed([{ present: 1 }] as unknown as ReadonlyArray<A>);
81
81
  }
82
- if (text.includes('FROM pg_database')) {
82
+ if (text.includes('FROM pg_catalog.pg_database')) {
83
83
  if (created === undefined) return Effect.succeed([] as unknown as ReadonlyArray<A>);
84
84
  const row = wireAccurateRow(text, created.oid, created.connectionLimit);
85
85
  return Effect.succeed([row] as unknown as ReadonlyArray<A>);
@@ -103,7 +103,7 @@ export const isDuplicateDatabaseRace = (error: SqlError): boolean => {
103
103
  );
104
104
  };
105
105
 
106
- const ROLE_EXISTS_SQL = 'SELECT 1 AS present FROM pg_roles WHERE rolname = $1';
106
+ const ROLE_EXISTS_SQL = 'SELECT 1 AS present FROM pg_catalog.pg_roles WHERE rolname = $1';
107
107
 
108
108
  /** `OWNER` is checked against `pg_roles` before any `CREATE`, so a missing role is
109
109
  * `PostgresDatabaseOwnerMissing` at reconcile time rather than the server's own
@@ -114,7 +114,7 @@ export const roleExists = (pg: PgExecutor, role: string): Effect.Effect<boolean,
114
114
  (rows) => rows.length > 0,
115
115
  );
116
116
 
117
- const DATABASE_EXISTS_SQL = 'SELECT 1 AS present FROM pg_database WHERE datname = $1';
117
+ const DATABASE_EXISTS_SQL = 'SELECT 1 AS present FROM pg_catalog.pg_database WHERE datname = $1';
118
118
 
119
119
  /**
120
120
  * Does one database of the cluster exist? `Postgres.Schema` runs this over the FAMILY
@@ -152,8 +152,8 @@ export const databaseExists = (pg: PgExecutor, name: string): Effect.Effect<bool
152
152
  const SELECT_DATABASE_SQL = `SELECT
153
153
  d.oid AS oid,
154
154
  d.datname AS name,
155
- pg_get_userbyid(d.datdba) AS owner,
156
- pg_encoding_to_char(d.encoding) AS encoding,
155
+ pg_catalog.pg_get_userbyid(d.datdba) AS owner,
156
+ pg_catalog.pg_encoding_to_char(d.encoding) AS encoding,
157
157
  CASE d.datlocprovider
158
158
  WHEN 'b' THEN 'builtin' WHEN 'i' THEN 'icu' WHEN 'c' THEN 'libc'
159
159
  ELSE 'libc'
@@ -164,8 +164,8 @@ const SELECT_DATABASE_SQL = `SELECT
164
164
  d.datconnlimit AS "connectionLimit",
165
165
  d.datistemplate AS "isTemplate",
166
166
  t.spcname AS tablespace
167
- FROM pg_database d
168
- JOIN pg_tablespace t ON t.oid = d.dattablespace
167
+ FROM pg_catalog.pg_database d
168
+ JOIN pg_catalog.pg_tablespace t ON t.oid = d.dattablespace
169
169
  WHERE d.datname = $1`;
170
170
 
171
171
  /** The read: one bound `SELECT` on `pg_database`, joined to the two functions that turn its raw
@@ -96,17 +96,17 @@ export const answerRead = <A extends object>(
96
96
  ): Effect.Effect<ReadonlyArray<A>, SqlError> | undefined => {
97
97
  const { acl, schemas, roles, databases, tables } = model;
98
98
 
99
- if (text.startsWith('SELECT 1 AS present FROM pg_roles')) {
99
+ if (text.startsWith('SELECT 1 AS present FROM pg_catalog.pg_roles')) {
100
100
  return Effect.succeed(
101
101
  (roles.has(params[0] as string) ? [{ present: 1 }] : []) as unknown as ReadonlyArray<A>,
102
102
  );
103
103
  }
104
- if (text.startsWith('SELECT 1 AS present FROM pg_namespace')) {
104
+ if (text.startsWith('SELECT 1 AS present FROM pg_catalog.pg_namespace')) {
105
105
  return Effect.succeed(
106
106
  (schemas.has(params[0] as string) ? [{ present: 1 }] : []) as unknown as ReadonlyArray<A>,
107
107
  );
108
108
  }
109
- if (text.startsWith('SELECT 1 AS present FROM pg_database')) {
109
+ if (text.startsWith('SELECT 1 AS present FROM pg_catalog.pg_database')) {
110
110
  return Effect.succeed(
111
111
  (databases.has(params[0] as string) ? [{ present: 1 }] : []) as unknown as ReadonlyArray<A>,
112
112
  );
@@ -97,7 +97,7 @@ export const applyRoleStatement = <A extends object>(
97
97
  text: string,
98
98
  params: ReadonlyArray<unknown>,
99
99
  ): Effect.Effect<ReadonlyArray<A>, SqlError> | undefined => {
100
- if (text.includes('FROM pg_auth_members')) {
100
+ if (text.includes('FROM pg_catalog.pg_auth_members')) {
101
101
  const member = params[0] as string;
102
102
  const rows = [...state.memberships]
103
103
  .filter((key) => key.split('\0')[0] === member)
@@ -119,7 +119,7 @@ export const applyRoleStatement = <A extends object>(
119
119
  return Effect.succeed(rows as unknown as ReadonlyArray<A>);
120
120
  }
121
121
 
122
- if (text.includes('FROM pg_roles r')) {
122
+ if (text.includes('FROM pg_catalog.pg_roles r')) {
123
123
  const name = params[0] as string;
124
124
  const row = state.roleRows.get(name);
125
125
  // pg_roles answers no password value (S25): even a seeded seal is not read back — state
@@ -0,0 +1,75 @@
1
+ /**
2
+ * The fake's reading of the atomic `DO` drop `schema-drop-sql.ts` emits.
3
+ *
4
+ * ⛔ IT PARSES ITS OWN OUTPUT (the rule `fake-sql.ts` documents): the four `DECLARE` literals are
5
+ * read back out of the generated text and the block's decisions are replayed against the
6
+ * in-memory catalog in the same order the SQL makes them — absent → return, identity, empty or
7
+ * cross-schema dependents, drop. A changed block shape fails the parse loudly.
8
+ */
9
+ import * as Effect from 'effect/Effect';
10
+ import { SqlError, UnknownError } from 'effect/unstable/sql/SqlError';
11
+ import { parseLiteral } from './fake-sql-quote.ts';
12
+ import { parseDropSchema } from './fake-sql-parse.ts';
13
+ import type { FakeSchemaState } from './fake-schema-sql.ts';
14
+ import {
15
+ CASCADE_SEQUENCES,
16
+ CROSS_SCHEMA_DEPENDENTS,
17
+ IDENTITY_CHANGED,
18
+ NOT_EMPTY,
19
+ } from './schema-drop-sql.ts';
20
+
21
+ const refusal = (code: string, message: string): SqlError =>
22
+ new SqlError({
23
+ reason: new UnknownError({
24
+ cause: Object.assign(new Error(message), { code }),
25
+ message,
26
+ operation: 'DO',
27
+ }),
28
+ });
29
+
30
+ /** `E'…'` literal at the end of a generated line, decoded. */
31
+ const literalAfter = (body: string, label: string): string => {
32
+ const match = new RegExp(`${label} [a-z]+ := (E'(?:[^']|'')*');`).exec(body);
33
+ if (match === null) throw new Error(`fake-sql: could not parse DO block ${label}: ${body}`);
34
+ return parseLiteral(match[1] as string);
35
+ };
36
+
37
+ export const applyAtomicDrop = (
38
+ state: FakeSchemaState,
39
+ text: string,
40
+ ): Effect.Effect<ReadonlyArray<never>, SqlError> => {
41
+ const body = parseLiteral(text.slice('DO '.length));
42
+ const name = literalAfter(body, 'v_name');
43
+ const owner = literalAfter(body, 'v_owner');
44
+ const oid = Number(/v_oid oid := (\d+);/.exec(body)?.[1]);
45
+ const cascade = /v_cascade boolean := (true|false);/.exec(body)?.[1] === 'true';
46
+ const drop = /EXECUTE (E'(?:[^']|'')*');/.exec(body)?.[1];
47
+ if (drop === undefined || Number.isNaN(oid)) {
48
+ throw new Error(`fake-sql: could not parse DO block: ${body}`);
49
+ }
50
+ if (parseDropSchema(parseLiteral(drop)).name !== name) {
51
+ throw new Error('fake-sql: DO block drops a different schema than it proves');
52
+ }
53
+ const row = state.schemas.get(name);
54
+ if (row === undefined) return Effect.succeed([]);
55
+ if (row.oid !== oid || row.owner !== owner) {
56
+ return Effect.fail(refusal(IDENTITY_CHANGED, 'schema identity changed'));
57
+ }
58
+ if (!cascade) {
59
+ if (state.relationsIn.has(name)) return Effect.fail(refusal(NOT_EMPTY, 'schema not empty'));
60
+ } else {
61
+ const sequences = state.sequencesIn.get(name) ?? 0;
62
+ if (sequences > 0) {
63
+ return Effect.fail(refusal(CASCADE_SEQUENCES, `cascade sequences: ${String(sequences)}`));
64
+ }
65
+ const outside = state.dependentsOutside.get(name) ?? 0;
66
+ if (outside > 0) {
67
+ return Effect.fail(
68
+ refusal(CROSS_SCHEMA_DEPENDENTS, `cross-schema dependents: ${String(outside)}`),
69
+ );
70
+ }
71
+ state.relationsIn.delete(name);
72
+ }
73
+ state.schemas.delete(name);
74
+ return Effect.succeed([]);
75
+ };
@@ -10,6 +10,7 @@
10
10
  */
11
11
  import * as Effect from 'effect/Effect';
12
12
  import { SqlError, SqlSyntaxError, UnknownError } from 'effect/unstable/sql/SqlError';
13
+ import { applyAtomicDrop } from './fake-schema-drop.ts';
13
14
  import { parseCommentSchema, parseCreateSchema, parseDropSchema } from './fake-sql-parse.ts';
14
15
  import type { PostgresSchemaAttributes } from './schema-attrs.ts';
15
16
 
@@ -18,6 +19,11 @@ export interface FakeSchemaState {
18
19
  readonly schemas: Map<string, PostgresSchemaAttributes>;
19
20
  /** Names of schemas this fake pretends hold at least one relation. */
20
21
  readonly relationsIn: Set<string>;
22
+ /** Per schema name: how many objects in OTHER schemas depend on it (what the atomic drop's
23
+ * `pg_depend` count answers). Absent means none. */
24
+ readonly dependentsOutside: Map<string, number>;
25
+ /** Per schema name: how many sequences it holds (a cascade drop refuses any: `HF004`). */
26
+ readonly sequencesIn: Map<string, number>;
21
27
  /** What `current_database()` answers, and the stamp on every read row. */
22
28
  readonly database: string;
23
29
  /** What `current_user` answers, and the owner of a `CREATE SCHEMA` without `AUTHORIZATION`. */
@@ -44,6 +50,9 @@ export const applySchemaStatement = <A extends object>(
44
50
  text: string,
45
51
  params: ReadonlyArray<unknown>,
46
52
  ): Effect.Effect<ReadonlyArray<A>, SqlError> | undefined => {
53
+ // The atomic drop's text contains every marker below; matched first, by its own prefix.
54
+ if (text.startsWith('DO ')) return applyAtomicDrop(state, text);
55
+
47
56
  if (text.startsWith('SELECT current_database()')) {
48
57
  return Effect.succeed([{ database: state.database }] as unknown as ReadonlyArray<A>);
49
58
  }
@@ -57,7 +66,7 @@ export const applySchemaStatement = <A extends object>(
57
66
  return Effect.succeed([{ empty: !state.relationsIn.has(name) }] as unknown as ReadonlyArray<A>);
58
67
  }
59
68
 
60
- if (text.includes('FROM pg_namespace')) {
69
+ if (text.includes('FROM pg_catalog.pg_namespace')) {
61
70
  const name = params[0] as string;
62
71
  const row = state.schemas.get(name);
63
72
  return Effect.succeed(