@homeflare/alchemy 0.28.0 → 0.29.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.
- package/README.md +2 -0
- package/dist/caddy/admin-address.d.ts +27 -0
- package/dist/caddy/admin-address.d.ts.map +1 -0
- package/dist/caddy/admin-calls.d.ts +25 -28
- package/dist/caddy/admin-calls.d.ts.map +1 -1
- package/dist/caddy/admin.d.ts +28 -38
- package/dist/caddy/admin.d.ts.map +1 -1
- package/dist/caddy/caddy-http-client.d.ts +25 -0
- package/dist/caddy/caddy-http-client.d.ts.map +1 -0
- package/dist/caddy/config-errors.d.ts +24 -0
- package/dist/caddy/config-errors.d.ts.map +1 -0
- package/dist/caddy/config-lifecycle.d.ts +20 -49
- package/dist/caddy/config-lifecycle.d.ts.map +1 -1
- package/dist/caddy/config-reconcile.d.ts +45 -0
- package/dist/caddy/config-reconcile.d.ts.map +1 -0
- package/dist/caddy/config.d.ts +4 -3
- package/dist/caddy/config.d.ts.map +1 -1
- package/dist/caddy/fake-caddy.d.ts +10 -2
- package/dist/caddy/fake-caddy.d.ts.map +1 -1
- package/dist/caddy/index.d.ts +3 -3
- package/dist/caddy/index.d.ts.map +1 -1
- package/dist/caddy/index.js +487 -444
- package/dist/caddy/index.js.map +16 -12
- package/dist/caddy/local-admin.d.ts +20 -34
- package/dist/caddy/local-admin.d.ts.map +1 -1
- package/dist/caddy/providers.d.ts +19 -3
- package/dist/caddy/providers.d.ts.map +1 -1
- package/dist/cloudflare/mesh-node-token.d.ts +1 -1
- package/dist/cloudflare/mesh-node-token.d.ts.map +1 -1
- package/dist/forgejo/branch-protection.d.ts +4 -4
- package/dist/forgejo/org-actions-secrets.d.ts +4 -4
- package/dist/forgejo/org-label.d.ts +4 -4
- package/dist/forgejo/org-team.d.ts +4 -4
- package/dist/forgejo/repo-webhook.d.ts +4 -4
- package/dist/forgejo/repository.d.ts +4 -4
- package/dist/forgejo/team-member.d.ts +4 -4
- package/dist/github/declare-repo-baseline.d.ts +1 -1
- package/dist/github/declare-repo-baseline.d.ts.map +1 -1
- package/dist/github/index.d.ts +8 -4
- package/dist/github/index.d.ts.map +1 -1
- package/dist/github/index.js +83 -24
- package/dist/github/index.js.map +14 -12
- package/dist/github/repo-apply-order-fake-providers.d.ts +48 -0
- package/dist/github/repo-apply-order-fake-providers.d.ts.map +1 -0
- package/dist/github/repo-apply-order-fake-stack.d.ts +13 -0
- package/dist/github/repo-apply-order-fake-stack.d.ts.map +1 -0
- package/dist/github/repo-auto-merge-gate.d.ts +54 -0
- package/dist/github/repo-auto-merge-gate.d.ts.map +1 -0
- package/dist/github/repo-baseline-data.d.ts +10 -1
- package/dist/github/repo-baseline-data.d.ts.map +1 -1
- package/dist/github/repo-policy.d.ts +1 -1
- package/dist/github/repo-policy.d.ts.map +1 -1
- package/dist/github/repository-ruleset-errors.d.ts +3 -2
- package/dist/github/repository-ruleset-errors.d.ts.map +1 -1
- package/dist/github/repository-ruleset-form.d.ts.map +1 -1
- package/dist/github/repository-ruleset-guards.d.ts +3 -2
- package/dist/github/repository-ruleset-guards.d.ts.map +1 -1
- package/dist/github/repository-ruleset-octokit.d.ts +49 -0
- package/dist/github/repository-ruleset-octokit.d.ts.map +1 -1
- package/dist/github/repository-ruleset-probe.d.ts +2 -1
- package/dist/github/repository-ruleset-probe.d.ts.map +1 -1
- package/dist/github/repository-ruleset-props.d.ts +7 -4
- package/dist/github/repository-ruleset-props.d.ts.map +1 -1
- package/dist/github/repository-ruleset-providers.d.ts +42 -0
- package/dist/github/repository-ruleset-providers.d.ts.map +1 -0
- package/dist/github/repository-ruleset.d.ts +26 -4
- package/dist/github/repository-ruleset.d.ts.map +1 -1
- package/dist/litellm/fake-litellm.d.ts +27 -16
- package/dist/litellm/fake-litellm.d.ts.map +1 -1
- package/dist/litellm/fake-stack.d.ts +9 -4
- package/dist/litellm/fake-stack.d.ts.map +1 -1
- package/dist/litellm/index.d.ts +7 -5
- package/dist/litellm/index.d.ts.map +1 -1
- package/dist/litellm/index.js +53 -123
- package/dist/litellm/index.js.map +8 -8
- package/dist/litellm/operations.d.ts +60 -0
- package/dist/litellm/operations.d.ts.map +1 -0
- package/dist/litellm/pass-through-endpoint.d.ts +10 -28
- package/dist/litellm/pass-through-endpoint.d.ts.map +1 -1
- package/dist/litellm/pass-through-errors.d.ts +37 -0
- package/dist/litellm/pass-through-errors.d.ts.map +1 -0
- package/dist/litellm/pass-through-form.d.ts +11 -4
- package/dist/litellm/pass-through-form.d.ts.map +1 -1
- package/dist/litellm/providers.d.ts +7 -5
- package/dist/litellm/providers.d.ts.map +1 -1
- package/dist/netbox/prefix.d.ts +4 -4
- package/dist/paperless/custom-field.d.ts +21 -21
- package/dist/paperless/custom-field.d.ts.map +1 -1
- package/dist/paperless/document-type.d.ts +34 -4
- package/dist/paperless/document-type.d.ts.map +1 -1
- package/dist/paperless/fake-paperless.d.ts +36 -19
- package/dist/paperless/fake-paperless.d.ts.map +1 -1
- package/dist/paperless/index.d.ts +7 -2
- package/dist/paperless/index.d.ts.map +1 -1
- package/dist/paperless/index.js +164 -228
- package/dist/paperless/index.js.map +10 -13
- package/dist/paperless/matching-locate.d.ts +42 -20
- package/dist/paperless/matching-locate.d.ts.map +1 -1
- package/dist/paperless/matching-types.d.ts +47 -18
- package/dist/paperless/matching-types.d.ts.map +1 -1
- package/dist/paperless/matching.d.ts +30 -27
- package/dist/paperless/matching.d.ts.map +1 -1
- package/dist/paperless/providers.d.ts +13 -7
- package/dist/paperless/providers.d.ts.map +1 -1
- package/dist/paperless/storage-path.d.ts +34 -4
- package/dist/paperless/storage-path.d.ts.map +1 -1
- package/dist/paperless/tag.d.ts +43 -8
- package/dist/paperless/tag.d.ts.map +1 -1
- package/dist/proxmox/acl.d.ts +30 -23
- package/dist/proxmox/acl.d.ts.map +1 -1
- package/dist/proxmox/distilled-pve.d.ts +89 -0
- package/dist/proxmox/distilled-pve.d.ts.map +1 -0
- package/dist/proxmox/index.js +663 -609
- package/dist/proxmox/index.js.map +12 -11
- package/dist/proxmox/members.d.ts +29 -10
- package/dist/proxmox/members.d.ts.map +1 -1
- package/docs/caddy-admin.md +1 -1
- package/docs/caddy.md +3 -2
- package/docs/credentials.md +16 -0
- package/docs/litellm.md +21 -13
- package/docs/paperless.md +12 -0
- package/docs/peers.md +5 -0
- package/docs/repo-policy.md +16 -1
- package/docs/repository-ruleset.md +53 -13
- package/docs/upstream-conformance.md +126 -10
- package/package.json +5 -2
- package/src/caddy/admin-address.ts +71 -0
- package/src/caddy/admin-calls.ts +48 -100
- package/src/caddy/admin.ts +37 -41
- package/src/caddy/adopt-scope.test.ts +1 -1
- package/src/caddy/caddy-http-client.ts +229 -0
- package/src/caddy/config-adopt-provider.test.ts +139 -0
- package/src/caddy/config-adopt.test.ts +60 -126
- package/src/caddy/config-errors.ts +47 -0
- package/src/caddy/config-lifecycle.test.ts +55 -42
- package/src/caddy/config-lifecycle.ts +72 -162
- package/src/caddy/config-reconcile.ts +138 -0
- package/src/caddy/config.ts +36 -42
- package/src/caddy/fake-caddy.ts +20 -2
- package/src/caddy/index.ts +3 -8
- package/src/caddy/local-admin.test.ts +35 -21
- package/src/caddy/local-admin.ts +60 -167
- package/src/caddy/providers.test.ts +14 -6
- package/src/caddy/providers.ts +20 -4
- package/src/github/declare-repo-baseline-apply-order.test.ts +86 -0
- package/src/github/declare-repo-baseline.ts +40 -7
- package/src/github/index.ts +8 -4
- package/src/github/repo-apply-order-fake-providers.ts +119 -0
- package/src/github/repo-apply-order-fake-stack.ts +74 -0
- package/src/github/repo-auto-merge-gate.ts +57 -0
- package/src/github/repo-baseline-data.test.ts +13 -0
- package/src/github/repo-baseline-data.ts +11 -2
- package/src/github/repo-policy-apply-order.test.ts +72 -0
- package/src/github/repo-policy-stack.test.ts +59 -0
- package/src/github/repo-policy.ts +31 -3
- package/src/github/repository-ruleset-errors.ts +6 -4
- package/src/github/repository-ruleset-exact-adopt-k1.test.ts +109 -0
- package/src/github/repository-ruleset-exact-adopt.test.ts +14 -9
- package/src/github/repository-ruleset-form.ts +7 -3
- package/src/github/repository-ruleset-guards.ts +3 -2
- package/src/github/repository-ruleset-octokit.test.ts +141 -0
- package/src/github/repository-ruleset-octokit.ts +121 -30
- package/src/github/repository-ruleset-probe.ts +2 -1
- package/src/github/repository-ruleset-props.ts +7 -4
- package/src/github/repository-ruleset-providers.ts +46 -0
- package/src/github/repository-ruleset-wire.test.ts +38 -27
- package/src/github/repository-ruleset.ts +27 -3
- package/src/litellm/fake-litellm.ts +83 -78
- package/src/litellm/fake-stack.ts +13 -5
- package/src/litellm/index.ts +7 -21
- package/src/litellm/operations.ts +149 -0
- package/src/litellm/pass-through-endpoint-delete-swallow.test.ts +17 -20
- package/src/litellm/pass-through-endpoint-replace.test.ts +3 -4
- package/src/litellm/pass-through-endpoint.test.ts +8 -8
- package/src/litellm/pass-through-endpoint.ts +21 -31
- package/src/litellm/pass-through-errors.ts +37 -0
- package/src/litellm/pass-through-form.ts +20 -10
- package/src/litellm/providers.ts +8 -7
- package/src/paperless/custom-field.test.ts +49 -40
- package/src/paperless/custom-field.ts +57 -33
- package/src/paperless/document-type.test.ts +48 -0
- package/src/paperless/document-type.ts +51 -22
- package/src/paperless/fake-paperless.ts +63 -72
- package/src/paperless/index.ts +7 -13
- package/src/paperless/matching-locate.test.ts +102 -0
- package/src/paperless/matching-locate.ts +80 -86
- package/src/paperless/matching-types.ts +49 -16
- package/src/paperless/matching.ts +77 -90
- package/src/paperless/providers.ts +14 -10
- package/src/paperless/storage-path.test.ts +56 -0
- package/src/paperless/storage-path.ts +53 -24
- package/src/paperless/tag-identity.test.ts +127 -0
- package/src/paperless/tag.test.ts +100 -0
- package/src/paperless/tag.ts +62 -32
- package/src/proxmox/acl.test.ts +145 -0
- package/src/proxmox/acl.ts +128 -150
- package/src/proxmox/distilled-pve.test.ts +231 -0
- package/src/proxmox/distilled-pve.ts +147 -0
- package/src/proxmox/members-timeout.test.ts +161 -0
- package/src/proxmox/members.test.ts +9 -0
- package/src/proxmox/members.ts +36 -2
- package/dist/litellm/client.d.ts +0 -76
- package/dist/litellm/client.d.ts.map +0 -1
- package/dist/litellm/credentials.d.ts +0 -48
- package/dist/litellm/credentials.d.ts.map +0 -1
- package/dist/litellm/generated/pass-through.d.ts +0 -71
- package/dist/litellm/generated/pass-through.d.ts.map +0 -1
- package/dist/paperless/client.d.ts +0 -37
- package/dist/paperless/client.d.ts.map +0 -1
- package/dist/paperless/credentials.d.ts +0 -40
- package/dist/paperless/credentials.d.ts.map +0 -1
- package/dist/paperless/errors.d.ts +0 -49
- package/dist/paperless/errors.d.ts.map +0 -1
- package/src/litellm/client.ts +0 -195
- package/src/litellm/credentials.ts +0 -73
- package/src/litellm/generated/pass-through.ts +0 -73
- package/src/paperless/client.ts +0 -88
- package/src/paperless/credentials.ts +0 -69
- package/src/paperless/errors.ts +0 -62
- package/src/paperless/matching-identity.test.ts +0 -194
- package/src/paperless/matching.test.ts +0 -128
|
@@ -1,44 +1,47 @@
|
|
|
1
1
|
import type { Input } from 'alchemy/Input';
|
|
2
|
+
import type { PaperlessNgxOpContext } from '@distilled.cloud/paperless-ngx/Protocol';
|
|
2
3
|
import * as Effect from 'effect/Effect';
|
|
3
4
|
import type { MatchingProps, MatchingSpec } from './matching-types.ts';
|
|
4
5
|
/** Re-exported so `tag.ts`/`document-type.ts`/`storage-path.ts`/`custom-field.ts` keep importing the contract from `./matching.ts` — only the shape itself lives in `matching-types.ts`, split out to hold the file-size cap. */
|
|
5
|
-
export type { FieldSpec,
|
|
6
|
-
export
|
|
6
|
+
export type { FieldSpec, MatchingPage, MatchingProps, MatchingRequirements, MatchingSpec, } from './matching-types.ts';
|
|
7
|
+
export { LOCATE_PAGE, soleMatch } from './matching-locate.ts';
|
|
8
|
+
export declare const matchingOperations: <Props extends MatchingProps, Live extends {
|
|
7
9
|
readonly id: number;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
+
readonly name: string;
|
|
11
|
+
}, Attributes extends {
|
|
12
|
+
readonly id: number;
|
|
13
|
+
}, E>(spec: MatchingSpec<Props, Live, Attributes, E>) => {
|
|
14
|
+
read: (props: Props) => Effect.Effect<Attributes | undefined, E, PaperlessNgxOpContext>;
|
|
10
15
|
diff: (news: Input<Props>, output: Attributes | undefined) => Effect.Effect<{
|
|
11
16
|
readonly action: 'update';
|
|
12
17
|
} | {
|
|
13
18
|
readonly action: 'noop';
|
|
14
19
|
} | {
|
|
15
20
|
readonly action: 'replace';
|
|
16
|
-
} | undefined,
|
|
17
|
-
reconcile: (news: Props, output: Attributes | undefined) => Effect.Effect<Attributes,
|
|
18
|
-
/**
|
|
19
|
-
|
|
20
|
-
* passes `output` here too (`Apply.ts`'s delete call sites all carry `output: attr`) —
|
|
21
|
-
* `olds` is only the PROPS as last persisted, which drifts from the live object exactly
|
|
22
|
-
* the way `news` did pre-fix: rename it out of band (by hand, or by another tool) after
|
|
23
|
-
* the last successful reconcile, and a name-only locate silently finds nothing, `destroy`
|
|
24
|
-
* returns as if there were nothing to delete, and the engine drops the state row anyway —
|
|
25
|
-
* reporting "deleted" while the live object survives, permanently orphaned (the same
|
|
26
|
-
* consequence `matching.ts`'s header describes for the original bug, on the delete path
|
|
27
|
-
* the original fix didn't touch). A 404 on the `output.id` itself is NOT an error here,
|
|
28
|
-
* unlike `fetchLive`'s die — it means the object is already gone, which is exactly the
|
|
29
|
-
* idempotent success `delete` is supposed to report (S11, alchemy-provider-standard).
|
|
30
|
-
*/
|
|
31
|
-
destroy: (olds: Props, output: Attributes | undefined) => Effect.Effect<void, import("./credentials.ts").PaperlessCredentialsError | import("./errors.ts").PaperlessUnauthorized | import("./errors.ts").PaperlessUnavailable | import("./errors.ts").PaperlessValidation, import("./client.ts").PaperlessRequirements>;
|
|
21
|
+
} | undefined, E, PaperlessNgxOpContext>;
|
|
22
|
+
reconcile: (news: Props, output: Attributes | undefined) => Effect.Effect<Attributes, E, PaperlessNgxOpContext>;
|
|
23
|
+
/** ⛔ `output.id` FIRST, SAME SWITCH AS `fetchLive` (PR 163 re-review). A 404 on `output.id` is idempotent success, not an error, unlike `fetchLive`'s die. */
|
|
24
|
+
destroy: (olds: Props, output: Attributes | undefined) => Effect.Effect<void, E, PaperlessNgxOpContext>;
|
|
32
25
|
};
|
|
33
|
-
/**
|
|
34
|
-
|
|
26
|
+
/**
|
|
27
|
+
* The five provider handlers for a spec'd Paperless taxonomy object, wired once.
|
|
28
|
+
*
|
|
29
|
+
* ★ CREDENTIALS ARE PROVIDED HERE, ONCE, NOT BY EVERY CALLER — the same shape
|
|
30
|
+
* `netboxHandlers`/`forgejoHandlers` use. `CredentialsFromEnv` resolves `PAPERLESS_URL` /
|
|
31
|
+
* `PAPERLESS_TOKEN` on the calling fiber per request, so this closure keeps that laziness —
|
|
32
|
+
* nothing is captured at module load. ⛔ THE TOKEN IS NEVER A PROP.
|
|
33
|
+
*/
|
|
34
|
+
export declare const matchingHandlers: <Props extends MatchingProps, Live extends {
|
|
35
|
+
readonly id: number;
|
|
36
|
+
readonly name: string;
|
|
37
|
+
}, Attributes extends {
|
|
35
38
|
readonly id: number;
|
|
36
|
-
}>(spec: MatchingSpec<Props, Attributes>) => {
|
|
39
|
+
}, E>(spec: MatchingSpec<Props, Live, Attributes, E>) => {
|
|
37
40
|
/** ⚠️ `[]` — Paperless index endpoints return the whole estate; adoption stays explicit. */
|
|
38
41
|
list: () => Effect.Effect<never[], never, never>;
|
|
39
42
|
read: ({ olds }: {
|
|
40
43
|
olds: Props;
|
|
41
|
-
}) => Effect.Effect<Attributes | undefined,
|
|
44
|
+
}) => Effect.Effect<Attributes | undefined, E, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
42
45
|
diff: ({ news, output }: {
|
|
43
46
|
news: Input<Props>;
|
|
44
47
|
output: Attributes | undefined;
|
|
@@ -48,14 +51,14 @@ export declare const matchingHandlers: <Props extends MatchingProps, Attributes
|
|
|
48
51
|
readonly action: 'noop';
|
|
49
52
|
} | {
|
|
50
53
|
readonly action: 'replace';
|
|
51
|
-
} | undefined,
|
|
54
|
+
} | undefined, E, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
52
55
|
reconcile: ({ news, output }: {
|
|
53
56
|
news: Props;
|
|
54
57
|
output?: Attributes | undefined;
|
|
55
|
-
}) => Effect.Effect<Attributes,
|
|
58
|
+
}) => Effect.Effect<Attributes, E, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
56
59
|
delete: ({ olds, output }: {
|
|
57
60
|
olds: Props;
|
|
58
61
|
output?: Attributes | undefined;
|
|
59
|
-
}) => Effect.Effect<void,
|
|
62
|
+
}) => Effect.Effect<void, E, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
60
63
|
};
|
|
61
64
|
//# sourceMappingURL=matching.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"matching.d.ts","sourceRoot":"","sources":["../../src/paperless/matching.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"matching.d.ts","sourceRoot":"","sources":["../../src/paperless/matching.ts"],"names":[],"mappings":"AAkCA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAE3C,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,yCAAyC,CAAC;AACrF,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAIxC,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEvE,iOAAiO;AACjO,YAAY,EACV,SAAS,EACT,YAAY,EACZ,aAAa,EACb,oBAAoB,EACpB,YAAY,GACb,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAM9D,eAAO,MAAM,kBAAkB,GAC7B,KAAK,SAAS,aAAa,EAC3B,IAAI,SAAS;IAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAC3D,UAAU,SAAS;IAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,EAC1C,CAAC,QAEK,YAAY,CAAC,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC;kBAyBzB,KAAK;iBAgDX,KAAK,CAAC,KAAK,CAAC,UAAU,UAAU,GAAG,SAAS;yBAIZ,QAAQ;;yBAGP,MAAM;;yBAEjC,SAAS;;sBAIV,KAAK,UAAU,UAAU,GAAG,SAAS;IA+BvD,8JAA8J;oBAC9I,KAAK,UAAU,UAAU,GAAG,SAAS;CAOxD,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,GAC3B,KAAK,SAAS,aAAa,EAC3B,IAAI,SAAS;IAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAC3D,UAAU,SAAS;IAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,EAC1C,CAAC,QAEK,YAAY,CAAC,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC;IAM5C,4FAA4F;IAC5F,IAAI;IACJ,IAAI,aAAa;QAAE,IAAI,EAAE,KAAK,CAAA;KAAE;IAMhC,IAAI,qBAAqB;QAAE,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;QAAC,MAAM,EAAE,UAAU,GAAG,SAAS,CAAA;KAAE;yBA9ElC,QAAQ;;yBAGP,MAAM;;yBAEjC,SAAS;;IA2E5B,SAAS,qBAAqB;QAAE,IAAI,EAAE,KAAK,CAAC;QAAC,MAAM,CAAC,EAAE,UAAU,GAAG,SAAS,CAAA;KAAE;IAE9E,MAAM,qBAAqB;QAAE,IAAI,EAAE,KAAK,CAAC;QAAC,MAAM,CAAC,EAAE,UAAU,GAAG,SAAS,CAAA;KAAE;CAG9E,CAAC"}
|
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* All four Paperless taxonomy providers
|
|
2
|
+
* All four Paperless taxonomy providers.
|
|
3
3
|
*
|
|
4
4
|
* Layer.mergeAll(paperlessProviders(), …the stack's other providers)
|
|
5
5
|
*
|
|
6
6
|
* ⚠️ `HttpClient.HttpClient` IS NOT BUNDLED HERE, and that follows `../netbox/index.ts` and
|
|
7
|
-
* `../forgejo/client.ts`'s own precedent
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* `../forgejo/client.ts`'s own precedent: the transport is the consuming STACK's choice
|
|
8
|
+
* (`FetchHttpClient.layer` in production, a fake `HttpClient` in a test), not a house default a
|
|
9
|
+
* provider file should pin.
|
|
10
|
+
*
|
|
11
|
+
* 🔴 NO `credentials` PARAMETER ANY MORE (2026-09-24, the `@distilled.cloud/paperless-ngx`
|
|
12
|
+
* migration — see `matching.ts`'s header). Each of the four `xxxProvider()`s now bakes in
|
|
13
|
+
* `CredentialsFromEnv` itself, the same way `netboxHandlers`/`forgejoHandlers` do — there is no
|
|
14
|
+
* longer a layer for this file to merge in or accept an override for. Nothing in this estate
|
|
15
|
+
* called `paperlessProviders(customLayer)` (measured 2026-09-24: no tray repo declares a
|
|
16
|
+
* Paperless resource at all — grep across `homeflare-landscape`'s tray repos' `origin/main`),
|
|
17
|
+
* so this is an uneventful simplification, not a behaviour change for anything live.
|
|
11
18
|
*/
|
|
12
19
|
import * as Layer from 'effect/Layer';
|
|
13
|
-
import
|
|
14
|
-
export declare const paperlessProviders: (credentials?: Layer.Layer<PaperlessCredentials>) => Layer.Layer<import("alchemy").Provider<import("./custom-field.ts").CustomField> | import("alchemy").Provider<import("./document-type.ts").DocumentType> | import("alchemy").Provider<import("./storage-path.ts").StoragePath> | import("alchemy").Provider<import("./tag.ts").Tag>, never, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
20
|
+
export declare const paperlessProviders: () => Layer.Layer<import("alchemy").Provider<import("./custom-field.ts").CustomField> | import("alchemy").Provider<import("./document-type.ts").DocumentType> | import("alchemy").Provider<import("./storage-path.ts").StoragePath> | import("alchemy").Provider<import("./tag.ts").Tag>, never, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
15
21
|
//# sourceMappingURL=providers.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"providers.d.ts","sourceRoot":"","sources":["../../src/paperless/providers.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"providers.d.ts","sourceRoot":"","sources":["../../src/paperless/providers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AAMtC,eAAO,MAAM,kBAAkB,wVAM5B,CAAC"}
|
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `Paperless.StoragePath` — a Paperless-ngx storage path template, create-or-update and never
|
|
3
|
-
* deleted. Same shape as `document-type.ts` plus the vendor-required `path` template string
|
|
3
|
+
* deleted. Same shape as `document-type.ts` plus the vendor-required `path` template string; see
|
|
4
|
+
* `tag.ts`'s header for the 2026-09-24 transport migration this file follows exactly.
|
|
4
5
|
*/
|
|
5
6
|
import { Resource } from 'alchemy';
|
|
6
7
|
import * as Provider from 'alchemy/Provider';
|
|
8
|
+
import * as storagePaths from '@distilled.cloud/paperless-ngx/storage_paths';
|
|
9
|
+
import * as Effect from 'effect/Effect';
|
|
7
10
|
import type { PaperlessStoragePathRequest } from './generated/types/storage-paths.ts';
|
|
8
|
-
import { type
|
|
9
|
-
export interface StoragePathProps
|
|
11
|
+
import { type MatchingRequirements, type MatchingSpec } from './matching.ts';
|
|
12
|
+
export interface StoragePathProps {
|
|
10
13
|
/** ⚠️ maxLength 128 (generated constraint table). The locate key. */
|
|
11
14
|
name: string;
|
|
15
|
+
owner?: number;
|
|
12
16
|
/** ⚠️ minLength 1 (generated table). Paperless's storage-path template syntax, e.g. `{correspondent}/{title}`. */
|
|
13
17
|
path: string;
|
|
14
18
|
/** ⚠️ maxLength 256. */
|
|
@@ -28,5 +32,31 @@ export interface StoragePathAttributes {
|
|
|
28
32
|
export interface StoragePath extends Resource<'Paperless.StoragePath', StoragePathProps, StoragePathAttributes, never, MatchingRequirements> {
|
|
29
33
|
}
|
|
30
34
|
export declare const StoragePath: import("alchemy").ResourceClass<StoragePath>;
|
|
31
|
-
|
|
35
|
+
/** ★ Exported for direct testing — see tag.ts's own note on this seam. */
|
|
36
|
+
export declare const spec: MatchingSpec<StoragePathProps, storagePaths.StoragePath, StoragePathAttributes, storagePaths.CreateStoragePathError | storagePaths.GetStoragePathError | storagePaths.ListStoragePathsError | storagePaths.UpdateStoragePathsPartialError | storagePaths.StoragePathsDestroyError>;
|
|
37
|
+
export declare const handlers: {
|
|
38
|
+
list: () => Effect.Effect<never[], never, never>;
|
|
39
|
+
read: ({ olds }: {
|
|
40
|
+
olds: StoragePathProps;
|
|
41
|
+
}) => Effect.Effect<StoragePathAttributes | undefined, import("@distilled.cloud/caddy").BadGateway | storagePaths.BadRequest | import("@distilled.cloud/caddy").ConfigError | storagePaths.Forbidden | import("@distilled.cloud/caddy").GatewayTimeout | import("effect/unstable/http/HttpClientError").HttpClientError | import("@distilled.cloud/caddy").InternalServerError | storagePaths.NotFound | import("@distilled.cloud/paperless-ngx/Errors").PaperlessNgxParseError | import("@distilled.cloud/caddy").ServiceUnavailable | import("@distilled.cloud/caddy").TooManyRequests | import("@distilled.cloud/caddy").Unauthorized | import("@distilled.cloud/paperless-ngx/Errors").UnknownPaperlessNgxError, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
42
|
+
diff: ({ news, output }: {
|
|
43
|
+
news: import("alchemy").Input<StoragePathProps>;
|
|
44
|
+
output: StoragePathAttributes | undefined;
|
|
45
|
+
}) => Effect.Effect<{
|
|
46
|
+
readonly action: 'update';
|
|
47
|
+
} | {
|
|
48
|
+
readonly action: 'noop';
|
|
49
|
+
} | {
|
|
50
|
+
readonly action: 'replace';
|
|
51
|
+
} | undefined, import("@distilled.cloud/caddy").BadGateway | storagePaths.BadRequest | import("@distilled.cloud/caddy").ConfigError | storagePaths.Forbidden | import("@distilled.cloud/caddy").GatewayTimeout | import("effect/unstable/http/HttpClientError").HttpClientError | import("@distilled.cloud/caddy").InternalServerError | storagePaths.NotFound | import("@distilled.cloud/paperless-ngx/Errors").PaperlessNgxParseError | import("@distilled.cloud/caddy").ServiceUnavailable | import("@distilled.cloud/caddy").TooManyRequests | import("@distilled.cloud/caddy").Unauthorized | import("@distilled.cloud/paperless-ngx/Errors").UnknownPaperlessNgxError, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
52
|
+
reconcile: ({ news, output }: {
|
|
53
|
+
news: StoragePathProps;
|
|
54
|
+
output?: StoragePathAttributes | undefined;
|
|
55
|
+
}) => Effect.Effect<StoragePathAttributes, import("@distilled.cloud/caddy").BadGateway | storagePaths.BadRequest | import("@distilled.cloud/caddy").ConfigError | storagePaths.Forbidden | import("@distilled.cloud/caddy").GatewayTimeout | import("effect/unstable/http/HttpClientError").HttpClientError | import("@distilled.cloud/caddy").InternalServerError | storagePaths.NotFound | import("@distilled.cloud/paperless-ngx/Errors").PaperlessNgxParseError | import("@distilled.cloud/caddy").ServiceUnavailable | import("@distilled.cloud/caddy").TooManyRequests | import("@distilled.cloud/caddy").Unauthorized | import("@distilled.cloud/paperless-ngx/Errors").UnknownPaperlessNgxError, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
56
|
+
delete: ({ olds, output }: {
|
|
57
|
+
olds: StoragePathProps;
|
|
58
|
+
output?: StoragePathAttributes | undefined;
|
|
59
|
+
}) => Effect.Effect<void, import("@distilled.cloud/caddy").BadGateway | storagePaths.BadRequest | import("@distilled.cloud/caddy").ConfigError | storagePaths.Forbidden | import("@distilled.cloud/caddy").GatewayTimeout | import("effect/unstable/http/HttpClientError").HttpClientError | import("@distilled.cloud/caddy").InternalServerError | storagePaths.NotFound | import("@distilled.cloud/paperless-ngx/Errors").PaperlessNgxParseError | import("@distilled.cloud/caddy").ServiceUnavailable | import("@distilled.cloud/caddy").TooManyRequests | import("@distilled.cloud/caddy").Unauthorized | import("@distilled.cloud/paperless-ngx/Errors").UnknownPaperlessNgxError, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
60
|
+
};
|
|
61
|
+
export declare const StoragePathProvider: () => import("effect/Layer").Layer<Provider.Provider<StoragePath>, never, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
32
62
|
//# sourceMappingURL=storage-path.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage-path.d.ts","sourceRoot":"","sources":["../../src/paperless/storage-path.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"storage-path.d.ts","sourceRoot":"","sources":["../../src/paperless/storage-path.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,YAAY,MAAM,8CAA8C,CAAC;AAC7E,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,oCAAoC,CAAC;AACtF,OAAO,EAEL,KAAK,oBAAoB,EACzB,KAAK,YAAY,EAElB,MAAM,eAAe,CAAC;AAEvB,MAAM,WAAW,gBAAgB;IAC/B,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kHAAkH;IAClH,IAAI,EAAE,MAAM,CAAC;IACb,wBAAwB;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iBAAiB,CAAC,EAAE,2BAA2B,CAAC,oBAAoB,CAAC,CAAC;IACtE,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,iBAAiB,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,OAAO,CAAC;IACvB,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3B;AAED,MAAM,WAAW,WAAY,SAAQ,QAAQ,CAC3C,uBAAuB,EACvB,gBAAgB,EAChB,qBAAqB,EACrB,KAAK,EACL,oBAAoB,CACrB;CAAG;AAEJ,eAAO,MAAM,WAAW,8CAEtB,CAAC;AAEH,0EAA0E;AAC1E,eAAO,MAAM,IAAI,EAAE,YAAY,CAC7B,gBAAgB,EAChB,YAAY,CAAC,WAAW,EACxB,qBAAqB,EACnB,YAAY,CAAC,sBAAsB,GACnC,YAAY,CAAC,mBAAmB,GAChC,YAAY,CAAC,qBAAqB,GAClC,YAAY,CAAC,8BAA8B,GAC3C,YAAY,CAAC,wBAAwB,CAyCxC,CAAC;AAEF,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;CAAyB,CAAC;AAE/C,eAAO,MAAM,mBAAmB,iIACiD,CAAC"}
|
package/dist/paperless/tag.d.ts
CHANGED
|
@@ -2,22 +2,26 @@
|
|
|
2
2
|
* `Paperless.Tag` — a Paperless-ngx tag, create-or-update and never deleted by default.
|
|
3
3
|
*
|
|
4
4
|
* ★ `matchingAlgorithm` AND THE OTHER ENUM-SHAPED FIELDS ARE INDEXED OFF THE GENERATED REQUEST
|
|
5
|
-
* TYPE (`PaperlessTagRequest['matching_algorithm']`), NEVER RETYPED
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* forbids, and a vendor that adds a seventh algorithm would leave this type silently stale.
|
|
5
|
+
* TYPE (`PaperlessTagRequest['matching_algorithm']`), NEVER RETYPED — see this family's header
|
|
6
|
+
* in `docs/paperless.md`. Unchanged by the 2026-09-24 transport migration below: that generated
|
|
7
|
+
* table is `codegen/paperless.ts`'s own concern, not `@distilled.cloud/paperless-ngx`'s.
|
|
9
8
|
*
|
|
10
|
-
* ⛔ NO CREDENTIAL IS A PROP. `PAPERLESS_URL`/`PAPERLESS_TOKEN` come from
|
|
9
|
+
* ⛔ NO CREDENTIAL IS A PROP. `PAPERLESS_URL`/`PAPERLESS_TOKEN` come from
|
|
10
|
+
* `@distilled.cloud/paperless-ngx`'s `CredentialsFromEnv` (matching.ts), the same two variable
|
|
11
|
+
* names the retired hand-rolled `credentials.ts` read.
|
|
11
12
|
* ⛔ `defaultRemovalPolicy: 'retain'` — the reconciler this replaces never deletes taxonomy, and
|
|
12
13
|
* a tag's id is referenced by every document it is attached to.
|
|
13
14
|
*/
|
|
14
15
|
import { Resource } from 'alchemy';
|
|
15
16
|
import * as Provider from 'alchemy/Provider';
|
|
17
|
+
import * as tags from '@distilled.cloud/paperless-ngx/tags';
|
|
18
|
+
import * as Effect from 'effect/Effect';
|
|
16
19
|
import type { PaperlessTagRequest } from './generated/types/tags.ts';
|
|
17
|
-
import { type
|
|
18
|
-
export interface TagProps
|
|
20
|
+
import { type MatchingRequirements, type MatchingSpec } from './matching.ts';
|
|
21
|
+
export interface TagProps {
|
|
19
22
|
/** ⚠️ maxLength 128 (generated constraint table). The locate key — see matching.ts's header. */
|
|
20
23
|
name: string;
|
|
24
|
+
owner?: number;
|
|
21
25
|
/** ⚠️ minLength 1, maxLength 7 (a CSS hex color, generated table doesn't enforce the `#`). */
|
|
22
26
|
color?: string;
|
|
23
27
|
/** ⚠️ maxLength 256. The text or pattern documents are matched against. */
|
|
@@ -43,5 +47,36 @@ export interface TagAttributes {
|
|
|
43
47
|
export interface Tag extends Resource<'Paperless.Tag', TagProps, TagAttributes, never, MatchingRequirements> {
|
|
44
48
|
}
|
|
45
49
|
export declare const Tag: import("alchemy").ResourceClass<Tag>;
|
|
46
|
-
|
|
50
|
+
/**
|
|
51
|
+
* ★ EXPORTED, NOT JUST PASSED INLINE — so a test can call `matchingOperations(spec)` directly
|
|
52
|
+
* (tag.test.ts does), against an explicit fake `Credentials` layer, without touching
|
|
53
|
+
* `matchingHandlers`'s baked-in `CredentialsFromEnv` — the same seam `../forgejo/repository.ts`
|
|
54
|
+
* and `../netbox/prefix.ts` use.
|
|
55
|
+
*/
|
|
56
|
+
export declare const spec: MatchingSpec<TagProps, tags.Tag, TagAttributes, tags.CreateTagError | tags.GetTagError | tags.ListTagsError | tags.UpdateTagsPartialError | tags.TagsDestroyError>;
|
|
57
|
+
export declare const handlers: {
|
|
58
|
+
list: () => Effect.Effect<never[], never, never>;
|
|
59
|
+
read: ({ olds }: {
|
|
60
|
+
olds: TagProps;
|
|
61
|
+
}) => Effect.Effect<TagAttributes | undefined, import("@distilled.cloud/caddy").BadGateway | tags.BadRequest | import("@distilled.cloud/caddy").ConfigError | tags.Forbidden | import("@distilled.cloud/caddy").GatewayTimeout | import("effect/unstable/http/HttpClientError").HttpClientError | import("@distilled.cloud/caddy").InternalServerError | tags.NotFound | import("@distilled.cloud/paperless-ngx/Errors").PaperlessNgxParseError | import("@distilled.cloud/caddy").ServiceUnavailable | import("@distilled.cloud/caddy").TooManyRequests | import("@distilled.cloud/caddy").Unauthorized | import("@distilled.cloud/paperless-ngx/Errors").UnknownPaperlessNgxError, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
62
|
+
diff: ({ news, output }: {
|
|
63
|
+
news: import("alchemy").Input<TagProps>;
|
|
64
|
+
output: TagAttributes | undefined;
|
|
65
|
+
}) => Effect.Effect<{
|
|
66
|
+
readonly action: 'update';
|
|
67
|
+
} | {
|
|
68
|
+
readonly action: 'noop';
|
|
69
|
+
} | {
|
|
70
|
+
readonly action: 'replace';
|
|
71
|
+
} | undefined, import("@distilled.cloud/caddy").BadGateway | tags.BadRequest | import("@distilled.cloud/caddy").ConfigError | tags.Forbidden | import("@distilled.cloud/caddy").GatewayTimeout | import("effect/unstable/http/HttpClientError").HttpClientError | import("@distilled.cloud/caddy").InternalServerError | tags.NotFound | import("@distilled.cloud/paperless-ngx/Errors").PaperlessNgxParseError | import("@distilled.cloud/caddy").ServiceUnavailable | import("@distilled.cloud/caddy").TooManyRequests | import("@distilled.cloud/caddy").Unauthorized | import("@distilled.cloud/paperless-ngx/Errors").UnknownPaperlessNgxError, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
72
|
+
reconcile: ({ news, output }: {
|
|
73
|
+
news: TagProps;
|
|
74
|
+
output?: TagAttributes | undefined;
|
|
75
|
+
}) => Effect.Effect<TagAttributes, import("@distilled.cloud/caddy").BadGateway | tags.BadRequest | import("@distilled.cloud/caddy").ConfigError | tags.Forbidden | import("@distilled.cloud/caddy").GatewayTimeout | import("effect/unstable/http/HttpClientError").HttpClientError | import("@distilled.cloud/caddy").InternalServerError | tags.NotFound | import("@distilled.cloud/paperless-ngx/Errors").PaperlessNgxParseError | import("@distilled.cloud/caddy").ServiceUnavailable | import("@distilled.cloud/caddy").TooManyRequests | import("@distilled.cloud/caddy").Unauthorized | import("@distilled.cloud/paperless-ngx/Errors").UnknownPaperlessNgxError, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
76
|
+
delete: ({ olds, output }: {
|
|
77
|
+
olds: TagProps;
|
|
78
|
+
output?: TagAttributes | undefined;
|
|
79
|
+
}) => Effect.Effect<void, import("@distilled.cloud/caddy").BadGateway | tags.BadRequest | import("@distilled.cloud/caddy").ConfigError | tags.Forbidden | import("@distilled.cloud/caddy").GatewayTimeout | import("effect/unstable/http/HttpClientError").HttpClientError | import("@distilled.cloud/caddy").InternalServerError | tags.NotFound | import("@distilled.cloud/paperless-ngx/Errors").PaperlessNgxParseError | import("@distilled.cloud/caddy").ServiceUnavailable | import("@distilled.cloud/caddy").TooManyRequests | import("@distilled.cloud/caddy").Unauthorized | import("@distilled.cloud/paperless-ngx/Errors").UnknownPaperlessNgxError, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
80
|
+
};
|
|
81
|
+
export declare const TagProvider: () => import("effect/Layer").Layer<Provider.Provider<Tag>, never, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
47
82
|
//# sourceMappingURL=tag.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tag.d.ts","sourceRoot":"","sources":["../../src/paperless/tag.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"tag.d.ts","sourceRoot":"","sources":["../../src/paperless/tag.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,IAAI,MAAM,qCAAqC,CAAC;AAC5D,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,EAEL,KAAK,oBAAoB,EACzB,KAAK,YAAY,EAElB,MAAM,eAAe,CAAC;AAEvB,MAAM,WAAW,QAAQ;IACvB,gGAAgG;IAChG,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8FAA8F;IAC9F,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2EAA2E;IAC3E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iBAAiB,CAAC,EAAE,mBAAmB,CAAC,oBAAoB,CAAC,CAAC;IAC9D,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,mFAAmF;IACnF,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,gFAAgF;IAChF,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,iBAAiB,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,OAAO,CAAC;IACvB,UAAU,EAAE,OAAO,CAAC;IACpB,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5B;AAED,MAAM,WAAW,GAAI,SAAQ,QAAQ,CACnC,eAAe,EACf,QAAQ,EACR,aAAa,EACb,KAAK,EACL,oBAAoB,CACrB;CAAG;AAEJ,eAAO,MAAM,GAAG,sCAAqE,CAAC;AAEtF;;;;;GAKG;AACH,eAAO,MAAM,IAAI,EAAE,YAAY,CAC7B,QAAQ,EACR,IAAI,CAAC,GAAG,EACR,aAAa,EACX,IAAI,CAAC,cAAc,GACnB,IAAI,CAAC,WAAW,GAChB,IAAI,CAAC,aAAa,GAClB,IAAI,CAAC,sBAAsB,GAC3B,IAAI,CAAC,gBAAgB,CAsCxB,CAAC;AAEF,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;CAAyB,CAAC;AAE/C,eAAO,MAAM,WAAW,yHAAwE,CAAC"}
|
package/dist/proxmox/acl.d.ts
CHANGED
|
@@ -6,39 +6,52 @@
|
|
|
6
6
|
* {path, user|group|token, role} exists. That is what makes those four families worth declaring,
|
|
7
7
|
* and why the grants that let OpenBao mint a PVE token today are only clicks somebody made once.
|
|
8
8
|
*
|
|
9
|
-
* ⛔ THE ONE FAMILY THAT DOES NOT FIT
|
|
9
|
+
* ⛔ THE ONE FAMILY THAT DOES NOT FIT A GENERIC PATH+FORM SHAPE, AND THE MISFIT IS MEASURED. The
|
|
10
10
|
* cluster's own schema — `/pve-docs/api-viewer/apidoc.js`, read unauthenticated 2026-09-13 —
|
|
11
11
|
* lists exactly TWO methods on `/access/acl`: GET and PUT. There is no DELETE (it answers
|
|
12
|
-
* "Method 'DELETE /access/acl' not implemented") and, less famously, NO POST either
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* with it, and this override disappears. Until then it is local and loud — do not "restore
|
|
17
|
-
* symmetry" by pointing `delete` at `ops.destroy`, which reports every removal as a 501 while
|
|
18
|
-
* the grant stays exactly where it was.
|
|
12
|
+
* "Method 'DELETE /access/acl' not implemented") and, less famously, NO POST either — which is
|
|
13
|
+
* why `@distilled.cloud/proxmox`'s generator, reading the same schema, emits `listAccessAcl` and
|
|
14
|
+
* `putAccessAcl` and NOTHING ELSE for this path. So create and update are the SAME call, `PUT
|
|
15
|
+
* /access/acl`, and removal is that PUT with `delete=1` — there is no `destroyAccessAcl` to call.
|
|
19
16
|
*
|
|
20
17
|
* ⛔ A SINGLE GRANT HAS NO URL OF ITS OWN. `GET /access/acl` answers ONE FLAT LIST for the whole
|
|
21
|
-
* cluster, so identity is the tuple (path, type, ugid, roleid), matched client-side in
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
18
|
+
* cluster, so identity is the tuple (path, type, ugid, roleid), matched client-side in `find`
|
|
19
|
+
* below. Two resources declaring the SAME tuple are the SAME grant — Alchemy sees two resource
|
|
20
|
+
* ids, not one collision — and deleting either takes the access away from both, the same hazard
|
|
21
|
+
* as two guests declaring one vmid.
|
|
25
22
|
*
|
|
26
23
|
* ⛔ AND THAT LIST IS FILTERED BY WHO IS ASKING. The schema's own words for GET: "The returned list
|
|
27
24
|
* is restricted to objects where you have rights to modify permissions." A credential that can
|
|
28
25
|
* read the cluster but not modify permissions is answered `[]` — not a 403, not an error — so
|
|
29
26
|
* the mount's `read` role needs permission-modify rights on the declared path exactly as
|
|
30
|
-
* `provision` does
|
|
27
|
+
* `provision` does (below, `listAcl` reads with the `provision` role for this reason). Work
|
|
28
|
+
* reported on a grant that is plainly there means that role is too narrow.
|
|
29
|
+
*
|
|
30
|
+
* ★ MIGRATED OFF `client.ts`'s generic `pve()` ONTO `@distilled.cloud/proxmox`'s typed
|
|
31
|
+
* `access.listAccessAcl`/`access.putAccessAcl` (2026-09-23, decision 43's proxmox walk-down,
|
|
32
|
+
* the sub-area's first resource — ACL was already the misfit `pveOperations` could not cover
|
|
33
|
+
* cleanly, which is exactly why it goes first). `distilled-pve.ts`'s `runPve` replaces `pve()`:
|
|
34
|
+
* same lease reuse (lease-cache.ts), same cluster-member failover (members.ts), now handing the
|
|
35
|
+
* request itself to the SDK instead of a hand-assembled `HttpClientRequest`.
|
|
36
|
+
* ⛔ ONE DEAD BRANCH REMOVED, NOT PRESERVED: the old code's generic factory POSTed on a "live
|
|
37
|
+
* undefined" read, which its own header called unreachable on a healthy cluster and
|
|
38
|
+
* documented as MISLEADING — PVE has no POST here, so that 501 said nothing about the real
|
|
39
|
+
* failure. `@distilled.cloud/proxmox` has no `createAccessAcl` at all (the vendor schema has
|
|
40
|
+
* no POST to generate one from), so there is nothing to call that way. `reconcile` below
|
|
41
|
+
* always PUTs instead — the only write PVE actually implements for this path — so a read that
|
|
42
|
+
* genuinely failed now surfaces its REAL cause (a typed error, or `PveClusterExhausted`)
|
|
43
|
+
* rather than a confusing 501 from a verb that was never there.
|
|
31
44
|
*/
|
|
32
45
|
import { Resource } from 'alchemy';
|
|
33
46
|
import * as Provider from 'alchemy/Provider';
|
|
34
|
-
import {
|
|
47
|
+
import type { PveRequirements, WithTarget } from './resource-spec.ts';
|
|
35
48
|
/** PVE's three kinds of subject. The read answers this word; the write wants its plural. */
|
|
36
49
|
export type AclSubjectType = 'user' | 'group' | 'token';
|
|
37
50
|
export interface AclProps extends WithTarget {
|
|
38
51
|
/**
|
|
39
52
|
* The PVE object path the grant is ON — `/`, `/pool/lab`, `/vms/101`, `/storage/local-zfs`.
|
|
40
|
-
* ⚠️ NOT THE API PATH
|
|
41
|
-
*
|
|
53
|
+
* ⚠️ NOT THE API PATH — the endpoint is fixed (`access/acl`). PVE's API overloads the word,
|
|
54
|
+
* so this file does too rather than renaming a field the cluster calls `path`.
|
|
42
55
|
*/
|
|
43
56
|
path: string;
|
|
44
57
|
/** Which kind of subject `ugid` names. Identity: changing it is a different grant. */
|
|
@@ -56,13 +69,7 @@ export interface AclAttributes {
|
|
|
56
69
|
ugid: string;
|
|
57
70
|
roleid: string;
|
|
58
71
|
propagate: boolean;
|
|
59
|
-
/**
|
|
60
|
-
* ⛔ PRESENCE AS AN ATTRIBUTE, AND IT IS WHAT MAKES THE FACTORY WORK HERE. `attributes` never
|
|
61
|
-
* returns undefined: if it did, `reconcile` would take the create branch and POST to
|
|
62
|
-
* `access/acl`, which PVE does not implement. Present-but-unbound sends every write down the
|
|
63
|
-
* PUT branch instead, the only branch PVE has. The cost: the factory's read-back guard cannot
|
|
64
|
-
* fire, so `reconcile` below re-checks this field itself and dies with the same honesty.
|
|
65
|
-
*/
|
|
72
|
+
/** Presence, so a PUT-only family can tell "matches" from "does not exist yet" — see below. */
|
|
66
73
|
bound: boolean;
|
|
67
74
|
}
|
|
68
75
|
export interface ProxmoxAcl extends Resource<'Proxmox.Acl', AclProps, AclAttributes, never, PveRequirements> {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"acl.d.ts","sourceRoot":"","sources":["../../src/proxmox/acl.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"acl.d.ts","sourceRoot":"","sources":["../../src/proxmox/acl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAI7C,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAGtE,4FAA4F;AAC5F,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;AAExD,MAAM,WAAW,QAAS,SAAQ,UAAU;IAC1C;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,sFAAsF;IACtF,IAAI,EAAE,cAAc,CAAC;IACrB,0EAA0E;IAC1E,IAAI,EAAE,MAAM,CAAC;IACb,gGAAgG;IAChG,MAAM,EAAE,MAAM,CAAC;IACf,8FAA8F;IAC9F,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,cAAc,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,OAAO,CAAC;IACnB,+FAA+F;IAC/F,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,UAAW,SAAQ,QAAQ,CAC1C,aAAa,EACb,QAAQ,EACR,aAAa,EACb,KAAK,EACL,eAAe,CAChB;CAAG;AAEJ,eAAO,MAAM,UAAU,6CAAsC,CAAC;AAuF9D,eAAO,MAAM,kBAAkB,gIAoE5B,CAAC"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Running one `@distilled.cloud/proxmox` typed operation the way `client.ts`'s `pve()` ran a raw
|
|
3
|
+
* HTTP call — same credential lease reuse (lease-cache.ts) and the same cluster-member failover
|
|
4
|
+
* safety rule (members.ts), now handing the request itself to the SDK instead of building one.
|
|
5
|
+
*
|
|
6
|
+
* ★ THIS IS THE SEAM A DISTILLED-BACKED FAMILY CALLS INSTEAD OF `pve()`. `client.ts` stays for
|
|
7
|
+
* every family not yet migrated — see acl.ts, the first one, for what a migrated family looks
|
|
8
|
+
* like. A family migrates by calling its own `@distilled.cloud/proxmox/<segment>` typed
|
|
9
|
+
* operations through `runPve` below, never through a generic method+path+form call: that is
|
|
10
|
+
* what "no hand-rolled client" means in practice (upstream Alchemy PR 1425).
|
|
11
|
+
*
|
|
12
|
+
* ⚠️ THE WHOLE OPERATION IS RE-RUN PER MEMBER, NOT JUST ONE HTTP CALL. `executeOnCluster` in
|
|
13
|
+
* members.ts retries a single already-built `HttpClientRequest`; a distilled operation builds
|
|
14
|
+
* its OWN request internally from whatever `Credentials` it is given, so there is no request to
|
|
15
|
+
* hand between attempts. Failover here instead re-provides `Credentials` with the next member's
|
|
16
|
+
* `apiBaseUrl` and re-runs the same operation value. This is safe under the identical rule
|
|
17
|
+
* `executeOnCluster` documents and members.ts's classifiers still enforce: a write may repeat
|
|
18
|
+
* only on a PROVABLY pre-send transport failure (connection refused, DNS, a refused
|
|
19
|
+
* certificate) — never on a timeout after connect, and never on an HTTP answer of any kind,
|
|
20
|
+
* because an answer (even a typed error the SDK decoded) proves the request reached a live
|
|
21
|
+
* member and must not be sent again elsewhere. Reads may fail over on any transport failure.
|
|
22
|
+
*
|
|
23
|
+
* ⛔ A TYPED ANSWER-ERROR IS NEVER A REASON TO TRY ANOTHER MEMBER. `Forbidden`,
|
|
24
|
+
* `ParameterVerificationFailed`, `ClusterNodeUnreachable`, … all mean the request reached a
|
|
25
|
+
* member and that member answered — `isTransportFailure`/`isPreSendTransport` (members.ts)
|
|
26
|
+
* return `false` for all of them, exactly as they do for the `HttpClientResponse` case in
|
|
27
|
+
* `executeOnCluster`, since neither function inspects anything but a transport-level
|
|
28
|
+
* `HttpClientError`. Such an error propagates from here UNCHANGED — not wrapped, not retried —
|
|
29
|
+
* which is what lets a caller `catchTag` it directly.
|
|
30
|
+
*
|
|
31
|
+
* TRAP FOUND WHILE WRITING THE FAILOVER TEST: `Retry.none` on every attempt, and it is not
|
|
32
|
+
* optional. `@distilled.cloud/core`'s DEFAULT policy retries any error in its `ServerError`
|
|
33
|
+
* category automatically, with no caller opt-in (protocol.ts's own header names the mechanism:
|
|
34
|
+
* `core/category.ts`'s `isTransientError` via `makeDefault`) -- and that category covers
|
|
35
|
+
* transport failures too, not just 5xx answers: MEASURED -- a plain `listAccessAcl({})` against
|
|
36
|
+
* a member that refuses the connection hung past a 5-second test timeout, backing off inside
|
|
37
|
+
* the SDK before this file's own failover loop ever saw a failure to classify. Left enabled, a
|
|
38
|
+
* WRITE could be resent to the SAME member several times before `runPveWith` gets a result at
|
|
39
|
+
* all -- silently reintroducing, inside one member, the exact risk `executeOnCluster`
|
|
40
|
+
* (members.ts) exists to rule out across members ("a request that may have reached a member
|
|
41
|
+
* must not be sent again"). The hand-rolled client never auto-retried anything; member failover
|
|
42
|
+
* was its only resilience. `Retry.none` below keeps that property true here too.
|
|
43
|
+
*
|
|
44
|
+
* ⛔ GAP FOUND ON REVIEW, FIXED 2026-09-24: `Retry.none` only stops the SDK re-sending to the SAME
|
|
45
|
+
* member -- it added no bound on how long ONE attempt may wait. A member that accepts the TCP
|
|
46
|
+
* connection but never answers hung this whole function forever, since the for-loop below never
|
|
47
|
+
* got a result to classify and so never reached a healthy member. `MEMBER_TIMEOUT` (members.ts,
|
|
48
|
+
* shared with `client.ts`'s `executeOnCluster`) now bounds each attempt; `isTransportFailure`
|
|
49
|
+
* treats the resulting `Cause.TimeoutError` as failover-eligible for a read, and
|
|
50
|
+
* `isPreSendTransport` still refuses it for a write, for the same reason a post-send timeout
|
|
51
|
+
* always was refused.
|
|
52
|
+
*/
|
|
53
|
+
import type * as Cause from 'effect/Cause';
|
|
54
|
+
import type * as Duration from 'effect/Duration';
|
|
55
|
+
import * as Effect from 'effect/Effect';
|
|
56
|
+
import type * as HttpClient from 'effect/unstable/http/HttpClient';
|
|
57
|
+
import type { ProxmoxOpContext } from '@distilled.cloud/proxmox/Protocol';
|
|
58
|
+
import type { PveCredential, PveRole, PveTarget } from './credentials.ts';
|
|
59
|
+
import { type MemberAttempt } from './members.ts';
|
|
60
|
+
/** Every member answered nothing usable — the direct successor to `client.ts`'s `clusterExhausted`. */
|
|
61
|
+
export declare class PveClusterExhausted extends Error {
|
|
62
|
+
readonly attempts: readonly MemberAttempt[];
|
|
63
|
+
constructor(attempts: readonly MemberAttempt[]);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Run `op` against `target`'s cluster on an ALREADY-HELD credential — the direct successor to
|
|
67
|
+
* `client.ts`'s `pveWith`, for a caller reasoning about identity rather than cost (or a test that
|
|
68
|
+
* wants a fixed, explicit credential rather than a live OpenBao mint — see distilled-pve.test.ts).
|
|
69
|
+
*
|
|
70
|
+
* @param isWrite Whether `op` may have a side effect once sent — decides which transport
|
|
71
|
+
* failures may retry on the next member. Pass `true` for anything that is not a plain read.
|
|
72
|
+
* @param timeout Bound on ONE member's attempt — defaults to `MEMBER_TIMEOUT` (members.ts). A
|
|
73
|
+
* test passes a short one to prove failover/bounding without waiting out the real bound;
|
|
74
|
+
* production code never overrides it. A member that answers past this is failed over (a read)
|
|
75
|
+
* or failed outright (a write, since the request may already have landed) — see
|
|
76
|
+
* `isTransportFailure`/`isPreSendTransport`.
|
|
77
|
+
*/
|
|
78
|
+
export declare const runPveWith: <A, E>(target: PveTarget, credential: PveCredential, isWrite: boolean, op: Effect.Effect<A, E, ProxmoxOpContext>, timeout?: Duration.Input) => Effect.Effect<A, E | Cause.TimeoutError | PveClusterExhausted, HttpClient.HttpClient>;
|
|
79
|
+
/**
|
|
80
|
+
* `runPveWith`, minting/reusing a `role` lease exactly as `pve()` did — the seam a migrated
|
|
81
|
+
* resource file calls.
|
|
82
|
+
*
|
|
83
|
+
* ⚠️ `| Error` IN THE RETURN TYPE IS `leased()`'s OWN FAILURE, NOT A NEW ONE THIS FILE ADDS.
|
|
84
|
+
* `credentials.ts`'s `mint` fails with a plain `Error` (an OpenBao refusal — sealed, denied, no
|
|
85
|
+
* such role) and was never typed more narrowly there; `pve()` carried the same untyped
|
|
86
|
+
* possibility before this file existed, just without a signature that said so.
|
|
87
|
+
*/
|
|
88
|
+
export declare const runPve: <A, E>(target: PveTarget, role: PveRole, isWrite: boolean, op: Effect.Effect<A, E, ProxmoxOpContext>) => Effect.Effect<A, E | Cause.TimeoutError | PveClusterExhausted | Error, HttpClient.HttpClient>;
|
|
89
|
+
//# sourceMappingURL=distilled-pve.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"distilled-pve.d.ts","sourceRoot":"","sources":["../../src/proxmox/distilled-pve.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AACH,OAAO,KAAK,KAAK,KAAK,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,KAAK,QAAQ,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,OAAO,KAAK,KAAK,UAAU,MAAM,iCAAiC,CAAC;AAEnE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAE1E,OAAO,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE1E,OAAO,EAEL,KAAK,aAAa,EAOnB,MAAM,cAAc,CAAC;AAEtB,uGAAuG;AACvG,qBAAa,mBAAoB,SAAQ,KAAK;IAChC,QAAQ,CAAC,QAAQ,EAAE,SAAS,aAAa,EAAE;IAAvD,YAAqB,QAAQ,EAAE,SAAS,aAAa,EAAE,EAGtD;CACF;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,UAAU,GAAI,CAAC,EAAE,CAAC,UACrB,SAAS,cACL,aAAa,WAChB,OAAO,MACZ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,gBAAgB,CAAC,YAChC,QAAQ,CAAC,KAAK,KACtB,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,YAAY,GAAG,mBAAmB,EAAE,UAAU,CAAC,UAAU,CA0BnF,CAAC;AAEL;;;;;;;;GAQG;AACH,eAAO,MAAM,MAAM,GAAI,CAAC,EAAE,CAAC,UACjB,SAAS,QACX,OAAO,WACJ,OAAO,MACZ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,gBAAgB,CAAC,KACxC,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,YAAY,GAAG,mBAAmB,GAAG,KAAK,EAAE,UAAU,CAAC,UAAU,CAI3F,CAAC"}
|