@homeflare/alchemy 0.37.2 → 0.37.3

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 (155) hide show
  1. package/dist/proxmox/backup-job-config.d.ts +95 -0
  2. package/dist/proxmox/backup-job-config.d.ts.map +1 -0
  3. package/dist/proxmox/backup-job-distilled.d.ts +46 -0
  4. package/dist/proxmox/backup-job-distilled.d.ts.map +1 -0
  5. package/dist/proxmox/backup-job.d.ts +9 -26
  6. package/dist/proxmox/backup-job.d.ts.map +1 -1
  7. package/dist/proxmox/distilled-pbs.d.ts +12 -0
  8. package/dist/proxmox/distilled-pbs.d.ts.map +1 -0
  9. package/dist/proxmox/fake-pbs-jobs.d.ts +23 -0
  10. package/dist/proxmox/fake-pbs-jobs.d.ts.map +1 -0
  11. package/dist/proxmox/fake-pbs-notify.d.ts.map +1 -1
  12. package/dist/proxmox/fake-pve.d.ts +1 -1
  13. package/dist/proxmox/fake-pve.d.ts.map +1 -1
  14. package/dist/proxmox/index.js +1501 -607
  15. package/dist/proxmox/index.js.map +48 -24
  16. package/dist/proxmox/live-core-fixtures.d.ts +40 -0
  17. package/dist/proxmox/live-core-fixtures.d.ts.map +1 -0
  18. package/dist/proxmox/metric-server-config.d.ts +49 -0
  19. package/dist/proxmox/metric-server-config.d.ts.map +1 -0
  20. package/dist/proxmox/metric-server-distilled.d.ts +43 -0
  21. package/dist/proxmox/metric-server-distilled.d.ts.map +1 -0
  22. package/dist/proxmox/metric-server.d.ts +2 -10
  23. package/dist/proxmox/metric-server.d.ts.map +1 -1
  24. package/dist/proxmox/notification-matcher-distilled.d.ts +9 -0
  25. package/dist/proxmox/notification-matcher-distilled.d.ts.map +1 -0
  26. package/dist/proxmox/notification-matcher-lifecycle.d.ts +27 -0
  27. package/dist/proxmox/notification-matcher-lifecycle.d.ts.map +1 -0
  28. package/dist/proxmox/notification-matcher.d.ts +5 -5
  29. package/dist/proxmox/notification-matcher.d.ts.map +1 -1
  30. package/dist/proxmox/notification-sdk-input.d.ts +22 -0
  31. package/dist/proxmox/notification-sdk-input.d.ts.map +1 -0
  32. package/dist/proxmox/notification-target-distilled.d.ts +12 -0
  33. package/dist/proxmox/notification-target-distilled.d.ts.map +1 -0
  34. package/dist/proxmox/notification-target-form.d.ts +2 -0
  35. package/dist/proxmox/notification-target-form.d.ts.map +1 -1
  36. package/dist/proxmox/notification-target-lifecycle.d.ts +29 -0
  37. package/dist/proxmox/notification-target-lifecycle.d.ts.map +1 -0
  38. package/dist/proxmox/notification-target-spec.d.ts +5 -0
  39. package/dist/proxmox/notification-target-spec.d.ts.map +1 -0
  40. package/dist/proxmox/notification-target.d.ts +4 -5
  41. package/dist/proxmox/notification-target.d.ts.map +1 -1
  42. package/dist/proxmox/pbs-datastore-endpoint.d.ts +1 -1
  43. package/dist/proxmox/pbs-datastore-form.d.ts +18 -42
  44. package/dist/proxmox/pbs-datastore-form.d.ts.map +1 -1
  45. package/dist/proxmox/pbs-datastore-guard.d.ts +7 -4
  46. package/dist/proxmox/pbs-datastore-guard.d.ts.map +1 -1
  47. package/dist/proxmox/pbs-datastore-lifecycle.d.ts +56 -0
  48. package/dist/proxmox/pbs-datastore-lifecycle.d.ts.map +1 -0
  49. package/dist/proxmox/pbs-datastore-wire.d.ts +21 -0
  50. package/dist/proxmox/pbs-datastore-wire.d.ts.map +1 -0
  51. package/dist/proxmox/pbs-datastore.d.ts +5 -6
  52. package/dist/proxmox/pbs-datastore.d.ts.map +1 -1
  53. package/dist/proxmox/pbs-notification-input.d.ts +18 -0
  54. package/dist/proxmox/pbs-notification-input.d.ts.map +1 -0
  55. package/dist/proxmox/pbs-notification-matcher-distilled.d.ts +11 -0
  56. package/dist/proxmox/pbs-notification-matcher-distilled.d.ts.map +1 -0
  57. package/dist/proxmox/pbs-notification-matcher.d.ts +4 -3
  58. package/dist/proxmox/pbs-notification-matcher.d.ts.map +1 -1
  59. package/dist/proxmox/pbs-notification-target-distilled.d.ts +20 -0
  60. package/dist/proxmox/pbs-notification-target-distilled.d.ts.map +1 -0
  61. package/dist/proxmox/pbs-notification-target-lifecycle.d.ts +4 -4
  62. package/dist/proxmox/pbs-notification-target-lifecycle.d.ts.map +1 -1
  63. package/dist/proxmox/pbs-notification-target.d.ts +1 -1
  64. package/dist/proxmox/pbs-prune-job-lifecycle.d.ts +22 -0
  65. package/dist/proxmox/pbs-prune-job-lifecycle.d.ts.map +1 -0
  66. package/dist/proxmox/pbs-prune-job-spec.d.ts +5 -0
  67. package/dist/proxmox/pbs-prune-job-spec.d.ts.map +1 -0
  68. package/dist/proxmox/pbs-prune-job-wire.d.ts +11 -0
  69. package/dist/proxmox/pbs-prune-job-wire.d.ts.map +1 -0
  70. package/dist/proxmox/pbs-prune-job.d.ts +9 -8
  71. package/dist/proxmox/pbs-prune-job.d.ts.map +1 -1
  72. package/dist/proxmox/pbs-sync-job-attributes.d.ts +34 -0
  73. package/dist/proxmox/pbs-sync-job-attributes.d.ts.map +1 -0
  74. package/dist/proxmox/pbs-sync-job-form.d.ts +21 -22
  75. package/dist/proxmox/pbs-sync-job-form.d.ts.map +1 -1
  76. package/dist/proxmox/pbs-sync-job-lifecycle.d.ts +22 -0
  77. package/dist/proxmox/pbs-sync-job-lifecycle.d.ts.map +1 -0
  78. package/dist/proxmox/pbs-sync-job-wire.d.ts +11 -0
  79. package/dist/proxmox/pbs-sync-job-wire.d.ts.map +1 -0
  80. package/dist/proxmox/pbs-sync-job.d.ts +9 -39
  81. package/dist/proxmox/pbs-sync-job.d.ts.map +1 -1
  82. package/dist/proxmox/pbs-verify-job-lifecycle.d.ts +22 -0
  83. package/dist/proxmox/pbs-verify-job-lifecycle.d.ts.map +1 -0
  84. package/dist/proxmox/pbs-verify-job-spec.d.ts +16 -0
  85. package/dist/proxmox/pbs-verify-job-spec.d.ts.map +1 -0
  86. package/dist/proxmox/pbs-verify-job-wire.d.ts +11 -0
  87. package/dist/proxmox/pbs-verify-job-wire.d.ts.map +1 -0
  88. package/dist/proxmox/pbs-verify-job.d.ts +12 -12
  89. package/dist/proxmox/pbs-verify-job.d.ts.map +1 -1
  90. package/dist/proxmox/pool.d.ts +10 -1
  91. package/dist/proxmox/pool.d.ts.map +1 -1
  92. package/docs/pbs-distilled-jobs.md +31 -0
  93. package/docs/pbs-notifications.md +8 -2
  94. package/docs/proxmox-notification-sdk.md +28 -0
  95. package/docs/proxmox-sdk-migration.md +60 -0
  96. package/docs/upstream-conformance-families.md +6 -2
  97. package/package.json +1 -1
  98. package/src/proxmox/adopt-noop.test.ts +4 -5
  99. package/src/proxmox/backup-job-config.ts +95 -0
  100. package/src/proxmox/backup-job-distilled.ts +86 -0
  101. package/src/proxmox/backup-job.ts +78 -116
  102. package/src/proxmox/constraints.test.ts +9 -8
  103. package/src/proxmox/distilled-pbs.test.ts +87 -0
  104. package/src/proxmox/distilled-pbs.ts +50 -0
  105. package/src/proxmox/fake-pbs-jobs.ts +99 -0
  106. package/src/proxmox/fake-pbs-notify.ts +4 -1
  107. package/src/proxmox/fake-pve.ts +3 -2
  108. package/src/proxmox/live-core-fixtures.ts +126 -0
  109. package/src/proxmox/live-core-lifecycle.test.ts +115 -0
  110. package/src/proxmox/live-core-wire.test.ts +190 -0
  111. package/src/proxmox/metric-server-config.ts +87 -0
  112. package/src/proxmox/metric-server-distilled.ts +81 -0
  113. package/src/proxmox/metric-server.ts +77 -88
  114. package/src/proxmox/notification-distilled.test.ts +162 -0
  115. package/src/proxmox/notification-matcher-distilled.ts +60 -0
  116. package/src/proxmox/notification-matcher-lifecycle.ts +71 -0
  117. package/src/proxmox/notification-matcher.ts +9 -26
  118. package/src/proxmox/notification-replacement.test.ts +190 -0
  119. package/src/proxmox/notification-sdk-input.ts +39 -0
  120. package/src/proxmox/notification-target-distilled.ts +160 -0
  121. package/src/proxmox/notification-target-form.ts +2 -0
  122. package/src/proxmox/notification-target-lifecycle.ts +75 -0
  123. package/src/proxmox/notification-target-spec.ts +68 -0
  124. package/src/proxmox/notification-target.ts +6 -68
  125. package/src/proxmox/pbs-datastore-endpoint.ts +1 -1
  126. package/src/proxmox/pbs-datastore-form.ts +18 -49
  127. package/src/proxmox/pbs-datastore-guard.ts +7 -4
  128. package/src/proxmox/pbs-datastore-lifecycle.ts +114 -0
  129. package/src/proxmox/pbs-datastore-wire.ts +94 -0
  130. package/src/proxmox/pbs-datastore.ts +7 -133
  131. package/src/proxmox/pbs-jobs-delete.test.ts +40 -0
  132. package/src/proxmox/pbs-jobs-sdk.test.ts +215 -0
  133. package/src/proxmox/pbs-notification-errors.test.ts +93 -0
  134. package/src/proxmox/pbs-notification-input.ts +25 -0
  135. package/src/proxmox/pbs-notification-mail.test.ts +64 -0
  136. package/src/proxmox/pbs-notification-matcher-distilled.ts +65 -0
  137. package/src/proxmox/pbs-notification-matcher.ts +70 -17
  138. package/src/proxmox/pbs-notification-target-distilled.ts +124 -0
  139. package/src/proxmox/pbs-notification-target-lifecycle.ts +26 -17
  140. package/src/proxmox/pbs-notification-target.ts +1 -1
  141. package/src/proxmox/pbs-notifications-stack.test.ts +25 -0
  142. package/src/proxmox/pbs-prune-job-lifecycle.ts +52 -0
  143. package/src/proxmox/pbs-prune-job-spec.ts +53 -0
  144. package/src/proxmox/pbs-prune-job-wire.ts +68 -0
  145. package/src/proxmox/pbs-prune-job.ts +10 -58
  146. package/src/proxmox/pbs-sync-job-attributes.ts +33 -0
  147. package/src/proxmox/pbs-sync-job-form.ts +7 -6
  148. package/src/proxmox/pbs-sync-job-lifecycle.ts +46 -0
  149. package/src/proxmox/pbs-sync-job-wire.ts +71 -0
  150. package/src/proxmox/pbs-sync-job.ts +10 -41
  151. package/src/proxmox/pbs-verify-job-lifecycle.ts +52 -0
  152. package/src/proxmox/pbs-verify-job-spec.ts +76 -0
  153. package/src/proxmox/pbs-verify-job-wire.ts +58 -0
  154. package/src/proxmox/pbs-verify-job.ts +13 -85
  155. package/src/proxmox/pool.ts +92 -10
@@ -16,14 +16,17 @@
16
16
  * reconcile needs, so widening stays a deliberate act rather than a reaction to a 403.
17
17
  */
18
18
  import { Resource } from 'alchemy';
19
+ import { isResolved } from 'alchemy/Diff';
19
20
  import * as Provider from 'alchemy/Provider';
21
+ import * as pools from '@distilled.cloud/proxmox/pools';
22
+ import { ProxmoxParseError } from '@distilled.cloud/proxmox/Errors';
23
+ import * as Schema from 'effect/Schema';
20
24
  import * as Effect from 'effect/Effect';
21
- import type {
22
- PoolsPoolidGetReturn,
23
- PoolsPoolidPutParams,
24
- PoolsPostParams,
25
- } from './generated/pve.ts';
26
- import { type PveRequirements, type WithTarget, pveHandlers } from './resource.ts';
25
+ import type { PoolsPoolidPutParams, PoolsPostParams } from './generated/pve.ts';
26
+ import type { PveRequirements, PveSpec, WithTarget } from './resource-spec.ts';
27
+ import { runPve } from './distilled-pve.ts';
28
+ import { specGuards } from './resource-guard.ts';
29
+ import { formToSend } from './update-guard.ts';
27
30
 
28
31
  export interface PoolProps extends WithTarget {
29
32
  /** PVE's primary key for a pool. Changing it is a replace, not an update. */
@@ -60,9 +63,9 @@ export const poolUpdateForm = (props: PoolProps): Pick<PoolsPoolidPutParams, 'co
60
63
  comment: props.comment ?? '',
61
64
  });
62
65
 
63
- const handlers = pveHandlers<PoolProps, PoolAttributes>({
66
+ const spec = {
64
67
  attributes: (live, props) => {
65
- const row = live as PoolsPoolidGetReturn;
68
+ const row = live;
66
69
  return {
67
70
  comment: typeof row.comment === 'string' ? row.comment : '',
68
71
  members: Array.isArray(row.members) ? row.members.length : 0,
@@ -76,7 +79,86 @@ const handlers = pveHandlers<PoolProps, PoolAttributes>({
76
79
  matches: (attributes, props) => attributes.comment === (props.comment ?? ''),
77
80
  path: (props) => `pools/${props.poolid}`,
78
81
  updateForm: poolUpdateForm,
79
- });
82
+ } satisfies PveSpec<PoolProps, PoolAttributes>;
83
+
84
+ const { guardCreate, guardUpdate } = specGuards(spec);
85
+
86
+ /** Pool absence is PVE's typed 500, not a catch-all; pve-manager 9.2.11. */
87
+ export const readPool = (props: PoolProps) =>
88
+ runPve(props.target, 'read', false, pools.getPool({ poolid: props.poolid })).pipe(
89
+ Effect.flatMap((live) =>
90
+ Schema.decodeUnknownEffect(Schema.toType(pools.GetPoolResponse))(live).pipe(
91
+ Effect.mapError(
92
+ () =>
93
+ new ProxmoxParseError({
94
+ body: undefined,
95
+ cause: 'Pool response does not match its vendor schema',
96
+ }),
97
+ ),
98
+ Effect.as(live),
99
+ ),
100
+ ),
101
+ Effect.map((live) => spec.attributes({ ...live }, props)),
102
+ Effect.catchTag('PoolNotFound', () => Effect.succeed(undefined)),
103
+ Effect.catchTag('NotFound', () => Effect.succeed(undefined)),
104
+ );
105
+
106
+ export const deletePool = (props: PoolProps) =>
107
+ runPve(props.target, 'provision', true, pools.deletePool({ poolid: props.poolid })).pipe(
108
+ Effect.catchTag('PoolNotFound', () => Effect.void),
109
+ Effect.catchTag('NotFound', () => Effect.void),
110
+ );
80
111
 
81
112
  export const ProxmoxPoolProvider = () =>
82
- Provider.effect(ProxmoxPool, Effect.succeed(ProxmoxPool.Provider.of(handlers)));
113
+ Provider.effect(
114
+ ProxmoxPool,
115
+ Effect.succeed(
116
+ ProxmoxPool.Provider.of({
117
+ /** Pools are adopted explicitly; an index must not silently claim every existing pool. */
118
+ list: () => Effect.succeed([]),
119
+ read: ({ olds }) => readPool(olds),
120
+ diff: Effect.fn(function* ({ news, output }) {
121
+ if (!isResolved(news)) return undefined;
122
+ yield* guardCreate(news, output === undefined);
123
+ yield* guardUpdate(news);
124
+ if (output === undefined) return undefined;
125
+ const live = yield* readPool(news);
126
+ if (live === undefined) {
127
+ yield* guardCreate(news, true);
128
+ return { action: 'update' } as const;
129
+ }
130
+ return { action: spec.matches(live, news) ? 'noop' : 'update' } as const;
131
+ }),
132
+ reconcile: Effect.fn(function* ({ news }) {
133
+ const live = yield* readPool(news);
134
+ yield* guardCreate(news, live === undefined);
135
+ yield* guardUpdate(news);
136
+ if (live === undefined) {
137
+ yield* runPve(news.target, 'provision', true, pools.createPool(poolCreateForm(news)));
138
+ } else {
139
+ const form = formToSend(spec.matches, live, news, poolUpdateForm(news));
140
+ if (form !== undefined)
141
+ yield* runPve(
142
+ news.target,
143
+ 'provision',
144
+ true,
145
+ pools.putPool({
146
+ ...form,
147
+ poolid: news.poolid,
148
+ }),
149
+ );
150
+ }
151
+ const after = yield* readPool(news);
152
+ if (after === undefined)
153
+ return yield* Effect.fail(
154
+ new Error(
155
+ `pools/${news.poolid}: write returned success but the pool is still absent`,
156
+ ),
157
+ );
158
+ return after;
159
+ }),
160
+ /** A nonempty-pool refusal still propagates; there is no force-delete fallback. */
161
+ delete: ({ olds }) => deletePool(olds),
162
+ }),
163
+ ),
164
+ );