domainkit 0.8.0 → 0.9.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 +213 -41
- package/dist/Approval.mjs +34 -0
- package/dist/Approval.mjs.map +1 -0
- package/dist/Cleanup.mjs +97 -0
- package/dist/Cleanup.mjs.map +1 -0
- package/dist/Cloudflare.mjs +225 -0
- package/dist/Cloudflare.mjs.map +1 -0
- package/dist/Connect.mjs +463 -0
- package/dist/Connect.mjs.map +1 -0
- package/dist/Custody.mjs +64 -0
- package/dist/Custody.mjs.map +1 -0
- package/dist/DnsRecord.mjs +213 -0
- package/dist/DnsRecord.mjs.map +1 -0
- package/dist/DomainKit.mjs +34 -0
- package/dist/DomainKit.mjs.map +1 -0
- package/dist/DomainName.mjs +100 -0
- package/dist/DomainName.mjs.map +1 -0
- package/dist/Plan.mjs +102 -0
- package/dist/Plan.mjs.map +1 -0
- package/dist/Principal.mjs +23 -0
- package/dist/Principal.mjs.map +1 -0
- package/dist/Provider.mjs +130 -0
- package/dist/Provider.mjs.map +1 -0
- package/dist/Providers.mjs +40 -0
- package/dist/Providers.mjs.map +1 -0
- package/dist/Provision.mjs +93 -0
- package/dist/Provision.mjs.map +1 -0
- package/dist/Reason.mjs +162 -0
- package/dist/Reason.mjs.map +1 -0
- package/dist/Receipt.mjs +71 -0
- package/dist/Receipt.mjs.map +1 -0
- package/dist/Resolver.mjs +75 -0
- package/dist/Resolver.mjs.map +1 -0
- package/dist/Server.mjs +516 -0
- package/dist/Server.mjs.map +1 -0
- package/dist/Storage.mjs +236 -0
- package/dist/Storage.mjs.map +1 -0
- package/dist/Testing.mjs +226 -0
- package/dist/Testing.mjs.map +1 -0
- package/dist/Transport.mjs +187 -0
- package/dist/Transport.mjs.map +1 -0
- package/dist/Vercel.mjs +116 -0
- package/dist/Vercel.mjs.map +1 -0
- package/dist/Verify.mjs +292 -0
- package/dist/Verify.mjs.map +1 -0
- package/dist/{rolldown-runtime-D7D4PA-g.mjs → _virtual/_rolldown/runtime.mjs} +1 -1
- package/dist/entry/client.mjs +2 -0
- package/dist/entry/server.mjs +2 -0
- package/dist/entry/testing.mjs +3 -0
- package/dist/index.mjs +33 -462
- package/dist/index.mjs.map +1 -1
- package/dist/internal/aes.mjs +61 -0
- package/dist/internal/aes.mjs.map +1 -0
- package/dist/internal/attempts.mjs +284 -0
- package/dist/internal/attempts.mjs.map +1 -0
- package/dist/internal/cloudflare/client.mjs +100 -0
- package/dist/internal/cloudflare/client.mjs.map +1 -0
- package/dist/internal/cloudflare/protocol.mjs +59 -0
- package/dist/internal/cloudflare/protocol.mjs.map +1 -0
- package/dist/internal/cloudflare/records.mjs +156 -0
- package/dist/internal/cloudflare/records.mjs.map +1 -0
- package/dist/internal/config.mjs +13 -0
- package/dist/internal/config.mjs.map +1 -0
- package/dist/internal/conformance/provider.mjs +207 -0
- package/dist/internal/conformance/provider.mjs.map +1 -0
- package/dist/internal/conformance/storage.mjs +428 -0
- package/dist/internal/conformance/storage.mjs.map +1 -0
- package/dist/internal/digest.mjs +25 -0
- package/dist/internal/digest.mjs.map +1 -0
- package/dist/internal/doh.mjs +153 -0
- package/dist/internal/doh.mjs.map +1 -0
- package/dist/internal/error.mjs +69 -0
- package/dist/internal/error.mjs.map +1 -0
- package/dist/internal/http.mjs +90 -0
- package/dist/internal/http.mjs.map +1 -0
- package/dist/internal/ids.mjs +8 -0
- package/dist/internal/ids.mjs.map +1 -0
- package/dist/internal/oauth.mjs +113 -0
- package/dist/internal/oauth.mjs.map +1 -0
- package/dist/internal/planner.mjs +88 -0
- package/dist/internal/planner.mjs.map +1 -0
- package/dist/internal/provider-async.mjs +83 -0
- package/dist/internal/provider-async.mjs.map +1 -0
- package/dist/internal/storage-memory.mjs +302 -0
- package/dist/internal/storage-memory.mjs.map +1 -0
- package/dist/internal/vercel/client.mjs +122 -0
- package/dist/internal/vercel/client.mjs.map +1 -0
- package/dist/internal/vercel/protocol.mjs +80 -0
- package/dist/internal/vercel/protocol.mjs.map +1 -0
- package/dist/internal/vercel/records.mjs +151 -0
- package/dist/internal/vercel/records.mjs.map +1 -0
- package/dist/package.mjs +6 -0
- package/dist/package.mjs.map +1 -0
- package/dist/types/Approval.d.ts +26 -0
- package/dist/types/Cleanup.d.ts +41 -0
- package/dist/types/Cloudflare.d.ts +36 -0
- package/dist/types/Connect.d.ts +506 -0
- package/dist/types/Custody.d.ts +38 -0
- package/dist/types/DnsRecord.d.ts +158 -0
- package/dist/types/DomainKit.d.ts +31 -0
- package/dist/types/DomainName.d.ts +27 -0
- package/dist/types/Plan.d.ts +76 -0
- package/dist/types/Principal.d.ts +18 -0
- package/dist/types/Provider.d.ts +248 -0
- package/dist/types/Providers.d.ts +16 -0
- package/dist/types/Provision.d.ts +75 -0
- package/dist/types/Reason.d.ts +118 -0
- package/dist/types/Receipt.d.ts +55 -0
- package/dist/types/Resolver.d.ts +135 -0
- package/dist/types/Server.d.ts +6523 -0
- package/dist/types/Storage.d.ts +314 -0
- package/dist/types/Testing.d.ts +84 -0
- package/dist/types/Transport.d.ts +124 -0
- package/dist/types/Vercel.d.ts +25 -0
- package/dist/types/Verify.d.ts +109 -0
- package/dist/types/entry/client.d.ts +2 -0
- package/dist/types/entry/server.d.ts +2 -0
- package/dist/types/entry/testing.d.ts +3 -0
- package/dist/types/index.d.ts +21 -0
- package/dist/types/internal/aes.d.ts +8 -0
- package/dist/types/internal/attempts.d.ts +40 -0
- package/dist/types/internal/cloudflare/client.d.ts +23 -0
- package/dist/types/internal/cloudflare/protocol.d.ts +71 -0
- package/dist/types/internal/cloudflare/records.d.ts +8 -0
- package/dist/types/internal/config.d.ts +4 -0
- package/dist/types/internal/conformance/provider.d.ts +12 -0
- package/dist/types/internal/conformance/storage.d.ts +14 -0
- package/dist/types/internal/digest.d.ts +10 -0
- package/dist/types/internal/doh.d.ts +19 -0
- package/dist/types/internal/error.d.ts +24 -0
- package/dist/types/internal/http.d.ts +27 -0
- package/dist/types/internal/ids.d.ts +3 -0
- package/dist/types/internal/oauth.d.ts +58 -0
- package/dist/types/internal/planner.d.ts +22 -0
- package/dist/types/internal/provider-async.d.ts +78 -0
- package/dist/types/internal/storage-memory.d.ts +2 -0
- package/dist/types/internal/vercel/client.d.ts +29 -0
- package/dist/types/internal/vercel/protocol.d.ts +124 -0
- package/dist/types/internal/vercel/records.d.ts +7 -0
- package/package.json +88 -12
- package/dist/canonical-json-CQYKsZw7.mjs +0 -310
- package/dist/canonical-json-CQYKsZw7.mjs.map +0 -1
- package/dist/connect-D-X8T9W8.d.mts +0 -1597
- package/dist/connect-oI8hcMxc.mjs +0 -185
- package/dist/connect-oI8hcMxc.mjs.map +0 -1
- package/dist/connection-Dp6ZdSo8.d.mts +0 -186
- package/dist/connection-api-lqG1nBtD.d.mts +0 -26
- package/dist/index.d.mts +0 -1551
- package/dist/managed-dns-connections-C3A_GBaz.mjs +0 -99
- package/dist/managed-dns-connections-C3A_GBaz.mjs.map +0 -1
- package/dist/promise.d.mts +0 -378
- package/dist/promise.mjs +0 -598
- package/dist/promise.mjs.map +0 -1
- package/dist/resolver-BeGz1ukw.mjs +0 -921
- package/dist/resolver-BeGz1ukw.mjs.map +0 -1
- package/dist/resolver-xoSwyXDJ.d.mts +0 -3734
- package/dist/server.d.mts +0 -128
- package/dist/server.mjs +0 -319
- package/dist/server.mjs.map +0 -1
- package/dist/testing.d.mts +0 -62
- package/dist/testing.mjs +0 -507
- package/dist/testing.mjs.map +0 -1
- package/dist/verify-DvGPb5C9.mjs +0 -2402
- package/dist/verify-DvGPb5C9.mjs.map +0 -1
- package/dist/verify-FpunAcM7.d.mts +0 -1335
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
import { txt } from "../../DnsRecord.mjs";
|
|
2
|
+
import { Create, Digest, Model, OperationId, PlanId } from "../../Plan.mjs";
|
|
3
|
+
import { isDomainKitError } from "../error.mjs";
|
|
4
|
+
import { ApprovalId, Model as Model$1 } from "../../Approval.mjs";
|
|
5
|
+
import { Service, make } from "../../Principal.mjs";
|
|
6
|
+
import { Applied, Model as Model$2, ReceiptId } from "../../Receipt.mjs";
|
|
7
|
+
import { Attempt, Authorization, Continuation, Credential, Readiness, Service as Service$1 } from "../../Storage.mjs";
|
|
8
|
+
import { DateTime, Deferred, Effect, Fiber, Option } from "effect";
|
|
9
|
+
//#region src/internal/conformance/storage.ts
|
|
10
|
+
const owner = make({
|
|
11
|
+
ownerId: "conformance-owner-a",
|
|
12
|
+
actorId: "actor-a"
|
|
13
|
+
});
|
|
14
|
+
const other = make({
|
|
15
|
+
ownerId: "conformance-owner-b",
|
|
16
|
+
actorId: "actor-b"
|
|
17
|
+
});
|
|
18
|
+
var Failure = class extends Error {
|
|
19
|
+
constructor(message) {
|
|
20
|
+
super(message);
|
|
21
|
+
this.name = "StorageConformanceFailure";
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
const expect = (condition, message) => condition ? Effect.void : Effect.fail(new Failure(message));
|
|
25
|
+
const expectReason = (effect, tag, label) => effect.pipe(Effect.flip, Effect.mapError(() => new Failure(`${label}: expected failure ${tag}, effect succeeded`)), Effect.flatMap((cause) => expect(isDomainKitError(cause) && cause.reason._tag === tag, `${label}: expected reason ${tag}, received ${isDomainKitError(cause) ? cause.reason._tag : String(cause)}`)));
|
|
26
|
+
const authorizationRow = (principal, id, now) => new Authorization({
|
|
27
|
+
id,
|
|
28
|
+
ownerId: principal.ownerId,
|
|
29
|
+
provider: "fake",
|
|
30
|
+
method: "token",
|
|
31
|
+
capabilities: ["dns:read"],
|
|
32
|
+
context: { account: "acc" },
|
|
33
|
+
revocation: "active",
|
|
34
|
+
createdBy: principal.actorId,
|
|
35
|
+
createdAt: now
|
|
36
|
+
});
|
|
37
|
+
const credentialRow = (now, ciphertext = "sealed-1") => new Credential({
|
|
38
|
+
ciphertext,
|
|
39
|
+
expiresAt: null,
|
|
40
|
+
rotatedAt: now
|
|
41
|
+
});
|
|
42
|
+
const connect = (principal, id) => Effect.gen(function* () {
|
|
43
|
+
const storage = yield* Service$1;
|
|
44
|
+
const now = yield* DateTime.now;
|
|
45
|
+
yield* storage.authorizations.upsert({
|
|
46
|
+
authorization: authorizationRow(principal, id, now),
|
|
47
|
+
credential: credentialRow(now)
|
|
48
|
+
});
|
|
49
|
+
const connection = yield* storage.connections.create(id);
|
|
50
|
+
return {
|
|
51
|
+
storage,
|
|
52
|
+
connection,
|
|
53
|
+
attachment: yield* storage.attachments.create({
|
|
54
|
+
connectionId: connection.id,
|
|
55
|
+
domain: `app.${id}.example.com`,
|
|
56
|
+
zone: `${id}.example.com`,
|
|
57
|
+
target: { zoneId: `zone-${id}` }
|
|
58
|
+
})
|
|
59
|
+
};
|
|
60
|
+
}).pipe(Effect.provideService(Service, principal));
|
|
61
|
+
const planRow = (attachmentId, now, suffix) => new Model({
|
|
62
|
+
id: PlanId.make(`plan-${suffix}`),
|
|
63
|
+
version: "domainkit.plan.v2",
|
|
64
|
+
kind: "provisioning",
|
|
65
|
+
digest: Digest.make(`digest-${suffix}`),
|
|
66
|
+
domain: "app.example.com",
|
|
67
|
+
zone: "example.com",
|
|
68
|
+
provider: "fake",
|
|
69
|
+
attachmentId,
|
|
70
|
+
operations: [new Create({
|
|
71
|
+
id: OperationId.make(`op-${suffix}`),
|
|
72
|
+
record: txt({
|
|
73
|
+
name: "app.example.com",
|
|
74
|
+
value: suffix
|
|
75
|
+
})
|
|
76
|
+
})],
|
|
77
|
+
createdAt: now,
|
|
78
|
+
expiresAt: DateTime.add(now, { hours: 1 })
|
|
79
|
+
});
|
|
80
|
+
const attemptRow = (principal, plan) => new Attempt({
|
|
81
|
+
id: plan.id,
|
|
82
|
+
ownerId: principal.ownerId,
|
|
83
|
+
attachmentId: plan.attachmentId,
|
|
84
|
+
kind: plan.kind,
|
|
85
|
+
status: "planned",
|
|
86
|
+
plan,
|
|
87
|
+
approval: null,
|
|
88
|
+
receipt: null,
|
|
89
|
+
rejection: null,
|
|
90
|
+
sourceReceiptId: null,
|
|
91
|
+
leaseExpiresAt: null,
|
|
92
|
+
failure: null,
|
|
93
|
+
updatedAt: plan.createdAt
|
|
94
|
+
});
|
|
95
|
+
const approvalRow = (plan, suffix) => new Model$1({
|
|
96
|
+
id: ApprovalId.make(`approval-${suffix}`),
|
|
97
|
+
version: "domainkit.approval.v2",
|
|
98
|
+
kind: plan.kind,
|
|
99
|
+
planId: plan.id,
|
|
100
|
+
digest: plan.digest,
|
|
101
|
+
operationIds: plan.operations.map(({ id }) => id),
|
|
102
|
+
approvedBy: "actor",
|
|
103
|
+
approvedAt: plan.createdAt,
|
|
104
|
+
expiresAt: plan.expiresAt
|
|
105
|
+
});
|
|
106
|
+
const receiptRow = (plan, approval, suffix) => new Model$2({
|
|
107
|
+
id: ReceiptId.make(`receipt-${suffix}`),
|
|
108
|
+
version: "domainkit.receipt.v2",
|
|
109
|
+
kind: plan.kind,
|
|
110
|
+
planId: plan.id,
|
|
111
|
+
approvalId: approval.id,
|
|
112
|
+
digest: plan.digest,
|
|
113
|
+
provider: plan.provider,
|
|
114
|
+
zone: plan.zone,
|
|
115
|
+
status: "complete",
|
|
116
|
+
outcomes: plan.operations.map((operation) => new Applied({
|
|
117
|
+
operationId: operation.id,
|
|
118
|
+
providerRecordId: "r1"
|
|
119
|
+
})),
|
|
120
|
+
appliedAt: plan.createdAt
|
|
121
|
+
});
|
|
122
|
+
/** Every invariant a `Storage` implementation must hold. Each case runs against a fresh layer. */
|
|
123
|
+
const cases = (layer) => {
|
|
124
|
+
const run = (effect) => effect.pipe(Effect.provide(layer));
|
|
125
|
+
return [
|
|
126
|
+
{
|
|
127
|
+
name: "scopes every row by the principal's owner",
|
|
128
|
+
run: run(Effect.gen(function* () {
|
|
129
|
+
const { storage, connection, attachment } = yield* connect(owner, "auth-a");
|
|
130
|
+
const asOther = (effect) => effect.pipe(Effect.provideService(Service, other));
|
|
131
|
+
yield* expectReason(asOther(storage.authorizations.get("auth-a")), "NotFound", "authorizations.get");
|
|
132
|
+
yield* expectReason(asOther(storage.authorizations.credential("auth-a")), "NotFound", "authorizations.credential");
|
|
133
|
+
yield* expectReason(asOther(storage.connections.get(connection.id)), "NotFound", "connections.get");
|
|
134
|
+
yield* expectReason(asOther(storage.attachments.get(attachment.id)), "NotFound", "attachments.get");
|
|
135
|
+
yield* expectReason(asOther(storage.attachments.list(connection.id)), "NotFound", "attachments.list");
|
|
136
|
+
const connections = yield* asOther(storage.connections.list());
|
|
137
|
+
yield* expect(connections.length === 0, "connections.list leaked across owners");
|
|
138
|
+
const byDomain = yield* asOther(storage.attachments.byDomain(attachment.domain));
|
|
139
|
+
yield* expect(Option.isNone(byDomain), "attachments.byDomain leaked across owners");
|
|
140
|
+
const now = yield* DateTime.now;
|
|
141
|
+
yield* expectReason(asOther(storage.authorizations.upsert({
|
|
142
|
+
authorization: authorizationRow(owner, "auth-b", now),
|
|
143
|
+
credential: credentialRow(now)
|
|
144
|
+
})), "InvalidInput", "authorizations.upsert with a foreign ownerId");
|
|
145
|
+
}))
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
name: "stores, rotates, and promotes credentials without exposing plaintext",
|
|
149
|
+
run: run(Effect.gen(function* () {
|
|
150
|
+
const { storage } = yield* connect(owner, "auth-cred");
|
|
151
|
+
const first = yield* storage.authorizations.credential("auth-cred");
|
|
152
|
+
yield* expect(first.ciphertext === "sealed-1", "credential ciphertext was not stored");
|
|
153
|
+
const now = yield* DateTime.now;
|
|
154
|
+
yield* storage.authorizations.rotate("auth-cred", credentialRow(now, "sealed-2"));
|
|
155
|
+
const rotated = yield* storage.authorizations.credential("auth-cred");
|
|
156
|
+
yield* expect(rotated.ciphertext === "sealed-2", "rotate did not replace the credential");
|
|
157
|
+
yield* storage.authorizations.promoteCapabilities("auth-cred", ["dns:write", "dns:read"]);
|
|
158
|
+
const promoted = yield* storage.authorizations.get("auth-cred");
|
|
159
|
+
yield* expect([...promoted.capabilities].sort().join(",") === "dns:read,dns:write", "promoteCapabilities did not merge capabilities");
|
|
160
|
+
}).pipe(Effect.provideService(Service, owner)))
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
name: "replaces an authorization only through the expected id",
|
|
164
|
+
run: run(Effect.gen(function* () {
|
|
165
|
+
const { storage } = yield* connect(owner, "auth-cas");
|
|
166
|
+
const now = yield* DateTime.now;
|
|
167
|
+
const replaced = yield* storage.authorizations.upsert({
|
|
168
|
+
authorization: new Authorization({
|
|
169
|
+
...authorizationRow(owner, "auth-cas", now),
|
|
170
|
+
method: "oauth"
|
|
171
|
+
}),
|
|
172
|
+
credential: credentialRow(now, "sealed-oauth"),
|
|
173
|
+
expectedId: "auth-cas"
|
|
174
|
+
});
|
|
175
|
+
yield* expect(replaced.method === "oauth", "upsert with expectedId did not replace the row");
|
|
176
|
+
const credential = yield* storage.authorizations.credential("auth-cas");
|
|
177
|
+
yield* expect(credential.ciphertext === "sealed-oauth", "upsert with expectedId did not replace the credential");
|
|
178
|
+
yield* expectReason(storage.authorizations.upsert({
|
|
179
|
+
authorization: authorizationRow(owner, "auth-missing", now),
|
|
180
|
+
credential: credentialRow(now),
|
|
181
|
+
expectedId: "auth-missing"
|
|
182
|
+
}), "NotFound", "upsert with an unknown expectedId");
|
|
183
|
+
yield* expectReason(storage.authorizations.upsert({
|
|
184
|
+
authorization: authorizationRow(owner, "auth-cas", now),
|
|
185
|
+
credential: credentialRow(now)
|
|
186
|
+
}), "InvalidInput", "insert of an existing id");
|
|
187
|
+
}).pipe(Effect.provideService(Service, owner)))
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
name: "consumes continuations exactly once and rejects expired ones",
|
|
191
|
+
run: run(Effect.gen(function* () {
|
|
192
|
+
const storage = yield* Service$1;
|
|
193
|
+
const now = yield* DateTime.now;
|
|
194
|
+
const continuation = new Continuation({
|
|
195
|
+
id: "cont-1",
|
|
196
|
+
ownerId: owner.ownerId,
|
|
197
|
+
actorId: owner.actorId,
|
|
198
|
+
provider: "fake",
|
|
199
|
+
payload: { codeVerifier: "v" },
|
|
200
|
+
returnTo: null,
|
|
201
|
+
expiresAt: DateTime.add(now, { minutes: 15 })
|
|
202
|
+
});
|
|
203
|
+
yield* storage.continuations.put(continuation);
|
|
204
|
+
yield* expectReason(storage.continuations.consume("cont-1").pipe(Effect.provideService(Service, other)), "NotFound", "consume by another owner");
|
|
205
|
+
const peeked = yield* storage.continuations.get("cont-1");
|
|
206
|
+
yield* expect(peeked.id === "cont-1", "get did not return the continuation");
|
|
207
|
+
const consumed = yield* storage.continuations.consume("cont-1");
|
|
208
|
+
yield* expect(consumed.id === "cont-1", "consume did not return the continuation");
|
|
209
|
+
yield* expectReason(storage.continuations.get("cont-1"), "NotFound", "get after consume");
|
|
210
|
+
yield* expectReason(storage.continuations.consume("cont-1"), "NotFound", "second consume");
|
|
211
|
+
yield* storage.continuations.put(new Continuation({
|
|
212
|
+
...continuation,
|
|
213
|
+
id: "cont-2",
|
|
214
|
+
expiresAt: DateTime.subtract(now, { minutes: 1 })
|
|
215
|
+
}));
|
|
216
|
+
yield* expectReason(storage.continuations.get("cont-2"), "Expired", "expired get");
|
|
217
|
+
yield* expectReason(storage.continuations.consume("cont-2"), "Expired", "expired consume");
|
|
218
|
+
yield* expectReason(storage.continuations.consume("cont-2"), "NotFound", "expired consume is also spent");
|
|
219
|
+
}).pipe(Effect.provideService(Service, owner)))
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
name: "moves attempts through planned, approved, applying, and complete with leases",
|
|
223
|
+
run: run(Effect.gen(function* () {
|
|
224
|
+
const { storage, attachment } = yield* connect(owner, "auth-attempt");
|
|
225
|
+
const now = yield* DateTime.now;
|
|
226
|
+
const plan = planRow(attachment.id, now, "1");
|
|
227
|
+
yield* storage.attempts.create(attemptRow(owner, plan));
|
|
228
|
+
yield* expectReason(storage.attempts.create(attemptRow(owner, plan)), "InvalidInput", "duplicate attempt");
|
|
229
|
+
yield* expectReason(storage.attempts.claim(plan.id, DateTime.add(now, { minutes: 2 })), "Stale", "claim before approval");
|
|
230
|
+
const approval = approvalRow(plan, "1");
|
|
231
|
+
const approved = yield* storage.attempts.approve(plan.id, approval);
|
|
232
|
+
yield* expect(approved.status === "approved", "approve did not move the status");
|
|
233
|
+
const again = yield* storage.attempts.approve(plan.id, approval);
|
|
234
|
+
yield* expect(again.approval?.id === approval.id, "re-approving with the same approval must be idempotent");
|
|
235
|
+
yield* expectReason(storage.attempts.approve(plan.id, approvalRow(plan, "other")), "Stale", "second approval");
|
|
236
|
+
const byApproval = yield* storage.attempts.byApproval(approval.id);
|
|
237
|
+
yield* expect(byApproval.id === plan.id, "byApproval did not find the attempt");
|
|
238
|
+
const claimed = yield* storage.attempts.claim(plan.id, DateTime.add(now, { minutes: 2 }));
|
|
239
|
+
yield* expect(claimed.status === "applying" && claimed.leaseExpiresAt !== null, "claim did not lease");
|
|
240
|
+
yield* expectReason(storage.attempts.claim(plan.id, DateTime.add(now, { minutes: 2 })), "Busy", "claim while leased");
|
|
241
|
+
yield* storage.attempts.fail(plan.id, "provider down");
|
|
242
|
+
const failed = yield* storage.attempts.get(plan.id);
|
|
243
|
+
yield* expect(failed.status === "failed" && failed.failure === "provider down", "fail did not record the message");
|
|
244
|
+
const reclaimed = yield* storage.attempts.claim(plan.id, DateTime.subtract(now, { minutes: 1 }));
|
|
245
|
+
yield* expect(reclaimed.failure === null, "claim did not clear the failure");
|
|
246
|
+
const expiredLease = yield* storage.attempts.claim(plan.id, DateTime.add(now, { minutes: 2 }));
|
|
247
|
+
yield* expect(expiredLease.status === "applying", "an expired lease must be reclaimable");
|
|
248
|
+
const receipt = receiptRow(plan, approval, "1");
|
|
249
|
+
const completed = yield* storage.attempts.complete(plan.id, receipt);
|
|
250
|
+
yield* expect(completed.status === "complete" && completed.leaseExpiresAt === null, "complete did not clear the lease");
|
|
251
|
+
yield* expectReason(storage.attempts.claim(plan.id, DateTime.add(now, { minutes: 2 })), "Stale", "claim after completion");
|
|
252
|
+
const byReceipt = yield* storage.attempts.byReceipt(receipt.id);
|
|
253
|
+
yield* expect(byReceipt.receipt?.id === receipt.id, "byReceipt did not find the attempt");
|
|
254
|
+
const later = planRow(attachment.id, DateTime.add(now, { seconds: 1 }), "2");
|
|
255
|
+
yield* storage.attempts.create(attemptRow(owner, later));
|
|
256
|
+
const latest = yield* storage.attempts.latest(attachment.id, "provisioning");
|
|
257
|
+
yield* expect(Option.isSome(latest) && latest.value.id === later.id, "latest did not return the newest attempt");
|
|
258
|
+
const cleanup = yield* storage.attempts.latest(attachment.id, "cleanup");
|
|
259
|
+
yield* expect(Option.isNone(cleanup), "latest must filter by kind");
|
|
260
|
+
yield* expectReason(storage.attempts.get(plan.id).pipe(Effect.provideService(Service, other)), "NotFound", "attempts.get across owners");
|
|
261
|
+
}).pipe(Effect.provideService(Service, owner)))
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
name: "rejects a planned attempt once and terminally",
|
|
265
|
+
run: run(Effect.gen(function* () {
|
|
266
|
+
const { storage, attachment } = yield* connect(owner, "auth-reject");
|
|
267
|
+
const now = yield* DateTime.now;
|
|
268
|
+
const plan = planRow(attachment.id, now, "r1");
|
|
269
|
+
yield* storage.attempts.create(attemptRow(owner, plan));
|
|
270
|
+
yield* expectReason(storage.attempts.reject(plan.id, {
|
|
271
|
+
digest: Digest.make("other"),
|
|
272
|
+
actorId: "a",
|
|
273
|
+
reason: null
|
|
274
|
+
}), "Stale", "reject with a digest mismatch");
|
|
275
|
+
yield* expectReason(storage.attempts.reject(plan.id, {
|
|
276
|
+
digest: plan.digest,
|
|
277
|
+
actorId: other.actorId,
|
|
278
|
+
reason: null
|
|
279
|
+
}).pipe(Effect.provideService(Service, other)), "NotFound", "reject by another owner");
|
|
280
|
+
const rejected = yield* storage.attempts.reject(plan.id, {
|
|
281
|
+
digest: plan.digest,
|
|
282
|
+
actorId: owner.actorId,
|
|
283
|
+
reason: "wrong zone"
|
|
284
|
+
});
|
|
285
|
+
yield* expect(rejected.status === "rejected" && rejected.rejection?.actorId === owner.actorId && rejected.rejection.reason === "wrong zone" && DateTime.toEpochMillis(rejected.rejection.at) >= DateTime.toEpochMillis(now), "reject did not record the actor, reason, and time");
|
|
286
|
+
const again = yield* storage.attempts.reject(plan.id, {
|
|
287
|
+
digest: plan.digest,
|
|
288
|
+
actorId: "someone-else",
|
|
289
|
+
reason: "later"
|
|
290
|
+
});
|
|
291
|
+
yield* expect(again.rejection?.reason === "wrong zone", "rejecting a rejected attempt must return it unchanged");
|
|
292
|
+
yield* expectReason(storage.attempts.approve(plan.id, approvalRow(plan, "r1")), "Stale", "approve after reject");
|
|
293
|
+
yield* expectReason(storage.attempts.claim(plan.id, DateTime.add(now, { minutes: 2 })), "Stale", "claim after reject");
|
|
294
|
+
const approvedPlan = planRow(attachment.id, now, "r2");
|
|
295
|
+
yield* storage.attempts.create(attemptRow(owner, approvedPlan));
|
|
296
|
+
yield* storage.attempts.approve(approvedPlan.id, approvalRow(approvedPlan, "r2"));
|
|
297
|
+
yield* expectReason(storage.attempts.reject(approvedPlan.id, {
|
|
298
|
+
digest: approvedPlan.digest,
|
|
299
|
+
actorId: "a",
|
|
300
|
+
reason: null
|
|
301
|
+
}), "Stale", "reject after approve");
|
|
302
|
+
const expiredPlan = planRow(attachment.id, now, "r3");
|
|
303
|
+
yield* storage.attempts.create(new Attempt({
|
|
304
|
+
...attemptRow(owner, expiredPlan),
|
|
305
|
+
status: "expired"
|
|
306
|
+
}));
|
|
307
|
+
yield* expectReason(storage.attempts.reject(expiredPlan.id, {
|
|
308
|
+
digest: expiredPlan.digest,
|
|
309
|
+
actorId: "a",
|
|
310
|
+
reason: null
|
|
311
|
+
}), "Expired", "reject an expired plan");
|
|
312
|
+
}).pipe(Effect.provideService(Service, owner)))
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
name: "keeps a failed revocation pending and finishes it on recovery",
|
|
316
|
+
run: run(Effect.gen(function* () {
|
|
317
|
+
const { storage, connection, attachment } = yield* connect(owner, "auth-revoke");
|
|
318
|
+
yield* storage.attachments.remove(attachment.id);
|
|
319
|
+
yield* storage.connections.remove(connection.id);
|
|
320
|
+
const revokeFailure = yield* storage.authorizations.revoke("auth-revoke", Effect.fail(new Failure("provider unavailable"))).pipe(Effect.flip);
|
|
321
|
+
yield* expect(revokeFailure instanceof Failure, "revoke must surface the provider failure");
|
|
322
|
+
const pending = yield* storage.authorizations.get("auth-revoke");
|
|
323
|
+
yield* expect(pending.revocation === "pending", "a failed revoke must leave the row pending");
|
|
324
|
+
const now = yield* DateTime.now;
|
|
325
|
+
yield* expectReason(storage.authorizations.upsert({
|
|
326
|
+
authorization: authorizationRow(owner, "auth-revoke", now),
|
|
327
|
+
credential: credentialRow(now),
|
|
328
|
+
expectedId: "auth-revoke"
|
|
329
|
+
}), "Busy", "reconnect while revocation is pending");
|
|
330
|
+
const recoveredByOther = yield* storage.authorizations.recoverRevocations(() => Effect.void).pipe(Effect.provideService(Service, other));
|
|
331
|
+
yield* expect(recoveredByOther === 0, "recovery must not cross owners");
|
|
332
|
+
let calls = 0;
|
|
333
|
+
const recovered = yield* storage.authorizations.recoverRevocations(() => Effect.sync(() => void (calls += 1)));
|
|
334
|
+
yield* expect(recovered === 1 && calls === 1, "recovery must revoke exactly the pending authorization");
|
|
335
|
+
yield* expectReason(storage.authorizations.get("auth-revoke"), "NotFound", "authorization after recovery");
|
|
336
|
+
const again = yield* storage.authorizations.recoverRevocations(() => Effect.void);
|
|
337
|
+
yield* expect(again === 0, "recovery must be idempotent");
|
|
338
|
+
}).pipe(Effect.provideService(Service, owner)))
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
name: "blocks connection removal while attachments exist",
|
|
342
|
+
run: run(Effect.gen(function* () {
|
|
343
|
+
const { storage, connection, attachment } = yield* connect(owner, "auth-remove");
|
|
344
|
+
yield* expectReason(storage.connections.remove(connection.id), "InvalidInput", "remove with attachments");
|
|
345
|
+
yield* expectReason(storage.attachments.create({
|
|
346
|
+
connectionId: connection.id,
|
|
347
|
+
domain: attachment.domain,
|
|
348
|
+
zone: attachment.zone,
|
|
349
|
+
target: {}
|
|
350
|
+
}), "InvalidInput", "duplicate domain attachment");
|
|
351
|
+
yield* storage.attachments.remove(attachment.id);
|
|
352
|
+
yield* storage.connections.remove(connection.id);
|
|
353
|
+
yield* expectReason(storage.connections.get(connection.id), "NotFound", "connection after remove");
|
|
354
|
+
}).pipe(Effect.provideService(Service, owner)))
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
name: "fails Busy while a lock is held and releases it afterwards",
|
|
358
|
+
run: run(Effect.gen(function* () {
|
|
359
|
+
const storage = yield* Service$1;
|
|
360
|
+
const gate = yield* Deferred.make();
|
|
361
|
+
const holder = yield* Effect.forkChild(storage.withLock("refresh:auth-1", Deferred.await(gate)));
|
|
362
|
+
yield* Effect.yieldNow;
|
|
363
|
+
yield* expectReason(storage.withLock("refresh:auth-1", Effect.void), "Busy", "second holder");
|
|
364
|
+
const otherOwner = yield* storage.withLock("refresh:auth-1", Effect.succeed("ok")).pipe(Effect.provideService(Service, other));
|
|
365
|
+
yield* expect(otherOwner === "ok", "locks must be scoped by owner");
|
|
366
|
+
yield* Deferred.succeed(gate, void 0);
|
|
367
|
+
yield* Fiber.join(holder);
|
|
368
|
+
const after = yield* storage.withLock("refresh:auth-1", Effect.succeed("free"));
|
|
369
|
+
yield* expect(after === "free", "lock was not released");
|
|
370
|
+
yield* storage.withLock("refresh:auth-1", Effect.fail(new Failure("inside"))).pipe(Effect.ignore);
|
|
371
|
+
const afterFailure = yield* storage.withLock("refresh:auth-1", Effect.succeed("free"));
|
|
372
|
+
yield* expect(afterFailure === "free", "lock was not released after a failure");
|
|
373
|
+
}).pipe(Effect.provideService(Service, owner)))
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
name: "stores readiness per domain, with or without an attachment",
|
|
377
|
+
run: run(Effect.gen(function* () {
|
|
378
|
+
const { storage, attachment } = yield* connect(owner, "auth-ready");
|
|
379
|
+
const now = yield* DateTime.now;
|
|
380
|
+
const readiness = new Readiness({
|
|
381
|
+
domain: attachment.domain,
|
|
382
|
+
attachmentId: attachment.id,
|
|
383
|
+
ownerId: owner.ownerId,
|
|
384
|
+
overall: "pending",
|
|
385
|
+
requirements: [{
|
|
386
|
+
operationId: null,
|
|
387
|
+
record: txt({
|
|
388
|
+
name: "app.example.com",
|
|
389
|
+
value: "v"
|
|
390
|
+
}),
|
|
391
|
+
status: "missing",
|
|
392
|
+
evidence: []
|
|
393
|
+
}],
|
|
394
|
+
host: [],
|
|
395
|
+
pendingSince: now,
|
|
396
|
+
checkedAt: now,
|
|
397
|
+
nextCheckAt: DateTime.add(now, { seconds: 15 })
|
|
398
|
+
});
|
|
399
|
+
yield* storage.readiness.put(readiness);
|
|
400
|
+
const stored = yield* storage.readiness.get(attachment.domain);
|
|
401
|
+
yield* expect(Option.isSome(stored) && stored.value.overall === "pending", "readiness was not stored");
|
|
402
|
+
const foreign = yield* storage.readiness.get(attachment.domain).pipe(Effect.provideService(Service, other));
|
|
403
|
+
yield* expect(Option.isNone(foreign), "readiness leaked across owners");
|
|
404
|
+
yield* storage.readiness.put(new Readiness({
|
|
405
|
+
...readiness,
|
|
406
|
+
domain: "observe-only.example.com",
|
|
407
|
+
attachmentId: null
|
|
408
|
+
}));
|
|
409
|
+
const observeOnly = yield* storage.readiness.get("observe-only.example.com");
|
|
410
|
+
yield* expect(Option.isSome(observeOnly) && observeOnly.value.attachmentId === null, "readiness without an attachment was not stored");
|
|
411
|
+
yield* expectReason(storage.readiness.put(new Readiness({
|
|
412
|
+
...readiness,
|
|
413
|
+
attachmentId: "att-missing"
|
|
414
|
+
})), "NotFound", "readiness with an unknown attachment");
|
|
415
|
+
}).pipe(Effect.provideService(Service, owner)))
|
|
416
|
+
}
|
|
417
|
+
];
|
|
418
|
+
};
|
|
419
|
+
/** Registers every case with `options.it` when given; always returns the cases. */
|
|
420
|
+
const storage = (layer, options = {}) => {
|
|
421
|
+
const all = cases(layer);
|
|
422
|
+
if (options.it !== void 0) for (const item of all) options.it(item.name, () => Effect.runPromise(item.run));
|
|
423
|
+
return all;
|
|
424
|
+
};
|
|
425
|
+
//#endregion
|
|
426
|
+
export { cases, storage };
|
|
427
|
+
|
|
428
|
+
//# sourceMappingURL=storage.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.mjs","names":["Principal.make","Errors.isDomainKitError","Storage.Authorization","Storage.Credential","Storage.Service","Principal.Service","Plan.Model","Plan.Create","DnsRecord.txt","Storage.Attempt","Approval.Model","Receipt.Model","Receipt.Applied","Storage.Continuation","Storage.Readiness"],"sources":["../../../src/internal/conformance/storage.ts"],"sourcesContent":["import { DateTime, Deferred, Effect, Fiber, Layer, Option } from \"effect\";\n\nimport * as Approval from \"../../Approval.ts\";\nimport * as DnsRecord from \"../../DnsRecord.ts\";\nimport * as Errors from \"../error.ts\";\nimport * as Reason from \"../../Reason.ts\";\nimport * as Plan from \"../../Plan.ts\";\nimport * as Principal from \"../../Principal.ts\";\nimport * as Receipt from \"../../Receipt.ts\";\nimport * as Storage from \"../../Storage.ts\";\n\nexport interface Case {\n readonly name: string;\n readonly run: Effect.Effect<void, unknown>;\n}\n\nexport interface Options {\n /** Register each case with a test runner, e.g. vitest's `it`. */\n readonly it?: (name: string, run: () => Promise<void>) => void;\n}\n\nconst owner = Principal.make({ ownerId: \"conformance-owner-a\", actorId: \"actor-a\" });\nconst other = Principal.make({ ownerId: \"conformance-owner-b\", actorId: \"actor-b\" });\n\nclass Failure extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"StorageConformanceFailure\";\n }\n}\n\nconst expect = (condition: boolean, message: string) =>\n condition ? Effect.void : Effect.fail(new Failure(message));\n\nconst expectReason = <A, R>(\n effect: Effect.Effect<A, unknown, R>,\n tag: Reason.Model[\"_tag\"],\n label: string,\n): Effect.Effect<void, Failure, R> =>\n effect.pipe(\n Effect.flip,\n Effect.mapError(() => new Failure(`${label}: expected failure ${tag}, effect succeeded`)),\n Effect.flatMap((cause) =>\n expect(\n Errors.isDomainKitError(cause) && cause.reason._tag === tag,\n `${label}: expected reason ${tag}, received ${Errors.isDomainKitError(cause) ? cause.reason._tag : String(cause)}`,\n ),\n ),\n );\n\nconst authorizationRow = (principal: Principal.Interface, id: string, now: DateTime.Utc) =>\n new Storage.Authorization({\n id,\n ownerId: principal.ownerId,\n provider: \"fake\",\n method: \"token\",\n capabilities: [\"dns:read\"],\n context: { account: \"acc\" },\n revocation: \"active\",\n createdBy: principal.actorId,\n createdAt: now,\n });\n\nconst credentialRow = (now: DateTime.Utc, ciphertext = \"sealed-1\") =>\n new Storage.Credential({ ciphertext, expiresAt: null, rotatedAt: now });\n\nconst connect = (principal: Principal.Interface, id: string) =>\n Effect.gen(function* () {\n const storage = yield* Storage.Service;\n const now = yield* DateTime.now;\n yield* storage.authorizations.upsert({\n authorization: authorizationRow(principal, id, now),\n credential: credentialRow(now),\n });\n const connection = yield* storage.connections.create(id);\n const attachment = yield* storage.attachments.create({\n connectionId: connection.id,\n domain: `app.${id}.example.com`,\n zone: `${id}.example.com`,\n target: { zoneId: `zone-${id}` },\n });\n return { storage, connection, attachment };\n }).pipe(Effect.provideService(Principal.Service, principal));\n\nconst planRow = (attachmentId: string, now: DateTime.Utc, suffix: string) =>\n new Plan.Model({\n id: Plan.PlanId.make(`plan-${suffix}`),\n version: \"domainkit.plan.v2\",\n kind: \"provisioning\",\n digest: Plan.Digest.make(`digest-${suffix}`),\n domain: \"app.example.com\",\n zone: \"example.com\",\n provider: \"fake\",\n attachmentId,\n operations: [\n new Plan.Create({\n id: Plan.OperationId.make(`op-${suffix}`),\n record: DnsRecord.txt({ name: \"app.example.com\", value: suffix }),\n }),\n ],\n createdAt: now,\n expiresAt: DateTime.add(now, { hours: 1 }),\n });\n\nconst attemptRow = (principal: Principal.Interface, plan: Plan.Model) =>\n new Storage.Attempt({\n id: plan.id,\n ownerId: principal.ownerId,\n attachmentId: plan.attachmentId,\n kind: plan.kind,\n status: \"planned\",\n plan,\n approval: null,\n receipt: null,\n rejection: null,\n sourceReceiptId: null,\n leaseExpiresAt: null,\n failure: null,\n updatedAt: plan.createdAt,\n });\n\nconst approvalRow = (plan: Plan.Model, suffix: string) =>\n new Approval.Model({\n id: Approval.ApprovalId.make(`approval-${suffix}`),\n version: \"domainkit.approval.v2\",\n kind: plan.kind,\n planId: plan.id,\n digest: plan.digest,\n operationIds: plan.operations.map(({ id }) => id),\n approvedBy: \"actor\",\n approvedAt: plan.createdAt,\n expiresAt: plan.expiresAt,\n });\n\nconst receiptRow = (plan: Plan.Model, approval: Approval.Model, suffix: string) =>\n new Receipt.Model({\n id: Receipt.ReceiptId.make(`receipt-${suffix}`),\n version: \"domainkit.receipt.v2\",\n kind: plan.kind,\n planId: plan.id,\n approvalId: approval.id,\n digest: plan.digest,\n provider: plan.provider,\n zone: plan.zone,\n status: \"complete\",\n outcomes: plan.operations.map(\n (operation) => new Receipt.Applied({ operationId: operation.id, providerRecordId: \"r1\" }),\n ),\n appliedAt: plan.createdAt,\n });\n\n/** Every invariant a `Storage` implementation must hold. Each case runs against a fresh layer. */\nexport const cases = (layer: Layer.Layer<Storage.Service, unknown>): ReadonlyArray<Case> => {\n const run = <E>(effect: Effect.Effect<void, E, Storage.Service>): Effect.Effect<void, unknown> =>\n effect.pipe(Effect.provide(layer));\n return [\n {\n name: \"scopes every row by the principal's owner\",\n run: run(\n Effect.gen(function* () {\n const { storage, connection, attachment } = yield* connect(owner, \"auth-a\");\n const asOther = <A, E>(effect: Effect.Effect<A, E, Principal.Service>) =>\n effect.pipe(Effect.provideService(Principal.Service, other));\n yield* expectReason(\n asOther(storage.authorizations.get(\"auth-a\")),\n \"NotFound\",\n \"authorizations.get\",\n );\n yield* expectReason(\n asOther(storage.authorizations.credential(\"auth-a\")),\n \"NotFound\",\n \"authorizations.credential\",\n );\n yield* expectReason(\n asOther(storage.connections.get(connection.id)),\n \"NotFound\",\n \"connections.get\",\n );\n yield* expectReason(\n asOther(storage.attachments.get(attachment.id)),\n \"NotFound\",\n \"attachments.get\",\n );\n yield* expectReason(\n asOther(storage.attachments.list(connection.id)),\n \"NotFound\",\n \"attachments.list\",\n );\n const connections = yield* asOther(storage.connections.list());\n yield* expect(connections.length === 0, \"connections.list leaked across owners\");\n const byDomain = yield* asOther(storage.attachments.byDomain(attachment.domain));\n yield* expect(Option.isNone(byDomain), \"attachments.byDomain leaked across owners\");\n const now = yield* DateTime.now;\n yield* expectReason(\n asOther(\n storage.authorizations.upsert({\n authorization: authorizationRow(owner, \"auth-b\", now),\n credential: credentialRow(now),\n }),\n ),\n \"InvalidInput\",\n \"authorizations.upsert with a foreign ownerId\",\n );\n }),\n ),\n },\n {\n name: \"stores, rotates, and promotes credentials without exposing plaintext\",\n run: run(\n Effect.gen(function* () {\n const { storage } = yield* connect(owner, \"auth-cred\");\n const first = yield* storage.authorizations.credential(\"auth-cred\");\n yield* expect(first.ciphertext === \"sealed-1\", \"credential ciphertext was not stored\");\n const now = yield* DateTime.now;\n yield* storage.authorizations.rotate(\"auth-cred\", credentialRow(now, \"sealed-2\"));\n const rotated = yield* storage.authorizations.credential(\"auth-cred\");\n yield* expect(rotated.ciphertext === \"sealed-2\", \"rotate did not replace the credential\");\n yield* storage.authorizations.promoteCapabilities(\"auth-cred\", [\"dns:write\", \"dns:read\"]);\n const promoted = yield* storage.authorizations.get(\"auth-cred\");\n yield* expect(\n [...promoted.capabilities].sort().join(\",\") === \"dns:read,dns:write\",\n \"promoteCapabilities did not merge capabilities\",\n );\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n {\n name: \"replaces an authorization only through the expected id\",\n run: run(\n Effect.gen(function* () {\n const { storage } = yield* connect(owner, \"auth-cas\");\n const now = yield* DateTime.now;\n const replaced = yield* storage.authorizations.upsert({\n authorization: new Storage.Authorization({\n ...authorizationRow(owner, \"auth-cas\", now),\n method: \"oauth\",\n }),\n credential: credentialRow(now, \"sealed-oauth\"),\n expectedId: \"auth-cas\",\n });\n yield* expect(\n replaced.method === \"oauth\",\n \"upsert with expectedId did not replace the row\",\n );\n const credential = yield* storage.authorizations.credential(\"auth-cas\");\n yield* expect(\n credential.ciphertext === \"sealed-oauth\",\n \"upsert with expectedId did not replace the credential\",\n );\n yield* expectReason(\n storage.authorizations.upsert({\n authorization: authorizationRow(owner, \"auth-missing\", now),\n credential: credentialRow(now),\n expectedId: \"auth-missing\",\n }),\n \"NotFound\",\n \"upsert with an unknown expectedId\",\n );\n yield* expectReason(\n storage.authorizations.upsert({\n authorization: authorizationRow(owner, \"auth-cas\", now),\n credential: credentialRow(now),\n }),\n \"InvalidInput\",\n \"insert of an existing id\",\n );\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n {\n name: \"consumes continuations exactly once and rejects expired ones\",\n run: run(\n Effect.gen(function* () {\n const storage = yield* Storage.Service;\n const now = yield* DateTime.now;\n const continuation = new Storage.Continuation({\n id: \"cont-1\",\n ownerId: owner.ownerId,\n actorId: owner.actorId,\n provider: \"fake\",\n payload: { codeVerifier: \"v\" },\n returnTo: null,\n expiresAt: DateTime.add(now, { minutes: 15 }),\n });\n yield* storage.continuations.put(continuation);\n yield* expectReason(\n storage.continuations\n .consume(\"cont-1\")\n .pipe(Effect.provideService(Principal.Service, other)),\n \"NotFound\",\n \"consume by another owner\",\n );\n const peeked = yield* storage.continuations.get(\"cont-1\");\n yield* expect(peeked.id === \"cont-1\", \"get did not return the continuation\");\n const consumed = yield* storage.continuations.consume(\"cont-1\");\n yield* expect(consumed.id === \"cont-1\", \"consume did not return the continuation\");\n yield* expectReason(storage.continuations.get(\"cont-1\"), \"NotFound\", \"get after consume\");\n yield* expectReason(\n storage.continuations.consume(\"cont-1\"),\n \"NotFound\",\n \"second consume\",\n );\n yield* storage.continuations.put(\n new Storage.Continuation({\n ...continuation,\n id: \"cont-2\",\n expiresAt: DateTime.subtract(now, { minutes: 1 }),\n }),\n );\n yield* expectReason(storage.continuations.get(\"cont-2\"), \"Expired\", \"expired get\");\n yield* expectReason(\n storage.continuations.consume(\"cont-2\"),\n \"Expired\",\n \"expired consume\",\n );\n yield* expectReason(\n storage.continuations.consume(\"cont-2\"),\n \"NotFound\",\n \"expired consume is also spent\",\n );\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n {\n name: \"moves attempts through planned, approved, applying, and complete with leases\",\n run: run(\n Effect.gen(function* () {\n const { storage, attachment } = yield* connect(owner, \"auth-attempt\");\n const now = yield* DateTime.now;\n const plan = planRow(attachment.id, now, \"1\");\n yield* storage.attempts.create(attemptRow(owner, plan));\n yield* expectReason(\n storage.attempts.create(attemptRow(owner, plan)),\n \"InvalidInput\",\n \"duplicate attempt\",\n );\n yield* expectReason(\n storage.attempts.claim(plan.id, DateTime.add(now, { minutes: 2 })),\n \"Stale\",\n \"claim before approval\",\n );\n const approval = approvalRow(plan, \"1\");\n const approved = yield* storage.attempts.approve(plan.id, approval);\n yield* expect(approved.status === \"approved\", \"approve did not move the status\");\n const again = yield* storage.attempts.approve(plan.id, approval);\n yield* expect(\n again.approval?.id === approval.id,\n \"re-approving with the same approval must be idempotent\",\n );\n yield* expectReason(\n storage.attempts.approve(plan.id, approvalRow(plan, \"other\")),\n \"Stale\",\n \"second approval\",\n );\n const byApproval = yield* storage.attempts.byApproval(approval.id);\n yield* expect(byApproval.id === plan.id, \"byApproval did not find the attempt\");\n const claimed = yield* storage.attempts.claim(plan.id, DateTime.add(now, { minutes: 2 }));\n yield* expect(\n claimed.status === \"applying\" && claimed.leaseExpiresAt !== null,\n \"claim did not lease\",\n );\n yield* expectReason(\n storage.attempts.claim(plan.id, DateTime.add(now, { minutes: 2 })),\n \"Busy\",\n \"claim while leased\",\n );\n yield* storage.attempts.fail(plan.id, \"provider down\");\n const failed = yield* storage.attempts.get(plan.id);\n yield* expect(\n failed.status === \"failed\" && failed.failure === \"provider down\",\n \"fail did not record the message\",\n );\n const reclaimed = yield* storage.attempts.claim(\n plan.id,\n DateTime.subtract(now, { minutes: 1 }),\n );\n yield* expect(reclaimed.failure === null, \"claim did not clear the failure\");\n const expiredLease = yield* storage.attempts.claim(\n plan.id,\n DateTime.add(now, { minutes: 2 }),\n );\n yield* expect(expiredLease.status === \"applying\", \"an expired lease must be reclaimable\");\n const receipt = receiptRow(plan, approval, \"1\");\n const completed = yield* storage.attempts.complete(plan.id, receipt);\n yield* expect(\n completed.status === \"complete\" && completed.leaseExpiresAt === null,\n \"complete did not clear the lease\",\n );\n yield* expectReason(\n storage.attempts.claim(plan.id, DateTime.add(now, { minutes: 2 })),\n \"Stale\",\n \"claim after completion\",\n );\n const byReceipt = yield* storage.attempts.byReceipt(receipt.id);\n yield* expect(byReceipt.receipt?.id === receipt.id, \"byReceipt did not find the attempt\");\n const later = planRow(attachment.id, DateTime.add(now, { seconds: 1 }), \"2\");\n yield* storage.attempts.create(attemptRow(owner, later));\n const latest = yield* storage.attempts.latest(attachment.id, \"provisioning\");\n yield* expect(\n Option.isSome(latest) && latest.value.id === later.id,\n \"latest did not return the newest attempt\",\n );\n const cleanup = yield* storage.attempts.latest(attachment.id, \"cleanup\");\n yield* expect(Option.isNone(cleanup), \"latest must filter by kind\");\n yield* expectReason(\n storage.attempts.get(plan.id).pipe(Effect.provideService(Principal.Service, other)),\n \"NotFound\",\n \"attempts.get across owners\",\n );\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n {\n name: \"rejects a planned attempt once and terminally\",\n run: run(\n Effect.gen(function* () {\n const { storage, attachment } = yield* connect(owner, \"auth-reject\");\n const now = yield* DateTime.now;\n const plan = planRow(attachment.id, now, \"r1\");\n yield* storage.attempts.create(attemptRow(owner, plan));\n yield* expectReason(\n storage.attempts.reject(plan.id, {\n digest: Plan.Digest.make(\"other\"),\n actorId: \"a\",\n reason: null,\n }),\n \"Stale\",\n \"reject with a digest mismatch\",\n );\n yield* expectReason(\n storage.attempts\n .reject(plan.id, { digest: plan.digest, actorId: other.actorId, reason: null })\n .pipe(Effect.provideService(Principal.Service, other)),\n \"NotFound\",\n \"reject by another owner\",\n );\n const rejected = yield* storage.attempts.reject(plan.id, {\n digest: plan.digest,\n actorId: owner.actorId,\n reason: \"wrong zone\",\n });\n yield* expect(\n rejected.status === \"rejected\" &&\n rejected.rejection?.actorId === owner.actorId &&\n rejected.rejection.reason === \"wrong zone\" &&\n DateTime.toEpochMillis(rejected.rejection.at) >= DateTime.toEpochMillis(now),\n \"reject did not record the actor, reason, and time\",\n );\n const again = yield* storage.attempts.reject(plan.id, {\n digest: plan.digest,\n actorId: \"someone-else\",\n reason: \"later\",\n });\n yield* expect(\n again.rejection?.reason === \"wrong zone\",\n \"rejecting a rejected attempt must return it unchanged\",\n );\n yield* expectReason(\n storage.attempts.approve(plan.id, approvalRow(plan, \"r1\")),\n \"Stale\",\n \"approve after reject\",\n );\n yield* expectReason(\n storage.attempts.claim(plan.id, DateTime.add(now, { minutes: 2 })),\n \"Stale\",\n \"claim after reject\",\n );\n const approvedPlan = planRow(attachment.id, now, \"r2\");\n yield* storage.attempts.create(attemptRow(owner, approvedPlan));\n yield* storage.attempts.approve(approvedPlan.id, approvalRow(approvedPlan, \"r2\"));\n yield* expectReason(\n storage.attempts.reject(approvedPlan.id, {\n digest: approvedPlan.digest,\n actorId: \"a\",\n reason: null,\n }),\n \"Stale\",\n \"reject after approve\",\n );\n const expiredPlan = planRow(attachment.id, now, \"r3\");\n yield* storage.attempts.create(\n new Storage.Attempt({ ...attemptRow(owner, expiredPlan), status: \"expired\" }),\n );\n yield* expectReason(\n storage.attempts.reject(expiredPlan.id, {\n digest: expiredPlan.digest,\n actorId: \"a\",\n reason: null,\n }),\n \"Expired\",\n \"reject an expired plan\",\n );\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n {\n name: \"keeps a failed revocation pending and finishes it on recovery\",\n run: run(\n Effect.gen(function* () {\n const { storage, connection, attachment } = yield* connect(owner, \"auth-revoke\");\n yield* storage.attachments.remove(attachment.id);\n yield* storage.connections.remove(connection.id);\n const revokeFailure = yield* storage.authorizations\n .revoke(\"auth-revoke\", Effect.fail(new Failure(\"provider unavailable\")))\n .pipe(Effect.flip);\n yield* expect(\n revokeFailure instanceof Failure,\n \"revoke must surface the provider failure\",\n );\n const pending = yield* storage.authorizations.get(\"auth-revoke\");\n yield* expect(\n pending.revocation === \"pending\",\n \"a failed revoke must leave the row pending\",\n );\n const now = yield* DateTime.now;\n yield* expectReason(\n storage.authorizations.upsert({\n authorization: authorizationRow(owner, \"auth-revoke\", now),\n credential: credentialRow(now),\n expectedId: \"auth-revoke\",\n }),\n \"Busy\",\n \"reconnect while revocation is pending\",\n );\n const recoveredByOther = yield* storage.authorizations\n .recoverRevocations(() => Effect.void)\n .pipe(Effect.provideService(Principal.Service, other));\n yield* expect(recoveredByOther === 0, \"recovery must not cross owners\");\n let calls = 0;\n const recovered = yield* storage.authorizations.recoverRevocations(() =>\n Effect.sync(() => void (calls += 1)),\n );\n yield* expect(\n recovered === 1 && calls === 1,\n \"recovery must revoke exactly the pending authorization\",\n );\n yield* expectReason(\n storage.authorizations.get(\"auth-revoke\"),\n \"NotFound\",\n \"authorization after recovery\",\n );\n const again = yield* storage.authorizations.recoverRevocations(() => Effect.void);\n yield* expect(again === 0, \"recovery must be idempotent\");\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n {\n name: \"blocks connection removal while attachments exist\",\n run: run(\n Effect.gen(function* () {\n const { storage, connection, attachment } = yield* connect(owner, \"auth-remove\");\n yield* expectReason(\n storage.connections.remove(connection.id),\n \"InvalidInput\",\n \"remove with attachments\",\n );\n yield* expectReason(\n storage.attachments.create({\n connectionId: connection.id,\n domain: attachment.domain,\n zone: attachment.zone,\n target: {},\n }),\n \"InvalidInput\",\n \"duplicate domain attachment\",\n );\n yield* storage.attachments.remove(attachment.id);\n yield* storage.connections.remove(connection.id);\n yield* expectReason(\n storage.connections.get(connection.id),\n \"NotFound\",\n \"connection after remove\",\n );\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n {\n name: \"fails Busy while a lock is held and releases it afterwards\",\n run: run(\n Effect.gen(function* () {\n const storage = yield* Storage.Service;\n const gate = yield* Deferred.make<void>();\n const holder = yield* Effect.forkChild(\n storage.withLock(\"refresh:auth-1\", Deferred.await(gate)),\n );\n yield* Effect.yieldNow;\n yield* expectReason(\n storage.withLock(\"refresh:auth-1\", Effect.void),\n \"Busy\",\n \"second holder\",\n );\n const otherOwner = yield* storage\n .withLock(\"refresh:auth-1\", Effect.succeed(\"ok\"))\n .pipe(Effect.provideService(Principal.Service, other));\n yield* expect(otherOwner === \"ok\", \"locks must be scoped by owner\");\n yield* Deferred.succeed(gate, undefined);\n yield* Fiber.join(holder);\n const after = yield* storage.withLock(\"refresh:auth-1\", Effect.succeed(\"free\"));\n yield* expect(after === \"free\", \"lock was not released\");\n yield* storage\n .withLock(\"refresh:auth-1\", Effect.fail(new Failure(\"inside\")))\n .pipe(Effect.ignore);\n const afterFailure = yield* storage.withLock(\"refresh:auth-1\", Effect.succeed(\"free\"));\n yield* expect(afterFailure === \"free\", \"lock was not released after a failure\");\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n {\n name: \"stores readiness per domain, with or without an attachment\",\n run: run(\n Effect.gen(function* () {\n const { storage, attachment } = yield* connect(owner, \"auth-ready\");\n const now = yield* DateTime.now;\n const readiness = new Storage.Readiness({\n domain: attachment.domain,\n attachmentId: attachment.id,\n ownerId: owner.ownerId,\n overall: \"pending\",\n requirements: [\n {\n operationId: null,\n record: DnsRecord.txt({ name: \"app.example.com\", value: \"v\" }),\n status: \"missing\",\n evidence: [],\n },\n ],\n host: [],\n pendingSince: now,\n checkedAt: now,\n nextCheckAt: DateTime.add(now, { seconds: 15 }),\n });\n yield* storage.readiness.put(readiness);\n const stored = yield* storage.readiness.get(attachment.domain);\n yield* expect(\n Option.isSome(stored) && stored.value.overall === \"pending\",\n \"readiness was not stored\",\n );\n const foreign = yield* storage.readiness\n .get(attachment.domain)\n .pipe(Effect.provideService(Principal.Service, other));\n yield* expect(Option.isNone(foreign), \"readiness leaked across owners\");\n yield* storage.readiness.put(\n new Storage.Readiness({\n ...readiness,\n domain: \"observe-only.example.com\",\n attachmentId: null,\n }),\n );\n const observeOnly = yield* storage.readiness.get(\"observe-only.example.com\");\n yield* expect(\n Option.isSome(observeOnly) && observeOnly.value.attachmentId === null,\n \"readiness without an attachment was not stored\",\n );\n yield* expectReason(\n storage.readiness.put(\n new Storage.Readiness({ ...readiness, attachmentId: \"att-missing\" }),\n ),\n \"NotFound\",\n \"readiness with an unknown attachment\",\n );\n }).pipe(Effect.provideService(Principal.Service, owner)),\n ),\n },\n ];\n};\n\n/** Registers every case with `options.it` when given; always returns the cases. */\nexport const storage = (\n layer: Layer.Layer<Storage.Service, unknown>,\n options: Options = {},\n): ReadonlyArray<Case> => {\n const all = cases(layer);\n if (options.it !== undefined) {\n for (const item of all) options.it(item.name, () => Effect.runPromise(item.run));\n }\n return all;\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { CryptoFailed } from "../Reason.mjs";
|
|
2
|
+
import { DomainKitError } from "./error.mjs";
|
|
3
|
+
import { Effect } from "effect";
|
|
4
|
+
//#region src/internal/digest.ts
|
|
5
|
+
/** Deterministic JSON serialization for already schema-encoded protocol values. */
|
|
6
|
+
function stringify(value) {
|
|
7
|
+
if (value === null || typeof value !== "object") {
|
|
8
|
+
const encoded = JSON.stringify(value);
|
|
9
|
+
if (encoded === void 0) throw new TypeError("Value is not representable as JSON");
|
|
10
|
+
return encoded;
|
|
11
|
+
}
|
|
12
|
+
if (Array.isArray(value)) return `[${value.map(stringify).join(",")}]`;
|
|
13
|
+
return `{${Object.entries(value).filter(([, entry]) => entry !== void 0).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0).map(([key, entry]) => `${JSON.stringify(key)}:${stringify(entry)}`).join(",")}}`;
|
|
14
|
+
}
|
|
15
|
+
const cryptoFailed = () => new DomainKitError({ reason: new CryptoFailed({ operation: "digest" }) });
|
|
16
|
+
const sha256Hex = (value) => Effect.tryPromise({
|
|
17
|
+
try: async () => {
|
|
18
|
+
return [...new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(value)))].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
19
|
+
},
|
|
20
|
+
catch: cryptoFailed
|
|
21
|
+
});
|
|
22
|
+
//#endregion
|
|
23
|
+
export { sha256Hex, stringify };
|
|
24
|
+
|
|
25
|
+
//# sourceMappingURL=digest.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"digest.mjs","names":["Errors.DomainKitError","Reason.CryptoFailed"],"sources":["../../src/internal/digest.ts"],"sourcesContent":["import { Effect, Schema } from \"effect\";\n\nimport * as Errors from \"./error.ts\";\nimport * as Reason from \"../Reason.ts\";\n\nexport type Json =\n | null\n | boolean\n | number\n | string\n | ReadonlyArray<Json>\n | { readonly [key: string]: Json };\n\n/** Deterministic JSON serialization for already schema-encoded protocol values. */\nexport function stringify(value: unknown): string {\n if (value === null || typeof value !== \"object\") {\n const encoded = JSON.stringify(value);\n if (encoded === undefined) throw new TypeError(\"Value is not representable as JSON\");\n return encoded;\n }\n if (Array.isArray(value)) return `[${value.map(stringify).join(\",\")}]`;\n return `{${Object.entries(value as Record<string, unknown>)\n .filter(([, entry]) => entry !== undefined)\n .sort(([left], [right]) => (left < right ? -1 : left > right ? 1 : 0))\n .map(([key, entry]) => `${JSON.stringify(key)}:${stringify(entry)}`)\n .join(\",\")}}`;\n}\n\nconst cryptoFailed = () =>\n new Errors.DomainKitError({\n reason: new Reason.CryptoFailed({ operation: \"digest\" }),\n });\n\nexport const sha256Hex = (value: string): Effect.Effect<string, Errors.DomainKitError> =>\n Effect.tryPromise({\n try: async () => {\n const bytes = new Uint8Array(\n await crypto.subtle.digest(\"SHA-256\", new TextEncoder().encode(value)),\n );\n return [...bytes].map((byte) => byte.toString(16).padStart(2, \"0\")).join(\"\");\n },\n catch: cryptoFailed,\n });\n\n/** Digest of the canonical JSON encoding of `value` under `schema`. */\nexport const sha256Encoded = <S extends Schema.ConstraintEncoder<unknown>>(\n schema: S,\n value: S[\"Type\"],\n): Effect.Effect<string, Errors.DomainKitError> =>\n Effect.try({\n try: () => stringify(Schema.encodeSync(schema)(value)),\n catch: cryptoFailed,\n }).pipe(Effect.flatMap(sha256Hex));\n"],"mappings":";;;;;AAcA,SAAgB,UAAU,OAAwB;CAChD,IAAI,UAAU,QAAQ,OAAO,UAAU,UAAU;EAC/C,MAAM,UAAU,KAAK,UAAU,KAAK;EACpC,IAAI,YAAY,KAAA,GAAW,MAAM,IAAI,UAAU,oCAAoC;EACnF,OAAO;CACT;CACA,IAAI,MAAM,QAAQ,KAAK,GAAG,OAAO,IAAI,MAAM,IAAI,SAAS,CAAC,CAAC,KAAK,GAAG,EAAE;CACpE,OAAO,IAAI,OAAO,QAAQ,KAAgC,CAAC,CACxD,QAAQ,GAAG,WAAW,UAAU,KAAA,CAAS,CAAC,CAC1C,MAAM,CAAC,OAAO,CAAC,WAAY,OAAO,QAAQ,KAAK,OAAO,QAAQ,IAAI,CAAE,CAAC,CACrE,KAAK,CAAC,KAAK,WAAW,GAAG,KAAK,UAAU,GAAG,EAAE,GAAG,UAAU,KAAK,GAAG,CAAC,CACnE,KAAK,GAAG,EAAE;AACf;AAEA,MAAM,qBACJ,IAAIA,eAAsB,EACxB,QAAQ,IAAIC,aAAoB,EAAE,WAAW,SAAS,CAAC,EACzD,CAAC;AAEH,MAAa,aAAa,UACxB,OAAO,WAAW;CAChB,KAAK,YAAY;EAIf,OAAO,CAAC,GAAG,IAHO,WAChB,MAAM,OAAO,OAAO,OAAO,WAAW,IAAI,YAAY,CAAC,CAAC,OAAO,KAAK,CAAC,CAExD,CAAC,CAAC,CAAC,KAAK,SAAS,KAAK,SAAS,EAAE,CAAC,CAAC,SAAS,GAAG,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE;CAC7E;CACA,OAAO;AACT,CAAC"}
|