alchemy 0.2.5 → 0.3.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 (151) hide show
  1. package/README.md +3 -3
  2. package/lib/ai/astro-file.d.ts +137 -0
  3. package/lib/ai/astro-file.js +173 -0
  4. package/lib/ai/client.d.ts +2 -10
  5. package/lib/ai/client.js +8 -24
  6. package/lib/ai/css-file.d.ts +131 -0
  7. package/lib/ai/css-file.js +158 -0
  8. package/lib/ai/data.d.ts +45 -5
  9. package/lib/ai/data.js +45 -10
  10. package/lib/ai/document.d.ts +44 -20
  11. package/lib/ai/document.js +95 -28
  12. package/lib/ai/html-file.d.ts +128 -0
  13. package/lib/ai/html-file.js +155 -0
  14. package/lib/ai/index.d.ts +7 -0
  15. package/lib/ai/index.js +7 -0
  16. package/lib/ai/json-file.d.ts +160 -0
  17. package/lib/ai/json-file.js +208 -0
  18. package/lib/ai/typescript-file.d.ts +140 -0
  19. package/lib/ai/typescript-file.js +176 -0
  20. package/lib/ai/vue-file.d.ts +126 -0
  21. package/lib/ai/vue-file.js +153 -0
  22. package/lib/ai/yaml-file.d.ts +169 -0
  23. package/lib/ai/yaml-file.js +236 -0
  24. package/lib/cloudflare/dns.d.ts +88 -0
  25. package/lib/cloudflare/dns.js +192 -0
  26. package/lib/cloudflare/index.d.ts +2 -1
  27. package/lib/cloudflare/index.js +2 -1
  28. package/lib/cloudflare/r2-rest-state-store.d.ts +125 -0
  29. package/lib/cloudflare/r2-rest-state-store.js +254 -0
  30. package/lib/cloudflare/response.d.ts +12 -0
  31. package/lib/cloudflare/response.js +0 -0
  32. package/lib/cloudflare/zone.d.ts +18 -0
  33. package/lib/cloudflare/zone.js +17 -4
  34. package/lib/dns/godaddy.d.ts +7 -0
  35. package/lib/dns/godaddy.js +21 -0
  36. package/lib/dns/import-dns.d.ts +82 -0
  37. package/lib/dns/import-dns.js +114 -0
  38. package/lib/dns/index.d.ts +2 -0
  39. package/lib/dns/index.js +2 -0
  40. package/lib/dns/record.d.ts +88 -0
  41. package/lib/dns/record.js +13 -0
  42. package/lib/fs/file-system-state-store.d.ts +17 -0
  43. package/lib/fs/file-system-state-store.js +87 -0
  44. package/lib/fs/folder.d.ts +10 -0
  45. package/lib/fs/folder.js +2 -2
  46. package/lib/fs/index.d.ts +4 -4
  47. package/lib/fs/index.js +4 -4
  48. package/lib/fs/{json-file.d.ts → static-json-file.d.ts} +2 -2
  49. package/lib/fs/static-json-file.js +14 -0
  50. package/lib/fs/{text-file.d.ts → static-text-file.d.ts} +2 -2
  51. package/lib/fs/{text-file.js → static-text-file.js} +1 -1
  52. package/lib/fs/{typescript-file.d.ts → static-typescript-file.d.ts} +2 -2
  53. package/lib/fs/{typescript-file.js → static-typescript-file.js} +1 -1
  54. package/lib/fs/{yaml-file.d.ts → static-yaml-file.d.ts} +2 -2
  55. package/lib/fs/{yaml-file.js → static-yaml-file.js} +1 -1
  56. package/lib/internal/getting-started.d.ts +10 -0
  57. package/lib/internal/getting-started.js +94 -0
  58. package/lib/internal/project.d.ts +13 -0
  59. package/lib/internal/project.js +101 -0
  60. package/lib/internal/providers.d.ts +23 -0
  61. package/lib/internal/providers.js +148 -0
  62. package/lib/resource.js +8 -1
  63. package/lib/scope.d.ts +1 -1
  64. package/lib/scope.js +4 -2
  65. package/lib/state.d.ts +1 -16
  66. package/lib/state.js +0 -87
  67. package/lib/test/bun.d.ts +11 -0
  68. package/lib/test/bun.js +36 -8
  69. package/lib/web/astro.d.ts +147 -0
  70. package/lib/web/astro.js +414 -0
  71. package/lib/{shadcn/component.js → web/shadcn-component.js} +2 -9
  72. package/lib/web/shadcn.d.ts +97 -0
  73. package/lib/web/shadcn.js +99 -0
  74. package/lib/web/tailwind.d.ts +64 -0
  75. package/lib/web/tailwind.js +97 -0
  76. package/lib/{vite → web}/vite.d.ts +5 -0
  77. package/lib/{vite → web}/vite.js +44 -44
  78. package/lib/web/vitepress/custom-theme.d.ts +173 -0
  79. package/lib/web/vitepress/custom-theme.js +336 -0
  80. package/lib/{vitepress → web/vitepress}/dependencies.d.ts +2 -2
  81. package/lib/{vitepress → web/vitepress}/dependencies.js +1 -1
  82. package/lib/web/vitepress/home-page.d.ts +244 -0
  83. package/lib/web/vitepress/home-page.js +134 -0
  84. package/lib/{vitepress → web/vitepress}/index.d.ts +1 -0
  85. package/lib/{vitepress → web/vitepress}/index.js +1 -0
  86. package/lib/{vitepress → web/vitepress}/vitepress.d.ts +10 -8
  87. package/lib/{vitepress → web/vitepress}/vitepress.js +11 -12
  88. package/package.json +6 -2
  89. package/src/ai/astro-file.ts +288 -0
  90. package/src/ai/client.ts +8 -28
  91. package/src/ai/css-file.ts +266 -0
  92. package/src/ai/data.ts +53 -16
  93. package/src/ai/document.ts +130 -33
  94. package/src/ai/html-file.ts +263 -0
  95. package/src/ai/index.ts +7 -0
  96. package/src/ai/json-file.ts +349 -0
  97. package/src/ai/typescript-file.ts +293 -0
  98. package/src/ai/vue-file.ts +261 -0
  99. package/src/ai/yaml-file.ts +368 -0
  100. package/src/apply.ts +0 -1
  101. package/src/cloudflare/dns.ts +344 -0
  102. package/src/cloudflare/index.ts +2 -1
  103. package/src/cloudflare/r2-rest-state-store.ts +353 -0
  104. package/src/cloudflare/response.ts +9 -0
  105. package/src/cloudflare/zone.ts +39 -23
  106. package/src/dns/godaddy.ts +29 -0
  107. package/src/dns/import-dns.ts +213 -0
  108. package/src/dns/index.ts +2 -0
  109. package/src/dns/record.ts +121 -0
  110. package/src/fs/file-system-state-store.ts +109 -0
  111. package/src/fs/folder.ts +14 -2
  112. package/src/fs/index.ts +4 -4
  113. package/src/fs/{json-file.ts → static-json-file.ts} +13 -3
  114. package/src/fs/{text-file.ts → static-text-file.ts} +5 -2
  115. package/src/fs/{typescript-file.ts → static-typescript-file.ts} +3 -3
  116. package/src/fs/{yaml-file.ts → static-yaml-file.ts} +5 -2
  117. package/src/internal/getting-started.ts +107 -0
  118. package/src/internal/project.ts +119 -0
  119. package/src/internal/providers.ts +209 -0
  120. package/src/resource.ts +10 -1
  121. package/src/scope.ts +5 -6
  122. package/src/state.ts +1 -111
  123. package/src/test/bun.ts +60 -10
  124. package/src/web/astro.ts +644 -0
  125. package/src/{shadcn/component.ts → web/shadcn-component.ts} +5 -13
  126. package/src/web/shadcn.ts +219 -0
  127. package/src/web/tailwind.ts +167 -0
  128. package/src/{vite → web}/vite.ts +54 -56
  129. package/src/web/vitepress/custom-theme.ts +514 -0
  130. package/src/{vitepress → web/vitepress}/dependencies.ts +2 -2
  131. package/src/web/vitepress/home-page.ts +363 -0
  132. package/src/{vitepress → web/vitepress}/index.ts +1 -0
  133. package/src/{vitepress → web/vitepress}/vitepress.ts +34 -27
  134. package/lib/cloudflare/state.d.ts +0 -82
  135. package/lib/cloudflare/state.js +0 -108
  136. package/lib/fs/json-file.js +0 -7
  137. package/lib/internal/docs.d.ts +0 -5
  138. package/lib/internal/docs.js +0 -287
  139. package/lib/shadcn/index.d.ts +0 -1
  140. package/lib/shadcn/index.js +0 -1
  141. package/lib/vite/index.d.ts +0 -1
  142. package/lib/vite/index.js +0 -1
  143. package/lib/vitepress/home-page.d.ts +0 -133
  144. package/lib/vitepress/home-page.js +0 -11
  145. package/src/cloudflare/state.ts +0 -138
  146. package/src/internal/docs.ts +0 -338
  147. package/src/shadcn/index.ts +0 -1
  148. package/src/vite/index.ts +0 -1
  149. package/src/vitepress/home-page.ts +0 -166
  150. /package/lib/{shadcn/component.d.ts → web/shadcn-component.d.ts} +0 -0
  151. /package/src/{vitepress → web/vitepress}/index.md +0 -0
@@ -0,0 +1,254 @@
1
+ import { withExponentialBackoff } from "../util/retry";
2
+ import { deserialize, serialize } from "../util/serde";
3
+ import { createCloudflareApi } from "./api";
4
+ /**
5
+ * State store implementation using Cloudflare R2 API
6
+ * Uses R2 for immediate consistency compared to KV's eventual consistency
7
+ */
8
+ export class R2RestStateStore {
9
+ scope;
10
+ api;
11
+ prefix;
12
+ bucketName;
13
+ apiKey;
14
+ accountId;
15
+ email;
16
+ initialized = false;
17
+ /**
18
+ * Create a new CloudflareR2StateStore
19
+ *
20
+ * @param scope The scope this store belongs to
21
+ * @param options Options for the state store
22
+ */
23
+ constructor(scope, options) {
24
+ this.scope = scope;
25
+ // Use the scope's chain to build the prefix, similar to how FileSystemStateStore builds its directory
26
+ const scopePath = scope.chain.join("/");
27
+ this.prefix = options.prefix
28
+ ? `${options.prefix}${scopePath}/`
29
+ : `alchemy/${scopePath}/`;
30
+ if (!options.bucketName) {
31
+ throw new Error("bucketName is required for CloudflareR2StateStore");
32
+ }
33
+ this.bucketName = options.bucketName;
34
+ this.apiKey = options.apiKey;
35
+ this.accountId = options.accountId;
36
+ this.email = options.email;
37
+ // We'll initialize the API in init() to allow for async creation
38
+ this.api = null;
39
+ }
40
+ /**
41
+ * Initialize the R2 client
42
+ */
43
+ async init() {
44
+ if (this.initialized)
45
+ return;
46
+ // Create Cloudflare API client with automatic account discovery
47
+ this.api = await createCloudflareApi({
48
+ apiKey: this.apiKey,
49
+ accountId: this.accountId,
50
+ email: this.email,
51
+ });
52
+ this.initialized = true;
53
+ }
54
+ /**
55
+ * R2 buckets cannot be deleted programmatically via this method
56
+ */
57
+ async deinit() {
58
+ // We don't delete the bucket here, only via explicit resource deletion
59
+ }
60
+ /**
61
+ * List all resources in the state store
62
+ */
63
+ async list() {
64
+ await this.ensureInitialized();
65
+ // Using pagination to get all objects
66
+ let keys = [];
67
+ let cursor = null;
68
+ do {
69
+ const params = new URLSearchParams({
70
+ prefix: this.prefix,
71
+ limit: "1000",
72
+ });
73
+ if (cursor) {
74
+ params.append("cursor", cursor);
75
+ }
76
+ const listPath = `/accounts/${this.api.accountId}/r2/buckets/${this.bucketName}/objects?${params.toString()}`;
77
+ const response = await this.api.get(listPath);
78
+ if (!response.ok) {
79
+ const errorData = await response.json().catch(() => ({
80
+ errors: [{ message: response.statusText }],
81
+ }));
82
+ throw new Error(`Error listing R2 objects: ${errorData.errors?.[0]?.message || response.statusText}`);
83
+ }
84
+ const data = (await response.json());
85
+ // The result structure may be under "result" key in Cloudflare's API
86
+ const result = data.result || data;
87
+ const objects = result.objects || [];
88
+ // Add keys to our list, removing the prefix and converting from storage format
89
+ keys = keys.concat(objects.map((obj) => {
90
+ const keyName = obj.key || obj.name;
91
+ return this.convertKeyFromStorage(keyName.slice(this.prefix.length));
92
+ }));
93
+ // Update cursor for next page if available
94
+ cursor =
95
+ result.truncated || result.cursor_pagination
96
+ ? result.cursor || null
97
+ : null;
98
+ } while (cursor);
99
+ return keys;
100
+ }
101
+ /**
102
+ * Count the number of items in the state store
103
+ */
104
+ async count() {
105
+ const keys = await this.list();
106
+ return keys.length;
107
+ }
108
+ /**
109
+ * Get a state by key
110
+ *
111
+ * @param key The key to look up
112
+ * @returns The state or undefined if not found
113
+ */
114
+ async get(key) {
115
+ await this.ensureInitialized();
116
+ try {
117
+ const response = await this.api.get(`/accounts/${this.api.accountId}/r2/buckets/${this.bucketName}/objects/${this.getObjectKey(key)}`);
118
+ if (!response.ok) {
119
+ if (response.status === 404) {
120
+ return undefined;
121
+ }
122
+ const errorData = await response.json().catch(() => ({
123
+ errors: [{ message: response.statusText }],
124
+ }));
125
+ throw new Error(`Error getting R2 object: ${errorData.errors?.[0]?.message || response.statusText}`);
126
+ }
127
+ // Parse and deserialize the state data
128
+ const rawData = await response.json();
129
+ const state = (await deserialize(this.scope, rawData));
130
+ // Create a new state object with proper output
131
+ return {
132
+ ...state,
133
+ output: {
134
+ ...(state.output || {}),
135
+ Scope: this.scope,
136
+ },
137
+ };
138
+ }
139
+ catch (error) {
140
+ if (error.message?.includes("404")) {
141
+ return undefined;
142
+ }
143
+ throw error;
144
+ }
145
+ }
146
+ /**
147
+ * Get multiple states by their keys
148
+ *
149
+ * @param ids Array of keys to fetch
150
+ * @returns Record mapping keys to their states
151
+ */
152
+ async getBatch(ids) {
153
+ const result = {};
154
+ // R2 REST API doesn't have a batch get operation, so we need to make multiple requests
155
+ const promises = ids.map(async (id) => {
156
+ const state = await this.get(id);
157
+ if (state) {
158
+ result[id] = state;
159
+ }
160
+ });
161
+ await Promise.all(promises);
162
+ return result;
163
+ }
164
+ /**
165
+ * Get all states in the store
166
+ *
167
+ * @returns Record mapping all keys to their states
168
+ */
169
+ async all() {
170
+ const keys = await this.list();
171
+ return this.getBatch(keys);
172
+ }
173
+ /**
174
+ * Set a state for a key
175
+ *
176
+ * @param key The key to set
177
+ * @param value The state to store
178
+ */
179
+ async set(key, value) {
180
+ await this.ensureInitialized();
181
+ const objectKey = this.getObjectKey(key);
182
+ // Serialize the state to handle cyclic structures
183
+ const serializedData = await serialize(this.scope, value);
184
+ // Using withExponentialBackoff for reliability
185
+ await withExponentialBackoff(async () => {
186
+ const response = await this.api.put(`/accounts/${this.api.accountId}/r2/buckets/${this.bucketName}/objects/${objectKey}`, serializedData, {
187
+ headers: {
188
+ "Content-Type": "application/json",
189
+ },
190
+ });
191
+ if (!response.ok) {
192
+ const errorData = await response.json().catch(() => ({
193
+ errors: [{ message: response.statusText }],
194
+ }));
195
+ throw new Error(`Error writing to R2: ${errorData.errors?.[0]?.message || response.statusText}`);
196
+ }
197
+ return response;
198
+ },
199
+ // Retry on transient errors
200
+ (error) => error.message?.includes("503") || error.message?.includes("timeout"), 5, // 5 retry attempts
201
+ 1000);
202
+ }
203
+ /**
204
+ * Delete a state by key
205
+ *
206
+ * @param key The key to delete
207
+ */
208
+ async delete(key) {
209
+ await this.ensureInitialized();
210
+ const response = await this.api.delete(`/accounts/${this.api.accountId}/r2/buckets/${this.bucketName}/objects/${this.getObjectKey(key)}`);
211
+ if (!response.ok && response.status !== 404) {
212
+ const errorData = await response.json().catch(() => ({
213
+ errors: [{ message: response.statusText }],
214
+ }));
215
+ throw new Error(`Error deleting from R2: ${errorData.errors?.[0]?.message || response.statusText}`);
216
+ }
217
+ }
218
+ /**
219
+ * Convert key for storage by replacing slashes with colons
220
+ * since R2 treats slashes as directory separators
221
+ *
222
+ * @param key The original key
223
+ * @returns Key with slashes replaced by colons
224
+ */
225
+ convertKeyForStorage(key) {
226
+ return key.replaceAll("/", ":");
227
+ }
228
+ /**
229
+ * Convert key from storage by replacing colons with slashes
230
+ *
231
+ * @param key The storage key
232
+ * @returns Key with colons replaced by slashes
233
+ */
234
+ convertKeyFromStorage(key) {
235
+ return key.replaceAll(":", "/");
236
+ }
237
+ /**
238
+ * Get the full object key for storage
239
+ *
240
+ * @param key The original key
241
+ * @returns The key with prefix for use in the R2 bucket
242
+ */
243
+ getObjectKey(key) {
244
+ return `${this.prefix}${this.convertKeyForStorage(key)}`;
245
+ }
246
+ /**
247
+ * Ensure the store is initialized before operations
248
+ */
249
+ async ensureInitialized() {
250
+ if (!this.initialized) {
251
+ await this.init();
252
+ }
253
+ }
254
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Cloudflare API response format
3
+ */
4
+ export interface CloudflareResponse<T = unknown> {
5
+ result: T;
6
+ success: boolean;
7
+ errors: Array<{
8
+ code: number;
9
+ message: string;
10
+ }>;
11
+ messages: string[];
12
+ }
File without changes
@@ -249,3 +249,21 @@ export interface Zone extends Resource<"cloudflare::Zone"> {
249
249
  * @see https://developers.cloudflare.com/dns/zone-setups/
250
250
  */
251
251
  export declare const Zone: (((this: any, id: string, props?: {}) => never) & (new (_: never) => never)) | ((this: Context<Zone>, id: string, props: ZoneProps) => Promise<Zone>);
252
+ /**
253
+ * Cloudflare Zone response format
254
+ */
255
+ export interface CloudflareZone {
256
+ id: string;
257
+ name: string;
258
+ type: "full" | "partial" | "secondary";
259
+ status: string;
260
+ paused: boolean;
261
+ account: {
262
+ id: string;
263
+ };
264
+ name_servers: string[];
265
+ original_name_servers: string[] | null;
266
+ created_on: string;
267
+ modified_on: string;
268
+ activated_on: string | null;
269
+ }
@@ -82,8 +82,8 @@ export const Zone = Resource("cloudflare::Zone", async function (id, props) {
82
82
  if (!response.ok) {
83
83
  throw new Error(`Error getting zone '${props.name}': ${response.statusText}`);
84
84
  }
85
- const result = (await response.json());
86
- const zoneData = result.result;
85
+ const zoneData = (await response.json())
86
+ .result;
87
87
  // Update zone settings if provided
88
88
  if (props.settings) {
89
89
  await updateZoneSettings(api, this.output.id, props.settings);
@@ -118,15 +118,28 @@ export const Zone = Resource("cloudflare::Zone", async function (id, props) {
118
118
  },
119
119
  });
120
120
  const body = await response.text();
121
+ let zoneData;
121
122
  if (!response.ok) {
122
123
  if (response.status === 400 && body.includes("already exists")) {
123
- // TODO: should we error?
124
+ // Zone already exists, fetch it instead
125
+ console.warn(`Zone '${props.name}' already exists during Zone create, adopting it...`);
126
+ const getResponse = await api.get(`/zones?name=${props.name}`);
127
+ if (!getResponse.ok) {
128
+ throw new Error(`Error fetching existing zone '${props.name}': ${getResponse.statusText}`);
129
+ }
130
+ const zones = (await getResponse.json()).result;
131
+ if (zones.length === 0) {
132
+ throw new Error(`Zone '${props.name}' does not exist, but the name is reserved for another user.`);
133
+ }
134
+ zoneData = zones[0];
124
135
  }
125
136
  else {
126
137
  throw new Error(`Error creating zone '${props.name}': ${response.statusText}\n${body}`);
127
138
  }
128
139
  }
129
- const zoneData = JSON.parse(body).result;
140
+ else {
141
+ zoneData = JSON.parse(body).result;
142
+ }
130
143
  // Update zone settings if provided
131
144
  if (props.settings) {
132
145
  await updateZoneSettings(api, zoneData.id, props.settings);
@@ -0,0 +1,7 @@
1
+ export type UpdateNameserversOptions = {
2
+ domain: string;
3
+ apiKey: string;
4
+ apiSecret: string;
5
+ nameservers: string[];
6
+ };
7
+ export declare function updateNameservers(options: UpdateNameserversOptions): Promise<void>;
@@ -0,0 +1,21 @@
1
+ export async function updateNameservers(options) {
2
+ const url = `https://api.godaddy.com/v1/domains/${options.domain}/nameservers`;
3
+ const response = await fetch(url, {
4
+ method: "PUT",
5
+ headers: {
6
+ Authorization: `sso-key ${options.apiKey}:${options.apiSecret}`,
7
+ "Content-Type": "application/json",
8
+ Accept: "application/json",
9
+ },
10
+ body: JSON.stringify({
11
+ nameservers: options.nameservers,
12
+ }),
13
+ });
14
+ if (response.ok) {
15
+ console.log(`✅ Nameservers updated for ${options.domain}`);
16
+ }
17
+ else {
18
+ const error = await response.json();
19
+ console.error(`❌ Failed to update nameservers:`, error);
20
+ }
21
+ }
@@ -0,0 +1,82 @@
1
+ import type { Context } from "../context";
2
+ import { Resource } from "../resource";
3
+ import { type DnsRecordType } from "./record";
4
+ /**
5
+ * DNS record response structure from Cloudflare DNS API
6
+ * with both data (original) and content (for compatibility)
7
+ */
8
+ export interface DnsApiRecord {
9
+ name: string;
10
+ type: DnsRecordType;
11
+ TTL: number;
12
+ data: string;
13
+ content: string;
14
+ ttl: number;
15
+ priority?: number;
16
+ }
17
+ /**
18
+ * Properties for importing DNS records
19
+ */
20
+ export interface ImportDnsRecordsProps {
21
+ /**
22
+ * The domain to fetch DNS records for
23
+ */
24
+ domain: string;
25
+ /**
26
+ * Specific record types to fetch. If not provided, defaults to all supported types.
27
+ */
28
+ recordTypes?: DnsRecordType[];
29
+ /**
30
+ * Bump the resource to force a new import
31
+ */
32
+ bump?: number;
33
+ }
34
+ /**
35
+ * Output returned after DNS records import
36
+ */
37
+ export interface ImportDnsRecords extends Resource<"dns::ImportDnsRecords">, ImportDnsRecordsProps {
38
+ /**
39
+ * The DNS records as a flat array, directly compatible with DnsRecords function
40
+ */
41
+ records: DnsApiRecord[];
42
+ /**
43
+ * Time at which the records were imported
44
+ */
45
+ importedAt: number;
46
+ }
47
+ /**
48
+ * Import DNS records for a domain using Cloudflare's DNS-over-HTTPS API.
49
+ * This resource allows you to fetch DNS records for a domain and store them
50
+ * in a structured format.
51
+ *
52
+ * @example
53
+ * // Import all default record types
54
+ * const allRecords = await ImportDnsRecords("example.com", {
55
+ * domain: "example.com"
56
+ * });
57
+ *
58
+ * @example
59
+ * // Import only specific record types
60
+ * const specificRecords = await ImportDnsRecords("example.com", {
61
+ * domain: "example.com",
62
+ * recordTypes: ["A", "MX"]
63
+ * });
64
+ *
65
+ * @example
66
+ * // Import DNS records and transfer them to a Cloudflare zone
67
+ * const dnsRecords = await ImportDnsRecords("dns-records", {
68
+ * domain: "example.com",
69
+ * });
70
+ *
71
+ * const zone = await Zone("example.com", {
72
+ * name: "example.com",
73
+ * type: "full",
74
+ * });
75
+ *
76
+ * // Records are directly compatible with DnsRecords function
77
+ * await DnsRecords("transfer-dns-records", {
78
+ * zoneId: zone.id,
79
+ * records: dnsRecords.records,
80
+ * });
81
+ */
82
+ export declare const ImportDnsRecords: (((this: any, id: string, props?: {}) => never) & (new (_: never) => never)) | ((this: Context<ImportDnsRecords>, id: string, props: ImportDnsRecordsProps) => Promise<ImportDnsRecords>);
@@ -0,0 +1,114 @@
1
+ import { Resource } from "../resource";
2
+ import { DEFAULT_RECORD_TYPES } from "./record";
3
+ /**
4
+ * Map numeric DNS record type to string type
5
+ */
6
+ function mapDnsRecordType(type) {
7
+ const typeMap = {
8
+ 1: "A",
9
+ 2: "NS",
10
+ 5: "CNAME",
11
+ 6: "SOA",
12
+ 15: "MX",
13
+ 16: "TXT",
14
+ 28: "AAAA",
15
+ 33: "SRV",
16
+ 12: "PTR",
17
+ };
18
+ return typeMap[type] || "A";
19
+ }
20
+ /**
21
+ * Import DNS records for a domain using Cloudflare's DNS-over-HTTPS API.
22
+ * This resource allows you to fetch DNS records for a domain and store them
23
+ * in a structured format.
24
+ *
25
+ * @example
26
+ * // Import all default record types
27
+ * const allRecords = await ImportDnsRecords("example.com", {
28
+ * domain: "example.com"
29
+ * });
30
+ *
31
+ * @example
32
+ * // Import only specific record types
33
+ * const specificRecords = await ImportDnsRecords("example.com", {
34
+ * domain: "example.com",
35
+ * recordTypes: ["A", "MX"]
36
+ * });
37
+ *
38
+ * @example
39
+ * // Import DNS records and transfer them to a Cloudflare zone
40
+ * const dnsRecords = await ImportDnsRecords("dns-records", {
41
+ * domain: "example.com",
42
+ * });
43
+ *
44
+ * const zone = await Zone("example.com", {
45
+ * name: "example.com",
46
+ * type: "full",
47
+ * });
48
+ *
49
+ * // Records are directly compatible with DnsRecords function
50
+ * await DnsRecords("transfer-dns-records", {
51
+ * zoneId: zone.id,
52
+ * records: dnsRecords.records,
53
+ * });
54
+ */
55
+ export const ImportDnsRecords = Resource("dns::ImportDnsRecords", async function (id, props) {
56
+ // For delete phase, just return destroyed state since this is a read-only resource
57
+ if (this.phase === "delete") {
58
+ return this.destroy();
59
+ }
60
+ const recordTypes = props.recordTypes || DEFAULT_RECORD_TYPES;
61
+ const allRecords = [];
62
+ for (const type of recordTypes) {
63
+ try {
64
+ const res = await fetch(`https://cloudflare-dns.com/dns-query?name=${props.domain}&type=${type}`, {
65
+ headers: {
66
+ accept: "application/dns-json",
67
+ },
68
+ });
69
+ if (!res.ok) {
70
+ throw new Error(`Failed to fetch ${type} records: ${res.statusText}`);
71
+ }
72
+ const data = (await res.json());
73
+ if (data.Answer) {
74
+ // Transform records to be compatible with DnsRecords function
75
+ const compatRecords = data.Answer.map((record) => {
76
+ const recordType = mapDnsRecordType(record.type);
77
+ const result = {
78
+ name: record.name,
79
+ type: recordType,
80
+ TTL: record.TTL,
81
+ data: record.data,
82
+ content: record.data, // Default mapping
83
+ ttl: record.TTL, // Normalized lowercase ttl
84
+ };
85
+ // Special handling for MX records to split priority and content
86
+ if (recordType === "MX") {
87
+ const parts = record.data.split(" ");
88
+ if (parts.length >= 2) {
89
+ const priority = Number.parseInt(parts[0], 10);
90
+ // Join the rest as hostname in case there are spaces
91
+ const hostname = parts.slice(1).join(" ");
92
+ if (!isNaN(priority)) {
93
+ result.priority = priority;
94
+ result.content = hostname;
95
+ }
96
+ }
97
+ }
98
+ return result;
99
+ });
100
+ allRecords.push(...compatRecords);
101
+ }
102
+ }
103
+ catch (error) {
104
+ console.warn(`Failed to fetch ${type} records for ${props.domain}:`, error);
105
+ }
106
+ }
107
+ // Return the resource with fetched records as a flat array
108
+ return this({
109
+ domain: props.domain,
110
+ recordTypes: [...recordTypes],
111
+ records: allRecords,
112
+ importedAt: Date.now(),
113
+ });
114
+ });
@@ -0,0 +1,2 @@
1
+ export * from "./import-dns";
2
+ export * from "./record";
@@ -0,0 +1,2 @@
1
+ export * from "./import-dns";
2
+ export * from "./record";
@@ -0,0 +1,88 @@
1
+ /**
2
+ * DNS record types supported across different providers
3
+ */
4
+ export type DnsRecordType = "A" | "AAAA" | "MX" | "TXT" | "NS" | "CNAME" | "SOA" | "SRV" | "PTR";
5
+ /**
6
+ * Default DNS record types to fetch
7
+ */
8
+ export declare const DEFAULT_RECORD_TYPES: readonly DnsRecordType[];
9
+ /**
10
+ * Base DNS record interface with common properties
11
+ */
12
+ export interface DnsRecordBase {
13
+ /**
14
+ * DNS record name (e.g., "example.com", "subdomain.example.com")
15
+ */
16
+ name: string;
17
+ /**
18
+ * Record type (A, AAAA, CNAME, etc.)
19
+ */
20
+ type: DnsRecordType;
21
+ }
22
+ /**
23
+ * DNS record response structure from DNS-over-HTTPS APIs
24
+ */
25
+ export interface DohDnsRecord extends DnsRecordBase {
26
+ /**
27
+ * Time To Live in seconds
28
+ */
29
+ TTL: number;
30
+ /**
31
+ * Record data/content (varies by record type)
32
+ */
33
+ data: string;
34
+ }
35
+ /**
36
+ * Comprehensive DNS record structure for creating/managing records
37
+ */
38
+ export interface DnsRecord extends DnsRecordBase {
39
+ /**
40
+ * DNS record content (e.g., IP address, hostname)
41
+ */
42
+ content: string;
43
+ /**
44
+ * Time To Live (TTL) in seconds
45
+ * Setting to 1 means 'automatic'
46
+ * Value must be between 60 and 86400, with minimum reduced to 30 for Enterprise zones
47
+ * @default 1
48
+ */
49
+ ttl?: number;
50
+ /**
51
+ * Whether the record is receiving proxied traffic through Cloudflare
52
+ * @default false
53
+ */
54
+ proxied?: boolean;
55
+ /**
56
+ * Comments or notes about the record
57
+ */
58
+ comment?: string;
59
+ /**
60
+ * Record tags
61
+ */
62
+ tags?: string[];
63
+ /**
64
+ * Priority value for MX/SRV records
65
+ */
66
+ priority?: number;
67
+ }
68
+ /**
69
+ * DNS record with provider-specific ID and metadata
70
+ */
71
+ export interface DnsRecordWithMetadata extends DnsRecord {
72
+ /**
73
+ * Record ID
74
+ */
75
+ id: string;
76
+ /**
77
+ * Zone or domain ID the record belongs to
78
+ */
79
+ zoneId: string;
80
+ /**
81
+ * Time at which the record was created
82
+ */
83
+ createdAt: number;
84
+ /**
85
+ * Time at which the record was last modified
86
+ */
87
+ modifiedAt?: number;
88
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Default DNS record types to fetch
3
+ */
4
+ export const DEFAULT_RECORD_TYPES = [
5
+ "A",
6
+ "AAAA",
7
+ "MX",
8
+ "TXT",
9
+ "NS",
10
+ "CNAME",
11
+ "SOA",
12
+ "SRV",
13
+ ];
@@ -0,0 +1,17 @@
1
+ import type { Scope } from "../scope";
2
+ import type { State, StateStore } from "../state";
3
+ export declare class FileSystemStateStore implements StateStore {
4
+ readonly scope: Scope;
5
+ readonly dir: string;
6
+ constructor(scope: Scope);
7
+ init(): Promise<void>;
8
+ deinit(): Promise<void>;
9
+ count(): Promise<number>;
10
+ list(): Promise<string[]>;
11
+ get(key: string): Promise<State | undefined>;
12
+ set(key: string, value: State): Promise<void>;
13
+ delete(key: string): Promise<void>;
14
+ all(): Promise<Record<string, State>>;
15
+ getBatch(ids: string[]): Promise<Record<string, State>>;
16
+ private getPath;
17
+ }