domainkit 0.3.1 → 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/README.md +48 -115
- 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-BoWy70ew.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-BoWy70ew.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -1,139 +1,72 @@
|
|
|
1
1
|
# DomainKit
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
authorized provisioning plans. Products can connect customer domains without making one
|
|
5
|
-
registrar's API or authorization model their architecture.
|
|
3
|
+
Domain setup infrastructure for TypeScript SaaS applications.
|
|
6
4
|
|
|
7
|
-
DomainKit
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
DomainKit turns DNS requirements into reviewable plans, requires explicit digest-bound
|
|
6
|
+
authorization before writes, records applied changes in receipts, and uses those receipts to plan
|
|
7
|
+
safe cleanup. It supports Cloudflare and Vercel without making either provider's API your product
|
|
8
|
+
architecture.
|
|
10
9
|
|
|
11
|
-
##
|
|
10
|
+
## Install
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
```sh
|
|
13
|
+
npm install domainkit effect@rc
|
|
14
|
+
```
|
|
16
15
|
|
|
17
|
-
|
|
18
|
-
Promise namespaces delegate to the Effect implementation rather than maintaining another planner,
|
|
19
|
-
authorization engine, or verifier. There are no provider-specific subpaths: `Cloudflare` and
|
|
20
|
-
`Vercel` are cohesive namespaces on each root.
|
|
16
|
+
Node.js 24.10 or newer and Effect 4 are required.
|
|
21
17
|
|
|
22
18
|
## Plan, approve, and apply
|
|
23
19
|
|
|
24
20
|
```ts
|
|
21
|
+
import { Digest, DnsRecord, DomainName, Provisioning } from "domainkit";
|
|
22
|
+
import { InMemoryDnsProvider } from "domainkit/testing";
|
|
25
23
|
import { Effect, Layer } from "effect";
|
|
26
|
-
import { Digest, DnsProvider, Provisioning } from "domainkit";
|
|
27
|
-
|
|
28
|
-
const program = Provisioning.create({
|
|
29
|
-
requirements,
|
|
30
|
-
target: Provisioning.Target.ExactZone({ zone: "example.com" }),
|
|
31
|
-
}).pipe(
|
|
32
|
-
Effect.flatMap(({ plan }) =>
|
|
33
|
-
Provisioning.authorize(plan).pipe(
|
|
34
|
-
Effect.flatMap((authorization) => Provisioning.apply({ authorization, plan })),
|
|
35
|
-
),
|
|
36
|
-
),
|
|
37
|
-
Effect.provide(Layer.merge(Layer.succeed(DnsProvider.Service, provider), Digest.webCryptoLayer)),
|
|
38
|
-
);
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
Promise consumers use the explicit secondary entry point at a JavaScript boundary:
|
|
42
24
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
25
|
+
const requirement = DnsRecord.parse({
|
|
26
|
+
_tag: "CNAME",
|
|
27
|
+
metadata: {
|
|
28
|
+
ownership: "customer",
|
|
29
|
+
provenance: "product-onboarding",
|
|
30
|
+
purpose: "tracking",
|
|
31
|
+
},
|
|
32
|
+
name: "track.example.com",
|
|
33
|
+
policy: "exclusive",
|
|
34
|
+
target: "tracking.example.net",
|
|
35
|
+
ttl: 300,
|
|
50
36
|
});
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
Use `DiscoverFromDomain` when the host has several authorized provider accounts. `ZoneDiscovery`
|
|
54
|
-
checks the requested name and its parents, returns `Resolved`, `SelectionRequired`, or `NotFound`,
|
|
55
|
-
and never guesses between ambiguous accounts.
|
|
56
|
-
|
|
57
|
-
## Connect provider accounts
|
|
58
|
-
|
|
59
|
-
`Connection.start` handles token and interactive provider methods. It returns either `Connected`
|
|
60
|
-
or `Redirect`; `Connection.complete` consumes an interactive continuation exactly once. Cloudflare
|
|
61
|
-
implements OAuth and token methods. Vercel implements its installation-code flow without
|
|
62
|
-
mislabeling it as generic OAuth.
|
|
63
|
-
|
|
64
|
-
After a host proves that an existing connected account owns another domain and obtains the owner's
|
|
65
|
-
consent, `Connection.extend` adds that domain to the existing owner grant without repeating provider
|
|
66
|
-
authentication. It preserves earlier domain grants and rejects cross-owner, expired, or revoking
|
|
67
|
-
connections.
|
|
68
|
-
|
|
69
|
-
Cloudflare can discover the selected account from a domain already visible to the credential, so a
|
|
70
|
-
customer does not need to find or type an account ID. Vercel retains explicit personal or team
|
|
71
|
-
context returned by the installation. Both contexts are versioned, non-secret values that can
|
|
72
|
-
reconstruct the correct provider client later.
|
|
73
|
-
|
|
74
|
-
Hosts provide one `AuthorizationLifecycle` repository for an authorization aggregate, its
|
|
75
|
-
credential, and its owner bindings. SQL hosts can implement one transaction; split database and
|
|
76
|
-
vault hosts can implement a recoverable saga behind the same interface. Interactive continuations
|
|
77
|
-
remain separate, short-lived, one-time state. DomainKit does not choose a database, cache, vault,
|
|
78
|
-
callback route, session model, consent UI, or audit system.
|
|
79
37
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
import { DnsProvider, Verification } from "domainkit";
|
|
91
|
-
|
|
92
|
-
const result = await Effect.runPromise(Verification.observe({ record }));
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
The default resolver pool queries Cloudflare and Google concurrently using RFC wire-format DNS over
|
|
96
|
-
HTTPS and applies `AnyMatch`. Results preserve every named answer, negative response, timeout, and
|
|
97
|
-
failure. Hosts can supply another resolver pool or choose tagged `AllMatch` and `Quorum` policies.
|
|
98
|
-
|
|
99
|
-
Authoritative-provider observation is opt-in:
|
|
100
|
-
|
|
101
|
-
```ts
|
|
102
|
-
const result = await Verification.observe({
|
|
103
|
-
provider: Verification.Provider.Enabled({ zone }),
|
|
104
|
-
publicDns: Verification.PublicDns.Disabled(),
|
|
105
|
-
record,
|
|
106
|
-
}).pipe(Effect.provideService(DnsProvider.Service, provider), Effect.runPromise);
|
|
38
|
+
const program = Effect.gen(function* () {
|
|
39
|
+
const { plan } = yield* Provisioning.create({
|
|
40
|
+
requirements: [requirement],
|
|
41
|
+
target: Provisioning.Target.ExactZone({
|
|
42
|
+
zone: DomainName.parse("example.com"),
|
|
43
|
+
}),
|
|
44
|
+
});
|
|
45
|
+
const authorization = yield* Provisioning.authorize(plan);
|
|
46
|
+
return yield* Provisioning.apply({ authorization, plan });
|
|
47
|
+
}).pipe(Effect.provide(Layer.merge(InMemoryDnsProvider.layer(), Digest.webCryptoLayer)));
|
|
107
48
|
```
|
|
108
49
|
|
|
109
|
-
|
|
110
|
-
|
|
50
|
+
Planning is additive and fail-closed: exact records become no-ops, missing records become creates,
|
|
51
|
+
and incompatible state becomes a conflict. DomainKit never silently overwrites DNS.
|
|
111
52
|
|
|
112
|
-
##
|
|
53
|
+
## Public entry points
|
|
113
54
|
|
|
114
|
-
|
|
115
|
-
|
|
55
|
+
- `domainkit` — canonical Effect services, schemas, plans, providers, and verification;
|
|
56
|
+
- `domainkit/promise` — Promise adapters for foreign-runtime boundaries;
|
|
57
|
+
- `domainkit/testing` — in-memory services and provider conformance helpers.
|
|
116
58
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
import { ProviderConformance } from "domainkit/testing";
|
|
121
|
-
|
|
122
|
-
const report = await Effect.runPromise(
|
|
123
|
-
ProviderConformance.run({
|
|
124
|
-
makeProvider: ProviderConformance.fromAsync(() => makeFreshPromiseProvider()),
|
|
125
|
-
zone: DomainName.parse("example.com"),
|
|
126
|
-
}),
|
|
127
|
-
);
|
|
128
|
-
```
|
|
59
|
+
Provider credentials, OAuth state, durable connections, plans, receipts, authorization, and audit
|
|
60
|
+
history remain owned by your application. DomainKit supplies the domain model and operations, not a
|
|
61
|
+
hosted control plane.
|
|
129
62
|
|
|
130
|
-
|
|
131
|
-
plans, partial receipts, and receipt-bound cleanup. First-party providers run the same contract and
|
|
132
|
-
credential-gated live profiles against disposable records.
|
|
63
|
+
## Learn more
|
|
133
64
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
[
|
|
65
|
+
- [Executable quickstart](https://github.com/AryaLabsHQ/domainkit/blob/main/packages/domainkit/examples/effect/quickstart.ts)
|
|
66
|
+
- [Architecture decisions](https://github.com/AryaLabsHQ/domainkit/tree/main/docs/adr)
|
|
67
|
+
- [Provider behavior](https://github.com/AryaLabsHQ/domainkit/blob/main/docs/providers.md)
|
|
68
|
+
- [Examples](https://github.com/AryaLabsHQ/domainkit/tree/main/packages/domainkit/examples)
|
|
69
|
+
- [Issues](https://github.com/AryaLabsHQ/domainkit/issues)
|
|
137
70
|
|
|
138
71
|
## License
|
|
139
72
|
|