domainkit 0.4.0 → 0.5.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/index.d.mts +262 -42
- package/dist/index.mjs +56 -26
- package/dist/index.mjs.map +1 -1
- package/dist/promise.d.mts +99 -66
- package/dist/promise.mjs +154 -111
- package/dist/promise.mjs.map +1 -1
- package/dist/{resolver-uqe1h5Vz.d.mts → resolver-BGBLnPjJ.d.mts} +208 -477
- package/dist/{resolver-CvK7JoJM.mjs → resolver-tZU6gEVq.mjs} +122 -12
- package/dist/resolver-tZU6gEVq.mjs.map +1 -0
- package/dist/testing.d.mts +2 -2
- package/dist/testing.mjs +121 -60
- package/dist/testing.mjs.map +1 -1
- package/dist/{verify-CkgVrwyl.d.mts → verify-6xK05pwL.d.mts} +185 -44
- package/dist/{verify-BUB4N5zs.mjs → verify-B2fPyjde.mjs} +555 -394
- package/dist/verify-B2fPyjde.mjs.map +1 -0
- package/package.json +1 -1
- package/dist/resolver-CvK7JoJM.mjs.map +0 -1
- package/dist/verify-BUB4N5zs.mjs.map +0 -1
|
@@ -1,176 +1,10 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
|
-
import { I as Error$3, L as Service$3,
|
|
2
|
+
import { At as Error$2, Dt as decode$7, Et as Schema$3, I as Error$3, L as Service$3, R as fromAsync$1, V as toAsync$4, Y as Opaque, bt as assertAttachment, i as toAsync$5, it as equals, kt as parse$1, mt as AuthorizationError, n as Service$2, nt as decode$8, pt as Service$1, t as Error$4, tt as data, ut as candidates, wt as project, x as authorize$1, xt as authorizationError } from "./resolver-tZU6gEVq.mjs";
|
|
3
3
|
import { Clock, Context, Crypto, Data, Effect, Layer, Option, Schema, SchemaIssue, SchemaTransformation } from "effect";
|
|
4
4
|
import * as oauth from "oauth4webapi";
|
|
5
5
|
import * as DnsPacket from "@leichtgewicht/dns-packet";
|
|
6
6
|
//#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
|
-
}
|
|
7
|
+
var version = "0.5.0";
|
|
174
8
|
//#endregion
|
|
175
9
|
//#region src/auth/connect.ts
|
|
176
10
|
const Method$1 = Data.taggedEnum();
|
|
@@ -190,57 +24,84 @@ var Error$1 = class extends Schema.TaggedError()("ConnectionError", {
|
|
|
190
24
|
"unknown"
|
|
191
25
|
])
|
|
192
26
|
}) {};
|
|
193
|
-
const
|
|
194
|
-
const
|
|
27
|
+
const missingCapability = (authentication, requiredCapabilities) => requiredCapabilities.find((capability) => !authentication.capabilityEvidence.some((item) => item.capability === capability));
|
|
28
|
+
const freshId = Effect.fn("Connection.freshId")(function* () {
|
|
29
|
+
return yield* (yield* Crypto.Crypto).randomUUIDv4.pipe(Effect.mapError((cause) => new Error$1({
|
|
30
|
+
category: "validation",
|
|
31
|
+
message: cause.message,
|
|
32
|
+
operation: "Connection.connect",
|
|
33
|
+
retry: "safe"
|
|
34
|
+
})));
|
|
35
|
+
});
|
|
36
|
+
/** Persist one authenticated provider connection, optionally reusing a proven authorization. */
|
|
37
|
+
const connect = Effect.fn("Connection.connect")(function* (input) {
|
|
195
38
|
const repository = yield* Service$1;
|
|
196
|
-
const crypto = yield* Crypto.Crypto;
|
|
197
39
|
const now = new Date(yield* Clock.currentTimeMillis);
|
|
198
|
-
const
|
|
199
|
-
if (
|
|
40
|
+
const missing = missingCapability(input.authentication, input.requiredCapabilities);
|
|
41
|
+
if (missing !== void 0) return yield* new Error$1({
|
|
200
42
|
category: "authorization",
|
|
201
|
-
message: `Provider authorization lacks evidence for ${
|
|
202
|
-
operation: "Connection.
|
|
43
|
+
message: `Provider authorization lacks evidence for ${missing}`,
|
|
44
|
+
operation: "Connection.connect",
|
|
203
45
|
retry: "after-user-action"
|
|
204
46
|
});
|
|
205
|
-
const existing = yield* repository.
|
|
206
|
-
|
|
207
|
-
category: "
|
|
208
|
-
message:
|
|
209
|
-
operation: "Connection.
|
|
210
|
-
retry: "
|
|
211
|
-
})
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
47
|
+
const existing = input.authorizationId === void 0 ? null : yield* repository.get(input.authorizationId);
|
|
48
|
+
if (input.authorizationId !== void 0 && existing === null) return yield* new Error$1({
|
|
49
|
+
category: "authorization",
|
|
50
|
+
message: "Provider authorization does not exist",
|
|
51
|
+
operation: "Connection.connect",
|
|
52
|
+
retry: "after-user-action"
|
|
53
|
+
});
|
|
54
|
+
if (existing?.authorization.revocation._tag === "Pending") return yield* new Error$1({
|
|
55
|
+
category: "authorization",
|
|
56
|
+
message: "Provider authorization is awaiting revocation recovery",
|
|
57
|
+
operation: "Connection.connect",
|
|
58
|
+
retry: "after-user-action"
|
|
59
|
+
});
|
|
60
|
+
if (existing !== null && (existing.authorization.providerId !== input.providerId || existing.authorization.method !== input.method)) return yield* new Error$1({
|
|
61
|
+
category: "authorization",
|
|
62
|
+
message: "Provider proof does not match the existing authorization",
|
|
63
|
+
operation: "Connection.connect",
|
|
64
|
+
retry: "never"
|
|
65
|
+
});
|
|
66
|
+
const authorizationId = existing?.authorization.id ?? (yield* freshId());
|
|
67
|
+
const authorization = existing === null ? {
|
|
68
|
+
authorizedById: input.authorizedById,
|
|
69
|
+
capabilityEvidence: [...input.authentication.capabilityEvidence],
|
|
70
|
+
createdAt: now,
|
|
71
|
+
expiresAt: input.authentication.expiresAt,
|
|
72
|
+
id: authorizationId,
|
|
73
|
+
method: input.method,
|
|
74
|
+
providerContext: input.authentication.providerContext,
|
|
75
|
+
providerId: input.providerId,
|
|
76
|
+
requiredCapabilities: [...input.requiredCapabilities],
|
|
77
|
+
revocation: { _tag: "Active" },
|
|
78
|
+
scopes: [...input.authentication.scopes]
|
|
79
|
+
} : {
|
|
80
|
+
...existing.authorization,
|
|
81
|
+
capabilityEvidence: mergeEvidence(existing.authorization.capabilityEvidence, input.authentication.capabilityEvidence),
|
|
82
|
+
expiresAt: input.authentication.expiresAt,
|
|
83
|
+
method: input.method,
|
|
84
|
+
providerContext: input.authentication.providerContext,
|
|
85
|
+
requiredCapabilities: [...input.requiredCapabilities],
|
|
86
|
+
revocation: { _tag: "Active" },
|
|
87
|
+
scopes: [...input.authentication.scopes]
|
|
224
88
|
};
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
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
|
|
89
|
+
const existingConnection = yield* repository.findConnection(input.ownerId, authorizationId);
|
|
90
|
+
const connection = {
|
|
91
|
+
authorizationId,
|
|
92
|
+
createdAt: existingConnection?.createdAt ?? now,
|
|
93
|
+
id: existingConnection?.id ?? (yield* freshId()),
|
|
94
|
+
method: input.method,
|
|
95
|
+
ownerId: input.ownerId,
|
|
96
|
+
providerId: input.providerId
|
|
242
97
|
};
|
|
243
|
-
return yield* repository.connect(
|
|
98
|
+
return yield* repository.connect({
|
|
99
|
+
authorization,
|
|
100
|
+
connection,
|
|
101
|
+
credential: input.authentication.credential,
|
|
102
|
+
...existing === null ? {} : { expectedAuthorizationId: existing.authorization.id },
|
|
103
|
+
...existingConnection === null ? {} : { expectedConnectionId: existingConnection.id }
|
|
104
|
+
});
|
|
244
105
|
});
|
|
245
106
|
const start$1 = Effect.fn("Connection.start")(function* (input) {
|
|
246
107
|
if (input.method._tag === "Interactive") {
|
|
@@ -255,8 +116,8 @@ const start$1 = Effect.fn("Connection.start")(function* (input) {
|
|
|
255
116
|
const started = yield* input.method.flow.start(continuationId);
|
|
256
117
|
yield* input.method.continuations.put({
|
|
257
118
|
authorizedById: input.authorizedById,
|
|
119
|
+
...input.authorizationId === void 0 ? {} : { authorizationId: input.authorizationId },
|
|
258
120
|
expiresAt: new Date(now + (input.method.ttlMs ?? 6e5)),
|
|
259
|
-
grant: input.grant,
|
|
260
121
|
id: continuationId,
|
|
261
122
|
method: input.method.flow.method,
|
|
262
123
|
ownerId: input.ownerId,
|
|
@@ -270,16 +131,23 @@ const start$1 = Effect.fn("Connection.start")(function* (input) {
|
|
|
270
131
|
});
|
|
271
132
|
}
|
|
272
133
|
const authentication = yield* input.method.authenticate(input.method.token);
|
|
273
|
-
const aggregate = yield*
|
|
134
|
+
const aggregate = yield* connect({
|
|
274
135
|
authentication,
|
|
275
136
|
authorizedById: input.authorizedById,
|
|
276
|
-
|
|
137
|
+
...input.authorizationId === void 0 ? {} : { authorizationId: input.authorizationId },
|
|
277
138
|
method: "token",
|
|
278
139
|
ownerId: input.ownerId,
|
|
279
140
|
providerId: input.method.providerId,
|
|
280
141
|
requiredCapabilities: input.method.requiredCapabilities
|
|
281
142
|
});
|
|
282
|
-
|
|
143
|
+
const storedConnection = aggregate.connections.find(({ ownerId }) => ownerId === input.ownerId);
|
|
144
|
+
if (storedConnection === void 0) return yield* new Error$1({
|
|
145
|
+
category: "validation",
|
|
146
|
+
message: "Connected provider authorization returned no organization connection",
|
|
147
|
+
operation: "Connection.start",
|
|
148
|
+
retry: "safe"
|
|
149
|
+
});
|
|
150
|
+
return StartResult.Connected({ connection: project(storedConnection, aggregate.authorization) });
|
|
283
151
|
});
|
|
284
152
|
const complete$1 = Effect.fn("Connection.complete")(function* (input) {
|
|
285
153
|
const continuation = yield* input.continuations.consume(input.continuationId, new Date(yield* Clock.currentTimeMillis));
|
|
@@ -296,77 +164,29 @@ const complete$1 = Effect.fn("Connection.complete")(function* (input) {
|
|
|
296
164
|
retry: "never"
|
|
297
165
|
});
|
|
298
166
|
const authentication = yield* input.flow.complete(continuation.payload, input.callbackUrl);
|
|
299
|
-
|
|
167
|
+
const aggregate = yield* connect({
|
|
300
168
|
authentication,
|
|
301
169
|
authorizedById: continuation.authorizedById,
|
|
302
|
-
|
|
170
|
+
...continuation.authorizationId === void 0 ? {} : { authorizationId: continuation.authorizationId },
|
|
303
171
|
method: continuation.method,
|
|
304
172
|
ownerId: continuation.ownerId,
|
|
305
173
|
providerId: continuation.providerId,
|
|
306
174
|
requiredCapabilities: continuation.requiredCapabilities
|
|
307
175
|
});
|
|
308
|
-
});
|
|
309
|
-
|
|
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({
|
|
176
|
+
const storedConnection = aggregate.connections.find(({ ownerId }) => ownerId === continuation.ownerId);
|
|
177
|
+
if (storedConnection === void 0) return yield* new Error$1({
|
|
350
178
|
category: "validation",
|
|
351
|
-
message:
|
|
352
|
-
operation: "Connection.
|
|
353
|
-
retry: "
|
|
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
|
-
};
|
|
179
|
+
message: "Connected provider authorization returned no organization connection",
|
|
180
|
+
operation: "Connection.complete",
|
|
181
|
+
retry: "safe"
|
|
367
182
|
});
|
|
368
|
-
return
|
|
183
|
+
return project(storedConnection, aggregate.authorization);
|
|
369
184
|
});
|
|
185
|
+
function mergeEvidence(current, incoming) {
|
|
186
|
+
const byCapability = new Map(current.map((item) => [item.capability, item]));
|
|
187
|
+
for (const item of incoming) byCapability.set(item.capability, item);
|
|
188
|
+
return [...byCapability.values()];
|
|
189
|
+
}
|
|
370
190
|
//#endregion
|
|
371
191
|
//#region src/auth/diagnostic.ts
|
|
372
192
|
var diagnostic_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -407,8 +227,8 @@ function from(error) {
|
|
|
407
227
|
//#region src/auth/manifest.ts
|
|
408
228
|
var manifest_exports = /* @__PURE__ */ __exportAll({
|
|
409
229
|
Method: () => Method,
|
|
410
|
-
Schema: () => Schema$
|
|
411
|
-
decode: () => decode$
|
|
230
|
+
Schema: () => Schema$2,
|
|
231
|
+
decode: () => decode$6
|
|
412
232
|
});
|
|
413
233
|
const Method = Schema.TaggedUnion({
|
|
414
234
|
integration: {
|
|
@@ -436,11 +256,37 @@ const Method = Schema.TaggedUnion({
|
|
|
436
256
|
instructionsUrl: Schema.String
|
|
437
257
|
}
|
|
438
258
|
});
|
|
439
|
-
const Schema$
|
|
259
|
+
const Schema$2 = Schema.Struct({
|
|
440
260
|
methods: Schema.Array(Method),
|
|
441
261
|
providerId: Schema.String
|
|
442
262
|
});
|
|
443
|
-
const decode$
|
|
263
|
+
const decode$6 = Effect.fn("ProviderAuth.decode")((input) => Schema.decodeUnknownEffect(Schema$2)(input).pipe(Effect.mapError((cause) => new Error$2({ message: cause.message }))));
|
|
264
|
+
//#endregion
|
|
265
|
+
//#region src/auth/provider-context.ts
|
|
266
|
+
var provider_context_exports = /* @__PURE__ */ __exportAll({
|
|
267
|
+
Envelope: () => Envelope,
|
|
268
|
+
codec: () => codec,
|
|
269
|
+
decode: () => decode$5,
|
|
270
|
+
encode: () => encode$3
|
|
271
|
+
});
|
|
272
|
+
/** A provider-owned, versioned, non-secret context value persisted by the host. */
|
|
273
|
+
const Envelope = Schema.Struct({
|
|
274
|
+
value: Schema.Json,
|
|
275
|
+
version: Schema.String
|
|
276
|
+
});
|
|
277
|
+
/** Build an adapter-owned context codec from a schema and stable version identifier. */
|
|
278
|
+
function codec(version, schema) {
|
|
279
|
+
return {
|
|
280
|
+
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 })))),
|
|
281
|
+
encode: Effect.fn("ProviderContext.encode")((value) => Schema.encodeEffect(schema)(value).pipe(Effect.flatMap((encoded) => Schema.decodeUnknownEffect(Schema.Json)(encoded)), Effect.map((encoded) => ({
|
|
282
|
+
value: encoded,
|
|
283
|
+
version
|
|
284
|
+
})), Effect.mapError((cause) => new Error$2({ message: cause.message })))),
|
|
285
|
+
version
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
const decode$5 = Effect.fn("ProviderContext.Envelope.decode")((input) => Schema.decodeUnknownEffect(Envelope)(input).pipe(Effect.mapError((cause) => new Error$2({ message: cause.message }))));
|
|
289
|
+
const encode$3 = Schema.encodeSync(Envelope);
|
|
444
290
|
//#endregion
|
|
445
291
|
//#region src/auth/secret.ts
|
|
446
292
|
var secret_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -471,26 +317,26 @@ const make$6 = Value.from;
|
|
|
471
317
|
//#endregion
|
|
472
318
|
//#region src/discovery/selection.ts
|
|
473
319
|
var selection_exports = /* @__PURE__ */ __exportAll({
|
|
474
|
-
Evidence: () => Evidence,
|
|
320
|
+
Evidence: () => Evidence$1,
|
|
475
321
|
Selection: () => Selection,
|
|
476
322
|
select: () => select
|
|
477
323
|
});
|
|
478
|
-
const Evidence = Schema.Struct({
|
|
324
|
+
const Evidence$1 = Schema.Struct({
|
|
479
325
|
accountId: Schema.String,
|
|
480
326
|
connectionId: Schema.String,
|
|
481
327
|
decisiveNameserverMatch: Schema.Boolean,
|
|
482
|
-
matchedNameservers: Schema.Array(Schema$
|
|
328
|
+
matchedNameservers: Schema.Array(Schema$3),
|
|
483
329
|
providerId: Schema.String,
|
|
484
|
-
zone: Schema$
|
|
330
|
+
zone: Schema$3
|
|
485
331
|
});
|
|
486
332
|
const Selection = Schema.TaggedUnion({
|
|
487
333
|
selected: {
|
|
488
|
-
candidate: Evidence,
|
|
489
|
-
evidence: Schema.Array(Evidence),
|
|
334
|
+
candidate: Evidence$1,
|
|
335
|
+
evidence: Schema.Array(Evidence$1),
|
|
490
336
|
reason: Schema.Literals(["explicit", "unique-nameserver-match"])
|
|
491
337
|
},
|
|
492
338
|
manual: {
|
|
493
|
-
evidence: Schema.Array(Evidence),
|
|
339
|
+
evidence: Schema.Array(Evidence$1),
|
|
494
340
|
reason: Schema.Literals(["ambiguous", "unsupported"])
|
|
495
341
|
}
|
|
496
342
|
});
|
|
@@ -499,13 +345,16 @@ function select(input) {
|
|
|
499
345
|
const zoneCandidates = new Set(candidates(domain));
|
|
500
346
|
const authoritative = nameserverSet(input.authoritativeNameservers);
|
|
501
347
|
const eligible = input.connectedZones.filter((connected) => {
|
|
502
|
-
if (!zoneCandidates.has(
|
|
348
|
+
if (!zoneCandidates.has(connected.attachment.target.zoneName)) return false;
|
|
503
349
|
try {
|
|
504
|
-
|
|
350
|
+
assertAttachment({
|
|
351
|
+
attachment: connected.attachment,
|
|
505
352
|
capability: "dns:read",
|
|
353
|
+
connection: connected.connection,
|
|
506
354
|
domain,
|
|
507
355
|
...input.now === void 0 ? {} : { now: input.now },
|
|
508
|
-
providerId: connected.providerId
|
|
356
|
+
providerId: connected.connection.providerId,
|
|
357
|
+
authorization: connected.authorization
|
|
509
358
|
});
|
|
510
359
|
return true;
|
|
511
360
|
} catch {
|
|
@@ -515,8 +364,8 @@ function select(input) {
|
|
|
515
364
|
const evidence = eligible.map((connected) => evidenceFor(connected, authoritative));
|
|
516
365
|
if (input.explicit !== void 0) {
|
|
517
366
|
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
|
|
367
|
+
const index = eligible.findIndex((connected) => connected.connection.providerId === input.explicit?.providerId && connected.attachment.target.accountId === input.explicit.accountId && connected.attachment.target.zoneName === explicitZone);
|
|
368
|
+
if (index < 0) throw authorizationError("Explicit provider zone is not connected for this domain", "ProviderDiscovery.select");
|
|
520
369
|
const candidate = evidence[index];
|
|
521
370
|
if (candidate === void 0) throw new Error("Eligible provider evidence is missing");
|
|
522
371
|
return {
|
|
@@ -543,32 +392,78 @@ function evidenceFor(connected, authoritative) {
|
|
|
543
392
|
const configured = new Set(nameserverSet(connected.nameservers));
|
|
544
393
|
const matchedNameservers = authoritative.filter((nameserver) => configured.has(nameserver));
|
|
545
394
|
return {
|
|
546
|
-
accountId: connected.
|
|
395
|
+
accountId: connected.attachment.target.accountId,
|
|
547
396
|
connectionId: connected.connection.id,
|
|
548
397
|
decisiveNameserverMatch: authoritative.length > 0 && matchedNameservers.length === authoritative.length,
|
|
549
398
|
matchedNameservers,
|
|
550
|
-
providerId: connected.providerId,
|
|
551
|
-
zone:
|
|
399
|
+
providerId: connected.connection.providerId,
|
|
400
|
+
zone: connected.attachment.target.zoneName
|
|
552
401
|
};
|
|
553
402
|
}
|
|
554
403
|
function nameserverSet(nameservers) {
|
|
555
404
|
return [...new Set(nameservers.map(parse$1))].sort();
|
|
556
405
|
}
|
|
557
406
|
//#endregion
|
|
407
|
+
//#region src/provider/session.ts
|
|
408
|
+
var session_exports = /* @__PURE__ */ __exportAll({
|
|
409
|
+
Resolution: () => Resolution,
|
|
410
|
+
fromAsync: () => fromAsync,
|
|
411
|
+
toAsync: () => toAsync$3
|
|
412
|
+
});
|
|
413
|
+
const Resolution = Data.taggedEnum();
|
|
414
|
+
/** Adapts a Promise-shaped session to the Effect-native session boundary. */
|
|
415
|
+
const fromAsync = (session) => ({
|
|
416
|
+
providerId: session.providerId,
|
|
417
|
+
listTargets: (input) => Effect.tryPromise({
|
|
418
|
+
try: () => session.listTargets(input),
|
|
419
|
+
catch: (cause) => sessionFailure(session.providerId, "listTargets", cause)
|
|
420
|
+
}),
|
|
421
|
+
resolveTarget: (domain) => Effect.tryPromise({
|
|
422
|
+
try: () => session.resolveTarget(domain),
|
|
423
|
+
catch: (cause) => sessionFailure(session.providerId, "resolveTarget", cause)
|
|
424
|
+
}),
|
|
425
|
+
forTarget: (target) => Effect.tryPromise({
|
|
426
|
+
try: () => session.forTarget(target),
|
|
427
|
+
catch: (cause) => sessionFailure(session.providerId, "forTarget", cause)
|
|
428
|
+
}).pipe(Effect.map(fromAsync$1))
|
|
429
|
+
});
|
|
430
|
+
/** Adapts an Effect-native session to the Promise facade. */
|
|
431
|
+
const toAsync$3 = (session) => ({
|
|
432
|
+
providerId: session.providerId,
|
|
433
|
+
listTargets: (input) => Effect.runPromise(session.listTargets(input)),
|
|
434
|
+
resolveTarget: (domain) => Effect.runPromise(session.resolveTarget(domain)),
|
|
435
|
+
forTarget: (target) => Effect.runPromise(session.forTarget(target)).then(toAsync$4)
|
|
436
|
+
});
|
|
437
|
+
function sessionFailure(providerId, operation, cause) {
|
|
438
|
+
return cause instanceof Error$3 ? cause : new Error$3({
|
|
439
|
+
message: cause instanceof globalThis.Error ? cause.message : String(cause),
|
|
440
|
+
operation: `ProviderSession.${operation}`,
|
|
441
|
+
providerId,
|
|
442
|
+
reason: "transport"
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
//#endregion
|
|
558
446
|
//#region src/plan/connection-authorization.ts
|
|
559
447
|
var connection_authorization_exports = /* @__PURE__ */ __exportAll({ authorize: () => authorize });
|
|
560
448
|
const authorize = Effect.fn("ConnectionAuthorization.authorize")(function* (input) {
|
|
449
|
+
const aggregate = yield* (yield* Service$1).getByConnectionId(input.connection.id);
|
|
450
|
+
const storedConnection = aggregate?.connections.find(({ id }) => id === input.connection.id);
|
|
451
|
+
const storedAttachment = aggregate?.attachments.find(({ id }) => id === input.attachment.id);
|
|
452
|
+
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
453
|
const domain = yield* Effect.try({
|
|
562
|
-
try: () =>
|
|
454
|
+
try: () => assertAttachment({
|
|
455
|
+
attachment: storedAttachment,
|
|
563
456
|
capability: "dns:write",
|
|
457
|
+
authorization: aggregate.authorization,
|
|
458
|
+
connection: storedConnection,
|
|
564
459
|
domain: input.domain,
|
|
565
460
|
providerId: input.plan.providerId
|
|
566
461
|
}),
|
|
567
|
-
catch: (cause) => cause instanceof AuthorizationError || cause instanceof Error$2 ? cause : authorizationError("
|
|
462
|
+
catch: (cause) => cause instanceof AuthorizationError || cause instanceof Error$2 ? cause : authorizationError("Domain attachment is invalid", "ConnectionAuthorization.authorize")
|
|
568
463
|
});
|
|
569
464
|
const authorization = yield* authorize$1(input.plan, input.operationIds, input.allowPartial === void 0 ? {} : { allowPartial: input.allowPartial });
|
|
570
465
|
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
|
|
466
|
+
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
467
|
return authorization;
|
|
573
468
|
});
|
|
574
469
|
//#endregion
|
|
@@ -598,7 +493,7 @@ const start = Effect.fn("AuthorizationCode.start")(function* (input) {
|
|
|
598
493
|
});
|
|
599
494
|
const complete = Effect.fn("AuthorizationCode.complete")(function* (input) {
|
|
600
495
|
const payload = yield* decodePayload(input.payload);
|
|
601
|
-
if (input.callbackUrl.searchParams.get("state") !== payload.state) return yield* failure$
|
|
496
|
+
if (input.callbackUrl.searchParams.get("state") !== payload.state) return yield* failure$6("Authorization callback state does not match its continuation");
|
|
602
497
|
const server = input.method.authorizationServer;
|
|
603
498
|
const client = { client_id: input.client.clientId };
|
|
604
499
|
const authentication = yield* clientAuthentication(input.method, input.client);
|
|
@@ -632,8 +527,8 @@ function decodePayload(payload) {
|
|
|
632
527
|
function clientAuthentication(method, client) {
|
|
633
528
|
switch (method.clientAuth) {
|
|
634
529
|
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$
|
|
530
|
+
case "client_secret_basic": return client.clientSecret === void 0 ? failure$6("OAuth client secret is required") : Effect.succeed(oauth.ClientSecretBasic(client.clientSecret.expose()));
|
|
531
|
+
case "client_secret_post": return client.clientSecret === void 0 ? failure$6("OAuth client secret is required") : Effect.succeed(oauth.ClientSecretPost(client.clientSecret.expose()));
|
|
637
532
|
}
|
|
638
533
|
}
|
|
639
534
|
function requestOptions(fetchImplementation, signal) {
|
|
@@ -645,7 +540,7 @@ function requestOptions(fetchImplementation, signal) {
|
|
|
645
540
|
})
|
|
646
541
|
};
|
|
647
542
|
}
|
|
648
|
-
const failure$
|
|
543
|
+
const failure$6 = (message) => Effect.fail(new Error$1({
|
|
649
544
|
category: "authorization",
|
|
650
545
|
message,
|
|
651
546
|
operation: "AuthorizationCode",
|
|
@@ -673,6 +568,44 @@ function base64Url(bytes) {
|
|
|
673
568
|
return btoa(binary).replaceAll("+", "-").replaceAll("/", "_").replaceAll("=", "");
|
|
674
569
|
}
|
|
675
570
|
//#endregion
|
|
571
|
+
//#region src/auth/authorization.ts
|
|
572
|
+
const Capability = Schema.Literals(["dns:read", "dns:write"]);
|
|
573
|
+
const Evidence = Data.taggedEnum();
|
|
574
|
+
const EvidenceSchema = Schema.TaggedUnion({
|
|
575
|
+
Declared: {},
|
|
576
|
+
Exercised: { observedAt: Schema.DateFromString },
|
|
577
|
+
Introspected: { observedAt: Schema.DateFromString }
|
|
578
|
+
});
|
|
579
|
+
const CapabilityEvidence = Schema.Struct({
|
|
580
|
+
capability: Capability,
|
|
581
|
+
evidence: EvidenceSchema
|
|
582
|
+
});
|
|
583
|
+
const Revocation = Schema.TaggedUnion({
|
|
584
|
+
Active: {},
|
|
585
|
+
Pending: { requestedAt: Schema.DateFromString }
|
|
586
|
+
});
|
|
587
|
+
/** A provider credential authorization shared by one or more organization connections. */
|
|
588
|
+
const Schema$1 = Schema.Struct({
|
|
589
|
+
authorizedById: Schema.String,
|
|
590
|
+
capabilityEvidence: Schema.Array(CapabilityEvidence),
|
|
591
|
+
createdAt: Schema.DateFromString,
|
|
592
|
+
expiresAt: Schema.NullOr(Schema.DateFromString),
|
|
593
|
+
id: Schema.String,
|
|
594
|
+
method: Schema.Literals([
|
|
595
|
+
"integration",
|
|
596
|
+
"oauth2",
|
|
597
|
+
"token"
|
|
598
|
+
]),
|
|
599
|
+
providerContext: Envelope,
|
|
600
|
+
providerId: Schema.String,
|
|
601
|
+
requiredCapabilities: Schema.Array(Capability),
|
|
602
|
+
revocation: Revocation,
|
|
603
|
+
scopes: Schema.Array(Schema.String)
|
|
604
|
+
});
|
|
605
|
+
Effect.fn("ProviderAuthorization.decode")((input) => Schema.decodeUnknownEffect(Schema$1)(input).pipe(Effect.mapError((cause) => new Error$2({ message: cause.message }))));
|
|
606
|
+
Effect.fn("ProviderAuthorization.validate")((input) => Schema.decodeUnknownEffect(Schema.toType(Schema$1))(input).pipe(Effect.mapError((cause) => new Error$2({ message: cause.message }))));
|
|
607
|
+
Schema.encodeSync(Schema$1);
|
|
608
|
+
//#endregion
|
|
676
609
|
//#region src/providers/cloudflare/protocol.ts
|
|
677
610
|
const ErrorDetail$1 = Schema.Struct({
|
|
678
611
|
code: Schema.Number,
|
|
@@ -703,7 +636,13 @@ const Zone = Schema.Struct({
|
|
|
703
636
|
id: Schema.String,
|
|
704
637
|
name: Schema.String,
|
|
705
638
|
name_servers: Schema.Array(Schema.String),
|
|
706
|
-
status: Schema.optionalKey(Schema.String)
|
|
639
|
+
status: Schema.optionalKey(Schema.String),
|
|
640
|
+
type: Schema.optionalKey(Schema.Literals([
|
|
641
|
+
"full",
|
|
642
|
+
"partial",
|
|
643
|
+
"secondary",
|
|
644
|
+
"internal"
|
|
645
|
+
]))
|
|
707
646
|
});
|
|
708
647
|
const Record$1 = Schema.Struct({
|
|
709
648
|
content: Schema.optionalKey(Schema.String),
|
|
@@ -763,7 +702,7 @@ const Data$1 = Schema.Struct({
|
|
|
763
702
|
weight: Schema.optionalKey(Schema.Number)
|
|
764
703
|
});
|
|
765
704
|
const decode$3 = Effect.fn("CloudflareRecords.decode")((record) => Effect.gen(function* () {
|
|
766
|
-
const name = yield* decode$
|
|
705
|
+
const name = yield* decode$7(record.name).pipe(Effect.match({
|
|
767
706
|
onFailure: () => null,
|
|
768
707
|
onSuccess: (value) => value
|
|
769
708
|
}));
|
|
@@ -778,46 +717,46 @@ const decode$3 = Effect.fn("CloudflareRecords.decode")((record) => Effect.gen(fu
|
|
|
778
717
|
policy: record.type === "CNAME" ? "exclusive" : "append",
|
|
779
718
|
ttl: record.ttl === 1 ? null : record.ttl
|
|
780
719
|
};
|
|
781
|
-
const content = () => record.content === void 0 ? Effect.fail(failure$
|
|
720
|
+
const content = () => record.content === void 0 ? Effect.fail(failure$5("decodeRecord", `Cloudflare ${record.type} record has no content`)) : Effect.succeed(record.content);
|
|
782
721
|
switch (record.type) {
|
|
783
722
|
case "A":
|
|
784
|
-
case "AAAA": return yield* decode$
|
|
723
|
+
case "AAAA": return yield* decode$8({
|
|
785
724
|
...common,
|
|
786
725
|
_tag: record.type,
|
|
787
726
|
address: yield* content()
|
|
788
|
-
}).pipe(Effect.mapError((cause) => failure$
|
|
727
|
+
}).pipe(Effect.mapError((cause) => failure$5("decodeRecord", cause.message)));
|
|
789
728
|
case "CNAME":
|
|
790
|
-
case "NS": return yield* decode$
|
|
729
|
+
case "NS": return yield* decode$8({
|
|
791
730
|
...common,
|
|
792
731
|
_tag: record.type,
|
|
793
732
|
target: yield* content()
|
|
794
|
-
}).pipe(Effect.mapError((cause) => failure$
|
|
795
|
-
case "TXT": return yield* decode$
|
|
733
|
+
}).pipe(Effect.mapError((cause) => failure$5("decodeRecord", cause.message)));
|
|
734
|
+
case "TXT": return yield* decode$8({
|
|
796
735
|
...common,
|
|
797
736
|
_tag: "TXT",
|
|
798
737
|
value: yield* content()
|
|
799
|
-
}).pipe(Effect.mapError((cause) => failure$
|
|
800
|
-
case "MX": return yield* decode$
|
|
738
|
+
}).pipe(Effect.mapError((cause) => failure$5("decodeRecord", cause.message)));
|
|
739
|
+
case "MX": return yield* decode$8({
|
|
801
740
|
...common,
|
|
802
741
|
_tag: "MX",
|
|
803
742
|
exchange: yield* content(),
|
|
804
743
|
priority: record.priority
|
|
805
|
-
}).pipe(Effect.mapError((cause) => failure$
|
|
744
|
+
}).pipe(Effect.mapError((cause) => failure$5("decodeRecord", cause.message)));
|
|
806
745
|
case "CAA": {
|
|
807
746
|
const data = yield* decodeData(record);
|
|
808
|
-
return yield* decode$
|
|
747
|
+
return yield* decode$8({
|
|
809
748
|
...common,
|
|
810
749
|
_tag: "CAA",
|
|
811
750
|
...data
|
|
812
|
-
}).pipe(Effect.mapError((cause) => failure$
|
|
751
|
+
}).pipe(Effect.mapError((cause) => failure$5("decodeRecord", cause.message)));
|
|
813
752
|
}
|
|
814
753
|
case "SRV": {
|
|
815
754
|
const data = yield* decodeData(record);
|
|
816
|
-
return yield* decode$
|
|
755
|
+
return yield* decode$8({
|
|
817
756
|
...common,
|
|
818
757
|
_tag: "SRV",
|
|
819
758
|
...data
|
|
820
|
-
}).pipe(Effect.mapError((cause) => failure$
|
|
759
|
+
}).pipe(Effect.mapError((cause) => failure$5("decodeRecord", cause.message)));
|
|
821
760
|
}
|
|
822
761
|
default: return yield* decodeOpaque$1(record);
|
|
823
762
|
}
|
|
@@ -827,7 +766,7 @@ const decodeOpaque$1 = Effect.fn("CloudflareRecords.decodeOpaque")((record) => S
|
|
|
827
766
|
name: record.name,
|
|
828
767
|
providerRecordId: record.id,
|
|
829
768
|
providerType: record.type
|
|
830
|
-
}).pipe(Effect.mapError((cause) => failure$
|
|
769
|
+
}).pipe(Effect.mapError((cause) => failure$5("decodeRecord", cause.message))));
|
|
831
770
|
function encode$1(record) {
|
|
832
771
|
const common = {
|
|
833
772
|
name: record.name,
|
|
@@ -874,8 +813,8 @@ function encode$1(record) {
|
|
|
874
813
|
};
|
|
875
814
|
}
|
|
876
815
|
}
|
|
877
|
-
const decodeData = Effect.fn("CloudflareRecords.decodeData")((record) => Schema.decodeUnknownEffect(Data$1)(record.data).pipe(Effect.mapError((cause) => failure$
|
|
878
|
-
function failure$
|
|
816
|
+
const decodeData = Effect.fn("CloudflareRecords.decodeData")((record) => Schema.decodeUnknownEffect(Data$1)(record.data).pipe(Effect.mapError((cause) => failure$5("decodeRecord", cause.message))));
|
|
817
|
+
function failure$5(operation, message, fields = {}) {
|
|
879
818
|
return new Error$3({
|
|
880
819
|
message,
|
|
881
820
|
operation,
|
|
@@ -915,14 +854,14 @@ const makeTransport = (options) => {
|
|
|
915
854
|
...init?.headers
|
|
916
855
|
}
|
|
917
856
|
}),
|
|
918
|
-
catch: () => failure$
|
|
857
|
+
catch: () => failure$4(operation, "Cloudflare request failed", { reason: "transport" })
|
|
919
858
|
});
|
|
920
859
|
return {
|
|
921
860
|
body: yield* Effect.tryPromise({
|
|
922
861
|
try: () => response.json(),
|
|
923
862
|
catch: () => {
|
|
924
863
|
const retryAfterMs = retryAfter(response.headers.get("retry-after"));
|
|
925
|
-
return failure$
|
|
864
|
+
return failure$4(operation, response.ok ? "Cloudflare returned a non-JSON response" : `Cloudflare request failed with HTTP ${response.status}`, {
|
|
926
865
|
reason: response.ok ? "response" : reason$1(response.status),
|
|
927
866
|
...retryAfterMs === void 0 ? {} : { retryAfterMs },
|
|
928
867
|
status: response.status
|
|
@@ -960,12 +899,12 @@ const makeTransport = (options) => {
|
|
|
960
899
|
const { body, response } = yield* request(path, "validateToken");
|
|
961
900
|
yield* ensureSuccess(body, "validateToken", response);
|
|
962
901
|
const envelope = yield* decode$2(TokenEnvelope, body, "validateToken", response);
|
|
963
|
-
if (envelope.result.status !== "active") return yield* Effect.fail(failure$
|
|
902
|
+
if (envelope.result.status !== "active") return yield* Effect.fail(failure$4("validateToken", `Cloudflare token is ${envelope.result.status}`, {
|
|
964
903
|
reason: "authentication",
|
|
965
904
|
status: response.status
|
|
966
905
|
}));
|
|
967
906
|
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$
|
|
907
|
+
if (expiresAt !== null && Number.isNaN(expiresAt.valueOf())) return yield* Effect.fail(failure$4("validateToken", "Cloudflare token expiry is not a valid timestamp", {
|
|
969
908
|
reason: "response",
|
|
970
909
|
status: response.status
|
|
971
910
|
}));
|
|
@@ -981,9 +920,9 @@ const discoverAccount = Effect.fn("CloudflareClient.discoverAccount")((options)
|
|
|
981
920
|
const match = matches[0];
|
|
982
921
|
if (matches.length === 0) continue;
|
|
983
922
|
if (matches.length === 1 && match !== void 0) return match.account;
|
|
984
|
-
return yield* Effect.fail(failure$
|
|
923
|
+
return yield* Effect.fail(failure$4("discoverAccount", `Cloudflare returned multiple accessible zones named ${candidate}`, { reason: "conflict" }));
|
|
985
924
|
}
|
|
986
|
-
return yield* Effect.fail(failure$
|
|
925
|
+
return yield* Effect.fail(failure$4("discoverAccount", `Cloudflare did not expose an accessible zone for ${options.domain}`, { reason: "not_found" }));
|
|
987
926
|
}));
|
|
988
927
|
const discoverAuthorizationAccount = discoverAccount;
|
|
989
928
|
const validateDomainToken = Effect.fn("CloudflareClient.validateDomainToken")((options) => Effect.gen(function* () {
|
|
@@ -999,12 +938,27 @@ const validateDomainToken = Effect.fn("CloudflareClient.validateDomainToken")((o
|
|
|
999
938
|
}));
|
|
1000
939
|
/** Creates an Effect-native Cloudflare client without owning the credential lifecycle. */
|
|
1001
940
|
function make$5(options) {
|
|
941
|
+
return makeClient$1(options);
|
|
942
|
+
}
|
|
943
|
+
function makeClient$1(options) {
|
|
1002
944
|
const { allZones, inspectToken, request } = makeTransport(options);
|
|
1003
945
|
const resolveZone = Effect.fn("CloudflareClient.resolveZone")((name) => Effect.gen(function* () {
|
|
946
|
+
if (options.target !== void 0) {
|
|
947
|
+
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" }));
|
|
948
|
+
return {
|
|
949
|
+
account: {
|
|
950
|
+
id: options.target.accountId,
|
|
951
|
+
name: options.target.accountId
|
|
952
|
+
},
|
|
953
|
+
id: options.target.zoneId,
|
|
954
|
+
name: options.target.zoneName,
|
|
955
|
+
name_servers: options.target.evidence?.nameservers ?? []
|
|
956
|
+
};
|
|
957
|
+
}
|
|
1004
958
|
const matches = yield* allZones(options.accountId, name);
|
|
1005
959
|
const match = matches[0];
|
|
1006
960
|
if (matches.length === 1 && match !== void 0) return match;
|
|
1007
|
-
return yield* Effect.fail(failure$
|
|
961
|
+
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
962
|
}));
|
|
1009
963
|
const listRecords = Effect.fn("CloudflareClient.listRecords")((zoneName) => Effect.gen(function* () {
|
|
1010
964
|
const zone = yield* resolveZone(zoneName);
|
|
@@ -1041,28 +995,72 @@ function make$5(options) {
|
|
|
1041
995
|
const envelope = yield* decode$2(RecordEnvelope, body, "getRecord", response);
|
|
1042
996
|
return yield* decode$3(envelope.result);
|
|
1043
997
|
}));
|
|
998
|
+
const deleteRecord = Effect.fn("CloudflareClient.deleteRecord")((zoneName, providerRecordId) => Effect.gen(function* () {
|
|
999
|
+
const zone = yield* resolveZone(zoneName);
|
|
1000
|
+
const { body, response } = yield* request(`/zones/${encodeURIComponent(zone.id)}/dns_records/${encodeURIComponent(providerRecordId)}`, "deleteRecord", { method: "DELETE" });
|
|
1001
|
+
yield* ensureSuccess(body, "deleteRecord", response);
|
|
1002
|
+
}));
|
|
1003
|
+
const listAccounts = Effect.fn("CloudflareClient.listAccounts")(() => allZones().pipe(Effect.map((zones) => {
|
|
1004
|
+
const accounts = /* @__PURE__ */ new Map();
|
|
1005
|
+
for (const zone of zones) accounts.set(zone.account.id, zone.account);
|
|
1006
|
+
return [...accounts.values()].sort((left, right) => left.name.localeCompare(right.name));
|
|
1007
|
+
})));
|
|
1008
|
+
const listZones = Effect.fn("CloudflareClient.listZones")((input = {}) => allZones(input.accountId ?? options.accountId, input.name).pipe(Effect.flatMap((zones) => Effect.forEach(zones, projectZone$1))));
|
|
1009
|
+
const listTargets = Effect.fn("CloudflareClient.listTargets")((input = {}) => Effect.gen(function* () {
|
|
1010
|
+
const names = input.domain === void 0 ? void 0 : new Set(candidates(input.domain).map((name) => String(name)));
|
|
1011
|
+
const zones = yield* allZones(input.accountId ?? options.accountId);
|
|
1012
|
+
return yield* Effect.forEach(zones.filter((zone) => zone.type !== "internal" && (names === void 0 || names.has(zone.name))), targetFromZone);
|
|
1013
|
+
}));
|
|
1044
1014
|
return {
|
|
1045
1015
|
id: "cloudflare",
|
|
1016
|
+
providerId: "cloudflare",
|
|
1046
1017
|
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
|
-
})),
|
|
1018
|
+
deleteRecord,
|
|
1052
1019
|
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
|
-
}))),
|
|
1020
|
+
listAccounts,
|
|
1058
1021
|
listRecords,
|
|
1059
|
-
listZones
|
|
1022
|
+
listZones,
|
|
1023
|
+
listTargets,
|
|
1024
|
+
resolveTarget: Effect.fn("CloudflareClient.resolveTarget")((domain) => Effect.gen(function* () {
|
|
1025
|
+
const candidates$2 = yield* listTargets({ domain });
|
|
1026
|
+
for (const zoneName of candidates(domain)) {
|
|
1027
|
+
const matches = candidates$2.filter((target) => target.zoneName === zoneName);
|
|
1028
|
+
if (matches.length === 1) {
|
|
1029
|
+
const target = matches[0];
|
|
1030
|
+
if (target !== void 0) return Resolution.Resolved({ target });
|
|
1031
|
+
}
|
|
1032
|
+
if (matches.length > 1) return Resolution.SelectionRequired({ candidates: matches });
|
|
1033
|
+
}
|
|
1034
|
+
return Resolution.NotFound({ domain });
|
|
1035
|
+
})),
|
|
1036
|
+
forTarget: Effect.fn("CloudflareClient.forTarget")((target) => Effect.gen(function* () {
|
|
1037
|
+
if (target.accountKind !== null && target.accountKind !== "account") return yield* Effect.fail(failure$4("forTarget", "Cloudflare targets must use the account account kind", { reason: "request" }));
|
|
1038
|
+
if (target.evidence?.zoneType === "internal") return yield* Effect.fail(failure$4("forTarget", "Cloudflare internal zones cannot host managed DNS records", { reason: "unsupported" }));
|
|
1039
|
+
const selected = (yield* listTargets({
|
|
1040
|
+
accountId: target.accountId,
|
|
1041
|
+
domain: target.zoneName
|
|
1042
|
+
})).find((candidate) => candidate.accountId === target.accountId && candidate.zoneId === target.zoneId && candidate.zoneName === target.zoneName);
|
|
1043
|
+
if (selected === void 0) return yield* Effect.fail(failure$4("forTarget", `Cloudflare target ${target.zoneName} is not visible to this credential`, { reason: "authorization" }));
|
|
1044
|
+
if (selected.evidence?.zoneType === "internal") return yield* Effect.fail(failure$4("forTarget", "Cloudflare internal zones cannot host managed DNS records", { reason: "unsupported" }));
|
|
1045
|
+
const client = makeClient$1({
|
|
1046
|
+
...options,
|
|
1047
|
+
accountId: selected.accountId,
|
|
1048
|
+
target: selected
|
|
1049
|
+
});
|
|
1050
|
+
return {
|
|
1051
|
+
id: client.id,
|
|
1052
|
+
createRecord: client.createRecord,
|
|
1053
|
+
deleteRecord: client.deleteRecord,
|
|
1054
|
+
getRecord: client.getRecord,
|
|
1055
|
+
listRecords: client.listRecords
|
|
1056
|
+
};
|
|
1057
|
+
})),
|
|
1060
1058
|
validateToken: Effect.fn("CloudflareClient.validateToken")(() => Effect.gen(function* () {
|
|
1061
|
-
const path = options.tokenKind === "account" ? `/accounts/${encodeURIComponent(options.accountId)}/tokens/verify` : "/user/tokens/verify";
|
|
1059
|
+
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
1060
|
const expiresAt = yield* inspectToken(path);
|
|
1063
1061
|
yield* allZones(options.accountId);
|
|
1064
1062
|
return {
|
|
1065
|
-
accountId: options.accountId,
|
|
1063
|
+
accountId: options.accountId ?? "",
|
|
1066
1064
|
capabilities: options.capabilities,
|
|
1067
1065
|
expiresAt,
|
|
1068
1066
|
scopes: []
|
|
@@ -1070,9 +1068,25 @@ function make$5(options) {
|
|
|
1070
1068
|
}))
|
|
1071
1069
|
};
|
|
1072
1070
|
}
|
|
1071
|
+
const targetFromZone = Effect.fn("CloudflareClient.targetFromZone")((zone) => Effect.gen(function* () {
|
|
1072
|
+
const name = yield* decode$7(zone.name).pipe(Effect.mapError((cause) => failure$4("listTargets", cause.message, { reason: "response" })));
|
|
1073
|
+
const nameservers = yield* Effect.forEach(zone.name_servers, (nameserver) => decode$7(nameserver).pipe(Effect.mapError((cause) => failure$4("listTargets", cause.message, { reason: "response" }))));
|
|
1074
|
+
return {
|
|
1075
|
+
accountId: zone.account.id,
|
|
1076
|
+
accountKind: "account",
|
|
1077
|
+
evidence: {
|
|
1078
|
+
accountName: zone.account.name,
|
|
1079
|
+
nameservers,
|
|
1080
|
+
status: zone.status ?? "unknown",
|
|
1081
|
+
zoneType: zone.type ?? "unknown"
|
|
1082
|
+
},
|
|
1083
|
+
zoneId: zone.id,
|
|
1084
|
+
zoneName: name
|
|
1085
|
+
};
|
|
1086
|
+
}));
|
|
1073
1087
|
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$
|
|
1088
|
+
const name = yield* decode$7(zone.name).pipe(Effect.mapError((cause) => failure$4("listZones", cause.message, { reason: "response" })));
|
|
1089
|
+
const nameservers = yield* Effect.forEach(zone.name_servers, (nameserver) => decode$7(nameserver).pipe(Effect.mapError((cause) => failure$4("listZones", cause.message, { reason: "response" }))));
|
|
1076
1090
|
return {
|
|
1077
1091
|
accountId: zone.account.id,
|
|
1078
1092
|
id: zone.id,
|
|
@@ -1082,20 +1096,20 @@ const projectZone$1 = Effect.fn("CloudflareClient.projectZone")((zone) => Effect
|
|
|
1082
1096
|
};
|
|
1083
1097
|
}));
|
|
1084
1098
|
function decode$2(schema, input, operation, response) {
|
|
1085
|
-
return Schema.decodeUnknownEffect(schema)(input).pipe(Effect.mapError((cause) => failure$
|
|
1099
|
+
return Schema.decodeUnknownEffect(schema)(input).pipe(Effect.mapError((cause) => failure$4(operation, `Cloudflare response did not match its API contract: ${cause.message}`, {
|
|
1086
1100
|
reason: "response",
|
|
1087
1101
|
status: response.status
|
|
1088
1102
|
})));
|
|
1089
1103
|
}
|
|
1090
1104
|
function ensureSuccess(input, operation, response) {
|
|
1091
|
-
return Schema.decodeUnknownEffect(BaseEnvelope)(input).pipe(Effect.mapError(() => failure$
|
|
1105
|
+
return Schema.decodeUnknownEffect(BaseEnvelope)(input).pipe(Effect.mapError(() => failure$4(operation, "Cloudflare response did not contain a valid API envelope", {
|
|
1092
1106
|
reason: "response",
|
|
1093
1107
|
status: response.status
|
|
1094
1108
|
})), Effect.flatMap((envelope) => {
|
|
1095
1109
|
if (response.ok && envelope.success) return Effect.void;
|
|
1096
1110
|
const detail = envelope.errors[0];
|
|
1097
1111
|
const retryAfterMs = retryAfter(response.headers.get("retry-after"));
|
|
1098
|
-
return Effect.fail(failure$
|
|
1112
|
+
return Effect.fail(failure$4(operation, detail?.message ?? `Cloudflare request failed with HTTP ${response.status}`, {
|
|
1099
1113
|
...detail === void 0 ? {} : { code: detail.code },
|
|
1100
1114
|
reason: reason$1(response.status, detail?.code),
|
|
1101
1115
|
...retryAfterMs === void 0 ? {} : { retryAfterMs },
|
|
@@ -1119,7 +1133,7 @@ function retryAfter(value) {
|
|
|
1119
1133
|
const date = Date.parse(value);
|
|
1120
1134
|
return Number.isNaN(date) ? void 0 : Math.max(0, date - Date.now());
|
|
1121
1135
|
}
|
|
1122
|
-
function failure$
|
|
1136
|
+
function failure$4(operation, message, fields = {}) {
|
|
1123
1137
|
return new Error$3({
|
|
1124
1138
|
...fields,
|
|
1125
1139
|
message,
|
|
@@ -1135,6 +1149,7 @@ var auth_exports$1 = /* @__PURE__ */ __exportAll({
|
|
|
1135
1149
|
oauthAuthentication: () => oauthAuthentication,
|
|
1136
1150
|
oauthFlow: () => oauthFlow,
|
|
1137
1151
|
oauthMethod: () => oauthMethod,
|
|
1152
|
+
restore: () => restore$1,
|
|
1138
1153
|
subjectResolver: () => subjectResolver,
|
|
1139
1154
|
tokenConnectionMethod: () => tokenConnectionMethod$1,
|
|
1140
1155
|
tokenMethod: () => tokenMethod$1,
|
|
@@ -1146,7 +1161,10 @@ const authorizationServer = {
|
|
|
1146
1161
|
revocation_endpoint: "https://dash.cloudflare.com/oauth2/revoke",
|
|
1147
1162
|
token_endpoint: "https://dash.cloudflare.com/oauth2/token"
|
|
1148
1163
|
};
|
|
1149
|
-
const contextCodec$1 = codec("cloudflare.v1", Schema.Struct({
|
|
1164
|
+
const contextCodec$1 = codec("cloudflare.v1", Schema.Struct({
|
|
1165
|
+
accountId: Schema.optionalKey(Schema.String),
|
|
1166
|
+
tokenKind: Schema.Literals(["account", "user"])
|
|
1167
|
+
}));
|
|
1150
1168
|
/** Describes Cloudflare's caller-created API-token authorization method. */
|
|
1151
1169
|
function tokenMethod$1(capabilities) {
|
|
1152
1170
|
return {
|
|
@@ -1172,7 +1190,7 @@ function manifest$1(options) {
|
|
|
1172
1190
|
providerId: "cloudflare"
|
|
1173
1191
|
};
|
|
1174
1192
|
}
|
|
1175
|
-
/** Resolves the Cloudflare account selected by a completed OAuth
|
|
1193
|
+
/** Resolves the Cloudflare account selected by a completed OAuth authorization. */
|
|
1176
1194
|
function subjectResolver(options) {
|
|
1177
1195
|
return (tokens, accessToken) => Effect.gen(function* () {
|
|
1178
1196
|
const accountId = options.accountId ?? (yield* discoverAuthorizationAccount({
|
|
@@ -1224,7 +1242,7 @@ function tokenConnectionMethod$1(options) {
|
|
|
1224
1242
|
return {
|
|
1225
1243
|
capabilityEvidence: validated.capabilities.map((capability) => ({
|
|
1226
1244
|
capability,
|
|
1227
|
-
evidence: Evidence
|
|
1245
|
+
evidence: Evidence.Introspected({ observedAt })
|
|
1228
1246
|
})),
|
|
1229
1247
|
credential: {
|
|
1230
1248
|
accessToken: token,
|
|
@@ -1233,7 +1251,10 @@ function tokenConnectionMethod$1(options) {
|
|
|
1233
1251
|
},
|
|
1234
1252
|
expiresAt: validated.expiresAt,
|
|
1235
1253
|
providerAccountId: validated.accountId,
|
|
1236
|
-
providerContext: yield* contextCodec$1.encode(
|
|
1254
|
+
providerContext: yield* contextCodec$1.encode(options.tokenKind === "account" ? {
|
|
1255
|
+
accountId: validated.accountId,
|
|
1256
|
+
tokenKind: "account"
|
|
1257
|
+
} : { tokenKind: "user" }),
|
|
1237
1258
|
scopes: [...validated.scopes]
|
|
1238
1259
|
};
|
|
1239
1260
|
}),
|
|
@@ -1242,14 +1263,14 @@ function tokenConnectionMethod$1(options) {
|
|
|
1242
1263
|
token: options.token
|
|
1243
1264
|
});
|
|
1244
1265
|
}
|
|
1245
|
-
/** Converts a completed Cloudflare OAuth
|
|
1266
|
+
/** Converts a completed Cloudflare OAuth authorization into canonical connection authentication. */
|
|
1246
1267
|
const oauthAuthentication = Effect.fn("CloudflareAuth.oauthAuthentication")(function* (options) {
|
|
1247
1268
|
const resolved = yield* subjectResolver(options)(options.tokens, options.accessToken);
|
|
1248
1269
|
const observedAt = new Date(yield* Clock.currentTimeMillis);
|
|
1249
1270
|
return {
|
|
1250
1271
|
capabilityEvidence: options.capabilities.map((capability) => ({
|
|
1251
1272
|
capability,
|
|
1252
|
-
evidence: Evidence
|
|
1273
|
+
evidence: Evidence.Introspected({ observedAt })
|
|
1253
1274
|
})),
|
|
1254
1275
|
credential: {
|
|
1255
1276
|
accessToken: options.accessToken,
|
|
@@ -1262,6 +1283,26 @@ const oauthAuthentication = Effect.fn("CloudflareAuth.oauthAuthentication")(func
|
|
|
1262
1283
|
scopes: (options.tokens.scope ?? "").split(" ").filter(Boolean)
|
|
1263
1284
|
};
|
|
1264
1285
|
});
|
|
1286
|
+
/** Restore a credential-scoped Cloudflare session from persisted authorization state. */
|
|
1287
|
+
function restore$1(options) {
|
|
1288
|
+
return Effect.gen(function* () {
|
|
1289
|
+
if (options.authorization.providerId !== "cloudflare") return yield* Effect.fail(failure$3("restore", "Provider authorization belongs to another provider", "authorization"));
|
|
1290
|
+
if (options.authorization.method === "integration") return yield* Effect.fail(failure$3("restore", "Cloudflare does not support integration credentials", "unsupported"));
|
|
1291
|
+
if (options.authorization.revocation._tag !== "Active") return yield* Effect.fail(failure$3("restore", "Cloudflare authorization is pending revocation", "authorization"));
|
|
1292
|
+
const missingCapability = options.authorization.requiredCapabilities.find((capability) => !options.authorization.capabilityEvidence.some((item) => item.capability === capability));
|
|
1293
|
+
if (missingCapability !== void 0) return yield* Effect.fail(failure$3("restore", `Cloudflare authorization lacks evidence for ${missingCapability}`, "authorization"));
|
|
1294
|
+
if (options.authorization.expiresAt !== null && (Number.isNaN(options.authorization.expiresAt.valueOf()) || options.authorization.expiresAt <= /* @__PURE__ */ new Date())) return yield* Effect.fail(failure$3("restore", "Cloudflare authorization has expired", "authentication"));
|
|
1295
|
+
const context = yield* contextCodec$1.decode(options.authorization.providerContext).pipe(Effect.mapError((cause) => failure$3("restore", cause.message, "response")));
|
|
1296
|
+
return make$5({
|
|
1297
|
+
...context.accountId === void 0 ? {} : { accountId: context.accountId },
|
|
1298
|
+
...options.baseUrl === void 0 ? {} : { baseUrl: options.baseUrl },
|
|
1299
|
+
capabilities: options.authorization.capabilityEvidence.map(({ capability }) => capability),
|
|
1300
|
+
...options.fetch === void 0 ? {} : { fetch: options.fetch },
|
|
1301
|
+
token: options.credential.accessToken,
|
|
1302
|
+
tokenKind: context.tokenKind
|
|
1303
|
+
});
|
|
1304
|
+
});
|
|
1305
|
+
}
|
|
1265
1306
|
/** Creates Cloudflare's OAuth implementation of the common interactive connection capability. */
|
|
1266
1307
|
function oauthFlow(options) {
|
|
1267
1308
|
const method = oauthMethod({
|
|
@@ -1299,6 +1340,14 @@ function oauthFlow(options) {
|
|
|
1299
1340
|
})
|
|
1300
1341
|
};
|
|
1301
1342
|
}
|
|
1343
|
+
function failure$3(operation, message, reason) {
|
|
1344
|
+
return new Error$3({
|
|
1345
|
+
message,
|
|
1346
|
+
operation,
|
|
1347
|
+
providerId: "cloudflare",
|
|
1348
|
+
reason
|
|
1349
|
+
});
|
|
1350
|
+
}
|
|
1302
1351
|
//#endregion
|
|
1303
1352
|
//#region src/providers/vercel/protocol.ts
|
|
1304
1353
|
const Pagination = Schema.Struct({
|
|
@@ -1388,7 +1437,7 @@ const IntegrationToken = Schema.Struct({
|
|
|
1388
1437
|
//#region src/providers/vercel/records.ts
|
|
1389
1438
|
const decode$1 = Effect.fn("VercelRecords.decode")((zone, record) => Effect.gen(function* () {
|
|
1390
1439
|
const absolute = absoluteNameCandidate(zone, record.name);
|
|
1391
|
-
const name = yield* decode$
|
|
1440
|
+
const name = yield* decode$7(absolute).pipe(Effect.match({
|
|
1392
1441
|
onFailure: () => null,
|
|
1393
1442
|
onSuccess: (value) => value
|
|
1394
1443
|
}));
|
|
@@ -1403,7 +1452,7 @@ const decode$1 = Effect.fn("VercelRecords.decode")((zone, record) => Effect.gen(
|
|
|
1403
1452
|
policy: record.type === "CNAME" ? "exclusive" : "append",
|
|
1404
1453
|
ttl: record.ttl ?? 60
|
|
1405
1454
|
};
|
|
1406
|
-
const fromRecord = (input) => decode$
|
|
1455
|
+
const fromRecord = (input) => decode$8(input).pipe(Effect.mapError((cause) => failure$2("decodeRecord", cause.message)));
|
|
1407
1456
|
switch (record.type) {
|
|
1408
1457
|
case "A":
|
|
1409
1458
|
case "AAAA": return yield* fromRecord({
|
|
@@ -1544,6 +1593,9 @@ function discovery(client) {
|
|
|
1544
1593
|
}
|
|
1545
1594
|
/** Creates an Effect-native Vercel client without owning the credential lifecycle. */
|
|
1546
1595
|
function make$4(options) {
|
|
1596
|
+
return makeClient(options);
|
|
1597
|
+
}
|
|
1598
|
+
function makeClient(options) {
|
|
1547
1599
|
const fetch = options.fetch ?? globalThis.fetch;
|
|
1548
1600
|
const baseUrl = (options.baseUrl ?? "https://api.vercel.com").replace(/\/$/, "");
|
|
1549
1601
|
const withContext = (path, values = {}) => {
|
|
@@ -1615,9 +1667,13 @@ function make$4(options) {
|
|
|
1615
1667
|
return envelope.domain;
|
|
1616
1668
|
}).pipe(Effect.catchTag("ProviderError", (cause) => cause.reason === "not_found" ? Effect.succeed(null) : Effect.fail(cause))));
|
|
1617
1669
|
const resolveZone = Effect.fn("VercelClient.resolveZone")((name) => Effect.gen(function* () {
|
|
1670
|
+
if (options.target !== void 0) {
|
|
1671
|
+
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" }));
|
|
1672
|
+
return options.target.zoneName;
|
|
1673
|
+
}
|
|
1618
1674
|
const domain = yield* discoverZone(name);
|
|
1619
1675
|
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$
|
|
1676
|
+
return yield* decode$7(domain.name).pipe(Effect.mapError((cause) => failure$1("resolveZone", `Vercel returned an invalid storage zone: ${cause.message}`)));
|
|
1621
1677
|
}));
|
|
1622
1678
|
const listRecords = Effect.fn("VercelClient.listRecords")((zoneName) => Effect.gen(function* () {
|
|
1623
1679
|
const storageZone = yield* resolveZone(zoneName);
|
|
@@ -1661,34 +1717,84 @@ function make$4(options) {
|
|
|
1661
1717
|
until = next;
|
|
1662
1718
|
}
|
|
1663
1719
|
}));
|
|
1720
|
+
const deleteRecord = Effect.fn("VercelClient.deleteRecord")((zoneName, providerRecordId) => Effect.gen(function* () {
|
|
1721
|
+
const storageZone = yield* resolveZone(zoneName);
|
|
1722
|
+
yield* request(withContext(`/v2/domains/${encodeURIComponent(storageZone)}/records/${encodeURIComponent(providerRecordId)}`), "deleteRecord", { method: "DELETE" });
|
|
1723
|
+
}));
|
|
1724
|
+
const listAccounts = Effect.fn("VercelClient.listAccounts")(() => Effect.gen(function* () {
|
|
1725
|
+
const userResult = yield* request(new URL(`${baseUrl}/v2/user`), "listAccounts");
|
|
1726
|
+
const { user } = yield* decode(UserEnvelope, userResult.body, "listAccounts", userResult.response);
|
|
1727
|
+
const teams = yield* allTeams();
|
|
1728
|
+
return [{
|
|
1729
|
+
id: user.id,
|
|
1730
|
+
name: user.name ?? user.username,
|
|
1731
|
+
type: "personal"
|
|
1732
|
+
}, ...teams.map((team) => ({
|
|
1733
|
+
id: team.id,
|
|
1734
|
+
name: team.name ?? team.slug,
|
|
1735
|
+
type: "team"
|
|
1736
|
+
}))];
|
|
1737
|
+
}));
|
|
1738
|
+
const listZones = Effect.fn("VercelClient.listZones")((input = {}) => Effect.gen(function* () {
|
|
1739
|
+
const domains = (yield* allDomains()).filter((domain) => hasDnsStorage(domain) && (input.name === void 0 || domain.name === input.name));
|
|
1740
|
+
if (domains.length > 0 || input.name === void 0) return yield* Effect.forEach(domains, projectZone);
|
|
1741
|
+
const discovered = yield* discoverZone(input.name);
|
|
1742
|
+
return discovered === null ? [] : [yield* projectZone(discovered)];
|
|
1743
|
+
}));
|
|
1744
|
+
const listTargets = Effect.fn("VercelClient.listTargets")((input = {}) => Effect.gen(function* () {
|
|
1745
|
+
const names = input.domain === void 0 ? void 0 : new Set(candidates(input.domain).map((name) => String(name)));
|
|
1746
|
+
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));
|
|
1747
|
+
if (domains.length > 0 || input.domain === void 0) return yield* Effect.forEach(domains, targetFromDomain);
|
|
1748
|
+
for (const name of candidates(input.domain)) {
|
|
1749
|
+
const discovered = yield* discoverZone(name);
|
|
1750
|
+
if (discovered !== null && (input.accountId === void 0 || (discovered.teamId ?? discovered.userId) === input.accountId)) return [yield* targetFromDomain(discovered)];
|
|
1751
|
+
}
|
|
1752
|
+
return [];
|
|
1753
|
+
}));
|
|
1664
1754
|
return {
|
|
1665
1755
|
id: "vercel",
|
|
1756
|
+
providerId: "vercel",
|
|
1666
1757
|
createRecord,
|
|
1667
|
-
deleteRecord
|
|
1668
|
-
const storageZone = yield* resolveZone(zoneName);
|
|
1669
|
-
yield* request(withContext(`/v2/domains/${encodeURIComponent(storageZone)}/records/${encodeURIComponent(providerRecordId)}`), "deleteRecord", { method: "DELETE" });
|
|
1670
|
-
})),
|
|
1758
|
+
deleteRecord,
|
|
1671
1759
|
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
|
-
})),
|
|
1760
|
+
listAccounts,
|
|
1686
1761
|
listRecords,
|
|
1687
|
-
listZones
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
const
|
|
1691
|
-
|
|
1762
|
+
listZones,
|
|
1763
|
+
listTargets,
|
|
1764
|
+
resolveTarget: Effect.fn("VercelClient.resolveTarget")((domain) => Effect.gen(function* () {
|
|
1765
|
+
const candidates$1 = yield* listTargets({ domain });
|
|
1766
|
+
for (const zoneName of candidates(domain)) {
|
|
1767
|
+
const matches = candidates$1.filter((target) => target.zoneName === zoneName);
|
|
1768
|
+
if (matches.length === 1) {
|
|
1769
|
+
const target = matches[0];
|
|
1770
|
+
if (target !== void 0) return Resolution.Resolved({ target });
|
|
1771
|
+
}
|
|
1772
|
+
if (matches.length > 1) return Resolution.SelectionRequired({ candidates: matches });
|
|
1773
|
+
}
|
|
1774
|
+
return Resolution.NotFound({ domain });
|
|
1775
|
+
})),
|
|
1776
|
+
forTarget: Effect.fn("VercelClient.forTarget")((target) => Effect.gen(function* () {
|
|
1777
|
+
if (target.accountKind === "team") {
|
|
1778
|
+
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" }));
|
|
1779
|
+
} 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" }));
|
|
1780
|
+
else if (target.accountKind === "account") return yield* Effect.fail(failure$1("forTarget", "Vercel does not support account-kind targets", { reason: "unsupported" }));
|
|
1781
|
+
else if (target.accountKind === null) return yield* Effect.fail(failure$1("forTarget", "Vercel targets must identify a personal or team account", { reason: "request" }));
|
|
1782
|
+
const selected = (yield* listTargets({
|
|
1783
|
+
accountId: target.accountId,
|
|
1784
|
+
domain: target.zoneName
|
|
1785
|
+
})).find((candidate) => candidate.accountId === target.accountId && candidate.accountKind === target.accountKind && candidate.zoneId === target.zoneId && candidate.zoneName === target.zoneName);
|
|
1786
|
+
if (selected === void 0) return yield* Effect.fail(failure$1("forTarget", `Vercel target ${target.zoneName} is not visible to this installation`, { reason: "authorization" }));
|
|
1787
|
+
const client = makeClient({
|
|
1788
|
+
...options,
|
|
1789
|
+
target: selected
|
|
1790
|
+
});
|
|
1791
|
+
return {
|
|
1792
|
+
id: client.id,
|
|
1793
|
+
createRecord: client.createRecord,
|
|
1794
|
+
deleteRecord: client.deleteRecord,
|
|
1795
|
+
getRecord: client.getRecord,
|
|
1796
|
+
listRecords: client.listRecords
|
|
1797
|
+
};
|
|
1692
1798
|
})),
|
|
1693
1799
|
validateToken: Effect.fn("VercelClient.validateToken")(() => Effect.gen(function* () {
|
|
1694
1800
|
let accountId;
|
|
@@ -1713,8 +1819,8 @@ function hasDnsStorage(domain, configuredServiceType = domain.serviceType) {
|
|
|
1713
1819
|
return configuredServiceType === "zeit.world" || domain.zone === true || domain.intendedNameservers.length > 0;
|
|
1714
1820
|
}
|
|
1715
1821
|
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$
|
|
1822
|
+
const name = yield* decode$7(domain.name).pipe(Effect.mapError((cause) => failure$1("listZones", cause.message, { reason: "response" })));
|
|
1823
|
+
const nameservers = yield* Effect.forEach(domain.intendedNameservers, (nameserver) => decode$7(nameserver).pipe(Effect.mapError((cause) => failure$1("listZones", cause.message, { reason: "response" }))));
|
|
1718
1824
|
return {
|
|
1719
1825
|
accountId: domain.teamId ?? domain.userId,
|
|
1720
1826
|
id: domain.id,
|
|
@@ -1723,6 +1829,21 @@ const projectZone = Effect.fn("VercelClient.projectZone")((domain) => Effect.gen
|
|
|
1723
1829
|
status: domain.verified ? "active" : "pending"
|
|
1724
1830
|
};
|
|
1725
1831
|
}));
|
|
1832
|
+
const targetFromDomain = Effect.fn("VercelClient.targetFromDomain")((domain) => Effect.gen(function* () {
|
|
1833
|
+
const zoneName = yield* decode$7(domain.name).pipe(Effect.mapError((cause) => failure$1("listTargets", cause.message, { reason: "response" })));
|
|
1834
|
+
const nameservers = yield* Effect.forEach(domain.nameservers.length > 0 ? domain.nameservers : domain.intendedNameservers, (nameserver) => decode$7(nameserver).pipe(Effect.mapError((cause) => failure$1("listTargets", cause.message, { reason: "response" }))));
|
|
1835
|
+
return {
|
|
1836
|
+
accountId: domain.teamId ?? domain.userId,
|
|
1837
|
+
accountKind: domain.teamId === null ? "personal" : "team",
|
|
1838
|
+
evidence: {
|
|
1839
|
+
nameservers,
|
|
1840
|
+
status: domain.verified ? "active" : "pending",
|
|
1841
|
+
zoneType: domain.serviceType
|
|
1842
|
+
},
|
|
1843
|
+
zoneId: domain.id,
|
|
1844
|
+
zoneName
|
|
1845
|
+
};
|
|
1846
|
+
}));
|
|
1726
1847
|
function decode(schema, input, operation, response) {
|
|
1727
1848
|
return Schema.decodeUnknownEffect(schema)(input).pipe(Effect.mapError((cause) => failure$1(operation, `Vercel response did not match its API contract: ${cause.message}`, {
|
|
1728
1849
|
reason: "response",
|
|
@@ -1770,6 +1891,7 @@ var auth_exports = /* @__PURE__ */ __exportAll({
|
|
|
1770
1891
|
integrationFlow: () => integrationFlow,
|
|
1771
1892
|
integrationMethod: () => integrationMethod,
|
|
1772
1893
|
manifest: () => manifest,
|
|
1894
|
+
restore: () => restore,
|
|
1773
1895
|
tokenConnectionMethod: () => tokenConnectionMethod,
|
|
1774
1896
|
tokenMethod: () => tokenMethod
|
|
1775
1897
|
});
|
|
@@ -1794,7 +1916,7 @@ function integrationMethod(options) {
|
|
|
1794
1916
|
_tag: "integration",
|
|
1795
1917
|
capabilities: [...options.capabilities],
|
|
1796
1918
|
installUrl: `https://vercel.com/integrations/${encodeURIComponent(options.slug)}/new`,
|
|
1797
|
-
tokenEndpoint: "https://api.vercel.com/oauth/access_token"
|
|
1919
|
+
tokenEndpoint: "https://api.vercel.com/v2/oauth/access_token"
|
|
1798
1920
|
};
|
|
1799
1921
|
}
|
|
1800
1922
|
/** Creates the Vercel token and integration authorization manifest. */
|
|
@@ -1815,7 +1937,7 @@ const exchangeCode = Effect.fn("VercelAuth.exchangeCode")((options) => Effect.ge
|
|
|
1815
1937
|
redirect_uri: options.redirectUri
|
|
1816
1938
|
});
|
|
1817
1939
|
const response = yield* Effect.tryPromise({
|
|
1818
|
-
try: () => fetch(`${baseUrl}/oauth/access_token`, {
|
|
1940
|
+
try: () => fetch(`${baseUrl}/v2/oauth/access_token`, {
|
|
1819
1941
|
body,
|
|
1820
1942
|
headers: {
|
|
1821
1943
|
Accept: "application/json",
|
|
@@ -1840,7 +1962,7 @@ const exchangeCode = Effect.fn("VercelAuth.exchangeCode")((options) => Effect.ge
|
|
|
1840
1962
|
_tag: "team",
|
|
1841
1963
|
teamId: token.team_id
|
|
1842
1964
|
},
|
|
1843
|
-
installationId: token.installation_id ?? null,
|
|
1965
|
+
installationId: token.installation_id ?? options.configurationId ?? null,
|
|
1844
1966
|
userId: token.user_id
|
|
1845
1967
|
};
|
|
1846
1968
|
}));
|
|
@@ -1857,7 +1979,7 @@ function tokenConnectionMethod(options) {
|
|
|
1857
1979
|
return {
|
|
1858
1980
|
capabilityEvidence: validated.capabilities.map((capability) => ({
|
|
1859
1981
|
capability,
|
|
1860
|
-
evidence: Evidence
|
|
1982
|
+
evidence: Evidence.Introspected({ observedAt })
|
|
1861
1983
|
})),
|
|
1862
1984
|
credential: {
|
|
1863
1985
|
accessToken: token,
|
|
@@ -1889,7 +2011,7 @@ const integrationAuthentication = Effect.fn("VercelAuth.integrationAuthenticatio
|
|
|
1889
2011
|
return {
|
|
1890
2012
|
capabilityEvidence: options.capabilities.map((capability) => ({
|
|
1891
2013
|
capability,
|
|
1892
|
-
evidence: Evidence
|
|
2014
|
+
evidence: Evidence.Introspected({ observedAt })
|
|
1893
2015
|
})),
|
|
1894
2016
|
credential: {
|
|
1895
2017
|
accessToken: exchanged.accessToken,
|
|
@@ -1909,6 +2031,27 @@ const integrationAuthentication = Effect.fn("VercelAuth.integrationAuthenticatio
|
|
|
1909
2031
|
scopes: []
|
|
1910
2032
|
};
|
|
1911
2033
|
});
|
|
2034
|
+
/** Restore a credential-scoped Vercel session from persisted authorization state. */
|
|
2035
|
+
function restore(options) {
|
|
2036
|
+
return Effect.gen(function* () {
|
|
2037
|
+
if (options.authorization.providerId !== "vercel") return yield* Effect.fail(failure("restore", "Provider authorization belongs to another provider", "authorization"));
|
|
2038
|
+
if (options.authorization.revocation._tag !== "Active") return yield* Effect.fail(failure("restore", "Vercel authorization is pending revocation", "authorization"));
|
|
2039
|
+
const missingCapability = options.authorization.requiredCapabilities.find((capability) => !options.authorization.capabilityEvidence.some((item) => item.capability === capability));
|
|
2040
|
+
if (missingCapability !== void 0) return yield* Effect.fail(failure("restore", `Vercel authorization lacks evidence for ${missingCapability}`, "authorization"));
|
|
2041
|
+
if (options.authorization.expiresAt !== null && (Number.isNaN(options.authorization.expiresAt.valueOf()) || options.authorization.expiresAt <= /* @__PURE__ */ new Date())) return yield* Effect.fail(failure("restore", "Vercel authorization has expired", "authentication"));
|
|
2042
|
+
const context = yield* contextCodec.decode(options.authorization.providerContext).pipe(Effect.mapError((cause) => failure("restore", cause.message, "response")));
|
|
2043
|
+
return make$4({
|
|
2044
|
+
...options.baseUrl === void 0 ? {} : { baseUrl: options.baseUrl },
|
|
2045
|
+
capabilities: options.authorization.capabilityEvidence.map(({ capability }) => capability),
|
|
2046
|
+
...options.fetch === void 0 ? {} : { fetch: options.fetch },
|
|
2047
|
+
context: context._tag === "team" ? {
|
|
2048
|
+
_tag: "team",
|
|
2049
|
+
teamId: context.teamId
|
|
2050
|
+
} : { _tag: "personal" },
|
|
2051
|
+
token: options.credential.accessToken
|
|
2052
|
+
});
|
|
2053
|
+
});
|
|
2054
|
+
}
|
|
1912
2055
|
/** Creates Vercel Integration's implementation of the common interactive connection capability. */
|
|
1913
2056
|
function integrationFlow(options) {
|
|
1914
2057
|
const method = integrationMethod(options);
|
|
@@ -1922,21 +2065,39 @@ function integrationFlow(options) {
|
|
|
1922
2065
|
retry: "after-user-action"
|
|
1923
2066
|
})));
|
|
1924
2067
|
const code = callbackUrl.searchParams.get("code");
|
|
1925
|
-
|
|
2068
|
+
const configurationId = callbackUrl.searchParams.get("configurationId");
|
|
2069
|
+
if (callbackUrl.searchParams.get("state") !== continuation.state || code === null || configurationId === null) return yield* new Error$1({
|
|
1926
2070
|
category: "authorization",
|
|
1927
2071
|
message: "Vercel Integration callback does not match its continuation",
|
|
1928
2072
|
operation: "VercelAuth.integrationFlow.complete",
|
|
1929
2073
|
retry: "after-user-action"
|
|
1930
2074
|
});
|
|
1931
|
-
|
|
2075
|
+
const authentication = yield* integrationAuthentication({
|
|
1932
2076
|
...options.baseUrl === void 0 ? {} : { baseUrl: options.baseUrl },
|
|
1933
2077
|
capabilities: options.capabilities,
|
|
1934
2078
|
clientId: options.clientId,
|
|
1935
2079
|
clientSecret: options.clientSecret,
|
|
1936
2080
|
code: make$6(code),
|
|
2081
|
+
configurationId,
|
|
1937
2082
|
...options.fetch === void 0 ? {} : { fetch: options.fetch },
|
|
1938
2083
|
redirectUri: options.redirectUri
|
|
1939
2084
|
});
|
|
2085
|
+
const callbackTeamId = callbackUrl.searchParams.get("teamId");
|
|
2086
|
+
if (callbackTeamId !== null) {
|
|
2087
|
+
const context = yield* contextCodec.decode(authentication.providerContext).pipe(Effect.mapError((cause) => new Error$1({
|
|
2088
|
+
category: "provider",
|
|
2089
|
+
message: cause.message,
|
|
2090
|
+
operation: "VercelAuth.integrationFlow.complete",
|
|
2091
|
+
retry: "unknown"
|
|
2092
|
+
})));
|
|
2093
|
+
if (context._tag !== "team" || context.teamId !== callbackTeamId) return yield* new Error$1({
|
|
2094
|
+
category: "authorization",
|
|
2095
|
+
message: "Vercel Integration callback team does not match the exchanged installation",
|
|
2096
|
+
operation: "VercelAuth.integrationFlow.complete",
|
|
2097
|
+
retry: "after-user-action"
|
|
2098
|
+
});
|
|
2099
|
+
}
|
|
2100
|
+
return authentication;
|
|
1940
2101
|
}),
|
|
1941
2102
|
method: "integration",
|
|
1942
2103
|
providerId: "vercel",
|
|
@@ -2051,7 +2212,7 @@ function make$3(options) {
|
|
|
2051
2212
|
}).pipe(Effect.flatMap((message) => decodeResolution(query, message)))) };
|
|
2052
2213
|
}
|
|
2053
2214
|
const layer$2 = (options) => Layer.succeed(Service$2, make$3(options));
|
|
2054
|
-
const toAsync$2 = (options) => toAsync$
|
|
2215
|
+
const toAsync$2 = (options) => toAsync$5(make$3(options));
|
|
2055
2216
|
function decodeResolution(query, message) {
|
|
2056
2217
|
return Effect.try({
|
|
2057
2218
|
try: () => DnsPacket.decode(message),
|
|
@@ -2109,7 +2270,7 @@ function decodeAnswer(answer) {
|
|
|
2109
2270
|
})
|
|
2110
2271
|
});
|
|
2111
2272
|
if (decoded === void 0) return null;
|
|
2112
|
-
const name = yield* decode$
|
|
2273
|
+
const name = yield* decode$7(answer.name);
|
|
2113
2274
|
const data = yield* Effect.try({
|
|
2114
2275
|
try: () => parse(decoded.type, decoded.data),
|
|
2115
2276
|
catch: (cause) => new Error$4({
|
|
@@ -2249,7 +2410,7 @@ function make$2(options = {}) {
|
|
|
2249
2410
|
});
|
|
2250
2411
|
}
|
|
2251
2412
|
const layer$1 = (options = {}) => Layer.succeed(Service$2, make$2(options));
|
|
2252
|
-
const toAsync$1 = (options = {}) => toAsync$
|
|
2413
|
+
const toAsync$1 = (options = {}) => toAsync$5(make$2(options));
|
|
2253
2414
|
//#endregion
|
|
2254
2415
|
//#region src/verification/google-doh.ts
|
|
2255
2416
|
var google_doh_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -2265,7 +2426,7 @@ function make$1(options = {}) {
|
|
|
2265
2426
|
});
|
|
2266
2427
|
}
|
|
2267
2428
|
const layer = (options = {}) => Layer.succeed(Service$2, make$1(options));
|
|
2268
|
-
const toAsync = (options = {}) => toAsync$
|
|
2429
|
+
const toAsync = (options = {}) => toAsync$5(make$1(options));
|
|
2269
2430
|
//#endregion
|
|
2270
2431
|
//#region src/verification/resolver-pool.ts
|
|
2271
2432
|
var resolver_pool_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -2415,6 +2576,6 @@ function recordValue(requirement) {
|
|
|
2415
2576
|
}
|
|
2416
2577
|
}
|
|
2417
2578
|
//#endregion
|
|
2418
|
-
export {
|
|
2579
|
+
export { manifest_exports as $, discovery as A, tokenValidator as B, integrationFlow as C, tokenConnectionMethod as D, restore as E, oauthMethod as F, connection_authorization_exports as G, discovery$1 as H, restore$1 as I, secret_exports as J, session_exports as K, subjectResolver as L, auth_exports$1 as M, manifest$1 as N, tokenMethod as O, oauthFlow as P, decode$6 as Q, tokenConnectionMethod$1 as R, exchangeCode as S, manifest as T, make$5 as U, client_exports$1 as V, authorize as W, Method as X, provider_context_exports as Y, Schema$2 as Z, toAsync$1 as _, Result as a, start$1 as at, 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, complete$1 as it, make$4 as j, client_exports as k, Policy as l, google_doh_exports as m, ProviderObservation as n, Method$1 as nt, observe as o, version as ot, resolver_pool_exports as p, selection_exports as q, PublicDns as r, StartResult as rt, verify_exports as s, Provider as t, Error$1 as tt, Service as u, doh_exports as v, integrationMethod as w, auth_exports as x, toAsync$2 as y, tokenMethod$1 as z };
|
|
2419
2580
|
|
|
2420
|
-
//# sourceMappingURL=verify-
|
|
2581
|
+
//# sourceMappingURL=verify-B2fPyjde.mjs.map
|