domainkit 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/dist/canonical-json-9x7gHIoS.mjs +304 -0
- package/dist/canonical-json-9x7gHIoS.mjs.map +1 -0
- package/dist/connect-Db830jad.d.mts +1387 -0
- package/dist/connect-ks3cpqBD.mjs +185 -0
- package/dist/connect-ks3cpqBD.mjs.map +1 -0
- package/dist/connection-DuYDgWir.d.mts +186 -0
- package/dist/connection-api-B__0M3PA.d.mts +26 -0
- package/dist/index.d.mts +243 -42
- package/dist/index.mjs +63 -27
- package/dist/index.mjs.map +1 -1
- package/dist/managed-dns-connections-CaXts84E.mjs +99 -0
- package/dist/managed-dns-connections-CaXts84E.mjs.map +1 -0
- package/dist/promise.d.mts +39 -184
- package/dist/promise.mjs +113 -156
- package/dist/promise.mjs.map +1 -1
- package/dist/{resolver-uqe1h5Vz.d.mts → resolver-DUAIlm2I.d.mts} +24 -1396
- package/dist/{resolver-CvK7JoJM.mjs → resolver-DxJPsq6l.mjs} +9 -115
- package/dist/resolver-DxJPsq6l.mjs.map +1 -0
- package/dist/server.d.mts +128 -0
- package/dist/server.mjs +319 -0
- package/dist/server.mjs.map +1 -0
- package/dist/testing.d.mts +6 -5
- package/dist/testing.mjs +122 -64
- package/dist/testing.mjs.map +1 -1
- package/dist/{verify-BUB4N5zs.mjs → verify-DAtUAEeg.mjs} +461 -485
- package/dist/verify-DAtUAEeg.mjs.map +1 -0
- package/dist/{verify-CkgVrwyl.d.mts → verify-yAy-r9no.d.mts} +198 -42
- package/package.json +5 -1
- package/dist/resolver-CvK7JoJM.mjs.map +0 -1
- package/dist/verify-BUB4N5zs.mjs.map +0 -1
|
@@ -1,372 +1,12 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { E as decode$5, O as parse$1, T as Schema$2, b as authorizationError, c as Evidence$1, f as AuthorizationError, k as Error$1, s as Service$1, u as codec, y as assertAttachment } from "./canonical-json-9x7gHIoS.mjs";
|
|
3
|
+
import { n as Method$1, t as Error$2 } from "./connect-ks3cpqBD.mjs";
|
|
4
|
+
import { $ as data, F as Service$3, I as fromAsync$1, P as Error$3, ct as candidates, et as decode$6, i as toAsync$5, n as Service$2, nt as equals, q as Opaque, t as Error$4, x as authorize$1, z as toAsync$4 } from "./resolver-DxJPsq6l.mjs";
|
|
3
5
|
import { Clock, Context, Crypto, Data, Effect, Layer, Option, Schema, SchemaIssue, SchemaTransformation } from "effect";
|
|
4
6
|
import * as oauth from "oauth4webapi";
|
|
5
7
|
import * as DnsPacket from "@leichtgewicht/dns-packet";
|
|
6
8
|
//#region package.json
|
|
7
|
-
var version = "0.
|
|
8
|
-
//#endregion
|
|
9
|
-
//#region src/auth/provider-context.ts
|
|
10
|
-
var provider_context_exports = /* @__PURE__ */ __exportAll({
|
|
11
|
-
Envelope: () => Envelope,
|
|
12
|
-
codec: () => codec,
|
|
13
|
-
decode: () => decode$7,
|
|
14
|
-
encode: () => encode$4
|
|
15
|
-
});
|
|
16
|
-
/** A provider-owned, versioned, non-secret context value persisted by the host. */
|
|
17
|
-
const Envelope = Schema.Struct({
|
|
18
|
-
value: Schema.Json,
|
|
19
|
-
version: Schema.String
|
|
20
|
-
});
|
|
21
|
-
/** Build an adapter-owned context codec from a schema and stable version identifier. */
|
|
22
|
-
function codec(version, schema) {
|
|
23
|
-
return {
|
|
24
|
-
decode: Effect.fn("ProviderContext.decode")((envelope) => envelope.version !== version ? Effect.fail(new Error$2({ message: `Unsupported provider context version: ${envelope.version}` })) : Schema.decodeUnknownEffect(schema)(envelope.value).pipe(Effect.mapError((cause) => new Error$2({ message: cause.message })))),
|
|
25
|
-
encode: Effect.fn("ProviderContext.encode")((value) => Schema.encodeEffect(schema)(value).pipe(Effect.flatMap((encoded) => Schema.decodeUnknownEffect(Schema.Json)(encoded)), Effect.map((encoded) => ({
|
|
26
|
-
value: encoded,
|
|
27
|
-
version
|
|
28
|
-
})), Effect.mapError((cause) => new Error$2({ message: cause.message })))),
|
|
29
|
-
version
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
const decode$7 = Effect.fn("ProviderContext.Envelope.decode")((input) => Schema.decodeUnknownEffect(Envelope)(input).pipe(Effect.mapError((cause) => new Error$2({ message: cause.message }))));
|
|
33
|
-
const encode$4 = Schema.encodeSync(Envelope);
|
|
34
|
-
//#endregion
|
|
35
|
-
//#region src/auth/authorization.ts
|
|
36
|
-
var authorization_exports = /* @__PURE__ */ __exportAll({
|
|
37
|
-
Capability: () => Capability,
|
|
38
|
-
CapabilityEvidence: () => CapabilityEvidence,
|
|
39
|
-
Evidence: () => Evidence$1,
|
|
40
|
-
EvidenceSchema: () => EvidenceSchema,
|
|
41
|
-
Revocation: () => Revocation,
|
|
42
|
-
Schema: () => Schema$3,
|
|
43
|
-
decode: () => decode$6,
|
|
44
|
-
encode: () => encode$3,
|
|
45
|
-
evidenceFor: () => evidenceFor$1,
|
|
46
|
-
validate: () => validate$1
|
|
47
|
-
});
|
|
48
|
-
const Capability = Schema.Literals(["dns:read", "dns:write"]);
|
|
49
|
-
const Evidence$1 = Data.taggedEnum();
|
|
50
|
-
const EvidenceSchema = Schema.TaggedUnion({
|
|
51
|
-
Declared: {},
|
|
52
|
-
Exercised: { observedAt: Schema.DateFromString },
|
|
53
|
-
Introspected: { observedAt: Schema.DateFromString }
|
|
54
|
-
});
|
|
55
|
-
const CapabilityEvidence = Schema.Struct({
|
|
56
|
-
capability: Capability,
|
|
57
|
-
evidence: EvidenceSchema
|
|
58
|
-
});
|
|
59
|
-
const Revocation = Schema.TaggedUnion({
|
|
60
|
-
Active: {},
|
|
61
|
-
Pending: { requestedAt: Schema.DateFromString }
|
|
62
|
-
});
|
|
63
|
-
/** A provider credential authorization shared by one or more owner connections. */
|
|
64
|
-
const Schema$3 = Schema.Struct({
|
|
65
|
-
authorizedById: Schema.String,
|
|
66
|
-
capabilityEvidence: Schema.Array(CapabilityEvidence),
|
|
67
|
-
createdAt: Schema.DateFromString,
|
|
68
|
-
expiresAt: Schema.NullOr(Schema.DateFromString),
|
|
69
|
-
id: Schema.String,
|
|
70
|
-
method: Schema.Literals([
|
|
71
|
-
"integration",
|
|
72
|
-
"oauth2",
|
|
73
|
-
"token"
|
|
74
|
-
]),
|
|
75
|
-
providerAccountId: Schema.String,
|
|
76
|
-
providerContext: Envelope,
|
|
77
|
-
providerId: Schema.String,
|
|
78
|
-
requiredCapabilities: Schema.Array(Capability),
|
|
79
|
-
revocation: Revocation,
|
|
80
|
-
scopes: Schema.Array(Schema.String)
|
|
81
|
-
});
|
|
82
|
-
const decode$6 = Effect.fn("ProviderAuthorization.decode")((input) => Schema.decodeUnknownEffect(Schema$3)(input).pipe(Effect.mapError((cause) => new Error$2({ message: cause.message }))));
|
|
83
|
-
const validate$1 = Effect.fn("ProviderAuthorization.validate")((input) => Schema.decodeUnknownEffect(Schema.toType(Schema$3))(input).pipe(Effect.mapError((cause) => new Error$2({ message: cause.message }))));
|
|
84
|
-
const encode$3 = Schema.encodeSync(Schema$3);
|
|
85
|
-
function evidenceFor$1(authorization, capability) {
|
|
86
|
-
return authorization.capabilityEvidence.find((item) => item.capability === capability)?.evidence;
|
|
87
|
-
}
|
|
88
|
-
//#endregion
|
|
89
|
-
//#region src/auth/connection.ts
|
|
90
|
-
const Grant = Schema.TaggedUnion({
|
|
91
|
-
account: { excludedDomains: Schema.Array(Schema$4) },
|
|
92
|
-
domains: { domains: Schema.Array(Schema$4) }
|
|
93
|
-
});
|
|
94
|
-
/** Returns whether a grant includes one parsed domain. */
|
|
95
|
-
const coversDomain = (grant, domain) => grant._tag === "account" ? !grant.excludedDomains.includes(domain) : grant.domains.includes(domain);
|
|
96
|
-
/** Expands a grant to include the requested domains. */
|
|
97
|
-
const includeDomains = (current, requested) => {
|
|
98
|
-
if (current === void 0) return requested;
|
|
99
|
-
if (current._tag === "account" && requested._tag === "account") return {
|
|
100
|
-
_tag: "account",
|
|
101
|
-
excludedDomains: current.excludedDomains.filter((domain) => requested.excludedDomains.includes(domain))
|
|
102
|
-
};
|
|
103
|
-
if (current._tag === "account" && requested._tag === "domains") return {
|
|
104
|
-
_tag: "account",
|
|
105
|
-
excludedDomains: current.excludedDomains.filter((domain) => !requested.domains.includes(domain))
|
|
106
|
-
};
|
|
107
|
-
if (requested._tag === "account") {
|
|
108
|
-
const includedDomains = current._tag === "domains" ? current.domains : [];
|
|
109
|
-
return {
|
|
110
|
-
_tag: "account",
|
|
111
|
-
excludedDomains: requested.excludedDomains.filter((domain) => !includedDomains.includes(domain))
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
if (current._tag === "account") return current;
|
|
115
|
-
return {
|
|
116
|
-
_tag: "domains",
|
|
117
|
-
domains: [.../* @__PURE__ */ new Set([...current.domains, ...requested.domains])]
|
|
118
|
-
};
|
|
119
|
-
};
|
|
120
|
-
/** Contracts a grant so it no longer includes one parsed domain. */
|
|
121
|
-
const removeDomain$1 = (grant, domain) => grant._tag === "account" ? {
|
|
122
|
-
_tag: "account",
|
|
123
|
-
excludedDomains: [.../* @__PURE__ */ new Set([...grant.excludedDomains, domain])]
|
|
124
|
-
} : {
|
|
125
|
-
_tag: "domains",
|
|
126
|
-
domains: grant.domains.filter((candidate) => candidate !== domain)
|
|
127
|
-
};
|
|
128
|
-
/** An owner-scoped grant bound to one provider authorization. */
|
|
129
|
-
const Schema$2 = Schema.Struct({
|
|
130
|
-
authorizationId: Schema.String,
|
|
131
|
-
createdAt: Schema.DateFromString,
|
|
132
|
-
grant: Grant,
|
|
133
|
-
id: Schema.String,
|
|
134
|
-
ownerId: Schema.String
|
|
135
|
-
});
|
|
136
|
-
const decode$5 = Effect.fn("Connection.decode")((input) => Schema.decodeUnknownEffect(Schema$2)(input).pipe(Effect.mapError((cause) => new Error$2({ message: cause.message }))));
|
|
137
|
-
const validate = Effect.fn("Connection.validate")((input) => Schema.decodeUnknownEffect(Schema.toType(Schema$2))(input).pipe(Effect.mapError((cause) => new Error$2({ message: cause.message }))));
|
|
138
|
-
const encode$2 = Schema.encodeSync(Schema$2);
|
|
139
|
-
var AuthorizationError = class extends Schema.TaggedError()("AuthorizationError", {
|
|
140
|
-
category: Schema.Literal("authorization"),
|
|
141
|
-
message: Schema.String,
|
|
142
|
-
operation: Schema.String,
|
|
143
|
-
retry: Schema.Literals([
|
|
144
|
-
"never",
|
|
145
|
-
"after-user-action",
|
|
146
|
-
"safe",
|
|
147
|
-
"unknown"
|
|
148
|
-
])
|
|
149
|
-
}) {};
|
|
150
|
-
const authorizationError = (message, operation) => new AuthorizationError({
|
|
151
|
-
category: "authorization",
|
|
152
|
-
message,
|
|
153
|
-
operation,
|
|
154
|
-
retry: "after-user-action"
|
|
155
|
-
});
|
|
156
|
-
function assertGrant(connection, authorization, request) {
|
|
157
|
-
if (connection.authorizationId !== authorization.id || authorization.providerId !== request.providerId || authorization.revocation._tag !== "Active") throw authorizationError("Connection does not grant this provider account", "Connection.assertGrant");
|
|
158
|
-
if (authorization.expiresAt !== null) {
|
|
159
|
-
if (Number.isNaN(authorization.expiresAt.getTime())) throw authorizationError("Connection expiration is invalid", "Connection.assertGrant");
|
|
160
|
-
if (authorization.expiresAt <= (request.now ?? /* @__PURE__ */ new Date())) throw authorizationError("Connection has expired", "Connection.assertGrant");
|
|
161
|
-
}
|
|
162
|
-
if (!authorization.requiredCapabilities.includes(request.capability)) throw authorizationError(`Connection lacks the ${request.capability} capability`, "Connection.assertGrant");
|
|
163
|
-
if (evidenceFor$1(authorization, request.capability) === void 0) throw authorizationError(`Connection has no evidence for the ${request.capability} capability`, "Connection.assertGrant");
|
|
164
|
-
let domain;
|
|
165
|
-
try {
|
|
166
|
-
domain = parse$1(request.domain);
|
|
167
|
-
} catch (cause) {
|
|
168
|
-
if (cause instanceof Error$2) throw cause;
|
|
169
|
-
throw new Error$2({ message: cause instanceof Error ? cause.message : String(cause) });
|
|
170
|
-
}
|
|
171
|
-
if (!coversDomain(connection.grant, domain)) throw authorizationError("Connection does not grant this domain", "Connection.assertGrant");
|
|
172
|
-
return domain;
|
|
173
|
-
}
|
|
174
|
-
//#endregion
|
|
175
|
-
//#region src/auth/connect.ts
|
|
176
|
-
const Method$1 = Data.taggedEnum();
|
|
177
|
-
const StartResult = Data.taggedEnum();
|
|
178
|
-
var Error$1 = class extends Schema.TaggedError()("ConnectionError", {
|
|
179
|
-
category: Schema.Literals([
|
|
180
|
-
"authorization",
|
|
181
|
-
"provider",
|
|
182
|
-
"validation"
|
|
183
|
-
]),
|
|
184
|
-
message: Schema.String,
|
|
185
|
-
operation: Schema.String,
|
|
186
|
-
retry: Schema.Literals([
|
|
187
|
-
"never",
|
|
188
|
-
"after-user-action",
|
|
189
|
-
"safe",
|
|
190
|
-
"unknown"
|
|
191
|
-
])
|
|
192
|
-
}) {};
|
|
193
|
-
const RemoveDomainResult = Data.taggedEnum();
|
|
194
|
-
const persist = Effect.fn("Connection.persist")(function* (input) {
|
|
195
|
-
const repository = yield* Service$1;
|
|
196
|
-
const crypto = yield* Crypto.Crypto;
|
|
197
|
-
const now = new Date(yield* Clock.currentTimeMillis);
|
|
198
|
-
const missingCapability = input.requiredCapabilities.find((capability) => !input.authentication.capabilityEvidence.some((item) => item.capability === capability));
|
|
199
|
-
if (missingCapability !== void 0) return yield* new Error$1({
|
|
200
|
-
category: "authorization",
|
|
201
|
-
message: `Provider authorization lacks evidence for ${missingCapability}`,
|
|
202
|
-
operation: "Connection.persist",
|
|
203
|
-
retry: "after-user-action"
|
|
204
|
-
});
|
|
205
|
-
const existing = yield* repository.findByProviderAccount(input.providerId, input.authentication.providerAccountId);
|
|
206
|
-
const authorizationId = existing?.authorization.id ?? (yield* crypto.randomUUIDv4.pipe(Effect.mapError((cause) => new Error$1({
|
|
207
|
-
category: "validation",
|
|
208
|
-
message: cause.message,
|
|
209
|
-
operation: "Connection.persist",
|
|
210
|
-
retry: "safe"
|
|
211
|
-
}))));
|
|
212
|
-
const existingBinding = existing?.bindings.find((binding) => binding.ownerId === input.ownerId);
|
|
213
|
-
const binding = {
|
|
214
|
-
authorizationId,
|
|
215
|
-
createdAt: existingBinding?.createdAt ?? now,
|
|
216
|
-
grant: includeDomains(existingBinding?.grant, input.grant),
|
|
217
|
-
id: existingBinding?.id ?? (yield* crypto.randomUUIDv4.pipe(Effect.mapError((cause) => new Error$1({
|
|
218
|
-
category: "validation",
|
|
219
|
-
message: cause.message,
|
|
220
|
-
operation: "Connection.persist",
|
|
221
|
-
retry: "safe"
|
|
222
|
-
})))),
|
|
223
|
-
ownerId: input.ownerId
|
|
224
|
-
};
|
|
225
|
-
const aggregate = {
|
|
226
|
-
authorization: {
|
|
227
|
-
authorizedById: input.authorizedById,
|
|
228
|
-
capabilityEvidence: [...input.authentication.capabilityEvidence],
|
|
229
|
-
createdAt: existing?.authorization.createdAt ?? now,
|
|
230
|
-
expiresAt: input.authentication.expiresAt,
|
|
231
|
-
id: authorizationId,
|
|
232
|
-
method: input.method,
|
|
233
|
-
providerAccountId: input.authentication.providerAccountId,
|
|
234
|
-
providerContext: input.authentication.providerContext,
|
|
235
|
-
providerId: input.providerId,
|
|
236
|
-
requiredCapabilities: [...input.requiredCapabilities],
|
|
237
|
-
revocation: { _tag: "Active" },
|
|
238
|
-
scopes: [...input.authentication.scopes]
|
|
239
|
-
},
|
|
240
|
-
bindings: [...existing?.bindings.filter((candidate) => candidate.id !== binding.id) ?? [], binding],
|
|
241
|
-
credential: input.authentication.credential
|
|
242
|
-
};
|
|
243
|
-
return yield* repository.connect(aggregate);
|
|
244
|
-
});
|
|
245
|
-
const start$1 = Effect.fn("Connection.start")(function* (input) {
|
|
246
|
-
if (input.method._tag === "Interactive") {
|
|
247
|
-
const crypto = yield* Crypto.Crypto;
|
|
248
|
-
const now = yield* Clock.currentTimeMillis;
|
|
249
|
-
const continuationId = yield* crypto.randomUUIDv4.pipe(Effect.mapError((cause) => new Error$1({
|
|
250
|
-
category: "validation",
|
|
251
|
-
message: cause.message,
|
|
252
|
-
operation: "Connection.start",
|
|
253
|
-
retry: "safe"
|
|
254
|
-
})));
|
|
255
|
-
const started = yield* input.method.flow.start(continuationId);
|
|
256
|
-
yield* input.method.continuations.put({
|
|
257
|
-
authorizedById: input.authorizedById,
|
|
258
|
-
expiresAt: new Date(now + (input.method.ttlMs ?? 6e5)),
|
|
259
|
-
grant: input.grant,
|
|
260
|
-
id: continuationId,
|
|
261
|
-
method: input.method.flow.method,
|
|
262
|
-
ownerId: input.ownerId,
|
|
263
|
-
payload: started.payload,
|
|
264
|
-
providerId: input.method.flow.providerId,
|
|
265
|
-
requiredCapabilities: [...input.method.flow.requiredCapabilities]
|
|
266
|
-
});
|
|
267
|
-
return StartResult.Redirect({
|
|
268
|
-
authorizationUrl: started.authorizationUrl,
|
|
269
|
-
continuationId
|
|
270
|
-
});
|
|
271
|
-
}
|
|
272
|
-
const authentication = yield* input.method.authenticate(input.method.token);
|
|
273
|
-
const aggregate = yield* persist({
|
|
274
|
-
authentication,
|
|
275
|
-
authorizedById: input.authorizedById,
|
|
276
|
-
grant: input.grant,
|
|
277
|
-
method: "token",
|
|
278
|
-
ownerId: input.ownerId,
|
|
279
|
-
providerId: input.method.providerId,
|
|
280
|
-
requiredCapabilities: input.method.requiredCapabilities
|
|
281
|
-
});
|
|
282
|
-
return StartResult.Connected({ aggregate });
|
|
283
|
-
});
|
|
284
|
-
const complete$1 = Effect.fn("Connection.complete")(function* (input) {
|
|
285
|
-
const continuation = yield* input.continuations.consume(input.continuationId, new Date(yield* Clock.currentTimeMillis));
|
|
286
|
-
if (continuation === null) return yield* new Error$1({
|
|
287
|
-
category: "authorization",
|
|
288
|
-
message: "Connection continuation is expired, unknown, or already consumed",
|
|
289
|
-
operation: "Connection.complete",
|
|
290
|
-
retry: "after-user-action"
|
|
291
|
-
});
|
|
292
|
-
if (continuation.providerId !== input.flow.providerId || continuation.method !== input.flow.method) return yield* new Error$1({
|
|
293
|
-
category: "authorization",
|
|
294
|
-
message: "Connection continuation does not match the provider flow",
|
|
295
|
-
operation: "Connection.complete",
|
|
296
|
-
retry: "never"
|
|
297
|
-
});
|
|
298
|
-
const authentication = yield* input.flow.complete(continuation.payload, input.callbackUrl);
|
|
299
|
-
return yield* persist({
|
|
300
|
-
authentication,
|
|
301
|
-
authorizedById: continuation.authorizedById,
|
|
302
|
-
grant: continuation.grant,
|
|
303
|
-
method: continuation.method,
|
|
304
|
-
ownerId: continuation.ownerId,
|
|
305
|
-
providerId: continuation.providerId,
|
|
306
|
-
requiredCapabilities: continuation.requiredCapabilities
|
|
307
|
-
});
|
|
308
|
-
});
|
|
309
|
-
/** Extends one owner binding without repeating provider authentication. */
|
|
310
|
-
const extend = Effect.fn("Connection.extend")(function* (input) {
|
|
311
|
-
const repository = yield* Service$1;
|
|
312
|
-
const aggregate = yield* repository.getByConnectionId(input.connectionId);
|
|
313
|
-
const binding = aggregate?.bindings.find(({ id }) => id === input.connectionId);
|
|
314
|
-
if (aggregate === null || binding === void 0 || binding.ownerId !== input.ownerId) return yield* new Error$1({
|
|
315
|
-
category: "authorization",
|
|
316
|
-
message: "Connection does not belong to this owner",
|
|
317
|
-
operation: "Connection.extend",
|
|
318
|
-
retry: "never"
|
|
319
|
-
});
|
|
320
|
-
if (aggregate.authorization.revocation._tag !== "Active") return yield* new Error$1({
|
|
321
|
-
category: "authorization",
|
|
322
|
-
message: "Provider authorization is awaiting revocation",
|
|
323
|
-
operation: "Connection.extend",
|
|
324
|
-
retry: "after-user-action"
|
|
325
|
-
});
|
|
326
|
-
const now = new Date(yield* Clock.currentTimeMillis);
|
|
327
|
-
if (aggregate.authorization.expiresAt !== null && (Number.isNaN(aggregate.authorization.expiresAt.getTime()) || aggregate.authorization.expiresAt <= now)) return yield* new Error$1({
|
|
328
|
-
category: "authorization",
|
|
329
|
-
message: "Provider authorization has expired",
|
|
330
|
-
operation: "Connection.extend",
|
|
331
|
-
retry: "after-user-action"
|
|
332
|
-
});
|
|
333
|
-
return yield* repository.bind(aggregate.authorization.id, {
|
|
334
|
-
...binding,
|
|
335
|
-
grant: includeDomains(binding.grant, input.grant)
|
|
336
|
-
});
|
|
337
|
-
});
|
|
338
|
-
/** Removes one domain from an owner binding without revoking provider authorization. */
|
|
339
|
-
const removeDomain = Effect.fn("Connection.removeDomain")(function* (input) {
|
|
340
|
-
const repository = yield* Service$1;
|
|
341
|
-
const aggregate = yield* repository.getByConnectionId(input.connectionId);
|
|
342
|
-
const binding = aggregate?.bindings.find(({ id }) => id === input.connectionId);
|
|
343
|
-
if (aggregate === null || binding === void 0 || binding.ownerId !== input.ownerId) return yield* new Error$1({
|
|
344
|
-
category: "authorization",
|
|
345
|
-
message: "Connection does not belong to this owner",
|
|
346
|
-
operation: "Connection.removeDomain",
|
|
347
|
-
retry: "never"
|
|
348
|
-
});
|
|
349
|
-
const domain = yield* decode$8(input.domain).pipe(Effect.mapError((cause) => new Error$1({
|
|
350
|
-
category: "validation",
|
|
351
|
-
message: cause.message,
|
|
352
|
-
operation: "Connection.removeDomain",
|
|
353
|
-
retry: "never"
|
|
354
|
-
})));
|
|
355
|
-
const updated = yield* repository.modifyBinding(input.connectionId, (current) => {
|
|
356
|
-
if (!coversDomain(current.grant, domain)) return {
|
|
357
|
-
binding: current,
|
|
358
|
-
value: false
|
|
359
|
-
};
|
|
360
|
-
return {
|
|
361
|
-
binding: {
|
|
362
|
-
...current,
|
|
363
|
-
grant: removeDomain$1(current.grant, domain)
|
|
364
|
-
},
|
|
365
|
-
value: true
|
|
366
|
-
};
|
|
367
|
-
});
|
|
368
|
-
return updated.value ? RemoveDomainResult.Removed({ aggregate: updated.aggregate }) : RemoveDomainResult.AlreadyRemoved({ aggregate: updated.aggregate });
|
|
369
|
-
});
|
|
9
|
+
var version = "0.6.0";
|
|
370
10
|
//#endregion
|
|
371
11
|
//#region src/auth/diagnostic.ts
|
|
372
12
|
var diagnostic_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -440,7 +80,7 @@ const Schema$1 = Schema.Struct({
|
|
|
440
80
|
methods: Schema.Array(Method),
|
|
441
81
|
providerId: Schema.String
|
|
442
82
|
});
|
|
443
|
-
const decode$4 = Effect.fn("ProviderAuth.decode")((input) => Schema.decodeUnknownEffect(Schema$1)(input).pipe(Effect.mapError((cause) => new Error$
|
|
83
|
+
const decode$4 = Effect.fn("ProviderAuth.decode")((input) => Schema.decodeUnknownEffect(Schema$1)(input).pipe(Effect.mapError((cause) => new Error$1({ message: cause.message }))));
|
|
444
84
|
//#endregion
|
|
445
85
|
//#region src/auth/secret.ts
|
|
446
86
|
var secret_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -479,9 +119,9 @@ const Evidence = Schema.Struct({
|
|
|
479
119
|
accountId: Schema.String,
|
|
480
120
|
connectionId: Schema.String,
|
|
481
121
|
decisiveNameserverMatch: Schema.Boolean,
|
|
482
|
-
matchedNameservers: Schema.Array(Schema$
|
|
122
|
+
matchedNameservers: Schema.Array(Schema$2),
|
|
483
123
|
providerId: Schema.String,
|
|
484
|
-
zone: Schema$
|
|
124
|
+
zone: Schema$2
|
|
485
125
|
});
|
|
486
126
|
const Selection = Schema.TaggedUnion({
|
|
487
127
|
selected: {
|
|
@@ -499,13 +139,16 @@ function select(input) {
|
|
|
499
139
|
const zoneCandidates = new Set(candidates(domain));
|
|
500
140
|
const authoritative = nameserverSet(input.authoritativeNameservers);
|
|
501
141
|
const eligible = input.connectedZones.filter((connected) => {
|
|
502
|
-
if (!zoneCandidates.has(
|
|
142
|
+
if (!zoneCandidates.has(connected.attachment.target.zoneName)) return false;
|
|
503
143
|
try {
|
|
504
|
-
|
|
144
|
+
assertAttachment({
|
|
145
|
+
attachment: connected.attachment,
|
|
505
146
|
capability: "dns:read",
|
|
147
|
+
connection: connected.connection,
|
|
506
148
|
domain,
|
|
507
149
|
...input.now === void 0 ? {} : { now: input.now },
|
|
508
|
-
providerId: connected.providerId
|
|
150
|
+
providerId: connected.connection.providerId,
|
|
151
|
+
authorization: connected.authorization
|
|
509
152
|
});
|
|
510
153
|
return true;
|
|
511
154
|
} catch {
|
|
@@ -515,8 +158,8 @@ function select(input) {
|
|
|
515
158
|
const evidence = eligible.map((connected) => evidenceFor(connected, authoritative));
|
|
516
159
|
if (input.explicit !== void 0) {
|
|
517
160
|
const explicitZone = parse$1(input.explicit.zone);
|
|
518
|
-
const index = eligible.findIndex((connected) => connected.providerId === input.explicit?.providerId && connected.
|
|
519
|
-
if (index < 0) throw authorizationError("Explicit provider zone is not connected
|
|
161
|
+
const index = eligible.findIndex((connected) => connected.connection.providerId === input.explicit?.providerId && connected.attachment.target.accountId === input.explicit.accountId && connected.attachment.target.zoneName === explicitZone);
|
|
162
|
+
if (index < 0) throw authorizationError("Explicit provider zone is not connected for this domain", "ProviderDiscovery.select");
|
|
520
163
|
const candidate = evidence[index];
|
|
521
164
|
if (candidate === void 0) throw new Error("Eligible provider evidence is missing");
|
|
522
165
|
return {
|
|
@@ -543,32 +186,79 @@ function evidenceFor(connected, authoritative) {
|
|
|
543
186
|
const configured = new Set(nameserverSet(connected.nameservers));
|
|
544
187
|
const matchedNameservers = authoritative.filter((nameserver) => configured.has(nameserver));
|
|
545
188
|
return {
|
|
546
|
-
accountId: connected.
|
|
189
|
+
accountId: connected.attachment.target.accountId,
|
|
547
190
|
connectionId: connected.connection.id,
|
|
548
191
|
decisiveNameserverMatch: authoritative.length > 0 && matchedNameservers.length === authoritative.length,
|
|
549
192
|
matchedNameservers,
|
|
550
|
-
providerId: connected.providerId,
|
|
551
|
-
zone:
|
|
193
|
+
providerId: connected.connection.providerId,
|
|
194
|
+
zone: connected.attachment.target.zoneName
|
|
552
195
|
};
|
|
553
196
|
}
|
|
554
197
|
function nameserverSet(nameservers) {
|
|
555
198
|
return [...new Set(nameservers.map(parse$1))].sort();
|
|
556
199
|
}
|
|
557
200
|
//#endregion
|
|
201
|
+
//#region src/provider/session.ts
|
|
202
|
+
var session_exports = /* @__PURE__ */ __exportAll({
|
|
203
|
+
Resolution: () => Resolution,
|
|
204
|
+
fromAsync: () => fromAsync,
|
|
205
|
+
toAsync: () => toAsync$3
|
|
206
|
+
});
|
|
207
|
+
const Resolution = Data.taggedEnum();
|
|
208
|
+
/** Adapts a Promise-shaped session to the Effect-native session boundary. */
|
|
209
|
+
const fromAsync = (session) => ({
|
|
210
|
+
providerId: session.providerId,
|
|
211
|
+
listTargets: (input) => Effect.tryPromise({
|
|
212
|
+
try: () => session.listTargets(input),
|
|
213
|
+
catch: (cause) => sessionFailure(session.providerId, "listTargets", cause)
|
|
214
|
+
}),
|
|
215
|
+
resolveTarget: (domain) => Effect.tryPromise({
|
|
216
|
+
try: () => session.resolveTarget(domain),
|
|
217
|
+
catch: (cause) => sessionFailure(session.providerId, "resolveTarget", cause)
|
|
218
|
+
}),
|
|
219
|
+
forTarget: (target) => Effect.tryPromise({
|
|
220
|
+
try: () => session.forTarget(target),
|
|
221
|
+
catch: (cause) => sessionFailure(session.providerId, "forTarget", cause)
|
|
222
|
+
}).pipe(Effect.map(fromAsync$1))
|
|
223
|
+
});
|
|
224
|
+
/** Adapts an Effect-native session to the Promise facade. */
|
|
225
|
+
const toAsync$3 = (session) => ({
|
|
226
|
+
providerId: session.providerId,
|
|
227
|
+
listTargets: (input) => Effect.runPromise(session.listTargets(input)),
|
|
228
|
+
resolveTarget: (domain) => Effect.runPromise(session.resolveTarget(domain)),
|
|
229
|
+
forTarget: (target) => Effect.runPromise(session.forTarget(target)).then(toAsync$4)
|
|
230
|
+
});
|
|
231
|
+
function sessionFailure(providerId, operation, cause) {
|
|
232
|
+
return cause instanceof Error$3 ? cause : new Error$3({
|
|
233
|
+
message: cause instanceof globalThis.Error ? cause.message : String(cause),
|
|
234
|
+
operation: `ProviderSession.${operation}`,
|
|
235
|
+
providerId,
|
|
236
|
+
reason: "transport"
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
//#endregion
|
|
558
240
|
//#region src/plan/connection-authorization.ts
|
|
559
241
|
var connection_authorization_exports = /* @__PURE__ */ __exportAll({ authorize: () => authorize });
|
|
560
242
|
const authorize = Effect.fn("ConnectionAuthorization.authorize")(function* (input) {
|
|
243
|
+
const aggregate = yield* (yield* Service$1).getByConnectionId(input.connection.id);
|
|
244
|
+
const storedConnection = aggregate?.connections.find(({ id }) => id === input.connection.id);
|
|
245
|
+
const storedAttachment = aggregate?.attachments.find(({ id }) => id === input.attachment.id);
|
|
246
|
+
if (aggregate === null || storedConnection === void 0 || storedAttachment === void 0 || storedConnection.ownerId !== input.connection.ownerId || storedConnection.providerId !== input.connection.providerId || storedConnection.method !== aggregate.authorization.method || storedConnection.authorizationId !== input.authorization.id || storedAttachment.connectionId !== storedConnection.id) return yield* authorizationError("Domain attachment is not owned by the active provider connection", "ConnectionAuthorization.authorize");
|
|
561
247
|
const domain = yield* Effect.try({
|
|
562
|
-
try: () =>
|
|
248
|
+
try: () => assertAttachment({
|
|
249
|
+
attachment: storedAttachment,
|
|
563
250
|
capability: "dns:write",
|
|
251
|
+
authorization: aggregate.authorization,
|
|
252
|
+
connection: storedConnection,
|
|
253
|
+
credential: aggregate.credential,
|
|
564
254
|
domain: input.domain,
|
|
565
255
|
providerId: input.plan.providerId
|
|
566
256
|
}),
|
|
567
|
-
catch: (cause) => cause instanceof AuthorizationError || cause instanceof Error$
|
|
257
|
+
catch: (cause) => cause instanceof AuthorizationError || cause instanceof Error$1 ? cause : authorizationError("Domain attachment is invalid", "ConnectionAuthorization.authorize")
|
|
568
258
|
});
|
|
569
259
|
const authorization = yield* authorize$1(input.plan, input.operationIds, input.allowPartial === void 0 ? {} : { allowPartial: input.allowPartial });
|
|
570
260
|
const selected = new Set(authorization.operationIds);
|
|
571
|
-
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* authorizationError(`Plan operations are outside the
|
|
261
|
+
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* authorizationError(`Plan operations are outside the attached domain ${domain}`, "ConnectionAuthorization.authorize");
|
|
572
262
|
return authorization;
|
|
573
263
|
});
|
|
574
264
|
//#endregion
|
|
@@ -598,17 +288,17 @@ const start = Effect.fn("AuthorizationCode.start")(function* (input) {
|
|
|
598
288
|
});
|
|
599
289
|
const complete = Effect.fn("AuthorizationCode.complete")(function* (input) {
|
|
600
290
|
const payload = yield* decodePayload(input.payload);
|
|
601
|
-
if (input.callbackUrl.searchParams.get("state") !== payload.state) return yield* failure$
|
|
291
|
+
if (input.callbackUrl.searchParams.get("state") !== payload.state) return yield* failure$6("Authorization callback state does not match its continuation");
|
|
602
292
|
const server = input.method.authorizationServer;
|
|
603
293
|
const client = { client_id: input.client.clientId };
|
|
604
|
-
const authentication = yield* clientAuthentication(input.method, input.client);
|
|
294
|
+
const authentication = yield* clientAuthentication$1(input.method, input.client);
|
|
605
295
|
return yield* Effect.tryPromise({
|
|
606
296
|
try: async (signal) => {
|
|
607
297
|
const callback = oauth.validateAuthResponse(server, client, input.callbackUrl, payload.state);
|
|
608
298
|
const response = await oauth.authorizationCodeGrantRequest(server, client, authentication, callback, payload.redirectUri, payload.codeVerifier, requestOptions(input.fetch, signal));
|
|
609
299
|
return oauth.processAuthorizationCodeResponse(server, client, response);
|
|
610
300
|
},
|
|
611
|
-
catch: (cause) => new Error$
|
|
301
|
+
catch: (cause) => new Error$2({
|
|
612
302
|
category: "provider",
|
|
613
303
|
message: cause instanceof globalThis.Error ? cause.name : "OAuth provider request failed",
|
|
614
304
|
operation: "AuthorizationCode.complete",
|
|
@@ -622,18 +312,18 @@ const PayloadSchema = Schema.Struct({
|
|
|
622
312
|
state: Schema.String
|
|
623
313
|
});
|
|
624
314
|
function decodePayload(payload) {
|
|
625
|
-
return Schema.decodeUnknownEffect(Schema.fromJsonString(PayloadSchema))(payload.expose()).pipe(Effect.mapError(() => new Error$
|
|
315
|
+
return Schema.decodeUnknownEffect(Schema.fromJsonString(PayloadSchema))(payload.expose()).pipe(Effect.mapError(() => new Error$2({
|
|
626
316
|
category: "authorization",
|
|
627
317
|
message: "OAuth continuation payload is invalid",
|
|
628
318
|
operation: "AuthorizationCode",
|
|
629
319
|
retry: "after-user-action"
|
|
630
320
|
})));
|
|
631
321
|
}
|
|
632
|
-
function clientAuthentication(method, client) {
|
|
322
|
+
function clientAuthentication$1(method, client) {
|
|
633
323
|
switch (method.clientAuth) {
|
|
634
324
|
case "none": return Effect.succeed(oauth.None());
|
|
635
|
-
case "client_secret_basic": return client.clientSecret === void 0 ? failure$
|
|
636
|
-
case "client_secret_post": return client.clientSecret === void 0 ? failure$
|
|
325
|
+
case "client_secret_basic": return client.clientSecret === void 0 ? failure$6("OAuth client secret is required") : Effect.succeed(oauth.ClientSecretBasic(client.clientSecret.expose()));
|
|
326
|
+
case "client_secret_post": return client.clientSecret === void 0 ? failure$6("OAuth client secret is required") : Effect.succeed(oauth.ClientSecretPost(client.clientSecret.expose()));
|
|
637
327
|
}
|
|
638
328
|
}
|
|
639
329
|
function requestOptions(fetchImplementation, signal) {
|
|
@@ -645,14 +335,14 @@ function requestOptions(fetchImplementation, signal) {
|
|
|
645
335
|
})
|
|
646
336
|
};
|
|
647
337
|
}
|
|
648
|
-
const failure$
|
|
338
|
+
const failure$6 = (message) => Effect.fail(new Error$2({
|
|
649
339
|
category: "authorization",
|
|
650
340
|
message,
|
|
651
341
|
operation: "AuthorizationCode",
|
|
652
342
|
retry: "after-user-action"
|
|
653
343
|
}));
|
|
654
344
|
function randomBytes(crypto, size) {
|
|
655
|
-
return crypto.randomBytes(size).pipe(Effect.mapError((cause) => new Error$
|
|
345
|
+
return crypto.randomBytes(size).pipe(Effect.mapError((cause) => new Error$2({
|
|
656
346
|
category: "validation",
|
|
657
347
|
message: cause.message,
|
|
658
348
|
operation: "AuthorizationCode.start",
|
|
@@ -660,7 +350,7 @@ function randomBytes(crypto, size) {
|
|
|
660
350
|
})));
|
|
661
351
|
}
|
|
662
352
|
function digest(crypto, value) {
|
|
663
|
-
return crypto.digest("SHA-256", new TextEncoder().encode(value)).pipe(Effect.mapError((cause) => new Error$
|
|
353
|
+
return crypto.digest("SHA-256", new TextEncoder().encode(value)).pipe(Effect.mapError((cause) => new Error$2({
|
|
664
354
|
category: "validation",
|
|
665
355
|
message: cause.message,
|
|
666
356
|
operation: "AuthorizationCode.start",
|
|
@@ -703,7 +393,13 @@ const Zone = Schema.Struct({
|
|
|
703
393
|
id: Schema.String,
|
|
704
394
|
name: Schema.String,
|
|
705
395
|
name_servers: Schema.Array(Schema.String),
|
|
706
|
-
status: Schema.optionalKey(Schema.String)
|
|
396
|
+
status: Schema.optionalKey(Schema.String),
|
|
397
|
+
type: Schema.optionalKey(Schema.Literals([
|
|
398
|
+
"full",
|
|
399
|
+
"partial",
|
|
400
|
+
"secondary",
|
|
401
|
+
"internal"
|
|
402
|
+
]))
|
|
707
403
|
});
|
|
708
404
|
const Record$1 = Schema.Struct({
|
|
709
405
|
content: Schema.optionalKey(Schema.String),
|
|
@@ -763,7 +459,7 @@ const Data$1 = Schema.Struct({
|
|
|
763
459
|
weight: Schema.optionalKey(Schema.Number)
|
|
764
460
|
});
|
|
765
461
|
const decode$3 = Effect.fn("CloudflareRecords.decode")((record) => Effect.gen(function* () {
|
|
766
|
-
const name = yield* decode$
|
|
462
|
+
const name = yield* decode$5(record.name).pipe(Effect.match({
|
|
767
463
|
onFailure: () => null,
|
|
768
464
|
onSuccess: (value) => value
|
|
769
465
|
}));
|
|
@@ -778,46 +474,46 @@ const decode$3 = Effect.fn("CloudflareRecords.decode")((record) => Effect.gen(fu
|
|
|
778
474
|
policy: record.type === "CNAME" ? "exclusive" : "append",
|
|
779
475
|
ttl: record.ttl === 1 ? null : record.ttl
|
|
780
476
|
};
|
|
781
|
-
const content = () => record.content === void 0 ? Effect.fail(failure$
|
|
477
|
+
const content = () => record.content === void 0 ? Effect.fail(failure$5("decodeRecord", `Cloudflare ${record.type} record has no content`)) : Effect.succeed(record.content);
|
|
782
478
|
switch (record.type) {
|
|
783
479
|
case "A":
|
|
784
|
-
case "AAAA": return yield* decode$
|
|
480
|
+
case "AAAA": return yield* decode$6({
|
|
785
481
|
...common,
|
|
786
482
|
_tag: record.type,
|
|
787
483
|
address: yield* content()
|
|
788
|
-
}).pipe(Effect.mapError((cause) => failure$
|
|
484
|
+
}).pipe(Effect.mapError((cause) => failure$5("decodeRecord", cause.message)));
|
|
789
485
|
case "CNAME":
|
|
790
|
-
case "NS": return yield* decode$
|
|
486
|
+
case "NS": return yield* decode$6({
|
|
791
487
|
...common,
|
|
792
488
|
_tag: record.type,
|
|
793
489
|
target: yield* content()
|
|
794
|
-
}).pipe(Effect.mapError((cause) => failure$
|
|
795
|
-
case "TXT": return yield* decode$
|
|
490
|
+
}).pipe(Effect.mapError((cause) => failure$5("decodeRecord", cause.message)));
|
|
491
|
+
case "TXT": return yield* decode$6({
|
|
796
492
|
...common,
|
|
797
493
|
_tag: "TXT",
|
|
798
494
|
value: yield* content()
|
|
799
|
-
}).pipe(Effect.mapError((cause) => failure$
|
|
800
|
-
case "MX": return yield* decode$
|
|
495
|
+
}).pipe(Effect.mapError((cause) => failure$5("decodeRecord", cause.message)));
|
|
496
|
+
case "MX": return yield* decode$6({
|
|
801
497
|
...common,
|
|
802
498
|
_tag: "MX",
|
|
803
499
|
exchange: yield* content(),
|
|
804
500
|
priority: record.priority
|
|
805
|
-
}).pipe(Effect.mapError((cause) => failure$
|
|
501
|
+
}).pipe(Effect.mapError((cause) => failure$5("decodeRecord", cause.message)));
|
|
806
502
|
case "CAA": {
|
|
807
503
|
const data = yield* decodeData(record);
|
|
808
|
-
return yield* decode$
|
|
504
|
+
return yield* decode$6({
|
|
809
505
|
...common,
|
|
810
506
|
_tag: "CAA",
|
|
811
507
|
...data
|
|
812
|
-
}).pipe(Effect.mapError((cause) => failure$
|
|
508
|
+
}).pipe(Effect.mapError((cause) => failure$5("decodeRecord", cause.message)));
|
|
813
509
|
}
|
|
814
510
|
case "SRV": {
|
|
815
511
|
const data = yield* decodeData(record);
|
|
816
|
-
return yield* decode$
|
|
512
|
+
return yield* decode$6({
|
|
817
513
|
...common,
|
|
818
514
|
_tag: "SRV",
|
|
819
515
|
...data
|
|
820
|
-
}).pipe(Effect.mapError((cause) => failure$
|
|
516
|
+
}).pipe(Effect.mapError((cause) => failure$5("decodeRecord", cause.message)));
|
|
821
517
|
}
|
|
822
518
|
default: return yield* decodeOpaque$1(record);
|
|
823
519
|
}
|
|
@@ -827,7 +523,7 @@ const decodeOpaque$1 = Effect.fn("CloudflareRecords.decodeOpaque")((record) => S
|
|
|
827
523
|
name: record.name,
|
|
828
524
|
providerRecordId: record.id,
|
|
829
525
|
providerType: record.type
|
|
830
|
-
}).pipe(Effect.mapError((cause) => failure$
|
|
526
|
+
}).pipe(Effect.mapError((cause) => failure$5("decodeRecord", cause.message))));
|
|
831
527
|
function encode$1(record) {
|
|
832
528
|
const common = {
|
|
833
529
|
name: record.name,
|
|
@@ -874,8 +570,8 @@ function encode$1(record) {
|
|
|
874
570
|
};
|
|
875
571
|
}
|
|
876
572
|
}
|
|
877
|
-
const decodeData = Effect.fn("CloudflareRecords.decodeData")((record) => Schema.decodeUnknownEffect(Data$1)(record.data).pipe(Effect.mapError((cause) => failure$
|
|
878
|
-
function failure$
|
|
573
|
+
const decodeData = Effect.fn("CloudflareRecords.decodeData")((record) => Schema.decodeUnknownEffect(Data$1)(record.data).pipe(Effect.mapError((cause) => failure$5("decodeRecord", cause.message))));
|
|
574
|
+
function failure$5(operation, message, fields = {}) {
|
|
879
575
|
return new Error$3({
|
|
880
576
|
message,
|
|
881
577
|
operation,
|
|
@@ -915,14 +611,14 @@ const makeTransport = (options) => {
|
|
|
915
611
|
...init?.headers
|
|
916
612
|
}
|
|
917
613
|
}),
|
|
918
|
-
catch: () => failure$
|
|
614
|
+
catch: () => failure$4(operation, "Cloudflare request failed", { reason: "transport" })
|
|
919
615
|
});
|
|
920
616
|
return {
|
|
921
617
|
body: yield* Effect.tryPromise({
|
|
922
618
|
try: () => response.json(),
|
|
923
619
|
catch: () => {
|
|
924
620
|
const retryAfterMs = retryAfter(response.headers.get("retry-after"));
|
|
925
|
-
return failure$
|
|
621
|
+
return failure$4(operation, response.ok ? "Cloudflare returned a non-JSON response" : `Cloudflare request failed with HTTP ${response.status}`, {
|
|
926
622
|
reason: response.ok ? "response" : reason$1(response.status),
|
|
927
623
|
...retryAfterMs === void 0 ? {} : { retryAfterMs },
|
|
928
624
|
status: response.status
|
|
@@ -960,12 +656,12 @@ const makeTransport = (options) => {
|
|
|
960
656
|
const { body, response } = yield* request(path, "validateToken");
|
|
961
657
|
yield* ensureSuccess(body, "validateToken", response);
|
|
962
658
|
const envelope = yield* decode$2(TokenEnvelope, body, "validateToken", response);
|
|
963
|
-
if (envelope.result.status !== "active") return yield* Effect.fail(failure$
|
|
659
|
+
if (envelope.result.status !== "active") return yield* Effect.fail(failure$4("validateToken", `Cloudflare token is ${envelope.result.status}`, {
|
|
964
660
|
reason: "authentication",
|
|
965
661
|
status: response.status
|
|
966
662
|
}));
|
|
967
663
|
const expiresAt = envelope.result.expires_on === void 0 ? null : new Date(envelope.result.expires_on);
|
|
968
|
-
if (expiresAt !== null && Number.isNaN(expiresAt.valueOf())) return yield* Effect.fail(failure$
|
|
664
|
+
if (expiresAt !== null && Number.isNaN(expiresAt.valueOf())) return yield* Effect.fail(failure$4("validateToken", "Cloudflare token expiry is not a valid timestamp", {
|
|
969
665
|
reason: "response",
|
|
970
666
|
status: response.status
|
|
971
667
|
}));
|
|
@@ -981,9 +677,9 @@ const discoverAccount = Effect.fn("CloudflareClient.discoverAccount")((options)
|
|
|
981
677
|
const match = matches[0];
|
|
982
678
|
if (matches.length === 0) continue;
|
|
983
679
|
if (matches.length === 1 && match !== void 0) return match.account;
|
|
984
|
-
return yield* Effect.fail(failure$
|
|
680
|
+
return yield* Effect.fail(failure$4("discoverAccount", `Cloudflare returned multiple accessible zones named ${candidate}`, { reason: "conflict" }));
|
|
985
681
|
}
|
|
986
|
-
return yield* Effect.fail(failure$
|
|
682
|
+
return yield* Effect.fail(failure$4("discoverAccount", `Cloudflare did not expose an accessible zone for ${options.domain}`, { reason: "not_found" }));
|
|
987
683
|
}));
|
|
988
684
|
const discoverAuthorizationAccount = discoverAccount;
|
|
989
685
|
const validateDomainToken = Effect.fn("CloudflareClient.validateDomainToken")((options) => Effect.gen(function* () {
|
|
@@ -999,12 +695,27 @@ const validateDomainToken = Effect.fn("CloudflareClient.validateDomainToken")((o
|
|
|
999
695
|
}));
|
|
1000
696
|
/** Creates an Effect-native Cloudflare client without owning the credential lifecycle. */
|
|
1001
697
|
function make$5(options) {
|
|
698
|
+
return makeClient$1(options);
|
|
699
|
+
}
|
|
700
|
+
function makeClient$1(options) {
|
|
1002
701
|
const { allZones, inspectToken, request } = makeTransport(options);
|
|
1003
702
|
const resolveZone = Effect.fn("CloudflareClient.resolveZone")((name) => Effect.gen(function* () {
|
|
703
|
+
if (options.target !== void 0) {
|
|
704
|
+
if (name !== options.target.zoneName && !name.endsWith(`.${options.target.zoneName}`)) return yield* Effect.fail(failure$4("resolveZone", `Cloudflare target ${options.target.zoneName} does not cover ${name}`, { reason: "not_found" }));
|
|
705
|
+
return {
|
|
706
|
+
account: {
|
|
707
|
+
id: options.target.accountId,
|
|
708
|
+
name: options.target.accountId
|
|
709
|
+
},
|
|
710
|
+
id: options.target.zoneId,
|
|
711
|
+
name: options.target.zoneName,
|
|
712
|
+
name_servers: options.target.evidence?.nameservers ?? []
|
|
713
|
+
};
|
|
714
|
+
}
|
|
1004
715
|
const matches = yield* allZones(options.accountId, name);
|
|
1005
716
|
const match = matches[0];
|
|
1006
717
|
if (matches.length === 1 && match !== void 0) return match;
|
|
1007
|
-
return yield* Effect.fail(failure$
|
|
718
|
+
return yield* Effect.fail(failure$4("resolveZone", matches.length === 0 ? options.accountId === void 0 ? `Cloudflare zone ${name} was not found in the authorized accounts` : `Cloudflare zone ${name} was not found in account ${options.accountId}` : options.accountId === void 0 ? `Cloudflare returned multiple zones named ${name} across authorized accounts` : `Cloudflare returned multiple zones named ${name} in account ${options.accountId}`, { reason: matches.length === 0 ? "not_found" : "response" }));
|
|
1008
719
|
}));
|
|
1009
720
|
const listRecords = Effect.fn("CloudflareClient.listRecords")((zoneName) => Effect.gen(function* () {
|
|
1010
721
|
const zone = yield* resolveZone(zoneName);
|
|
@@ -1041,28 +752,72 @@ function make$5(options) {
|
|
|
1041
752
|
const envelope = yield* decode$2(RecordEnvelope, body, "getRecord", response);
|
|
1042
753
|
return yield* decode$3(envelope.result);
|
|
1043
754
|
}));
|
|
755
|
+
const deleteRecord = Effect.fn("CloudflareClient.deleteRecord")((zoneName, providerRecordId) => Effect.gen(function* () {
|
|
756
|
+
const zone = yield* resolveZone(zoneName);
|
|
757
|
+
const { body, response } = yield* request(`/zones/${encodeURIComponent(zone.id)}/dns_records/${encodeURIComponent(providerRecordId)}`, "deleteRecord", { method: "DELETE" });
|
|
758
|
+
yield* ensureSuccess(body, "deleteRecord", response);
|
|
759
|
+
}));
|
|
760
|
+
const listAccounts = Effect.fn("CloudflareClient.listAccounts")(() => allZones().pipe(Effect.map((zones) => {
|
|
761
|
+
const accounts = /* @__PURE__ */ new Map();
|
|
762
|
+
for (const zone of zones) accounts.set(zone.account.id, zone.account);
|
|
763
|
+
return [...accounts.values()].sort((left, right) => left.name.localeCompare(right.name));
|
|
764
|
+
})));
|
|
765
|
+
const listZones = Effect.fn("CloudflareClient.listZones")((input = {}) => allZones(input.accountId ?? options.accountId, input.name).pipe(Effect.flatMap((zones) => Effect.forEach(zones, projectZone$1))));
|
|
766
|
+
const listTargets = Effect.fn("CloudflareClient.listTargets")((input = {}) => Effect.gen(function* () {
|
|
767
|
+
const names = input.domain === void 0 ? void 0 : new Set(candidates(input.domain).map((name) => String(name)));
|
|
768
|
+
const zones = yield* allZones(input.accountId ?? options.accountId);
|
|
769
|
+
return yield* Effect.forEach(zones.filter((zone) => zone.type !== "internal" && (names === void 0 || names.has(zone.name))), targetFromZone);
|
|
770
|
+
}));
|
|
1044
771
|
return {
|
|
1045
772
|
id: "cloudflare",
|
|
773
|
+
providerId: "cloudflare",
|
|
1046
774
|
createRecord,
|
|
1047
|
-
deleteRecord
|
|
1048
|
-
const zone = yield* resolveZone(zoneName);
|
|
1049
|
-
const { body, response } = yield* request(`/zones/${encodeURIComponent(zone.id)}/dns_records/${encodeURIComponent(providerRecordId)}`, "deleteRecord", { method: "DELETE" });
|
|
1050
|
-
yield* ensureSuccess(body, "deleteRecord", response);
|
|
1051
|
-
})),
|
|
775
|
+
deleteRecord,
|
|
1052
776
|
getRecord,
|
|
1053
|
-
listAccounts
|
|
1054
|
-
const accounts = /* @__PURE__ */ new Map();
|
|
1055
|
-
for (const zone of zones) accounts.set(zone.account.id, zone.account);
|
|
1056
|
-
return [...accounts.values()].sort((left, right) => left.name.localeCompare(right.name));
|
|
1057
|
-
}))),
|
|
777
|
+
listAccounts,
|
|
1058
778
|
listRecords,
|
|
1059
|
-
listZones
|
|
779
|
+
listZones,
|
|
780
|
+
listTargets,
|
|
781
|
+
resolveTarget: Effect.fn("CloudflareClient.resolveTarget")((domain) => Effect.gen(function* () {
|
|
782
|
+
const candidates$2 = yield* listTargets({ domain });
|
|
783
|
+
for (const zoneName of candidates(domain)) {
|
|
784
|
+
const matches = candidates$2.filter((target) => target.zoneName === zoneName);
|
|
785
|
+
if (matches.length === 1) {
|
|
786
|
+
const target = matches[0];
|
|
787
|
+
if (target !== void 0) return Resolution.Resolved({ target });
|
|
788
|
+
}
|
|
789
|
+
if (matches.length > 1) return Resolution.SelectionRequired({ candidates: matches });
|
|
790
|
+
}
|
|
791
|
+
return Resolution.NotFound({ domain });
|
|
792
|
+
})),
|
|
793
|
+
forTarget: Effect.fn("CloudflareClient.forTarget")((target) => Effect.gen(function* () {
|
|
794
|
+
if (target.accountKind !== null && target.accountKind !== "account") return yield* Effect.fail(failure$4("forTarget", "Cloudflare targets must use the account account kind", { reason: "request" }));
|
|
795
|
+
if (target.evidence?.zoneType === "internal") return yield* Effect.fail(failure$4("forTarget", "Cloudflare internal zones cannot host managed DNS records", { reason: "unsupported" }));
|
|
796
|
+
const selected = (yield* listTargets({
|
|
797
|
+
accountId: target.accountId,
|
|
798
|
+
domain: target.zoneName
|
|
799
|
+
})).find((candidate) => candidate.accountId === target.accountId && candidate.zoneId === target.zoneId && candidate.zoneName === target.zoneName);
|
|
800
|
+
if (selected === void 0) return yield* Effect.fail(failure$4("forTarget", `Cloudflare target ${target.zoneName} is not visible to this credential`, { reason: "authorization" }));
|
|
801
|
+
if (selected.evidence?.zoneType === "internal") return yield* Effect.fail(failure$4("forTarget", "Cloudflare internal zones cannot host managed DNS records", { reason: "unsupported" }));
|
|
802
|
+
const client = makeClient$1({
|
|
803
|
+
...options,
|
|
804
|
+
accountId: selected.accountId,
|
|
805
|
+
target: selected
|
|
806
|
+
});
|
|
807
|
+
return {
|
|
808
|
+
id: client.id,
|
|
809
|
+
createRecord: client.createRecord,
|
|
810
|
+
deleteRecord: client.deleteRecord,
|
|
811
|
+
getRecord: client.getRecord,
|
|
812
|
+
listRecords: client.listRecords
|
|
813
|
+
};
|
|
814
|
+
})),
|
|
1060
815
|
validateToken: Effect.fn("CloudflareClient.validateToken")(() => Effect.gen(function* () {
|
|
1061
|
-
const path = options.tokenKind === "account" ? `/accounts/${encodeURIComponent(options.accountId)}/tokens/verify` : "/user/tokens/verify";
|
|
816
|
+
const path = options.tokenKind === "account" ? options.accountId === void 0 ? yield* Effect.fail(failure$4("validateToken", "Cloudflare account tokens require an account ID", { reason: "request" })) : `/accounts/${encodeURIComponent(options.accountId)}/tokens/verify` : "/user/tokens/verify";
|
|
1062
817
|
const expiresAt = yield* inspectToken(path);
|
|
1063
818
|
yield* allZones(options.accountId);
|
|
1064
819
|
return {
|
|
1065
|
-
accountId: options.accountId,
|
|
820
|
+
accountId: options.accountId ?? "",
|
|
1066
821
|
capabilities: options.capabilities,
|
|
1067
822
|
expiresAt,
|
|
1068
823
|
scopes: []
|
|
@@ -1070,9 +825,25 @@ function make$5(options) {
|
|
|
1070
825
|
}))
|
|
1071
826
|
};
|
|
1072
827
|
}
|
|
828
|
+
const targetFromZone = Effect.fn("CloudflareClient.targetFromZone")((zone) => Effect.gen(function* () {
|
|
829
|
+
const name = yield* decode$5(zone.name).pipe(Effect.mapError((cause) => failure$4("listTargets", cause.message, { reason: "response" })));
|
|
830
|
+
const nameservers = yield* Effect.forEach(zone.name_servers, (nameserver) => decode$5(nameserver).pipe(Effect.mapError((cause) => failure$4("listTargets", cause.message, { reason: "response" }))));
|
|
831
|
+
return {
|
|
832
|
+
accountId: zone.account.id,
|
|
833
|
+
accountKind: "account",
|
|
834
|
+
evidence: {
|
|
835
|
+
accountName: zone.account.name,
|
|
836
|
+
nameservers,
|
|
837
|
+
status: zone.status ?? "unknown",
|
|
838
|
+
zoneType: zone.type ?? "unknown"
|
|
839
|
+
},
|
|
840
|
+
zoneId: zone.id,
|
|
841
|
+
zoneName: name
|
|
842
|
+
};
|
|
843
|
+
}));
|
|
1073
844
|
const projectZone$1 = Effect.fn("CloudflareClient.projectZone")((zone) => Effect.gen(function* () {
|
|
1074
|
-
const name = yield* decode$
|
|
1075
|
-
const nameservers = yield* Effect.forEach(zone.name_servers, (nameserver) => decode$
|
|
845
|
+
const name = yield* decode$5(zone.name).pipe(Effect.mapError((cause) => failure$4("listZones", cause.message, { reason: "response" })));
|
|
846
|
+
const nameservers = yield* Effect.forEach(zone.name_servers, (nameserver) => decode$5(nameserver).pipe(Effect.mapError((cause) => failure$4("listZones", cause.message, { reason: "response" }))));
|
|
1076
847
|
return {
|
|
1077
848
|
accountId: zone.account.id,
|
|
1078
849
|
id: zone.id,
|
|
@@ -1082,20 +853,20 @@ const projectZone$1 = Effect.fn("CloudflareClient.projectZone")((zone) => Effect
|
|
|
1082
853
|
};
|
|
1083
854
|
}));
|
|
1084
855
|
function decode$2(schema, input, operation, response) {
|
|
1085
|
-
return Schema.decodeUnknownEffect(schema)(input).pipe(Effect.mapError((cause) => failure$
|
|
856
|
+
return Schema.decodeUnknownEffect(schema)(input).pipe(Effect.mapError((cause) => failure$4(operation, `Cloudflare response did not match its API contract: ${cause.message}`, {
|
|
1086
857
|
reason: "response",
|
|
1087
858
|
status: response.status
|
|
1088
859
|
})));
|
|
1089
860
|
}
|
|
1090
861
|
function ensureSuccess(input, operation, response) {
|
|
1091
|
-
return Schema.decodeUnknownEffect(BaseEnvelope)(input).pipe(Effect.mapError(() => failure$
|
|
862
|
+
return Schema.decodeUnknownEffect(BaseEnvelope)(input).pipe(Effect.mapError(() => failure$4(operation, "Cloudflare response did not contain a valid API envelope", {
|
|
1092
863
|
reason: "response",
|
|
1093
864
|
status: response.status
|
|
1094
865
|
})), Effect.flatMap((envelope) => {
|
|
1095
866
|
if (response.ok && envelope.success) return Effect.void;
|
|
1096
867
|
const detail = envelope.errors[0];
|
|
1097
868
|
const retryAfterMs = retryAfter(response.headers.get("retry-after"));
|
|
1098
|
-
return Effect.fail(failure$
|
|
869
|
+
return Effect.fail(failure$4(operation, detail?.message ?? `Cloudflare request failed with HTTP ${response.status}`, {
|
|
1099
870
|
...detail === void 0 ? {} : { code: detail.code },
|
|
1100
871
|
reason: reason$1(response.status, detail?.code),
|
|
1101
872
|
...retryAfterMs === void 0 ? {} : { retryAfterMs },
|
|
@@ -1119,7 +890,7 @@ function retryAfter(value) {
|
|
|
1119
890
|
const date = Date.parse(value);
|
|
1120
891
|
return Number.isNaN(date) ? void 0 : Math.max(0, date - Date.now());
|
|
1121
892
|
}
|
|
1122
|
-
function failure$
|
|
893
|
+
function failure$4(operation, message, fields = {}) {
|
|
1123
894
|
return new Error$3({
|
|
1124
895
|
...fields,
|
|
1125
896
|
message,
|
|
@@ -1135,6 +906,8 @@ var auth_exports$1 = /* @__PURE__ */ __exportAll({
|
|
|
1135
906
|
oauthAuthentication: () => oauthAuthentication,
|
|
1136
907
|
oauthFlow: () => oauthFlow,
|
|
1137
908
|
oauthMethod: () => oauthMethod,
|
|
909
|
+
refreshCredential: () => refreshCredential,
|
|
910
|
+
restore: () => restore$1,
|
|
1138
911
|
subjectResolver: () => subjectResolver,
|
|
1139
912
|
tokenConnectionMethod: () => tokenConnectionMethod$1,
|
|
1140
913
|
tokenMethod: () => tokenMethod$1,
|
|
@@ -1146,7 +919,11 @@ const authorizationServer = {
|
|
|
1146
919
|
revocation_endpoint: "https://dash.cloudflare.com/oauth2/revoke",
|
|
1147
920
|
token_endpoint: "https://dash.cloudflare.com/oauth2/token"
|
|
1148
921
|
};
|
|
1149
|
-
const
|
|
922
|
+
const Context$2 = Schema.Struct({
|
|
923
|
+
accountId: Schema.optionalKey(Schema.String),
|
|
924
|
+
tokenKind: Schema.Literals(["account", "user"])
|
|
925
|
+
});
|
|
926
|
+
const contextCodec$1 = codec("cloudflare.v1", Context$2);
|
|
1150
927
|
/** Describes Cloudflare's caller-created API-token authorization method. */
|
|
1151
928
|
function tokenMethod$1(capabilities) {
|
|
1152
929
|
return {
|
|
@@ -1172,7 +949,7 @@ function manifest$1(options) {
|
|
|
1172
949
|
providerId: "cloudflare"
|
|
1173
950
|
};
|
|
1174
951
|
}
|
|
1175
|
-
/** Resolves the Cloudflare account selected by a completed OAuth
|
|
952
|
+
/** Resolves the Cloudflare account selected by a completed OAuth authorization. */
|
|
1176
953
|
function subjectResolver(options) {
|
|
1177
954
|
return (tokens, accessToken) => Effect.gen(function* () {
|
|
1178
955
|
const accountId = options.accountId ?? (yield* discoverAuthorizationAccount({
|
|
@@ -1228,12 +1005,15 @@ function tokenConnectionMethod$1(options) {
|
|
|
1228
1005
|
})),
|
|
1229
1006
|
credential: {
|
|
1230
1007
|
accessToken: token,
|
|
1008
|
+
expiresAt: validated.expiresAt,
|
|
1231
1009
|
refreshToken: null,
|
|
1232
1010
|
tokenType: "bearer"
|
|
1233
1011
|
},
|
|
1234
|
-
expiresAt: validated.expiresAt,
|
|
1235
1012
|
providerAccountId: validated.accountId,
|
|
1236
|
-
providerContext: yield* contextCodec$1.encode(
|
|
1013
|
+
providerContext: yield* contextCodec$1.encode(options.tokenKind === "account" ? {
|
|
1014
|
+
accountId: validated.accountId,
|
|
1015
|
+
tokenKind: "account"
|
|
1016
|
+
} : { tokenKind: "user" }),
|
|
1237
1017
|
scopes: [...validated.scopes]
|
|
1238
1018
|
};
|
|
1239
1019
|
}),
|
|
@@ -1242,7 +1022,7 @@ function tokenConnectionMethod$1(options) {
|
|
|
1242
1022
|
token: options.token
|
|
1243
1023
|
});
|
|
1244
1024
|
}
|
|
1245
|
-
/** Converts a completed Cloudflare OAuth
|
|
1025
|
+
/** Converts a completed Cloudflare OAuth authorization into canonical connection authentication. */
|
|
1246
1026
|
const oauthAuthentication = Effect.fn("CloudflareAuth.oauthAuthentication")(function* (options) {
|
|
1247
1027
|
const resolved = yield* subjectResolver(options)(options.tokens, options.accessToken);
|
|
1248
1028
|
const observedAt = new Date(yield* Clock.currentTimeMillis);
|
|
@@ -1253,15 +1033,76 @@ const oauthAuthentication = Effect.fn("CloudflareAuth.oauthAuthentication")(func
|
|
|
1253
1033
|
})),
|
|
1254
1034
|
credential: {
|
|
1255
1035
|
accessToken: options.accessToken,
|
|
1036
|
+
expiresAt: resolved.expiresAt,
|
|
1256
1037
|
refreshToken: options.tokens.refresh_token === void 0 ? null : Value.from(options.tokens.refresh_token),
|
|
1257
1038
|
tokenType: options.tokens.token_type
|
|
1258
1039
|
},
|
|
1259
|
-
expiresAt: resolved.expiresAt,
|
|
1260
1040
|
providerAccountId: resolved.accountId,
|
|
1261
1041
|
providerContext: yield* contextCodec$1.encode({ tokenKind: "user" }),
|
|
1262
1042
|
scopes: (options.tokens.scope ?? "").split(" ").filter(Boolean)
|
|
1263
1043
|
};
|
|
1264
1044
|
});
|
|
1045
|
+
/** Restore a credential-scoped Cloudflare session from persisted authorization state. */
|
|
1046
|
+
function restore$1(options) {
|
|
1047
|
+
return Effect.gen(function* () {
|
|
1048
|
+
if (options.authorization.providerId !== "cloudflare") return yield* Effect.fail(failure$3("restore", "Provider authorization belongs to another provider", "authorization"));
|
|
1049
|
+
if (options.authorization.method === "integration") return yield* Effect.fail(failure$3("restore", "Cloudflare does not support integration credentials", "unsupported"));
|
|
1050
|
+
if (options.authorization.revocation._tag !== "Active") return yield* Effect.fail(failure$3("restore", "Cloudflare authorization is pending revocation", "authorization"));
|
|
1051
|
+
const missingCapability = options.authorization.requiredCapabilities.find((capability) => !options.authorization.capabilityEvidence.some((item) => item.capability === capability));
|
|
1052
|
+
if (missingCapability !== void 0) return yield* Effect.fail(failure$3("restore", `Cloudflare authorization lacks evidence for ${missingCapability}`, "authorization"));
|
|
1053
|
+
if (options.credential.expiresAt !== null && (Number.isNaN(options.credential.expiresAt.valueOf()) || options.credential.expiresAt <= /* @__PURE__ */ new Date())) return yield* Effect.fail(failure$3("restore", "Cloudflare authorization has expired", "authentication"));
|
|
1054
|
+
const context = yield* contextCodec$1.decode(options.authorization.providerContext).pipe(Effect.mapError((cause) => failure$3("restore", cause.message, "response")));
|
|
1055
|
+
return make$5({
|
|
1056
|
+
...context.accountId === void 0 ? {} : { accountId: context.accountId },
|
|
1057
|
+
...options.baseUrl === void 0 ? {} : { baseUrl: options.baseUrl },
|
|
1058
|
+
capabilities: options.authorization.capabilityEvidence.map(({ capability }) => capability),
|
|
1059
|
+
...options.fetch === void 0 ? {} : { fetch: options.fetch },
|
|
1060
|
+
token: options.credential.accessToken,
|
|
1061
|
+
tokenKind: context.tokenKind
|
|
1062
|
+
});
|
|
1063
|
+
});
|
|
1064
|
+
}
|
|
1065
|
+
/** Exchanges a Cloudflare refresh token for a fresh provider credential. */
|
|
1066
|
+
const refreshCredential = Effect.fn("CloudflareAuth.refreshCredential")(function* (options) {
|
|
1067
|
+
if (options.credential.refreshToken === null) return yield* new Error$2({
|
|
1068
|
+
category: "authorization",
|
|
1069
|
+
message: "Cloudflare credential has no refresh token",
|
|
1070
|
+
operation: "CloudflareAuth.refreshCredential",
|
|
1071
|
+
retry: "after-user-action"
|
|
1072
|
+
});
|
|
1073
|
+
const refreshToken = options.credential.refreshToken;
|
|
1074
|
+
const client = { client_id: options.client.clientId };
|
|
1075
|
+
const authentication = yield* Effect.try({
|
|
1076
|
+
try: () => clientAuthentication(options.clientAuth, options.client),
|
|
1077
|
+
catch: () => new Error$2({
|
|
1078
|
+
category: "authorization",
|
|
1079
|
+
message: "Cloudflare OAuth client authentication is invalid",
|
|
1080
|
+
operation: "CloudflareAuth.refreshCredential",
|
|
1081
|
+
retry: "never"
|
|
1082
|
+
})
|
|
1083
|
+
});
|
|
1084
|
+
const fetch = options.fetch;
|
|
1085
|
+
const tokens = yield* Effect.tryPromise({
|
|
1086
|
+
try: async (signal) => {
|
|
1087
|
+
const request = await oauth.refreshTokenGrantRequest(authorizationServer, client, authentication, refreshToken.expose(), fetch === void 0 ? { signal } : {
|
|
1088
|
+
signal,
|
|
1089
|
+
[oauth.customFetch]: (url, init) => fetch(url, {
|
|
1090
|
+
...init,
|
|
1091
|
+
body: init.body
|
|
1092
|
+
})
|
|
1093
|
+
});
|
|
1094
|
+
return oauth.processRefreshTokenResponse(authorizationServer, client, request);
|
|
1095
|
+
},
|
|
1096
|
+
catch: (cause) => refreshFailure(cause)
|
|
1097
|
+
});
|
|
1098
|
+
const now = yield* Clock.currentTimeMillis;
|
|
1099
|
+
return {
|
|
1100
|
+
accessToken: Value.from(tokens.access_token),
|
|
1101
|
+
expiresAt: typeof tokens.expires_in === "number" ? new Date(now + tokens.expires_in * 1e3) : null,
|
|
1102
|
+
refreshToken: tokens.refresh_token === void 0 ? refreshToken : Value.from(tokens.refresh_token),
|
|
1103
|
+
tokenType: tokens.token_type
|
|
1104
|
+
};
|
|
1105
|
+
});
|
|
1265
1106
|
/** Creates Cloudflare's OAuth implementation of the common interactive connection capability. */
|
|
1266
1107
|
function oauthFlow(options) {
|
|
1267
1108
|
const method = oauthMethod({
|
|
@@ -1299,6 +1140,28 @@ function oauthFlow(options) {
|
|
|
1299
1140
|
})
|
|
1300
1141
|
};
|
|
1301
1142
|
}
|
|
1143
|
+
function failure$3(operation, message, reason) {
|
|
1144
|
+
return new Error$3({
|
|
1145
|
+
message,
|
|
1146
|
+
operation,
|
|
1147
|
+
providerId: "cloudflare",
|
|
1148
|
+
reason
|
|
1149
|
+
});
|
|
1150
|
+
}
|
|
1151
|
+
function clientAuthentication(method, client) {
|
|
1152
|
+
if (method === "none") return oauth.None();
|
|
1153
|
+
if (client.clientSecret === void 0) throw new Error("Client secret is required");
|
|
1154
|
+
return method === "client_secret_basic" ? oauth.ClientSecretBasic(client.clientSecret.expose()) : oauth.ClientSecretPost(client.clientSecret.expose());
|
|
1155
|
+
}
|
|
1156
|
+
function refreshFailure(cause) {
|
|
1157
|
+
if (cause instanceof oauth.ResponseBodyError && ["invalid_client", "invalid_grant"].includes(cause.error)) return new Error$2({
|
|
1158
|
+
category: "authorization",
|
|
1159
|
+
message: "Cloudflare authorization can no longer be refreshed",
|
|
1160
|
+
operation: "CloudflareAuth.refreshCredential",
|
|
1161
|
+
retry: "after-user-action"
|
|
1162
|
+
});
|
|
1163
|
+
return failure$3("refreshCredential", cause instanceof Error ? cause.message : "Cloudflare token refresh failed", cause instanceof oauth.ResponseBodyError ? "response" : "transport");
|
|
1164
|
+
}
|
|
1302
1165
|
//#endregion
|
|
1303
1166
|
//#region src/providers/vercel/protocol.ts
|
|
1304
1167
|
const Pagination = Schema.Struct({
|
|
@@ -1388,7 +1251,7 @@ const IntegrationToken = Schema.Struct({
|
|
|
1388
1251
|
//#region src/providers/vercel/records.ts
|
|
1389
1252
|
const decode$1 = Effect.fn("VercelRecords.decode")((zone, record) => Effect.gen(function* () {
|
|
1390
1253
|
const absolute = absoluteNameCandidate(zone, record.name);
|
|
1391
|
-
const name = yield* decode$
|
|
1254
|
+
const name = yield* decode$5(absolute).pipe(Effect.match({
|
|
1392
1255
|
onFailure: () => null,
|
|
1393
1256
|
onSuccess: (value) => value
|
|
1394
1257
|
}));
|
|
@@ -1403,7 +1266,7 @@ const decode$1 = Effect.fn("VercelRecords.decode")((zone, record) => Effect.gen(
|
|
|
1403
1266
|
policy: record.type === "CNAME" ? "exclusive" : "append",
|
|
1404
1267
|
ttl: record.ttl ?? 60
|
|
1405
1268
|
};
|
|
1406
|
-
const fromRecord = (input) => decode$
|
|
1269
|
+
const fromRecord = (input) => decode$6(input).pipe(Effect.mapError((cause) => failure$2("decodeRecord", cause.message)));
|
|
1407
1270
|
switch (record.type) {
|
|
1408
1271
|
case "A":
|
|
1409
1272
|
case "AAAA": return yield* fromRecord({
|
|
@@ -1544,6 +1407,9 @@ function discovery(client) {
|
|
|
1544
1407
|
}
|
|
1545
1408
|
/** Creates an Effect-native Vercel client without owning the credential lifecycle. */
|
|
1546
1409
|
function make$4(options) {
|
|
1410
|
+
return makeClient(options);
|
|
1411
|
+
}
|
|
1412
|
+
function makeClient(options) {
|
|
1547
1413
|
const fetch = options.fetch ?? globalThis.fetch;
|
|
1548
1414
|
const baseUrl = (options.baseUrl ?? "https://api.vercel.com").replace(/\/$/, "");
|
|
1549
1415
|
const withContext = (path, values = {}) => {
|
|
@@ -1615,9 +1481,13 @@ function make$4(options) {
|
|
|
1615
1481
|
return envelope.domain;
|
|
1616
1482
|
}).pipe(Effect.catchTag("ProviderError", (cause) => cause.reason === "not_found" ? Effect.succeed(null) : Effect.fail(cause))));
|
|
1617
1483
|
const resolveZone = Effect.fn("VercelClient.resolveZone")((name) => Effect.gen(function* () {
|
|
1484
|
+
if (options.target !== void 0) {
|
|
1485
|
+
if (name !== options.target.zoneName && !name.endsWith(`.${options.target.zoneName}`)) return yield* Effect.fail(failure$1("resolveZone", `Vercel target ${options.target.zoneName} does not cover ${name}`, { reason: "not_found" }));
|
|
1486
|
+
return options.target.zoneName;
|
|
1487
|
+
}
|
|
1618
1488
|
const domain = yield* discoverZone(name);
|
|
1619
1489
|
if (domain === null) return yield* Effect.fail(failure$1("resolveZone", `Vercel zone ${name} is not configured for authoritative DNS in the selected account`, { reason: "not_found" }));
|
|
1620
|
-
return yield* decode$
|
|
1490
|
+
return yield* decode$5(domain.name).pipe(Effect.mapError((cause) => failure$1("resolveZone", `Vercel returned an invalid storage zone: ${cause.message}`)));
|
|
1621
1491
|
}));
|
|
1622
1492
|
const listRecords = Effect.fn("VercelClient.listRecords")((zoneName) => Effect.gen(function* () {
|
|
1623
1493
|
const storageZone = yield* resolveZone(zoneName);
|
|
@@ -1661,34 +1531,84 @@ function make$4(options) {
|
|
|
1661
1531
|
until = next;
|
|
1662
1532
|
}
|
|
1663
1533
|
}));
|
|
1534
|
+
const deleteRecord = Effect.fn("VercelClient.deleteRecord")((zoneName, providerRecordId) => Effect.gen(function* () {
|
|
1535
|
+
const storageZone = yield* resolveZone(zoneName);
|
|
1536
|
+
yield* request(withContext(`/v2/domains/${encodeURIComponent(storageZone)}/records/${encodeURIComponent(providerRecordId)}`), "deleteRecord", { method: "DELETE" });
|
|
1537
|
+
}));
|
|
1538
|
+
const listAccounts = Effect.fn("VercelClient.listAccounts")(() => Effect.gen(function* () {
|
|
1539
|
+
const userResult = yield* request(new URL(`${baseUrl}/v2/user`), "listAccounts");
|
|
1540
|
+
const { user } = yield* decode(UserEnvelope, userResult.body, "listAccounts", userResult.response);
|
|
1541
|
+
const teams = yield* allTeams();
|
|
1542
|
+
return [{
|
|
1543
|
+
id: user.id,
|
|
1544
|
+
name: user.name ?? user.username,
|
|
1545
|
+
type: "personal"
|
|
1546
|
+
}, ...teams.map((team) => ({
|
|
1547
|
+
id: team.id,
|
|
1548
|
+
name: team.name ?? team.slug,
|
|
1549
|
+
type: "team"
|
|
1550
|
+
}))];
|
|
1551
|
+
}));
|
|
1552
|
+
const listZones = Effect.fn("VercelClient.listZones")((input = {}) => Effect.gen(function* () {
|
|
1553
|
+
const domains = (yield* allDomains()).filter((domain) => hasDnsStorage(domain) && (input.name === void 0 || domain.name === input.name));
|
|
1554
|
+
if (domains.length > 0 || input.name === void 0) return yield* Effect.forEach(domains, projectZone);
|
|
1555
|
+
const discovered = yield* discoverZone(input.name);
|
|
1556
|
+
return discovered === null ? [] : [yield* projectZone(discovered)];
|
|
1557
|
+
}));
|
|
1558
|
+
const listTargets = Effect.fn("VercelClient.listTargets")((input = {}) => Effect.gen(function* () {
|
|
1559
|
+
const names = input.domain === void 0 ? void 0 : new Set(candidates(input.domain).map((name) => String(name)));
|
|
1560
|
+
const domains = (yield* allDomains()).filter((domain) => hasDnsStorage(domain) && (names === void 0 || names.has(domain.name)) && (input.accountId === void 0 || (domain.teamId ?? domain.userId) === input.accountId));
|
|
1561
|
+
if (domains.length > 0 || input.domain === void 0) return yield* Effect.forEach(domains, targetFromDomain);
|
|
1562
|
+
for (const name of candidates(input.domain)) {
|
|
1563
|
+
const discovered = yield* discoverZone(name);
|
|
1564
|
+
if (discovered !== null && (input.accountId === void 0 || (discovered.teamId ?? discovered.userId) === input.accountId)) return [yield* targetFromDomain(discovered)];
|
|
1565
|
+
}
|
|
1566
|
+
return [];
|
|
1567
|
+
}));
|
|
1664
1568
|
return {
|
|
1665
1569
|
id: "vercel",
|
|
1570
|
+
providerId: "vercel",
|
|
1666
1571
|
createRecord,
|
|
1667
|
-
deleteRecord
|
|
1668
|
-
const storageZone = yield* resolveZone(zoneName);
|
|
1669
|
-
yield* request(withContext(`/v2/domains/${encodeURIComponent(storageZone)}/records/${encodeURIComponent(providerRecordId)}`), "deleteRecord", { method: "DELETE" });
|
|
1670
|
-
})),
|
|
1572
|
+
deleteRecord,
|
|
1671
1573
|
getRecord,
|
|
1672
|
-
listAccounts
|
|
1673
|
-
const userResult = yield* request(new URL(`${baseUrl}/v2/user`), "listAccounts");
|
|
1674
|
-
const { user } = yield* decode(UserEnvelope, userResult.body, "listAccounts", userResult.response);
|
|
1675
|
-
const teams = yield* allTeams();
|
|
1676
|
-
return [{
|
|
1677
|
-
id: user.id,
|
|
1678
|
-
name: user.name ?? user.username,
|
|
1679
|
-
type: "personal"
|
|
1680
|
-
}, ...teams.map((team) => ({
|
|
1681
|
-
id: team.id,
|
|
1682
|
-
name: team.name ?? team.slug,
|
|
1683
|
-
type: "team"
|
|
1684
|
-
}))];
|
|
1685
|
-
})),
|
|
1574
|
+
listAccounts,
|
|
1686
1575
|
listRecords,
|
|
1687
|
-
listZones
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
const
|
|
1691
|
-
|
|
1576
|
+
listZones,
|
|
1577
|
+
listTargets,
|
|
1578
|
+
resolveTarget: Effect.fn("VercelClient.resolveTarget")((domain) => Effect.gen(function* () {
|
|
1579
|
+
const candidates$1 = yield* listTargets({ domain });
|
|
1580
|
+
for (const zoneName of candidates(domain)) {
|
|
1581
|
+
const matches = candidates$1.filter((target) => target.zoneName === zoneName);
|
|
1582
|
+
if (matches.length === 1) {
|
|
1583
|
+
const target = matches[0];
|
|
1584
|
+
if (target !== void 0) return Resolution.Resolved({ target });
|
|
1585
|
+
}
|
|
1586
|
+
if (matches.length > 1) return Resolution.SelectionRequired({ candidates: matches });
|
|
1587
|
+
}
|
|
1588
|
+
return Resolution.NotFound({ domain });
|
|
1589
|
+
})),
|
|
1590
|
+
forTarget: Effect.fn("VercelClient.forTarget")((target) => Effect.gen(function* () {
|
|
1591
|
+
if (target.accountKind === "team") {
|
|
1592
|
+
if (options.context._tag !== "team" || options.context.teamId !== target.accountId) return yield* Effect.fail(failure$1("forTarget", "Vercel team target is outside the selected installation", { reason: "authorization" }));
|
|
1593
|
+
} else if (target.accountKind === "personal" && options.context._tag !== "personal") return yield* Effect.fail(failure$1("forTarget", "Vercel personal target is outside the selected installation", { reason: "authorization" }));
|
|
1594
|
+
else if (target.accountKind === "account") return yield* Effect.fail(failure$1("forTarget", "Vercel does not support account-kind targets", { reason: "unsupported" }));
|
|
1595
|
+
else if (target.accountKind === null) return yield* Effect.fail(failure$1("forTarget", "Vercel targets must identify a personal or team account", { reason: "request" }));
|
|
1596
|
+
const selected = (yield* listTargets({
|
|
1597
|
+
accountId: target.accountId,
|
|
1598
|
+
domain: target.zoneName
|
|
1599
|
+
})).find((candidate) => candidate.accountId === target.accountId && candidate.accountKind === target.accountKind && candidate.zoneId === target.zoneId && candidate.zoneName === target.zoneName);
|
|
1600
|
+
if (selected === void 0) return yield* Effect.fail(failure$1("forTarget", `Vercel target ${target.zoneName} is not visible to this installation`, { reason: "authorization" }));
|
|
1601
|
+
const client = makeClient({
|
|
1602
|
+
...options,
|
|
1603
|
+
target: selected
|
|
1604
|
+
});
|
|
1605
|
+
return {
|
|
1606
|
+
id: client.id,
|
|
1607
|
+
createRecord: client.createRecord,
|
|
1608
|
+
deleteRecord: client.deleteRecord,
|
|
1609
|
+
getRecord: client.getRecord,
|
|
1610
|
+
listRecords: client.listRecords
|
|
1611
|
+
};
|
|
1692
1612
|
})),
|
|
1693
1613
|
validateToken: Effect.fn("VercelClient.validateToken")(() => Effect.gen(function* () {
|
|
1694
1614
|
let accountId;
|
|
@@ -1713,8 +1633,8 @@ function hasDnsStorage(domain, configuredServiceType = domain.serviceType) {
|
|
|
1713
1633
|
return configuredServiceType === "zeit.world" || domain.zone === true || domain.intendedNameservers.length > 0;
|
|
1714
1634
|
}
|
|
1715
1635
|
const projectZone = Effect.fn("VercelClient.projectZone")((domain) => Effect.gen(function* () {
|
|
1716
|
-
const name = yield* decode$
|
|
1717
|
-
const nameservers = yield* Effect.forEach(domain.intendedNameservers, (nameserver) => decode$
|
|
1636
|
+
const name = yield* decode$5(domain.name).pipe(Effect.mapError((cause) => failure$1("listZones", cause.message, { reason: "response" })));
|
|
1637
|
+
const nameservers = yield* Effect.forEach(domain.intendedNameservers, (nameserver) => decode$5(nameserver).pipe(Effect.mapError((cause) => failure$1("listZones", cause.message, { reason: "response" }))));
|
|
1718
1638
|
return {
|
|
1719
1639
|
accountId: domain.teamId ?? domain.userId,
|
|
1720
1640
|
id: domain.id,
|
|
@@ -1723,6 +1643,21 @@ const projectZone = Effect.fn("VercelClient.projectZone")((domain) => Effect.gen
|
|
|
1723
1643
|
status: domain.verified ? "active" : "pending"
|
|
1724
1644
|
};
|
|
1725
1645
|
}));
|
|
1646
|
+
const targetFromDomain = Effect.fn("VercelClient.targetFromDomain")((domain) => Effect.gen(function* () {
|
|
1647
|
+
const zoneName = yield* decode$5(domain.name).pipe(Effect.mapError((cause) => failure$1("listTargets", cause.message, { reason: "response" })));
|
|
1648
|
+
const nameservers = yield* Effect.forEach(domain.nameservers.length > 0 ? domain.nameservers : domain.intendedNameservers, (nameserver) => decode$5(nameserver).pipe(Effect.mapError((cause) => failure$1("listTargets", cause.message, { reason: "response" }))));
|
|
1649
|
+
return {
|
|
1650
|
+
accountId: domain.teamId ?? domain.userId,
|
|
1651
|
+
accountKind: domain.teamId === null ? "personal" : "team",
|
|
1652
|
+
evidence: {
|
|
1653
|
+
nameservers,
|
|
1654
|
+
status: domain.verified ? "active" : "pending",
|
|
1655
|
+
zoneType: domain.serviceType
|
|
1656
|
+
},
|
|
1657
|
+
zoneId: domain.id,
|
|
1658
|
+
zoneName
|
|
1659
|
+
};
|
|
1660
|
+
}));
|
|
1726
1661
|
function decode(schema, input, operation, response) {
|
|
1727
1662
|
return Schema.decodeUnknownEffect(schema)(input).pipe(Effect.mapError((cause) => failure$1(operation, `Vercel response did not match its API contract: ${cause.message}`, {
|
|
1728
1663
|
reason: "response",
|
|
@@ -1770,16 +1705,18 @@ var auth_exports = /* @__PURE__ */ __exportAll({
|
|
|
1770
1705
|
integrationFlow: () => integrationFlow,
|
|
1771
1706
|
integrationMethod: () => integrationMethod,
|
|
1772
1707
|
manifest: () => manifest,
|
|
1708
|
+
restore: () => restore,
|
|
1773
1709
|
tokenConnectionMethod: () => tokenConnectionMethod,
|
|
1774
1710
|
tokenMethod: () => tokenMethod
|
|
1775
1711
|
});
|
|
1776
|
-
const
|
|
1712
|
+
const Context$1 = Schema.TaggedUnion({
|
|
1777
1713
|
personal: { installationId: Schema.NullOr(Schema.String) },
|
|
1778
1714
|
team: {
|
|
1779
1715
|
installationId: Schema.NullOr(Schema.String),
|
|
1780
1716
|
teamId: Schema.String
|
|
1781
1717
|
}
|
|
1782
|
-
})
|
|
1718
|
+
});
|
|
1719
|
+
const contextCodec = codec("vercel.v1", Context$1);
|
|
1783
1720
|
/** Describes Vercel's caller-created personal access-token method. */
|
|
1784
1721
|
function tokenMethod(capabilities) {
|
|
1785
1722
|
return {
|
|
@@ -1794,7 +1731,7 @@ function integrationMethod(options) {
|
|
|
1794
1731
|
_tag: "integration",
|
|
1795
1732
|
capabilities: [...options.capabilities],
|
|
1796
1733
|
installUrl: `https://vercel.com/integrations/${encodeURIComponent(options.slug)}/new`,
|
|
1797
|
-
tokenEndpoint: "https://api.vercel.com/oauth/access_token"
|
|
1734
|
+
tokenEndpoint: "https://api.vercel.com/v2/oauth/access_token"
|
|
1798
1735
|
};
|
|
1799
1736
|
}
|
|
1800
1737
|
/** Creates the Vercel token and integration authorization manifest. */
|
|
@@ -1815,7 +1752,7 @@ const exchangeCode = Effect.fn("VercelAuth.exchangeCode")((options) => Effect.ge
|
|
|
1815
1752
|
redirect_uri: options.redirectUri
|
|
1816
1753
|
});
|
|
1817
1754
|
const response = yield* Effect.tryPromise({
|
|
1818
|
-
try: () => fetch(`${baseUrl}/oauth/access_token`, {
|
|
1755
|
+
try: () => fetch(`${baseUrl}/v2/oauth/access_token`, {
|
|
1819
1756
|
body,
|
|
1820
1757
|
headers: {
|
|
1821
1758
|
Accept: "application/json",
|
|
@@ -1840,7 +1777,7 @@ const exchangeCode = Effect.fn("VercelAuth.exchangeCode")((options) => Effect.ge
|
|
|
1840
1777
|
_tag: "team",
|
|
1841
1778
|
teamId: token.team_id
|
|
1842
1779
|
},
|
|
1843
|
-
installationId: token.installation_id ?? null,
|
|
1780
|
+
installationId: token.installation_id ?? options.configurationId ?? null,
|
|
1844
1781
|
userId: token.user_id
|
|
1845
1782
|
};
|
|
1846
1783
|
}));
|
|
@@ -1861,10 +1798,10 @@ function tokenConnectionMethod(options) {
|
|
|
1861
1798
|
})),
|
|
1862
1799
|
credential: {
|
|
1863
1800
|
accessToken: token,
|
|
1801
|
+
expiresAt: validated.expiresAt,
|
|
1864
1802
|
refreshToken: null,
|
|
1865
1803
|
tokenType: "bearer"
|
|
1866
1804
|
},
|
|
1867
|
-
expiresAt: validated.expiresAt,
|
|
1868
1805
|
providerAccountId: validated.accountId,
|
|
1869
1806
|
providerContext: yield* contextCodec.encode(options.context._tag === "team" ? {
|
|
1870
1807
|
_tag: "team",
|
|
@@ -1893,10 +1830,10 @@ const integrationAuthentication = Effect.fn("VercelAuth.integrationAuthenticatio
|
|
|
1893
1830
|
})),
|
|
1894
1831
|
credential: {
|
|
1895
1832
|
accessToken: exchanged.accessToken,
|
|
1833
|
+
expiresAt: null,
|
|
1896
1834
|
refreshToken: null,
|
|
1897
1835
|
tokenType: "bearer"
|
|
1898
1836
|
},
|
|
1899
|
-
expiresAt: null,
|
|
1900
1837
|
providerAccountId: exchanged.context._tag === "team" ? exchanged.context.teamId : exchanged.userId,
|
|
1901
1838
|
providerContext: yield* contextCodec.encode(exchanged.context._tag === "team" ? {
|
|
1902
1839
|
_tag: "team",
|
|
@@ -1909,34 +1846,73 @@ const integrationAuthentication = Effect.fn("VercelAuth.integrationAuthenticatio
|
|
|
1909
1846
|
scopes: []
|
|
1910
1847
|
};
|
|
1911
1848
|
});
|
|
1849
|
+
/** Restore a credential-scoped Vercel session from persisted authorization state. */
|
|
1850
|
+
function restore(options) {
|
|
1851
|
+
return Effect.gen(function* () {
|
|
1852
|
+
if (options.authorization.providerId !== "vercel") return yield* Effect.fail(failure("restore", "Provider authorization belongs to another provider", "authorization"));
|
|
1853
|
+
if (options.authorization.revocation._tag !== "Active") return yield* Effect.fail(failure("restore", "Vercel authorization is pending revocation", "authorization"));
|
|
1854
|
+
const missingCapability = options.authorization.requiredCapabilities.find((capability) => !options.authorization.capabilityEvidence.some((item) => item.capability === capability));
|
|
1855
|
+
if (missingCapability !== void 0) return yield* Effect.fail(failure("restore", `Vercel authorization lacks evidence for ${missingCapability}`, "authorization"));
|
|
1856
|
+
if (options.credential.expiresAt !== null && (Number.isNaN(options.credential.expiresAt.valueOf()) || options.credential.expiresAt <= /* @__PURE__ */ new Date())) return yield* Effect.fail(failure("restore", "Vercel authorization has expired", "authentication"));
|
|
1857
|
+
const context = yield* contextCodec.decode(options.authorization.providerContext).pipe(Effect.mapError((cause) => failure("restore", cause.message, "response")));
|
|
1858
|
+
return make$4({
|
|
1859
|
+
...options.baseUrl === void 0 ? {} : { baseUrl: options.baseUrl },
|
|
1860
|
+
capabilities: options.authorization.capabilityEvidence.map(({ capability }) => capability),
|
|
1861
|
+
...options.fetch === void 0 ? {} : { fetch: options.fetch },
|
|
1862
|
+
context: context._tag === "team" ? {
|
|
1863
|
+
_tag: "team",
|
|
1864
|
+
teamId: context.teamId
|
|
1865
|
+
} : { _tag: "personal" },
|
|
1866
|
+
token: options.credential.accessToken
|
|
1867
|
+
});
|
|
1868
|
+
});
|
|
1869
|
+
}
|
|
1912
1870
|
/** Creates Vercel Integration's implementation of the common interactive connection capability. */
|
|
1913
1871
|
function integrationFlow(options) {
|
|
1914
1872
|
const method = integrationMethod(options);
|
|
1915
1873
|
const Payload = Schema.fromJsonString(Schema.Struct({ state: Schema.String }));
|
|
1916
1874
|
return {
|
|
1917
1875
|
complete: Effect.fn("VercelAuth.integrationFlow.complete")(function* (payload, callbackUrl) {
|
|
1918
|
-
const continuation = yield* Schema.decodeUnknownEffect(Payload)(payload.expose()).pipe(Effect.mapError(() => new Error$
|
|
1876
|
+
const continuation = yield* Schema.decodeUnknownEffect(Payload)(payload.expose()).pipe(Effect.mapError(() => new Error$2({
|
|
1919
1877
|
category: "authorization",
|
|
1920
1878
|
message: "Vercel Integration continuation payload is invalid",
|
|
1921
1879
|
operation: "VercelAuth.integrationFlow.complete",
|
|
1922
1880
|
retry: "after-user-action"
|
|
1923
1881
|
})));
|
|
1924
1882
|
const code = callbackUrl.searchParams.get("code");
|
|
1925
|
-
|
|
1883
|
+
const configurationId = callbackUrl.searchParams.get("configurationId");
|
|
1884
|
+
if (callbackUrl.searchParams.get("state") !== continuation.state || code === null || configurationId === null) return yield* new Error$2({
|
|
1926
1885
|
category: "authorization",
|
|
1927
1886
|
message: "Vercel Integration callback does not match its continuation",
|
|
1928
1887
|
operation: "VercelAuth.integrationFlow.complete",
|
|
1929
1888
|
retry: "after-user-action"
|
|
1930
1889
|
});
|
|
1931
|
-
|
|
1890
|
+
const authentication = yield* integrationAuthentication({
|
|
1932
1891
|
...options.baseUrl === void 0 ? {} : { baseUrl: options.baseUrl },
|
|
1933
1892
|
capabilities: options.capabilities,
|
|
1934
1893
|
clientId: options.clientId,
|
|
1935
1894
|
clientSecret: options.clientSecret,
|
|
1936
1895
|
code: make$6(code),
|
|
1896
|
+
configurationId,
|
|
1937
1897
|
...options.fetch === void 0 ? {} : { fetch: options.fetch },
|
|
1938
1898
|
redirectUri: options.redirectUri
|
|
1939
1899
|
});
|
|
1900
|
+
const callbackTeamId = callbackUrl.searchParams.get("teamId");
|
|
1901
|
+
if (callbackTeamId !== null) {
|
|
1902
|
+
const context = yield* contextCodec.decode(authentication.providerContext).pipe(Effect.mapError((cause) => new Error$2({
|
|
1903
|
+
category: "provider",
|
|
1904
|
+
message: cause.message,
|
|
1905
|
+
operation: "VercelAuth.integrationFlow.complete",
|
|
1906
|
+
retry: "unknown"
|
|
1907
|
+
})));
|
|
1908
|
+
if (context._tag !== "team" || context.teamId !== callbackTeamId) return yield* new Error$2({
|
|
1909
|
+
category: "authorization",
|
|
1910
|
+
message: "Vercel Integration callback team does not match the exchanged installation",
|
|
1911
|
+
operation: "VercelAuth.integrationFlow.complete",
|
|
1912
|
+
retry: "after-user-action"
|
|
1913
|
+
});
|
|
1914
|
+
}
|
|
1915
|
+
return authentication;
|
|
1940
1916
|
}),
|
|
1941
1917
|
method: "integration",
|
|
1942
1918
|
providerId: "vercel",
|
|
@@ -2006,7 +1982,7 @@ function parse(type, input) {
|
|
|
2006
1982
|
try {
|
|
2007
1983
|
return Schema.decodeUnknownSync(schema)(input);
|
|
2008
1984
|
} catch (cause) {
|
|
2009
|
-
throw new Error$
|
|
1985
|
+
throw new Error$1({ message: cause instanceof Error ? cause.message : String(cause) });
|
|
2010
1986
|
}
|
|
2011
1987
|
}
|
|
2012
1988
|
function tokenizedDomain(length, domainIndex) {
|
|
@@ -2051,7 +2027,7 @@ function make$3(options) {
|
|
|
2051
2027
|
}).pipe(Effect.flatMap((message) => decodeResolution(query, message)))) };
|
|
2052
2028
|
}
|
|
2053
2029
|
const layer$2 = (options) => Layer.succeed(Service$2, make$3(options));
|
|
2054
|
-
const toAsync$2 = (options) => toAsync$
|
|
2030
|
+
const toAsync$2 = (options) => toAsync$5(make$3(options));
|
|
2055
2031
|
function decodeResolution(query, message) {
|
|
2056
2032
|
return Effect.try({
|
|
2057
2033
|
try: () => DnsPacket.decode(message),
|
|
@@ -2109,7 +2085,7 @@ function decodeAnswer(answer) {
|
|
|
2109
2085
|
})
|
|
2110
2086
|
});
|
|
2111
2087
|
if (decoded === void 0) return null;
|
|
2112
|
-
const name = yield* decode$
|
|
2088
|
+
const name = yield* decode$5(answer.name);
|
|
2113
2089
|
const data = yield* Effect.try({
|
|
2114
2090
|
try: () => parse(decoded.type, decoded.data),
|
|
2115
2091
|
catch: (cause) => new Error$4({
|
|
@@ -2249,7 +2225,7 @@ function make$2(options = {}) {
|
|
|
2249
2225
|
});
|
|
2250
2226
|
}
|
|
2251
2227
|
const layer$1 = (options = {}) => Layer.succeed(Service$2, make$2(options));
|
|
2252
|
-
const toAsync$1 = (options = {}) => toAsync$
|
|
2228
|
+
const toAsync$1 = (options = {}) => toAsync$5(make$2(options));
|
|
2253
2229
|
//#endregion
|
|
2254
2230
|
//#region src/verification/google-doh.ts
|
|
2255
2231
|
var google_doh_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -2265,7 +2241,7 @@ function make$1(options = {}) {
|
|
|
2265
2241
|
});
|
|
2266
2242
|
}
|
|
2267
2243
|
const layer = (options = {}) => Layer.succeed(Service$2, make$1(options));
|
|
2268
|
-
const toAsync = (options = {}) => toAsync$
|
|
2244
|
+
const toAsync = (options = {}) => toAsync$5(make$1(options));
|
|
2269
2245
|
//#endregion
|
|
2270
2246
|
//#region src/verification/resolver-pool.ts
|
|
2271
2247
|
var resolver_pool_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -2366,13 +2342,13 @@ function observeProvider(provider, zone, requirement) {
|
|
|
2366
2342
|
}
|
|
2367
2343
|
function observePublicDns(requirement, policy) {
|
|
2368
2344
|
return Effect.gen(function* () {
|
|
2369
|
-
const validatedPolicy = yield* Schema.decodeUnknownEffect(Policy.Schema)(policy).pipe(Effect.mapError((cause) => new Error$
|
|
2345
|
+
const validatedPolicy = yield* Schema.decodeUnknownEffect(Policy.Schema)(policy).pipe(Effect.mapError((cause) => new Error$1({ message: cause.message })));
|
|
2370
2346
|
const configuredPool = yield* Effect.serviceOption(Service);
|
|
2371
2347
|
const evidence = yield* Option.getOrElse(configuredPool, () => defaultMake()).observe({
|
|
2372
2348
|
name: requirement.name,
|
|
2373
2349
|
type: requirement._tag
|
|
2374
2350
|
});
|
|
2375
|
-
if (validatedPolicy._tag === "Quorum" && validatedPolicy.minimum > evidence.length) return yield* new Error$
|
|
2351
|
+
if (validatedPolicy._tag === "Quorum" && validatedPolicy.minimum > evidence.length) return yield* new Error$1({ message: `Quorum minimum ${validatedPolicy.minimum} exceeds the ${evidence.length} configured resolvers` });
|
|
2376
2352
|
return evaluatePublicDns(requirement, validatedPolicy, evidence);
|
|
2377
2353
|
});
|
|
2378
2354
|
}
|
|
@@ -2415,6 +2391,6 @@ function recordValue(requirement) {
|
|
|
2415
2391
|
}
|
|
2416
2392
|
}
|
|
2417
2393
|
//#endregion
|
|
2418
|
-
export {
|
|
2394
|
+
export { manifest_exports as $, discovery as A, tokenMethod$1 as B, integrationFlow as C, tokenConnectionMethod as D, restore as E, oauthMethod as F, authorize as G, client_exports$1 as H, refreshCredential as I, selection_exports as J, connection_authorization_exports as K, restore$1 as L, auth_exports$1 as M, manifest$1 as N, tokenMethod as O, oauthFlow as P, decode$4 as Q, subjectResolver as R, exchangeCode as S, manifest as T, discovery$1 as U, tokenValidator as V, make$5 as W, Method as X, secret_exports as Y, Schema$1 as Z, toAsync$1 as _, Result as a, dns_data_exports as b, Observation as c, defaultMake as d, diagnostic_exports as et, make as f, cloudflare_doh_exports as g, toAsync as h, PublicDnsObservation as i, make$4 as j, client_exports as k, Policy as l, google_doh_exports as m, ProviderObservation as n, observe as o, resolver_pool_exports as p, session_exports as q, PublicDns as r, verify_exports as s, Provider as t, version as tt, Service as u, doh_exports as v, integrationMethod as w, auth_exports as x, toAsync$2 as y, tokenConnectionMethod$1 as z };
|
|
2419
2395
|
|
|
2420
|
-
//# sourceMappingURL=verify-
|
|
2396
|
+
//# sourceMappingURL=verify-DAtUAEeg.mjs.map
|