domainkit 0.1.0-beta.0 → 0.1.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/dist/{auth-Df9BuC25.mjs → auth-Vi-hhXbV.mjs} +24 -9
- package/dist/auth-Vi-hhXbV.mjs.map +1 -0
- package/dist/{client-DvXfJx_E.mjs → client-BnD7QLRF.mjs} +54 -21
- package/dist/client-BnD7QLRF.mjs.map +1 -0
- package/dist/{cloudflare-C_ixZsJo.mjs → cloudflare-C6ryPlCZ.mjs} +4 -2
- package/dist/{cloudflare-C_ixZsJo.mjs.map → cloudflare-C6ryPlCZ.mjs.map} +1 -1
- package/dist/{cloudflare-DClWuAm8.d.mts → cloudflare-tdjNS8vE.d.mts} +3 -3
- package/dist/cloudflare.d.mts +2 -2
- package/dist/cloudflare.mjs +1 -1
- package/dist/{dns-record-Dzo4flM5.mjs → dns-record-C_KwGPLf.mjs} +11 -1
- package/dist/dns-record-C_KwGPLf.mjs.map +1 -0
- package/dist/effect-cloudflare.d.mts +1 -1
- package/dist/effect-cloudflare.mjs +1 -1
- package/dist/effect-vercel.d.mts +1 -1
- package/dist/effect-vercel.mjs +1 -1
- package/dist/effect.d.mts +33 -23
- package/dist/effect.mjs +7 -7
- package/dist/effect.mjs.map +1 -1
- package/dist/{index-DQAjUrZu.d.mts → index-CEzk2gbZ.d.mts} +2 -2
- package/dist/{index-D1KtyjuI.d.mts → index-CS9Rl5LQ.d.mts} +2 -2
- package/dist/index.d.mts +72 -27
- package/dist/index.mjs +88 -25
- package/dist/index.mjs.map +1 -1
- package/dist/{manifest-DjH3BVB9.d.mts → manifest-CP85ePFn.d.mts} +5 -1
- package/dist/{receipt-C7PjnibZ.mjs → receipt-CGSnfZQ6.mjs} +2 -2
- package/dist/{receipt-C7PjnibZ.mjs.map → receipt-CGSnfZQ6.mjs.map} +1 -1
- package/dist/{resolver-BjZwN2JE.d.mts → resolver-BHPsADzE.d.mts} +115 -55
- package/dist/{resolver-D5nVrR10.mjs → resolver-tEXpKxmH.mjs} +69 -22
- package/dist/resolver-tEXpKxmH.mjs.map +1 -0
- package/dist/testing.d.mts +24 -18
- package/dist/testing.mjs +72 -24
- package/dist/testing.mjs.map +1 -1
- package/dist/{vercel-Vc_K_87s.d.mts → vercel-D-E2aqb1.d.mts} +3 -3
- package/dist/{vercel-BWz_9Y1v.mjs → vercel-DD89DCqH.mjs} +4 -2
- package/dist/{vercel-BWz_9Y1v.mjs.map → vercel-DD89DCqH.mjs.map} +1 -1
- package/dist/vercel.d.mts +2 -2
- package/dist/vercel.mjs +1 -1
- package/dist/{verify-DPB1OfZ5.d.mts → verify-B80xCBh2.d.mts} +457 -29
- package/dist/{verify-CeMW6XtK.mjs → verify-BjrR_4NF.mjs} +426 -134
- package/dist/verify-BjrR_4NF.mjs.map +1 -0
- package/package.json +4 -2
- package/dist/auth-Df9BuC25.mjs.map +0 -1
- package/dist/client-DvXfJx_E.mjs.map +0 -1
- package/dist/dns-record-Dzo4flM5.mjs.map +0 -1
- package/dist/resolver-D5nVrR10.mjs.map +0 -1
- package/dist/verify-CeMW6XtK.mjs.map +0 -1
|
@@ -1,64 +1,143 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
|
-
import { D as parse$1, O as Error$
|
|
3
|
-
import { a as toAsync$2, m as Service$
|
|
4
|
-
import { c as Value } from "./client-
|
|
2
|
+
import { D as parse$1, O as Error$3, T as decode$4, _ as equals, b as Service, h as decode$5, m as data, s as Observed, u as Schema$6, w as Schema$5, y as Error$4 } from "./dns-record-C_KwGPLf.mjs";
|
|
3
|
+
import { a as toAsync$2, m as Service$3, n as Service$5, o as Service$4, t as Error$5, u as Service$2, v as Service$1 } from "./resolver-tEXpKxmH.mjs";
|
|
4
|
+
import { c as Value } from "./client-BnD7QLRF.mjs";
|
|
5
5
|
import { Cause, Clock, Crypto, Effect, Layer, PlatformError, Schema, SchemaIssue, SchemaTransformation } from "effect";
|
|
6
6
|
import * as oauth from "oauth4webapi";
|
|
7
7
|
import { getDomain } from "tldts";
|
|
8
8
|
import * as DnsPacket from "@leichtgewicht/dns-packet";
|
|
9
9
|
//#region package.json
|
|
10
|
-
var version = "0.1.0
|
|
10
|
+
var version = "0.1.0";
|
|
11
11
|
//#endregion
|
|
12
12
|
//#region src/auth/connection.ts
|
|
13
13
|
var connection_exports = /* @__PURE__ */ __exportAll({
|
|
14
14
|
AuthorizationError: () => AuthorizationError$1,
|
|
15
|
-
Capability: () => Capability,
|
|
16
15
|
Grant: () => Grant,
|
|
17
|
-
Schema: () => Schema$
|
|
16
|
+
Schema: () => Schema$4,
|
|
18
17
|
assertGrant: () => assertGrant,
|
|
19
|
-
decode: () => decode$
|
|
20
|
-
encode: () => encode$
|
|
21
|
-
validate: () => validate
|
|
18
|
+
decode: () => decode$3,
|
|
19
|
+
encode: () => encode$2,
|
|
20
|
+
validate: () => validate$3
|
|
22
21
|
});
|
|
23
|
-
const Capability = Schema.Literals(["dns:read", "dns:write"]);
|
|
24
22
|
const Grant = Schema.TaggedUnion({
|
|
25
23
|
account: {},
|
|
26
|
-
domains: { domains: Schema.Array(Schema$
|
|
24
|
+
domains: { domains: Schema.Array(Schema$5) }
|
|
27
25
|
});
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
/** An owner-scoped grant bound to one provider authorization. */
|
|
27
|
+
const Schema$4 = Schema.Struct({
|
|
28
|
+
authorizationId: Schema.String,
|
|
31
29
|
createdAt: Schema.DateFromString,
|
|
32
|
-
expiresAt: Schema.NullOr(Schema.DateFromString),
|
|
33
30
|
grant: Grant,
|
|
34
31
|
id: Schema.String,
|
|
35
|
-
|
|
36
|
-
providerId: Schema.String,
|
|
37
|
-
scopes: Schema.Array(Schema.String),
|
|
38
|
-
subjectId: Schema.String
|
|
32
|
+
ownerId: Schema.String
|
|
39
33
|
});
|
|
40
|
-
const decode$
|
|
41
|
-
const validate = Effect.fn("Connection.validate")((input) => Schema.decodeUnknownEffect(Schema.toType(Schema$
|
|
42
|
-
const encode$
|
|
34
|
+
const decode$3 = Effect.fn("Connection.decode")((input) => Schema.decodeUnknownEffect(Schema$4)(input).pipe(Effect.mapError((cause) => new Error$3({ message: cause.message }))));
|
|
35
|
+
const validate$3 = Effect.fn("Connection.validate")((input) => Schema.decodeUnknownEffect(Schema.toType(Schema$4))(input).pipe(Effect.mapError((cause) => new Error$3({ message: cause.message }))));
|
|
36
|
+
const encode$2 = Schema.encodeSync(Schema$4);
|
|
43
37
|
var AuthorizationError$1 = class extends Schema.TaggedError()("AuthorizationError", { message: Schema.String }) {};
|
|
44
|
-
function assertGrant(connection, request) {
|
|
45
|
-
if (connection.
|
|
46
|
-
if (
|
|
47
|
-
if (Number.isNaN(
|
|
48
|
-
if (
|
|
38
|
+
function assertGrant(connection, authorization, request) {
|
|
39
|
+
if (connection.authorizationId !== authorization.id || authorization.providerId !== request.providerId) throw new AuthorizationError$1({ message: "Connection does not grant this provider account" });
|
|
40
|
+
if (authorization.expiresAt !== null) {
|
|
41
|
+
if (Number.isNaN(authorization.expiresAt.getTime())) throw new AuthorizationError$1({ message: "Connection expiration is invalid" });
|
|
42
|
+
if (authorization.expiresAt <= (request.now ?? /* @__PURE__ */ new Date())) throw new AuthorizationError$1({ message: "Connection has expired" });
|
|
49
43
|
}
|
|
50
|
-
if (!
|
|
44
|
+
if (!authorization.capabilities.includes(request.capability)) throw new AuthorizationError$1({ message: `Connection lacks the ${request.capability} capability` });
|
|
51
45
|
let domain;
|
|
52
46
|
try {
|
|
53
47
|
domain = parse$1(request.domain);
|
|
54
48
|
} catch (cause) {
|
|
55
|
-
if (cause instanceof Error$
|
|
56
|
-
throw new Error$
|
|
49
|
+
if (cause instanceof Error$3) throw cause;
|
|
50
|
+
throw new Error$3({ message: cause instanceof Error ? cause.message : String(cause) });
|
|
57
51
|
}
|
|
58
52
|
if (connection.grant._tag === "domains" && !connection.grant.domains.includes(domain)) throw new AuthorizationError$1({ message: "Connection does not grant this domain" });
|
|
59
53
|
return domain;
|
|
60
54
|
}
|
|
61
55
|
//#endregion
|
|
56
|
+
//#region src/auth/connection-lifecycle.ts
|
|
57
|
+
var connection_lifecycle_exports = /* @__PURE__ */ __exportAll({
|
|
58
|
+
Error: () => Error$2,
|
|
59
|
+
detach: () => detach,
|
|
60
|
+
updateGrant: () => updateGrant
|
|
61
|
+
});
|
|
62
|
+
var Error$2 = class extends Schema.TaggedError()("ConnectionLifecycleError", { message: Schema.String }) {};
|
|
63
|
+
/** Replace the grant on an owner binding after the host has obtained explicit consent. */
|
|
64
|
+
const updateGrant = Effect.fn("ConnectionLifecycle.updateGrant")(function* (input) {
|
|
65
|
+
const connectionStore = yield* Service$1;
|
|
66
|
+
const connection = yield* validate$3({
|
|
67
|
+
...input.connection,
|
|
68
|
+
grant: input.grant
|
|
69
|
+
});
|
|
70
|
+
yield* connectionStore.put(connection);
|
|
71
|
+
return connection;
|
|
72
|
+
});
|
|
73
|
+
/**
|
|
74
|
+
* Detach one owner binding. The final binding invokes provider revocation before deleting local
|
|
75
|
+
* credential and authorization state. The callback keeps provider-specific revocation outside the
|
|
76
|
+
* portable authorization model.
|
|
77
|
+
*/
|
|
78
|
+
function detach(input) {
|
|
79
|
+
return Effect.gen(function* () {
|
|
80
|
+
const authorizationStore = yield* Service$2;
|
|
81
|
+
const connectionStore = yield* Service$1;
|
|
82
|
+
const credentialStore = yield* Service$3;
|
|
83
|
+
const connection = yield* connectionStore.get(input.connectionId);
|
|
84
|
+
if (connection === null) return yield* new Error$2({ message: "Connection binding does not exist" });
|
|
85
|
+
const remainingBindings = (yield* connectionStore.listByAuthorizationId(connection.authorizationId)).filter(({ id }) => id !== connection.id);
|
|
86
|
+
if (remainingBindings.length > 0) {
|
|
87
|
+
yield* connectionStore.delete(connection.id);
|
|
88
|
+
return {
|
|
89
|
+
connection,
|
|
90
|
+
remainingBindings: remainingBindings.length,
|
|
91
|
+
revokedAuthorization: false
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
const authorization = yield* authorizationStore.get(connection.authorizationId);
|
|
95
|
+
if (authorization === null) {
|
|
96
|
+
yield* credentialStore.delete(connection.authorizationId);
|
|
97
|
+
yield* connectionStore.delete(connection.id);
|
|
98
|
+
return {
|
|
99
|
+
connection,
|
|
100
|
+
remainingBindings: 0,
|
|
101
|
+
revokedAuthorization: true
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
yield* input.revokeAuthorization(authorization);
|
|
105
|
+
yield* credentialStore.delete(authorization.id);
|
|
106
|
+
yield* authorizationStore.delete(authorization.id);
|
|
107
|
+
yield* connectionStore.delete(connection.id);
|
|
108
|
+
return {
|
|
109
|
+
connection,
|
|
110
|
+
remainingBindings: 0,
|
|
111
|
+
revokedAuthorization: true
|
|
112
|
+
};
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
//#endregion
|
|
116
|
+
//#region src/auth/authorization.ts
|
|
117
|
+
var authorization_exports = /* @__PURE__ */ __exportAll({
|
|
118
|
+
Capability: () => Capability,
|
|
119
|
+
Schema: () => Schema$3,
|
|
120
|
+
decode: () => decode$2,
|
|
121
|
+
encode: () => encode$1,
|
|
122
|
+
validate: () => validate$2
|
|
123
|
+
});
|
|
124
|
+
const Capability = Schema.Literals(["dns:read", "dns:write"]);
|
|
125
|
+
/** A provider credential authorization shared by one or more owner connections. */
|
|
126
|
+
const Schema$3 = Schema.Struct({
|
|
127
|
+
accountId: Schema.String,
|
|
128
|
+
capabilities: Schema.Array(Capability),
|
|
129
|
+
createdAt: Schema.DateFromString,
|
|
130
|
+
expiresAt: Schema.NullOr(Schema.DateFromString),
|
|
131
|
+
id: Schema.String,
|
|
132
|
+
kind: Schema.Literals(["oauth2", "token"]),
|
|
133
|
+
providerId: Schema.String,
|
|
134
|
+
scopes: Schema.Array(Schema.String),
|
|
135
|
+
subjectId: Schema.String
|
|
136
|
+
});
|
|
137
|
+
const decode$2 = Effect.fn("ProviderAuthorization.decode")((input) => Schema.decodeUnknownEffect(Schema$3)(input).pipe(Effect.mapError((cause) => new Error$3({ message: cause.message }))));
|
|
138
|
+
const validate$2 = Effect.fn("ProviderAuthorization.validate")((input) => Schema.decodeUnknownEffect(Schema.toType(Schema$3))(input).pipe(Effect.mapError((cause) => new Error$3({ message: cause.message }))));
|
|
139
|
+
const encode$1 = Schema.encodeSync(Schema$3);
|
|
140
|
+
//#endregion
|
|
62
141
|
//#region src/plan/canonical-json.ts
|
|
63
142
|
var canonical_json_exports = /* @__PURE__ */ __exportAll({
|
|
64
143
|
CryptoError: () => CryptoError,
|
|
@@ -120,7 +199,7 @@ var oauth_exports = /* @__PURE__ */ __exportAll({
|
|
|
120
199
|
});
|
|
121
200
|
function beginProgram(input) {
|
|
122
201
|
return Effect.gen(function* () {
|
|
123
|
-
const stateStore = yield* Service$
|
|
202
|
+
const stateStore = yield* Service$4;
|
|
124
203
|
const cryptoService = yield* Crypto.Crypto;
|
|
125
204
|
const now = yield* Clock.currentTimeMillis;
|
|
126
205
|
const state = base64Url(yield* cryptoBytes(cryptoService, 32));
|
|
@@ -143,6 +222,7 @@ function beginProgram(input) {
|
|
|
143
222
|
expiresAt: new Date(now + (input.ttlMs ?? 6e5)),
|
|
144
223
|
grant: input.grant,
|
|
145
224
|
method: input.method,
|
|
225
|
+
ownerId: input.ownerId,
|
|
146
226
|
redirectUri: input.redirectUri,
|
|
147
227
|
stateHash,
|
|
148
228
|
subjectId: input.subjectId
|
|
@@ -152,8 +232,9 @@ function beginProgram(input) {
|
|
|
152
232
|
}
|
|
153
233
|
function completeProgram(input) {
|
|
154
234
|
return Effect.gen(function* () {
|
|
155
|
-
const stateStore = yield* Service$
|
|
156
|
-
const
|
|
235
|
+
const stateStore = yield* Service$4;
|
|
236
|
+
const authorizationStore = yield* Service$2;
|
|
237
|
+
const connectionStore = yield* Service$1;
|
|
157
238
|
const credentialStore = yield* Service$3;
|
|
158
239
|
const cryptoService = yield* Crypto.Crypto;
|
|
159
240
|
const state = input.callbackUrl.searchParams.get("state");
|
|
@@ -172,42 +253,54 @@ function completeProgram(input) {
|
|
|
172
253
|
});
|
|
173
254
|
const accessToken = Value.from(tokens.access_token);
|
|
174
255
|
const subject = yield* input.resolveSubject(tokens, accessToken);
|
|
175
|
-
const
|
|
176
|
-
const
|
|
256
|
+
const existingAuthorization = yield* authorizationStore.findByProviderAccount(input.providerId, subject.accountId);
|
|
257
|
+
const authorizationId = existingAuthorization?.id ?? (yield* cryptoService.randomUUIDv4.pipe(Effect.mapError((cause) => new CryptoError({ message: cause.message }))));
|
|
258
|
+
const authorization = yield* validate$2({
|
|
177
259
|
accountId: subject.accountId,
|
|
178
260
|
capabilities: [...continuation.method.capabilities],
|
|
179
|
-
createdAt: new Date(now),
|
|
261
|
+
createdAt: existingAuthorization?.createdAt ?? new Date(now),
|
|
180
262
|
expiresAt: subject.expiresAt,
|
|
181
|
-
|
|
182
|
-
id,
|
|
263
|
+
id: authorizationId,
|
|
183
264
|
kind: "oauth2",
|
|
184
265
|
providerId: input.providerId,
|
|
185
266
|
scopes: (tokens.scope ?? continuation.method.scopes.join(" ")).split(" ").filter(Boolean),
|
|
186
267
|
subjectId: continuation.subjectId
|
|
187
268
|
});
|
|
188
|
-
yield*
|
|
269
|
+
const existingConnection = yield* connectionStore.find(continuation.ownerId, authorization.id);
|
|
270
|
+
const connection = yield* validate$3({
|
|
271
|
+
authorizationId: authorization.id,
|
|
272
|
+
createdAt: existingConnection?.createdAt ?? new Date(now),
|
|
273
|
+
grant: continuation.grant,
|
|
274
|
+
id: existingConnection?.id ?? (yield* cryptoService.randomUUIDv4.pipe(Effect.mapError((cause) => new CryptoError({ message: cause.message })))),
|
|
275
|
+
ownerId: continuation.ownerId
|
|
276
|
+
});
|
|
277
|
+
yield* credentialStore.put(authorization.id, {
|
|
189
278
|
accessToken,
|
|
190
279
|
refreshToken: tokens.refresh_token === void 0 ? null : Value.from(tokens.refresh_token),
|
|
191
280
|
tokenType: tokens.token_type
|
|
192
281
|
});
|
|
282
|
+
yield* authorizationStore.put(authorization);
|
|
193
283
|
yield* connectionStore.put(connection);
|
|
194
|
-
return
|
|
284
|
+
return {
|
|
285
|
+
authorization,
|
|
286
|
+
connection
|
|
287
|
+
};
|
|
195
288
|
});
|
|
196
289
|
}
|
|
197
290
|
function refreshProgram(input) {
|
|
198
291
|
return Effect.gen(function* () {
|
|
199
292
|
const credentialStore = yield* Service$3;
|
|
200
|
-
const credential = yield* requireCredential(input.
|
|
293
|
+
const credential = yield* requireCredential(input.authorization.id, credentialStore);
|
|
201
294
|
const refreshToken = credential.refreshToken;
|
|
202
295
|
if (refreshToken === null) return yield* new AuthorizationError$1({ message: "Connection has no refresh token" });
|
|
203
296
|
const as = input.method.authorizationServer;
|
|
204
297
|
const client = { client_id: input.client.clientId };
|
|
205
298
|
const authentication = yield* effectClientAuthentication(input.method, input.client);
|
|
206
|
-
const tokens = yield* providerRequest(input.
|
|
299
|
+
const tokens = yield* providerRequest(input.authorization.providerId, async (signal) => {
|
|
207
300
|
const response = await oauth.refreshTokenGrantRequest(as, client, authentication, refreshToken.expose(), requestOptions(input.fetch, signal));
|
|
208
301
|
return oauth.processRefreshTokenResponse(as, client, response);
|
|
209
302
|
});
|
|
210
|
-
yield* credentialStore.put(input.
|
|
303
|
+
yield* credentialStore.put(input.authorization.id, {
|
|
211
304
|
accessToken: Value.from(tokens.access_token),
|
|
212
305
|
refreshToken: tokens.refresh_token === void 0 ? credential.refreshToken : Value.from(tokens.refresh_token),
|
|
213
306
|
tokenType: tokens.token_type
|
|
@@ -218,30 +311,30 @@ function revokeProgram(input) {
|
|
|
218
311
|
return Effect.gen(function* () {
|
|
219
312
|
if (input.method.authorizationServer.revocation_endpoint === void 0) return yield* new AuthorizationError$1({ message: "Provider does not advertise token revocation" });
|
|
220
313
|
const credentialStore = yield* Service$3;
|
|
221
|
-
const credential = yield* requireCredential(input.
|
|
314
|
+
const credential = yield* requireCredential(input.authorization.id, credentialStore);
|
|
222
315
|
const as = input.method.authorizationServer;
|
|
223
316
|
const authentication = yield* effectClientAuthentication(input.method, input.client);
|
|
224
|
-
yield* providerRequest(input.
|
|
317
|
+
yield* providerRequest(input.authorization.providerId, async (signal) => {
|
|
225
318
|
const response = await oauth.revocationRequest(as, { client_id: input.client.clientId }, authentication, credential.accessToken.expose(), requestOptions(input.fetch, signal));
|
|
226
319
|
await oauth.processRevocationResponse(response);
|
|
227
320
|
});
|
|
228
|
-
yield* credentialStore.delete(input.
|
|
321
|
+
yield* credentialStore.delete(input.authorization.id);
|
|
229
322
|
});
|
|
230
323
|
}
|
|
231
324
|
function effectClientAuthentication(method, client) {
|
|
232
325
|
return Effect.try({
|
|
233
326
|
try: () => clientAuthentication(method, client),
|
|
234
|
-
catch: (cause) => cause instanceof Error$
|
|
327
|
+
catch: (cause) => cause instanceof Error$3 ? cause : new Error$3({ message: "OAuth client authentication is invalid" })
|
|
235
328
|
});
|
|
236
329
|
}
|
|
237
330
|
function clientAuthentication(method, client) {
|
|
238
331
|
switch (method.clientAuth) {
|
|
239
332
|
case "none": return oauth.None();
|
|
240
333
|
case "client_secret_basic":
|
|
241
|
-
if (client.clientSecret === void 0) throw new Error$
|
|
334
|
+
if (client.clientSecret === void 0) throw new Error$3({ message: "Client secret is required" });
|
|
242
335
|
return oauth.ClientSecretBasic(client.clientSecret.expose());
|
|
243
336
|
case "client_secret_post":
|
|
244
|
-
if (client.clientSecret === void 0) throw new Error$
|
|
337
|
+
if (client.clientSecret === void 0) throw new Error$3({ message: "Client secret is required" });
|
|
245
338
|
return oauth.ClientSecretPost(client.clientSecret.expose());
|
|
246
339
|
}
|
|
247
340
|
}
|
|
@@ -260,7 +353,7 @@ function requireCredential(connectionId, store) {
|
|
|
260
353
|
function providerRequest(providerId, request) {
|
|
261
354
|
return Effect.tryPromise({
|
|
262
355
|
try: request,
|
|
263
|
-
catch: (cause) => cause instanceof AuthorizationError$1 || cause instanceof Error$
|
|
356
|
+
catch: (cause) => cause instanceof AuthorizationError$1 || cause instanceof Error$3 ? cause : new Error$4({
|
|
264
357
|
cause,
|
|
265
358
|
message: safeOAuthMessage(cause),
|
|
266
359
|
operation: "oauthRequest",
|
|
@@ -320,36 +413,48 @@ const Schema$2 = Schema.Struct({
|
|
|
320
413
|
methods: Schema.Array(Method),
|
|
321
414
|
providerId: Schema.String
|
|
322
415
|
});
|
|
323
|
-
const decode$1 = Effect.fn("ProviderAuth.decode")((input) => Schema.decodeUnknownEffect(Schema$2)(input).pipe(Effect.mapError((cause) => new Error$
|
|
416
|
+
const decode$1 = Effect.fn("ProviderAuth.decode")((input) => Schema.decodeUnknownEffect(Schema$2)(input).pipe(Effect.mapError((cause) => new Error$3({ message: cause.message }))));
|
|
324
417
|
//#endregion
|
|
325
418
|
//#region src/auth/token.ts
|
|
326
419
|
var token_exports = /* @__PURE__ */ __exportAll({ connect: () => connect });
|
|
327
420
|
const connect = Effect.fn("TokenConnection.connect")(function* (input) {
|
|
328
|
-
const
|
|
421
|
+
const authorizationStore = yield* Service$2;
|
|
422
|
+
const connectionStore = yield* Service$1;
|
|
329
423
|
const credentialStore = yield* Service$3;
|
|
330
424
|
const cryptoService = yield* Crypto.Crypto;
|
|
331
425
|
const validation = yield* input.validate(input.token);
|
|
332
426
|
const createdAt = new Date(yield* Clock.currentTimeMillis);
|
|
333
|
-
const
|
|
334
|
-
const
|
|
427
|
+
const existingAuthorization = yield* authorizationStore.findByProviderAccount(input.providerId, validation.accountId);
|
|
428
|
+
const authorization = yield* validate$2({
|
|
335
429
|
accountId: validation.accountId,
|
|
336
430
|
capabilities: [...validation.capabilities],
|
|
337
|
-
createdAt,
|
|
431
|
+
createdAt: existingAuthorization?.createdAt ?? createdAt,
|
|
338
432
|
expiresAt: validation.expiresAt,
|
|
339
|
-
|
|
340
|
-
id,
|
|
433
|
+
id: existingAuthorization?.id ?? (yield* cryptoService.randomUUIDv4.pipe(Effect.mapError((cause) => new CryptoError({ message: cause.message })))),
|
|
341
434
|
kind: "token",
|
|
342
435
|
providerId: input.providerId,
|
|
343
436
|
scopes: [...validation.scopes],
|
|
344
437
|
subjectId: input.subjectId
|
|
345
438
|
});
|
|
346
|
-
yield*
|
|
439
|
+
const existingConnection = yield* connectionStore.find(input.ownerId, authorization.id);
|
|
440
|
+
const connection = yield* validate$3({
|
|
441
|
+
authorizationId: authorization.id,
|
|
442
|
+
createdAt: existingConnection?.createdAt ?? createdAt,
|
|
443
|
+
grant: input.grant,
|
|
444
|
+
id: existingConnection?.id ?? (yield* cryptoService.randomUUIDv4.pipe(Effect.mapError((cause) => new CryptoError({ message: cause.message })))),
|
|
445
|
+
ownerId: input.ownerId
|
|
446
|
+
});
|
|
447
|
+
yield* credentialStore.put(authorization.id, {
|
|
347
448
|
accessToken: input.token,
|
|
348
449
|
refreshToken: null,
|
|
349
450
|
tokenType: "bearer"
|
|
350
451
|
});
|
|
452
|
+
yield* authorizationStore.put(authorization);
|
|
351
453
|
yield* connectionStore.put(connection);
|
|
352
|
-
return
|
|
454
|
+
return {
|
|
455
|
+
authorization,
|
|
456
|
+
connection
|
|
457
|
+
};
|
|
353
458
|
});
|
|
354
459
|
//#endregion
|
|
355
460
|
//#region src/discovery/zones.ts
|
|
@@ -357,7 +462,7 @@ var zones_exports = /* @__PURE__ */ __exportAll({ candidates: () => candidates }
|
|
|
357
462
|
function candidates(input) {
|
|
358
463
|
const domain = parse$1(input);
|
|
359
464
|
const registrable = getDomain(domain, { allowPrivateDomains: true });
|
|
360
|
-
if (registrable === null) throw new Error$
|
|
465
|
+
if (registrable === null) throw new Error$3({ message: "Domain does not have a registrable DNS zone" });
|
|
361
466
|
const labels = domain.split(".");
|
|
362
467
|
const registrableLabels = registrable.split(".").length;
|
|
363
468
|
const values = [];
|
|
@@ -375,9 +480,9 @@ const Evidence = Schema.Struct({
|
|
|
375
480
|
accountId: Schema.String,
|
|
376
481
|
connectionId: Schema.String,
|
|
377
482
|
decisiveNameserverMatch: Schema.Boolean,
|
|
378
|
-
matchedNameservers: Schema.Array(Schema$
|
|
483
|
+
matchedNameservers: Schema.Array(Schema$5),
|
|
379
484
|
providerId: Schema.String,
|
|
380
|
-
zone: Schema$
|
|
485
|
+
zone: Schema$5
|
|
381
486
|
});
|
|
382
487
|
const Selection = Schema.TaggedUnion({
|
|
383
488
|
selected: {
|
|
@@ -397,8 +502,7 @@ function select(input) {
|
|
|
397
502
|
const eligible = input.connectedZones.filter((connected) => {
|
|
398
503
|
if (!zoneCandidates.has(parse$1(connected.zone))) return false;
|
|
399
504
|
try {
|
|
400
|
-
assertGrant(connected.connection, {
|
|
401
|
-
accountId: connected.accountId,
|
|
505
|
+
assertGrant(connected.connection, connected.authorization, {
|
|
402
506
|
capability: "dns:read",
|
|
403
507
|
domain,
|
|
404
508
|
...input.now === void 0 ? {} : { now: input.now },
|
|
@@ -412,7 +516,7 @@ function select(input) {
|
|
|
412
516
|
const evidence = eligible.map((connected) => evidenceFor(connected, authoritative));
|
|
413
517
|
if (input.explicit !== void 0) {
|
|
414
518
|
const explicitZone = parse$1(input.explicit.zone);
|
|
415
|
-
const index = eligible.findIndex((connected) => connected.providerId === input.explicit?.providerId && connected.accountId === input.explicit.accountId && parse$1(connected.zone) === explicitZone);
|
|
519
|
+
const index = eligible.findIndex((connected) => connected.providerId === input.explicit?.providerId && connected.authorization.accountId === input.explicit.accountId && parse$1(connected.zone) === explicitZone);
|
|
416
520
|
if (index < 0) throw new AuthorizationError$1({ message: "Explicit provider zone is not connected or granted" });
|
|
417
521
|
const candidate = evidence[index];
|
|
418
522
|
if (candidate === void 0) throw new Error("Eligible provider evidence is missing");
|
|
@@ -440,7 +544,7 @@ function evidenceFor(connected, authoritative) {
|
|
|
440
544
|
const configured = new Set(nameserverSet(connected.nameservers));
|
|
441
545
|
const matchedNameservers = authoritative.filter((nameserver) => configured.has(nameserver));
|
|
442
546
|
return {
|
|
443
|
-
accountId: connected.accountId,
|
|
547
|
+
accountId: connected.authorization.accountId,
|
|
444
548
|
connectionId: connected.connection.id,
|
|
445
549
|
decisiveNameserverMatch: authoritative.length > 0 && matchedNameservers.length === authoritative.length,
|
|
446
550
|
matchedNameservers,
|
|
@@ -454,46 +558,46 @@ function nameserverSet(nameservers) {
|
|
|
454
558
|
//#endregion
|
|
455
559
|
//#region src/plan/types.ts
|
|
456
560
|
var types_exports = /* @__PURE__ */ __exportAll({
|
|
457
|
-
Authorization: () => Authorization,
|
|
458
|
-
Operation: () => Operation,
|
|
459
|
-
Receipt: () => Receipt,
|
|
561
|
+
Authorization: () => Authorization$1,
|
|
562
|
+
Operation: () => Operation$1,
|
|
563
|
+
Receipt: () => Receipt$1,
|
|
460
564
|
Schema: () => Schema$1,
|
|
461
565
|
decode: () => decode,
|
|
462
566
|
decodeReceipt: () => decodeReceipt,
|
|
463
567
|
encode: () => encode,
|
|
464
568
|
encodeReceipt: () => encodeReceipt
|
|
465
569
|
});
|
|
466
|
-
const Operation = Schema.TaggedUnion({
|
|
570
|
+
const Operation$1 = Schema.TaggedUnion({
|
|
467
571
|
create: {
|
|
468
572
|
id: Schema.String,
|
|
469
|
-
requirement: Schema$
|
|
573
|
+
requirement: Schema$6
|
|
470
574
|
},
|
|
471
575
|
noop: {
|
|
472
576
|
id: Schema.String,
|
|
473
|
-
requirement: Schema$
|
|
577
|
+
requirement: Schema$6,
|
|
474
578
|
ttlDrift: Schema.Boolean
|
|
475
579
|
},
|
|
476
580
|
conflict: {
|
|
477
581
|
existing: Schema.Array(Observed),
|
|
478
582
|
id: Schema.String,
|
|
479
583
|
reason: Schema.String,
|
|
480
|
-
requirement: Schema$
|
|
584
|
+
requirement: Schema$6
|
|
481
585
|
}
|
|
482
586
|
});
|
|
483
587
|
const Schema$1 = Schema.Struct({
|
|
484
588
|
digest: Schema.String,
|
|
485
|
-
operations: Schema.Array(Operation),
|
|
589
|
+
operations: Schema.Array(Operation$1),
|
|
486
590
|
providerId: Schema.String,
|
|
487
591
|
version: Schema.Literal("domainkit.dns-plan.v1"),
|
|
488
|
-
zone: Schema$
|
|
592
|
+
zone: Schema$5
|
|
489
593
|
});
|
|
490
|
-
const Authorization = Schema.Struct({
|
|
594
|
+
const Authorization$1 = Schema.Struct({
|
|
491
595
|
allowPartial: Schema.Boolean,
|
|
492
596
|
operationIds: Schema.Array(Schema.String),
|
|
493
597
|
planDigest: Schema.String,
|
|
494
598
|
version: Schema.Literal("domainkit.plan-authorization.v1")
|
|
495
599
|
});
|
|
496
|
-
const Receipt = Schema.Struct({
|
|
600
|
+
const Receipt$1 = Schema.Struct({
|
|
497
601
|
appliedAt: Schema.DateFromString,
|
|
498
602
|
operations: Schema.Array(Schema.Struct({
|
|
499
603
|
operationId: Schema.String,
|
|
@@ -503,12 +607,12 @@ const Receipt = Schema.Struct({
|
|
|
503
607
|
providerId: Schema.String,
|
|
504
608
|
status: Schema.Literals(["complete", "partial"]),
|
|
505
609
|
version: Schema.Literal("domainkit.apply-receipt.v1"),
|
|
506
|
-
zone: Schema$
|
|
610
|
+
zone: Schema$5
|
|
507
611
|
});
|
|
508
|
-
const decode = Effect.fn("DnsPlan.decode")((input) => Schema.decodeUnknownEffect(Schema$1)(input).pipe(Effect.mapError((cause) => new Error$
|
|
612
|
+
const decode = Effect.fn("DnsPlan.decode")((input) => Schema.decodeUnknownEffect(Schema$1)(input).pipe(Effect.mapError((cause) => new Error$3({ message: cause.message }))));
|
|
509
613
|
const encode = Schema.encodeSync(Schema$1);
|
|
510
|
-
const decodeReceipt = Effect.fn("DnsPlan.decodeReceipt")((input) => Schema.decodeUnknownEffect(Receipt)(input).pipe(Effect.mapError((cause) => new Error$
|
|
511
|
-
const encodeReceipt = Schema.encodeSync(Receipt);
|
|
614
|
+
const decodeReceipt = Effect.fn("DnsPlan.decodeReceipt")((input) => Schema.decodeUnknownEffect(Receipt$1)(input).pipe(Effect.mapError((cause) => new Error$3({ message: cause.message }))));
|
|
615
|
+
const encodeReceipt = Schema.encodeSync(Receipt$1);
|
|
512
616
|
//#endregion
|
|
513
617
|
//#region src/plan/plan.ts
|
|
514
618
|
var plan_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -516,18 +620,19 @@ var plan_exports = /* @__PURE__ */ __exportAll({
|
|
|
516
620
|
ConflictError: () => ConflictError,
|
|
517
621
|
PartialApplyError: () => PartialApplyError,
|
|
518
622
|
StaleError: () => StaleError,
|
|
519
|
-
apply: () => apply,
|
|
520
|
-
authorize: () => authorize$
|
|
521
|
-
create: () => create,
|
|
522
|
-
renderManualInstructions: () => renderManualInstructions
|
|
623
|
+
apply: () => apply$1,
|
|
624
|
+
authorize: () => authorize$2,
|
|
625
|
+
create: () => create$1,
|
|
626
|
+
renderManualInstructions: () => renderManualInstructions,
|
|
627
|
+
validate: () => validate$1
|
|
523
628
|
});
|
|
524
|
-
const UnsignedPlan = Schema.Struct({
|
|
525
|
-
operations: Schema.Array(Operation),
|
|
629
|
+
const UnsignedPlan$1 = Schema.Struct({
|
|
630
|
+
operations: Schema.Array(Operation$1),
|
|
526
631
|
providerId: Schema.String,
|
|
527
632
|
version: Schema.Literal("domainkit.dns-plan.v1"),
|
|
528
|
-
zone: Schema$
|
|
633
|
+
zone: Schema$5
|
|
529
634
|
});
|
|
530
|
-
const RequirementDigest = Schema.Struct({ requirement: Schema$
|
|
635
|
+
const RequirementDigest = Schema.Struct({ requirement: Schema$6 });
|
|
531
636
|
var ConflictError = class extends Schema.TaggedError()("PlanConflictError", { operationIds: Schema.Array(Schema.String) }) {};
|
|
532
637
|
var AuthorizationError = class extends Schema.TaggedError()("AuthorizationError", { message: Schema.String }) {};
|
|
533
638
|
var StaleError = class extends Schema.TaggedError()("StalePlanError", {
|
|
@@ -544,13 +649,13 @@ var PartialApplyError = class extends Schema.TaggedError()("PartialApplyError",
|
|
|
544
649
|
]),
|
|
545
650
|
failedOperationId: Schema.String,
|
|
546
651
|
message: Schema.String,
|
|
547
|
-
receipt: Receipt
|
|
652
|
+
receipt: Receipt$1
|
|
548
653
|
}) {};
|
|
549
654
|
/** Builds a deterministic DNS plan through the provider in the Effect environment. */
|
|
550
|
-
const create = Effect.fn("Provisioning.create")(function* (input) {
|
|
655
|
+
const create$1 = Effect.fn("Provisioning.create")(function* (input) {
|
|
551
656
|
const provider = yield* Service;
|
|
552
|
-
const zone = yield* decode$
|
|
553
|
-
const requirements = (yield* Effect.forEach(input.requirements, decode$
|
|
657
|
+
const zone = yield* decode$4(input.zone);
|
|
658
|
+
const requirements = (yield* Effect.forEach(input.requirements, decode$5, { concurrency: "unbounded" })).sort(compareRecords);
|
|
554
659
|
const existing = [...yield* provider.listRecords(zone)].sort(compareRecords);
|
|
555
660
|
const { operations } = yield* Effect.reduce(requirements, () => ({
|
|
556
661
|
operations: [],
|
|
@@ -567,12 +672,12 @@ const create = Effect.fn("Provisioning.create")(function* (input) {
|
|
|
567
672
|
};
|
|
568
673
|
return {
|
|
569
674
|
...unsigned,
|
|
570
|
-
digest: yield* sha256Encoded(UnsignedPlan, unsigned)
|
|
675
|
+
digest: yield* sha256Encoded(UnsignedPlan$1, unsigned)
|
|
571
676
|
};
|
|
572
677
|
});
|
|
573
678
|
/** Authorizes all or a selected subset of create operations in a digest-bound plan. */
|
|
574
|
-
const authorize$
|
|
575
|
-
yield*
|
|
679
|
+
const authorize$2 = Effect.fn("Provisioning.authorize")(function* (plan, operationIds, options = {}) {
|
|
680
|
+
yield* validate$1(plan);
|
|
576
681
|
const createIds = plan.operations.filter((operation) => operation._tag === "create").map(({ id }) => id);
|
|
577
682
|
const selected = [...new Set(operationIds ?? createIds)].sort();
|
|
578
683
|
const unknown = selected.filter((id) => !createIds.includes(id));
|
|
@@ -586,20 +691,20 @@ const authorize$1 = Effect.fn("Provisioning.authorize")(function* (plan, operati
|
|
|
586
691
|
};
|
|
587
692
|
});
|
|
588
693
|
/** Applies approved creates sequentially after full and per-operation revalidation. */
|
|
589
|
-
const apply = Effect.fn("Provisioning.apply")(function* (input) {
|
|
694
|
+
const apply$1 = Effect.fn("Provisioning.apply")(function* (input) {
|
|
590
695
|
const { authorization, plan } = input;
|
|
591
696
|
const provider = yield* Service;
|
|
592
|
-
yield*
|
|
697
|
+
yield* validate$1(plan);
|
|
593
698
|
if (authorization.planDigest !== plan.digest) return yield* new AuthorizationError({ message: "Authorization belongs to a different plan" });
|
|
594
699
|
if (provider.id !== plan.providerId) return yield* new AuthorizationError({ message: "Provider does not match the approved plan" });
|
|
595
700
|
const conflicts = plan.operations.filter((operation) => operation._tag === "conflict");
|
|
596
701
|
if (conflicts.length > 0) return yield* new ConflictError({ operationIds: conflicts.map(({ id }) => id) });
|
|
597
|
-
const creates = plan.operations.filter(Operation.guards.create);
|
|
702
|
+
const creates = plan.operations.filter(Operation$1.guards.create);
|
|
598
703
|
const approved = new Set(authorization.operationIds);
|
|
599
704
|
const unknown = authorization.operationIds.filter((id) => !creates.some((operation) => operation.id === id));
|
|
600
705
|
if (unknown.length > 0) return yield* new AuthorizationError({ message: `Authorization contains unknown operations: ${unknown.join(", ")}` });
|
|
601
706
|
if (!authorization.allowPartial && creates.some(({ id }) => !approved.has(id))) return yield* new AuthorizationError({ message: "Authorization does not cover every create operation" });
|
|
602
|
-
const currentPlan = yield* create({
|
|
707
|
+
const currentPlan = yield* create$1({
|
|
603
708
|
requirements: plan.operations.map(({ requirement }) => requirement),
|
|
604
709
|
zone: plan.zone
|
|
605
710
|
});
|
|
@@ -617,14 +722,14 @@ const apply = Effect.fn("Provisioning.apply")(function* (input) {
|
|
|
617
722
|
operationId: operation.id,
|
|
618
723
|
providerRecordId: result.providerRecordId
|
|
619
724
|
});
|
|
620
|
-
}).pipe(Effect.catch((failure) => operations.length === 0 ? Effect.fail(failure) : makeReceipt(input, operations, "partial").pipe(Effect.flatMap((receipt) => Effect.fail(new PartialApplyError({
|
|
725
|
+
}).pipe(Effect.catch((failure) => operations.length === 0 ? Effect.fail(failure) : makeReceipt$1(input, operations, "partial").pipe(Effect.flatMap((receipt) => Effect.fail(new PartialApplyError({
|
|
621
726
|
causeTag: failure._tag,
|
|
622
727
|
failedOperationId: operation.id,
|
|
623
728
|
message: failureMessage(failure),
|
|
624
729
|
receipt
|
|
625
730
|
}))))), Effect.catchCause((cause) => {
|
|
626
731
|
if (!Cause.hasInterruptsOnly(cause) || operations.length === 0) return Effect.failCause(cause);
|
|
627
|
-
return makeReceipt(input, operations, "partial").pipe(Effect.flatMap((receipt) => Effect.fail(new PartialApplyError({
|
|
732
|
+
return makeReceipt$1(input, operations, "partial").pipe(Effect.flatMap((receipt) => Effect.fail(new PartialApplyError({
|
|
628
733
|
causeTag: "Interrupted",
|
|
629
734
|
failedOperationId: operation.id,
|
|
630
735
|
message: "DNS apply was interrupted after confirmed writes",
|
|
@@ -632,7 +737,7 @@ const apply = Effect.fn("Provisioning.apply")(function* (input) {
|
|
|
632
737
|
}))));
|
|
633
738
|
}));
|
|
634
739
|
}
|
|
635
|
-
return yield* makeReceipt(input, operations, "complete");
|
|
740
|
+
return yield* makeReceipt$1(input, operations, "complete");
|
|
636
741
|
});
|
|
637
742
|
function renderManualInstructions(plan) {
|
|
638
743
|
return plan.operations.map((operation) => {
|
|
@@ -641,7 +746,7 @@ function renderManualInstructions(plan) {
|
|
|
641
746
|
return `${operation._tag.toUpperCase()} ${record._tag} ${record.name} ${data$2}`.trim();
|
|
642
747
|
});
|
|
643
748
|
}
|
|
644
|
-
function makeReceipt(input, operations, status) {
|
|
749
|
+
function makeReceipt$1(input, operations, status) {
|
|
645
750
|
return Clock.currentTimeMillis.pipe(Effect.map((now) => ({
|
|
646
751
|
appliedAt: new Date(now),
|
|
647
752
|
operations,
|
|
@@ -653,7 +758,7 @@ function makeReceipt(input, operations, status) {
|
|
|
653
758
|
})));
|
|
654
759
|
}
|
|
655
760
|
function assertStillCreatable(plan, operation) {
|
|
656
|
-
return create({
|
|
761
|
+
return create$1({
|
|
657
762
|
requirements: [operation.requirement],
|
|
658
763
|
zone: plan.zone
|
|
659
764
|
}).pipe(Effect.flatMap((currentPlan) => {
|
|
@@ -697,14 +802,14 @@ function reconcileRequirement(requirement, allExisting) {
|
|
|
697
802
|
};
|
|
698
803
|
});
|
|
699
804
|
}
|
|
700
|
-
function
|
|
805
|
+
function validate$1(plan) {
|
|
701
806
|
const unsigned = {
|
|
702
807
|
operations: plan.operations,
|
|
703
808
|
providerId: plan.providerId,
|
|
704
809
|
version: plan.version,
|
|
705
810
|
zone: plan.zone
|
|
706
811
|
};
|
|
707
|
-
return sha256Encoded(UnsignedPlan, unsigned).pipe(Effect.flatMap((digest) => digest === plan.digest ? Effect.void : Effect.fail(new AuthorizationError({ message: "Plan digest does not match its contents" }))));
|
|
812
|
+
return sha256Encoded(UnsignedPlan$1, unsigned).pipe(Effect.flatMap((digest) => digest === plan.digest ? Effect.void : Effect.fail(new AuthorizationError({ message: "Plan digest does not match its contents" }))));
|
|
708
813
|
}
|
|
709
814
|
function failureMessage(failure) {
|
|
710
815
|
return failure._tag === "StalePlanError" ? "DNS state changed while applying the approved plan" : failure.message;
|
|
@@ -718,20 +823,207 @@ function compareRecords(left, right) {
|
|
|
718
823
|
}
|
|
719
824
|
//#endregion
|
|
720
825
|
//#region src/plan/connection-authorization.ts
|
|
721
|
-
var connection_authorization_exports = /* @__PURE__ */ __exportAll({ authorize: () => authorize });
|
|
722
|
-
const authorize = Effect.fn("ConnectionAuthorization.authorize")(function* (input) {
|
|
723
|
-
yield* Effect.try({
|
|
724
|
-
try: () => assertGrant(input.connection, {
|
|
725
|
-
accountId: input.accountId,
|
|
826
|
+
var connection_authorization_exports = /* @__PURE__ */ __exportAll({ authorize: () => authorize$1 });
|
|
827
|
+
const authorize$1 = Effect.fn("ConnectionAuthorization.authorize")(function* (input) {
|
|
828
|
+
const domain = yield* Effect.try({
|
|
829
|
+
try: () => assertGrant(input.connection, input.authorization, {
|
|
726
830
|
capability: "dns:write",
|
|
727
|
-
domain: input.
|
|
831
|
+
domain: input.domain,
|
|
728
832
|
providerId: input.plan.providerId
|
|
729
833
|
}),
|
|
730
|
-
catch: (cause) => cause instanceof AuthorizationError$1 || cause instanceof Error$
|
|
834
|
+
catch: (cause) => cause instanceof AuthorizationError$1 || cause instanceof Error$3 ? cause : new AuthorizationError$1({ message: "Connection grant is invalid" })
|
|
731
835
|
});
|
|
732
|
-
|
|
836
|
+
const authorization = yield* authorize$2(input.plan, input.operationIds, input.allowPartial === void 0 ? {} : { allowPartial: input.allowPartial });
|
|
837
|
+
const selected = new Set(authorization.operationIds);
|
|
838
|
+
if (input.plan.operations.filter((operation) => operation._tag === "create" && selected.has(operation.id) && operation.requirement.name !== domain && !operation.requirement.name.endsWith(`.${domain}`)).length > 0) return yield* new AuthorizationError$1({ message: `Plan operations are outside the granted domain ${domain}` });
|
|
839
|
+
return authorization;
|
|
733
840
|
});
|
|
734
841
|
//#endregion
|
|
842
|
+
//#region src/plan/deletion.ts
|
|
843
|
+
var deletion_exports = /* @__PURE__ */ __exportAll({
|
|
844
|
+
Authorization: () => Authorization,
|
|
845
|
+
Error: () => Error$1,
|
|
846
|
+
Operation: () => Operation,
|
|
847
|
+
PartialError: () => PartialError,
|
|
848
|
+
Plan: () => Plan,
|
|
849
|
+
Receipt: () => Receipt,
|
|
850
|
+
apply: () => apply,
|
|
851
|
+
authorize: () => authorize,
|
|
852
|
+
create: () => create,
|
|
853
|
+
validate: () => validate
|
|
854
|
+
});
|
|
855
|
+
const Operation = Schema.Struct({
|
|
856
|
+
id: Schema.String,
|
|
857
|
+
providerRecordId: Schema.String,
|
|
858
|
+
record: Schema$6
|
|
859
|
+
});
|
|
860
|
+
const UnsignedPlan = Schema.Struct({
|
|
861
|
+
createdAt: Schema.DateFromString,
|
|
862
|
+
expiresAt: Schema.DateFromString,
|
|
863
|
+
operations: Schema.Array(Operation),
|
|
864
|
+
providerId: Schema.String,
|
|
865
|
+
sourcePlanDigest: Schema.String,
|
|
866
|
+
version: Schema.Literal("domainkit.deletion-plan.v1"),
|
|
867
|
+
zone: Schema$5
|
|
868
|
+
});
|
|
869
|
+
const Plan = Schema.Struct({
|
|
870
|
+
...UnsignedPlan.fields,
|
|
871
|
+
digest: Schema.String
|
|
872
|
+
});
|
|
873
|
+
const Authorization = Schema.Struct({
|
|
874
|
+
operationIds: Schema.Array(Schema.String),
|
|
875
|
+
planDigest: Schema.String,
|
|
876
|
+
version: Schema.Literal("domainkit.deletion-authorization.v1")
|
|
877
|
+
});
|
|
878
|
+
const Receipt = Schema.Struct({
|
|
879
|
+
deletedAt: Schema.DateFromString,
|
|
880
|
+
operations: Schema.Array(Schema.Struct({
|
|
881
|
+
operationId: Schema.String,
|
|
882
|
+
providerRecordId: Schema.String
|
|
883
|
+
})),
|
|
884
|
+
planDigest: Schema.String,
|
|
885
|
+
providerId: Schema.String,
|
|
886
|
+
status: Schema.Literals(["complete", "partial"]),
|
|
887
|
+
version: Schema.Literal("domainkit.deletion-receipt.v1"),
|
|
888
|
+
zone: Schema$5
|
|
889
|
+
});
|
|
890
|
+
var Error$1 = class extends Schema.TaggedError()("UnsafeDeletionError", {
|
|
891
|
+
message: Schema.String,
|
|
892
|
+
operationId: Schema.optionalKey(Schema.String)
|
|
893
|
+
}) {};
|
|
894
|
+
var PartialError = class extends Schema.TaggedError()("PartialDeletionError", {
|
|
895
|
+
failedOperationId: Schema.String,
|
|
896
|
+
message: Schema.String,
|
|
897
|
+
receipt: Receipt
|
|
898
|
+
}) {};
|
|
899
|
+
/** Build a fresh deletion plan only from records proven by a prior create receipt. */
|
|
900
|
+
const create = Effect.fn("Deletion.create")(function* (input) {
|
|
901
|
+
const provider = yield* Service;
|
|
902
|
+
yield* validate$1(input.plan);
|
|
903
|
+
if (input.receipt.planDigest !== input.plan.digest || input.receipt.providerId !== input.plan.providerId || input.receipt.zone !== input.plan.zone) return yield* new Error$1({ message: "Create receipt does not belong to the source plan" });
|
|
904
|
+
if (provider.id !== input.plan.providerId) return yield* new Error$1({ message: "Provider does not match the source receipt" });
|
|
905
|
+
const operations = yield* Effect.forEach(input.receipt.operations, (applied) => Effect.gen(function* () {
|
|
906
|
+
const source = input.plan.operations.find((operation) => operation._tag === "create" && operation.id === applied.operationId);
|
|
907
|
+
if (source === void 0 || source._tag !== "create") return yield* new Error$1({
|
|
908
|
+
message: "Receipt operation is not a created source record",
|
|
909
|
+
operationId: applied.operationId
|
|
910
|
+
});
|
|
911
|
+
if (applied.providerRecordId === null) return yield* new Error$1({
|
|
912
|
+
message: "Provider record ID is required for safe deletion",
|
|
913
|
+
operationId: applied.operationId
|
|
914
|
+
});
|
|
915
|
+
yield* assertExact(provider, input.plan.zone, applied.providerRecordId, source.requirement, source.id);
|
|
916
|
+
return {
|
|
917
|
+
id: source.id,
|
|
918
|
+
providerRecordId: applied.providerRecordId,
|
|
919
|
+
record: source.requirement
|
|
920
|
+
};
|
|
921
|
+
}));
|
|
922
|
+
const now = yield* Clock.currentTimeMillis;
|
|
923
|
+
const unsigned = {
|
|
924
|
+
createdAt: new Date(now),
|
|
925
|
+
expiresAt: new Date(now + (input.ttlMs ?? 9e5)),
|
|
926
|
+
operations,
|
|
927
|
+
providerId: input.plan.providerId,
|
|
928
|
+
sourcePlanDigest: input.plan.digest,
|
|
929
|
+
version: "domainkit.deletion-plan.v1",
|
|
930
|
+
zone: input.plan.zone
|
|
931
|
+
};
|
|
932
|
+
return {
|
|
933
|
+
...unsigned,
|
|
934
|
+
digest: yield* sha256Encoded(UnsignedPlan, unsigned)
|
|
935
|
+
};
|
|
936
|
+
});
|
|
937
|
+
/** Produce a separate digest-bound consent artifact for destructive operations. */
|
|
938
|
+
const authorize = Effect.fn("Deletion.authorize")(function* (plan, operationIds) {
|
|
939
|
+
yield* validate(plan);
|
|
940
|
+
const selected = [...new Set(operationIds ?? plan.operations.map(({ id }) => id))].sort();
|
|
941
|
+
const unknown = selected.filter((id) => !plan.operations.some((operation) => operation.id === id));
|
|
942
|
+
if (unknown.length > 0) return yield* new Error$1({ message: `Unknown deletion operation IDs: ${unknown.join(", ")}` });
|
|
943
|
+
return {
|
|
944
|
+
operationIds: selected,
|
|
945
|
+
planDigest: plan.digest,
|
|
946
|
+
version: "domainkit.deletion-authorization.v1"
|
|
947
|
+
};
|
|
948
|
+
});
|
|
949
|
+
/** Re-read every approved record before the first delete, then delete by provider record ID. */
|
|
950
|
+
const apply = Effect.fn("Deletion.apply")(function* (input) {
|
|
951
|
+
const provider = yield* Service;
|
|
952
|
+
yield* validate(input.plan);
|
|
953
|
+
if (input.authorization.planDigest !== input.plan.digest) return yield* new Error$1({ message: "Deletion authorization belongs to a different plan" });
|
|
954
|
+
if (provider.id !== input.plan.providerId) return yield* new Error$1({ message: "Provider does not match the deletion plan" });
|
|
955
|
+
const now = yield* Clock.currentTimeMillis;
|
|
956
|
+
if (input.plan.expiresAt <= new Date(now)) return yield* new Error$1({ message: "Deletion plan has expired" });
|
|
957
|
+
const selected = input.plan.operations.filter(({ id }) => input.authorization.operationIds.includes(id));
|
|
958
|
+
if (selected.length !== input.authorization.operationIds.length) return yield* new Error$1({ message: "Deletion authorization contains unknown operations" });
|
|
959
|
+
const previouslyDeleted = yield* validatePriorReceipt(input.plan, input.priorReceipt);
|
|
960
|
+
const completedIds = new Set(previouslyDeleted.map(({ operationId }) => operationId));
|
|
961
|
+
if (previouslyDeleted.some(({ operationId }) => !input.authorization.operationIds.includes(operationId))) return yield* new Error$1({ message: "Prior receipt contains an unauthorized operation" });
|
|
962
|
+
const remaining = selected.filter(({ id }) => !completedIds.has(id));
|
|
963
|
+
yield* Effect.forEach(remaining, (operation) => assertExact(provider, input.plan.zone, operation.providerRecordId, operation.record, operation.id));
|
|
964
|
+
const deleted = [...previouslyDeleted];
|
|
965
|
+
for (const operation of remaining) {
|
|
966
|
+
const failure = yield* Effect.gen(function* () {
|
|
967
|
+
yield* assertExact(provider, input.plan.zone, operation.providerRecordId, operation.record, operation.id);
|
|
968
|
+
yield* provider.deleteRecord(input.plan.zone, operation.providerRecordId);
|
|
969
|
+
deleted.push({
|
|
970
|
+
operationId: operation.id,
|
|
971
|
+
providerRecordId: operation.providerRecordId
|
|
972
|
+
});
|
|
973
|
+
}).pipe(Effect.match({
|
|
974
|
+
onFailure: (cause) => cause,
|
|
975
|
+
onSuccess: () => null
|
|
976
|
+
}));
|
|
977
|
+
if (failure !== null) {
|
|
978
|
+
if (deleted.length === 0) return yield* failure;
|
|
979
|
+
return yield* new PartialError({
|
|
980
|
+
failedOperationId: operation.id,
|
|
981
|
+
message: failure.message,
|
|
982
|
+
receipt: yield* makeReceipt(input.plan, deleted, "partial")
|
|
983
|
+
});
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
return yield* makeReceipt(input.plan, deleted, "complete");
|
|
987
|
+
});
|
|
988
|
+
function validate(plan) {
|
|
989
|
+
const unsigned = {
|
|
990
|
+
createdAt: plan.createdAt,
|
|
991
|
+
expiresAt: plan.expiresAt,
|
|
992
|
+
operations: plan.operations,
|
|
993
|
+
providerId: plan.providerId,
|
|
994
|
+
sourcePlanDigest: plan.sourcePlanDigest,
|
|
995
|
+
version: plan.version,
|
|
996
|
+
zone: plan.zone
|
|
997
|
+
};
|
|
998
|
+
return sha256Encoded(UnsignedPlan, unsigned).pipe(Effect.flatMap((digest) => digest === plan.digest ? Effect.void : Effect.fail(new Error$1({ message: "Deletion plan digest does not match its contents" }))));
|
|
999
|
+
}
|
|
1000
|
+
function validatePriorReceipt(plan, receipt) {
|
|
1001
|
+
if (receipt === void 0) return Effect.succeed([]);
|
|
1002
|
+
if (receipt.planDigest !== plan.digest || receipt.providerId !== plan.providerId || receipt.zone !== plan.zone || receipt.status !== "partial") return Effect.fail(new Error$1({ message: "Prior receipt does not belong to this deletion plan" }));
|
|
1003
|
+
for (const completed of receipt.operations) {
|
|
1004
|
+
const operation = plan.operations.find(({ id }) => id === completed.operationId);
|
|
1005
|
+
if (operation === void 0 || operation.providerRecordId !== completed.providerRecordId) return Effect.fail(new Error$1({ message: "Prior receipt operation does not match the deletion plan" }));
|
|
1006
|
+
}
|
|
1007
|
+
return Effect.succeed([...receipt.operations]);
|
|
1008
|
+
}
|
|
1009
|
+
function assertExact(provider, zone, providerRecordId, expected, operationId) {
|
|
1010
|
+
return provider.getRecord(zone, providerRecordId).pipe(Effect.flatMap((observed) => observed !== null && observed._tag !== "Opaque" && equals(observed, expected) ? Effect.void : Effect.fail(new Error$1({
|
|
1011
|
+
message: observed === null ? "Created record no longer exists" : "Created record content no longer matches its receipt",
|
|
1012
|
+
operationId
|
|
1013
|
+
}))));
|
|
1014
|
+
}
|
|
1015
|
+
function makeReceipt(plan, operations, status) {
|
|
1016
|
+
return Clock.currentTimeMillis.pipe(Effect.map((now) => ({
|
|
1017
|
+
deletedAt: new Date(now),
|
|
1018
|
+
operations,
|
|
1019
|
+
planDigest: plan.digest,
|
|
1020
|
+
providerId: plan.providerId,
|
|
1021
|
+
status,
|
|
1022
|
+
version: "domainkit.deletion-receipt.v1",
|
|
1023
|
+
zone: plan.zone
|
|
1024
|
+
})));
|
|
1025
|
+
}
|
|
1026
|
+
//#endregion
|
|
735
1027
|
//#region src/verification/dns-data.ts
|
|
736
1028
|
var dns_data_exports = /* @__PURE__ */ __exportAll({ parse: () => parse });
|
|
737
1029
|
const CanonicalAddress = Schema.String.pipe(Schema.decodeTo(Schema.String, SchemaTransformation.transform({
|
|
@@ -775,7 +1067,7 @@ function parse(type, input) {
|
|
|
775
1067
|
try {
|
|
776
1068
|
return Schema.decodeUnknownSync(schema)(input);
|
|
777
1069
|
} catch (cause) {
|
|
778
|
-
throw new Error$
|
|
1070
|
+
throw new Error$3({ message: cause instanceof Error ? cause.message : String(cause) });
|
|
779
1071
|
}
|
|
780
1072
|
}
|
|
781
1073
|
function tokenizedDomain(length, domainIndex) {
|
|
@@ -812,19 +1104,19 @@ function make$1(options) {
|
|
|
812
1104
|
timeoutMs,
|
|
813
1105
|
endpoint: options.endpoint
|
|
814
1106
|
}),
|
|
815
|
-
catch: (cause) => cause instanceof Error$
|
|
1107
|
+
catch: (cause) => cause instanceof Error$5 ? cause : new Error$5({
|
|
816
1108
|
cause,
|
|
817
1109
|
message: cause instanceof Error ? cause.message : "DNS resolution failed",
|
|
818
1110
|
reason: "transport"
|
|
819
1111
|
})
|
|
820
1112
|
}).pipe(Effect.flatMap((message) => decodeResolution(query, message)))) };
|
|
821
1113
|
}
|
|
822
|
-
const layer$1 = (options) => Layer.succeed(Service$
|
|
1114
|
+
const layer$1 = (options) => Layer.succeed(Service$5, make$1(options));
|
|
823
1115
|
const toAsync$1 = (options) => toAsync$2(make$1(options));
|
|
824
1116
|
function decodeResolution(query, message) {
|
|
825
1117
|
return Effect.try({
|
|
826
1118
|
try: () => DnsPacket.decode(message),
|
|
827
|
-
catch: (cause) => new Error$
|
|
1119
|
+
catch: (cause) => new Error$5({
|
|
828
1120
|
cause,
|
|
829
1121
|
message: "DoH returned an invalid DNS message",
|
|
830
1122
|
reason: "response"
|
|
@@ -833,7 +1125,7 @@ function decodeResolution(query, message) {
|
|
|
833
1125
|
}
|
|
834
1126
|
function decodePacket(packet) {
|
|
835
1127
|
if (packet.rcode === "NXDOMAIN") return Effect.succeed({ _tag: "nodata" });
|
|
836
|
-
if (packet.rcode !== "NOERROR") return Effect.fail(new Error$
|
|
1128
|
+
if (packet.rcode !== "NOERROR") return Effect.fail(new Error$5({
|
|
837
1129
|
message: `DNS resolver returned ${packet.rcode ?? "an unknown response code"}`,
|
|
838
1130
|
reason: "response"
|
|
839
1131
|
}));
|
|
@@ -859,7 +1151,7 @@ function validatePacket(query, packet) {
|
|
|
859
1151
|
if (question.class !== "IN" || question.type !== query.type || parse$1(question.name) !== query.name) throw new Error("DoH response did not match the DNS question");
|
|
860
1152
|
return packet;
|
|
861
1153
|
},
|
|
862
|
-
catch: (cause) => new Error$
|
|
1154
|
+
catch: (cause) => new Error$5({
|
|
863
1155
|
cause,
|
|
864
1156
|
message: cause instanceof Error ? cause.message : "DoH returned an invalid DNS response",
|
|
865
1157
|
reason: "response"
|
|
@@ -871,23 +1163,23 @@ function decodeAnswer(answer) {
|
|
|
871
1163
|
if (answer.class !== "IN") return null;
|
|
872
1164
|
const decoded = yield* Effect.try({
|
|
873
1165
|
try: () => answerData(answer),
|
|
874
|
-
catch: (cause) => new Error$
|
|
1166
|
+
catch: (cause) => new Error$5({
|
|
875
1167
|
cause,
|
|
876
1168
|
message: "DoH returned invalid DNS record data",
|
|
877
1169
|
reason: "response"
|
|
878
1170
|
})
|
|
879
1171
|
});
|
|
880
1172
|
if (decoded === void 0) return null;
|
|
881
|
-
const name = yield* decode$
|
|
1173
|
+
const name = yield* decode$4(answer.name);
|
|
882
1174
|
const data = yield* Effect.try({
|
|
883
1175
|
try: () => parse(decoded.type, decoded.data),
|
|
884
|
-
catch: (cause) => new Error$
|
|
1176
|
+
catch: (cause) => new Error$5({
|
|
885
1177
|
cause,
|
|
886
1178
|
message: "DoH returned invalid DNS record data",
|
|
887
1179
|
reason: "response"
|
|
888
1180
|
})
|
|
889
1181
|
});
|
|
890
|
-
if (answer.ttl === void 0) return yield* new Error$
|
|
1182
|
+
if (answer.ttl === void 0) return yield* new Error$5({
|
|
891
1183
|
message: "DoH response omitted the DNS record TTL",
|
|
892
1184
|
reason: "response"
|
|
893
1185
|
});
|
|
@@ -897,7 +1189,7 @@ function decodeAnswer(answer) {
|
|
|
897
1189
|
ttl: answer.ttl,
|
|
898
1190
|
type: decoded.type
|
|
899
1191
|
};
|
|
900
|
-
}).pipe(Effect.mapError((cause) => cause instanceof Error$
|
|
1192
|
+
}).pipe(Effect.mapError((cause) => cause instanceof Error$5 ? cause : new Error$5({
|
|
901
1193
|
cause,
|
|
902
1194
|
message: cause.message,
|
|
903
1195
|
reason: "response"
|
|
@@ -981,18 +1273,18 @@ async function request(input) {
|
|
|
981
1273
|
method: "POST",
|
|
982
1274
|
signal: controller.signal
|
|
983
1275
|
});
|
|
984
|
-
if (!response.ok) throw new Error$
|
|
1276
|
+
if (!response.ok) throw new Error$5({
|
|
985
1277
|
message: `DoH returned HTTP ${response.status}`,
|
|
986
1278
|
reason: "transport"
|
|
987
1279
|
});
|
|
988
1280
|
const contentType = response.headers.get("content-type")?.split(";", 1)[0]?.trim().toLowerCase();
|
|
989
|
-
if (contentType !== "application/dns-message") throw new Error$
|
|
1281
|
+
if (contentType !== "application/dns-message") throw new Error$5({
|
|
990
1282
|
message: `DoH returned unsupported content type ${contentType ?? "<missing>"}`,
|
|
991
1283
|
reason: "response"
|
|
992
1284
|
});
|
|
993
1285
|
return new Uint8Array(await response.arrayBuffer());
|
|
994
1286
|
} catch (cause) {
|
|
995
|
-
if (timedOut) throw new Error$
|
|
1287
|
+
if (timedOut) throw new Error$5({
|
|
996
1288
|
message: "DNS query timed out",
|
|
997
1289
|
reason: "timeout"
|
|
998
1290
|
});
|
|
@@ -1017,7 +1309,7 @@ function make(options = {}) {
|
|
|
1017
1309
|
endpoint: options.endpoint ?? endpoint
|
|
1018
1310
|
});
|
|
1019
1311
|
}
|
|
1020
|
-
const layer = (options = {}) => Layer.succeed(Service$
|
|
1312
|
+
const layer = (options = {}) => Layer.succeed(Service$5, make(options));
|
|
1021
1313
|
const toAsync = (options = {}) => toAsync$2(make(options));
|
|
1022
1314
|
//#endregion
|
|
1023
1315
|
//#region src/verification/verify.ts
|
|
@@ -1030,7 +1322,7 @@ var verify_exports = /* @__PURE__ */ __exportAll({
|
|
|
1030
1322
|
const ProviderObservation = Schema.TaggedUnion({
|
|
1031
1323
|
match: {},
|
|
1032
1324
|
missing: {},
|
|
1033
|
-
mismatch: { records: Schema.Array(Schema$
|
|
1325
|
+
mismatch: { records: Schema.Array(Schema$6) },
|
|
1034
1326
|
failure: { message: Schema.String }
|
|
1035
1327
|
});
|
|
1036
1328
|
const PublicDnsObservation = Schema.TaggedUnion({
|
|
@@ -1052,7 +1344,7 @@ const Result = Schema.Struct({
|
|
|
1052
1344
|
});
|
|
1053
1345
|
const record = Effect.fn("Verification.record")(function* (input) {
|
|
1054
1346
|
const provider = yield* Service;
|
|
1055
|
-
const resolver = yield* Service$
|
|
1347
|
+
const resolver = yield* Service$5;
|
|
1056
1348
|
const [providerObservation, publicDns] = yield* Effect.all([observeProvider(provider, input.zone, input.record).pipe(Effect.catch((failure) => Effect.succeed({
|
|
1057
1349
|
_tag: "failure",
|
|
1058
1350
|
message: failure.message
|
|
@@ -1104,6 +1396,6 @@ function recordValue(requirement) {
|
|
|
1104
1396
|
}
|
|
1105
1397
|
}
|
|
1106
1398
|
//#endregion
|
|
1107
|
-
export {
|
|
1399
|
+
export { encode$1 as $, decodeReceipt as A, decode$1 as B, plan_exports as C, Receipt$1 as D, Operation$1 as E, zones_exports as F, refresh as G, begin as H, connect as I, webCryptoLayer as J, revoke as K, token_exports as L, encodeReceipt as M, types_exports as N, Schema$1 as O, selection_exports as P, decode$2 as Q, Method as R, create$1 as S, Authorization$1 as T, complete as U, manifest_exports as V, oauth_exports as W, Schema$3 as X, Capability as Y, authorization_exports as Z, deletion_exports as _, doh_exports as a, Grant as at, apply$1 as b, Authorization as c, connection_exports as ct, PartialError as d, version as dt, Error$2 as et, Plan as f, create as g, authorize as h, toAsync as i, AuthorizationError$1 as it, encode as j, decode as k, Error$1 as l, decode$3 as lt, apply as m, verify_exports as n, detach as nt, toAsync$1 as o, Schema$4 as ot, Receipt as p, canonical_json_exports as q, cloudflare_doh_exports as r, updateGrant as rt, dns_data_exports as s, assertGrant as st, record as t, connection_lifecycle_exports as tt, Operation as u, encode$2 as ut, authorize$1 as v, renderManualInstructions as w, authorize$2 as x, connection_authorization_exports as y, Schema$2 as z };
|
|
1108
1400
|
|
|
1109
|
-
//# sourceMappingURL=verify-
|
|
1401
|
+
//# sourceMappingURL=verify-BjrR_4NF.mjs.map
|