@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
package/dist/caddy/index.js
CHANGED
|
@@ -43,304 +43,134 @@ var accessForwardAuth = (props) => {
|
|
|
43
43
|
import * as Context from "effect/Context";
|
|
44
44
|
import * as Layer from "effect/Layer";
|
|
45
45
|
|
|
46
|
-
class CaddyUnreachableError extends Error {
|
|
47
|
-
name = "CaddyUnreachableError";
|
|
48
|
-
}
|
|
49
|
-
|
|
50
46
|
class CaddyAdminService extends Context.Service()("homeflare/caddy/CaddyAdmin") {
|
|
51
47
|
}
|
|
52
|
-
var caddyAdminLayer = (
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
// src/
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
var
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
return text.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll("\r", " ");
|
|
78
|
-
};
|
|
79
|
-
var isPlainObject = (value) => {
|
|
80
|
-
const proto = Object.getPrototypeOf(value);
|
|
81
|
-
return proto === Object.prototype || proto === null;
|
|
82
|
-
};
|
|
83
|
-
var renderValue = (value, path, depth, out) => {
|
|
84
|
-
const pad = "\t".repeat(depth);
|
|
85
|
-
if (depth > MAX_DEPTH)
|
|
86
|
-
throw new PlistError(path, `nested deeper than ${String(MAX_DEPTH)}`);
|
|
87
|
-
if (typeof value === "string") {
|
|
88
|
-
out.push(`${pad}<string>${escapeText(value, path)}</string>`);
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
if (typeof value === "boolean") {
|
|
92
|
-
out.push(`${pad}<${value ? "true" : "false"}/>`);
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
if (typeof value === "number") {
|
|
96
|
-
if (!Number.isSafeInteger(value)) {
|
|
97
|
-
throw new PlistError(path, `${String(value)} is not a safe integer (no <real> support)`);
|
|
98
|
-
}
|
|
99
|
-
out.push(`${pad}<integer>${String(value === 0 ? 0 : value)}</integer>`);
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
if (Array.isArray(value)) {
|
|
103
|
-
if (value.length === 0) {
|
|
104
|
-
out.push(`${pad}<array/>`);
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
out.push(`${pad}<array>`);
|
|
108
|
-
for (const [index, item] of value.entries()) {
|
|
109
|
-
renderValue(item, `${path}[${String(index)}]`, depth + 1, out);
|
|
110
|
-
}
|
|
111
|
-
out.push(`${pad}</array>`);
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
if (typeof value === "object" && value !== null && isPlainObject(value)) {
|
|
115
|
-
renderDict(value, path, depth, out);
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
const kind = value === null ? "null" : typeof value;
|
|
119
|
-
throw new PlistError(path, `${kind} has no plist form — omit the key instead`);
|
|
120
|
-
};
|
|
121
|
-
var renderDict = (dict, path, depth, out) => {
|
|
122
|
-
const pad = "\t".repeat(depth);
|
|
123
|
-
const keys = Object.keys(dict).sort();
|
|
124
|
-
if (keys.length === 0) {
|
|
125
|
-
out.push(`${pad}<dict/>`);
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
out.push(`${pad}<dict>`);
|
|
129
|
-
for (const key of keys) {
|
|
130
|
-
const child = path === "" ? key : `${path}.${key}`;
|
|
131
|
-
out.push(`${pad} <key>${escapeText(key, child)}</key>`);
|
|
132
|
-
renderValue(dict[key], child, depth + 1, out);
|
|
133
|
-
}
|
|
134
|
-
out.push(`${pad}</dict>`);
|
|
135
|
-
};
|
|
136
|
-
var renderPlist = (dict) => {
|
|
137
|
-
if (typeof dict !== "object" || dict === null || Array.isArray(dict) || !isPlainObject(dict)) {
|
|
138
|
-
throw new PlistError("", "the top level of a launchd plist must be a dict");
|
|
139
|
-
}
|
|
140
|
-
const out = [];
|
|
141
|
-
renderDict(dict, "", 0, out);
|
|
142
|
-
return `${HEADER}${out.join(`
|
|
143
|
-
`)}
|
|
144
|
-
</plist>
|
|
145
|
-
`;
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
// src/launchd/job-form.ts
|
|
149
|
-
var parseDomain = (domain) => {
|
|
150
|
-
if (domain === "system")
|
|
151
|
-
return { kind: "system" };
|
|
152
|
-
const match = /^gui\/(\d+)$/.exec(domain);
|
|
153
|
-
return match?.[1] === undefined ? undefined : { kind: "gui", uid: Number(match[1]) };
|
|
154
|
-
};
|
|
155
|
-
var serviceTarget = (label, domain) => `${domain}/${label}`;
|
|
156
|
-
var plistPathFor = (label, domain, home) => {
|
|
157
|
-
if (domain.kind === "system")
|
|
158
|
-
return `/Library/LaunchDaemons/${label}.plist`;
|
|
159
|
-
if (home === undefined || !home.startsWith("/")) {
|
|
160
|
-
throw new Error(`gui/${String(domain.uid)}: no home directory for that uid on this host`);
|
|
161
|
-
}
|
|
162
|
-
return `${home}/Library/LaunchAgents/${label}.plist`;
|
|
163
|
-
};
|
|
164
|
-
var OWNED_KEYS = [
|
|
165
|
-
"EnvironmentVariables",
|
|
166
|
-
"GroupName",
|
|
167
|
-
"KeepAlive",
|
|
168
|
-
"Label",
|
|
169
|
-
"Program",
|
|
170
|
-
"ProgramArguments",
|
|
171
|
-
"RunAtLoad",
|
|
172
|
-
"StandardErrorPath",
|
|
173
|
-
"StandardOutPath",
|
|
174
|
-
"StartCalendarInterval",
|
|
175
|
-
"StartInterval",
|
|
176
|
-
"ThrottleInterval",
|
|
177
|
-
"UserName",
|
|
178
|
-
"WorkingDirectory"
|
|
179
|
-
];
|
|
180
|
-
var calendarDict = (entry) => {
|
|
181
|
-
const dict = {};
|
|
182
|
-
if (entry.minute !== undefined)
|
|
183
|
-
dict["Minute"] = entry.minute;
|
|
184
|
-
if (entry.hour !== undefined)
|
|
185
|
-
dict["Hour"] = entry.hour;
|
|
186
|
-
if (entry.day !== undefined)
|
|
187
|
-
dict["Day"] = entry.day;
|
|
188
|
-
if (entry.weekday !== undefined)
|
|
189
|
-
dict["Weekday"] = entry.weekday;
|
|
190
|
-
if (entry.month !== undefined)
|
|
191
|
-
dict["Month"] = entry.month;
|
|
192
|
-
return dict;
|
|
193
|
-
};
|
|
194
|
-
var keepAliveValue = (keepAlive) => {
|
|
195
|
-
if (typeof keepAlive === "boolean")
|
|
196
|
-
return keepAlive;
|
|
197
|
-
const dict = {};
|
|
198
|
-
if (keepAlive.successfulExit !== undefined)
|
|
199
|
-
dict["SuccessfulExit"] = keepAlive.successfulExit;
|
|
200
|
-
if (keepAlive.crashed !== undefined)
|
|
201
|
-
dict["Crashed"] = keepAlive.crashed;
|
|
202
|
-
return dict;
|
|
203
|
-
};
|
|
204
|
-
var jobPlistDict = (props) => {
|
|
205
|
-
const dict = { ...props.extraKeys };
|
|
206
|
-
dict["Label"] = props.label;
|
|
207
|
-
dict["ProgramArguments"] = [...props.programArguments];
|
|
208
|
-
const env = props.environment ?? {};
|
|
209
|
-
if (Object.keys(env).length > 0)
|
|
210
|
-
dict["EnvironmentVariables"] = { ...env };
|
|
211
|
-
if (props.runAtLoad !== undefined)
|
|
212
|
-
dict["RunAtLoad"] = props.runAtLoad;
|
|
213
|
-
if (props.keepAlive !== undefined)
|
|
214
|
-
dict["KeepAlive"] = keepAliveValue(props.keepAlive);
|
|
215
|
-
if (props.startInterval !== undefined)
|
|
216
|
-
dict["StartInterval"] = props.startInterval;
|
|
217
|
-
const calendar = props.startCalendarInterval;
|
|
218
|
-
if (calendar !== undefined) {
|
|
219
|
-
dict["StartCalendarInterval"] = Array.isArray(calendar) ? calendar.map(calendarDict) : calendarDict(calendar);
|
|
220
|
-
}
|
|
221
|
-
if (props.throttleInterval !== undefined)
|
|
222
|
-
dict["ThrottleInterval"] = props.throttleInterval;
|
|
223
|
-
if (props.standardOutPath !== undefined)
|
|
224
|
-
dict["StandardOutPath"] = props.standardOutPath;
|
|
225
|
-
if (props.standardErrorPath !== undefined)
|
|
226
|
-
dict["StandardErrorPath"] = props.standardErrorPath;
|
|
227
|
-
if (props.workingDirectory !== undefined)
|
|
228
|
-
dict["WorkingDirectory"] = props.workingDirectory;
|
|
229
|
-
if (props.userName !== undefined)
|
|
230
|
-
dict["UserName"] = props.userName;
|
|
231
|
-
if (props.groupName !== undefined)
|
|
232
|
-
dict["GroupName"] = props.groupName;
|
|
233
|
-
return dict;
|
|
234
|
-
};
|
|
235
|
-
var sha256Hex = (bytes) => createHash("sha256").update(bytes).digest("hex");
|
|
236
|
-
var renderJob = (props) => {
|
|
237
|
-
const text = renderPlist(jobPlistDict(props));
|
|
238
|
-
const bytes = new TextEncoder().encode(text);
|
|
239
|
-
return { bytes, sha256: sha256Hex(bytes), text };
|
|
240
|
-
};
|
|
241
|
-
|
|
242
|
-
// src/caddy/digest.ts
|
|
243
|
-
var sorted = (value) => {
|
|
244
|
-
if (Array.isArray(value))
|
|
245
|
-
return value.map(sorted);
|
|
246
|
-
if (typeof value === "object" && value !== null) {
|
|
247
|
-
const object = value;
|
|
248
|
-
const out = {};
|
|
249
|
-
for (const key of Object.keys(object).sort())
|
|
250
|
-
out[key] = sorted(object[key] ?? null);
|
|
251
|
-
return out;
|
|
252
|
-
}
|
|
253
|
-
return value;
|
|
254
|
-
};
|
|
255
|
-
var parseConfig = (text) => JSON.parse(text);
|
|
256
|
-
var canonicalConfig = (config) => JSON.stringify(sorted(config ?? null));
|
|
257
|
-
var configDigest = (config) => sha256Hex(canonicalConfig(config));
|
|
258
|
-
|
|
259
|
-
// src/caddy/admin-calls.ts
|
|
260
|
-
class CaddyAdminError extends Error {
|
|
261
|
-
status;
|
|
262
|
-
method;
|
|
263
|
-
path;
|
|
264
|
-
constructor(status, method, path, reason) {
|
|
265
|
-
super(`Caddy ${method} ${path} → ${String(status)}: ${reason}`);
|
|
266
|
-
this.status = status;
|
|
267
|
-
this.method = method;
|
|
268
|
-
this.path = path;
|
|
269
|
-
this.name = "CaddyAdminError";
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
var CADDYFILE = "text/caddyfile";
|
|
273
|
-
var ERROR_OPEN = '{"error":';
|
|
274
|
-
var describe = (warning) => `${warning.file ?? "Caddyfile"}:${String(warning.line ?? 0)}: ${warning.message ?? "(no message)"}`;
|
|
275
|
-
var errorIn = (body) => {
|
|
276
|
-
const at = body.lastIndexOf(ERROR_OPEN);
|
|
277
|
-
if (at === -1)
|
|
278
|
-
return;
|
|
279
|
-
try {
|
|
280
|
-
const parsed = JSON.parse(body.slice(at));
|
|
281
|
-
return typeof parsed.error === "string" ? parsed.error : undefined;
|
|
282
|
-
} catch {
|
|
283
|
-
return;
|
|
284
|
-
}
|
|
285
|
-
};
|
|
286
|
-
var warningsIn = (text) => {
|
|
287
|
-
if (text.trim() === "")
|
|
288
|
-
return [];
|
|
289
|
-
try {
|
|
290
|
-
const parsed = JSON.parse(text);
|
|
291
|
-
return Array.isArray(parsed) ? parsed.map(describe) : [];
|
|
292
|
-
} catch {
|
|
293
|
-
return [];
|
|
48
|
+
var caddyAdminLayer = (transport) => Layer.mergeAll(Layer.succeed(CaddyAdminService, {
|
|
49
|
+
endpoint: transport.endpoint,
|
|
50
|
+
listener: transport.listener
|
|
51
|
+
}), transport.layer);
|
|
52
|
+
// src/caddy/caddy-http-client.ts
|
|
53
|
+
import * as http from "node:http";
|
|
54
|
+
import { Readable } from "node:stream";
|
|
55
|
+
import * as Duration from "effect/Duration";
|
|
56
|
+
import * as Effect from "effect/Effect";
|
|
57
|
+
import * as Layer2 from "effect/Layer";
|
|
58
|
+
import * as Result from "effect/Result";
|
|
59
|
+
import * as HttpClient from "effect/unstable/http/HttpClient";
|
|
60
|
+
import * as HttpClientError from "effect/unstable/http/HttpClientError";
|
|
61
|
+
import * as HttpClientResponse from "effect/unstable/http/HttpClientResponse";
|
|
62
|
+
var NEVER_CONNECTED = new Set(["ECONNREFUSED", "ENOENT"]);
|
|
63
|
+
var codeOf = (cause) => typeof cause === "object" && cause !== null && ("code" in cause) ? String(cause.code) : undefined;
|
|
64
|
+
var isUnreachable = (error) => error instanceof HttpClientError.HttpClientError && error.reason._tag === "TransportError" && NEVER_CONNECTED.has(codeOf(error.reason.cause) ?? "");
|
|
65
|
+
var transportFailure = (request2, cause) => new HttpClientError.HttpClientError({
|
|
66
|
+
reason: new HttpClientError.TransportError({ request: request2, cause })
|
|
67
|
+
});
|
|
68
|
+
var flattenHeaders = (headers) => {
|
|
69
|
+
const flat = {};
|
|
70
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
71
|
+
if (value !== undefined)
|
|
72
|
+
flat[key] = Array.isArray(value) ? value.join(", ") : value;
|
|
294
73
|
}
|
|
74
|
+
return flat;
|
|
295
75
|
};
|
|
296
|
-
var
|
|
297
|
-
const
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
76
|
+
var waitForResponse = (nodeRequest, request2) => Effect.callback((resume) => {
|
|
77
|
+
const onError2 = (cause) => {
|
|
78
|
+
resume(Effect.fail(transportFailure(request2, cause)));
|
|
79
|
+
};
|
|
80
|
+
nodeRequest.on("error", onError2);
|
|
81
|
+
const onResponse = (response) => {
|
|
82
|
+
nodeRequest.off("error", onError2);
|
|
83
|
+
resume(Effect.succeed(response));
|
|
84
|
+
};
|
|
85
|
+
nodeRequest.on("response", onResponse);
|
|
86
|
+
return Effect.sync(() => {
|
|
87
|
+
nodeRequest.off("error", onError2);
|
|
88
|
+
nodeRequest.off("response", onResponse);
|
|
307
89
|
});
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
const
|
|
311
|
-
|
|
312
|
-
};
|
|
313
|
-
|
|
314
|
-
const
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
headers: {
|
|
323
|
-
"Content-Type": CADDYFILE,
|
|
324
|
-
...sourceFile === undefined ? {} : { "Caddy-Config-Source-Adapter": "caddyfile", "Caddy-Config-Source-File": sourceFile }
|
|
325
|
-
},
|
|
326
|
-
method: "POST",
|
|
327
|
-
path: "/load"
|
|
90
|
+
});
|
|
91
|
+
var waitForFinish = (nodeRequest, request2) => Effect.callback((resume) => {
|
|
92
|
+
const onError2 = (cause) => {
|
|
93
|
+
resume(Effect.fail(transportFailure(request2, cause)));
|
|
94
|
+
};
|
|
95
|
+
nodeRequest.once("error", onError2);
|
|
96
|
+
const onFinish = () => {
|
|
97
|
+
nodeRequest.off("error", onError2);
|
|
98
|
+
resume(Effect.void);
|
|
99
|
+
};
|
|
100
|
+
nodeRequest.once("finish", onFinish);
|
|
101
|
+
return Effect.sync(() => {
|
|
102
|
+
nodeRequest.off("error", onError2);
|
|
103
|
+
nodeRequest.off("finish", onFinish);
|
|
328
104
|
});
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
105
|
+
});
|
|
106
|
+
var sendBody = (nodeRequest, request2) => Effect.suspend(() => {
|
|
107
|
+
const body = request2.body;
|
|
108
|
+
if (body._tag === "Empty") {
|
|
109
|
+
nodeRequest.end();
|
|
110
|
+
return waitForFinish(nodeRequest, request2);
|
|
111
|
+
}
|
|
112
|
+
if (body._tag === "Uint8Array") {
|
|
113
|
+
nodeRequest.setHeader("content-length", body.contentLength);
|
|
114
|
+
nodeRequest.end(body.body);
|
|
115
|
+
return waitForFinish(nodeRequest, request2);
|
|
116
|
+
}
|
|
117
|
+
return Effect.fail(new HttpClientError.HttpClientError({
|
|
118
|
+
reason: new HttpClientError.EncodeError({
|
|
119
|
+
request: request2,
|
|
120
|
+
description: `Caddy admin transport: unsupported request body "${body._tag}" — every Caddy operation sends Empty or a Uint8Array body`
|
|
121
|
+
})
|
|
122
|
+
}));
|
|
123
|
+
});
|
|
124
|
+
var toWebResponse = (source) => new Response(Readable.toWeb(source), {
|
|
125
|
+
headers: flattenHeaders(source.headers),
|
|
126
|
+
status: source.statusCode ?? 0
|
|
127
|
+
});
|
|
128
|
+
var exchangeOnce = (target, request2, url, signal, timeoutMs) => Effect.suspend(() => {
|
|
129
|
+
const nodeRequest = http.request({
|
|
130
|
+
headers: request2.headers,
|
|
131
|
+
method: request2.method,
|
|
132
|
+
path: `${url.pathname}${url.search}`,
|
|
133
|
+
signal,
|
|
134
|
+
timeout: timeoutMs,
|
|
135
|
+
...target.kind === "unix" ? { socketPath: target.socketPath } : { host: target.host, port: target.port }
|
|
136
|
+
});
|
|
137
|
+
nodeRequest.on("timeout", () => {
|
|
138
|
+
nodeRequest.destroy(new Error(`no answer in ${String(timeoutMs)} ms`));
|
|
139
|
+
});
|
|
140
|
+
return Effect.raceFirst(waitForResponse(nodeRequest, request2), sendBody(nodeRequest, request2).pipe(Effect.andThen(Effect.never))).pipe(Effect.onError(() => Effect.sync(() => nodeRequest.destroy())), Effect.map((source) => HttpClientResponse.fromWeb(request2, toWebResponse(source))));
|
|
141
|
+
});
|
|
142
|
+
var withUnreachableRetry = (attempt, retries, retryDelayMs) => Effect.gen(function* () {
|
|
143
|
+
let attemptsLeft = retries;
|
|
144
|
+
for (;; ) {
|
|
145
|
+
const outcome = yield* Effect.result(attempt);
|
|
146
|
+
if (Result.isSuccess(outcome))
|
|
147
|
+
return outcome.success;
|
|
148
|
+
if (attemptsLeft <= 0 || !isUnreachable(outcome.failure)) {
|
|
149
|
+
return yield* Effect.fail(outcome.failure);
|
|
150
|
+
}
|
|
151
|
+
attemptsLeft -= 1;
|
|
152
|
+
yield* Effect.sleep(Duration.millis(retryDelayMs));
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
var makeLocalHttpClient = (target, options = {}) => {
|
|
156
|
+
const timeoutMs = options.timeoutMs ?? 60000;
|
|
157
|
+
const retries = options.retries ?? 2;
|
|
158
|
+
const retryDelayMs = options.retryDelayMs ?? 500;
|
|
159
|
+
return Layer2.succeed(HttpClient.HttpClient, HttpClient.make((request2, url, signal) => {
|
|
160
|
+
const attempt = exchangeOnce(target, request2, url, signal, timeoutMs);
|
|
161
|
+
return retries <= 0 ? attempt : withUnreachableRetry(attempt, retries, retryDelayMs);
|
|
162
|
+
}));
|
|
333
163
|
};
|
|
334
164
|
// src/caddy/config.ts
|
|
335
165
|
import { Resource } from "alchemy";
|
|
336
166
|
import { Unowned as Unowned2 } from "alchemy/AdoptPolicy";
|
|
337
167
|
import { isResolved } from "alchemy/Diff";
|
|
338
168
|
import * as Provider from "alchemy/Provider";
|
|
339
|
-
import * as
|
|
169
|
+
import * as Effect11 from "effect/Effect";
|
|
340
170
|
|
|
341
171
|
// src/launchd/host-effect.ts
|
|
342
|
-
import * as
|
|
343
|
-
var lift = (step) =>
|
|
172
|
+
import * as Effect2 from "effect/Effect";
|
|
173
|
+
var lift = (step) => Effect2.tryPromise({
|
|
344
174
|
catch: (cause) => cause instanceof Error ? cause : new Error(String(cause)),
|
|
345
175
|
try: step
|
|
346
176
|
});
|
|
@@ -355,32 +185,32 @@ var resolvedString = (news, key) => {
|
|
|
355
185
|
import { AdoptPolicy } from "alchemy/AdoptPolicy";
|
|
356
186
|
import { AlchemyContext } from "alchemy/AlchemyContext";
|
|
357
187
|
import { Stack as Stack3 } from "alchemy/Stack";
|
|
358
|
-
import * as
|
|
188
|
+
import * as Effect6 from "effect/Effect";
|
|
359
189
|
import * as Option4 from "effect/Option";
|
|
360
190
|
|
|
361
191
|
// src/ownership/resume.ts
|
|
362
192
|
import { Unowned } from "alchemy/AdoptPolicy";
|
|
363
193
|
import { Artifacts } from "alchemy/Artifacts";
|
|
364
|
-
import * as
|
|
194
|
+
import * as Effect3 from "effect/Effect";
|
|
365
195
|
import * as Option from "effect/Option";
|
|
366
196
|
var PROVEN = "homeflare/ownership/resumes";
|
|
367
197
|
var UNFINISHED = "homeflare/ownership/unfinished";
|
|
368
|
-
var note = (key, instanceId) =>
|
|
369
|
-
const bag = yield*
|
|
198
|
+
var note = (key, instanceId) => Effect3.gen(function* () {
|
|
199
|
+
const bag = yield* Effect3.serviceOption(Artifacts);
|
|
370
200
|
if (Option.isSome(bag))
|
|
371
201
|
yield* bag.value.set(key, instanceId);
|
|
372
202
|
});
|
|
373
|
-
var noted = (key, instanceId) =>
|
|
374
|
-
const bag = yield*
|
|
203
|
+
var noted = (key, instanceId) => Effect3.gen(function* () {
|
|
204
|
+
const bag = yield* Effect3.serviceOption(Artifacts);
|
|
375
205
|
if (Option.isNone(bag))
|
|
376
206
|
return false;
|
|
377
207
|
return (yield* bag.value.get(key)) === instanceId;
|
|
378
208
|
});
|
|
379
|
-
var noteUnfinished = (instanceId) =>
|
|
380
|
-
var noteResume = (instanceId, recovered) =>
|
|
209
|
+
var noteUnfinished = (instanceId) => Effect3.as(note(UNFINISHED, instanceId), undefined);
|
|
210
|
+
var noteResume = (instanceId, recovered) => Effect3.gen(function* () {
|
|
381
211
|
yield* noteUnfinished(instanceId);
|
|
382
|
-
const unproven = () =>
|
|
383
|
-
const found = yield* recovered.pipe(
|
|
212
|
+
const unproven = () => Effect3.succeed(undefined);
|
|
213
|
+
const found = yield* recovered.pipe(Effect3.catch(unproven), Effect3.catchDefect(unproven));
|
|
384
214
|
if (found === undefined || found === null || Unowned.is(found))
|
|
385
215
|
return;
|
|
386
216
|
yield* note(PROVEN, instanceId);
|
|
@@ -391,7 +221,7 @@ var provingResumes = (service) => {
|
|
|
391
221
|
const { diff, read } = service;
|
|
392
222
|
if (diff === undefined || read === undefined)
|
|
393
223
|
return service;
|
|
394
|
-
const proving = (input) =>
|
|
224
|
+
const proving = (input) => Effect3.gen(function* () {
|
|
395
225
|
if (input.output === undefined) {
|
|
396
226
|
yield* noteResume(input.instanceId, read({ ...input, output: undefined }));
|
|
397
227
|
}
|
|
@@ -403,15 +233,15 @@ var provingResumes = (service) => {
|
|
|
403
233
|
// src/ownership/rows.ts
|
|
404
234
|
import { Stack as Stack2 } from "alchemy/Stack";
|
|
405
235
|
import { State, isActionState } from "alchemy/State/State";
|
|
406
|
-
import * as
|
|
236
|
+
import * as Effect5 from "effect/Effect";
|
|
407
237
|
import * as Option3 from "effect/Option";
|
|
408
238
|
|
|
409
239
|
// src/ownership/whole.ts
|
|
410
240
|
import { Stack } from "alchemy/Stack";
|
|
411
|
-
import * as
|
|
241
|
+
import * as Effect4 from "effect/Effect";
|
|
412
242
|
import * as Option2 from "effect/Option";
|
|
413
243
|
var absent = (value) => value === undefined || value === null;
|
|
414
|
-
var
|
|
244
|
+
var isPlainObject = (value) => {
|
|
415
245
|
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
416
246
|
return false;
|
|
417
247
|
const proto = Object.getPrototypeOf(value);
|
|
@@ -427,29 +257,29 @@ var carries = (declared, recorded) => {
|
|
|
427
257
|
return false;
|
|
428
258
|
return declared.every((each, index) => carries(each, recorded[index]));
|
|
429
259
|
}
|
|
430
|
-
if (
|
|
431
|
-
if (!
|
|
260
|
+
if (isPlainObject(declared)) {
|
|
261
|
+
if (!isPlainObject(recorded))
|
|
432
262
|
return false;
|
|
433
263
|
return Object.entries(declared).every(([key, each]) => carries(each, recorded[key]));
|
|
434
264
|
}
|
|
435
265
|
return true;
|
|
436
266
|
};
|
|
437
|
-
var wholeDeclaration = (fqn, props) =>
|
|
267
|
+
var wholeDeclaration = (fqn, props) => Effect4.map(Effect4.serviceOption(Stack), (stack) => {
|
|
438
268
|
const declared = Option2.isSome(stack) ? stack.value.resources[fqn] : undefined;
|
|
439
269
|
return declared !== undefined && carries(declared.Props, props);
|
|
440
270
|
});
|
|
441
271
|
|
|
442
272
|
// src/ownership/rows.ts
|
|
443
|
-
var storeOf =
|
|
444
|
-
const stack = yield*
|
|
445
|
-
const state = yield*
|
|
273
|
+
var storeOf = Effect5.gen(function* () {
|
|
274
|
+
const stack = yield* Effect5.serviceOption(Stack2);
|
|
275
|
+
const state = yield* Effect5.serviceOption(State);
|
|
446
276
|
if (Option3.isNone(stack) || Option3.isNone(state))
|
|
447
277
|
return;
|
|
448
278
|
const store = yield* state.value;
|
|
449
279
|
const where = { stack: stack.value.name, stage: stack.value.stage };
|
|
450
280
|
return { resources: stack.value.resources, store, where };
|
|
451
281
|
});
|
|
452
|
-
var rowAt = (store, where, fqn) => store.get({ ...where, fqn }).pipe(
|
|
282
|
+
var rowAt = (store, where, fqn) => store.get({ ...where, fqn }).pipe(Effect5.orElseSucceed(() => {
|
|
453
283
|
return;
|
|
454
284
|
}));
|
|
455
285
|
var generationOf = (row, instanceId) => {
|
|
@@ -462,7 +292,7 @@ var generationOf = (row, instanceId) => {
|
|
|
462
292
|
}
|
|
463
293
|
return;
|
|
464
294
|
};
|
|
465
|
-
var recordedGeneration = (fqn, instanceId) =>
|
|
295
|
+
var recordedGeneration = (fqn, instanceId) => Effect5.gen(function* () {
|
|
466
296
|
const found = yield* storeOf;
|
|
467
297
|
if (found === undefined)
|
|
468
298
|
return;
|
|
@@ -473,14 +303,14 @@ var recordedGeneration = (fqn, instanceId) => Effect4.gen(function* () {
|
|
|
473
303
|
}
|
|
474
304
|
return;
|
|
475
305
|
});
|
|
476
|
-
var recordedInstance = (fqn, instanceId) =>
|
|
306
|
+
var recordedInstance = (fqn, instanceId) => Effect5.gen(function* () {
|
|
477
307
|
const generation = yield* recordedGeneration(fqn, instanceId);
|
|
478
308
|
if (generation === undefined)
|
|
479
309
|
return "absent";
|
|
480
310
|
return (yield* wholeDeclaration(fqn, generation.props)) ? "whole" : "partial";
|
|
481
311
|
});
|
|
482
|
-
var isCreate = (fqn, instanceId) =>
|
|
483
|
-
var forgetRefusedCreate = (fqn, instanceId) =>
|
|
312
|
+
var isCreate = (fqn, instanceId) => Effect5.map(recordedGeneration(fqn, instanceId), (generation) => generation?.status === "creating");
|
|
313
|
+
var forgetRefusedCreate = (fqn, instanceId) => Effect5.gen(function* () {
|
|
484
314
|
const found = yield* storeOf;
|
|
485
315
|
if (found === undefined)
|
|
486
316
|
return;
|
|
@@ -489,32 +319,32 @@ var forgetRefusedCreate = (fqn, instanceId) => Effect4.gen(function* () {
|
|
|
489
319
|
if (mine?.status !== "creating" || mine.instanceId !== instanceId || mine.attr !== undefined) {
|
|
490
320
|
return;
|
|
491
321
|
}
|
|
492
|
-
yield* found.store.delete({ ...found.where, fqn }).pipe(
|
|
322
|
+
yield* found.store.delete({ ...found.where, fqn }).pipe(Effect5.ignore);
|
|
493
323
|
});
|
|
494
324
|
|
|
495
325
|
// src/ownership/adopt.ts
|
|
496
|
-
var adoptEnabled = (fqn) =>
|
|
497
|
-
const stack = yield*
|
|
326
|
+
var adoptEnabled = (fqn) => Effect6.gen(function* () {
|
|
327
|
+
const stack = yield* Effect6.serviceOption(Stack3);
|
|
498
328
|
const scoped = Option4.isSome(stack) ? stack.value.resources[fqn]?.Adopt : undefined;
|
|
499
329
|
if (scoped !== undefined)
|
|
500
330
|
return scoped;
|
|
501
|
-
const policy = yield*
|
|
331
|
+
const policy = yield* Effect6.serviceOption(AdoptPolicy);
|
|
502
332
|
if (Option4.isSome(policy))
|
|
503
333
|
return policy.value;
|
|
504
|
-
const context = yield*
|
|
334
|
+
const context = yield* Effect6.serviceOption(AlchemyContext);
|
|
505
335
|
return Option4.isSome(context) ? context.value.adopt : false;
|
|
506
336
|
});
|
|
507
|
-
var adoptable = (owner) =>
|
|
337
|
+
var adoptable = (owner) => Effect6.gen(function* () {
|
|
508
338
|
if (yield* isCreate(owner.fqn, owner.instanceId))
|
|
509
339
|
return true;
|
|
510
340
|
return yield* unfinished(owner.instanceId);
|
|
511
341
|
});
|
|
512
|
-
var adoptsAtApply = (owner) =>
|
|
342
|
+
var adoptsAtApply = (owner) => Effect6.gen(function* () {
|
|
513
343
|
if (!(yield* adoptEnabled(owner.fqn)))
|
|
514
344
|
return false;
|
|
515
345
|
return yield* adoptable(owner);
|
|
516
346
|
});
|
|
517
|
-
var refuseTakeover = (owner, what) =>
|
|
347
|
+
var refuseTakeover = (owner, what) => Effect6.gen(function* () {
|
|
518
348
|
if (owner.output !== undefined)
|
|
519
349
|
return;
|
|
520
350
|
if (yield* resumes(owner.instanceId))
|
|
@@ -524,10 +354,30 @@ var refuseTakeover = (owner, what) => Effect5.gen(function* () {
|
|
|
524
354
|
return;
|
|
525
355
|
yield* forgetRefusedCreate(owner.fqn, owner.instanceId);
|
|
526
356
|
const remedy = may ? "Deploy with --adopt (or wrap the resource in adopt(true)) to take it over, or remove it first." : "It is the new identity of a replace, which --adopt does not cover: remove it first.";
|
|
527
|
-
return yield*
|
|
357
|
+
return yield* Effect6.die(new Error(`${what}: already exists, and this stack holds no state for it — the plan could not ask ` + "first (a prop was still an Output, a replace moved onto it, or it appeared after the " + `plan). Nothing was written. ${remedy}`));
|
|
528
358
|
});
|
|
529
359
|
var claimFor = (owner) => (what) => refuseTakeover(owner, what);
|
|
530
360
|
|
|
361
|
+
// src/caddy/config-lifecycle.ts
|
|
362
|
+
import * as Effect9 from "effect/Effect";
|
|
363
|
+
import * as Result2 from "effect/Result";
|
|
364
|
+
|
|
365
|
+
// src/caddy/admin-calls.ts
|
|
366
|
+
import * as Effect7 from "effect/Effect";
|
|
367
|
+
import * as Caddy from "@distilled.cloud/caddy";
|
|
368
|
+
var CADDYFILE = "text/caddyfile";
|
|
369
|
+
var describe = (warning) => `${warning.file ?? "Caddyfile"}:${String(warning.line ?? 0)}: ${warning.message ?? "(no message)"}`;
|
|
370
|
+
var adaptCaddyfile = (caddyfile) => Effect7.map(Caddy.Services.admin.adaptConfig({ config: caddyfile, contentType: CADDYFILE }), (response) => ({
|
|
371
|
+
config: response.result ?? null,
|
|
372
|
+
warnings: (response.warnings ?? []).map(describe)
|
|
373
|
+
}));
|
|
374
|
+
var readRunningConfig = () => Effect7.map(Caddy.Services.admin.getConfig({ path: "" }), (response) => response.value ?? null);
|
|
375
|
+
var loadCaddyfile = (caddyfile, sourceFile) => Effect7.map(Caddy.Services.admin.loadConfig({
|
|
376
|
+
config: caddyfile,
|
|
377
|
+
contentType: CADDYFILE,
|
|
378
|
+
...sourceFile === undefined ? {} : { sourceAdapter: "caddyfile", sourceFile }
|
|
379
|
+
}), (response) => (response.warnings ?? []).map(describe));
|
|
380
|
+
|
|
531
381
|
// src/caddy/admin-guard.ts
|
|
532
382
|
var DEFAULT_ADMIN_PORT = 2019;
|
|
533
383
|
var LOOPBACK_HOST = /^(?:localhost|127(?:\.\d{1,3}){3}|\[?::1\]?)$/;
|
|
@@ -606,6 +456,14 @@ var adminProblems = (adapted, transport) => {
|
|
|
606
456
|
return found;
|
|
607
457
|
};
|
|
608
458
|
|
|
459
|
+
// src/caddy/config-errors.ts
|
|
460
|
+
import * as Effect8 from "effect/Effect";
|
|
461
|
+
var refuse = (endpoint, message) => new Error(`Caddy.Config at ${endpoint}: ${message}`);
|
|
462
|
+
var short = (digest) => digest.slice(0, 12);
|
|
463
|
+
var messageOf = (error) => ("message" in error) && typeof error.message === "string" ? error.message : String(error);
|
|
464
|
+
var operationFailure = (endpoint, op, error) => error._tag === "LoadRefused" ? refuse(endpoint, `Caddy refused the Caddyfile (${error._tag}, a 200 whose body carried the error, not a ` + `failure status) — ${error.message}`) : refuse(endpoint, `${op} failed (${error._tag}) — ${messageOf(error)}`);
|
|
465
|
+
var wrapOperation = (endpoint, op) => (effect) => effect.pipe(Effect8.mapError((error) => isUnreachable(error) ? error : operationFailure(endpoint, op, error)));
|
|
466
|
+
|
|
609
467
|
// src/launchd/secret-tripwire.ts
|
|
610
468
|
var ALTERNATIVE = "props are stored unencrypted in Alchemy state. Have a secret renderer (e.g. openbao-agent) " + "write it to a root-owned file and declare the file path instead (FOO_FILE=/path, --foo-file /path).";
|
|
611
469
|
var SECRET_NAME = /(?:^|_)(?:TOKEN|SECRET|PASSWORD|PASSWD|PASSPHRASE|API_?KEY|ACCESS_KEY|PRIVATE_KEY|SECRET_KEY|CREDENTIALS?)$/i;
|
|
@@ -743,139 +601,356 @@ var configProblems = (props) => {
|
|
|
743
601
|
return found;
|
|
744
602
|
};
|
|
745
603
|
|
|
604
|
+
// src/launchd/job-form.ts
|
|
605
|
+
import { createHash } from "node:crypto";
|
|
606
|
+
|
|
607
|
+
// src/launchd/plist.ts
|
|
608
|
+
class PlistError extends Error {
|
|
609
|
+
path;
|
|
610
|
+
constructor(path, detail) {
|
|
611
|
+
super(`plist ${path === "" ? "(root)" : path}: ${detail}`);
|
|
612
|
+
this.path = path;
|
|
613
|
+
this.name = "PlistError";
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
var HEADER = `<?xml version="1.0" encoding="UTF-8"?>
|
|
617
|
+
` + '<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" ' + `"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
618
|
+
` + `<plist version="1.0">
|
|
619
|
+
`;
|
|
620
|
+
var MAX_DEPTH = 32;
|
|
621
|
+
var FORBIDDEN = /[\x00-\x08\x0B\x0C\x0E-\x1F\uD800-\uDFFF\uFFFE\uFFFF]/u;
|
|
622
|
+
var escapeText = (text, path) => {
|
|
623
|
+
const bad = FORBIDDEN.exec(text);
|
|
624
|
+
if (bad !== null) {
|
|
625
|
+
const code = bad[0].codePointAt(0) ?? 0;
|
|
626
|
+
throw new PlistError(path, `U+${code.toString(16).toUpperCase().padStart(4, "0")} cannot appear in XML 1.0`);
|
|
627
|
+
}
|
|
628
|
+
return text.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll("\r", " ");
|
|
629
|
+
};
|
|
630
|
+
var isPlainObject2 = (value) => {
|
|
631
|
+
const proto = Object.getPrototypeOf(value);
|
|
632
|
+
return proto === Object.prototype || proto === null;
|
|
633
|
+
};
|
|
634
|
+
var renderValue = (value, path, depth, out) => {
|
|
635
|
+
const pad = "\t".repeat(depth);
|
|
636
|
+
if (depth > MAX_DEPTH)
|
|
637
|
+
throw new PlistError(path, `nested deeper than ${String(MAX_DEPTH)}`);
|
|
638
|
+
if (typeof value === "string") {
|
|
639
|
+
out.push(`${pad}<string>${escapeText(value, path)}</string>`);
|
|
640
|
+
return;
|
|
641
|
+
}
|
|
642
|
+
if (typeof value === "boolean") {
|
|
643
|
+
out.push(`${pad}<${value ? "true" : "false"}/>`);
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
if (typeof value === "number") {
|
|
647
|
+
if (!Number.isSafeInteger(value)) {
|
|
648
|
+
throw new PlistError(path, `${String(value)} is not a safe integer (no <real> support)`);
|
|
649
|
+
}
|
|
650
|
+
out.push(`${pad}<integer>${String(value === 0 ? 0 : value)}</integer>`);
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
653
|
+
if (Array.isArray(value)) {
|
|
654
|
+
if (value.length === 0) {
|
|
655
|
+
out.push(`${pad}<array/>`);
|
|
656
|
+
return;
|
|
657
|
+
}
|
|
658
|
+
out.push(`${pad}<array>`);
|
|
659
|
+
for (const [index, item] of value.entries()) {
|
|
660
|
+
renderValue(item, `${path}[${String(index)}]`, depth + 1, out);
|
|
661
|
+
}
|
|
662
|
+
out.push(`${pad}</array>`);
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
if (typeof value === "object" && value !== null && isPlainObject2(value)) {
|
|
666
|
+
renderDict(value, path, depth, out);
|
|
667
|
+
return;
|
|
668
|
+
}
|
|
669
|
+
const kind = value === null ? "null" : typeof value;
|
|
670
|
+
throw new PlistError(path, `${kind} has no plist form — omit the key instead`);
|
|
671
|
+
};
|
|
672
|
+
var renderDict = (dict, path, depth, out) => {
|
|
673
|
+
const pad = "\t".repeat(depth);
|
|
674
|
+
const keys = Object.keys(dict).sort();
|
|
675
|
+
if (keys.length === 0) {
|
|
676
|
+
out.push(`${pad}<dict/>`);
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
out.push(`${pad}<dict>`);
|
|
680
|
+
for (const key of keys) {
|
|
681
|
+
const child = path === "" ? key : `${path}.${key}`;
|
|
682
|
+
out.push(`${pad} <key>${escapeText(key, child)}</key>`);
|
|
683
|
+
renderValue(dict[key], child, depth + 1, out);
|
|
684
|
+
}
|
|
685
|
+
out.push(`${pad}</dict>`);
|
|
686
|
+
};
|
|
687
|
+
var renderPlist = (dict) => {
|
|
688
|
+
if (typeof dict !== "object" || dict === null || Array.isArray(dict) || !isPlainObject2(dict)) {
|
|
689
|
+
throw new PlistError("", "the top level of a launchd plist must be a dict");
|
|
690
|
+
}
|
|
691
|
+
const out = [];
|
|
692
|
+
renderDict(dict, "", 0, out);
|
|
693
|
+
return `${HEADER}${out.join(`
|
|
694
|
+
`)}
|
|
695
|
+
</plist>
|
|
696
|
+
`;
|
|
697
|
+
};
|
|
698
|
+
|
|
699
|
+
// src/launchd/job-form.ts
|
|
700
|
+
var parseDomain = (domain) => {
|
|
701
|
+
if (domain === "system")
|
|
702
|
+
return { kind: "system" };
|
|
703
|
+
const match = /^gui\/(\d+)$/.exec(domain);
|
|
704
|
+
return match?.[1] === undefined ? undefined : { kind: "gui", uid: Number(match[1]) };
|
|
705
|
+
};
|
|
706
|
+
var serviceTarget = (label, domain) => `${domain}/${label}`;
|
|
707
|
+
var plistPathFor = (label, domain, home) => {
|
|
708
|
+
if (domain.kind === "system")
|
|
709
|
+
return `/Library/LaunchDaemons/${label}.plist`;
|
|
710
|
+
if (home === undefined || !home.startsWith("/")) {
|
|
711
|
+
throw new Error(`gui/${String(domain.uid)}: no home directory for that uid on this host`);
|
|
712
|
+
}
|
|
713
|
+
return `${home}/Library/LaunchAgents/${label}.plist`;
|
|
714
|
+
};
|
|
715
|
+
var OWNED_KEYS = [
|
|
716
|
+
"EnvironmentVariables",
|
|
717
|
+
"GroupName",
|
|
718
|
+
"KeepAlive",
|
|
719
|
+
"Label",
|
|
720
|
+
"Program",
|
|
721
|
+
"ProgramArguments",
|
|
722
|
+
"RunAtLoad",
|
|
723
|
+
"StandardErrorPath",
|
|
724
|
+
"StandardOutPath",
|
|
725
|
+
"StartCalendarInterval",
|
|
726
|
+
"StartInterval",
|
|
727
|
+
"ThrottleInterval",
|
|
728
|
+
"UserName",
|
|
729
|
+
"WorkingDirectory"
|
|
730
|
+
];
|
|
731
|
+
var calendarDict = (entry) => {
|
|
732
|
+
const dict = {};
|
|
733
|
+
if (entry.minute !== undefined)
|
|
734
|
+
dict["Minute"] = entry.minute;
|
|
735
|
+
if (entry.hour !== undefined)
|
|
736
|
+
dict["Hour"] = entry.hour;
|
|
737
|
+
if (entry.day !== undefined)
|
|
738
|
+
dict["Day"] = entry.day;
|
|
739
|
+
if (entry.weekday !== undefined)
|
|
740
|
+
dict["Weekday"] = entry.weekday;
|
|
741
|
+
if (entry.month !== undefined)
|
|
742
|
+
dict["Month"] = entry.month;
|
|
743
|
+
return dict;
|
|
744
|
+
};
|
|
745
|
+
var keepAliveValue = (keepAlive) => {
|
|
746
|
+
if (typeof keepAlive === "boolean")
|
|
747
|
+
return keepAlive;
|
|
748
|
+
const dict = {};
|
|
749
|
+
if (keepAlive.successfulExit !== undefined)
|
|
750
|
+
dict["SuccessfulExit"] = keepAlive.successfulExit;
|
|
751
|
+
if (keepAlive.crashed !== undefined)
|
|
752
|
+
dict["Crashed"] = keepAlive.crashed;
|
|
753
|
+
return dict;
|
|
754
|
+
};
|
|
755
|
+
var jobPlistDict = (props) => {
|
|
756
|
+
const dict = { ...props.extraKeys };
|
|
757
|
+
dict["Label"] = props.label;
|
|
758
|
+
dict["ProgramArguments"] = [...props.programArguments];
|
|
759
|
+
const env = props.environment ?? {};
|
|
760
|
+
if (Object.keys(env).length > 0)
|
|
761
|
+
dict["EnvironmentVariables"] = { ...env };
|
|
762
|
+
if (props.runAtLoad !== undefined)
|
|
763
|
+
dict["RunAtLoad"] = props.runAtLoad;
|
|
764
|
+
if (props.keepAlive !== undefined)
|
|
765
|
+
dict["KeepAlive"] = keepAliveValue(props.keepAlive);
|
|
766
|
+
if (props.startInterval !== undefined)
|
|
767
|
+
dict["StartInterval"] = props.startInterval;
|
|
768
|
+
const calendar = props.startCalendarInterval;
|
|
769
|
+
if (calendar !== undefined) {
|
|
770
|
+
dict["StartCalendarInterval"] = Array.isArray(calendar) ? calendar.map(calendarDict) : calendarDict(calendar);
|
|
771
|
+
}
|
|
772
|
+
if (props.throttleInterval !== undefined)
|
|
773
|
+
dict["ThrottleInterval"] = props.throttleInterval;
|
|
774
|
+
if (props.standardOutPath !== undefined)
|
|
775
|
+
dict["StandardOutPath"] = props.standardOutPath;
|
|
776
|
+
if (props.standardErrorPath !== undefined)
|
|
777
|
+
dict["StandardErrorPath"] = props.standardErrorPath;
|
|
778
|
+
if (props.workingDirectory !== undefined)
|
|
779
|
+
dict["WorkingDirectory"] = props.workingDirectory;
|
|
780
|
+
if (props.userName !== undefined)
|
|
781
|
+
dict["UserName"] = props.userName;
|
|
782
|
+
if (props.groupName !== undefined)
|
|
783
|
+
dict["GroupName"] = props.groupName;
|
|
784
|
+
return dict;
|
|
785
|
+
};
|
|
786
|
+
var sha256Hex = (bytes) => createHash("sha256").update(bytes).digest("hex");
|
|
787
|
+
var renderJob = (props) => {
|
|
788
|
+
const text = renderPlist(jobPlistDict(props));
|
|
789
|
+
const bytes = new TextEncoder().encode(text);
|
|
790
|
+
return { bytes, sha256: sha256Hex(bytes), text };
|
|
791
|
+
};
|
|
792
|
+
|
|
793
|
+
// src/caddy/digest.ts
|
|
794
|
+
var sorted = (value) => {
|
|
795
|
+
if (Array.isArray(value))
|
|
796
|
+
return value.map(sorted);
|
|
797
|
+
if (typeof value === "object" && value !== null) {
|
|
798
|
+
const object = value;
|
|
799
|
+
const out = {};
|
|
800
|
+
for (const key of Object.keys(object).sort())
|
|
801
|
+
out[key] = sorted(object[key] ?? null);
|
|
802
|
+
return out;
|
|
803
|
+
}
|
|
804
|
+
return value;
|
|
805
|
+
};
|
|
806
|
+
var canonicalConfig = (config) => JSON.stringify(sorted(config ?? null));
|
|
807
|
+
var configDigest = (config) => sha256Hex(canonicalConfig(config));
|
|
808
|
+
|
|
746
809
|
// src/caddy/config-lifecycle.ts
|
|
747
|
-
var refuse = (admin, message) => new Error(`Caddy.Config at ${admin.endpoint}: ${message}`);
|
|
748
|
-
var short = (digest) => digest.slice(0, 12);
|
|
749
810
|
var servesNothing = (config) => {
|
|
750
811
|
const apps = typeof config === "object" && config !== null ? config.apps : undefined;
|
|
751
812
|
return typeof apps !== "object" || apps === null || Object.keys(apps).length === 0;
|
|
752
813
|
};
|
|
753
|
-
var desiredConfig =
|
|
814
|
+
var desiredConfig = (props) => Effect9.gen(function* () {
|
|
815
|
+
const { endpoint, listener } = yield* CaddyAdminService;
|
|
754
816
|
const found = configProblems(props);
|
|
755
817
|
if (found.length > 0)
|
|
756
|
-
|
|
757
|
-
const adapted =
|
|
818
|
+
return yield* Effect9.fail(refuse(endpoint, found.join("; ")));
|
|
819
|
+
const adapted = yield* adaptCaddyfile(props.caddyfile).pipe(wrapOperation(endpoint, "POST /adapt"));
|
|
758
820
|
if (servesNothing(adapted.config)) {
|
|
759
|
-
|
|
821
|
+
return yield* Effect9.fail(refuse(endpoint, "the Caddyfile adapts to no apps — loading it would stop every site"));
|
|
760
822
|
}
|
|
761
|
-
const guard = adminProblems(adapted.config,
|
|
823
|
+
const guard = adminProblems(adapted.config, listener);
|
|
762
824
|
if (guard.length > 0)
|
|
763
|
-
|
|
825
|
+
return yield* Effect9.fail(refuse(endpoint, guard.join("; ")));
|
|
764
826
|
return { digest: configDigest(adapted.config), warnings: adapted.warnings };
|
|
765
|
-
};
|
|
766
|
-
var attributesOf = (
|
|
827
|
+
});
|
|
828
|
+
var attributesOf = (endpoint, running, sourceFile) => ({
|
|
767
829
|
configSha256: configDigest(running),
|
|
768
|
-
endpoint
|
|
830
|
+
endpoint,
|
|
769
831
|
...sourceFile === undefined ? {} : { sourceFile }
|
|
770
832
|
});
|
|
771
|
-
var readLive =
|
|
833
|
+
var readLive = (sourceFile) => Effect9.gen(function* () {
|
|
834
|
+
const { endpoint } = yield* CaddyAdminService;
|
|
835
|
+
const running = yield* readRunningConfig().pipe(wrapOperation(endpoint, "GET /config/"));
|
|
836
|
+
return attributesOf(endpoint, running, sourceFile);
|
|
837
|
+
});
|
|
772
838
|
var claimable = (running, declaredDigest) => servesNothing(running) || configDigest(running) === declaredDigest;
|
|
773
|
-
var probeLive =
|
|
774
|
-
const
|
|
839
|
+
var probeLive = (caddyfile, sourceFile) => Effect9.gen(function* () {
|
|
840
|
+
const { endpoint } = yield* CaddyAdminService;
|
|
841
|
+
const running = yield* readRunningConfig();
|
|
775
842
|
if (servesNothing(running))
|
|
776
843
|
return;
|
|
777
|
-
const attributes = attributesOf(
|
|
844
|
+
const attributes = attributesOf(endpoint, running, sourceFile);
|
|
778
845
|
if (caddyfile === undefined) {
|
|
779
846
|
return { attributes, ours: false, unchecked: "the stored Caddyfile is not plain text" };
|
|
780
847
|
}
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
return { attributes, ours: claimable(running,
|
|
784
|
-
} catch (cause) {
|
|
785
|
-
if (cause instanceof CaddyUnreachableError)
|
|
786
|
-
throw cause;
|
|
787
|
-
return {
|
|
788
|
-
attributes,
|
|
789
|
-
ours: false,
|
|
790
|
-
unchecked: cause instanceof Error ? cause.message : String(cause)
|
|
791
|
-
};
|
|
848
|
+
const desired = yield* Effect9.result(desiredConfig({ caddyfile }));
|
|
849
|
+
if (Result2.isSuccess(desired)) {
|
|
850
|
+
return { attributes, ours: claimable(running, desired.success.digest) };
|
|
792
851
|
}
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
const
|
|
796
|
-
|
|
797
|
-
|
|
852
|
+
if (isUnreachable(desired.failure))
|
|
853
|
+
return yield* Effect9.fail(desired.failure);
|
|
854
|
+
const cause = desired.failure;
|
|
855
|
+
return {
|
|
856
|
+
attributes,
|
|
857
|
+
ours: false,
|
|
858
|
+
unchecked: cause instanceof Error ? cause.message : String(cause)
|
|
859
|
+
};
|
|
860
|
+
});
|
|
861
|
+
|
|
862
|
+
// src/caddy/config-reconcile.ts
|
|
863
|
+
import * as Effect10 from "effect/Effect";
|
|
864
|
+
import * as Result3 from "effect/Result";
|
|
865
|
+
var diffConfig = (news, output) => Effect10.gen(function* () {
|
|
866
|
+
const { endpoint } = yield* CaddyAdminService;
|
|
867
|
+
const want = yield* desiredConfig(news);
|
|
868
|
+
const live = yield* readLive();
|
|
869
|
+
const converged = want.digest === live.configSha256 && live.configSha256 === output.configSha256 && news.sourceFile === output.sourceFile && endpoint === output.endpoint;
|
|
798
870
|
return converged ? { action: "noop" } : { action: "update" };
|
|
799
|
-
};
|
|
800
|
-
var reconcileConfig =
|
|
801
|
-
const
|
|
802
|
-
const
|
|
871
|
+
});
|
|
872
|
+
var reconcileConfig = (props, authority) => Effect10.gen(function* () {
|
|
873
|
+
const { endpoint } = yield* CaddyAdminService;
|
|
874
|
+
const want = yield* desiredConfig(props);
|
|
875
|
+
const running = yield* readRunningConfig().pipe(wrapOperation(endpoint, "GET /config/"));
|
|
803
876
|
const before = configDigest(running);
|
|
804
877
|
const known = before === authority.stored || claimable(running, want.digest);
|
|
805
878
|
if (!authority.takeOver && !known) {
|
|
806
|
-
|
|
879
|
+
return yield* Effect10.fail(refuse(endpoint, `Caddy runs config ${short(before)}, which this stack did not load, and the Caddyfile ` + `adapts to ${short(want.digest)} — loading it would replace every site that config ` + "serves. Deploy with --adopt (or wrap the resource in adopt(true)) to take this Caddy " + "over, or point caddyProviders() at the Caddy you meant."));
|
|
807
880
|
}
|
|
808
881
|
let warnings = want.warnings;
|
|
809
882
|
const loaded = before !== want.digest;
|
|
810
883
|
if (loaded) {
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
if (
|
|
815
|
-
|
|
816
|
-
const
|
|
817
|
-
|
|
818
|
-
});
|
|
884
|
+
const outcome = yield* Effect10.result(loadCaddyfile(props.caddyfile, props.sourceFile));
|
|
885
|
+
if (Result3.isFailure(outcome)) {
|
|
886
|
+
const error = outcome.failure;
|
|
887
|
+
if (isUnreachable(error))
|
|
888
|
+
return yield* Effect10.fail(error);
|
|
889
|
+
const afterRead = yield* Effect10.result(readLive());
|
|
890
|
+
const after = Result3.isSuccess(afterRead) ? afterRead.success : undefined;
|
|
819
891
|
const state = after === undefined ? "and the running config could not be read back" : after.configSha256 === before ? "Caddy kept the previous config (still serving)" : `and the running config is now ${short(after.configSha256)}, not the previous ${short(before)}`;
|
|
820
|
-
|
|
892
|
+
const reason = error._tag === "LoadRefused" ? `${error.message} (LoadRefused: a 200 whose body carried the error, not a failure status)` : messageOf(error);
|
|
893
|
+
return yield* Effect10.fail(refuse(endpoint, `Caddy refused the Caddyfile — ${reason}; ${state}`));
|
|
821
894
|
}
|
|
895
|
+
warnings = outcome.success;
|
|
822
896
|
}
|
|
823
|
-
const attributes =
|
|
897
|
+
const attributes = yield* readLive(props.sourceFile);
|
|
824
898
|
if (attributes.configSha256 !== want.digest) {
|
|
825
|
-
|
|
899
|
+
return yield* Effect10.fail(refuse(endpoint, `after the load Caddy runs config ${short(attributes.configSha256)}, not the ` + `${short(want.digest)} this Caddyfile adapts to — another writer changed it, or it was ` + "not applied"));
|
|
826
900
|
}
|
|
827
901
|
return { attributes, loaded, warnings };
|
|
828
|
-
};
|
|
902
|
+
});
|
|
829
903
|
|
|
830
904
|
// src/caddy/config.ts
|
|
831
905
|
var CaddyConfig = Resource("Caddy.Config", {
|
|
832
906
|
defaultRemovalPolicy: "retain"
|
|
833
907
|
});
|
|
834
|
-
var
|
|
835
|
-
var
|
|
836
|
-
var CaddyConfigProvider = () => Provider.effect(CaddyConfig, Effect6.gen(function* () {
|
|
908
|
+
var planWithoutCaddy = (admin, fallback) => (error) => Effect11.as(Effect11.logWarning(`Caddy.Config: no Caddy at ${admin.endpoint} ` + `(${error instanceof Error ? error.message : String(error)}); planning without it — ` + "the Caddyfile is validated when the deploy reaches Caddy"), fallback);
|
|
909
|
+
var CaddyConfigProvider = () => Provider.effect(CaddyConfig, Effect11.gen(function* () {
|
|
837
910
|
const admin = yield* CaddyAdminService;
|
|
838
|
-
const probe = (caddyfile, sourceFile) =>
|
|
911
|
+
const probe = (caddyfile, sourceFile) => Effect11.flatMap(probeLive(caddyfile, sourceFile), (found) => {
|
|
839
912
|
if (found === undefined)
|
|
840
|
-
return
|
|
913
|
+
return Effect11.succeed(undefined);
|
|
841
914
|
if (found.ours)
|
|
842
|
-
return
|
|
915
|
+
return Effect11.succeed(found.attributes);
|
|
843
916
|
const unowned = Unowned2(found.attributes);
|
|
844
|
-
return found.unchecked === undefined ?
|
|
917
|
+
return found.unchecked === undefined ? Effect11.succeed(unowned) : Effect11.as(Effect11.logWarning(`Caddy.Config at ${admin.endpoint}: the running config could not be compared ` + `with the declared Caddyfile (${found.unchecked}), so it is not treated as ours`), unowned);
|
|
845
918
|
});
|
|
846
919
|
return CaddyConfig.Provider.of({
|
|
847
|
-
list: () =>
|
|
848
|
-
read: ({ olds, output }) => (
|
|
920
|
+
list: () => Effect11.succeed([]),
|
|
921
|
+
read: ({ olds, output }) => Effect11.gen(function* () {
|
|
922
|
+
return output === undefined ? yield* probe(resolvedString(olds, "caddyfile"), resolvedString(olds, "sourceFile")) : yield* readLive(resolvedString(olds, "sourceFile"));
|
|
923
|
+
}).pipe(Effect11.catchIf(isUnreachable, planWithoutCaddy(admin, undefined))),
|
|
849
924
|
diff: ({ news, output }) => {
|
|
850
925
|
if (output === undefined)
|
|
851
|
-
return
|
|
926
|
+
return Effect11.succeed(undefined);
|
|
852
927
|
const update = planWithoutCaddy(admin, { action: "update" });
|
|
853
928
|
if (isResolved(news)) {
|
|
854
|
-
return
|
|
929
|
+
return diffConfig(news, output).pipe(Effect11.catchIf(isUnreachable, update));
|
|
855
930
|
}
|
|
856
931
|
const caddyfile = resolvedString(news, "caddyfile");
|
|
857
932
|
if (caddyfile === undefined)
|
|
858
|
-
return
|
|
859
|
-
return
|
|
860
|
-
await diffConfig(admin, { caddyfile }, output);
|
|
861
|
-
return { action: "update" };
|
|
862
|
-
}).pipe(Effect6.catchIf(unreachable, update));
|
|
933
|
+
return Effect11.succeed(undefined);
|
|
934
|
+
return Effect11.as(diffConfig({ caddyfile }, output), { action: "update" }).pipe(Effect11.catchIf(isUnreachable, update));
|
|
863
935
|
},
|
|
864
|
-
reconcile: ({ fqn, news, output }) =>
|
|
936
|
+
reconcile: ({ fqn, news, output }) => Effect11.gen(function* () {
|
|
865
937
|
const sameCaddy = output !== undefined && output.endpoint === admin.endpoint;
|
|
866
938
|
const takeOver = sameCaddy || (yield* adoptEnabled(fqn));
|
|
867
939
|
const stored = output === undefined ? {} : { stored: output.configSha256 };
|
|
868
|
-
const applied = yield*
|
|
940
|
+
const applied = yield* reconcileConfig(news, { takeOver, ...stored });
|
|
869
941
|
for (const warning of applied.warnings) {
|
|
870
|
-
yield*
|
|
942
|
+
yield* Effect11.logWarning(`Caddy.Config at ${admin.endpoint}: ${warning}`);
|
|
871
943
|
}
|
|
872
944
|
return applied.attributes;
|
|
873
945
|
}),
|
|
874
|
-
delete: () =>
|
|
946
|
+
delete: () => Effect11.void
|
|
875
947
|
});
|
|
876
948
|
}));
|
|
877
949
|
// src/caddy/local-admin.ts
|
|
878
|
-
import
|
|
950
|
+
import * as Layer3 from "effect/Layer";
|
|
951
|
+
import * as Caddy2 from "@distilled.cloud/caddy";
|
|
952
|
+
|
|
953
|
+
// src/caddy/admin-address.ts
|
|
879
954
|
var DEFAULT_ADMIN_ADDRESS = "http://127.0.0.1:2019";
|
|
880
955
|
var UNIX = "unix://";
|
|
881
956
|
var HOST_PORT = /^(?:[A-Za-z0-9.-]+|\[[0-9A-Fa-f:.]+\]):\d{1,5}$/;
|
|
@@ -916,60 +991,29 @@ var parseAdminAddress = (address, hostHeader) => {
|
|
|
916
991
|
target: { host: url.hostname.replace(/^\[(.*)\]$/, "$1"), kind: "tcp", port }
|
|
917
992
|
};
|
|
918
993
|
};
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
var exchange = (target, method, path, headers, body, timeoutMs) => new Promise((resolve, reject) => {
|
|
922
|
-
const bytes = body === undefined ? undefined : Buffer.from(body, "utf8");
|
|
923
|
-
const request = httpRequest({
|
|
924
|
-
...target.kind === "tcp" ? { host: target.host, port: target.port } : { socketPath: target.socketPath },
|
|
925
|
-
headers: bytes === undefined ? headers : { ...headers, "Content-Length": bytes.length },
|
|
926
|
-
method,
|
|
927
|
-
path,
|
|
928
|
-
timeout: timeoutMs
|
|
929
|
-
}, (response) => {
|
|
930
|
-
const chunks = [];
|
|
931
|
-
response.on("data", (chunk) => chunks.push(chunk));
|
|
932
|
-
response.on("error", reject);
|
|
933
|
-
response.on("end", () => resolve({
|
|
934
|
-
body: Buffer.concat(chunks).toString("utf8"),
|
|
935
|
-
status: response.statusCode ?? 0
|
|
936
|
-
}));
|
|
937
|
-
});
|
|
938
|
-
request.on("timeout", () => request.destroy(new Error(`no answer in ${String(timeoutMs)} ms`)));
|
|
939
|
-
request.on("error", reject);
|
|
940
|
-
request.end(bytes);
|
|
941
|
-
});
|
|
994
|
+
|
|
995
|
+
// src/caddy/local-admin.ts
|
|
942
996
|
var localCaddyAdmin = (options = {}) => {
|
|
943
997
|
const address = options.address ?? DEFAULT_ADMIN_ADDRESS;
|
|
944
998
|
const { listener, target } = parseAdminAddress(address, options.hostHeader);
|
|
945
|
-
const
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
999
|
+
const credentials = target.kind === "unix" ? Caddy2.fromApiBaseUrl({
|
|
1000
|
+
apiBaseUrl: "http://localhost",
|
|
1001
|
+
hostHeader: options.hostHeader ?? "127.0.0.1"
|
|
1002
|
+
}) : Caddy2.fromApiBaseUrl({
|
|
1003
|
+
apiBaseUrl: address,
|
|
1004
|
+
...options.hostHeader === undefined ? {} : { hostHeader: options.hostHeader }
|
|
1005
|
+
});
|
|
949
1006
|
return {
|
|
950
1007
|
endpoint: address,
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
for (let attempt = 0;; attempt += 1) {
|
|
954
|
-
try {
|
|
955
|
-
return await exchange(target, method, path, { ...headers, ...fixed }, body, timeoutMs);
|
|
956
|
-
} catch (cause) {
|
|
957
|
-
if (!neverConnected(cause) || attempt >= retries) {
|
|
958
|
-
const reason = cause instanceof Error ? cause.message : String(cause);
|
|
959
|
-
const message = `Caddy admin ${method} ${path} at ${address}: ${reason}`;
|
|
960
|
-
throw neverConnected(cause) ? new CaddyUnreachableError(message) : new Error(message);
|
|
961
|
-
}
|
|
962
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
963
|
-
}
|
|
964
|
-
}
|
|
965
|
-
}
|
|
1008
|
+
layer: Layer3.mergeAll(credentials, makeLocalHttpClient(target, options), Layer3.succeed(Caddy2.Retry.Retry, { while: () => false })),
|
|
1009
|
+
listener
|
|
966
1010
|
};
|
|
967
1011
|
};
|
|
968
1012
|
// src/caddy/providers.ts
|
|
969
|
-
import * as
|
|
970
|
-
var caddyProviders = (
|
|
1013
|
+
import * as Layer4 from "effect/Layer";
|
|
1014
|
+
var caddyProviders = (transport = localCaddyAdmin()) => CaddyConfigProvider().pipe(Layer4.provideMerge(caddyAdminLayer(transport)));
|
|
971
1015
|
// src/caddy/with-file.ts
|
|
972
|
-
import * as
|
|
1016
|
+
import * as Effect13 from "effect/Effect";
|
|
973
1017
|
import * as RemovalPolicy from "alchemy/RemovalPolicy";
|
|
974
1018
|
|
|
975
1019
|
// src/launchd/host-file.ts
|
|
@@ -977,7 +1021,7 @@ import { Resource as Resource2 } from "alchemy";
|
|
|
977
1021
|
import { Unowned as Unowned3 } from "alchemy/AdoptPolicy";
|
|
978
1022
|
import { isResolved as isResolved2 } from "alchemy/Diff";
|
|
979
1023
|
import * as Provider2 from "alchemy/Provider";
|
|
980
|
-
import * as
|
|
1024
|
+
import * as Effect12 from "effect/Effect";
|
|
981
1025
|
|
|
982
1026
|
// src/launchd/file-identity.ts
|
|
983
1027
|
var sameEntry = (a, b) => {
|
|
@@ -1006,11 +1050,11 @@ var leaveOldPath = async (runner, oldPath, path, remove, refuse3) => {
|
|
|
1006
1050
|
|
|
1007
1051
|
// src/launchd/runner.ts
|
|
1008
1052
|
import * as Context2 from "effect/Context";
|
|
1009
|
-
import * as
|
|
1053
|
+
import * as Layer5 from "effect/Layer";
|
|
1010
1054
|
|
|
1011
1055
|
class HostRunnerService extends Context2.Service()("homeflare/launchd/HostRunner") {
|
|
1012
1056
|
}
|
|
1013
|
-
var hostRunnerLayer = (runner) =>
|
|
1057
|
+
var hostRunnerLayer = (runner) => Layer5.succeed(HostRunnerService, runner);
|
|
1014
1058
|
var canActAsRoot = (runner) => runner.privileged || runner.effectiveUid() === 0;
|
|
1015
1059
|
|
|
1016
1060
|
// src/launchd/file-converge.ts
|
|
@@ -1172,10 +1216,10 @@ var deleteFile = (runner, output) => removeWholeFile(runner, output, refuse3);
|
|
|
1172
1216
|
|
|
1173
1217
|
// src/launchd/host-file.ts
|
|
1174
1218
|
var HostFile = Resource2("Host.File");
|
|
1175
|
-
var HostFileProvider = () => Provider2.effect(HostFile,
|
|
1219
|
+
var HostFileProvider = () => Provider2.effect(HostFile, Effect12.gen(function* () {
|
|
1176
1220
|
const runner = yield* HostRunnerService;
|
|
1177
1221
|
return HostFile.Provider.of({
|
|
1178
|
-
list: () =>
|
|
1222
|
+
list: () => Effect12.succeed([]),
|
|
1179
1223
|
read: ({ olds, output }) => lift(async () => {
|
|
1180
1224
|
const found = await readFileAttributes(runner, olds.path);
|
|
1181
1225
|
if (found === undefined)
|
|
@@ -1189,19 +1233,19 @@ var HostFileProvider = () => Provider2.effect(HostFile, Effect7.gen(function* ()
|
|
|
1189
1233
|
return lift(() => diffFile(runner, news, output));
|
|
1190
1234
|
const path = resolvedString(news, "path");
|
|
1191
1235
|
if (path === undefined || path === output.path)
|
|
1192
|
-
return
|
|
1236
|
+
return Effect12.succeed(undefined);
|
|
1193
1237
|
return lift(async () => await oneFile(runner, path, output.path) === true ? undefined : { action: "replace" });
|
|
1194
1238
|
},
|
|
1195
|
-
reconcile: ({ fqn, instanceId, news, output }) =>
|
|
1239
|
+
reconcile: ({ fqn, instanceId, news, output }) => Effect12.flatMap(adoptsAtApply({ fqn, instanceId, output }), (adopt) => lift(() => reconcileFile(runner, news, output, adopt))),
|
|
1196
1240
|
delete: ({ output }) => lift(() => deleteFile(runner, output))
|
|
1197
1241
|
});
|
|
1198
1242
|
}));
|
|
1199
1243
|
|
|
1200
1244
|
// src/caddy/with-file.ts
|
|
1201
|
-
var caddyWithFile = (id, props) =>
|
|
1245
|
+
var caddyWithFile = (id, props) => Effect13.gen(function* () {
|
|
1202
1246
|
const found = configProblems({ caddyfile: props.caddyfile, sourceFile: props.path });
|
|
1203
1247
|
if (found.length > 0) {
|
|
1204
|
-
return yield*
|
|
1248
|
+
return yield* Effect13.fail(new Error(`caddyWithFile ${id}: ${found.join("; ")}`));
|
|
1205
1249
|
}
|
|
1206
1250
|
const file = yield* HostFile(`${id}-file`, {
|
|
1207
1251
|
content: props.caddyfile,
|
|
@@ -1214,19 +1258,18 @@ var caddyWithFile = (id, props) => Effect8.gen(function* () {
|
|
|
1214
1258
|
return { config, file };
|
|
1215
1259
|
});
|
|
1216
1260
|
export {
|
|
1217
|
-
CaddyAdminError,
|
|
1218
1261
|
CaddyAdminService,
|
|
1219
1262
|
CaddyConfig,
|
|
1220
1263
|
CaddyConfigProvider,
|
|
1221
|
-
CaddyUnreachableError,
|
|
1222
1264
|
DEFAULT_ADMIN_ADDRESS,
|
|
1223
1265
|
accessForwardAuth,
|
|
1224
1266
|
caddyAdminLayer,
|
|
1225
1267
|
caddyProviders,
|
|
1226
1268
|
caddyWithFile,
|
|
1269
|
+
isUnreachable,
|
|
1227
1270
|
localCaddyAdmin,
|
|
1228
1271
|
verifierProblems
|
|
1229
1272
|
};
|
|
1230
1273
|
|
|
1231
|
-
//# debugId=
|
|
1274
|
+
//# debugId=643BF5F5D21610F364756E2164756E21
|
|
1232
1275
|
//# sourceMappingURL=index.js.map
|