@homeflare/alchemy 0.59.0 → 0.60.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -11
- package/dist/caddy/config.d.ts +4 -6
- package/dist/caddy/config.d.ts.map +1 -1
- package/dist/caddy/index.js +61 -128
- package/dist/caddy/index.js.map +4 -6
- package/dist/scope/duplicate-id.d.ts +16 -16
- package/dist/scope/duplicate-id.d.ts.map +1 -1
- package/dist/scope/index.d.ts +16 -14
- package/dist/scope/index.d.ts.map +1 -1
- package/dist/scope/index.js +52 -57
- package/dist/scope/index.js.map +5 -5
- package/dist/scope/patch-check.d.ts +11 -0
- package/dist/scope/patch-check.d.ts.map +1 -0
- package/docs/duplicate-resource-id.md +90 -60
- package/docs/provider-standard.md +9 -9
- package/package.json +2 -1
- package/patches/alchemy@2.0.0-beta.81.patch +147 -0
- package/src/caddy/config.ts +7 -12
- package/src/scope/duplicate-id.test.ts +128 -69
- package/src/scope/duplicate-id.ts +39 -97
- package/src/scope/index.ts +16 -14
- package/src/scope/patch-check.test.ts +114 -0
- package/src/scope/patch-check.ts +118 -0
- package/dist/scope/guard.d.ts +0 -28
- package/dist/scope/guard.d.ts.map +0 -1
- package/src/scope/guard.test.ts +0 -128
- package/src/scope/guard.ts +0 -49
package/README.md
CHANGED
|
@@ -385,22 +385,24 @@ before export: every header dropped, every query string stripped, a denylisted h
|
|
|
385
385
|
blanked. Options, the redaction policy and what a spike measured actually arriving at a collector:
|
|
386
386
|
[docs/telemetry.md](./docs/telemetry.md) · [docs/telemetry-spike.md](./docs/telemetry-spike.md).
|
|
387
387
|
|
|
388
|
-
## Duplicate resource ids — `@homeflare/alchemy/scope`
|
|
388
|
+
## Duplicate resource ids — a patch to `alchemy`, `@homeflare/alchemy/scope`
|
|
389
389
|
|
|
390
390
|
⛔ Two resources of different types declaring the same logical id in one stack scope used to
|
|
391
391
|
collide silently — Alchemy's own idempotent-registration path treats the second one as a
|
|
392
|
-
reference to the first, and the second resource's props never reach a plan.
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
392
|
+
reference to the first, and the second resource's props never reach a plan. The fix is a PATCH
|
|
393
|
+
to `alchemy`'s own `Resource.ts` (`patches/alchemy@2.0.0-beta.81.patch`), applied at each
|
|
394
|
+
consuming stack's own root since `alchemy` is a peer dependency here:
|
|
395
|
+
|
|
396
|
+
```sh
|
|
397
|
+
cp node_modules/@homeflare/alchemy/patches/alchemy@2.0.0-beta.81.patch patches/
|
|
398
|
+
# then add to package.json: "patchedDependencies": { "alchemy@2.0.0-beta.81": "patches/alchemy@2.0.0-beta.81.patch" }
|
|
399
|
+
bun install
|
|
400
400
|
```
|
|
401
401
|
|
|
402
|
-
|
|
403
|
-
|
|
402
|
+
`checkAlchemyPatch` (`@homeflare/alchemy/scope`) is what a stack's own `check` calls to catch a
|
|
403
|
+
version bump that silently drops the patch. The measured incident, why a kit-side `Proxy`
|
|
404
|
+
wrapper could not actually fix it, and the upstream issue proposed for `alchemy` itself:
|
|
405
|
+
[docs/duplicate-resource-id.md](./docs/duplicate-resource-id.md).
|
|
404
406
|
|
|
405
407
|
## Credentials
|
|
406
408
|
|
package/dist/caddy/config.d.ts
CHANGED
|
@@ -28,12 +28,10 @@
|
|
|
28
28
|
* place that discharges them — scoped to the handful of effects that actually call
|
|
29
29
|
* `@distilled.cloud/caddy`'s operations, never leaking into a sibling fetch-based provider merged
|
|
30
30
|
* into the same stack (admin.ts's own ⛔; MEASURED 2026-09-26, admin-transport-scope.test.ts).
|
|
31
|
-
*
|
|
32
|
-
* 329:
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
* anywhere a plan could see (`scope/duplicate-id.ts` has the full mechanism). This resource was
|
|
36
|
-
* one half of that collision, so it is the first to carry the guard.
|
|
31
|
+
* ⚠️ `CADDY_ID`-style collisions with another resource's id in the same stack (MEASURED,
|
|
32
|
+
* homeflare-mini PR 329: `NixJobRetirement('caddy', …)` beside this resource's own `caddy`)
|
|
33
|
+
* are refused by the patched `alchemy` every consuming stack applies — not by anything in
|
|
34
|
+
* this file. See `packages/alchemy/docs/duplicate-resource-id.md`.
|
|
37
35
|
*/
|
|
38
36
|
import { Resource } from 'alchemy';
|
|
39
37
|
import * as Provider from 'alchemy/Provider';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/caddy/config.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/caddy/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAGnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAI7C,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEpE,OAAO,KAAK,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAKhF,YAAY,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEhF,MAAM,WAAW,WAAY,SAAQ,QAAQ,CAC3C,cAAc,EACd,gBAAgB,EAChB,qBAAqB,CACtB;CAAG;AAEJ,eAAO,MAAM,WAAW,8CAEtB,CAAC;AAoBH,eAAO,MAAM,mBAAmB,oHA+F7B,CAAC"}
|
package/dist/caddy/index.js
CHANGED
|
@@ -169,7 +169,7 @@ import { Resource } from "alchemy";
|
|
|
169
169
|
import { Unowned as Unowned2 } from "alchemy/AdoptPolicy";
|
|
170
170
|
import { isResolved } from "alchemy/Diff";
|
|
171
171
|
import * as Provider from "alchemy/Provider";
|
|
172
|
-
import * as
|
|
172
|
+
import * as Effect11 from "effect/Effect";
|
|
173
173
|
|
|
174
174
|
// src/launchd/host-effect.ts
|
|
175
175
|
import * as Effect2 from "effect/Effect";
|
|
@@ -395,88 +395,21 @@ var refuseTakeover = (owner, what) => Effect6.gen(function* () {
|
|
|
395
395
|
});
|
|
396
396
|
var claimFor = (owner) => (what) => refuseTakeover(owner, what);
|
|
397
397
|
|
|
398
|
-
// src/scope/guard.ts
|
|
399
|
-
import * as Effect8 from "effect/Effect";
|
|
400
|
-
|
|
401
|
-
// src/scope/duplicate-id.ts
|
|
402
|
-
import { toFqn } from "alchemy/FQN";
|
|
403
|
-
import { CurrentNamespace } from "alchemy/Namespace";
|
|
404
|
-
import { Stack as Stack4 } from "alchemy/Stack";
|
|
405
|
-
import * as Data from "effect/Data";
|
|
406
|
-
import * as Effect7 from "effect/Effect";
|
|
407
|
-
|
|
408
|
-
class DuplicateResourceIdError extends Data.TaggedError("DuplicateResourceIdError") {
|
|
409
|
-
}
|
|
410
|
-
var scopeLabel = (namespace) => {
|
|
411
|
-
if (!namespace)
|
|
412
|
-
return "(root)";
|
|
413
|
-
const path = [];
|
|
414
|
-
let node = namespace;
|
|
415
|
-
while (node) {
|
|
416
|
-
path.unshift(node.Id);
|
|
417
|
-
node = node.Parent;
|
|
418
|
-
}
|
|
419
|
-
return path.join("/");
|
|
420
|
-
};
|
|
421
|
-
var registries = new WeakMap;
|
|
422
|
-
var registryFor = (stack) => {
|
|
423
|
-
const existing = registries.get(stack);
|
|
424
|
-
if (existing)
|
|
425
|
-
return existing;
|
|
426
|
-
const created = new Map;
|
|
427
|
-
registries.set(stack, created);
|
|
428
|
-
return created;
|
|
429
|
-
};
|
|
430
|
-
var claimResourceId = (type, id) => Effect7.gen(function* () {
|
|
431
|
-
const stack = yield* Stack4;
|
|
432
|
-
const namespace = yield* CurrentNamespace;
|
|
433
|
-
const fqn = toFqn(namespace, id);
|
|
434
|
-
const registry = registryFor(stack);
|
|
435
|
-
const firstType = registry.get(fqn);
|
|
436
|
-
if (firstType === undefined) {
|
|
437
|
-
registry.set(fqn, type);
|
|
438
|
-
return;
|
|
439
|
-
}
|
|
440
|
-
if (firstType !== type) {
|
|
441
|
-
return yield* Effect7.die(new DuplicateResourceIdError({
|
|
442
|
-
message: `REFUSING: duplicate resource id "${fqn}" in scope ${scopeLabel(namespace)}: ` + `first declared by ${firstType}, again by ${type}`,
|
|
443
|
-
fqn,
|
|
444
|
-
scope: scopeLabel(namespace),
|
|
445
|
-
firstType,
|
|
446
|
-
secondType: type
|
|
447
|
-
}));
|
|
448
|
-
}
|
|
449
|
-
});
|
|
450
|
-
|
|
451
|
-
// src/scope/guard.ts
|
|
452
|
-
var guardDuplicateIds = (resourceClass) => new Proxy(resourceClass, {
|
|
453
|
-
apply(target, thisArg, args) {
|
|
454
|
-
const id = args[0];
|
|
455
|
-
if (typeof id !== "string") {
|
|
456
|
-
return Reflect.apply(target, thisArg, args);
|
|
457
|
-
}
|
|
458
|
-
return Effect8.gen(function* () {
|
|
459
|
-
yield* claimResourceId(resourceClass.Type, id);
|
|
460
|
-
return yield* Reflect.apply(target, thisArg, args);
|
|
461
|
-
});
|
|
462
|
-
}
|
|
463
|
-
});
|
|
464
|
-
|
|
465
398
|
// src/caddy/config-lifecycle.ts
|
|
466
|
-
import * as
|
|
399
|
+
import * as Effect9 from "effect/Effect";
|
|
467
400
|
import * as Result2 from "effect/Result";
|
|
468
401
|
|
|
469
402
|
// src/caddy/admin-calls.ts
|
|
470
|
-
import * as
|
|
403
|
+
import * as Effect7 from "effect/Effect";
|
|
471
404
|
import * as Caddy from "@distilled.cloud/caddy";
|
|
472
405
|
var CADDYFILE = "text/caddyfile";
|
|
473
406
|
var describe = (warning) => `${warning.file ?? "Caddyfile"}:${String(warning.line ?? 0)}: ${warning.message ?? "(no message)"}`;
|
|
474
|
-
var adaptCaddyfile = (caddyfile) =>
|
|
407
|
+
var adaptCaddyfile = (caddyfile) => Effect7.map(Caddy.Services.admin.adaptConfig({ config: caddyfile, contentType: CADDYFILE }), (response) => ({
|
|
475
408
|
config: response.result ?? null,
|
|
476
409
|
warnings: (response.warnings ?? []).map(describe)
|
|
477
410
|
}));
|
|
478
|
-
var readRunningConfig = () =>
|
|
479
|
-
var loadCaddyfile = (caddyfile, sourceFile) =>
|
|
411
|
+
var readRunningConfig = () => Effect7.map(Caddy.Services.admin.getConfig({ path: "" }), (response) => response.value ?? null);
|
|
412
|
+
var loadCaddyfile = (caddyfile, sourceFile) => Effect7.map(Caddy.Services.admin.loadConfig({
|
|
480
413
|
config: caddyfile,
|
|
481
414
|
contentType: CADDYFILE,
|
|
482
415
|
...sourceFile === undefined ? {} : { sourceAdapter: "caddyfile", sourceFile }
|
|
@@ -561,12 +494,12 @@ var adminProblems = (adapted, transport) => {
|
|
|
561
494
|
};
|
|
562
495
|
|
|
563
496
|
// src/caddy/config-errors.ts
|
|
564
|
-
import * as
|
|
497
|
+
import * as Effect8 from "effect/Effect";
|
|
565
498
|
var refuse = (endpoint, message) => new Error(`Caddy.Config at ${endpoint}: ${message}`);
|
|
566
499
|
var short = (digest) => digest.slice(0, 12);
|
|
567
500
|
var messageOf = (error) => ("message" in error) && typeof error.message === "string" ? error.message : String(error);
|
|
568
501
|
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)}`);
|
|
569
|
-
var wrapOperation = (endpoint, op) => (effect) => effect.pipe(
|
|
502
|
+
var wrapOperation = (endpoint, op) => (effect) => effect.pipe(Effect8.mapError((error) => isUnreachable(error) ? error : operationFailure(endpoint, op, error)));
|
|
570
503
|
|
|
571
504
|
// src/launchd/secret-tripwire.ts
|
|
572
505
|
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).";
|
|
@@ -915,18 +848,18 @@ var servesNothing = (config) => {
|
|
|
915
848
|
const apps = typeof config === "object" && config !== null ? config.apps : undefined;
|
|
916
849
|
return typeof apps !== "object" || apps === null || Object.keys(apps).length === 0;
|
|
917
850
|
};
|
|
918
|
-
var desiredConfig = (props) =>
|
|
851
|
+
var desiredConfig = (props) => Effect9.gen(function* () {
|
|
919
852
|
const { endpoint, listener } = yield* CaddyAdminService;
|
|
920
853
|
const found = configProblems(props);
|
|
921
854
|
if (found.length > 0)
|
|
922
|
-
return yield*
|
|
855
|
+
return yield* Effect9.fail(refuse(endpoint, found.join("; ")));
|
|
923
856
|
const adapted = yield* adaptCaddyfile(props.caddyfile).pipe(wrapOperation(endpoint, "POST /adapt"));
|
|
924
857
|
if (servesNothing(adapted.config)) {
|
|
925
|
-
return yield*
|
|
858
|
+
return yield* Effect9.fail(refuse(endpoint, "the Caddyfile adapts to no apps — loading it would stop every site"));
|
|
926
859
|
}
|
|
927
860
|
const guard = adminProblems(adapted.config, listener);
|
|
928
861
|
if (guard.length > 0)
|
|
929
|
-
return yield*
|
|
862
|
+
return yield* Effect9.fail(refuse(endpoint, guard.join("; ")));
|
|
930
863
|
return { digest: configDigest(adapted.config), warnings: adapted.warnings };
|
|
931
864
|
});
|
|
932
865
|
var attributesOf = (endpoint, running, sourceFile) => ({
|
|
@@ -934,13 +867,13 @@ var attributesOf = (endpoint, running, sourceFile) => ({
|
|
|
934
867
|
endpoint,
|
|
935
868
|
...sourceFile === undefined ? {} : { sourceFile }
|
|
936
869
|
});
|
|
937
|
-
var readLive = (sourceFile) =>
|
|
870
|
+
var readLive = (sourceFile) => Effect9.gen(function* () {
|
|
938
871
|
const { endpoint } = yield* CaddyAdminService;
|
|
939
872
|
const running = yield* readRunningConfig().pipe(wrapOperation(endpoint, "GET /config/"));
|
|
940
873
|
return attributesOf(endpoint, running, sourceFile);
|
|
941
874
|
});
|
|
942
875
|
var claimable = (running, declaredDigest) => servesNothing(running) || configDigest(running) === declaredDigest;
|
|
943
|
-
var probeLive = (caddyfile, sourceFile) =>
|
|
876
|
+
var probeLive = (caddyfile, sourceFile) => Effect9.gen(function* () {
|
|
944
877
|
const { endpoint } = yield* CaddyAdminService;
|
|
945
878
|
const running = yield* readRunningConfig();
|
|
946
879
|
if (servesNothing(running))
|
|
@@ -949,12 +882,12 @@ var probeLive = (caddyfile, sourceFile) => Effect11.gen(function* () {
|
|
|
949
882
|
if (caddyfile === undefined) {
|
|
950
883
|
return { attributes, ours: false, unchecked: "the stored Caddyfile is not plain text" };
|
|
951
884
|
}
|
|
952
|
-
const desired = yield*
|
|
885
|
+
const desired = yield* Effect9.result(desiredConfig({ caddyfile }));
|
|
953
886
|
if (Result2.isSuccess(desired)) {
|
|
954
887
|
return { attributes, ours: claimable(running, desired.success.digest) };
|
|
955
888
|
}
|
|
956
889
|
if (isUnreachable(desired.failure))
|
|
957
|
-
return yield*
|
|
890
|
+
return yield* Effect9.fail(desired.failure);
|
|
958
891
|
const cause = desired.failure;
|
|
959
892
|
return {
|
|
960
893
|
attributes,
|
|
@@ -964,7 +897,7 @@ var probeLive = (caddyfile, sourceFile) => Effect11.gen(function* () {
|
|
|
964
897
|
});
|
|
965
898
|
|
|
966
899
|
// src/caddy/config-reconcile.ts
|
|
967
|
-
import * as
|
|
900
|
+
import * as Effect10 from "effect/Effect";
|
|
968
901
|
import * as Result3 from "effect/Result";
|
|
969
902
|
|
|
970
903
|
// src/caddy/format-warnings.ts
|
|
@@ -976,100 +909,100 @@ var splitFormattingWarning = (warnings) => ({
|
|
|
976
909
|
var formattingFixLine = (endpoint) => `Caddy.Config at ${endpoint}: Caddyfile is not formatted — run \`caddy fmt\` on it, or format ` + "it with this package's formatCaddyfile() before declaring it, to silence this warning.";
|
|
977
910
|
|
|
978
911
|
// src/caddy/config-reconcile.ts
|
|
979
|
-
var diffConfig = (news, output) =>
|
|
912
|
+
var diffConfig = (news, output) => Effect10.gen(function* () {
|
|
980
913
|
const { endpoint } = yield* CaddyAdminService;
|
|
981
914
|
const want = yield* desiredConfig(news);
|
|
982
915
|
const { formatting } = splitFormattingWarning(want.warnings);
|
|
983
916
|
if (formatting.length > 0)
|
|
984
|
-
yield*
|
|
917
|
+
yield* Effect10.logWarning(formattingFixLine(endpoint));
|
|
985
918
|
const live = yield* readLive();
|
|
986
919
|
const converged = want.digest === live.configSha256 && live.configSha256 === output.configSha256 && news.sourceFile === output.sourceFile && endpoint === output.endpoint;
|
|
987
920
|
return converged ? { action: "noop" } : { action: "update" };
|
|
988
921
|
});
|
|
989
|
-
var reconcileConfig = (props, authority) =>
|
|
922
|
+
var reconcileConfig = (props, authority) => Effect10.gen(function* () {
|
|
990
923
|
const { endpoint } = yield* CaddyAdminService;
|
|
991
924
|
const want = yield* desiredConfig(props);
|
|
992
925
|
const running = yield* readRunningConfig().pipe(wrapOperation(endpoint, "GET /config/"));
|
|
993
926
|
const before = configDigest(running);
|
|
994
927
|
const known = before === authority.stored || claimable(running, want.digest);
|
|
995
928
|
if (!authority.takeOver && !known) {
|
|
996
|
-
return yield*
|
|
929
|
+
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."));
|
|
997
930
|
}
|
|
998
931
|
let warnings = want.warnings;
|
|
999
932
|
const loaded = before !== want.digest;
|
|
1000
933
|
if (loaded) {
|
|
1001
|
-
const outcome = yield*
|
|
934
|
+
const outcome = yield* Effect10.result(loadCaddyfile(props.caddyfile, props.sourceFile));
|
|
1002
935
|
if (Result3.isFailure(outcome)) {
|
|
1003
936
|
const error = outcome.failure;
|
|
1004
937
|
if (isUnreachable(error))
|
|
1005
|
-
return yield*
|
|
1006
|
-
const afterRead = yield*
|
|
938
|
+
return yield* Effect10.fail(error);
|
|
939
|
+
const afterRead = yield* Effect10.result(readLive());
|
|
1007
940
|
const after = Result3.isSuccess(afterRead) ? afterRead.success : undefined;
|
|
1008
941
|
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)}`;
|
|
1009
942
|
const reason = error._tag === "LoadRefused" ? `${error.message} (LoadRefused: a 200 whose body carried the error, not a failure status)` : messageOf(error);
|
|
1010
|
-
return yield*
|
|
943
|
+
return yield* Effect10.fail(refuse(endpoint, `Caddy refused the Caddyfile — ${reason}; ${state}`));
|
|
1011
944
|
}
|
|
1012
945
|
warnings = outcome.success;
|
|
1013
946
|
}
|
|
1014
947
|
const attributes = yield* readLive(props.sourceFile);
|
|
1015
948
|
if (attributes.configSha256 !== want.digest) {
|
|
1016
|
-
return yield*
|
|
949
|
+
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"));
|
|
1017
950
|
}
|
|
1018
951
|
return { attributes, loaded, warnings };
|
|
1019
952
|
});
|
|
1020
953
|
|
|
1021
954
|
// src/caddy/config.ts
|
|
1022
|
-
var CaddyConfig =
|
|
955
|
+
var CaddyConfig = Resource("Caddy.Config", {
|
|
1023
956
|
defaultRemovalPolicy: "retain"
|
|
1024
|
-
})
|
|
1025
|
-
var planWithoutCaddy = (admin, fallback) => (error) =>
|
|
1026
|
-
var CaddyConfigProvider = () => Provider.effect(CaddyConfig,
|
|
957
|
+
});
|
|
958
|
+
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);
|
|
959
|
+
var CaddyConfigProvider = () => Provider.effect(CaddyConfig, Effect11.gen(function* () {
|
|
1027
960
|
const admin = yield* CaddyAdminService;
|
|
1028
961
|
const transportLayer = yield* CaddyAdminTransport;
|
|
1029
|
-
const probe = (caddyfile, sourceFile) =>
|
|
962
|
+
const probe = (caddyfile, sourceFile) => Effect11.flatMap(probeLive(caddyfile, sourceFile), (found) => {
|
|
1030
963
|
if (found === undefined)
|
|
1031
|
-
return
|
|
964
|
+
return Effect11.succeed(undefined);
|
|
1032
965
|
if (found.ours)
|
|
1033
|
-
return
|
|
966
|
+
return Effect11.succeed(found.attributes);
|
|
1034
967
|
const unowned = Unowned2(found.attributes);
|
|
1035
|
-
return found.unchecked === undefined ?
|
|
968
|
+
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);
|
|
1036
969
|
});
|
|
1037
970
|
return CaddyConfig.Provider.of({
|
|
1038
|
-
list: () =>
|
|
1039
|
-
read: ({ olds, output }) =>
|
|
971
|
+
list: () => Effect11.succeed([]),
|
|
972
|
+
read: ({ olds, output }) => Effect11.gen(function* () {
|
|
1040
973
|
return output === undefined ? yield* probe(resolvedString(olds, "caddyfile"), resolvedString(olds, "sourceFile")) : yield* readLive(resolvedString(olds, "sourceFile"));
|
|
1041
|
-
}).pipe(
|
|
974
|
+
}).pipe(Effect11.provide(transportLayer), Effect11.catchIf(isUnreachable, planWithoutCaddy(admin, undefined))),
|
|
1042
975
|
diff: ({ news, output }) => {
|
|
1043
976
|
if (output === undefined)
|
|
1044
|
-
return
|
|
977
|
+
return Effect11.succeed(undefined);
|
|
1045
978
|
const update = planWithoutCaddy(admin, { action: "update" });
|
|
1046
979
|
if (isResolved(news)) {
|
|
1047
|
-
return diffConfig(news, output).pipe(
|
|
980
|
+
return diffConfig(news, output).pipe(Effect11.provide(transportLayer), Effect11.catchIf(isUnreachable, update));
|
|
1048
981
|
}
|
|
1049
982
|
const caddyfile = resolvedString(news, "caddyfile");
|
|
1050
983
|
if (caddyfile === undefined)
|
|
1051
|
-
return
|
|
1052
|
-
return
|
|
984
|
+
return Effect11.succeed(undefined);
|
|
985
|
+
return Effect11.as(diffConfig({ caddyfile }, output), { action: "update" }).pipe(Effect11.provide(transportLayer), Effect11.catchIf(isUnreachable, update));
|
|
1053
986
|
},
|
|
1054
|
-
reconcile: ({ fqn, news, output }) =>
|
|
987
|
+
reconcile: ({ fqn, news, output }) => Effect11.gen(function* () {
|
|
1055
988
|
const sameCaddy = output !== undefined && output.endpoint === admin.endpoint;
|
|
1056
989
|
const takeOver = sameCaddy || (yield* adoptEnabled(fqn));
|
|
1057
990
|
const stored = output === undefined ? {} : { stored: output.configSha256 };
|
|
1058
|
-
const applied = yield* reconcileConfig(news, { takeOver, ...stored }).pipe(
|
|
991
|
+
const applied = yield* reconcileConfig(news, { takeOver, ...stored }).pipe(Effect11.provide(transportLayer));
|
|
1059
992
|
const { formatting, rest } = splitFormattingWarning(applied.warnings);
|
|
1060
993
|
if (formatting.length > 0) {
|
|
1061
|
-
yield*
|
|
994
|
+
yield* Effect11.logWarning(formattingFixLine(admin.endpoint));
|
|
1062
995
|
}
|
|
1063
996
|
for (const warning of rest) {
|
|
1064
|
-
yield*
|
|
997
|
+
yield* Effect11.logWarning(`Caddy.Config at ${admin.endpoint}: ${warning}`);
|
|
1065
998
|
}
|
|
1066
999
|
return applied.attributes;
|
|
1067
1000
|
}),
|
|
1068
|
-
delete: () =>
|
|
1001
|
+
delete: () => Effect11.void
|
|
1069
1002
|
});
|
|
1070
1003
|
}));
|
|
1071
1004
|
// src/caddy/format.ts
|
|
1072
|
-
import * as
|
|
1005
|
+
import * as Effect12 from "effect/Effect";
|
|
1073
1006
|
import * as Stream from "effect/Stream";
|
|
1074
1007
|
import * as ChildProcess from "effect/process/ChildProcess";
|
|
1075
1008
|
import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner";
|
|
@@ -1096,7 +1029,7 @@ class CaddyFmtFailed extends Error {
|
|
|
1096
1029
|
this.name = "CaddyFmtFailed";
|
|
1097
1030
|
}
|
|
1098
1031
|
}
|
|
1099
|
-
var formatCaddyfile = (text, options = {}) =>
|
|
1032
|
+
var formatCaddyfile = (text, options = {}) => Effect12.gen(function* () {
|
|
1100
1033
|
const binaryPath = options.binaryPath ?? DEFAULT_CADDY_BINARY;
|
|
1101
1034
|
const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
|
|
1102
1035
|
const input = new TextEncoder().encode(text);
|
|
@@ -1105,13 +1038,13 @@ var formatCaddyfile = (text, options = {}) => Effect14.gen(function* () {
|
|
|
1105
1038
|
stderr: "pipe",
|
|
1106
1039
|
stdin: Stream.make(input),
|
|
1107
1040
|
stdout: "pipe"
|
|
1108
|
-
}).pipe(spawner.spawn,
|
|
1041
|
+
}).pipe(spawner.spawn, Effect12.flatMap((child) => Effect12.all({
|
|
1109
1042
|
exitCode: child.exitCode,
|
|
1110
|
-
stderr: child.stderr.pipe(Stream.decodeText, Stream.mkString,
|
|
1043
|
+
stderr: child.stderr.pipe(Stream.decodeText, Stream.mkString, Effect12.map((out) => out.trim())),
|
|
1111
1044
|
stdout: child.stdout.pipe(Stream.decodeText, Stream.mkString)
|
|
1112
|
-
}, { concurrency: "unbounded" })),
|
|
1045
|
+
}, { concurrency: "unbounded" })), Effect12.scoped, Effect12.catchTag("PlatformError", (error) => error.reason._tag === "NotFound" ? Effect12.fail(new CaddyFmtNotFound(binaryPath, error)) : Effect12.fail(error)));
|
|
1113
1046
|
if (result4.exitCode !== 0) {
|
|
1114
|
-
return yield*
|
|
1047
|
+
return yield* Effect12.fail(new CaddyFmtFailed(binaryPath, result4.exitCode, result4.stderr));
|
|
1115
1048
|
}
|
|
1116
1049
|
return result4.stdout;
|
|
1117
1050
|
});
|
|
@@ -1182,7 +1115,7 @@ var localCaddyAdmin = (options = {}) => {
|
|
|
1182
1115
|
import * as Layer4 from "effect/Layer";
|
|
1183
1116
|
var caddyProviders = (transport = localCaddyAdmin()) => CaddyConfigProvider().pipe(Layer4.provideMerge(caddyAdminLayer(transport)));
|
|
1184
1117
|
// src/caddy/with-file.ts
|
|
1185
|
-
import * as
|
|
1118
|
+
import * as Effect14 from "effect/Effect";
|
|
1186
1119
|
import * as RemovalPolicy from "alchemy/RemovalPolicy";
|
|
1187
1120
|
|
|
1188
1121
|
// src/launchd/host-file.ts
|
|
@@ -1190,7 +1123,7 @@ import { Resource as Resource2 } from "alchemy";
|
|
|
1190
1123
|
import { Unowned as Unowned3 } from "alchemy/AdoptPolicy";
|
|
1191
1124
|
import { isResolved as isResolved2 } from "alchemy/Diff";
|
|
1192
1125
|
import * as Provider2 from "alchemy/Provider";
|
|
1193
|
-
import * as
|
|
1126
|
+
import * as Effect13 from "effect/Effect";
|
|
1194
1127
|
|
|
1195
1128
|
// src/launchd/file-identity.ts
|
|
1196
1129
|
var sameEntry = (a, b) => {
|
|
@@ -1385,10 +1318,10 @@ var deleteFile = (runner, output) => removeWholeFile(runner, output, refuse3);
|
|
|
1385
1318
|
|
|
1386
1319
|
// src/launchd/host-file.ts
|
|
1387
1320
|
var HostFile = Resource2("Host.File");
|
|
1388
|
-
var HostFileProvider = () => Provider2.effect(HostFile,
|
|
1321
|
+
var HostFileProvider = () => Provider2.effect(HostFile, Effect13.gen(function* () {
|
|
1389
1322
|
const runner = yield* HostRunnerService;
|
|
1390
1323
|
return HostFile.Provider.of({
|
|
1391
|
-
list: () =>
|
|
1324
|
+
list: () => Effect13.succeed([]),
|
|
1392
1325
|
read: ({ olds, output }) => lift(async () => {
|
|
1393
1326
|
const found = await readFileAttributes(runner, olds.path);
|
|
1394
1327
|
if (found === undefined)
|
|
@@ -1402,19 +1335,19 @@ var HostFileProvider = () => Provider2.effect(HostFile, Effect15.gen(function* (
|
|
|
1402
1335
|
return lift(() => diffFile(runner, news, output));
|
|
1403
1336
|
const path = resolvedString(news, "path");
|
|
1404
1337
|
if (path === undefined || path === output.path)
|
|
1405
|
-
return
|
|
1338
|
+
return Effect13.succeed(undefined);
|
|
1406
1339
|
return lift(async () => await oneFile(runner, path, output.path) === true ? undefined : { action: "replace" });
|
|
1407
1340
|
},
|
|
1408
|
-
reconcile: ({ fqn, instanceId, news, output }) =>
|
|
1341
|
+
reconcile: ({ fqn, instanceId, news, output }) => Effect13.flatMap(adoptsAtApply({ fqn, instanceId, output }), (adopt) => lift(() => reconcileFile(runner, news, output, adopt))),
|
|
1409
1342
|
delete: ({ output }) => lift(() => deleteFile(runner, output))
|
|
1410
1343
|
});
|
|
1411
1344
|
}));
|
|
1412
1345
|
|
|
1413
1346
|
// src/caddy/with-file.ts
|
|
1414
|
-
var caddyWithFile = (id, props) =>
|
|
1347
|
+
var caddyWithFile = (id, props) => Effect14.gen(function* () {
|
|
1415
1348
|
const found = configProblems({ caddyfile: props.caddyfile, sourceFile: props.path });
|
|
1416
1349
|
if (found.length > 0) {
|
|
1417
|
-
return yield*
|
|
1350
|
+
return yield* Effect14.fail(new Error(`caddyWithFile ${id}: ${found.join("; ")}`));
|
|
1418
1351
|
}
|
|
1419
1352
|
const file = yield* HostFile(`${id}-file`, {
|
|
1420
1353
|
content: props.caddyfile,
|
|
@@ -1444,5 +1377,5 @@ export {
|
|
|
1444
1377
|
verifierProblems
|
|
1445
1378
|
};
|
|
1446
1379
|
|
|
1447
|
-
//# debugId=
|
|
1380
|
+
//# debugId=A4890D1A8DCAD67064756E2164756E21
|
|
1448
1381
|
//# sourceMappingURL=index.js.map
|