domainkit 0.7.0 → 0.9.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.
Files changed (165) hide show
  1. package/README.md +214 -39
  2. package/dist/Approval.mjs +34 -0
  3. package/dist/Approval.mjs.map +1 -0
  4. package/dist/Cleanup.mjs +97 -0
  5. package/dist/Cleanup.mjs.map +1 -0
  6. package/dist/Cloudflare.mjs +225 -0
  7. package/dist/Cloudflare.mjs.map +1 -0
  8. package/dist/Connect.mjs +463 -0
  9. package/dist/Connect.mjs.map +1 -0
  10. package/dist/Custody.mjs +64 -0
  11. package/dist/Custody.mjs.map +1 -0
  12. package/dist/DnsRecord.mjs +213 -0
  13. package/dist/DnsRecord.mjs.map +1 -0
  14. package/dist/DomainKit.mjs +34 -0
  15. package/dist/DomainKit.mjs.map +1 -0
  16. package/dist/DomainName.mjs +100 -0
  17. package/dist/DomainName.mjs.map +1 -0
  18. package/dist/Plan.mjs +102 -0
  19. package/dist/Plan.mjs.map +1 -0
  20. package/dist/Principal.mjs +23 -0
  21. package/dist/Principal.mjs.map +1 -0
  22. package/dist/Provider.mjs +130 -0
  23. package/dist/Provider.mjs.map +1 -0
  24. package/dist/Providers.mjs +40 -0
  25. package/dist/Providers.mjs.map +1 -0
  26. package/dist/Provision.mjs +93 -0
  27. package/dist/Provision.mjs.map +1 -0
  28. package/dist/Reason.mjs +162 -0
  29. package/dist/Reason.mjs.map +1 -0
  30. package/dist/Receipt.mjs +71 -0
  31. package/dist/Receipt.mjs.map +1 -0
  32. package/dist/Resolver.mjs +75 -0
  33. package/dist/Resolver.mjs.map +1 -0
  34. package/dist/Server.mjs +516 -0
  35. package/dist/Server.mjs.map +1 -0
  36. package/dist/Storage.mjs +236 -0
  37. package/dist/Storage.mjs.map +1 -0
  38. package/dist/Testing.mjs +226 -0
  39. package/dist/Testing.mjs.map +1 -0
  40. package/dist/Transport.mjs +187 -0
  41. package/dist/Transport.mjs.map +1 -0
  42. package/dist/Vercel.mjs +116 -0
  43. package/dist/Vercel.mjs.map +1 -0
  44. package/dist/Verify.mjs +292 -0
  45. package/dist/Verify.mjs.map +1 -0
  46. package/dist/{rolldown-runtime-D7D4PA-g.mjs → _virtual/_rolldown/runtime.mjs} +1 -1
  47. package/dist/entry/client.mjs +2 -0
  48. package/dist/entry/server.mjs +2 -0
  49. package/dist/entry/testing.mjs +3 -0
  50. package/dist/index.mjs +33 -356
  51. package/dist/index.mjs.map +1 -1
  52. package/dist/internal/aes.mjs +61 -0
  53. package/dist/internal/aes.mjs.map +1 -0
  54. package/dist/internal/attempts.mjs +284 -0
  55. package/dist/internal/attempts.mjs.map +1 -0
  56. package/dist/internal/cloudflare/client.mjs +100 -0
  57. package/dist/internal/cloudflare/client.mjs.map +1 -0
  58. package/dist/internal/cloudflare/protocol.mjs +59 -0
  59. package/dist/internal/cloudflare/protocol.mjs.map +1 -0
  60. package/dist/internal/cloudflare/records.mjs +156 -0
  61. package/dist/internal/cloudflare/records.mjs.map +1 -0
  62. package/dist/internal/config.mjs +13 -0
  63. package/dist/internal/config.mjs.map +1 -0
  64. package/dist/internal/conformance/provider.mjs +207 -0
  65. package/dist/internal/conformance/provider.mjs.map +1 -0
  66. package/dist/internal/conformance/storage.mjs +428 -0
  67. package/dist/internal/conformance/storage.mjs.map +1 -0
  68. package/dist/internal/digest.mjs +25 -0
  69. package/dist/internal/digest.mjs.map +1 -0
  70. package/dist/internal/doh.mjs +152 -0
  71. package/dist/internal/doh.mjs.map +1 -0
  72. package/dist/internal/error.mjs +69 -0
  73. package/dist/internal/error.mjs.map +1 -0
  74. package/dist/internal/http.mjs +89 -0
  75. package/dist/internal/http.mjs.map +1 -0
  76. package/dist/internal/ids.mjs +8 -0
  77. package/dist/internal/ids.mjs.map +1 -0
  78. package/dist/internal/oauth.mjs +113 -0
  79. package/dist/internal/oauth.mjs.map +1 -0
  80. package/dist/internal/planner.mjs +88 -0
  81. package/dist/internal/planner.mjs.map +1 -0
  82. package/dist/internal/provider-async.mjs +83 -0
  83. package/dist/internal/provider-async.mjs.map +1 -0
  84. package/dist/internal/storage-memory.mjs +302 -0
  85. package/dist/internal/storage-memory.mjs.map +1 -0
  86. package/dist/internal/vercel/client.mjs +122 -0
  87. package/dist/internal/vercel/client.mjs.map +1 -0
  88. package/dist/internal/vercel/protocol.mjs +80 -0
  89. package/dist/internal/vercel/protocol.mjs.map +1 -0
  90. package/dist/internal/vercel/records.mjs +151 -0
  91. package/dist/internal/vercel/records.mjs.map +1 -0
  92. package/dist/package.mjs +6 -0
  93. package/dist/package.mjs.map +1 -0
  94. package/dist/types/Approval.d.ts +26 -0
  95. package/dist/types/Cleanup.d.ts +41 -0
  96. package/dist/types/Cloudflare.d.ts +36 -0
  97. package/dist/types/Connect.d.ts +506 -0
  98. package/dist/types/Custody.d.ts +38 -0
  99. package/dist/types/DnsRecord.d.ts +158 -0
  100. package/dist/types/DomainKit.d.ts +31 -0
  101. package/dist/types/DomainName.d.ts +27 -0
  102. package/dist/types/Plan.d.ts +76 -0
  103. package/dist/types/Principal.d.ts +18 -0
  104. package/dist/types/Provider.d.ts +248 -0
  105. package/dist/types/Providers.d.ts +16 -0
  106. package/dist/types/Provision.d.ts +75 -0
  107. package/dist/types/Reason.d.ts +118 -0
  108. package/dist/types/Receipt.d.ts +55 -0
  109. package/dist/types/Resolver.d.ts +135 -0
  110. package/dist/types/Server.d.ts +6523 -0
  111. package/dist/types/Storage.d.ts +314 -0
  112. package/dist/types/Testing.d.ts +84 -0
  113. package/dist/types/Transport.d.ts +124 -0
  114. package/dist/types/Vercel.d.ts +25 -0
  115. package/dist/types/Verify.d.ts +109 -0
  116. package/dist/types/entry/client.d.ts +2 -0
  117. package/dist/types/entry/server.d.ts +2 -0
  118. package/dist/types/entry/testing.d.ts +3 -0
  119. package/dist/types/index.d.ts +21 -0
  120. package/dist/types/internal/aes.d.ts +8 -0
  121. package/dist/types/internal/attempts.d.ts +40 -0
  122. package/dist/types/internal/cloudflare/client.d.ts +23 -0
  123. package/dist/types/internal/cloudflare/protocol.d.ts +71 -0
  124. package/dist/types/internal/cloudflare/records.d.ts +8 -0
  125. package/dist/types/internal/config.d.ts +4 -0
  126. package/dist/types/internal/conformance/provider.d.ts +12 -0
  127. package/dist/types/internal/conformance/storage.d.ts +14 -0
  128. package/dist/types/internal/digest.d.ts +10 -0
  129. package/dist/types/internal/doh.d.ts +19 -0
  130. package/dist/types/internal/error.d.ts +24 -0
  131. package/dist/types/internal/http.d.ts +27 -0
  132. package/dist/types/internal/ids.d.ts +3 -0
  133. package/dist/types/internal/oauth.d.ts +58 -0
  134. package/dist/types/internal/planner.d.ts +22 -0
  135. package/dist/types/internal/provider-async.d.ts +78 -0
  136. package/dist/types/internal/storage-memory.d.ts +2 -0
  137. package/dist/types/internal/vercel/client.d.ts +29 -0
  138. package/dist/types/internal/vercel/protocol.d.ts +124 -0
  139. package/dist/types/internal/vercel/records.d.ts +7 -0
  140. package/package.json +88 -12
  141. package/dist/canonical-json-CQYKsZw7.mjs +0 -310
  142. package/dist/canonical-json-CQYKsZw7.mjs.map +0 -1
  143. package/dist/connect-10vHuShV.d.mts +0 -1401
  144. package/dist/connect-oI8hcMxc.mjs +0 -185
  145. package/dist/connect-oI8hcMxc.mjs.map +0 -1
  146. package/dist/connection-C0xsU41C.d.mts +0 -186
  147. package/dist/connection-api-DW7xUJdj.d.mts +0 -26
  148. package/dist/index.d.mts +0 -882
  149. package/dist/managed-dns-connections-C3A_GBaz.mjs +0 -99
  150. package/dist/managed-dns-connections-C3A_GBaz.mjs.map +0 -1
  151. package/dist/promise.d.mts +0 -378
  152. package/dist/promise.mjs +0 -598
  153. package/dist/promise.mjs.map +0 -1
  154. package/dist/resolver-DPJs8h0a.mjs +0 -859
  155. package/dist/resolver-DPJs8h0a.mjs.map +0 -1
  156. package/dist/resolver-DRrLm_hB.d.mts +0 -3734
  157. package/dist/server.d.mts +0 -128
  158. package/dist/server.mjs +0 -319
  159. package/dist/server.mjs.map +0 -1
  160. package/dist/testing.d.mts +0 -62
  161. package/dist/testing.mjs +0 -507
  162. package/dist/testing.mjs.map +0 -1
  163. package/dist/verify-C3V_GtzL.d.mts +0 -1268
  164. package/dist/verify-DDnK8U7C.mjs +0 -2396
  165. package/dist/verify-DDnK8U7C.mjs.map +0 -1
@@ -0,0 +1,64 @@
1
+ import { __exportAll } from "./_virtual/_rolldown/runtime.mjs";
2
+ import { CryptoFailed, InvalidInput } from "./Reason.mjs";
3
+ import { DomainKitError, fail, isDomainKitError } from "./internal/error.mjs";
4
+ import { decodeKey, generateKey as generateKey$1, importKey, open, seal } from "./internal/aes.mjs";
5
+ import { Config, Context, Effect, Layer, Redacted } from "effect";
6
+ //#region src/Custody.ts
7
+ /**
8
+ * Seals provider credentials before Storage writes them. The default is AES-256-GCM through
9
+ * WebCrypto from one configured key; hosts with a KMS provide their own. There is no plaintext
10
+ * mode.
11
+ *
12
+ * Envelope format: `v1.<iv>.<ciphertext>` with base64url segments and a 12-byte IV. Rotating
13
+ * the key requires re-sealing stored credentials; `open` fails with `CryptoFailed` on a mismatch.
14
+ */
15
+ var Custody_exports = /* @__PURE__ */ __exportAll({
16
+ Service: () => Service,
17
+ fromAsync: () => fromAsync,
18
+ generateKey: () => generateKey,
19
+ layer: () => layer,
20
+ layerConfig: () => layerConfig,
21
+ layerFromAsync: () => layerFromAsync,
22
+ make: () => make
23
+ });
24
+ var Service = class extends Context.Service()("@domainkit/Custody") {};
25
+ const make = (options) => Effect.gen(function* () {
26
+ const raw = decodeKey(Redacted.value(options.key));
27
+ if (raw === null) return yield* fail(new InvalidInput({
28
+ message: "Custody key must be 32 bytes encoded as base64 or hex",
29
+ field: "key"
30
+ }));
31
+ const key = yield* importKey(raw);
32
+ return {
33
+ seal: (plaintext) => seal(key, plaintext),
34
+ open: (ciphertext) => open(key, ciphertext)
35
+ };
36
+ });
37
+ const layer = (options) => Layer.effect(Service)(make(options));
38
+ /** Same options, each field a `Config`. Defaults to `DOMAINKIT_CUSTODY_KEY`. */
39
+ const layerConfig = (options = {}) => Layer.effect(Service)(Effect.gen(function* () {
40
+ const key = yield* (options.key ?? Config.redacted("DOMAINKIT_CUSTODY_KEY")).pipe(Effect.mapError((cause) => new DomainKitError({ reason: new InvalidInput({
41
+ message: cause.message,
42
+ field: "key"
43
+ }) })));
44
+ return yield* make({ key });
45
+ }));
46
+ /** A fresh random key in the accepted encoding; for tests, playgrounds, and first-time setup. */
47
+ const generateKey = generateKey$1;
48
+ const cryptoFailed = (operation) => (cause) => isDomainKitError(cause) ? cause : new DomainKitError({ reason: new CryptoFailed({ operation }) });
49
+ /** Wrap a Promise-shaped KMS client; rejections become `CryptoFailed`. */
50
+ const fromAsync = (service) => ({
51
+ seal: (plaintext) => Effect.tryPromise({
52
+ try: () => service.seal(Redacted.value(plaintext)),
53
+ catch: cryptoFailed("seal")
54
+ }),
55
+ open: (ciphertext) => Effect.tryPromise({
56
+ try: () => service.open(ciphertext),
57
+ catch: cryptoFailed("open")
58
+ }).pipe(Effect.map((value) => Redacted.make(value)))
59
+ });
60
+ const layerFromAsync = (service) => Layer.succeed(Service)(fromAsync(service));
61
+ //#endregion
62
+ export { Custody_exports, Service, fromAsync, generateKey, layer, layerConfig, layerFromAsync, make };
63
+
64
+ //# sourceMappingURL=Custody.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Custody.mjs","names":["Aes.decodeKey","Errors.fail","Reason.InvalidInput","Aes.importKey","Aes.seal","Aes.open","Errors.DomainKitError","Aes.generateKey","Errors.isDomainKitError","Reason.CryptoFailed"],"sources":["../src/Custody.ts"],"sourcesContent":["/**\n * Seals provider credentials before Storage writes them. The default is AES-256-GCM through\n * WebCrypto from one configured key; hosts with a KMS provide their own. There is no plaintext\n * mode.\n *\n * Envelope format: `v1.<iv>.<ciphertext>` with base64url segments and a 12-byte IV. Rotating\n * the key requires re-sealing stored credentials; `open` fails with `CryptoFailed` on a mismatch.\n */\nimport { Config, Context, Effect, Layer, Redacted } from \"effect\";\n\nimport * as Errors from \"./internal/error.ts\";\nimport * as Reason from \"./Reason.ts\";\nimport * as Aes from \"./internal/aes.ts\";\n\nexport interface Interface {\n readonly seal: (\n plaintext: Redacted.Redacted<string>,\n ) => Effect.Effect<string, Errors.DomainKitError>;\n readonly open: (\n ciphertext: string,\n ) => Effect.Effect<Redacted.Redacted<string>, Errors.DomainKitError>;\n}\n\nexport class Service extends Context.Service<Service, Interface>()(\"@domainkit/Custody\") {}\n\nexport interface Options {\n /** 32 bytes, base64 or hex. Rotating it requires re-sealing stored credentials. */\n readonly key: Redacted.Redacted<string>;\n}\n\nexport const make = (options: Options): Effect.Effect<Interface, Errors.DomainKitError> =>\n Effect.gen(function* () {\n const raw = Aes.decodeKey(Redacted.value(options.key));\n if (raw === null) {\n return yield* Errors.fail(\n new Reason.InvalidInput({\n message: \"Custody key must be 32 bytes encoded as base64 or hex\",\n field: \"key\",\n }),\n );\n }\n const key = yield* Aes.importKey(raw);\n return {\n seal: (plaintext) => Aes.seal(key, plaintext),\n open: (ciphertext) => Aes.open(key, ciphertext),\n };\n });\n\nexport const layer = (options: Options): Layer.Layer<Service, Errors.DomainKitError> =>\n Layer.effect(Service)(make(options));\n\n/** Same options, each field a `Config`. Defaults to `DOMAINKIT_CUSTODY_KEY`. */\nexport const layerConfig = (\n options: { readonly key?: Config.Config<Redacted.Redacted<string>> } = {},\n): Layer.Layer<Service, Errors.DomainKitError> =>\n Layer.effect(Service)(\n Effect.gen(function* () {\n const key = yield* (options.key ?? Config.redacted(\"DOMAINKIT_CUSTODY_KEY\")).pipe(\n Effect.mapError(\n (cause) =>\n new Errors.DomainKitError({\n reason: new Reason.InvalidInput({ message: cause.message, field: \"key\" }),\n }),\n ),\n );\n return yield* make({ key });\n }),\n );\n\n/** A fresh random key in the accepted encoding; for tests, playgrounds, and first-time setup. */\nexport const generateKey = Aes.generateKey;\n\nexport interface AsyncInterface {\n readonly seal: (plaintext: string) => Promise<string>;\n readonly open: (ciphertext: string) => Promise<string>;\n}\n\nconst cryptoFailed = (operation: \"seal\" | \"open\") => (cause: unknown) =>\n Errors.isDomainKitError(cause)\n ? cause\n : new Errors.DomainKitError({ reason: new Reason.CryptoFailed({ operation }) });\n\n/** Wrap a Promise-shaped KMS client; rejections become `CryptoFailed`. */\nexport const fromAsync = (service: AsyncInterface): Interface => ({\n seal: (plaintext) =>\n Effect.tryPromise({\n try: () => service.seal(Redacted.value(plaintext)),\n catch: cryptoFailed(\"seal\"),\n }),\n open: (ciphertext) =>\n Effect.tryPromise({ try: () => service.open(ciphertext), catch: cryptoFailed(\"open\") }).pipe(\n Effect.map((value) => Redacted.make(value)),\n ),\n});\n\nexport const layerFromAsync = (service: AsyncInterface): Layer.Layer<Service> =>\n Layer.succeed(Service)(fromAsync(service));\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAuBA,IAAa,UAAb,cAA6B,QAAQ,QAA4B,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC;AAO1F,MAAa,QAAQ,YACnB,OAAO,IAAI,aAAa;CACtB,MAAM,MAAMA,UAAc,SAAS,MAAM,QAAQ,GAAG,CAAC;CACrD,IAAI,QAAQ,MACV,OAAO,OAAOC,KACZ,IAAIC,aAAoB;EACtB,SAAS;EACT,OAAO;CACT,CAAC,CACH;CAEF,MAAM,MAAM,OAAOC,UAAc,GAAG;CACpC,OAAO;EACL,OAAO,cAAcC,KAAS,KAAK,SAAS;EAC5C,OAAO,eAAeC,KAAS,KAAK,UAAU;CAChD;AACF,CAAC;AAEH,MAAa,SAAS,YACpB,MAAM,OAAO,OAAO,CAAC,CAAC,KAAK,OAAO,CAAC;;AAGrC,MAAa,eACX,UAAuE,CAAC,MAExE,MAAM,OAAO,OAAO,CAAC,CACnB,OAAO,IAAI,aAAa;CACtB,MAAM,MAAM,QAAQ,QAAQ,OAAO,OAAO,SAAS,uBAAuB,EAAA,CAAG,KAC3E,OAAO,UACJ,UACC,IAAIC,eAAsB,EACxB,QAAQ,IAAIJ,aAAoB;EAAE,SAAS,MAAM;EAAS,OAAO;CAAM,CAAC,EAC1E,CAAC,CACL,CACF;CACA,OAAO,OAAO,KAAK,EAAE,IAAI,CAAC;AAC5B,CAAC,CACH;;AAGF,MAAa,cAAcK;AAO3B,MAAM,gBAAgB,eAAgC,UACpDC,iBAAwB,KAAK,IACzB,QACA,IAAIF,eAAsB,EAAE,QAAQ,IAAIG,aAAoB,EAAE,UAAU,CAAC,EAAE,CAAC;;AAGlF,MAAa,aAAa,aAAwC;CAChE,OAAO,cACL,OAAO,WAAW;EAChB,WAAW,QAAQ,KAAK,SAAS,MAAM,SAAS,CAAC;EACjD,OAAO,aAAa,MAAM;CAC5B,CAAC;CACH,OAAO,eACL,OAAO,WAAW;EAAE,WAAW,QAAQ,KAAK,UAAU;EAAG,OAAO,aAAa,MAAM;CAAE,CAAC,CAAC,CAAC,KACtF,OAAO,KAAK,UAAU,SAAS,KAAK,KAAK,CAAC,CAC5C;AACJ;AAEA,MAAa,kBAAkB,YAC7B,MAAM,QAAQ,OAAO,CAAC,CAAC,UAAU,OAAO,CAAC"}
@@ -0,0 +1,213 @@
1
+ import { __exportAll } from "./_virtual/_rolldown/runtime.mjs";
2
+ import { Model as Model$1, fromStringUnsafe } from "./DomainName.mjs";
3
+ import { Schema } from "effect";
4
+ //#region src/DnsRecord.ts
5
+ /**
6
+ * DNS record requirements a product asks a customer to satisfy, and the observed records a
7
+ * provider or resolver reports back.
8
+ *
9
+ * Constructors are lowercase functions (`DnsRecord.cname(...)`), Effect style. `policy` defaults
10
+ * to `exclusive` for CNAME and `append` for everything else; `ttl` defaults to `null` (provider
11
+ * default); `purpose` is an optional human label the UI shows.
12
+ */
13
+ var DnsRecord_exports = /* @__PURE__ */ __exportAll({
14
+ A: () => A,
15
+ AAAA: () => AAAA,
16
+ CAA: () => CAA,
17
+ CNAME: () => CNAME,
18
+ MX: () => MX,
19
+ Model: () => Model,
20
+ NS: () => NS,
21
+ Observed: () => Observed,
22
+ Opaque: () => Opaque,
23
+ Policy: () => Policy,
24
+ SRV: () => SRV,
25
+ TXT: () => TXT,
26
+ Type: () => Type,
27
+ a: () => a,
28
+ aaaa: () => aaaa,
29
+ caa: () => caa,
30
+ cname: () => cname,
31
+ data: () => data,
32
+ equals: () => equals,
33
+ isDnsRecord: () => isDnsRecord,
34
+ mx: () => mx,
35
+ ns: () => ns,
36
+ sameSet: () => sameSet,
37
+ srv: () => srv,
38
+ txt: () => txt
39
+ });
40
+ const Name = Schema.suspend(() => Model$1);
41
+ const Type = Schema.Literals([
42
+ "A",
43
+ "AAAA",
44
+ "CNAME",
45
+ "TXT",
46
+ "MX",
47
+ "CAA",
48
+ "NS",
49
+ "SRV"
50
+ ]);
51
+ const Policy = Schema.Literals(["exclusive", "append"]);
52
+ const Ttl = Schema.NullOr(Schema.Int.check(Schema.isBetween({
53
+ minimum: 1,
54
+ maximum: 2147483647
55
+ })));
56
+ const Port = Schema.Int.check(Schema.isBetween({
57
+ minimum: 0,
58
+ maximum: 65535
59
+ }));
60
+ const Priority = Schema.Int.check(Schema.isBetween({
61
+ minimum: 0,
62
+ maximum: 65535
63
+ }));
64
+ const Ipv4 = Schema.String.check(Schema.makeFilter((value) => isIpv4(value) ? void 0 : "Expected an IPv4 address"));
65
+ const Ipv6 = Schema.String.check(Schema.makeFilter((value) => isIpv6(value) ? void 0 : "Expected an IPv6 address"));
66
+ const NonEmpty = Schema.String.check(Schema.isMinLength(1));
67
+ const Common = {
68
+ name: Name,
69
+ ttl: Ttl,
70
+ policy: Policy,
71
+ purpose: Schema.optionalKey(Schema.String)
72
+ };
73
+ var A = class extends Schema.TaggedClass("@domainkit/DnsRecord/A")("A", {
74
+ ...Common,
75
+ address: Ipv4
76
+ }) {};
77
+ var AAAA = class extends Schema.TaggedClass("@domainkit/DnsRecord/AAAA")("AAAA", {
78
+ ...Common,
79
+ address: Ipv6
80
+ }) {};
81
+ var CNAME = class extends Schema.TaggedClass("@domainkit/DnsRecord/CNAME")("CNAME", {
82
+ ...Common,
83
+ target: Name
84
+ }) {};
85
+ var TXT = class extends Schema.TaggedClass("@domainkit/DnsRecord/TXT")("TXT", {
86
+ ...Common,
87
+ value: NonEmpty
88
+ }) {};
89
+ var MX = class extends Schema.TaggedClass("@domainkit/DnsRecord/MX")("MX", {
90
+ ...Common,
91
+ exchange: Name,
92
+ priority: Priority
93
+ }) {};
94
+ var CAA = class extends Schema.TaggedClass("@domainkit/DnsRecord/CAA")("CAA", {
95
+ ...Common,
96
+ flags: Schema.Int.check(Schema.isBetween({
97
+ minimum: 0,
98
+ maximum: 255
99
+ })),
100
+ tag: NonEmpty,
101
+ value: NonEmpty
102
+ }) {};
103
+ var NS = class extends Schema.TaggedClass("@domainkit/DnsRecord/NS")("NS", {
104
+ ...Common,
105
+ nameserver: Name
106
+ }) {};
107
+ var SRV = class extends Schema.TaggedClass("@domainkit/DnsRecord/SRV")("SRV", {
108
+ ...Common,
109
+ target: Name,
110
+ port: Port,
111
+ priority: Priority,
112
+ weight: Priority
113
+ }) {};
114
+ const Model = Schema.Union([
115
+ A,
116
+ AAAA,
117
+ CNAME,
118
+ TXT,
119
+ MX,
120
+ CAA,
121
+ NS,
122
+ SRV
123
+ ]);
124
+ /** A provider record DomainKit cannot model but must not overwrite. */
125
+ var Opaque = class extends Schema.TaggedClass("@domainkit/DnsRecord/Opaque")("Opaque", {
126
+ name: Schema.String,
127
+ type: Schema.String,
128
+ raw: Schema.Unknown
129
+ }) {};
130
+ const Observed = Schema.Union([Model, Opaque]);
131
+ const common = (name, options, defaultPolicy) => ({
132
+ name: fromStringUnsafe(name),
133
+ ttl: options.ttl ?? null,
134
+ policy: options.policy ?? defaultPolicy,
135
+ ...options.purpose === void 0 ? {} : { purpose: options.purpose }
136
+ });
137
+ const a = (input) => new A({
138
+ ...common(input.name, input, "append"),
139
+ address: input.address
140
+ });
141
+ const aaaa = (input) => new AAAA({
142
+ ...common(input.name, input, "append"),
143
+ address: input.address
144
+ });
145
+ const cname = (input) => new CNAME({
146
+ ...common(input.name, input, "exclusive"),
147
+ target: fromStringUnsafe(input.target)
148
+ });
149
+ const txt = (input) => new TXT({
150
+ ...common(input.name, input, "append"),
151
+ value: input.value
152
+ });
153
+ const mx = (input) => new MX({
154
+ ...common(input.name, input, "append"),
155
+ exchange: fromStringUnsafe(input.exchange),
156
+ priority: input.priority
157
+ });
158
+ const caa = (input) => new CAA({
159
+ ...common(input.name, input, "append"),
160
+ flags: input.flags,
161
+ tag: input.tag,
162
+ value: input.value
163
+ });
164
+ const ns = (input) => new NS({
165
+ ...common(input.name, input, "append"),
166
+ nameserver: fromStringUnsafe(input.nameserver)
167
+ });
168
+ const srv = (input) => new SRV({
169
+ ...common(input.name, input, "append"),
170
+ target: fromStringUnsafe(input.target),
171
+ port: input.port,
172
+ priority: input.priority,
173
+ weight: input.weight
174
+ });
175
+ const isDnsRecord = (input) => Schema.is(Model)(input);
176
+ /** The record's data portion as a canonical string, for display and zone-file rendering. */
177
+ const data = (record) => {
178
+ switch (record._tag) {
179
+ case "A":
180
+ case "AAAA": return record.address;
181
+ case "CNAME": return record.target;
182
+ case "NS": return record.nameserver;
183
+ case "TXT": return record.value;
184
+ case "MX": return `${record.priority} ${record.exchange}`;
185
+ case "CAA": return `${record.flags} ${record.tag} ${record.value}`;
186
+ case "SRV": return `${record.priority} ${record.weight} ${record.port} ${record.target}`;
187
+ }
188
+ };
189
+ /**
190
+ * Structural equality over type, name, and data. `ttl`, `policy`, and `purpose` are requirement
191
+ * metadata, not record identity, so they are ignored; TTL drift is reported on plan operations.
192
+ */
193
+ const equals = (left, right) => {
194
+ if (left._tag === "Opaque" || right._tag === "Opaque") return left._tag === "Opaque" && right._tag === "Opaque" && left.name === right.name && left.type === right.type && JSON.stringify(left.raw) === JSON.stringify(right.raw);
195
+ return left._tag === right._tag && left.name === right.name && data(left) === data(right);
196
+ };
197
+ /** `true` when both records occupy the same name and type, regardless of data. */
198
+ const sameSet = (left, right) => left.name === right.name && (left._tag === "Opaque" ? left.type : left._tag) === (right._tag === "Opaque" ? right.type : right._tag);
199
+ function isIpv4(value) {
200
+ const parts = value.split(".");
201
+ return parts.length === 4 && parts.every((part) => /^\d{1,3}$/.test(part) && Number(part) <= 255 && String(Number(part)) === part);
202
+ }
203
+ function isIpv6(value) {
204
+ try {
205
+ return new URL(`http://[${value}]/`).hostname.startsWith("[");
206
+ } catch {
207
+ return false;
208
+ }
209
+ }
210
+ //#endregion
211
+ export { A, AAAA, CAA, CNAME, DnsRecord_exports, MX, Model, NS, Observed, Opaque, Policy, SRV, TXT, Type, a, aaaa, caa, cname, data, equals, isDnsRecord, mx, ns, sameSet, srv, txt };
212
+
213
+ //# sourceMappingURL=DnsRecord.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DnsRecord.mjs","names":["DomainName.Model","DomainName.fromStringUnsafe"],"sources":["../src/DnsRecord.ts"],"sourcesContent":["/**\n * DNS record requirements a product asks a customer to satisfy, and the observed records a\n * provider or resolver reports back.\n *\n * Constructors are lowercase functions (`DnsRecord.cname(...)`), Effect style. `policy` defaults\n * to `exclusive` for CNAME and `append` for everything else; `ttl` defaults to `null` (provider\n * default); `purpose` is an optional human label the UI shows.\n */\nimport { Schema } from \"effect\";\n\nimport * as DomainName from \"./DomainName.ts\";\n\n// DomainName, internal/error, Reason, Plan, and DnsRecord form an import cycle. Field schemas\n// that cross it are read lazily so any module can be the first one evaluated.\nconst Name = Schema.suspend(() => DomainName.Model);\n\nexport const Type = Schema.Literals([\"A\", \"AAAA\", \"CNAME\", \"TXT\", \"MX\", \"CAA\", \"NS\", \"SRV\"]);\nexport type Type = typeof Type.Type;\n\nexport const Policy = Schema.Literals([\"exclusive\", \"append\"]);\nexport type Policy = typeof Policy.Type;\n\nconst Ttl = Schema.NullOr(\n Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 2_147_483_647 })),\n);\nconst Port = Schema.Int.check(Schema.isBetween({ minimum: 0, maximum: 65_535 }));\nconst Priority = Schema.Int.check(Schema.isBetween({ minimum: 0, maximum: 65_535 }));\nconst Ipv4 = Schema.String.check(\n Schema.makeFilter<string>((value) => (isIpv4(value) ? undefined : \"Expected an IPv4 address\")),\n);\nconst Ipv6 = Schema.String.check(\n Schema.makeFilter<string>((value) => (isIpv6(value) ? undefined : \"Expected an IPv6 address\")),\n);\nconst NonEmpty = Schema.String.check(Schema.isMinLength(1));\n\nconst Common = {\n name: Name,\n ttl: Ttl,\n policy: Policy,\n purpose: Schema.optionalKey(Schema.String),\n};\n\nexport class A extends Schema.TaggedClass<A>(\"@domainkit/DnsRecord/A\")(\"A\", {\n ...Common,\n address: Ipv4,\n}) {}\nexport class AAAA extends Schema.TaggedClass<AAAA>(\"@domainkit/DnsRecord/AAAA\")(\"AAAA\", {\n ...Common,\n address: Ipv6,\n}) {}\nexport class CNAME extends Schema.TaggedClass<CNAME>(\"@domainkit/DnsRecord/CNAME\")(\"CNAME\", {\n ...Common,\n target: Name,\n}) {}\nexport class TXT extends Schema.TaggedClass<TXT>(\"@domainkit/DnsRecord/TXT\")(\"TXT\", {\n ...Common,\n value: NonEmpty,\n}) {}\nexport class MX extends Schema.TaggedClass<MX>(\"@domainkit/DnsRecord/MX\")(\"MX\", {\n ...Common,\n exchange: Name,\n priority: Priority,\n}) {}\nexport class CAA extends Schema.TaggedClass<CAA>(\"@domainkit/DnsRecord/CAA\")(\"CAA\", {\n ...Common,\n flags: Schema.Int.check(Schema.isBetween({ minimum: 0, maximum: 255 })),\n tag: NonEmpty,\n value: NonEmpty,\n}) {}\nexport class NS extends Schema.TaggedClass<NS>(\"@domainkit/DnsRecord/NS\")(\"NS\", {\n ...Common,\n nameserver: Name,\n}) {}\nexport class SRV extends Schema.TaggedClass<SRV>(\"@domainkit/DnsRecord/SRV\")(\"SRV\", {\n ...Common,\n target: Name,\n port: Port,\n priority: Priority,\n weight: Priority,\n}) {}\n\nexport const Model = Schema.Union([A, AAAA, CNAME, TXT, MX, CAA, NS, SRV]);\nexport type Model = typeof Model.Type;\nexport type Encoded = typeof Model.Encoded;\n\n/** A provider record DomainKit cannot model but must not overwrite. */\nexport class Opaque extends Schema.TaggedClass<Opaque>(\"@domainkit/DnsRecord/Opaque\")(\"Opaque\", {\n name: Schema.String,\n type: Schema.String,\n raw: Schema.Unknown,\n}) {}\nexport const Observed = Schema.Union([Model, Opaque]);\nexport type Observed = typeof Observed.Type;\n\ntype Options = { readonly ttl?: number; readonly policy?: Policy; readonly purpose?: string };\n\nconst common = (name: string, options: Options, defaultPolicy: Policy) => ({\n name: DomainName.fromStringUnsafe(name),\n ttl: options.ttl ?? null,\n policy: options.policy ?? defaultPolicy,\n ...(options.purpose === undefined ? {} : { purpose: options.purpose }),\n});\n\nexport const a = (input: { readonly name: string; readonly address: string } & Options): A =>\n new A({ ...common(input.name, input, \"append\"), address: input.address });\nexport const aaaa = (input: { readonly name: string; readonly address: string } & Options): AAAA =>\n new AAAA({ ...common(input.name, input, \"append\"), address: input.address });\nexport const cname = (\n input: { readonly name: string; readonly target: string } & Omit<Options, \"policy\">,\n): CNAME =>\n new CNAME({\n ...common(input.name, input, \"exclusive\"),\n target: DomainName.fromStringUnsafe(input.target),\n });\nexport const txt = (input: { readonly name: string; readonly value: string } & Options): TXT =>\n new TXT({ ...common(input.name, input, \"append\"), value: input.value });\nexport const mx = (\n input: { readonly name: string; readonly exchange: string; readonly priority: number } & Options,\n): MX =>\n new MX({\n ...common(input.name, input, \"append\"),\n exchange: DomainName.fromStringUnsafe(input.exchange),\n priority: input.priority,\n });\nexport const caa = (\n input: {\n readonly name: string;\n readonly flags: number;\n readonly tag: string;\n readonly value: string;\n } & Options,\n): CAA =>\n new CAA({\n ...common(input.name, input, \"append\"),\n flags: input.flags,\n tag: input.tag,\n value: input.value,\n });\nexport const ns = (input: { readonly name: string; readonly nameserver: string } & Options): NS =>\n new NS({\n ...common(input.name, input, \"append\"),\n nameserver: DomainName.fromStringUnsafe(input.nameserver),\n });\nexport const srv = (\n input: {\n readonly name: string;\n readonly target: string;\n readonly port: number;\n readonly priority: number;\n readonly weight: number;\n } & Options,\n): SRV =>\n new SRV({\n ...common(input.name, input, \"append\"),\n target: DomainName.fromStringUnsafe(input.target),\n port: input.port,\n priority: input.priority,\n weight: input.weight,\n });\n\nexport const isDnsRecord = (input: unknown): input is Model => Schema.is(Model)(input);\n\n/** The record's data portion as a canonical string, for display and zone-file rendering. */\nexport const data = (record: Model): string => {\n switch (record._tag) {\n case \"A\":\n case \"AAAA\":\n return record.address;\n case \"CNAME\":\n return record.target;\n case \"NS\":\n return record.nameserver;\n case \"TXT\":\n return record.value;\n case \"MX\":\n return `${record.priority} ${record.exchange}`;\n case \"CAA\":\n return `${record.flags} ${record.tag} ${record.value}`;\n case \"SRV\":\n return `${record.priority} ${record.weight} ${record.port} ${record.target}`;\n }\n};\n\n/**\n * Structural equality over type, name, and data. `ttl`, `policy`, and `purpose` are requirement\n * metadata, not record identity, so they are ignored; TTL drift is reported on plan operations.\n */\nexport const equals = (left: Observed, right: Observed): boolean => {\n if (left._tag === \"Opaque\" || right._tag === \"Opaque\") {\n return (\n left._tag === \"Opaque\" &&\n right._tag === \"Opaque\" &&\n left.name === right.name &&\n left.type === right.type &&\n JSON.stringify(left.raw) === JSON.stringify(right.raw)\n );\n }\n return left._tag === right._tag && left.name === right.name && data(left) === data(right);\n};\n\n/** `true` when both records occupy the same name and type, regardless of data. */\nexport const sameSet = (left: Observed, right: Observed): boolean =>\n left.name === right.name &&\n (left._tag === \"Opaque\" ? left.type : left._tag) ===\n (right._tag === \"Opaque\" ? right.type : right._tag);\n\nfunction isIpv4(value: string): boolean {\n const parts = value.split(\".\");\n return (\n parts.length === 4 &&\n parts.every(\n (part) => /^\\d{1,3}$/.test(part) && Number(part) <= 255 && String(Number(part)) === part,\n )\n );\n}\n\nfunction isIpv6(value: string): boolean {\n try {\n return new URL(`http://[${value}]/`).hostname.startsWith(\"[\");\n } catch {\n return false;\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import { __exportAll } from "./_virtual/_rolldown/runtime.mjs";
2
+ import { DomainKitError } from "./internal/error.mjs";
3
+ import { generateKey, layer as layer$1 } from "./Custody.mjs";
4
+ import { layer as layer$2 } from "./Providers.mjs";
5
+ import { layer as layer$3 } from "./Resolver.mjs";
6
+ import { layerMemory as layerMemory$1 } from "./Storage.mjs";
7
+ import { layer as layer$4 } from "./Connect.mjs";
8
+ import { layer as layer$5 } from "./Provision.mjs";
9
+ import { layer as layer$6 } from "./Cleanup.mjs";
10
+ import { layer as layer$7 } from "./Verify.mjs";
11
+ import { Layer, Redacted } from "effect";
12
+ //#region src/DomainKit.ts
13
+ /**
14
+ * The composed layer. Provides every lifecycle service given a list of providers; the host
15
+ * provides `Storage` and `Custody` beneath it, the same way `Persistence.layer` sits over a
16
+ * backing store in Effect.
17
+ *
18
+ * Defaults: `Resolver.layer` (Cloudflare + Google DoH) and the `Policy` references. Override a
19
+ * policy with `Effect.provideService`; pass `resolver` for a different pool.
20
+ */
21
+ var DomainKit_exports = /* @__PURE__ */ __exportAll({
22
+ Error: () => DomainKitError,
23
+ isError: () => isError,
24
+ layer: () => layer,
25
+ layerMemory: () => layerMemory
26
+ });
27
+ const isError = (input) => input instanceof DomainKitError;
28
+ const layer = (options) => Layer.mergeAll(layer$5, layer$6, layer$7).pipe(Layer.provideMerge(layer$4), Layer.provideMerge(Layer.mergeAll(layer$2(options.providers), options.resolver ?? layer$3)));
29
+ /** `layer` with `Storage.layerMemory` and a throwaway custody key. Tests and playgrounds only. */
30
+ const layerMemory = (options) => layer(options).pipe(Layer.provideMerge(Layer.mergeAll(layerMemory$1, layer$1({ key: Redacted.make(generateKey()) }))));
31
+ //#endregion
32
+ export { DomainKit_exports, DomainKitError as Error, isError, layer, layerMemory };
33
+
34
+ //# sourceMappingURL=DomainKit.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DomainKit.mjs","names":["Provision.layer","Cleanup.layer","Verify.layer","Connect.layer","Providers.layer","Resolver.layer","Storage.layerMemory","Custody.layer","Custody.generateKey"],"sources":["../src/DomainKit.ts"],"sourcesContent":["/**\n * The composed layer. Provides every lifecycle service given a list of providers; the host\n * provides `Storage` and `Custody` beneath it, the same way `Persistence.layer` sits over a\n * backing store in Effect.\n *\n * Defaults: `Resolver.layer` (Cloudflare + Google DoH) and the `Policy` references. Override a\n * policy with `Effect.provideService`; pass `resolver` for a different pool.\n */\nimport { Layer, Redacted } from \"effect\";\n\nimport * as Cleanup from \"./Cleanup.ts\";\nimport * as Connect from \"./Connect.ts\";\nimport * as Custody from \"./Custody.ts\";\nimport { DomainKitError } from \"./internal/error.ts\";\nimport type * as Provider from \"./Provider.ts\";\nimport * as Providers from \"./Providers.ts\";\nimport * as Provision from \"./Provision.ts\";\nimport * as Resolver from \"./Resolver.ts\";\nimport * as Storage from \"./Storage.ts\";\nimport * as Verify from \"./Verify.ts\";\n\nexport { DomainKitError as Error };\nexport type { Category as ErrorCategory } from \"./internal/error.ts\";\n\nexport const isError = (input: unknown): input is DomainKitError => input instanceof DomainKitError;\n\nexport interface Options {\n readonly providers: ReadonlyArray<Provider.Definition>;\n /** Replace the public DNS pool (e.g. `Testing.resolver()`); default `Resolver.layer`. */\n readonly resolver?: Layer.Layer<Resolver.Service>;\n}\n\nexport type Services =\n | Provision.Service\n | Cleanup.Service\n | Connect.Service\n | Verify.Service\n | Providers.Service\n | Resolver.Service;\n\nexport const layer = (\n options: Options,\n): Layer.Layer<Services, never, Storage.Service | Custody.Service> =>\n Layer.mergeAll(Provision.layer, Cleanup.layer, Verify.layer).pipe(\n Layer.provideMerge(Connect.layer),\n Layer.provideMerge(\n Layer.mergeAll(Providers.layer(options.providers), options.resolver ?? Resolver.layer),\n ),\n );\n\n/** `layer` with `Storage.layerMemory` and a throwaway custody key. Tests and playgrounds only. */\nexport const layerMemory = (\n options: Options,\n): Layer.Layer<Services | Storage.Service | Custody.Service, DomainKitError> =>\n layer(options).pipe(\n Layer.provideMerge(\n Layer.mergeAll(\n Storage.layerMemory,\n Custody.layer({ key: Redacted.make(Custody.generateKey()) }),\n ),\n ),\n );\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAwBA,MAAa,WAAW,UAA4C,iBAAiB;AAgBrF,MAAa,SACX,YAEA,MAAM,SAASA,SAAiBC,SAAeC,OAAY,CAAC,CAAC,KAC3D,MAAM,aAAaC,OAAa,GAChC,MAAM,aACJ,MAAM,SAASC,QAAgB,QAAQ,SAAS,GAAG,QAAQ,YAAYC,OAAc,CACvF,CACF;;AAGF,MAAa,eACX,YAEA,MAAM,OAAO,CAAC,CAAC,KACb,MAAM,aACJ,MAAM,SACJC,eACAC,QAAc,EAAE,KAAK,SAAS,KAAKC,YAAoB,CAAC,EAAE,CAAC,CAC7D,CACF,CACF"}
@@ -0,0 +1,100 @@
1
+ import { __exportAll } from "./_virtual/_rolldown/runtime.mjs";
2
+ import { InvalidInput } from "./Reason.mjs";
3
+ import { DomainKitError, fail } from "./internal/error.mjs";
4
+ import { Effect, Option, Schema, SchemaIssue, SchemaTransformation } from "effect";
5
+ import { getDomain } from "tldts";
6
+ //#region src/DomainName.ts
7
+ /**
8
+ * A normalized, IDN-aware hostname. Lifecycle inputs accept plain strings and validate them, so
9
+ * most hosts never construct this directly; it exists for hosts that want the brand in their own
10
+ * types.
11
+ */
12
+ var DomainName_exports = /* @__PURE__ */ __exportAll({
13
+ Model: () => Model,
14
+ candidates: () => candidates,
15
+ decode: () => decode,
16
+ fromString: () => fromString,
17
+ fromStringUnsafe: () => fromStringUnsafe,
18
+ isDomainName: () => isDomainName,
19
+ isWithin: () => isWithin
20
+ });
21
+ const labelPattern = /^[a-z0-9_](?:[a-z0-9_-]{0,61}[a-z0-9_])?$/;
22
+ const Branded = Schema.String.check(Schema.makeFilter((value) => isNormalized(value) ? void 0 : "Expected a fully qualified DNS name", { identifier: "DomainName" })).pipe(Schema.brand("@domainkit/DomainName"));
23
+ /** Decodes any hostname spelling into its normalized, branded form; encodes back to a string. */
24
+ const Model = Schema.String.pipe(Schema.decodeTo(Branded, SchemaTransformation.transformOrFail({
25
+ decode: (input, options) => Effect.try({
26
+ try: () => normalize(input),
27
+ catch: (cause) => new SchemaIssue.InvalidValue({ message: errorMessage(cause) }, input, options)
28
+ }),
29
+ encode: (domain) => Effect.succeed(domain)
30
+ })));
31
+ /** Validates and normalizes; `None` when the input is not a hostname. */
32
+ const fromString = (input) => {
33
+ try {
34
+ return Option.some(normalizeUnsafe(input));
35
+ } catch {
36
+ return Option.none();
37
+ }
38
+ };
39
+ /** Validates and normalizes; throws `DomainKit.Error` (reason `InvalidInput`) when invalid. */
40
+ const fromStringUnsafe = (input) => {
41
+ try {
42
+ return normalizeUnsafe(input);
43
+ } catch (cause) {
44
+ throw new DomainKitError({ reason: new InvalidInput({
45
+ message: errorMessage(cause),
46
+ field: "domain"
47
+ }) });
48
+ }
49
+ };
50
+ /** Validates and normalizes inside Effect; fails with reason `InvalidInput`. */
51
+ const decode = (input, field = "domain") => Effect.suspend(() => {
52
+ try {
53
+ return Effect.succeed(normalizeUnsafe(input));
54
+ } catch (cause) {
55
+ return fail(new InvalidInput({
56
+ message: errorMessage(cause),
57
+ field
58
+ }));
59
+ }
60
+ });
61
+ const isDomainName = (input) => typeof input === "string" && isNormalized(input);
62
+ /**
63
+ * Zone candidates from the name itself down to the registrable domain, most specific first:
64
+ * `a.b.example.com` -> `a.b.example.com`, `b.example.com`, `example.com`. Empty when the name has
65
+ * no registrable suffix.
66
+ */
67
+ const candidates = (domain) => {
68
+ const registrable = getDomain(domain, { allowPrivateDomains: true });
69
+ if (registrable === null) return [];
70
+ const labels = domain.split(".");
71
+ const registrableLabels = registrable.split(".").length;
72
+ const values = [];
73
+ for (let index = 0; index <= labels.length - registrableLabels; index += 1) values.push(labels.slice(index).join("."));
74
+ return values;
75
+ };
76
+ /** `true` when `name` is `zone` or a subdomain of it. */
77
+ const isWithin = (name, zone) => name === zone || name.endsWith(`.${zone}`);
78
+ function isNormalized(value) {
79
+ return value.length <= 253 && value.includes(".") && value.split(".").every((label) => labelPattern.test(label));
80
+ }
81
+ function normalizeUnsafe(input) {
82
+ return normalize(input);
83
+ }
84
+ function normalize(input) {
85
+ const value = input.trim().replace(/\.+$/, "").split(".").map((label) => {
86
+ if (label.includes("_") || [...label].every((character) => character.charCodeAt(0) <= 127)) return label.toLowerCase();
87
+ const normalized = new URL(`http://${label}.example`).hostname.split(".")[0];
88
+ if (normalized === void 0) throw new Error("Domain label could not be normalized");
89
+ return normalized.toLowerCase();
90
+ }).join(".");
91
+ if (!isNormalized(value)) throw new Error(`Expected a fully qualified DNS name, received ${JSON.stringify(input)}`);
92
+ return value;
93
+ }
94
+ function errorMessage(cause) {
95
+ return cause instanceof Error ? cause.message : String(cause);
96
+ }
97
+ //#endregion
98
+ export { DomainName_exports, Model, candidates, decode, fromString, fromStringUnsafe, isDomainName, isWithin };
99
+
100
+ //# sourceMappingURL=DomainName.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DomainName.mjs","names":["Errors.DomainKitError","Reason.InvalidInput","Errors.fail"],"sources":["../src/DomainName.ts"],"sourcesContent":["/**\n * A normalized, IDN-aware hostname. Lifecycle inputs accept plain strings and validate them, so\n * most hosts never construct this directly; it exists for hosts that want the brand in their own\n * types.\n */\nimport { Effect, Option, Schema, SchemaIssue, SchemaTransformation } from \"effect\";\nimport { getDomain } from \"tldts\";\n\nimport * as Errors from \"./internal/error.ts\";\nimport * as Reason from \"./Reason.ts\";\n\nconst labelPattern = /^[a-z0-9_](?:[a-z0-9_-]{0,61}[a-z0-9_])?$/;\n\nconst Branded = Schema.String.check(\n Schema.makeFilter<string>(\n (value) => (isNormalized(value) ? undefined : \"Expected a fully qualified DNS name\"),\n { identifier: \"DomainName\" },\n ),\n).pipe(Schema.brand(\"@domainkit/DomainName\"));\n\n/** Decodes any hostname spelling into its normalized, branded form; encodes back to a string. */\nexport const Model = Schema.String.pipe(\n Schema.decodeTo(\n Branded,\n SchemaTransformation.transformOrFail({\n decode: (input, options) =>\n Effect.try({\n try: () => normalize(input),\n catch: (cause) =>\n new SchemaIssue.InvalidValue({ message: errorMessage(cause) }, input, options),\n }),\n encode: (domain) => Effect.succeed(domain),\n }),\n ),\n);\nexport type Model = typeof Branded.Type;\n\n/** Validates and normalizes; `None` when the input is not a hostname. */\nexport const fromString = (input: string): Option.Option<Model> => {\n try {\n return Option.some(normalizeUnsafe(input));\n } catch {\n return Option.none();\n }\n};\n\n/** Validates and normalizes; throws `DomainKit.Error` (reason `InvalidInput`) when invalid. */\nexport const fromStringUnsafe = (input: string): Model => {\n try {\n return normalizeUnsafe(input);\n } catch (cause) {\n throw new Errors.DomainKitError({\n reason: new Reason.InvalidInput({ message: errorMessage(cause), field: \"domain\" }),\n });\n }\n};\n\n/** Validates and normalizes inside Effect; fails with reason `InvalidInput`. */\nexport const decode = (\n input: string,\n field = \"domain\",\n): Effect.Effect<Model, Errors.DomainKitError> =>\n Effect.suspend(() => {\n try {\n return Effect.succeed(normalizeUnsafe(input));\n } catch (cause) {\n return Errors.fail(new Reason.InvalidInput({ message: errorMessage(cause), field }));\n }\n });\n\nexport const isDomainName = (input: unknown): input is Model =>\n typeof input === \"string\" && isNormalized(input);\n\n/**\n * Zone candidates from the name itself down to the registrable domain, most specific first:\n * `a.b.example.com` -> `a.b.example.com`, `b.example.com`, `example.com`. Empty when the name has\n * no registrable suffix.\n */\nexport const candidates = (domain: Model): ReadonlyArray<Model> => {\n const registrable = getDomain(domain, { allowPrivateDomains: true });\n if (registrable === null) return [];\n const labels = domain.split(\".\");\n const registrableLabels = registrable.split(\".\").length;\n const values: Array<Model> = [];\n for (let index = 0; index <= labels.length - registrableLabels; index += 1) {\n values.push(labels.slice(index).join(\".\") as Model);\n }\n return values;\n};\n\n/** `true` when `name` is `zone` or a subdomain of it. */\nexport const isWithin = (name: string, zone: string): boolean =>\n name === zone || name.endsWith(`.${zone}`);\n\nfunction isNormalized(value: string): boolean {\n return (\n value.length <= 253 &&\n value.includes(\".\") &&\n value.split(\".\").every((label) => labelPattern.test(label))\n );\n}\n\nfunction normalizeUnsafe(input: string): Model {\n return normalize(input) as Model;\n}\n\nfunction normalize(input: string): string {\n const labels = input\n .trim()\n .replace(/\\.+$/, \"\")\n .split(\".\")\n .map((label) => {\n if (label.includes(\"_\") || [...label].every((character) => character.charCodeAt(0) <= 127)) {\n return label.toLowerCase();\n }\n const hostname = new URL(`http://${label}.example`).hostname;\n const normalized = hostname.split(\".\")[0];\n if (normalized === undefined) throw new Error(\"Domain label could not be normalized\");\n return normalized.toLowerCase();\n });\n const value = labels.join(\".\");\n if (!isNormalized(value)) {\n throw new Error(`Expected a fully qualified DNS name, received ${JSON.stringify(input)}`);\n }\n return value;\n}\n\nfunction errorMessage(cause: unknown): string {\n return cause instanceof Error ? cause.message : String(cause);\n}\n"],"mappings":"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"}
package/dist/Plan.mjs ADDED
@@ -0,0 +1,102 @@
1
+ import { __exportAll } from "./_virtual/_rolldown/runtime.mjs";
2
+ import { Model as Model$1, Observed as Observed$1, data } from "./DnsRecord.mjs";
3
+ import { decode as decode$1 } from "./internal/error.mjs";
4
+ import { Schema } from "effect";
5
+ //#region src/Plan.ts
6
+ /**
7
+ * A reviewable, digest-bound set of DNS operations. Plans are additive and fail closed: matching
8
+ * records are `Noop`, missing records are `Create`, incompatible state is `Conflict`. A
9
+ * provisioning plan never updates or deletes; a cleanup plan carries `Delete` operations for
10
+ * records a receipt proves DomainKit created.
11
+ */
12
+ var Plan_exports = /* @__PURE__ */ __exportAll({
13
+ Conflict: () => Conflict,
14
+ Create: () => Create,
15
+ Delete: () => Delete,
16
+ Digest: () => Digest,
17
+ Kind: () => Kind,
18
+ Model: () => Model,
19
+ Noop: () => Noop,
20
+ Operation: () => Operation,
21
+ OperationId: () => OperationId,
22
+ PlanId: () => PlanId,
23
+ Version: () => Version,
24
+ conflicts: () => conflicts,
25
+ decode: () => decode,
26
+ encode: () => encode,
27
+ isApplicable: () => isApplicable,
28
+ renderInstructions: () => renderInstructions,
29
+ writes: () => writes
30
+ });
31
+ const Record = Schema.suspend(() => Model$1);
32
+ const Observed = Schema.suspend(() => Observed$1);
33
+ const PlanId = Schema.String.pipe(Schema.brand("@domainkit/PlanId"));
34
+ const Digest = Schema.String.pipe(Schema.brand("@domainkit/Digest"));
35
+ const OperationId = Schema.String.pipe(Schema.brand("@domainkit/OperationId"));
36
+ const Kind = Schema.Literals(["provisioning", "cleanup"]);
37
+ var Create = class extends Schema.TaggedClass("@domainkit/Plan/Create")("Create", {
38
+ id: OperationId,
39
+ record: Record
40
+ }) {};
41
+ var Noop = class extends Schema.TaggedClass("@domainkit/Plan/Noop")("Noop", {
42
+ id: OperationId,
43
+ record: Record,
44
+ existing: Observed,
45
+ ttlDrift: Schema.Boolean
46
+ }) {};
47
+ var Conflict = class extends Schema.TaggedClass("@domainkit/Plan/Conflict")("Conflict", {
48
+ id: OperationId,
49
+ record: Record,
50
+ existing: Schema.Array(Observed),
51
+ reason: Schema.Literals([
52
+ "exclusive-name",
53
+ "cname-collision",
54
+ "value-mismatch",
55
+ "opaque",
56
+ "missing"
57
+ ])
58
+ }) {};
59
+ /** Cleanup only: remove the provider record a receipt proves DomainKit created. */
60
+ var Delete = class extends Schema.TaggedClass("@domainkit/Plan/Delete")("Delete", {
61
+ id: OperationId,
62
+ record: Record,
63
+ providerRecordId: Schema.String
64
+ }) {};
65
+ const Operation = Schema.Union([
66
+ Create,
67
+ Noop,
68
+ Conflict,
69
+ Delete
70
+ ]);
71
+ const Version = Schema.Literal("domainkit.plan.v2");
72
+ var Model = class extends Schema.Class("@domainkit/Plan")({
73
+ id: PlanId,
74
+ version: Version,
75
+ kind: Kind,
76
+ digest: Digest,
77
+ domain: Schema.String,
78
+ zone: Schema.String,
79
+ provider: Schema.String,
80
+ attachmentId: Schema.String,
81
+ operations: Schema.Array(Operation),
82
+ createdAt: Schema.DateTimeUtcFromString,
83
+ expiresAt: Schema.DateTimeUtcFromString
84
+ }) {};
85
+ const decode = (input) => decode$1(Model, input);
86
+ const encode = Schema.encodeSync(Model);
87
+ /** `true` when the plan has no conflicts and at least one write (`Create` or `Delete`). */
88
+ const isApplicable = (plan) => conflicts(plan).length === 0 && writes(plan).length > 0;
89
+ const conflicts = (plan) => plan.operations.filter((operation) => operation._tag === "Conflict");
90
+ /** The operations that would touch the provider: creates for provisioning, deletes for cleanup. */
91
+ const writes = (plan) => plan.operations.filter((operation) => operation._tag === "Create" || operation._tag === "Delete");
92
+ /** Manual instructions for hosts whose customers apply records by hand. */
93
+ const renderInstructions = (plan) => plan.operations.map((operation) => {
94
+ const verb = operation._tag === "Create" ? "ADD" : operation._tag === "Delete" ? "REMOVE" : operation._tag === "Noop" ? "KEEP" : `CONFLICT (${operation.reason})`;
95
+ const record = operation.record;
96
+ const ttl = record.ttl === null ? "" : ` ttl=${record.ttl}`;
97
+ return `${verb} ${record._tag} ${record.name} ${data(record)}${ttl}`;
98
+ }).join("\n");
99
+ //#endregion
100
+ export { Conflict, Create, Delete, Digest, Kind, Model, Noop, Operation, OperationId, PlanId, Plan_exports, Version, conflicts, decode, encode, isApplicable, renderInstructions, writes };
101
+
102
+ //# sourceMappingURL=Plan.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Plan.mjs","names":["DnsRecord.Model","DnsRecord.Observed","Errors.decode","DnsRecord.data"],"sources":["../src/Plan.ts"],"sourcesContent":["/**\n * A reviewable, digest-bound set of DNS operations. Plans are additive and fail closed: matching\n * records are `Noop`, missing records are `Create`, incompatible state is `Conflict`. A\n * provisioning plan never updates or deletes; a cleanup plan carries `Delete` operations for\n * records a receipt proves DomainKit created.\n */\nimport { Effect, Schema } from \"effect\";\n\nimport * as DnsRecord from \"./DnsRecord.ts\";\nimport * as Errors from \"./internal/error.ts\";\n\n// Read lazily: DnsRecord sits in the same import cycle (see DnsRecord.ts).\nconst Record = Schema.suspend(() => DnsRecord.Model);\nconst Observed = Schema.suspend(() => DnsRecord.Observed);\n\nexport const PlanId = Schema.String.pipe(Schema.brand(\"@domainkit/PlanId\"));\nexport type PlanId = typeof PlanId.Type;\n\nexport const Digest = Schema.String.pipe(Schema.brand(\"@domainkit/Digest\"));\nexport type Digest = typeof Digest.Type;\n\nexport const OperationId = Schema.String.pipe(Schema.brand(\"@domainkit/OperationId\"));\nexport type OperationId = typeof OperationId.Type;\n\nexport const Kind = Schema.Literals([\"provisioning\", \"cleanup\"]);\nexport type Kind = typeof Kind.Type;\n\nexport class Create extends Schema.TaggedClass<Create>(\"@domainkit/Plan/Create\")(\"Create\", {\n id: OperationId,\n record: Record,\n}) {}\nexport class Noop extends Schema.TaggedClass<Noop>(\"@domainkit/Plan/Noop\")(\"Noop\", {\n id: OperationId,\n record: Record,\n existing: Observed,\n ttlDrift: Schema.Boolean,\n}) {}\nexport class Conflict extends Schema.TaggedClass<Conflict>(\"@domainkit/Plan/Conflict\")(\"Conflict\", {\n id: OperationId,\n record: Record,\n existing: Schema.Array(Observed),\n reason: Schema.Literals([\n \"exclusive-name\",\n \"cname-collision\",\n \"value-mismatch\",\n \"opaque\",\n \"missing\",\n ]),\n}) {}\n/** Cleanup only: remove the provider record a receipt proves DomainKit created. */\nexport class Delete extends Schema.TaggedClass<Delete>(\"@domainkit/Plan/Delete\")(\"Delete\", {\n id: OperationId,\n record: Record,\n providerRecordId: Schema.String,\n}) {}\nexport const Operation = Schema.Union([Create, Noop, Conflict, Delete]);\nexport type Operation = typeof Operation.Type;\n\nexport const Version = Schema.Literal(\"domainkit.plan.v2\");\n\nexport class Model extends Schema.Class<Model>(\"@domainkit/Plan\")({\n id: PlanId,\n version: Version,\n kind: Kind,\n digest: Digest,\n domain: Schema.String,\n zone: Schema.String,\n provider: Schema.String,\n attachmentId: Schema.String,\n operations: Schema.Array(Operation),\n createdAt: Schema.DateTimeUtcFromString,\n expiresAt: Schema.DateTimeUtcFromString,\n}) {}\nexport type Encoded = typeof Model.Encoded;\n\nexport const decode = (input: unknown): Effect.Effect<Model, Errors.DomainKitError> =>\n Errors.decode(Model, input);\nexport const encode: (plan: Model) => Encoded = Schema.encodeSync(Model);\n\n/** `true` when the plan has no conflicts and at least one write (`Create` or `Delete`). */\nexport const isApplicable = (plan: Model): boolean =>\n conflicts(plan).length === 0 && writes(plan).length > 0;\n\nexport const conflicts = (plan: Model): ReadonlyArray<Conflict> =>\n plan.operations.filter((operation): operation is Conflict => operation._tag === \"Conflict\");\n\n/** The operations that would touch the provider: creates for provisioning, deletes for cleanup. */\nexport const writes = (plan: Model): ReadonlyArray<Create | Delete> =>\n plan.operations.filter(\n (operation): operation is Create | Delete =>\n operation._tag === \"Create\" || operation._tag === \"Delete\",\n );\n\n/** Manual instructions for hosts whose customers apply records by hand. */\nexport const renderInstructions = (plan: Model): string =>\n plan.operations\n .map((operation) => {\n const verb =\n operation._tag === \"Create\"\n ? \"ADD\"\n : operation._tag === \"Delete\"\n ? \"REMOVE\"\n : operation._tag === \"Noop\"\n ? \"KEEP\"\n : `CONFLICT (${operation.reason})`;\n const record = operation.record;\n const ttl = record.ttl === null ? \"\" : ` ttl=${record.ttl}`;\n return `${verb} ${record._tag} ${record.name} ${DnsRecord.data(record)}${ttl}`;\n })\n .join(\"\\n\");\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAYA,MAAM,SAAS,OAAO,cAAcA,OAAe;AACnD,MAAM,WAAW,OAAO,cAAcC,UAAkB;AAExD,MAAa,SAAS,OAAO,OAAO,KAAK,OAAO,MAAM,mBAAmB,CAAC;AAG1E,MAAa,SAAS,OAAO,OAAO,KAAK,OAAO,MAAM,mBAAmB,CAAC;AAG1E,MAAa,cAAc,OAAO,OAAO,KAAK,OAAO,MAAM,wBAAwB,CAAC;AAGpF,MAAa,OAAO,OAAO,SAAS,CAAC,gBAAgB,SAAS,CAAC;AAG/D,IAAa,SAAb,cAA4B,OAAO,YAAoB,wBAAwB,CAAC,CAAC,UAAU;CACzF,IAAI;CACJ,QAAQ;AACV,CAAC,CAAC,CAAC,CAAC;AACJ,IAAa,OAAb,cAA0B,OAAO,YAAkB,sBAAsB,CAAC,CAAC,QAAQ;CACjF,IAAI;CACJ,QAAQ;CACR,UAAU;CACV,UAAU,OAAO;AACnB,CAAC,CAAC,CAAC,CAAC;AACJ,IAAa,WAAb,cAA8B,OAAO,YAAsB,0BAA0B,CAAC,CAAC,YAAY;CACjG,IAAI;CACJ,QAAQ;CACR,UAAU,OAAO,MAAM,QAAQ;CAC/B,QAAQ,OAAO,SAAS;EACtB;EACA;EACA;EACA;EACA;CACF,CAAC;AACH,CAAC,CAAC,CAAC,CAAC;;AAEJ,IAAa,SAAb,cAA4B,OAAO,YAAoB,wBAAwB,CAAC,CAAC,UAAU;CACzF,IAAI;CACJ,QAAQ;CACR,kBAAkB,OAAO;AAC3B,CAAC,CAAC,CAAC,CAAC;AACJ,MAAa,YAAY,OAAO,MAAM;CAAC;CAAQ;CAAM;CAAU;AAAM,CAAC;AAGtE,MAAa,UAAU,OAAO,QAAQ,mBAAmB;AAEzD,IAAa,QAAb,cAA2B,OAAO,MAAa,iBAAiB,CAAC,CAAC;CAChE,IAAI;CACJ,SAAS;CACT,MAAM;CACN,QAAQ;CACR,QAAQ,OAAO;CACf,MAAM,OAAO;CACb,UAAU,OAAO;CACjB,cAAc,OAAO;CACrB,YAAY,OAAO,MAAM,SAAS;CAClC,WAAW,OAAO;CAClB,WAAW,OAAO;AACpB,CAAC,CAAC,CAAC,CAAC;AAGJ,MAAa,UAAU,UACrBC,SAAc,OAAO,KAAK;AAC5B,MAAa,SAAmC,OAAO,WAAW,KAAK;;AAGvE,MAAa,gBAAgB,SAC3B,UAAU,IAAI,CAAC,CAAC,WAAW,KAAK,OAAO,IAAI,CAAC,CAAC,SAAS;AAExD,MAAa,aAAa,SACxB,KAAK,WAAW,QAAQ,cAAqC,UAAU,SAAS,UAAU;;AAG5F,MAAa,UAAU,SACrB,KAAK,WAAW,QACb,cACC,UAAU,SAAS,YAAY,UAAU,SAAS,QACtD;;AAGF,MAAa,sBAAsB,SACjC,KAAK,WACF,KAAK,cAAc;CAClB,MAAM,OACJ,UAAU,SAAS,WACf,QACA,UAAU,SAAS,WACjB,WACA,UAAU,SAAS,SACjB,SACA,aAAa,UAAU,OAAO;CACxC,MAAM,SAAS,UAAU;CACzB,MAAM,MAAM,OAAO,QAAQ,OAAO,KAAK,QAAQ,OAAO;CACtD,OAAO,GAAG,KAAK,GAAG,OAAO,KAAK,GAAG,OAAO,KAAK,GAAGC,KAAe,MAAM,IAAI;AAC3E,CAAC,CAAC,CACD,KAAK,IAAI"}
@@ -0,0 +1,23 @@
1
+ import { __exportAll } from "./_virtual/_rolldown/runtime.mjs";
2
+ import { Context, Layer } from "effect";
3
+ //#region src/Principal.ts
4
+ /**
5
+ * Who is acting. Required by every lifecycle operation and every Storage query; there is no
6
+ * default, so cross-tenant access is a type error rather than a runtime check. `domainkit/server`
7
+ * provides it per request from the host's `Identity`.
8
+ */
9
+ var Principal_exports = /* @__PURE__ */ __exportAll({
10
+ Service: () => Service,
11
+ layer: () => layer,
12
+ make: () => make
13
+ });
14
+ var Service = class extends Context.Service()("@domainkit/Principal") {};
15
+ const make = (input) => ({
16
+ ownerId: input.ownerId,
17
+ actorId: input.actorId
18
+ });
19
+ const layer = (input) => Layer.succeed(Service)(make(input));
20
+ //#endregion
21
+ export { Principal_exports, Service, layer, make };
22
+
23
+ //# sourceMappingURL=Principal.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Principal.mjs","names":[],"sources":["../src/Principal.ts"],"sourcesContent":["/**\n * Who is acting. Required by every lifecycle operation and every Storage query; there is no\n * default, so cross-tenant access is a type error rather than a runtime check. `domainkit/server`\n * provides it per request from the host's `Identity`.\n */\nimport { Context, Layer } from \"effect\";\n\nexport interface Interface {\n /** Tenant boundary: organization, workspace, account. Storage scopes every row by it. */\n readonly ownerId: string;\n /** The user or automation acting on the tenant's behalf; recorded on approvals and receipts. */\n readonly actorId: string;\n}\n\nexport class Service extends Context.Service<Service, Interface>()(\"@domainkit/Principal\") {}\n\nexport const make = (input: Interface): Interface => ({\n ownerId: input.ownerId,\n actorId: input.actorId,\n});\n\nexport const layer = (input: Interface): Layer.Layer<Service> =>\n Layer.succeed(Service)(make(input));\n"],"mappings":";;;;;;;;;;;;;AAcA,IAAa,UAAb,cAA6B,QAAQ,QAA4B,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC;AAE5F,MAAa,QAAQ,WAAiC;CACpD,SAAS,MAAM;CACf,SAAS,MAAM;AACjB;AAEA,MAAa,SAAS,UACpB,MAAM,QAAQ,OAAO,CAAC,CAAC,KAAK,KAAK,CAAC"}