domainkit 0.1.1 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +87 -83
- package/dist/{client-BnD7QLRF.mjs → auth-6-KRy1gN.mjs} +235 -131
- package/dist/auth-6-KRy1gN.mjs.map +1 -0
- package/dist/{auth-B0owkrpk.mjs → auth-B4jq3yk3.mjs} +212 -20
- package/dist/auth-B4jq3yk3.mjs.map +1 -0
- package/dist/authorization-CQSKKMrW.mjs +255 -0
- package/dist/authorization-CQSKKMrW.mjs.map +1 -0
- package/dist/canonical-json-CpvqGMcr.mjs +57 -0
- package/dist/canonical-json-CpvqGMcr.mjs.map +1 -0
- package/dist/{cloudflare-D0-J2MfP.mjs → cloudflare-Bg_p-M2c.mjs} +26 -3
- package/dist/cloudflare-Bg_p-M2c.mjs.map +1 -0
- package/dist/cloudflare-FDl5q7Dm.d.mts +32 -0
- package/dist/cloudflare.d.mts +3 -3
- package/dist/cloudflare.mjs +2 -2
- package/dist/connection-BXoSrhZs.mjs +58 -0
- package/dist/connection-BXoSrhZs.mjs.map +1 -0
- package/dist/connection-CEqadXSm.d.mts +182 -0
- package/dist/connection-X3rZhrBZ.mjs +186 -0
- package/dist/connection-X3rZhrBZ.mjs.map +1 -0
- package/dist/connection-api-DftK7eHo.d.mts +7 -0
- package/dist/{dns-record-C_KwGPLf.mjs → dns-record-BLeqo56C.mjs} +23 -3
- package/dist/dns-record-BLeqo56C.mjs.map +1 -0
- package/dist/effect-cloudflare.d.mts +2 -2
- package/dist/effect-cloudflare.mjs +2 -2
- package/dist/effect-vercel.d.mts +2 -2
- package/dist/effect-vercel.mjs +2 -2
- package/dist/effect.d.mts +9 -46
- package/dist/effect.mjs +29 -7
- package/dist/effect.mjs.map +1 -1
- package/dist/{index-D2howbNz.d.mts → index-B1opsgls.d.mts} +53 -6
- package/dist/{index-CEzk2gbZ.d.mts → index-Ewc__bQu.d.mts} +58 -7
- package/dist/index.d.mts +169 -113
- package/dist/index.mjs +201 -139
- package/dist/index.mjs.map +1 -1
- package/dist/{resolver-BHPsADzE.d.mts → resolver-BbVzTbCX.d.mts} +630 -273
- package/dist/resolver-CxE-VDw4.mjs +598 -0
- package/dist/resolver-CxE-VDw4.mjs.map +1 -0
- package/dist/testing.d.mts +53 -44
- package/dist/testing.mjs +379 -107
- package/dist/testing.mjs.map +1 -1
- package/dist/{vercel-DD89DCqH.mjs → vercel-BIFpZRPu.mjs} +23 -3
- package/dist/vercel-BIFpZRPu.mjs.map +1 -0
- package/dist/vercel-DgJaUX9D.d.mts +31 -0
- package/dist/vercel.d.mts +3 -3
- package/dist/vercel.mjs +2 -2
- package/dist/verify-7_EbKbFM.d.mts +805 -0
- package/dist/verify-Q5RYpnuw.mjs +642 -0
- package/dist/verify-Q5RYpnuw.mjs.map +1 -0
- package/dist/zone-discovery-BA5I24vu.d.mts +1390 -0
- package/dist/zones-DKHTpX9C.mjs +19 -0
- package/dist/zones-DKHTpX9C.mjs.map +1 -0
- package/package.json +1 -1
- package/dist/auth-B0owkrpk.mjs.map +0 -1
- package/dist/client-BnD7QLRF.mjs.map +0 -1
- package/dist/cloudflare-CprjjXfm.d.mts +0 -23
- package/dist/cloudflare-D0-J2MfP.mjs.map +0 -1
- package/dist/dns-record-C_KwGPLf.mjs.map +0 -1
- package/dist/manifest-CP85ePFn.d.mts +0 -583
- package/dist/receipt-CGSnfZQ6.mjs +0 -28
- package/dist/receipt-CGSnfZQ6.mjs.map +0 -1
- package/dist/resolver-tEXpKxmH.mjs +0 -200
- package/dist/resolver-tEXpKxmH.mjs.map +0 -1
- package/dist/vercel-D-E2aqb1.d.mts +0 -22
- package/dist/vercel-DD89DCqH.mjs.map +0 -1
- package/dist/verify-B80xCBh2.d.mts +0 -1407
- package/dist/verify-BXOvXE20.mjs +0 -1388
- package/dist/verify-BXOvXE20.mjs.map +0 -1
package/dist/testing.mjs
CHANGED
|
@@ -1,83 +1,166 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { Effect, Layer } from "effect";
|
|
6
|
-
//#region src/stores/in-memory-
|
|
7
|
-
var
|
|
8
|
-
layer: () => layer$
|
|
9
|
-
make: () => make$
|
|
10
|
-
toAsync: () => toAsync$6
|
|
2
|
+
import { M as Service, O as parse, _ as equals, b as Service$1, j as Error$1, v as parse$1, w as toAsync$2, x as fromAsync$1, y as Error$2 } from "./dns-record-BLeqo56C.mjs";
|
|
3
|
+
import { i as webCryptoLayer } from "./canonical-json-CpvqGMcr.mjs";
|
|
4
|
+
import { S as create, _ as PartialApplyError, b as apply, d as apply$1, f as authorize$1, h as ConflictError, i as toAsync$3, n as Service$2, p as create$1, v as StaleError, x as authorize, y as Target } from "./resolver-CxE-VDw4.mjs";
|
|
5
|
+
import { Effect, Layer, Ref, Schema, Semaphore } from "effect";
|
|
6
|
+
//#region src/stores/in-memory-lifecycle-repository.ts
|
|
7
|
+
var in_memory_lifecycle_repository_exports = /* @__PURE__ */ __exportAll({
|
|
8
|
+
layer: () => layer$2,
|
|
9
|
+
make: () => make$3
|
|
11
10
|
});
|
|
12
|
-
function
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
put: Effect.fn("InMemoryConnectionStore.put")((connection) => Effect.sync(() => void connections.set(connection.id, connection)))
|
|
20
|
-
};
|
|
11
|
+
function missing(operation, message) {
|
|
12
|
+
return new Error$1({
|
|
13
|
+
category: "storage",
|
|
14
|
+
message,
|
|
15
|
+
operation,
|
|
16
|
+
retry: "never"
|
|
17
|
+
});
|
|
21
18
|
}
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
19
|
+
function make$3(options = {}) {
|
|
20
|
+
const state = Effect.runSync(Ref.make(/* @__PURE__ */ new Map()));
|
|
21
|
+
const mutations = Semaphore.makeUnsafe(1);
|
|
22
|
+
const serialize = (effect) => mutations.withPermit(effect);
|
|
23
|
+
const commit = Effect.fn("InMemoryAuthorizationLifecycleRepository.commit")(function* (operation, aggregate) {
|
|
24
|
+
if (options.beforeCommit !== void 0) yield* options.beforeCommit(operation, aggregate);
|
|
25
|
+
yield* Ref.update(state, (entries) => {
|
|
26
|
+
const next = new Map(entries);
|
|
27
|
+
if (aggregate === null) return next;
|
|
28
|
+
next.set(aggregate.authorization.id, aggregate);
|
|
29
|
+
return next;
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
const remove = Effect.fn("InMemoryAuthorizationLifecycleRepository.remove")(function* (authorizationId) {
|
|
33
|
+
if (options.beforeCommit !== void 0) yield* options.beforeCommit("remove", null);
|
|
34
|
+
yield* Ref.update(state, (entries) => {
|
|
35
|
+
const next = new Map(entries);
|
|
36
|
+
next.delete(authorizationId);
|
|
37
|
+
return next;
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
const get = Effect.fn("AuthorizationLifecycleRepository.get")((authorizationId) => Ref.get(state).pipe(Effect.map((entries) => entries.get(authorizationId) ?? null)));
|
|
41
|
+
const getByConnectionId = Effect.fn("AuthorizationLifecycleRepository.getByConnectionId")((connectionId) => Ref.get(state).pipe(Effect.map((entries) => [...entries.values()].find((aggregate) => aggregate.bindings.some((binding) => binding.id === connectionId)) ?? null)));
|
|
42
|
+
const finishRevocation = Effect.fn("AuthorizationLifecycleRepository.finishRevocation")(function* (aggregate, revoke) {
|
|
43
|
+
const pending = {
|
|
44
|
+
...aggregate,
|
|
45
|
+
authorization: {
|
|
46
|
+
...aggregate.authorization,
|
|
47
|
+
revocation: aggregate.authorization.revocation._tag === "Pending" ? aggregate.authorization.revocation : {
|
|
48
|
+
_tag: "Pending",
|
|
49
|
+
requestedAt: /* @__PURE__ */ new Date()
|
|
50
|
+
}
|
|
41
51
|
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
52
|
+
};
|
|
53
|
+
yield* commit("prepareRevocation", pending);
|
|
54
|
+
yield* revoke(pending);
|
|
55
|
+
yield* remove(pending.authorization.id);
|
|
56
|
+
return {
|
|
57
|
+
authorizationId: pending.authorization.id,
|
|
58
|
+
remainingBindings: 0,
|
|
59
|
+
revokedAuthorization: true
|
|
60
|
+
};
|
|
61
|
+
});
|
|
62
|
+
return Service.of({
|
|
63
|
+
bind: Effect.fn("AuthorizationLifecycleRepository.bind")((authorizationId, binding) => serialize(Effect.gen(function* () {
|
|
64
|
+
const aggregate = yield* get(authorizationId);
|
|
65
|
+
if (aggregate === null) return yield* missing("bind", "Authorization aggregate does not exist");
|
|
66
|
+
const next = {
|
|
67
|
+
...aggregate,
|
|
68
|
+
bindings: [...aggregate.bindings.filter((candidate) => candidate.id !== binding.id), binding]
|
|
69
|
+
};
|
|
70
|
+
yield* commit("bind", next);
|
|
71
|
+
return next;
|
|
72
|
+
}))),
|
|
73
|
+
connect: Effect.fn("AuthorizationLifecycleRepository.connect")((aggregate) => serialize(Effect.gen(function* () {
|
|
74
|
+
yield* commit("connect", aggregate);
|
|
75
|
+
return aggregate;
|
|
76
|
+
}))),
|
|
77
|
+
detach: ({ connectionId, revoke }) => serialize(Effect.gen(function* () {
|
|
78
|
+
const aggregate = yield* getByConnectionId(connectionId);
|
|
79
|
+
if (aggregate === null) return yield* missing("detach", "Connection binding does not exist");
|
|
80
|
+
const remaining = aggregate.bindings.filter((binding) => binding.id !== connectionId);
|
|
81
|
+
if (remaining.length > 0) {
|
|
82
|
+
yield* commit("detachBinding", {
|
|
83
|
+
...aggregate,
|
|
84
|
+
bindings: remaining
|
|
85
|
+
});
|
|
86
|
+
return {
|
|
87
|
+
authorizationId: aggregate.authorization.id,
|
|
88
|
+
remainingBindings: remaining.length,
|
|
89
|
+
revokedAuthorization: false
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
return yield* finishRevocation(aggregate, revoke);
|
|
93
|
+
}).pipe(Effect.withSpan("AuthorizationLifecycleRepository.detach"))),
|
|
94
|
+
findByProviderAccount: Effect.fn("AuthorizationLifecycleRepository.findByProviderAccount")((providerId, providerAccountId) => Ref.get(state).pipe(Effect.map((entries) => [...entries.values()].find(({ authorization }) => authorization.providerId === providerId && authorization.providerAccountId === providerAccountId) ?? null))),
|
|
95
|
+
get,
|
|
96
|
+
getByConnectionId,
|
|
97
|
+
promoteEvidence: Effect.fn("AuthorizationLifecycleRepository.promoteEvidence")((authorizationId, evidence) => serialize(Effect.gen(function* () {
|
|
98
|
+
const aggregate = yield* get(authorizationId);
|
|
99
|
+
if (aggregate === null) return yield* missing("promoteEvidence", "Authorization aggregate does not exist");
|
|
100
|
+
const byCapability = new Map(aggregate.authorization.capabilityEvidence.map((item) => [item.capability, item]));
|
|
101
|
+
for (const item of evidence) byCapability.set(item.capability, item);
|
|
102
|
+
const next = {
|
|
103
|
+
...aggregate,
|
|
104
|
+
authorization: {
|
|
105
|
+
...aggregate.authorization,
|
|
106
|
+
capabilityEvidence: [...byCapability.values()]
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
yield* commit("promoteEvidence", next);
|
|
110
|
+
return next;
|
|
111
|
+
}))),
|
|
112
|
+
recover: ({ authorizationId, revoke }) => serialize(Effect.gen(function* () {
|
|
113
|
+
const aggregate = yield* get(authorizationId);
|
|
114
|
+
if (aggregate === null) return yield* missing("recover", "Authorization aggregate does not exist");
|
|
115
|
+
if (aggregate.authorization.revocation._tag !== "Pending") return yield* missing("recover", "Authorization is not awaiting revocation");
|
|
116
|
+
return yield* finishRevocation(aggregate, revoke);
|
|
117
|
+
}).pipe(Effect.withSpan("AuthorizationLifecycleRepository.recover"))),
|
|
118
|
+
rotate: Effect.fn("AuthorizationLifecycleRepository.rotate")((authorizationId, credential, expiresAt) => serialize(Effect.gen(function* () {
|
|
119
|
+
const aggregate = yield* get(authorizationId);
|
|
120
|
+
if (aggregate === null) return yield* missing("rotate", "Authorization aggregate does not exist");
|
|
121
|
+
const next = {
|
|
122
|
+
...aggregate,
|
|
123
|
+
authorization: {
|
|
124
|
+
...aggregate.authorization,
|
|
125
|
+
expiresAt
|
|
126
|
+
},
|
|
127
|
+
credential
|
|
128
|
+
};
|
|
129
|
+
yield* commit("rotate", next);
|
|
130
|
+
return next;
|
|
131
|
+
})))
|
|
132
|
+
});
|
|
53
133
|
}
|
|
54
|
-
const layer$
|
|
55
|
-
const toAsync$5 = (store = make$5()) => toAsync$9(store);
|
|
134
|
+
const layer$2 = (options = {}) => Layer.sync(Service, () => make$3(options));
|
|
56
135
|
//#endregion
|
|
57
|
-
//#region src/stores/in-memory-
|
|
58
|
-
var
|
|
59
|
-
|
|
60
|
-
make
|
|
61
|
-
toAsync: () => toAsync$4
|
|
62
|
-
});
|
|
63
|
-
function make$4() {
|
|
64
|
-
const credentials = /* @__PURE__ */ new Map();
|
|
136
|
+
//#region src/stores/in-memory-continuation.ts
|
|
137
|
+
var in_memory_continuation_exports = /* @__PURE__ */ __exportAll({ make: () => make$2 });
|
|
138
|
+
function make$2() {
|
|
139
|
+
const state = Effect.runSync(Ref.make(/* @__PURE__ */ new Map()));
|
|
65
140
|
return {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
141
|
+
consume: Effect.fn("ConnectionContinuations.consume")(function* (id, now) {
|
|
142
|
+
return yield* Ref.modify(state, (entries) => {
|
|
143
|
+
const continuation = entries.get(id);
|
|
144
|
+
const next = new Map(entries);
|
|
145
|
+
next.delete(id);
|
|
146
|
+
return [continuation !== void 0 && continuation.expiresAt > now ? continuation : null, next];
|
|
147
|
+
});
|
|
148
|
+
}),
|
|
149
|
+
put: Effect.fn("ConnectionContinuations.put")((continuation) => Ref.update(state, (entries) => {
|
|
150
|
+
const next = new Map(entries);
|
|
151
|
+
next.set(continuation.id, continuation);
|
|
152
|
+
return next;
|
|
153
|
+
}))
|
|
69
154
|
};
|
|
70
155
|
}
|
|
71
|
-
const layer$4 = () => Layer.succeed(Service$3, make$4());
|
|
72
|
-
const toAsync$4 = (store = make$4()) => toAsync$10(store);
|
|
73
156
|
//#endregion
|
|
74
157
|
//#region src/provider/in-memory.ts
|
|
75
158
|
var in_memory_exports = /* @__PURE__ */ __exportAll({
|
|
76
|
-
layer: () => layer$
|
|
77
|
-
make: () => make$
|
|
78
|
-
toAsync: () => toAsync$
|
|
159
|
+
layer: () => layer$1,
|
|
160
|
+
make: () => make$1,
|
|
161
|
+
toAsync: () => toAsync$1
|
|
79
162
|
});
|
|
80
|
-
function make$
|
|
163
|
+
function make$1(options = {}) {
|
|
81
164
|
const id = options.id ?? "memory";
|
|
82
165
|
const records = /* @__PURE__ */ new Map();
|
|
83
166
|
const recordIds = /* @__PURE__ */ new Map();
|
|
@@ -105,61 +188,250 @@ function make$3(options = {}) {
|
|
|
105
188
|
getRecord: Effect.fn("InMemoryDnsProvider.getRecord")((_zone, providerRecordId) => Effect.sync(() => recordIds.get(providerRecordId) ?? null))
|
|
106
189
|
};
|
|
107
190
|
}
|
|
108
|
-
const layer$
|
|
109
|
-
const toAsync$
|
|
191
|
+
const layer$1 = (options = {}) => Layer.succeed(Service$1, make$1(options));
|
|
192
|
+
const toAsync$1 = (options = {}) => toAsync$2(make$1(options));
|
|
110
193
|
//#endregion
|
|
111
194
|
//#region src/verification/in-memory.ts
|
|
112
195
|
var in_memory_exports$1 = /* @__PURE__ */ __exportAll({
|
|
113
|
-
layer: () => layer
|
|
114
|
-
make: () => make
|
|
115
|
-
toAsync: () => toAsync
|
|
196
|
+
layer: () => layer,
|
|
197
|
+
make: () => make,
|
|
198
|
+
toAsync: () => toAsync
|
|
116
199
|
});
|
|
117
|
-
function make
|
|
200
|
+
function make(resolve) {
|
|
118
201
|
return { resolve: Effect.fn("InMemoryDnsResolver.resolve")((query) => Effect.suspend(() => {
|
|
119
202
|
const resolution = resolve(query);
|
|
120
203
|
return Effect.isEffect(resolution) ? resolution : Effect.succeed(resolution);
|
|
121
204
|
})) };
|
|
122
205
|
}
|
|
123
|
-
const layer
|
|
124
|
-
const toAsync
|
|
206
|
+
const layer = (resolve) => Layer.succeed(Service$2, make(resolve));
|
|
207
|
+
const toAsync = (resolve) => toAsync$3(make(resolve));
|
|
125
208
|
//#endregion
|
|
126
|
-
//#region src/
|
|
127
|
-
var
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
209
|
+
//#region src/testing/provider-conformance.ts
|
|
210
|
+
var provider_conformance_exports = /* @__PURE__ */ __exportAll({
|
|
211
|
+
Error: () => Error,
|
|
212
|
+
fromAsync: () => fromAsync,
|
|
213
|
+
run: () => run
|
|
131
214
|
});
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
return continuation.expiresAt > now ? continuation : null;
|
|
141
|
-
}))
|
|
142
|
-
};
|
|
215
|
+
var Error = class extends Schema.TaggedError()("ProviderConformanceError", {
|
|
216
|
+
case: Schema.String,
|
|
217
|
+
message: Schema.String,
|
|
218
|
+
providerId: Schema.String
|
|
219
|
+
}) {};
|
|
220
|
+
/** Adapt a fresh Promise-shaped provider factory for the Effect-native conformance runner. */
|
|
221
|
+
function fromAsync(makeProvider) {
|
|
222
|
+
return () => Effect.sync(() => fromAsync$1(makeProvider()));
|
|
143
223
|
}
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
224
|
+
const cases = [
|
|
225
|
+
"create-readback-cleanup",
|
|
226
|
+
"exact-noop",
|
|
227
|
+
"conflict",
|
|
228
|
+
"stale-plan",
|
|
229
|
+
"partial-apply-cleanup"
|
|
230
|
+
];
|
|
231
|
+
/** Run the deterministic offline contract every DomainKit DNS adapter must satisfy. */
|
|
232
|
+
const run = Effect.fn("ProviderConformance.run")(function* (input) {
|
|
233
|
+
const makeProvider = () => Effect.suspend(input.makeProvider).pipe(Effect.catchDefect((cause) => Effect.fail(new Error({
|
|
234
|
+
case: "factory",
|
|
235
|
+
message: cause instanceof globalThis.Error ? cause.message : "Provider factory failed",
|
|
236
|
+
providerId: "unknown"
|
|
237
|
+
}))));
|
|
238
|
+
const first = yield* makeProvider();
|
|
239
|
+
const providerId = first.id;
|
|
240
|
+
const prefix = input.prefix ?? "domainkit-conformance";
|
|
241
|
+
yield* createReadbackCleanup(first, input.zone, prefix);
|
|
242
|
+
yield* exactNoop(yield* makeProvider(), input.zone, prefix);
|
|
243
|
+
yield* conflict(yield* makeProvider(), input.zone, prefix);
|
|
244
|
+
yield* stalePlan(yield* makeProvider(), input.zone, prefix);
|
|
245
|
+
yield* partialApplyCleanup(yield* makeProvider(), input.zone, prefix);
|
|
155
246
|
return {
|
|
156
|
-
|
|
157
|
-
|
|
247
|
+
cases,
|
|
248
|
+
providerId,
|
|
249
|
+
zone: input.zone
|
|
158
250
|
};
|
|
251
|
+
});
|
|
252
|
+
function createReadbackCleanup(provider, zone, prefix) {
|
|
253
|
+
const requirements = createRequirements(zone, prefix);
|
|
254
|
+
let cleanupRecordIds = [];
|
|
255
|
+
return Effect.gen(function* () {
|
|
256
|
+
const { plan } = yield* create({
|
|
257
|
+
requirements,
|
|
258
|
+
target: Target.ExactZone({ zone })
|
|
259
|
+
});
|
|
260
|
+
yield* requireTags(provider, "create-readback-cleanup", plan.operations, ["create", "create"]);
|
|
261
|
+
const receipt = yield* apply({
|
|
262
|
+
authorization: yield* authorize(plan),
|
|
263
|
+
plan
|
|
264
|
+
}).pipe(Effect.tapError((failure) => failure instanceof PartialApplyError ? Effect.sync(() => {
|
|
265
|
+
cleanupRecordIds = providerRecordIds(failure.receipt.operations);
|
|
266
|
+
}) : Effect.void));
|
|
267
|
+
cleanupRecordIds = providerRecordIds(receipt.operations);
|
|
268
|
+
yield* requireCondition(provider, "create-readback-cleanup", receipt.status === "complete" && receipt.operations.every(({ providerRecordId }) => providerRecordId !== null), "apply must return a complete receipt with provider record IDs");
|
|
269
|
+
const observed = yield* provider.listRecords(zone);
|
|
270
|
+
yield* requireCondition(provider, "create-readback-cleanup", requirements.every((requirement) => observed.some((record) => record._tag !== "Opaque" && equals(record, requirement))), "listRecords must return every created record, including across provider pages");
|
|
271
|
+
const deletion = yield* create$1({
|
|
272
|
+
plan,
|
|
273
|
+
receipt
|
|
274
|
+
});
|
|
275
|
+
yield* requireCondition(provider, "create-readback-cleanup", (yield* apply$1({
|
|
276
|
+
authorization: yield* authorize$1(deletion),
|
|
277
|
+
plan: deletion
|
|
278
|
+
})).status === "complete" && (yield* recordIdsAbsent(provider, zone, cleanupRecordIds)), "receipt-bound cleanup must remove every created record");
|
|
279
|
+
}).pipe(Effect.ensuring(bestEffortDelete(provider, zone, () => cleanupRecordIds)), Effect.provide(providerLayer(provider)));
|
|
280
|
+
}
|
|
281
|
+
function exactNoop(provider, zone, prefix) {
|
|
282
|
+
const requirement = cname(zone, `${prefix}-exact`);
|
|
283
|
+
let cleanupRecordIds = [];
|
|
284
|
+
return Effect.gen(function* () {
|
|
285
|
+
const created = yield* provider.createRecord(zone, requirement);
|
|
286
|
+
cleanupRecordIds = created.providerRecordId === null ? [] : [created.providerRecordId];
|
|
287
|
+
const { plan } = yield* create({
|
|
288
|
+
requirements: [requirement],
|
|
289
|
+
target: Target.ExactZone({ zone })
|
|
290
|
+
});
|
|
291
|
+
yield* requireTags(provider, "exact-noop", plan.operations, ["noop"]);
|
|
292
|
+
yield* requireCondition(provider, "exact-noop", (yield* apply({
|
|
293
|
+
authorization: yield* authorize(plan),
|
|
294
|
+
plan
|
|
295
|
+
})).operations.length === 0, "an exact plan must not create another record");
|
|
296
|
+
yield* deleteCreated(provider, zone, created.providerRecordId, "exact-noop");
|
|
297
|
+
}).pipe(Effect.ensuring(bestEffortDelete(provider, zone, () => cleanupRecordIds)), Effect.provide(providerLayer(provider)));
|
|
298
|
+
}
|
|
299
|
+
function conflict(provider, zone, prefix) {
|
|
300
|
+
const requirement = cname(zone, `${prefix}-conflict`);
|
|
301
|
+
const occupied = txt(zone, `${prefix}-conflict`, "occupied");
|
|
302
|
+
let cleanupRecordIds = [];
|
|
303
|
+
return Effect.gen(function* () {
|
|
304
|
+
const created = yield* provider.createRecord(zone, occupied);
|
|
305
|
+
cleanupRecordIds = created.providerRecordId === null ? [] : [created.providerRecordId];
|
|
306
|
+
const { plan } = yield* create({
|
|
307
|
+
requirements: [requirement],
|
|
308
|
+
target: Target.ExactZone({ zone })
|
|
309
|
+
});
|
|
310
|
+
yield* requireTags(provider, "conflict", plan.operations, ["conflict"]);
|
|
311
|
+
yield* requireCondition(provider, "conflict", (yield* apply({
|
|
312
|
+
authorization: yield* authorize(plan),
|
|
313
|
+
plan
|
|
314
|
+
}).pipe(Effect.flip)) instanceof ConflictError, "apply must fail closed on incompatible state");
|
|
315
|
+
yield* deleteCreated(provider, zone, created.providerRecordId, "conflict");
|
|
316
|
+
}).pipe(Effect.ensuring(bestEffortDelete(provider, zone, () => cleanupRecordIds)), Effect.provide(providerLayer(provider)));
|
|
317
|
+
}
|
|
318
|
+
function stalePlan(provider, zone, prefix) {
|
|
319
|
+
const requirement = cname(zone, `${prefix}-stale`);
|
|
320
|
+
let cleanupRecordIds = [];
|
|
321
|
+
return Effect.gen(function* () {
|
|
322
|
+
const { plan } = yield* create({
|
|
323
|
+
requirements: [requirement],
|
|
324
|
+
target: Target.ExactZone({ zone })
|
|
325
|
+
});
|
|
326
|
+
const authorization = yield* authorize(plan);
|
|
327
|
+
const created = yield* provider.createRecord(zone, requirement);
|
|
328
|
+
cleanupRecordIds = created.providerRecordId === null ? [] : [created.providerRecordId];
|
|
329
|
+
yield* requireCondition(provider, "stale-plan", (yield* apply({
|
|
330
|
+
authorization,
|
|
331
|
+
plan
|
|
332
|
+
}).pipe(Effect.flip)) instanceof StaleError, "apply must reject provider state that changed after authorization");
|
|
333
|
+
yield* deleteCreated(provider, zone, created.providerRecordId, "stale-plan");
|
|
334
|
+
}).pipe(Effect.ensuring(bestEffortDelete(provider, zone, () => cleanupRecordIds)), Effect.provide(providerLayer(provider)));
|
|
335
|
+
}
|
|
336
|
+
function partialApplyCleanup(provider, zone, prefix) {
|
|
337
|
+
let creates = 0;
|
|
338
|
+
const flaky = Service$1.of({
|
|
339
|
+
...provider,
|
|
340
|
+
createRecord: Effect.fn("ProviderConformance.createRecord")((target, record) => {
|
|
341
|
+
creates += 1;
|
|
342
|
+
return creates === 1 ? provider.createRecord(target, record) : Effect.fail(new Error$2({
|
|
343
|
+
message: "injected conformance failure",
|
|
344
|
+
operation: "createRecord",
|
|
345
|
+
providerId: provider.id
|
|
346
|
+
}));
|
|
347
|
+
})
|
|
348
|
+
});
|
|
349
|
+
const requirements = [cname(zone, `${prefix}-partial-one`), txt(zone, `${prefix}-partial-two`, "domainkit")];
|
|
350
|
+
let cleanupRecordIds = [];
|
|
351
|
+
return Effect.gen(function* () {
|
|
352
|
+
const { plan } = yield* create({
|
|
353
|
+
requirements,
|
|
354
|
+
target: Target.ExactZone({ zone })
|
|
355
|
+
});
|
|
356
|
+
const failure = yield* apply({
|
|
357
|
+
authorization: yield* authorize(plan),
|
|
358
|
+
plan
|
|
359
|
+
}).pipe(Effect.flip);
|
|
360
|
+
cleanupRecordIds = failure instanceof PartialApplyError ? providerRecordIds(failure.receipt.operations) : [];
|
|
361
|
+
yield* requireCondition(provider, "partial-apply-cleanup", failure instanceof PartialApplyError && failure.receipt.operations.length === 1, "a later write failure must preserve the first confirmed write in a partial receipt");
|
|
362
|
+
if (!(failure instanceof PartialApplyError)) return;
|
|
363
|
+
const deletion = yield* create$1({
|
|
364
|
+
plan,
|
|
365
|
+
receipt: failure.receipt
|
|
366
|
+
}).pipe(Effect.provide(providerLayer(provider)));
|
|
367
|
+
yield* apply$1({
|
|
368
|
+
authorization: yield* authorize$1(deletion),
|
|
369
|
+
plan: deletion
|
|
370
|
+
}).pipe(Effect.provide(providerLayer(provider)));
|
|
371
|
+
yield* requireCondition(provider, "partial-apply-cleanup", yield* recordIdsAbsent(provider, zone, cleanupRecordIds), "partial receipt cleanup must remove the confirmed write");
|
|
372
|
+
}).pipe(Effect.ensuring(bestEffortDelete(provider, zone, () => cleanupRecordIds)), Effect.provide(providerLayer(flaky)));
|
|
373
|
+
}
|
|
374
|
+
function providerRecordIds(operations) {
|
|
375
|
+
return operations.flatMap(({ providerRecordId }) => providerRecordId === null ? [] : [providerRecordId]);
|
|
376
|
+
}
|
|
377
|
+
function recordIdsAbsent(provider, zone, recordIds) {
|
|
378
|
+
return Effect.forEach(recordIds, (providerRecordId) => provider.getRecord(zone, providerRecordId)).pipe(Effect.map((records) => records.every((record) => record === null)));
|
|
379
|
+
}
|
|
380
|
+
function bestEffortDelete(provider, zone, recordIds) {
|
|
381
|
+
return Effect.suspend(() => Effect.forEach(recordIds(), (providerRecordId) => provider.deleteRecord(zone, providerRecordId).pipe(Effect.ignore), { discard: true }));
|
|
382
|
+
}
|
|
383
|
+
function providerLayer(provider) {
|
|
384
|
+
return Layer.merge(Layer.succeed(Service$1, provider), webCryptoLayer);
|
|
385
|
+
}
|
|
386
|
+
function createRequirements(zone, prefix) {
|
|
387
|
+
return [cname(zone, `${prefix}-create`), txt(zone, `${prefix}-page`, "domainkit")];
|
|
388
|
+
}
|
|
389
|
+
function cname(zone, label) {
|
|
390
|
+
return parse$1({
|
|
391
|
+
_tag: "CNAME",
|
|
392
|
+
metadata: {
|
|
393
|
+
ownership: "tester",
|
|
394
|
+
provenance: "domainkit",
|
|
395
|
+
purpose: "conformance"
|
|
396
|
+
},
|
|
397
|
+
name: `${label}.${zone}`,
|
|
398
|
+
policy: "exclusive",
|
|
399
|
+
target: "target.example.net",
|
|
400
|
+
ttl: 300
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
function txt(zone, label, value) {
|
|
404
|
+
return parse$1({
|
|
405
|
+
_tag: "TXT",
|
|
406
|
+
metadata: {
|
|
407
|
+
ownership: "tester",
|
|
408
|
+
provenance: "domainkit",
|
|
409
|
+
purpose: "conformance"
|
|
410
|
+
},
|
|
411
|
+
name: `${label}.${zone}`,
|
|
412
|
+
policy: "append",
|
|
413
|
+
ttl: 300,
|
|
414
|
+
value
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
function requireTags(provider, testCase, operations, expected) {
|
|
418
|
+
return requireCondition(provider, testCase, JSON.stringify(operations.map(({ _tag }) => _tag)) === JSON.stringify(expected), `expected operations ${expected.join(", ")}`);
|
|
419
|
+
}
|
|
420
|
+
function requireCondition(provider, testCase, condition, message) {
|
|
421
|
+
return condition ? Effect.void : Effect.fail(new Error({
|
|
422
|
+
case: testCase,
|
|
423
|
+
message,
|
|
424
|
+
providerId: provider.id
|
|
425
|
+
}));
|
|
426
|
+
}
|
|
427
|
+
function deleteCreated(provider, zone, providerRecordId, testCase) {
|
|
428
|
+
return providerRecordId === null ? Effect.fail(new Error({
|
|
429
|
+
case: testCase,
|
|
430
|
+
message: "provider must return record IDs for safe cleanup",
|
|
431
|
+
providerId: provider.id
|
|
432
|
+
})) : provider.deleteRecord(zone, providerRecordId);
|
|
159
433
|
}
|
|
160
|
-
const layer = () => Layer.succeed(Service$6, make());
|
|
161
|
-
const toAsync = (store = make()) => toAsync$13(store);
|
|
162
434
|
//#endregion
|
|
163
|
-
export {
|
|
435
|
+
export { in_memory_lifecycle_repository_exports as InMemoryAuthorizationLifecycle, in_memory_continuation_exports as InMemoryConnectionContinuations, in_memory_exports as InMemoryDnsProvider, in_memory_exports$1 as InMemoryDnsResolver, provider_conformance_exports as ProviderConformance };
|
|
164
436
|
|
|
165
437
|
//# sourceMappingURL=testing.mjs.map
|