@homeflare/alchemy 0.22.0 → 0.24.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.
- package/README.md +15 -1
- package/dist/litellm/client.d.ts +76 -0
- package/dist/litellm/client.d.ts.map +1 -0
- package/dist/litellm/credentials.d.ts +48 -0
- package/dist/litellm/credentials.d.ts.map +1 -0
- package/dist/litellm/fake-litellm.d.ts +36 -0
- package/dist/litellm/fake-litellm.d.ts.map +1 -0
- package/dist/litellm/fake-stack.d.ts +30 -0
- package/dist/litellm/fake-stack.d.ts.map +1 -0
- package/dist/litellm/generated/pass-through.d.ts +71 -0
- package/dist/litellm/generated/pass-through.d.ts.map +1 -0
- package/dist/litellm/index.d.ts +13 -0
- package/dist/litellm/index.d.ts.map +1 -0
- package/dist/litellm/index.js +270 -0
- package/dist/litellm/index.js.map +14 -0
- package/dist/litellm/pass-through-endpoint.d.ts +94 -0
- package/dist/litellm/pass-through-endpoint.d.ts.map +1 -0
- package/dist/litellm/pass-through-form.d.ts +40 -0
- package/dist/litellm/pass-through-form.d.ts.map +1 -0
- package/dist/litellm/providers.d.ts +16 -0
- package/dist/litellm/providers.d.ts.map +1 -0
- package/dist/postgres/connection.d.ts +50 -0
- package/dist/postgres/connection.d.ts.map +1 -0
- package/dist/postgres/database-attrs.d.ts +79 -0
- package/dist/postgres/database-attrs.d.ts.map +1 -0
- package/dist/postgres/database-sql.d.ts +73 -0
- package/dist/postgres/database-sql.d.ts.map +1 -0
- package/dist/postgres/database.d.ts +63 -0
- package/dist/postgres/database.d.ts.map +1 -0
- package/dist/postgres/errors.d.ts +86 -0
- package/dist/postgres/errors.d.ts.map +1 -0
- package/dist/postgres/fake-sql.d.ts +22 -0
- package/dist/postgres/fake-sql.d.ts.map +1 -0
- package/dist/postgres/index.d.ts +16 -0
- package/dist/postgres/index.d.ts.map +1 -0
- package/dist/postgres/index.js +238 -0
- package/dist/postgres/index.js.map +15 -0
- package/dist/postgres/providers.d.ts +14 -0
- package/dist/postgres/providers.d.ts.map +1 -0
- package/dist/proxmox/api-token-form.d.ts +12 -1
- package/dist/proxmox/api-token-form.d.ts.map +1 -1
- package/dist/proxmox/api-token.d.ts +8 -3
- package/dist/proxmox/api-token.d.ts.map +1 -1
- package/dist/proxmox/generated/pve/cluster-sdn-fabrics.d.ts +13 -13
- package/dist/proxmox/generated/pve/cluster-sdn-fabrics.d.ts.map +1 -1
- package/dist/proxmox/index.d.ts +9 -2
- package/dist/proxmox/index.d.ts.map +1 -1
- package/dist/proxmox/index.js +17 -9
- package/dist/proxmox/index.js.map +8 -7
- package/dist/proxmox/zfs-pool-spec.d.ts +27 -0
- package/dist/proxmox/zfs-pool-spec.d.ts.map +1 -0
- package/dist/proxmox/zfs-pool-write.d.ts +48 -7
- package/dist/proxmox/zfs-pool-write.d.ts.map +1 -1
- package/dist/proxmox/zfs-pool.d.ts +52 -22
- package/dist/proxmox/zfs-pool.d.ts.map +1 -1
- package/dist/release/archive.d.ts +6 -2
- package/dist/release/archive.d.ts.map +1 -1
- package/dist/release/binary-form.d.ts +14 -9
- package/dist/release/binary-form.d.ts.map +1 -1
- package/dist/release/binary-pins.d.ts +17 -0
- package/dist/release/binary-pins.d.ts.map +1 -0
- package/dist/release/catalog.d.ts.map +1 -1
- package/dist/release/index.js +90 -40
- package/dist/release/index.js.map +8 -7
- package/dist/release/tar.d.ts +9 -2
- package/dist/release/tar.d.ts.map +1 -1
- package/docs/adopted-deploys.md +14 -0
- package/docs/litellm.md +110 -0
- package/docs/peers.md +8 -1
- package/docs/postgres.md +125 -0
- package/docs/release-binary-catalogs.md +40 -4
- package/docs/release-binary-refusals.md +22 -8
- package/docs/release-binary-upstream.md +33 -15
- package/package.json +10 -1
- package/src/litellm/client.ts +195 -0
- package/src/litellm/credentials.ts +73 -0
- package/src/litellm/fake-litellm.ts +114 -0
- package/src/litellm/fake-stack.ts +100 -0
- package/src/litellm/generated/pass-through.ts +73 -0
- package/src/litellm/index.ts +42 -0
- package/src/litellm/pass-through-endpoint-delete-swallow.test.ts +66 -0
- package/src/litellm/pass-through-endpoint-replace.test.ts +84 -0
- package/src/litellm/pass-through-endpoint.test.ts +208 -0
- package/src/litellm/pass-through-endpoint.ts +258 -0
- package/src/litellm/pass-through-form.test.ts +128 -0
- package/src/litellm/pass-through-form.ts +136 -0
- package/src/litellm/providers.ts +19 -0
- package/src/postgres/connection.ts +62 -0
- package/src/postgres/database-attrs.ts +87 -0
- package/src/postgres/database-bigint-serialization.test.ts +140 -0
- package/src/postgres/database-lifecycle.test.ts +113 -0
- package/src/postgres/database-provenance.test.ts +118 -0
- package/src/postgres/database-refusals.test.ts +135 -0
- package/src/postgres/database-sql.ts +174 -0
- package/src/postgres/database.ts +193 -0
- package/src/postgres/errors.ts +131 -0
- package/src/postgres/fake-sql.ts +123 -0
- package/src/postgres/fixtures/pg-REL_18_6-create-database-synopsis.txt +26 -0
- package/src/postgres/fixtures/pg-REL_18_6-pg_database.h.txt +67 -0
- package/src/postgres/index.ts +30 -0
- package/src/postgres/providers.ts +16 -0
- package/src/proxmox/api-token-adopt.test.ts +138 -0
- package/src/proxmox/api-token-form.ts +19 -2
- package/src/proxmox/api-token.ts +8 -3
- package/src/proxmox/generated/pve/cluster-sdn-fabrics.ts +23 -13
- package/src/proxmox/index.test.ts +5 -2
- package/src/proxmox/index.ts +9 -2
- package/src/proxmox/zfs-pool-adopt.test.ts +141 -0
- package/src/proxmox/zfs-pool-spec.ts +76 -0
- package/src/proxmox/zfs-pool-write.ts +63 -9
- package/src/proxmox/zfs-pool.ts +55 -97
- package/src/release/archive.ts +10 -4
- package/src/release/binary-form.ts +24 -76
- package/src/release/binary-pins.ts +95 -0
- package/src/release/binary-root.test.ts +122 -0
- package/src/release/catalog.ts +10 -2
- package/src/release/tar-root.test.ts +114 -0
- package/src/release/tar.ts +78 -12
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `PveSpec` for `Proxmox.ZfsPool`: where a pool lives, what its GET reports, and the vendor
|
|
3
|
+
* endpoint its create form is checked against.
|
|
4
|
+
*
|
|
5
|
+
* ★ SPLIT OUT OF zfs-pool.ts TO KEEP THAT FILE UNDER THE 250-LINE CAP, and the seam is the same
|
|
6
|
+
* one metric-server-form.ts and zfs-pool-write.ts already cut on: zfs-pool.ts answers "what is
|
|
7
|
+
* a zpool and when has it changed", this file answers "what PVE's own GET/{node}/disks/zfs
|
|
8
|
+
* collection and item say, read into that shape". `leafDevices` moved with `spec` because it
|
|
9
|
+
* exists for exactly one field of `spec.attributes` and nothing else in this package calls it.
|
|
10
|
+
*
|
|
11
|
+
* ⚠️ THE `import type` BACK TO zfs-pool.ts IS A CYCLE ON PAPER ONLY — type-only, erased before
|
|
12
|
+
* anything runs, so the resource's public shape stays in the file that declares the resource.
|
|
13
|
+
* Same reasoning as zfs-pool-write.ts.
|
|
14
|
+
*/
|
|
15
|
+
import type { PveSpec } from './resource.ts';
|
|
16
|
+
import type { ZfsPoolAttributes, ZfsPoolProps } from './zfs-pool.ts';
|
|
17
|
+
/**
|
|
18
|
+
* The device paths at the bottom of PVE's vdev tree, in the order ZFS reports them.
|
|
19
|
+
*
|
|
20
|
+
* ⚠️ "NO CHILDREN" IS THE LEAF TEST, AND IT IS NOT A GUESS — MEASURED in the node's `preparetree`,
|
|
21
|
+
* which sets `leaf` to 0 when there are children and 1 otherwise. Reading the flag would work
|
|
22
|
+
* equally well; recursing on `children` needs no second field to be present and is what makes
|
|
23
|
+
* nested sections (mirrors inside a raid10, `spares`, `cache`) flatten correctly.
|
|
24
|
+
*/
|
|
25
|
+
export declare const leafDevices: (children: unknown) => string[];
|
|
26
|
+
export declare const spec: PveSpec<ZfsPoolProps, ZfsPoolAttributes>;
|
|
27
|
+
//# sourceMappingURL=zfs-pool-spec.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zfs-pool-spec.d.ts","sourceRoot":"","sources":["../../src/proxmox/zfs-pool-spec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAG7C,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAErE;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW,aAAc,OAAO,KAAG,MAAM,EAO9C,CAAC;AAET,eAAO,MAAM,IAAI,EAAE,OAAO,CAAC,YAAY,EAAE,iBAAiB,CAuCzD,CAAC"}
|
|
@@ -35,15 +35,44 @@ import type { ZfsPoolAttributes, ZfsPoolProps } from './zfs-pool.ts';
|
|
|
35
35
|
* declaration asked for, and this is the one call that writes to physical disks.
|
|
36
36
|
*/
|
|
37
37
|
/**
|
|
38
|
-
* The vendor rules
|
|
38
|
+
* The vendor rules a FULL declaration's create form is checked against at plan time.
|
|
39
39
|
*
|
|
40
40
|
* ⛔ HERE RATHER THAN INLINE IN zfs-pool.ts BECAUSE THAT FILE IS ALREADY OVER THE 250-LINE CAP,
|
|
41
41
|
* and a `pve:POST …` literal has to live in a non-test source file for the generator to table
|
|
42
|
-
* it at all.
|
|
42
|
+
* it at all. zfs-pool-spec.ts imports it on the line it already imports `createForm` from.
|
|
43
43
|
* ⚠️ NO UPDATE KEY: PVE registers no PUT under `/nodes/{node}/disks/zfs`, which is exactly why
|
|
44
44
|
* the spec declares no `updateForm` and a changed prop is a REPLACE.
|
|
45
|
+
* ⚠️ `spec.endpoint` DOES NOT RETURN THIS CONSTANT DIRECTLY — see the ★ on `zfsPoolEndpoint`
|
|
46
|
+
* below, and the ⛔ on `devices`/`raidlevel` in zfs-pool.ts (decision 9, 2026-09-23).
|
|
45
47
|
*/
|
|
46
48
|
export declare const ZFS_POOL_ENDPOINT: EndpointPair;
|
|
49
|
+
/**
|
|
50
|
+
* ★ `PveSpec['endpoint']` AS A FUNCTION OF PROPS, THE WAY notification-target.ts AND
|
|
51
|
+
* ceph-daemon.ts ALREADY DO IT FOR A DIFFERENT REASON (resource-spec.ts's ⚠️ on that field).
|
|
52
|
+
* Here the reason is decision 9's adopt-only mode: `guardCreate` runs on the FIRST plan of
|
|
53
|
+
* ANY declaration (resource.ts `ops.diff`, `output === undefined` branch) and checks the
|
|
54
|
+
* create form's REQUIRED fields whenever `endpoint.create` resolves to a key — and PVE marks
|
|
55
|
+
* both `devices` and `raidlevel` required (measured, codegen/constraints's `pve-nodes-disks`
|
|
56
|
+
* row). An adopt-only declaration deliberately omits both, so returning the real key there
|
|
57
|
+
* would refuse a MATCHING adoption at plan time, before `read` ever ran — exactly the gap
|
|
58
|
+
* `guardCreate`/`guardUpdate` being exported (resource.ts's ★) says this family must not fall
|
|
59
|
+
* through. Returning `{}` (no `create` key) makes `guardCreate` a no-op (`constraint-guard.ts`:
|
|
60
|
+
* `create === undefined ? Effect.void : …`) for an adopt-only declaration, while a FULL one
|
|
61
|
+
* (`devices` and `raidlevel` both present) is checked exactly as before.
|
|
62
|
+
* ⚠️ THE RUNTIME BACKSTOP IS `createPool` BELOW, NOT THIS FUNCTION. Skipping the plan-time guard
|
|
63
|
+
* for an adopt-only declaration does not skip the check that the pool is actually there —
|
|
64
|
+
* `createPool` refuses before any POST when the read comes back absent and either field is
|
|
65
|
+
* undefined, which is what a genuinely absent adopt-only declaration hits at apply time.
|
|
66
|
+
*/
|
|
67
|
+
export declare const zfsPoolEndpoint: (props: ZfsPoolProps) => EndpointPair;
|
|
68
|
+
/**
|
|
69
|
+
* ⚠️ EVERY KEY IS OMITTED WHEN ABSENT, `devices` AND `raidlevel` INCLUDED — not just the
|
|
70
|
+
* optional ones. An adopt-only declaration (both undefined, decision 9) must produce a form
|
|
71
|
+
* `createPool`'s own guard can inspect for their absence, and one this function itself does
|
|
72
|
+
* not throw building: `props.devices.join(',')` on an undefined `devices` is exactly the
|
|
73
|
+
* `TypeError` the plan-time guard hit before this change, because `guardCreate` evaluates
|
|
74
|
+
* `spec.createForm(props)` eagerly (resource-guard.ts) — before it even looks at `presence`.
|
|
75
|
+
*/
|
|
47
76
|
export declare const createForm: (props: ZfsPoolProps) => Record<string, string>;
|
|
48
77
|
/**
|
|
49
78
|
* Create the pool if it is absent, and otherwise leave it completely alone.
|
|
@@ -69,14 +98,26 @@ export declare const createForm: (props: ZfsPoolProps) => Record<string, string>
|
|
|
69
98
|
* ⚠️ SHORT POLLS WITH A HARD CAP, NOT ONE LONG WAIT. `read` cannot tell "not yet" from "forbidden"
|
|
70
99
|
* — both are `undefined` — so the loop gives up with the UPID rather than retrying forever, and
|
|
71
100
|
* the UPID is the only thing that leads to the worker's real error.
|
|
101
|
+
*
|
|
102
|
+
* ⛔ THE ADOPT-ONLY GUARD, RIGHT AFTER THE READ AND BEFORE ANYTHING ELSE. Decision 9 (2026-09-23):
|
|
103
|
+
* omitting `devices` and `raidlevel` declares a pool this resource may only ADOPT, for a pool
|
|
104
|
+
* PVE's own schema cannot fully describe — n1's `speed` is a stripe, and `raidlevel`'s enum has
|
|
105
|
+
* no stripe (zfs-pool.ts). `zfsPoolEndpoint` above already keeps the vendor constraint guard
|
|
106
|
+
* from refusing such a declaration at PLAN time; this is the matching refusal at APPLY time, for
|
|
107
|
+
* the one case that guard cannot see — the pool genuinely is not there. MEASURED without it: a
|
|
108
|
+
* tolerant `createForm` sends `POST … {name: "rpool"}` with no `devices`/`raidlevel`, which PVE's
|
|
109
|
+
* own schema would 400 on eventually, but only after the request left this process; refusing
|
|
110
|
+
* here is the same `Effect.die` idiom as every other refusal in this package, and it costs
|
|
111
|
+
* nothing this pool did not already lack.
|
|
72
112
|
*/
|
|
73
113
|
export declare const createPool: <E, R>(props: ZfsPoolProps, collection: string, path: string, read: (props: ZfsPoolProps) => Effect.Effect<ZfsPoolAttributes | undefined, E, R>) => Effect.Effect<ZfsPoolAttributes, E | Error, R | import("effect/unstable/http/HttpClient").HttpClient>;
|
|
74
114
|
/**
|
|
75
|
-
* ⛔ `DELETE /nodes/{node}/disks/zfs/{name}` RUNS `zpool destroy` AND THE DATASETS GO WITH IT.
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
* `defaultRemovalPolicy: 'retain'`
|
|
79
|
-
*
|
|
115
|
+
* ⛔ `DELETE /nodes/{node}/disks/zfs/{name}` RUNS `zpool destroy` AND THE DATASETS GO WITH IT. Per
|
|
116
|
+
* S14 (alchemy-provider-standard), it is implemented rather than stubbed — a `delete` that
|
|
117
|
+
* silently does nothing lies to whoever reads the plan — and what keeps an ordinary removed
|
|
118
|
+
* declaration from reaching it is the resource's `defaultRemovalPolicy: 'retain'` (zfs-pool.ts):
|
|
119
|
+
* an orphaned pool is forgotten, not destroyed, unless a caller opts in with
|
|
120
|
+
* `.pipe(RemovalPolicy.destroy())`. See the corrected header in zfs-pool.ts.
|
|
80
121
|
*
|
|
81
122
|
* ⚠️ `cleanup-config` IS NOT SENT. It would additionally remove the PVE storage entry pointing
|
|
82
123
|
* at this pool, which is a different object with its own resource (`Proxmox.Storage`); one
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zfs-pool-write.d.ts","sourceRoot":"","sources":["../../src/proxmox/zfs-pool-write.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAErE;;;;;;;;;;;;;GAaG;AACH
|
|
1
|
+
{"version":3,"file":"zfs-pool-write.d.ts","sourceRoot":"","sources":["../../src/proxmox/zfs-pool-write.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAErE;;;;;;;;;;;;;GAaG;AACH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iBAAiB,EAAE,YAA6D,CAAC;AAE9F;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,eAAe,UAAW,YAAY,KAAG,YACiC,CAAC;AAExF;;;;;;;GAOG;AACH,eAAO,MAAM,UAAU,UAAW,YAAY,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAOpE,CAAC;AAKH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,eAAO,MAAM,UAAU,GAAI,CAAC,EAAE,CAAC,SACtB,YAAY,cACP,MAAM,QACZ,MAAM,QACN,CAAC,KAAK,EAAE,YAAY,KAAK,MAAM,CAAC,MAAM,CAAC,iBAAiB,GAAG,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC,0GA8C/E,CAAC;AAEL;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,WAAW,SAAU,YAAY,QAAQ,MAAM,wFACb,CAAC"}
|
|
@@ -6,20 +6,22 @@
|
|
|
6
6
|
* moment the worker runs. Every other decision in this file follows from that one sentence, and
|
|
7
7
|
* each of them is written out below rather than left to be inferred.
|
|
8
8
|
*
|
|
9
|
-
* ⛔
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* `
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
9
|
+
* ⛔ ★ CORRECTED 2026-09-23 — THIS PARAGRAPH PREVIOUSLY SAID "DESTROY IS REFUSED" AND "`delete`
|
|
10
|
+
* MAKES NO API CALL". THAT WAS FALSE: MEASURED against this file's own `handlers.delete` below,
|
|
11
|
+
* which calls `destroyPool`, which sends `DELETE /nodes/{node}/disks/zfs/{name}` — the node's
|
|
12
|
+
* own source forks `zfsremove`, which runs `zpool destroy <name>` unconditionally, and with
|
|
13
|
+
* `cleanup-disks` then calls `wipe_blockdev` on every member (neither flag is offered as a prop,
|
|
14
|
+
* because there is no call here for them to reach). `delete` is FULLY IMPLEMENTED, per S14
|
|
15
|
+
* (alchemy-provider-standard): a `delete` that silently does nothing lies to whoever reads the
|
|
16
|
+
* plan. What actually keeps an ordinary removed declaration from reaching it is
|
|
17
|
+
* `defaultRemovalPolicy: 'retain'` below — Alchemy SKIPS `provider.delete` entirely for an
|
|
18
|
+
* orphaned or destroyed resource under `retain`, so the state row drops and the zpool and its
|
|
19
|
+
* data are left exactly where they are. The asymmetry is still the whole argument: an ORPHANED
|
|
20
|
+
* pool is recoverable (declare it again, or destroy it by hand once a human has looked at which
|
|
21
|
+
* disks are in it), a DESTROYED pool is not — which is why `retain` is the default rather than a
|
|
22
|
+
* documented caution, and why `delete` runs only for a caller who opts in by name with
|
|
23
|
+
* `.pipe(RemovalPolicy.destroy())`. See the ★ on `defaultRemovalPolicy` in resource-spec.ts, and
|
|
24
|
+
* zfs-pool-adopt.test.ts's mutation row, which pins exactly one DELETE reaching the fake cluster.
|
|
23
25
|
*
|
|
24
26
|
* ⛔ THERE IS NO PUT ON THIS FAMILY AT ALL. MEASURED from the cluster's own published schema
|
|
25
27
|
* (`/usr/share/pve-docs/api-viewer/apidoc.js`, read on node-b 2026-09-13): `/nodes/{node}/disks/zfs`
|
|
@@ -47,19 +49,37 @@
|
|
|
47
49
|
* ⚠️ TWO HANDLERS ARE OVERRIDDEN AND THE OTHER THREE COME FROM THE FACTORY — see the ★ above
|
|
48
50
|
* `handlers`. `delete` is the ⛔ above. `reconcile` is because the create is a FORKED WORKER, so
|
|
49
51
|
* the factory's immediate read-back would report a successful create as a failure; the measured
|
|
50
|
-
* detail
|
|
52
|
+
* detail, and the adopt-only refusal decision 9 adds, are both in `createPool` in
|
|
53
|
+
* zfs-pool-write.ts.
|
|
51
54
|
*
|
|
52
55
|
* ⚠️ PRIVILEGES, FROM THE SCHEMA. Read and diff need `Sys.Audit` on `/` — both GETs check it.
|
|
53
56
|
* Reconcile needs `Sys.Modify` on `/` for the POST. `Datastore.Allocate` on `/storage` is NOT
|
|
54
|
-
* needed, because `add_storage` is not offered — see the ⛔ on `ZfsPoolProps`.
|
|
55
|
-
*
|
|
57
|
+
* needed, because `add_storage` is not offered — see the ⛔ on `ZfsPoolProps`.
|
|
58
|
+
* ★ CORRECTED 2026-09-23, ALONGSIDE THE HEADER ABOVE: `delete` needs `Sys.Modify` on `/` too,
|
|
59
|
+
* for the DELETE it now sends under `.pipe(RemovalPolicy.destroy())` — the whole `disks/zfs`
|
|
60
|
+
* family checks it, not only the POST. MEASURED the same day as the rest of this paragraph:
|
|
61
|
+
* `ceph-osd.ts`'s own privilege comparison names `disks/zfs` as one of the sibling families
|
|
62
|
+
* whose write verbs DO carry a `Sys.Modify` check, unlike `ceph/osd`'s create and delete, which
|
|
63
|
+
* carry no permissions block at all and 403 for any identity but `root@pam`. This line
|
|
64
|
+
* previously said delete "needs nothing at all, since it calls nothing", which repeated the same
|
|
65
|
+
* mistake the header above corrects: `delete` calls the DELETE endpoint, so it needs whatever
|
|
66
|
+
* that endpoint checks.
|
|
56
67
|
*/
|
|
57
68
|
import { Resource } from 'alchemy';
|
|
58
69
|
import * as Provider from 'alchemy/Provider';
|
|
70
|
+
import type { NodesNodeDisksZfsPostParams } from './generated/pve.ts';
|
|
59
71
|
import { type PveRequirements, type WithTarget } from './resource.ts';
|
|
60
|
-
/**
|
|
61
|
-
|
|
62
|
-
|
|
72
|
+
/**
|
|
73
|
+
* PVE's layouts, generated (2026-09-23) from `NodesNodeDisksZfsPostParams['raidlevel']`
|
|
74
|
+
* (`generated/pve.ts`, manifest `pve-apidoc` pve-manager 9.2.11 sha256 9def8f13…) rather than
|
|
75
|
+
* hand-typed, per decision 9. ⚠️ Each has a minimum disk count PVE enforces, and `raid10` needs an
|
|
76
|
+
* even one. ⛔ THERE IS NO `stripe` IN THIS ENUM. n1's `speed` pool is one (decision 14 accepts it
|
|
77
|
+
* as-is): `ZfsPoolProps` below is why a stripe pool is declarable at all — as an ADOPT-ONLY
|
|
78
|
+
* declaration with no `raidlevel`, never as a full one.
|
|
79
|
+
*/
|
|
80
|
+
export type ZfsRaidLevel = NonNullable<NodesNodeDisksZfsPostParams['raidlevel']>;
|
|
81
|
+
/** Generated the same way, from `NodesNodeDisksZfsPostParams['compression']`. */
|
|
82
|
+
export type ZfsCompression = NonNullable<NodesNodeDisksZfsPostParams['compression']>;
|
|
63
83
|
/**
|
|
64
84
|
* ⛔ THERE IS NO `add_storage`, `cleanup-config` OR `cleanup-disks` PROP, AND THAT IS NOT AN
|
|
65
85
|
* OVERSIGHT. The last two belong to the DELETE this resource refuses to make. `add_storage=1`
|
|
@@ -91,9 +111,19 @@ export interface ZfsPoolProps extends WithTarget {
|
|
|
91
111
|
* `csv` sorts, and sorting this list silently re-pairs the mirrors into a different pool.
|
|
92
112
|
* ⚠️ PVE's only guard is `assert_disk_unused`, i.e. its own `disk_is_used` over the same `used`
|
|
93
113
|
* column the Disks view shows. A blank disk it considers free is wiped with no confirmation.
|
|
114
|
+
*
|
|
115
|
+
* ⛔ OPTIONAL SINCE DECISION 9 (2026-09-23), AND OMITTING IT MEANS SOMETHING SPECIFIC: leaving
|
|
116
|
+
* both this and `raidlevel` undeclared declares an ADOPT-ONLY pool — one this resource may
|
|
117
|
+
* read and report but never build. That is the only shape available for a pool PVE's own POST
|
|
118
|
+
* schema cannot fully describe, such as n1's `speed`, whose layout is a stripe and whose
|
|
119
|
+
* `raidlevel` enum (above) has no stripe to declare. `createForm` (zfs-pool-write.ts) omits
|
|
120
|
+
* both keys when they are absent, and `createPool` refuses before any POST if the pool it
|
|
121
|
+
* would need to build is not already there — an adopt-only declaration never creates. Declare
|
|
122
|
+
* both fields together for a pool this resource should build from scratch; declaring only one
|
|
123
|
+
* of the two is refused the same way, since PVE's POST requires both.
|
|
94
124
|
*/
|
|
95
|
-
devices
|
|
96
|
-
raidlevel
|
|
125
|
+
devices?: readonly string[];
|
|
126
|
+
raidlevel?: ZfsRaidLevel;
|
|
97
127
|
/**
|
|
98
128
|
* Sector size exponent, 9–16, PVE default 12.
|
|
99
129
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zfs-pool.d.ts","sourceRoot":"","sources":["../../src/proxmox/zfs-pool.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"zfs-pool.d.ts","sourceRoot":"","sources":["../../src/proxmox/zfs-pool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkEG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAE7C,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,oBAAoB,CAAC;AACtE,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,UAAU,EAA8B,MAAM,eAAe,CAAC;AAIlG;;;;;;;GAOG;AACH,MAAM,MAAM,YAAY,GAAG,WAAW,CAAC,2BAA2B,CAAC,WAAW,CAAC,CAAC,CAAC;AAEjF,iFAAiF;AACjF,MAAM,MAAM,cAAc,GAAG,WAAW,CAAC,2BAA2B,CAAC,aAAa,CAAC,CAAC,CAAC;AAErF;;;;;;;;GAQG;AACH,MAAM,WAAW,YAAa,SAAQ,UAAU;IAC9C;;;;;OAKG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,+FAA+F;IAC/F,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,SAAS,CAAC,EAAE,YAAY,CAAC;IACzB;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,WAAW,CAAC,EAAE,cAAc,CAAC;IAC7B;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,mFAAmF;IACnF,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAC;IACd,4DAA4D;IAC5D,MAAM,EAAE,MAAM,CAAC;IACf;;;;;;;OAOG;IACH,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,cAAe,SAAQ,QAAQ,CAC9C,iBAAiB,EACjB,YAAY,EACZ,iBAAiB,EACjB,KAAK,EACL,eAAe,CAChB;CAAG;AAEJ,mGAAmG;AACnG,eAAO,MAAM,cAAc,iDAEzB,CAAC;AAqBH,eAAO,MAAM,sBAAsB,oIACoD,CAAC"}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import type { PinnedDownload } from './binary-form.ts';
|
|
2
2
|
import { type TarContents } from './tar.ts';
|
|
3
|
-
/**
|
|
4
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Gunzip and read a tar, keeping only `wanted`. Throws ArchiveRefused.
|
|
5
|
+
* @param root The one declared leading directory to strip (tar.ts) — a directory-wrapped vendor
|
|
6
|
+
* archive (the Prometheus family, measured 2026-09-23) without it, or one that carries no root.
|
|
7
|
+
*/
|
|
8
|
+
export declare const extractMembers: (gzipped: Uint8Array, wanted: ReadonlySet<string>, root?: string) => Promise<TarContents>;
|
|
5
9
|
/**
|
|
6
10
|
* The declared member's bytes out of a downloaded archive, both layers checked.
|
|
7
11
|
* ⚠️ `archive` may be shared with another resource's extraction (download.ts sharingInFlight):
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"archive.d.ts","sourceRoot":"","sources":["../../src/release/archive.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAE,KAAK,WAAW,EAAa,MAAM,UAAU,CAAC;AAKvD
|
|
1
|
+
{"version":3,"file":"archive.d.ts","sourceRoot":"","sources":["../../src/release/archive.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAE,KAAK,WAAW,EAAa,MAAM,UAAU,CAAC;AAKvD;;;;GAIG;AACH,eAAO,MAAM,cAAc,YAChB,UAAU,UACX,WAAW,CAAC,MAAM,CAAC,SACpB,MAAM,KACZ,OAAO,CAAC,WAAW,CA8BrB,CAAC;AAUF;;;;GAIG;AACH,eAAO,MAAM,cAAc,YAChB,UAAU,WACV,cAAc,KACtB,OAAO,CAAC,UAAU,CAgBpB,CAAC"}
|
|
@@ -28,6 +28,17 @@ export interface ReleaseArchive {
|
|
|
28
28
|
readonly size: number;
|
|
29
29
|
/** The archive's SHA-256, lower-case hex, as the vendor's checksum file lists it. */
|
|
30
30
|
readonly sha256: string;
|
|
31
|
+
/**
|
|
32
|
+
* The one directory the vendor wraps every entry in, exactly as its own archive names it —
|
|
33
|
+
* `'alertmanager-0.33.1.darwin-arm64'`, never `'alertmanager-0.33.1.darwin-arm64/'` (no trailing
|
|
34
|
+
* slash). Pinned from the walk-down, never derived from `asset` or `tag`: the vendor spells its
|
|
35
|
+
* wrapper however it likes (`darwin_arm64`, an `-enterprise` suffix, no version at all), so a
|
|
36
|
+
* computed name would drift onto a sibling the moment one vendor's naming changes. Omit it for a
|
|
37
|
+
* flat archive (Victoria, OpenBao); the tar reader then refuses a directory entry exactly as it
|
|
38
|
+
* always has (tar.ts).
|
|
39
|
+
* @default undefined — no wrapping directory; the archive is flat.
|
|
40
|
+
*/
|
|
41
|
+
readonly root?: string;
|
|
31
42
|
}
|
|
32
43
|
export interface ReleaseBinaryProps {
|
|
33
44
|
/** The pinned archive the binary comes out of. `catalogBinary()` fills it from a data set. */
|
|
@@ -77,6 +88,8 @@ export type PinnedDownload = {
|
|
|
77
88
|
readonly member: string;
|
|
78
89
|
/** The member's pinned SHA-256. */
|
|
79
90
|
readonly memberSha256: string;
|
|
91
|
+
/** The one declared leading directory to strip, if the archive has one — `archive.root`. */
|
|
92
|
+
readonly root?: string;
|
|
80
93
|
};
|
|
81
94
|
export declare const DEFAULT_BINARY_MODE = 493;
|
|
82
95
|
/**
|
|
@@ -95,14 +108,7 @@ export declare const releaseBinaryPath: (props: {
|
|
|
95
108
|
/** The download a declaration means. ⚠️ Validate first (binaryProblems): this trusts its input. */
|
|
96
109
|
export declare const pinnedDownload: (props: ReleaseBinaryProps) => PinnedDownload;
|
|
97
110
|
export declare const modeProblems: (mode: number) => string[];
|
|
98
|
-
export
|
|
99
|
-
/** Everything wrong with the pins alone — plain strings in any declaration, resolved or not. */
|
|
100
|
-
export declare const pinProblems: (props: {
|
|
101
|
-
readonly archive?: Partial<ReleaseArchive>;
|
|
102
|
-
readonly member?: unknown;
|
|
103
|
-
readonly sha256?: unknown;
|
|
104
|
-
readonly name?: unknown;
|
|
105
|
-
}) => string[];
|
|
111
|
+
export { MAX_ARCHIVE, pinProblems } from './binary-pins.ts';
|
|
106
112
|
type Pins = Pick<ReleaseBinaryProps, 'archive' | 'member' | 'sha256'>;
|
|
107
113
|
/** Whether two declarations pin different bytes: another archive, member or member digest. */
|
|
108
114
|
export declare const pinsMoved: (olds: Pins, news: Pins) => boolean;
|
|
@@ -114,5 +120,4 @@ export declare const pinsMoved: (olds: Pins, news: Pins) => boolean;
|
|
|
114
120
|
export declare const inPlaceRefusal: (olds: Pins, news: Pins) => string;
|
|
115
121
|
/** Everything wrong with a declaration, before any host or network is asked anything. */
|
|
116
122
|
export declare const binaryProblems: (props: ReleaseBinaryProps) => string[];
|
|
117
|
-
export {};
|
|
118
123
|
//# sourceMappingURL=binary-form.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"binary-form.d.ts","sourceRoot":"","sources":["../../src/release/binary-form.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"binary-form.d.ts","sourceRoot":"","sources":["../../src/release/binary-form.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAIvE,oFAAoF;AACpF,MAAM,WAAW,cAAc;IAC7B,kGAAkG;IAClG,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,2DAA2D;IAC3D,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,yFAAyF;IACzF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,qFAAqF;IACrF,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;;;;;;;OASG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,kBAAkB;IACjC,8FAA8F;IAC9F,OAAO,EAAE,cAAc,CAAC;IACxB,oGAAoG;IACpG,MAAM,EAAE,MAAM,CAAC;IACf,uEAAuE;IACvE,MAAM,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;OAMG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,uEAAuE;IACvE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,yEAAyE;IACzE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,uBAAwB,SAAQ,kBAAkB;IACjE,8CAA8C;IAC9C,GAAG,EAAE,MAAM,CAAC;IACZ,uEAAuE;IACvE,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,4FAA4F;AAC5F,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,oCAAoC;IACpC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,oDAAoD;IACpD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,mCAAmC;IACnC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,4FAA4F;IAC5F,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,eAAO,MAAM,mBAAmB,MAAQ,CAAC;AAEzC;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,SAAU,MAAM,OAAO,MAAM,SAAS,MAAM,KAAG,MACN,CAAC;AAEjE,iEAAiE;AACjE,eAAO,MAAM,iBAAiB,UAAW;IACvC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB,KAAG,MAA4C,CAAC;AAEjD,mGAAmG;AACnG,eAAO,MAAM,cAAc,UAAW,kBAAkB,KAAG,cAQzD,CAAC;AAEH,eAAO,MAAM,YAAY,SAAU,MAAM,KAAG,MAAM,EAUjD,CAAC;AAKF,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAO5D,KAAK,IAAI,GAAG,IAAI,CAAC,kBAAkB,EAAE,SAAS,GAAG,QAAQ,GAAG,QAAQ,CAAC,CAAC;AAEtE,8FAA8F;AAC9F,eAAO,MAAM,SAAS,SAAU,IAAI,QAAQ,IAAI,KAAG,OAGkB,CAAC;AAEtE;;;;GAIG;AACH,eAAO,MAAM,cAAc,SAAU,IAAI,QAAQ,IAAI,KAAG,MAGqB,CAAC;AAE9E,yFAAyF;AACzF,eAAO,MAAM,cAAc,UAAW,kBAAkB,KAAG,MAAM,EAMhE,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `pinProblems` — everything wrong with a declaration's pins alone, as plain strings, checked
|
|
3
|
+
* before a plan resolves a directory or a host is asked anything. Split out of binary-form.ts
|
|
4
|
+
* 2026-09-23 to keep that file under the house's 250-line cap (it was 229, heading for ~250 once
|
|
5
|
+
* `archive.root` needed its own validation and JSDoc). binary-form.ts re-exports `pinProblems` and
|
|
6
|
+
* `MAX_ARCHIVE`, so nothing that imported them moves.
|
|
7
|
+
*/
|
|
8
|
+
import type { ReleaseArchive } from './binary-form.ts';
|
|
9
|
+
export declare const MAX_ARCHIVE: number;
|
|
10
|
+
/** Everything wrong with the pins alone — plain strings in any declaration, resolved or not. */
|
|
11
|
+
export declare const pinProblems: (props: {
|
|
12
|
+
readonly archive?: Partial<ReleaseArchive>;
|
|
13
|
+
readonly member?: unknown;
|
|
14
|
+
readonly sha256?: unknown;
|
|
15
|
+
readonly name?: unknown;
|
|
16
|
+
}) => string[];
|
|
17
|
+
//# sourceMappingURL=binary-pins.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"binary-pins.d.ts","sourceRoot":"","sources":["../../src/release/binary-pins.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAGvD,eAAO,MAAM,WAAW,QAAU,CAAC;AAcnC,gGAAgG;AAChG,eAAO,MAAM,WAAW,UAAW;IACjC,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;IAC3C,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;CACzB,KAAG,MAAM,EAgET,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../../src/release/catalog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAG3E;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,wFAAwF;IACxF,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,wFAAwF;AACxF,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,0CAA0C;IAC1C,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,6CAA6C;IAC7C,QAAQ,CAAC,GAAG,EAAE;QAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9F,+EAA+E;IAC/E,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,+FAA+F;AAC/F,MAAM,WAAW,aAAc,SAAQ,cAAc;IACnD,kGAAkG;IAClG,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACnD;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC;IAC7D,gGAAgG;IAChG,QAAQ,CAAC,SAAS,EAAE;QAClB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;QACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1B,+GAA+G;QAC/G,QAAQ,CAAC,SAAS,CAAC,EAAE,iBAAiB,CAAC;KACxC,CAAC;CACH;AAED,sEAAsE;AACtE,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACpD,uFAAuF;IACvF,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;CACtF;AAED,kCAAkC;AAClC,MAAM,WAAW,cAAc;IAC7B,8DAA8D;IAC9D,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC;CAC7D;AAED,+CAA+C;AAC/C,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,qEAAqE;IACrE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,+FAA+F;AAC/F,MAAM,MAAM,YAAY,GAAG,IAAI,CAAC,kBAAkB,EAAE,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC,CAAC;AAqB9F,iFAAiF;AACjF,eAAO,MAAM,eAAe,YAAa,cAAc,WAAW,cAAc,KAAG,MAAM,EAkDxF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,aAAa,YAAa,cAAc,WAAW,cAAc,KAAG,
|
|
1
|
+
{"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../../src/release/catalog.ts"],"names":[],"mappings":"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"}
|
package/dist/release/index.js
CHANGED
|
@@ -71,31 +71,7 @@ var fileProblems = (props) => {
|
|
|
71
71
|
return found;
|
|
72
72
|
};
|
|
73
73
|
|
|
74
|
-
// src/release/binary-
|
|
75
|
-
var DEFAULT_BINARY_MODE = 493;
|
|
76
|
-
var releaseUrl = (repo, tag, asset) => `https://github.com/${repo}/releases/download/${tag}/${asset}`;
|
|
77
|
-
var releaseBinaryPath = (props) => `${props.directory}/${props.name}`;
|
|
78
|
-
var pinnedDownload = (props) => ({
|
|
79
|
-
member: props.member,
|
|
80
|
-
memberSha256: props.sha256,
|
|
81
|
-
sha256: props.archive.sha256,
|
|
82
|
-
size: props.archive.size,
|
|
83
|
-
url: releaseUrl(props.archive.repo, props.archive.tag, props.archive.asset)
|
|
84
|
-
});
|
|
85
|
-
var modeProblems = (mode) => {
|
|
86
|
-
if (!Number.isSafeInteger(mode) || mode < 0 || mode > 4095)
|
|
87
|
-
return ["mode must be 0–0o7777"];
|
|
88
|
-
const found = [];
|
|
89
|
-
if ((mode & 3584) !== 0)
|
|
90
|
-
found.push("mode must not set setuid, setgid or sticky");
|
|
91
|
-
if ((mode & 18) !== 0)
|
|
92
|
-
found.push("mode must not be group- or world-writable");
|
|
93
|
-
if ((mode & 64) === 0)
|
|
94
|
-
found.push("mode must let the owner execute it");
|
|
95
|
-
if ((mode & 256) === 0)
|
|
96
|
-
found.push("mode must let the owner read it (it is re-hashed)");
|
|
97
|
-
return found;
|
|
98
|
-
};
|
|
74
|
+
// src/release/binary-pins.ts
|
|
99
75
|
var HEX64 = /^[0-9a-f]{64}$/;
|
|
100
76
|
var MAX_ARCHIVE = 1 << 30;
|
|
101
77
|
var REPO = /^[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?\/[A-Za-z0-9._-]+$/;
|
|
@@ -132,6 +108,15 @@ var pinProblems = (props) => {
|
|
|
132
108
|
else if (!TARBALL.test(asset))
|
|
133
109
|
found.push(`archive.asset must be a .tar.gz or .tgz: "${asset}"`);
|
|
134
110
|
}
|
|
111
|
+
if (archive.root !== undefined) {
|
|
112
|
+
if (typeof archive.root !== "string") {
|
|
113
|
+
found.push("archive.root must be a plain string when given");
|
|
114
|
+
} else {
|
|
115
|
+
const problem = segmentProblem("archive.root", archive.root);
|
|
116
|
+
if (problem !== undefined)
|
|
117
|
+
found.push(problem);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
135
120
|
const size = archive.size;
|
|
136
121
|
if (typeof size !== "number" || !Number.isSafeInteger(size) || size <= 0 || size > MAX_ARCHIVE) {
|
|
137
122
|
found.push(`archive.size must be the asset's byte count, 1 to ${String(MAX_ARCHIVE)}`);
|
|
@@ -150,7 +135,34 @@ var pinProblems = (props) => {
|
|
|
150
135
|
}
|
|
151
136
|
return found;
|
|
152
137
|
};
|
|
153
|
-
|
|
138
|
+
|
|
139
|
+
// src/release/binary-form.ts
|
|
140
|
+
var DEFAULT_BINARY_MODE = 493;
|
|
141
|
+
var releaseUrl = (repo, tag, asset) => `https://github.com/${repo}/releases/download/${tag}/${asset}`;
|
|
142
|
+
var releaseBinaryPath = (props) => `${props.directory}/${props.name}`;
|
|
143
|
+
var pinnedDownload = (props) => ({
|
|
144
|
+
member: props.member,
|
|
145
|
+
memberSha256: props.sha256,
|
|
146
|
+
sha256: props.archive.sha256,
|
|
147
|
+
size: props.archive.size,
|
|
148
|
+
url: releaseUrl(props.archive.repo, props.archive.tag, props.archive.asset),
|
|
149
|
+
...props.archive.root === undefined ? {} : { root: props.archive.root }
|
|
150
|
+
});
|
|
151
|
+
var modeProblems = (mode) => {
|
|
152
|
+
if (!Number.isSafeInteger(mode) || mode < 0 || mode > 4095)
|
|
153
|
+
return ["mode must be 0–0o7777"];
|
|
154
|
+
const found = [];
|
|
155
|
+
if ((mode & 3584) !== 0)
|
|
156
|
+
found.push("mode must not set setuid, setgid or sticky");
|
|
157
|
+
if ((mode & 18) !== 0)
|
|
158
|
+
found.push("mode must not be group- or world-writable");
|
|
159
|
+
if ((mode & 64) === 0)
|
|
160
|
+
found.push("mode must let the owner execute it");
|
|
161
|
+
if ((mode & 256) === 0)
|
|
162
|
+
found.push("mode must let the owner read it (it is re-hashed)");
|
|
163
|
+
return found;
|
|
164
|
+
};
|
|
165
|
+
var ARCHIVE_KEYS = ["repo", "tag", "asset", "size", "sha256", "root"];
|
|
154
166
|
var pinsMoved = (olds, news) => olds.member !== news.member || olds.sha256 !== news.sha256 || ARCHIVE_KEYS.some((key) => olds.archive[key] !== news.archive[key]);
|
|
155
167
|
var inPlaceRefusal = (olds, news) => `a new pin (${news.archive.tag} ${news.member} ${news.sha256}) at the path of the installed ` + `one (${olds.archive.tag} ${olds.member} ${olds.sha256}) would overwrite it in place; give each ` + "version its own directory (catalogDirectory), so an upgrade is a new path";
|
|
156
168
|
var binaryProblems = (props) => [
|
|
@@ -778,7 +790,7 @@ var concatBytes = (parts) => {
|
|
|
778
790
|
}
|
|
779
791
|
return out;
|
|
780
792
|
};
|
|
781
|
-
var tarReader = (wanted) => {
|
|
793
|
+
var tarReader = (wanted, root) => {
|
|
782
794
|
const header = new Uint8Array(BLOCK);
|
|
783
795
|
const members = new Map;
|
|
784
796
|
const names = [];
|
|
@@ -790,6 +802,8 @@ var tarReader = (wanted) => {
|
|
|
790
802
|
let zeros = 0;
|
|
791
803
|
let name = "";
|
|
792
804
|
let parts;
|
|
805
|
+
let rootSeen = false;
|
|
806
|
+
const rootPrefix = root === undefined ? undefined : `${root}/`;
|
|
793
807
|
const endEntry = () => {
|
|
794
808
|
if (parts !== undefined)
|
|
795
809
|
members.set(name, concatBytes(parts));
|
|
@@ -812,20 +826,45 @@ var tarReader = (wanted) => {
|
|
|
812
826
|
if (!posix && magic !== "ustar \x00")
|
|
813
827
|
throw refuse("an entry header is neither ustar nor GNU");
|
|
814
828
|
const prefix = posix ? text(header, 345, 155) : "";
|
|
815
|
-
|
|
816
|
-
const problem = nameProblem(
|
|
829
|
+
const rawName = prefix === "" ? text(header, 0, 100) : `${prefix}/${text(header, 0, 100)}`;
|
|
830
|
+
const problem = nameProblem(rawName);
|
|
817
831
|
if (problem !== undefined)
|
|
818
|
-
throw refuse(`entry ${JSON.stringify(
|
|
832
|
+
throw refuse(`entry ${JSON.stringify(rawName)} ${problem}`);
|
|
833
|
+
if (rootPrefix !== undefined && rawName === rootPrefix) {
|
|
834
|
+
if (rootSeen) {
|
|
835
|
+
throw refuse(`entry "${rawName}" reuses the declared root's own name, appearing twice`);
|
|
836
|
+
}
|
|
837
|
+
const type2 = String.fromCharCode(header[156] ?? 0);
|
|
838
|
+
const size = octal(header, 124, 12, `entry "${rawName}"'s size`);
|
|
839
|
+
if (type2 !== "5" || size !== 0) {
|
|
840
|
+
throw refuse(`entry "${rawName}" is the declared root but is not an empty directory`);
|
|
841
|
+
}
|
|
842
|
+
rootSeen = true;
|
|
843
|
+
phase = "header";
|
|
844
|
+
return;
|
|
845
|
+
}
|
|
846
|
+
let stripped = rawName;
|
|
847
|
+
if (rootPrefix !== undefined) {
|
|
848
|
+
if (!rawName.startsWith(rootPrefix)) {
|
|
849
|
+
throw refuse(`entry "${rawName}" is outside the declared root "${rootPrefix}"`);
|
|
850
|
+
}
|
|
851
|
+
stripped = rawName.slice(rootPrefix.length);
|
|
852
|
+
const strippedProblem = nameProblem(stripped);
|
|
853
|
+
if (strippedProblem !== undefined) {
|
|
854
|
+
throw refuse(`entry "${rawName}" strips to ${JSON.stringify(stripped)}, which ${strippedProblem}`);
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
name = stripped;
|
|
819
858
|
const type = String.fromCharCode(header[156] ?? 0);
|
|
820
859
|
if (type !== "0" && type !== "\x00") {
|
|
821
860
|
const kind = KINDS[type] ?? `of unknown type ${JSON.stringify(type)}`;
|
|
822
|
-
throw refuse(`entry "${
|
|
861
|
+
throw refuse(`entry "${rawName}" is ${kind}; only regular files are accepted`);
|
|
823
862
|
}
|
|
824
863
|
if (seen.has(name))
|
|
825
|
-
throw refuse(`entry "${
|
|
864
|
+
throw refuse(`entry "${rawName}" appears twice`);
|
|
826
865
|
seen.add(name);
|
|
827
866
|
names.push(name);
|
|
828
|
-
remaining = octal(header, 124, 12, `entry "${
|
|
867
|
+
remaining = octal(header, 124, 12, `entry "${rawName}"'s size`);
|
|
829
868
|
padding = (BLOCK - remaining % BLOCK) % BLOCK;
|
|
830
869
|
parts = wanted.has(name) ? [] : undefined;
|
|
831
870
|
phase = "data";
|
|
@@ -869,6 +908,9 @@ var tarReader = (wanted) => {
|
|
|
869
908
|
const finish = () => {
|
|
870
909
|
if (phase !== "end")
|
|
871
910
|
throw refuse("the archive stops before its end-of-archive marker");
|
|
911
|
+
if (rootPrefix !== undefined && !rootSeen) {
|
|
912
|
+
throw refuse(`the declared root "${rootPrefix}" never appears in the archive`);
|
|
913
|
+
}
|
|
872
914
|
return { members, names };
|
|
873
915
|
};
|
|
874
916
|
return { finish, push };
|
|
@@ -876,8 +918,8 @@ var tarReader = (wanted) => {
|
|
|
876
918
|
|
|
877
919
|
// src/release/archive.ts
|
|
878
920
|
var SLICE = 1 << 20;
|
|
879
|
-
var extractMembers = async (gzipped, wanted) => {
|
|
880
|
-
const tar = tarReader(wanted);
|
|
921
|
+
var extractMembers = async (gzipped, wanted, root) => {
|
|
922
|
+
const tar = tarReader(wanted, root);
|
|
881
923
|
let offset = 0;
|
|
882
924
|
const source = new ReadableStream({
|
|
883
925
|
pull(controller) {
|
|
@@ -917,11 +959,12 @@ var verifiedMember = async (archive, release) => {
|
|
|
917
959
|
const archiveSha = sha256Hex(archive);
|
|
918
960
|
if (archiveSha !== release.sha256)
|
|
919
961
|
throw mismatch(release.url, release.sha256, archiveSha);
|
|
920
|
-
const { members, names } = await extractMembers(archive, new Set([release.member]));
|
|
962
|
+
const { members, names } = await extractMembers(archive, new Set([release.member]), release.root);
|
|
921
963
|
const bytes = members.get(release.member);
|
|
922
964
|
if (bytes === undefined) {
|
|
965
|
+
const under = release.root === undefined ? "" : ` under root "${release.root}/"`;
|
|
923
966
|
throw new ArchiveRefused({
|
|
924
|
-
message: `${release.url} has no member named exactly "${release.member}" (it has ${names.map((n) => `"${n}"`).join(", ") || "none"})`
|
|
967
|
+
message: `${release.url} has no member named exactly "${release.member}"${under} (it has ${names.map((n) => `"${n}"`).join(", ") || "none"})`
|
|
925
968
|
});
|
|
926
969
|
}
|
|
927
970
|
const memberSha = sha256Hex(bytes);
|
|
@@ -1214,9 +1257,16 @@ var catalogBinary = (catalog, request) => {
|
|
|
1214
1257
|
message: `Release.Binary ${request.binary}: ${problems.join("; ")}. Nothing was fetched.`
|
|
1215
1258
|
});
|
|
1216
1259
|
}
|
|
1217
|
-
const { asset, repo, sha256: archiveSha256, size, tag } = archive;
|
|
1260
|
+
const { asset, repo, root, sha256: archiveSha256, size, tag } = archive;
|
|
1218
1261
|
return {
|
|
1219
|
-
archive: {
|
|
1262
|
+
archive: {
|
|
1263
|
+
asset,
|
|
1264
|
+
repo,
|
|
1265
|
+
sha256: archiveSha256,
|
|
1266
|
+
size,
|
|
1267
|
+
tag,
|
|
1268
|
+
...root === undefined ? {} : { root }
|
|
1269
|
+
},
|
|
1220
1270
|
member,
|
|
1221
1271
|
name: request.binary,
|
|
1222
1272
|
sha256
|
|
@@ -1421,5 +1471,5 @@ export {
|
|
|
1421
1471
|
releaseUrl
|
|
1422
1472
|
};
|
|
1423
1473
|
|
|
1424
|
-
//# debugId=
|
|
1474
|
+
//# debugId=684C5D2BD8F11B9A64756E2164756E21
|
|
1425
1475
|
//# sourceMappingURL=index.js.map
|