@homeflare/alchemy 0.37.5 → 0.37.7
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.
- package/dist/openbao/auth-role-form.d.ts +0 -6
- package/dist/openbao/auth-role-form.d.ts.map +1 -1
- package/dist/openbao/auth-role-identity.d.ts +11 -0
- package/dist/openbao/auth-role-identity.d.ts.map +1 -0
- package/dist/openbao/auth-role-wire.d.ts +23 -0
- package/dist/openbao/auth-role-wire.d.ts.map +1 -0
- package/dist/openbao/auth-role.d.ts.map +1 -1
- package/dist/openbao/bao-http.d.ts +34 -13
- package/dist/openbao/bao-http.d.ts.map +1 -1
- package/dist/openbao/distilled.d.ts +25 -0
- package/dist/openbao/distilled.d.ts.map +1 -0
- package/dist/openbao/fake-engines-roles.d.ts +5 -1
- package/dist/openbao/fake-engines-roles.d.ts.map +1 -1
- package/dist/openbao/index.js +358 -305
- package/dist/openbao/index.js.map +13 -9
- package/dist/openbao/policy-identity.d.ts +12 -0
- package/dist/openbao/policy-identity.d.ts.map +1 -0
- package/dist/openbao/policy-wire.d.ts +9 -8
- package/dist/openbao/policy-wire.d.ts.map +1 -1
- package/dist/openbao/policy.d.ts.map +1 -1
- package/dist/openbao/rename.d.ts +5 -0
- package/dist/openbao/rename.d.ts.map +1 -1
- package/dist/proxmox/firewall-alias-config.d.ts +36 -0
- package/dist/proxmox/firewall-alias-config.d.ts.map +1 -0
- package/dist/proxmox/firewall-alias-form.d.ts +3 -1
- package/dist/proxmox/firewall-alias-form.d.ts.map +1 -1
- package/dist/proxmox/firewall-alias-lifecycle.d.ts +43 -0
- package/dist/proxmox/firewall-alias-lifecycle.d.ts.map +1 -0
- package/dist/proxmox/firewall-alias.d.ts +2 -8
- package/dist/proxmox/firewall-alias.d.ts.map +1 -1
- package/dist/proxmox/index.js +795 -526
- package/dist/proxmox/index.js.map +24 -11
- package/dist/proxmox/lxc-errors.d.ts +12 -0
- package/dist/proxmox/lxc-errors.d.ts.map +1 -0
- package/dist/proxmox/lxc-lifecycle.d.ts +9 -31
- package/dist/proxmox/lxc-lifecycle.d.ts.map +1 -1
- package/dist/proxmox/lxc-read.d.ts +4 -0
- package/dist/proxmox/lxc-read.d.ts.map +1 -0
- package/dist/proxmox/lxc-task.d.ts +15 -0
- package/dist/proxmox/lxc-task.d.ts.map +1 -0
- package/dist/proxmox/network-apply-read.d.ts +7 -14
- package/dist/proxmox/network-apply-read.d.ts.map +1 -1
- package/dist/proxmox/network-apply-run.d.ts +7 -0
- package/dist/proxmox/network-apply-run.d.ts.map +1 -0
- package/dist/proxmox/network-apply.d.ts +2 -3
- package/dist/proxmox/network-apply.d.ts.map +1 -1
- package/dist/proxmox/node-network-wire.d.ts +14 -10
- package/dist/proxmox/node-network-wire.d.ts.map +1 -1
- package/dist/proxmox/qemu-errors.d.ts +5 -0
- package/dist/proxmox/qemu-errors.d.ts.map +1 -0
- package/dist/proxmox/qemu-form.d.ts +22 -0
- package/dist/proxmox/qemu-form.d.ts.map +1 -0
- package/dist/proxmox/qemu-lifecycle.d.ts +26 -0
- package/dist/proxmox/qemu-lifecycle.d.ts.map +1 -0
- package/dist/proxmox/qemu-read.d.ts +5 -0
- package/dist/proxmox/qemu-read.d.ts.map +1 -0
- package/dist/proxmox/qemu-task.d.ts +9 -0
- package/dist/proxmox/qemu-task.d.ts.map +1 -0
- package/dist/proxmox/qemu.d.ts +9 -14
- package/dist/proxmox/qemu.d.ts.map +1 -1
- package/dist/proxmox/replication-firewall-fixtures.d.ts +30 -0
- package/dist/proxmox/replication-firewall-fixtures.d.ts.map +1 -0
- package/dist/proxmox/replication-job-config.d.ts +56 -0
- package/dist/proxmox/replication-job-config.d.ts.map +1 -0
- package/dist/proxmox/replication-job-lifecycle.d.ts +62 -0
- package/dist/proxmox/replication-job-lifecycle.d.ts.map +1 -0
- package/dist/proxmox/replication-job.d.ts +5 -6
- package/dist/proxmox/replication-job.d.ts.map +1 -1
- package/dist/release/archive.d.ts.map +1 -1
- package/dist/release/index.js +3 -2
- package/dist/release/index.js.map +3 -3
- package/docs/proxmox-lxc-distilled.md +56 -0
- package/docs/proxmox-replication-firewall-distilled.md +53 -0
- package/docs/proxmox-sdk-migration.md +23 -18
- package/docs/upstream-conformance.md +5 -3
- package/package.json +3 -2
- package/src/openbao/auth-role-form.test.ts +11 -9
- package/src/openbao/auth-role-form.ts +0 -19
- package/src/openbao/auth-role-identity.ts +24 -0
- package/src/openbao/auth-role-sdk.test.ts +155 -0
- package/src/openbao/auth-role-wire.test.ts +128 -0
- package/src/openbao/auth-role-wire.ts +89 -0
- package/src/openbao/auth-role.ts +9 -9
- package/src/openbao/bao-http.ts +9 -7
- package/src/openbao/distilled-retry.test.ts +164 -0
- package/src/openbao/distilled.test.ts +146 -0
- package/src/openbao/distilled.ts +88 -0
- package/src/openbao/fake-engines-roles.ts +9 -2
- package/src/openbao/policy-distilled.test.ts +98 -0
- package/src/openbao/policy-identity.ts +24 -0
- package/src/openbao/policy-rename.test.ts +2 -2
- package/src/openbao/policy-wire.test.ts +5 -5
- package/src/openbao/policy-wire.ts +31 -11
- package/src/openbao/policy.ts +10 -13
- package/src/openbao/rename.ts +17 -1
- package/src/proxmox/firewall-alias-config.ts +39 -0
- package/src/proxmox/firewall-alias-form.ts +1 -1
- package/src/proxmox/firewall-alias-lifecycle.ts +100 -0
- package/src/proxmox/firewall-alias.ts +4 -67
- package/src/proxmox/lxc-create.test.ts +4 -1
- package/src/proxmox/lxc-distilled.test.ts +239 -0
- package/src/proxmox/lxc-errors.ts +9 -0
- package/src/proxmox/lxc-lifecycle.ts +31 -113
- package/src/proxmox/lxc-read.test.ts +29 -6
- package/src/proxmox/lxc-read.ts +88 -0
- package/src/proxmox/lxc-task.ts +71 -0
- package/src/proxmox/network-apply-distilled.test.ts +76 -0
- package/src/proxmox/network-apply-read.ts +18 -39
- package/src/proxmox/network-apply-run.ts +97 -0
- package/src/proxmox/network-apply.ts +5 -94
- package/src/proxmox/node-network-read-failure.test.ts +24 -0
- package/src/proxmox/node-network-wire.ts +15 -17
- package/src/proxmox/qemu-errors.ts +7 -0
- package/src/proxmox/qemu-form.ts +15 -0
- package/src/proxmox/qemu-lifecycle.test.ts +171 -0
- package/src/proxmox/qemu-lifecycle.ts +134 -0
- package/src/proxmox/qemu-read.ts +73 -0
- package/src/proxmox/qemu-task.ts +64 -0
- package/src/proxmox/qemu.ts +11 -44
- package/src/proxmox/replication-firewall-fixtures.ts +71 -0
- package/src/proxmox/replication-firewall-lifecycle.test.ts +109 -0
- package/src/proxmox/replication-firewall-wire.test.ts +173 -0
- package/src/proxmox/replication-job-config.ts +79 -0
- package/src/proxmox/replication-job-lifecycle.ts +113 -0
- package/src/proxmox/replication-job.ts +7 -84
- package/src/release/archive.ts +4 -1
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/** Named SDK transport; pve-manager 9.2.11 ReplicationConfig.pm defines absence and removal. */
|
|
2
|
+
import { isResolved } from 'alchemy/Diff';
|
|
3
|
+
import type { Input } from 'alchemy/Input';
|
|
4
|
+
import * as cluster from '@distilled.cloud/proxmox/cluster';
|
|
5
|
+
import { ProxmoxParseError } from '@distilled.cloud/proxmox/Errors';
|
|
6
|
+
import * as Effect from 'effect/Effect';
|
|
7
|
+
import * as Schema from 'effect/Schema';
|
|
8
|
+
import { runPve } from './distilled-pve.ts';
|
|
9
|
+
import type { ReplicationJobAttributes, ReplicationJobProps } from './replication-job.ts';
|
|
10
|
+
import { replicationJobSpec as spec } from './replication-job-config.ts';
|
|
11
|
+
import { jobId } from './replication-job-form.ts';
|
|
12
|
+
import { specGuards } from './resource-guard.ts';
|
|
13
|
+
import { formToSend } from './update-guard.ts';
|
|
14
|
+
import { int } from './values.ts';
|
|
15
|
+
|
|
16
|
+
const { guardCreate, guardUpdate } = specGuards(spec);
|
|
17
|
+
const malformed = () =>
|
|
18
|
+
new ProxmoxParseError({
|
|
19
|
+
body: undefined,
|
|
20
|
+
cause: 'Replication job response does not match the requested vendor identity',
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
export const readReplicationJob = (props: ReplicationJobProps) =>
|
|
24
|
+
runPve(props.target, 'read', false, cluster.getClusterReplication({ id: jobId(props) })).pipe(
|
|
25
|
+
Effect.flatMap((live) =>
|
|
26
|
+
// ⚠️ SectionConfig releases can echo `guest`/`jobnum` (and `rate`) as text rather than a JSON
|
|
27
|
+
// number — measured on replication-job-config.ts's own `int()`/`rateOf()` fields, which is
|
|
28
|
+
// why every other numeric read in this family goes through `int()` rather than a strict
|
|
29
|
+
// type. `GetClusterReplicationResponse` types `guest`/`jobnum` as `S.Number` with no
|
|
30
|
+
// coercion, so a string echo fails decode before the identity check below ever runs;
|
|
31
|
+
// normalize both through `int()` first, same as `rate` is normalized to skip its own
|
|
32
|
+
// release-dependent shape.
|
|
33
|
+
Schema.decodeUnknownEffect(Schema.toType(cluster.GetClusterReplicationResponse))({
|
|
34
|
+
...live,
|
|
35
|
+
guest: int(live.guest, Number.NaN),
|
|
36
|
+
jobnum: int(live.jobnum, Number.NaN),
|
|
37
|
+
rate: undefined,
|
|
38
|
+
}).pipe(Effect.mapError(malformed), Effect.as(live)),
|
|
39
|
+
),
|
|
40
|
+
Effect.flatMap((live) =>
|
|
41
|
+
// ⛔ `id` alone (a string PVE always returns, e.g. "900-0") already proves identity; the
|
|
42
|
+
// guest/jobnum recheck is defense in depth, so it must tolerate the same string-or-number
|
|
43
|
+
// wire shape as the decode above rather than `===` against the raw, possibly-text value.
|
|
44
|
+
live.id === jobId(props) &&
|
|
45
|
+
int(live.guest, Number.NaN) === props.guest &&
|
|
46
|
+
int(live.jobnum, Number.NaN) === props.jobnum
|
|
47
|
+
? Effect.succeed(spec.attributes({ ...live }, props))
|
|
48
|
+
: Effect.fail(malformed()),
|
|
49
|
+
),
|
|
50
|
+
Effect.catchTag('ReplicationJobNotFound', () => Effect.succeed(undefined)),
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
/** DELETE without force/keep marks full background removal; it must never force immediate removal. */
|
|
54
|
+
export const deleteReplicationJob = (props: ReplicationJobProps) =>
|
|
55
|
+
runPve(
|
|
56
|
+
props.target,
|
|
57
|
+
'provision',
|
|
58
|
+
true,
|
|
59
|
+
cluster.deleteClusterReplication({ id: jobId(props) }),
|
|
60
|
+
).pipe(Effect.catchTag('ReplicationJobNotFound', () => Effect.void));
|
|
61
|
+
|
|
62
|
+
export const replicationJobHandlers = {
|
|
63
|
+
// Explicit declaration is the only way to adopt a job and gain deletion authority over replicas.
|
|
64
|
+
list: () => Effect.succeed([]),
|
|
65
|
+
read: ({ olds }: { olds: ReplicationJobProps }) => readReplicationJob(olds),
|
|
66
|
+
diff: Effect.fn(function* ({
|
|
67
|
+
news,
|
|
68
|
+
output,
|
|
69
|
+
}: {
|
|
70
|
+
news: Input<ReplicationJobProps>;
|
|
71
|
+
output: ReplicationJobAttributes | undefined;
|
|
72
|
+
}) {
|
|
73
|
+
if (!isResolved(news)) return undefined;
|
|
74
|
+
yield* guardCreate(news, output === undefined);
|
|
75
|
+
yield* guardUpdate(news);
|
|
76
|
+
if (output === undefined) return undefined;
|
|
77
|
+
const live = yield* readReplicationJob(news);
|
|
78
|
+
if (live === undefined) {
|
|
79
|
+
yield* guardCreate(news, true);
|
|
80
|
+
return { action: 'update' } as const;
|
|
81
|
+
}
|
|
82
|
+
return { action: spec.matches(live, news) ? 'noop' : 'update' } as const;
|
|
83
|
+
}),
|
|
84
|
+
reconcile: Effect.fn(function* ({ news }: { news: ReplicationJobProps }) {
|
|
85
|
+
const live = yield* readReplicationJob(news);
|
|
86
|
+
yield* guardCreate(news, live === undefined);
|
|
87
|
+
yield* guardUpdate(news);
|
|
88
|
+
if (live === undefined) {
|
|
89
|
+
yield* runPve(
|
|
90
|
+
news.target,
|
|
91
|
+
'provision',
|
|
92
|
+
true,
|
|
93
|
+
cluster.createClusterReplication(spec.createForm(news)),
|
|
94
|
+
);
|
|
95
|
+
} else {
|
|
96
|
+
const form = formToSend(spec.matches, live, news, spec.updateForm(news));
|
|
97
|
+
if (form !== undefined)
|
|
98
|
+
yield* runPve(
|
|
99
|
+
news.target,
|
|
100
|
+
'provision',
|
|
101
|
+
true,
|
|
102
|
+
cluster.putClusterReplication({ ...form, id: jobId(news) }),
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
const after = yield* readReplicationJob(news);
|
|
106
|
+
if (after === undefined)
|
|
107
|
+
return yield* Effect.fail(
|
|
108
|
+
new Error(`${spec.path(news)}: write returned success but the resource is still absent`),
|
|
109
|
+
);
|
|
110
|
+
return after;
|
|
111
|
+
}),
|
|
112
|
+
delete: ({ olds }: { olds: ReplicationJobProps }) => deleteReplicationJob(olds),
|
|
113
|
+
};
|
|
@@ -56,13 +56,12 @@
|
|
|
56
56
|
* checks `VM.Audit` on `/vms/<guest>`, `hf-read@pve` holds the built-in `PVEAuditor` at `/`
|
|
57
57
|
* with propagate — both MEASURED — and `PVEAuditor` carries `VM.Audit`. This family is not a
|
|
58
58
|
* fourth one that must borrow the provision lease.
|
|
59
|
-
* ⚠️ A MISSING JOB IS A 500, NOT A 404 — "no such replication job '100-0'" — and
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
* later, from the create.
|
|
59
|
+
* ⚠️ A MISSING JOB IS A 500, NOT A 404 — "no such replication job '100-0'" — and distilled now
|
|
60
|
+
* exposes a precise missing-job tag. Permission failures and unrelated server errors propagate
|
|
61
|
+
* before any write; only the vendor missing-job signal means absent.
|
|
63
62
|
*
|
|
64
63
|
* ⛔ DELETE DOES NOT DELETE, AND A DESTROY IS NOT FREE. MEASURED: with neither `force` nor `keep` —
|
|
65
|
-
* and the
|
|
64
|
+
* and the named SDK delete sends neither option — the handler only sets `remove_job = 'full'`
|
|
66
65
|
* on the job and writes the config back. The job stays in replication.cfg until the SOURCE node's
|
|
67
66
|
* `pvesr` timer next runs it, at which point it removes the local replication snapshots, removes
|
|
68
67
|
* the replicated volumes on the target, and finally removes itself. So a destroy costs the
|
|
@@ -86,9 +85,8 @@
|
|
|
86
85
|
import { Resource } from 'alchemy';
|
|
87
86
|
import * as Provider from 'alchemy/Provider';
|
|
88
87
|
import * as Effect from 'effect/Effect';
|
|
89
|
-
import {
|
|
90
|
-
import {
|
|
91
|
-
import { bool, int, text } from './values.ts';
|
|
88
|
+
import type { PveRequirements, WithTarget } from './resource-spec.ts';
|
|
89
|
+
import { replicationJobHandlers } from './replication-job-lifecycle.ts';
|
|
92
90
|
|
|
93
91
|
export interface ReplicationJobProps extends WithTarget {
|
|
94
92
|
/** ⛔ The guest being replicated. 100 or above — PVE reserves everything below. Identity. */
|
|
@@ -164,81 +162,6 @@ export interface ProxmoxReplicationJob extends Resource<
|
|
|
164
162
|
|
|
165
163
|
export const ProxmoxReplicationJob = Resource<ProxmoxReplicationJob>('Proxmox.ReplicationJob');
|
|
166
164
|
|
|
167
|
-
/**
|
|
168
|
-
* ⚠️ `rate` IS THE ONLY FLOAT IN THIS PACKAGE, so it gets a coercion of its own rather than a sixth
|
|
169
|
-
* entry in `values.ts` — the rule that module states for itself: a coercion only one PVE object
|
|
170
|
-
* needs belongs in that object's file, next to the field it serves. `int` cannot serve it, since
|
|
171
|
-
* `Number.parseInt('10.5')` is 10 and a rate silently rounded down is both a limit nobody chose
|
|
172
|
-
* and a comparison that never settles. It accepts a string for the reason `int` does: these are
|
|
173
|
-
* SectionConfig sections, and whether `10` arrives as a number is a property of the release.
|
|
174
|
-
* ⚠️ 0 MEANS UNSET, and is safe as the marker because PVE's schema gives `rate` a minimum of 1 —
|
|
175
|
-
* the same trick as `metric-server.ts`'s `UNSET = -1`, for the same reason.
|
|
176
|
-
*/
|
|
177
|
-
const rateOf = (value: unknown): number => {
|
|
178
|
-
if (typeof value === 'number') return value;
|
|
179
|
-
const parsed = Number.parseFloat(text(value));
|
|
180
|
-
return Number.isFinite(parsed) ? parsed : 0;
|
|
181
|
-
};
|
|
182
|
-
|
|
183
|
-
const handlers = pveHandlers<ReplicationJobProps, ReplicationJobAttributes>({
|
|
184
|
-
/**
|
|
185
|
-
* ⚠️ EVERY FALLBACK IS PVE'S DOCUMENTED DEFAULT, NOT `false`/`''` PICKED FOR CONVENIENCE, so the
|
|
186
|
-
* answer does not depend on whether a given release echoes back a key it never wrote.
|
|
187
|
-
* SectionConfig stores what it was handed; a job created in the UI without a schedule has no
|
|
188
|
-
* schedule line, and reading that absence as anything but `DEFAULT_SCHEDULE` would report
|
|
189
|
-
* drift on a job nobody touched and then WRITE the reading back.
|
|
190
|
-
* ⛔ NO "IS IT REALLY THERE" GUARD, for `backup-job.ts`'s reason: absence is the API declining to
|
|
191
|
-
* answer, which `pveOperations.read` already handles, not a key missing from an answer that did
|
|
192
|
-
* arrive. `digest` is deliberately absent too — it changes when ANY job in the file changes,
|
|
193
|
-
* including one somebody else declared (`ha-resource.ts` hit exactly that).
|
|
194
|
-
*/
|
|
195
|
-
attributes: (live, props) => ({
|
|
196
|
-
comment: text(live['comment'], ''),
|
|
197
|
-
disable: bool(live['disable'], false),
|
|
198
|
-
guest: int(live['guest'], props.guest),
|
|
199
|
-
id: text(live['id'], jobId(props)),
|
|
200
|
-
jobnum: int(live['jobnum'], props.jobnum),
|
|
201
|
-
rate: rateOf(live['rate']),
|
|
202
|
-
remove_job: text(live['remove_job'], ''),
|
|
203
|
-
schedule: text(live['schedule'], DEFAULT_SCHEDULE),
|
|
204
|
-
source: text(live['source'], ''),
|
|
205
|
-
targetNode: text(live['target'], props.targetNode),
|
|
206
|
-
type: text(live['type'], 'local'),
|
|
207
|
-
}),
|
|
208
|
-
collection: () => 'cluster/replication',
|
|
209
|
-
createForm: createBody,
|
|
210
|
-
/**
|
|
211
|
-
* ⚠️ `targetNode` AND `source` ARE OUT OF THIS COMPARISON FOR TWO REASONS EACH, AND EITHER ALONE
|
|
212
|
-
* WOULD BE ENOUGH. They are create-only — `target` is a `fixed` option, `source` has no honest
|
|
213
|
-
* declared value — AND PVE REWRITES BOTH BEHIND YOUR BACK. MEASURED in
|
|
214
|
-
* `switch_replication_job_target_nolock`: when a guest migrates, PVE sets
|
|
215
|
-
* `$jobcfg->{target} = $new_target` and `$jobcfg->{source} = $old_target`, so the job follows
|
|
216
|
-
* the guest. Compared, a single migration would make every later plan report an update that the
|
|
217
|
-
* PUT cannot perform, forever. The cost of leaving them out is stated on the `targetNode` prop:
|
|
218
|
-
* a changed target plans as noop, and a migrated job is not dragged back.
|
|
219
|
-
* ⚠️ `guest`, `jobnum`, `type` AND `id` ARE NOT COMPARED EITHER: the first two are the id in
|
|
220
|
-
* integer form, `type` has one legal value, and `id` is the path — a change there is a
|
|
221
|
-
* different object, which reads as absent and is created.
|
|
222
|
-
* ⛔ `remove_job` IS COMPARED, AND IT IS THE ONE FIELD HERE THAT LOOKS LIKE A FOREVER-UPDATE AND
|
|
223
|
-
* IS NOT. A DELETE marks the job rather than removing it, so a re-declared id can name a job
|
|
224
|
-
* that is busy deleting itself, and matching that would be a noop over a vanishing object. It
|
|
225
|
-
* settles because `updateBody` actually clears the marker — see there.
|
|
226
|
-
*/
|
|
227
|
-
/** The vendor rules these forms are checked against at plan time — resource-spec.ts. */
|
|
228
|
-
endpoint: {
|
|
229
|
-
create: 'pve:POST /cluster/replication',
|
|
230
|
-
update: 'pve:PUT /cluster/replication/{id}',
|
|
231
|
-
},
|
|
232
|
-
matches: (attributes, props) =>
|
|
233
|
-
attributes.remove_job === '' &&
|
|
234
|
-
attributes.schedule === (props.schedule ?? DEFAULT_SCHEDULE) &&
|
|
235
|
-
attributes.disable === (props.disable === true) &&
|
|
236
|
-
attributes.comment === (props.comment ?? '') &&
|
|
237
|
-
(props.rate === undefined || attributes.rate === props.rate),
|
|
238
|
-
path: (props) => `cluster/replication/${jobId(props)}`,
|
|
239
|
-
updateForm: updateBody,
|
|
240
|
-
});
|
|
241
|
-
|
|
242
165
|
/**
|
|
243
166
|
* ⛔ Empty `list` like every other resource here: `GET /cluster/replication` hands back every job an
|
|
244
167
|
* operator ever made, and adopting one is how a later `alchemy destroy` takes away a standby copy
|
|
@@ -250,5 +173,5 @@ const handlers = pveHandlers<ReplicationJobProps, ReplicationJobAttributes>({
|
|
|
250
173
|
export const ProxmoxReplicationJobProvider = () =>
|
|
251
174
|
Provider.effect(
|
|
252
175
|
ProxmoxReplicationJob,
|
|
253
|
-
Effect.succeed(ProxmoxReplicationJob.Provider.of(
|
|
176
|
+
Effect.succeed(ProxmoxReplicationJob.Provider.of(replicationJobHandlers)),
|
|
254
177
|
);
|
package/src/release/archive.ts
CHANGED
|
@@ -54,7 +54,10 @@ export const extractMembers = async (
|
|
|
54
54
|
offset += SLICE;
|
|
55
55
|
},
|
|
56
56
|
});
|
|
57
|
-
|
|
57
|
+
// ⚠️ TS 7's DOM lib types this writable chunk as BufferSource, which pipeThrough
|
|
58
|
+
// will not accept as Uint8Array. The slices enqueued above are Uint8Array.
|
|
59
|
+
const gunzip = new DecompressionStream('gzip') as ReadableWritablePair<Uint8Array, Uint8Array>;
|
|
60
|
+
const unzipped = source.pipeThrough(gunzip).getReader();
|
|
58
61
|
try {
|
|
59
62
|
for (;;) {
|
|
60
63
|
const { done, value } = await unzipped.read();
|