alchemy 0.50.0 → 0.51.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/lib/apply.js +10 -9
- package/lib/apply.js.map +1 -1
- package/lib/aws/control/types.d.ts +4 -4
- package/lib/cloudflare/api-gateway-operation.d.ts +141 -0
- package/lib/cloudflare/api-gateway-operation.d.ts.map +1 -0
- package/lib/cloudflare/api-gateway-operation.js +153 -0
- package/lib/cloudflare/api-gateway-operation.js.map +1 -0
- package/lib/cloudflare/api-mitigation.d.ts +19 -0
- package/lib/cloudflare/api-mitigation.d.ts.map +1 -0
- package/lib/cloudflare/api-mitigation.js +1 -0
- package/lib/cloudflare/api-mitigation.js.map +1 -0
- package/lib/cloudflare/api-schema.d.ts +137 -0
- package/lib/cloudflare/api-schema.d.ts.map +1 -0
- package/lib/cloudflare/api-schema.js +197 -0
- package/lib/cloudflare/api-schema.js.map +1 -0
- package/lib/cloudflare/api-shield.d.ts +366 -0
- package/lib/cloudflare/api-shield.d.ts.map +1 -0
- package/lib/cloudflare/api-shield.js +427 -0
- package/lib/cloudflare/api-shield.js.map +1 -0
- package/lib/cloudflare/certificate-pack.d.ts +1 -1
- package/lib/cloudflare/certificate-pack.d.ts.map +1 -1
- package/lib/cloudflare/certificate-pack.js +18 -88
- package/lib/cloudflare/certificate-pack.js.map +1 -1
- package/lib/cloudflare/compatibility-date.gen.d.ts +1 -1
- package/lib/cloudflare/compatibility-date.gen.js +1 -1
- package/lib/cloudflare/index.d.ts +3 -0
- package/lib/cloudflare/index.d.ts.map +1 -1
- package/lib/cloudflare/index.js +3 -0
- package/lib/cloudflare/index.js.map +1 -1
- package/lib/cloudflare/zone.d.ts +12 -0
- package/lib/cloudflare/zone.d.ts.map +1 -1
- package/lib/cloudflare/zone.js +35 -0
- package/lib/cloudflare/zone.js.map +1 -1
- package/lib/destroy.d.ts.map +1 -1
- package/lib/destroy.js +26 -18
- package/lib/destroy.js.map +1 -1
- package/lib/scope.d.ts.map +1 -1
- package/lib/scope.js +2 -2
- package/lib/scope.js.map +1 -1
- package/package.json +2 -1
- package/src/apply.ts +11 -12
- package/src/aws/control/types.ts +4 -4
- package/src/cloudflare/api-gateway-operation.ts +340 -0
- package/src/cloudflare/api-mitigation.ts +22 -0
- package/src/cloudflare/api-schema.ts +364 -0
- package/src/cloudflare/api-shield.ts +676 -0
- package/src/cloudflare/certificate-pack.ts +28 -152
- package/src/cloudflare/compatibility-date.gen.ts +1 -1
- package/src/cloudflare/index.ts +3 -0
- package/src/cloudflare/zone.ts +52 -0
- package/src/destroy.ts +26 -20
- package/src/scope.ts +3 -3
- package/templates/tanstack-start/package.json +3 -3
- package/templates/tanstack-start/vite.config.ts +2 -6
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
type CloudflareApi,
|
|
8
8
|
type CloudflareApiOptions,
|
|
9
9
|
} from "./api.ts";
|
|
10
|
-
import type
|
|
10
|
+
import { findZoneForHostname, type Zone } from "./zone.ts";
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Certificate Authority options for Advanced Certificate Packs
|
|
@@ -298,8 +298,18 @@ export const CertificatePack = Resource(
|
|
|
298
298
|
const deleteResponse = await api.delete(
|
|
299
299
|
`/zones/${zoneId}/ssl/certificate_packs/${this.output.id}`,
|
|
300
300
|
);
|
|
301
|
-
|
|
302
|
-
|
|
301
|
+
const deleteResponseJson = (await deleteResponse.json()) as {
|
|
302
|
+
errors: Array<{ code: number }>;
|
|
303
|
+
};
|
|
304
|
+
const isCurrentlyDeleting =
|
|
305
|
+
deleteResponse.status === 400 &&
|
|
306
|
+
deleteResponseJson.errors[0].code === 1406;
|
|
307
|
+
|
|
308
|
+
if (
|
|
309
|
+
!deleteResponse.ok &&
|
|
310
|
+
deleteResponse.status !== 404 &&
|
|
311
|
+
!isCurrentlyDeleting
|
|
312
|
+
) {
|
|
303
313
|
await handleApiError(
|
|
304
314
|
deleteResponse,
|
|
305
315
|
"delete",
|
|
@@ -314,96 +324,7 @@ export const CertificatePack = Resource(
|
|
|
314
324
|
}
|
|
315
325
|
|
|
316
326
|
if (this.phase === "update" && this.output?.id) {
|
|
317
|
-
|
|
318
|
-
const currentPack = this.output;
|
|
319
|
-
|
|
320
|
-
if (props.certificateAuthority !== currentPack.certificateAuthority) {
|
|
321
|
-
throw new Error(
|
|
322
|
-
`Cannot change certificateAuthority from '${currentPack.certificateAuthority}' to '${props.certificateAuthority}'. Certificate Authority is immutable after creation. You must delete and recreate the certificate pack.`,
|
|
323
|
-
);
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
if (
|
|
327
|
-
JSON.stringify(props.hosts.sort()) !==
|
|
328
|
-
JSON.stringify(currentPack.hosts.sort())
|
|
329
|
-
) {
|
|
330
|
-
throw new Error(
|
|
331
|
-
`Cannot change hosts from [${currentPack.hosts.join(", ")}] to [${props.hosts.join(", ")}]. Hosts are immutable after creation. You must delete and recreate the certificate pack.`,
|
|
332
|
-
);
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
if (props.validationMethod !== currentPack.validationMethod) {
|
|
336
|
-
throw new Error(
|
|
337
|
-
`Cannot change validationMethod from '${currentPack.validationMethod}' to '${props.validationMethod}'. Validation method is immutable after creation. You must delete and recreate the certificate pack.`,
|
|
338
|
-
);
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
if (props.validityDays !== currentPack.validityDays) {
|
|
342
|
-
throw new Error(
|
|
343
|
-
`Cannot change validityDays from ${currentPack.validityDays} to ${props.validityDays}. Validity period is immutable after creation. You must delete and recreate the certificate pack.`,
|
|
344
|
-
);
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
const type = props.type || "advanced";
|
|
348
|
-
if (type !== currentPack.type) {
|
|
349
|
-
throw new Error(
|
|
350
|
-
`Cannot change type from '${currentPack.type}' to '${type}'. Type is immutable after creation. You must delete and recreate the certificate pack.`,
|
|
351
|
-
);
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
// Only cloudflareBranding can be updated
|
|
355
|
-
if (props.cloudflareBranding !== currentPack.cloudflareBranding) {
|
|
356
|
-
logger.log(
|
|
357
|
-
`Updating certificate pack cloudflare branding from ${currentPack.cloudflareBranding} to ${props.cloudflareBranding}`,
|
|
358
|
-
);
|
|
359
|
-
|
|
360
|
-
const updateResponse = await api.patch(
|
|
361
|
-
`/zones/${zoneId}/ssl/certificate_packs/${this.output.id}`,
|
|
362
|
-
{
|
|
363
|
-
cloudflare_branding: props.cloudflareBranding || false,
|
|
364
|
-
},
|
|
365
|
-
);
|
|
366
|
-
|
|
367
|
-
if (!updateResponse.ok) {
|
|
368
|
-
await handleApiError(
|
|
369
|
-
updateResponse,
|
|
370
|
-
"update",
|
|
371
|
-
"certificate pack",
|
|
372
|
-
this.output.id,
|
|
373
|
-
);
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
// Get updated certificate pack details
|
|
378
|
-
const response = await api.get(
|
|
379
|
-
`/zones/${zoneId}/ssl/certificate_packs/${this.output.id}`,
|
|
380
|
-
);
|
|
381
|
-
|
|
382
|
-
if (!response.ok) {
|
|
383
|
-
await handleApiError(
|
|
384
|
-
response,
|
|
385
|
-
"get",
|
|
386
|
-
"certificate pack",
|
|
387
|
-
this.output.id,
|
|
388
|
-
);
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
const updatedPack = (
|
|
392
|
-
(await response.json()) as { result: CloudflareCertificatePack }
|
|
393
|
-
).result;
|
|
394
|
-
|
|
395
|
-
return this({
|
|
396
|
-
id: updatedPack.id,
|
|
397
|
-
certificateAuthority: updatedPack.certificate_authority,
|
|
398
|
-
cloudflareBranding: updatedPack.cloudflare_branding,
|
|
399
|
-
hosts: updatedPack.hosts,
|
|
400
|
-
status: updatedPack.status,
|
|
401
|
-
type: updatedPack.type,
|
|
402
|
-
validationMethod: updatedPack.validation_method,
|
|
403
|
-
validityDays: updatedPack.validity_days,
|
|
404
|
-
zoneId: updatedPack.zone_id,
|
|
405
|
-
zoneName: zoneName,
|
|
406
|
-
});
|
|
327
|
+
this.replace();
|
|
407
328
|
}
|
|
408
329
|
|
|
409
330
|
// Create new certificate pack
|
|
@@ -438,13 +359,13 @@ export const CertificatePack = Resource(
|
|
|
438
359
|
return this({
|
|
439
360
|
id: existingPack.id,
|
|
440
361
|
certificateAuthority: existingPack.certificate_authority,
|
|
441
|
-
cloudflareBranding:
|
|
362
|
+
cloudflareBranding: props.cloudflareBranding ?? false,
|
|
442
363
|
hosts: existingPack.hosts,
|
|
443
364
|
status: existingPack.status,
|
|
444
365
|
type: existingPack.type,
|
|
445
366
|
validationMethod: existingPack.validation_method,
|
|
446
367
|
validityDays: existingPack.validity_days,
|
|
447
|
-
zoneId
|
|
368
|
+
zoneId,
|
|
448
369
|
zoneName: zoneName,
|
|
449
370
|
});
|
|
450
371
|
}
|
|
@@ -498,13 +419,13 @@ export const CertificatePack = Resource(
|
|
|
498
419
|
return this({
|
|
499
420
|
id: createdPack.id,
|
|
500
421
|
certificateAuthority: createdPack.certificate_authority,
|
|
501
|
-
cloudflareBranding:
|
|
422
|
+
cloudflareBranding: props.cloudflareBranding ?? false,
|
|
502
423
|
hosts: createdPack.hosts,
|
|
503
424
|
status: createdPack.status,
|
|
504
425
|
type: createdPack.type,
|
|
505
426
|
validationMethod: createdPack.validation_method,
|
|
506
427
|
validityDays: createdPack.validity_days,
|
|
507
|
-
zoneId
|
|
428
|
+
zoneId,
|
|
508
429
|
zoneName: zoneName,
|
|
509
430
|
});
|
|
510
431
|
},
|
|
@@ -516,13 +437,11 @@ export const CertificatePack = Resource(
|
|
|
516
437
|
interface CloudflareCertificatePack {
|
|
517
438
|
id: string;
|
|
518
439
|
certificate_authority: CertificateAuthority;
|
|
519
|
-
cloudflare_branding: boolean;
|
|
520
440
|
hosts: string[];
|
|
521
441
|
status: CertificatePackStatus;
|
|
522
442
|
type: "advanced";
|
|
523
443
|
validation_method: ValidationMethod;
|
|
524
444
|
validity_days: ValidityDays;
|
|
525
|
-
zone_id: string;
|
|
526
445
|
}
|
|
527
446
|
|
|
528
447
|
/**
|
|
@@ -591,58 +510,6 @@ export async function waitForCertificatePackActive(
|
|
|
591
510
|
);
|
|
592
511
|
}
|
|
593
512
|
|
|
594
|
-
/**
|
|
595
|
-
* Helper function to find zone ID from a hostname
|
|
596
|
-
* Searches for the zone that matches the hostname or its parent domains
|
|
597
|
-
*
|
|
598
|
-
* @param api CloudflareApi instance
|
|
599
|
-
* @param hostname The hostname to find the zone for
|
|
600
|
-
* @returns Promise resolving to the zone ID and zone name
|
|
601
|
-
*/
|
|
602
|
-
async function findZoneForHostname(
|
|
603
|
-
api: CloudflareApi,
|
|
604
|
-
hostname: string,
|
|
605
|
-
): Promise<{ zoneId: string; zoneName: string }> {
|
|
606
|
-
// Remove wildcard prefix if present
|
|
607
|
-
const cleanHostname = hostname.replace(/^\*\./, "");
|
|
608
|
-
|
|
609
|
-
// Get all zones and find the best match
|
|
610
|
-
const response = await api.get("/zones");
|
|
611
|
-
|
|
612
|
-
if (!response.ok) {
|
|
613
|
-
throw new Error(`Failed to list zones: ${response.statusText}`);
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
const zonesData = (await response.json()) as {
|
|
617
|
-
result: Array<{ id: string; name: string }>;
|
|
618
|
-
};
|
|
619
|
-
|
|
620
|
-
// Find the zone that best matches the hostname
|
|
621
|
-
// We look for the longest matching zone name (most specific)
|
|
622
|
-
let bestMatch: { zoneId: string; zoneName: string } | null = null;
|
|
623
|
-
let longestMatch = 0;
|
|
624
|
-
|
|
625
|
-
for (const zone of zonesData.result) {
|
|
626
|
-
if (
|
|
627
|
-
cleanHostname === zone.name ||
|
|
628
|
-
cleanHostname.endsWith(`.${zone.name}`)
|
|
629
|
-
) {
|
|
630
|
-
if (zone.name.length > longestMatch) {
|
|
631
|
-
longestMatch = zone.name.length;
|
|
632
|
-
bestMatch = { zoneId: zone.id, zoneName: zone.name };
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
if (!bestMatch) {
|
|
638
|
-
throw new Error(
|
|
639
|
-
`Could not find zone for hostname '${hostname}'. Available zones: ${zonesData.result.map((z) => z.name).join(", ")}`,
|
|
640
|
-
);
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
return bestMatch;
|
|
644
|
-
}
|
|
645
|
-
|
|
646
513
|
/**
|
|
647
514
|
* Helper function to find existing certificate packs that match the given configuration
|
|
648
515
|
* Used to adopt existing certificates instead of creating duplicates
|
|
@@ -657,7 +524,9 @@ async function findMatchingCertificatePack(
|
|
|
657
524
|
zoneId: string,
|
|
658
525
|
props: CertificatePackProps,
|
|
659
526
|
): Promise<CloudflareCertificatePack | null> {
|
|
660
|
-
const response = await api.get(
|
|
527
|
+
const response = await api.get(
|
|
528
|
+
`/zones/${zoneId}/ssl/certificate_packs?status=all`,
|
|
529
|
+
);
|
|
661
530
|
|
|
662
531
|
if (!response.ok) {
|
|
663
532
|
throw new Error(`Failed to list certificate packs: ${response.statusText}`);
|
|
@@ -686,6 +555,13 @@ async function findMatchingCertificatePack(
|
|
|
686
555
|
const allHostsCovered = props.hosts.every((host) => packHosts.has(host));
|
|
687
556
|
|
|
688
557
|
if (allHostsCovered) {
|
|
558
|
+
// If Cloudflare branding is enabled, ensure that the cert pack is cloudflare branded`
|
|
559
|
+
if (
|
|
560
|
+
props.cloudflareBranding &&
|
|
561
|
+
!pack.hosts.some((host) => host.endsWith("sni.cloudflaressl.com"))
|
|
562
|
+
) {
|
|
563
|
+
continue;
|
|
564
|
+
}
|
|
689
565
|
logger.log(
|
|
690
566
|
`Found existing certificate pack ${pack.id} that covers all requested hosts`,
|
|
691
567
|
);
|
package/src/cloudflare/index.ts
CHANGED
|
@@ -4,6 +4,9 @@ export * from "./ai-gateway.ts";
|
|
|
4
4
|
export * from "./ai.ts";
|
|
5
5
|
export * from "./analytics-engine.ts";
|
|
6
6
|
export * from "./api-error.ts";
|
|
7
|
+
export * from "./api-gateway-operation.ts";
|
|
8
|
+
export * from "./api-schema.ts";
|
|
9
|
+
export * from "./api-shield.ts";
|
|
7
10
|
export * from "./api.ts";
|
|
8
11
|
export * from "./assets.ts";
|
|
9
12
|
export * from "./astro.ts";
|
package/src/cloudflare/zone.ts
CHANGED
|
@@ -610,3 +610,55 @@ export interface CloudflareZone {
|
|
|
610
610
|
modified_on: string;
|
|
611
611
|
activated_on: string | null;
|
|
612
612
|
}
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* Helper function to find zone ID from a hostname
|
|
616
|
+
* Searches for the zone that matches the hostname or its parent domains
|
|
617
|
+
*
|
|
618
|
+
* @param api CloudflareApi instance
|
|
619
|
+
* @param hostname The hostname to find the zone for
|
|
620
|
+
* @returns Promise resolving to the zone ID and zone name
|
|
621
|
+
*/
|
|
622
|
+
export async function findZoneForHostname(
|
|
623
|
+
api: CloudflareApi,
|
|
624
|
+
hostname: string,
|
|
625
|
+
): Promise<{ zoneId: string; zoneName: string }> {
|
|
626
|
+
// Remove wildcard prefix if present
|
|
627
|
+
const cleanHostname = hostname.replace(/^\*\./, "");
|
|
628
|
+
|
|
629
|
+
// Get all zones and find the best match
|
|
630
|
+
const response = await api.get("/zones");
|
|
631
|
+
|
|
632
|
+
if (!response.ok) {
|
|
633
|
+
throw new Error(`Failed to list zones: ${response.statusText}`);
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
const zonesData = (await response.json()) as {
|
|
637
|
+
result: Array<{ id: string; name: string }>;
|
|
638
|
+
};
|
|
639
|
+
|
|
640
|
+
// Find the zone that best matches the hostname
|
|
641
|
+
// We look for the longest matching zone name (most specific)
|
|
642
|
+
let bestMatch: { zoneId: string; zoneName: string } | null = null;
|
|
643
|
+
let longestMatch = 0;
|
|
644
|
+
|
|
645
|
+
for (const zone of zonesData.result) {
|
|
646
|
+
if (
|
|
647
|
+
cleanHostname === zone.name ||
|
|
648
|
+
cleanHostname.endsWith(`.${zone.name}`)
|
|
649
|
+
) {
|
|
650
|
+
if (zone.name.length > longestMatch) {
|
|
651
|
+
longestMatch = zone.name.length;
|
|
652
|
+
bestMatch = { zoneId: zone.id, zoneName: zone.name };
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
if (!bestMatch) {
|
|
658
|
+
throw new Error(
|
|
659
|
+
`Could not find zone for hostname '${hostname}'. Available zones: ${zonesData.result.map((z) => z.name).join(", ")}`,
|
|
660
|
+
);
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
return bestMatch;
|
|
664
|
+
}
|
package/src/destroy.ts
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
ResourceSeq,
|
|
12
12
|
} from "./resource.ts";
|
|
13
13
|
import { isScope, type PendingDeletions, Scope } from "./scope.ts";
|
|
14
|
+
import type { State } from "./state.ts";
|
|
14
15
|
import { formatFQN } from "./util/cli.ts";
|
|
15
16
|
import { logger } from "./util/logger.ts";
|
|
16
17
|
|
|
@@ -108,12 +109,29 @@ export async function destroy<Type extends string>(
|
|
|
108
109
|
});
|
|
109
110
|
}
|
|
110
111
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
if (
|
|
114
|
-
|
|
112
|
+
let state: State;
|
|
113
|
+
let props: ResourceProps | undefined;
|
|
114
|
+
if (options?.replace) {
|
|
115
|
+
props = options.replace.props;
|
|
116
|
+
state = {
|
|
117
|
+
output: options.replace.output!,
|
|
118
|
+
status: "deleting",
|
|
119
|
+
oldProps: options.replace.props,
|
|
120
|
+
data: {},
|
|
121
|
+
kind: instance[ResourceKind],
|
|
122
|
+
id: instance[ResourceID],
|
|
123
|
+
fqn: instance[ResourceFQN],
|
|
124
|
+
seq: instance[ResourceSeq],
|
|
125
|
+
props,
|
|
126
|
+
};
|
|
127
|
+
} else {
|
|
128
|
+
const _state = await scope.state.get(instance[ResourceID]);
|
|
129
|
+
if (_state === undefined) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
state = _state;
|
|
133
|
+
props = state.props;
|
|
115
134
|
}
|
|
116
|
-
|
|
117
135
|
const ctx = context({
|
|
118
136
|
scope,
|
|
119
137
|
phase: "delete",
|
|
@@ -121,20 +139,8 @@ export async function destroy<Type extends string>(
|
|
|
121
139
|
id: instance[ResourceID],
|
|
122
140
|
fqn: instance[ResourceFQN],
|
|
123
141
|
seq: instance[ResourceSeq],
|
|
124
|
-
props
|
|
125
|
-
state
|
|
126
|
-
? {
|
|
127
|
-
output: options.replace.output,
|
|
128
|
-
status: "deleting",
|
|
129
|
-
oldProps: options.replace.props,
|
|
130
|
-
data: {},
|
|
131
|
-
kind: instance[ResourceKind],
|
|
132
|
-
id: instance[ResourceID],
|
|
133
|
-
fqn: instance[ResourceFQN],
|
|
134
|
-
seq: instance[ResourceSeq],
|
|
135
|
-
props: options.replace.props,
|
|
136
|
-
}
|
|
137
|
-
: state,
|
|
142
|
+
props,
|
|
143
|
+
state,
|
|
138
144
|
// TODO(sam|michael): should this always be false or !!options?.replace
|
|
139
145
|
isReplacement: false,
|
|
140
146
|
replace: () => {
|
|
@@ -157,7 +163,7 @@ export async function destroy<Type extends string>(
|
|
|
157
163
|
nestedScope = options?.replace?.props == null ? scope : undefined;
|
|
158
164
|
return await Provider.handler.bind(ctx)(
|
|
159
165
|
instance[ResourceID],
|
|
160
|
-
|
|
166
|
+
ctx.props,
|
|
161
167
|
);
|
|
162
168
|
},
|
|
163
169
|
);
|
package/src/scope.ts
CHANGED
|
@@ -142,7 +142,6 @@ export class Scope {
|
|
|
142
142
|
private isSkipped = false;
|
|
143
143
|
private finalized = false;
|
|
144
144
|
private startedAt = performance.now();
|
|
145
|
-
|
|
146
145
|
private deferred: (() => Promise<any>)[] = [];
|
|
147
146
|
|
|
148
147
|
public get appName(): string {
|
|
@@ -345,13 +344,13 @@ export class Scope {
|
|
|
345
344
|
}
|
|
346
345
|
|
|
347
346
|
public async set<T>(key: string, value: T): Promise<void> {
|
|
348
|
-
|
|
347
|
+
await this.withScopeState<void>(async (state, persist) => {
|
|
349
348
|
state.data[key] = value;
|
|
350
349
|
await persist(state); // only one line to save!
|
|
351
350
|
});
|
|
352
351
|
}
|
|
353
352
|
|
|
354
|
-
public
|
|
353
|
+
public get<T>(key: string): Promise<T> {
|
|
355
354
|
return this.withScopeState<T>(async (state) => state.data[key]);
|
|
356
355
|
}
|
|
357
356
|
|
|
@@ -475,6 +474,7 @@ export class Scope {
|
|
|
475
474
|
}
|
|
476
475
|
throw e;
|
|
477
476
|
})) ?? [];
|
|
477
|
+
|
|
478
478
|
//todo(michael): remove once we deprecate doss; see: https://github.com/sam-goodwin/alchemy/issues/585
|
|
479
479
|
let hasCorruptedResources = false;
|
|
480
480
|
if (pendingDeletions) {
|
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
"start": "node .output/server/index.mjs"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@tanstack/react-router": "^1.
|
|
15
|
-
"@tanstack/react-router-devtools": "^1.
|
|
16
|
-
"@tanstack/react-start": "^1.
|
|
14
|
+
"@tanstack/react-router": "^1.129.2",
|
|
15
|
+
"@tanstack/react-router-devtools": "^1.129.2",
|
|
16
|
+
"@tanstack/react-start": "^1.129.2",
|
|
17
17
|
"react": "^19.0.0",
|
|
18
18
|
"react-dom": "^19.0.0",
|
|
19
19
|
"tailwind-merge": "3",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import tailwindcss from "@tailwindcss/vite";
|
|
2
2
|
import { tanstackStart } from "@tanstack/react-start/plugin/vite";
|
|
3
|
+
import viteReact from "@vitejs/plugin-react";
|
|
3
4
|
import { cloudflareWorkersDevEnvironmentShim } from "alchemy/cloudflare";
|
|
4
5
|
import { defineConfig, PluginOption } from "vite";
|
|
5
6
|
import tsConfigPaths from "vite-tsconfig-paths";
|
|
@@ -23,12 +24,7 @@ export default defineConfig({
|
|
|
23
24
|
tanstackStart({
|
|
24
25
|
target: "cloudflare-module",
|
|
25
26
|
customViteReactPlugin: true,
|
|
26
|
-
tsr: {
|
|
27
|
-
routeTreeFileHeader: [
|
|
28
|
-
"/** biome-ignore-all lint/suspicious/noExplicitAny: code generated by @tanstack/react-start */",
|
|
29
|
-
],
|
|
30
|
-
quoteStyle: "double",
|
|
31
|
-
},
|
|
32
27
|
}),
|
|
28
|
+
viteReact(),
|
|
33
29
|
],
|
|
34
30
|
});
|