@alchemy.run/fly 2.0.0-beta.84 → 2.0.0-beta.86
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/App.d.ts +27 -5
- package/lib/App.d.ts.map +1 -1
- package/lib/App.js +29 -8
- package/lib/App.js.map +1 -1
- package/lib/AuthProvider.d.ts +1 -1
- package/lib/AuthProvider.d.ts.map +1 -1
- package/lib/AuthProvider.js.map +1 -1
- package/lib/BindService.d.ts +7 -7
- package/lib/BindService.d.ts.map +1 -1
- package/lib/BindService.js +21 -28
- package/lib/BindService.js.map +1 -1
- package/lib/Bootstrap.d.ts.map +1 -1
- package/lib/Bootstrap.js +2 -1
- package/lib/Bootstrap.js.map +1 -1
- package/lib/Bucket.d.ts +38 -4
- package/lib/Bucket.d.ts.map +1 -1
- package/lib/Bucket.js +190 -37
- package/lib/Bucket.js.map +1 -1
- package/lib/BucketBinding.d.ts.map +1 -1
- package/lib/BucketBinding.js +1 -1
- package/lib/BucketBinding.js.map +1 -1
- package/lib/Catalog.d.ts.map +1 -1
- package/lib/Catalog.js.map +1 -1
- package/lib/Certificate.d.ts +25 -1
- package/lib/Certificate.d.ts.map +1 -1
- package/lib/Certificate.js +22 -4
- package/lib/Certificate.js.map +1 -1
- package/lib/Checkpoint.d.ts.map +1 -1
- package/lib/Checkpoint.js.map +1 -1
- package/lib/CheckpointHttp.d.ts.map +1 -1
- package/lib/CheckpointHttp.js.map +1 -1
- package/lib/ConnectPostgres.d.ts +1 -1
- package/lib/ConnectPostgres.d.ts.map +1 -1
- package/lib/ConnectPostgres.js.map +1 -1
- package/lib/ConnectPostgresHttp.d.ts.map +1 -1
- package/lib/ConnectPostgresHttp.js +1 -1
- package/lib/ConnectPostgresHttp.js.map +1 -1
- package/lib/Contributions.d.ts +1 -1
- package/lib/Contributions.d.ts.map +1 -1
- package/lib/Contributions.js.map +1 -1
- package/lib/Credentials.d.ts +5 -4
- package/lib/Credentials.d.ts.map +1 -1
- package/lib/Credentials.js +18 -18
- package/lib/Credentials.js.map +1 -1
- package/lib/Decrypt.d.ts.map +1 -1
- package/lib/Decrypt.js.map +1 -1
- package/lib/DecryptHttp.d.ts.map +1 -1
- package/lib/DecryptHttp.js.map +1 -1
- package/lib/DeleteObject.d.ts.map +1 -1
- package/lib/DeleteObject.js.map +1 -1
- package/lib/DeleteObjectHttp.d.ts.map +1 -1
- package/lib/DeleteObjectHttp.js.map +1 -1
- package/lib/Deployment.d.ts.map +1 -1
- package/lib/Deployment.js.map +1 -1
- package/lib/DeploymentImages.d.ts.map +1 -1
- package/lib/DeploymentImages.js +1 -1
- package/lib/DeploymentImages.js.map +1 -1
- package/lib/DeploymentState.d.ts.map +1 -1
- package/lib/DeploymentState.js.map +1 -1
- package/lib/Encrypt.d.ts.map +1 -1
- package/lib/Encrypt.js.map +1 -1
- package/lib/EncryptHttp.d.ts.map +1 -1
- package/lib/EncryptHttp.js.map +1 -1
- package/lib/Environment.d.ts.map +1 -1
- package/lib/Environment.js.map +1 -1
- package/lib/Errors.d.ts +1 -1
- package/lib/Errors.d.ts.map +1 -1
- package/lib/Errors.js.map +1 -1
- package/lib/Exec.d.ts.map +1 -1
- package/lib/Exec.js.map +1 -1
- package/lib/ExecHttp.d.ts.map +1 -1
- package/lib/ExecHttp.js.map +1 -1
- package/lib/GetObject.d.ts.map +1 -1
- package/lib/GetObject.js.map +1 -1
- package/lib/GetObjectHttp.d.ts.map +1 -1
- package/lib/GetObjectHttp.js.map +1 -1
- package/lib/GetSecret.d.ts.map +1 -1
- package/lib/GetSecret.js.map +1 -1
- package/lib/GetSecretHttp.d.ts.map +1 -1
- package/lib/GetSecretHttp.js.map +1 -1
- package/lib/HeadObject.d.ts.map +1 -1
- package/lib/HeadObject.js.map +1 -1
- package/lib/HeadObjectHttp.d.ts.map +1 -1
- package/lib/HeadObjectHttp.js.map +1 -1
- package/lib/IpAssignment.d.ts +24 -1
- package/lib/IpAssignment.d.ts.map +1 -1
- package/lib/IpAssignment.js +21 -4
- package/lib/IpAssignment.js.map +1 -1
- package/lib/ListObjectsV2.d.ts.map +1 -1
- package/lib/ListObjectsV2.js.map +1 -1
- package/lib/ListObjectsV2Http.d.ts.map +1 -1
- package/lib/ListObjectsV2Http.js.map +1 -1
- package/lib/ListSecrets.d.ts.map +1 -1
- package/lib/ListSecrets.js.map +1 -1
- package/lib/ListSecretsHttp.d.ts.map +1 -1
- package/lib/ListSecretsHttp.js.map +1 -1
- package/lib/Machine.d.ts +27 -2
- package/lib/Machine.d.ts.map +1 -1
- package/lib/Machine.js +18 -3
- package/lib/Machine.js.map +1 -1
- package/lib/MachineContainers.d.ts.map +1 -1
- package/lib/MachineContainers.js.map +1 -1
- package/lib/Metadata.d.ts.map +1 -1
- package/lib/Metadata.js.map +1 -1
- package/lib/MountVolume.d.ts +7 -2
- package/lib/MountVolume.d.ts.map +1 -1
- package/lib/MountVolume.js.map +1 -1
- package/lib/Postgres.d.ts +34 -2
- package/lib/Postgres.d.ts.map +1 -1
- package/lib/Postgres.js +26 -6
- package/lib/Postgres.js.map +1 -1
- package/lib/PostgresMigrations.d.ts +7 -22
- package/lib/PostgresMigrations.d.ts.map +1 -1
- package/lib/PostgresMigrations.js +37 -89
- package/lib/PostgresMigrations.js.map +1 -1
- package/lib/Providers.d.ts +1 -1
- package/lib/Providers.d.ts.map +1 -1
- package/lib/Providers.js.map +1 -1
- package/lib/PutObject.d.ts.map +1 -1
- package/lib/PutObject.js.map +1 -1
- package/lib/PutObjectHttp.d.ts.map +1 -1
- package/lib/PutObjectHttp.js.map +1 -1
- package/lib/ReadRedis.d.ts.map +1 -1
- package/lib/ReadRedis.js.map +1 -1
- package/lib/ReadRedisHttp.d.ts.map +1 -1
- package/lib/ReadRedisHttp.js.map +1 -1
- package/lib/ReadWriteRedis.d.ts.map +1 -1
- package/lib/ReadWriteRedis.js.map +1 -1
- package/lib/ReadWriteRedisHttp.d.ts.map +1 -1
- package/lib/ReadWriteRedisHttp.js.map +1 -1
- package/lib/Redis.d.ts +23 -3
- package/lib/Redis.d.ts.map +1 -1
- package/lib/Redis.js +37 -6
- package/lib/Redis.js.map +1 -1
- package/lib/RedisBinding.d.ts.map +1 -1
- package/lib/RedisBinding.js +1 -1
- package/lib/RedisBinding.js.map +1 -1
- package/lib/RedisHttp.d.ts +1 -1
- package/lib/RedisHttp.d.ts.map +1 -1
- package/lib/RedisHttp.js +4 -1
- package/lib/RedisHttp.js.map +1 -1
- package/lib/Region.d.ts.map +1 -1
- package/lib/Region.js.map +1 -1
- package/lib/Secret.d.ts +23 -2
- package/lib/Secret.d.ts.map +1 -1
- package/lib/Secret.js +17 -2
- package/lib/Secret.js.map +1 -1
- package/lib/SecretHttp.d.ts.map +1 -1
- package/lib/SecretHttp.js.map +1 -1
- package/lib/SecretKey.d.ts +23 -2
- package/lib/SecretKey.d.ts.map +1 -1
- package/lib/SecretKey.js +17 -2
- package/lib/SecretKey.js.map +1 -1
- package/lib/SecretKeyHttp.d.ts.map +1 -1
- package/lib/SecretKeyHttp.js.map +1 -1
- package/lib/Service.d.ts +104 -34
- package/lib/Service.d.ts.map +1 -1
- package/lib/Service.js +301 -54
- package/lib/Service.js.map +1 -1
- package/lib/Sign.d.ts.map +1 -1
- package/lib/Sign.js.map +1 -1
- package/lib/SignHttp.d.ts.map +1 -1
- package/lib/SignHttp.js.map +1 -1
- package/lib/Sprite.d.ts +24 -5
- package/lib/Sprite.d.ts.map +1 -1
- package/lib/Sprite.js +22 -5
- package/lib/Sprite.js.map +1 -1
- package/lib/SpriteHttp.d.ts.map +1 -1
- package/lib/SpriteHttp.js.map +1 -1
- package/lib/Verify.d.ts.map +1 -1
- package/lib/Verify.js.map +1 -1
- package/lib/VerifyHttp.d.ts.map +1 -1
- package/lib/VerifyHttp.js.map +1 -1
- package/lib/Volume.d.ts +4 -4
- package/lib/Volume.d.ts.map +1 -1
- package/lib/Volume.js +21 -5
- package/lib/Volume.js.map +1 -1
- package/lib/VolumeSnapshot.d.ts +32 -1
- package/lib/VolumeSnapshot.d.ts.map +1 -1
- package/lib/VolumeSnapshot.js +24 -1
- package/lib/VolumeSnapshot.js.map +1 -1
- package/lib/Website/AssetDeployment.d.ts.map +1 -1
- package/lib/Website/AssetDeployment.js +9 -9
- package/lib/Website/AssetDeployment.js.map +1 -1
- package/lib/Website/Astro.d.ts +8 -6
- package/lib/Website/Astro.d.ts.map +1 -1
- package/lib/Website/Astro.js +12 -13
- package/lib/Website/Astro.js.map +1 -1
- package/lib/Website/Foldkit.d.ts.map +1 -1
- package/lib/Website/Foldkit.js.map +1 -1
- package/lib/Website/FrameworkSite.d.ts +39 -2
- package/lib/Website/FrameworkSite.d.ts.map +1 -1
- package/lib/Website/FrameworkSite.js +46 -3
- package/lib/Website/FrameworkSite.js.map +1 -1
- package/lib/Website/Nextjs.d.ts.map +1 -1
- package/lib/Website/Nextjs.js.map +1 -1
- package/lib/Website/Nuxt.d.ts.map +1 -1
- package/lib/Website/Nuxt.js.map +1 -1
- package/lib/Website/Octane.d.ts.map +1 -1
- package/lib/Website/Octane.js.map +1 -1
- package/lib/Website/ReactRouter.d.ts.map +1 -1
- package/lib/Website/ReactRouter.js.map +1 -1
- package/lib/Website/SolidStart.d.ts.map +1 -1
- package/lib/Website/SolidStart.js.map +1 -1
- package/lib/Website/StaticSite.d.ts +10 -3
- package/lib/Website/StaticSite.d.ts.map +1 -1
- package/lib/Website/StaticSite.js +16 -4
- package/lib/Website/StaticSite.js.map +1 -1
- package/lib/Website/SvelteKit.d.ts.map +1 -1
- package/lib/Website/SvelteKit.js.map +1 -1
- package/lib/Website/TanStackStart.d.ts.map +1 -1
- package/lib/Website/TanStackStart.js.map +1 -1
- package/lib/Website/Vinext.d.ts.map +1 -1
- package/lib/Website/Vinext.js.map +1 -1
- package/lib/Website/Vite.d.ts +9 -0
- package/lib/Website/Vite.d.ts.map +1 -1
- package/lib/Website/Vite.js +9 -0
- package/lib/Website/Vite.js.map +1 -1
- package/lib/Website/Vocs.d.ts.map +1 -1
- package/lib/Website/Vocs.js.map +1 -1
- package/lib/Website/Waku.d.ts.map +1 -1
- package/lib/Website/Waku.js.map +1 -1
- package/lib/Website/index.d.ts.map +1 -1
- package/lib/Website/index.js.map +1 -1
- package/lib/WriteCertificates.d.ts.map +1 -1
- package/lib/WriteCertificates.js.map +1 -1
- package/lib/WriteCertificatesHttp.d.ts.map +1 -1
- package/lib/WriteCertificatesHttp.js +2 -2
- package/lib/WriteCertificatesHttp.js.map +1 -1
- package/lib/WriteRedis.d.ts.map +1 -1
- package/lib/WriteRedis.js.map +1 -1
- package/lib/WriteRedisHttp.d.ts.map +1 -1
- package/lib/WriteRedisHttp.js.map +1 -1
- package/lib/WriteSecret.d.ts.map +1 -1
- package/lib/WriteSecret.js.map +1 -1
- package/lib/WriteSecretHttp.d.ts.map +1 -1
- package/lib/WriteSecretHttp.js.map +1 -1
- package/lib/bluegreen.d.ts +5 -4
- package/lib/bluegreen.d.ts.map +1 -1
- package/lib/bluegreen.js +7 -5
- package/lib/bluegreen.js.map +1 -1
- package/lib/hosted.d.ts +19 -4
- package/lib/hosted.d.ts.map +1 -1
- package/lib/hosted.js +101 -11
- package/lib/hosted.js.map +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/internal/Oidc.d.ts +15 -0
- package/lib/internal/Oidc.d.ts.map +1 -0
- package/lib/internal/Oidc.js +73 -0
- package/lib/internal/Oidc.js.map +1 -0
- package/lib/internal/RemoteBuilder.d.ts.map +1 -1
- package/lib/internal/RemoteBuilder.js +21 -8
- package/lib/internal/RemoteBuilder.js.map +1 -1
- package/lib/leases.d.ts +6 -2
- package/lib/leases.d.ts.map +1 -1
- package/lib/leases.js +25 -5
- package/lib/leases.js.map +1 -1
- package/lib/ports.d.ts.map +1 -1
- package/lib/ports.js.map +1 -1
- package/lib/replicas.d.ts +39 -27
- package/lib/replicas.d.ts.map +1 -1
- package/lib/replicas.js +63 -8
- package/lib/replicas.js.map +1 -1
- package/lib/rpc.d.ts +14 -12
- package/lib/rpc.d.ts.map +1 -1
- package/lib/rpc.js +38 -40
- package/lib/rpc.js.map +1 -1
- package/package.json +15 -15
- package/src/App.ts +41 -10
- package/src/BindService.ts +28 -36
- package/src/Bootstrap.ts +2 -0
- package/src/Bucket.ts +278 -45
- package/src/BucketBinding.ts +1 -1
- package/src/Certificate.ts +36 -5
- package/src/ConnectPostgres.ts +1 -1
- package/src/ConnectPostgresHttp.ts +1 -1
- package/src/Credentials.ts +18 -41
- package/src/DeploymentImages.ts +1 -1
- package/src/Errors.ts +1 -1
- package/src/IpAssignment.ts +35 -5
- package/src/Machine.ts +39 -4
- package/src/MountVolume.ts +5 -2
- package/src/Postgres.ts +50 -8
- package/src/PostgresMigrations.ts +39 -99
- package/src/Redis.ts +54 -9
- package/src/RedisBinding.ts +1 -1
- package/src/RedisHttp.ts +4 -1
- package/src/Secret.ts +32 -4
- package/src/SecretKey.ts +32 -4
- package/src/Service.ts +441 -88
- package/src/Sprite.ts +45 -17
- package/src/Volume.ts +30 -6
- package/src/VolumeSnapshot.ts +40 -2
- package/src/Website/AssetDeployment.ts +13 -15
- package/src/Website/Astro.ts +20 -15
- package/src/Website/FrameworkSite.ts +92 -6
- package/src/Website/StaticSite.ts +27 -7
- package/src/Website/Vite.ts +9 -0
- package/src/WriteCertificatesHttp.ts +2 -2
- package/src/bluegreen.ts +11 -5
- package/src/hosted.ts +121 -17
- package/src/internal/Oidc.ts +99 -0
- package/src/internal/RemoteBuilder.ts +37 -19
- package/src/leases.ts +35 -7
- package/src/replicas.ts +70 -9
- package/src/rpc.ts +37 -40
package/src/IpAssignment.ts
CHANGED
|
@@ -85,9 +85,21 @@ export type IpAssignment = Resource<
|
|
|
85
85
|
createdAt: string | undefined;
|
|
86
86
|
},
|
|
87
87
|
never,
|
|
88
|
-
Providers
|
|
88
|
+
Providers,
|
|
89
|
+
IpAssignmentLookup
|
|
89
90
|
>;
|
|
90
91
|
|
|
92
|
+
/**
|
|
93
|
+
* Props for {@link IpAssignment.lookup}: find an address this stack
|
|
94
|
+
* doesn't manage by its App and IP.
|
|
95
|
+
*/
|
|
96
|
+
export interface IpAssignmentLookup {
|
|
97
|
+
/** Fly App name the address is assigned to. */
|
|
98
|
+
appName: string;
|
|
99
|
+
/** The allocated address. */
|
|
100
|
+
ip: string;
|
|
101
|
+
}
|
|
102
|
+
|
|
91
103
|
const resolveIpAssignmentProps = (
|
|
92
104
|
props: IpAssignmentProps | Effect.Effect<IpAssignmentProps, never, Providers>,
|
|
93
105
|
): Effect.Effect<IpAssignmentProps, never, Providers> =>
|
|
@@ -247,6 +259,19 @@ const IpAssignmentResource = Resource<IpAssignment>("Fly.IpAssignment");
|
|
|
247
259
|
* );
|
|
248
260
|
* ```
|
|
249
261
|
*
|
|
262
|
+
* ### Looking up an existing address
|
|
263
|
+
* `IpAssignment.lookup` references an address Alchemy doesn't manage
|
|
264
|
+
* (allocated with `fly ips allocate-v4` or by another stack). It is read
|
|
265
|
+
* during plan/deploy, never allocated or released.
|
|
266
|
+
*
|
|
267
|
+
* **Example:** Reference an existing address
|
|
268
|
+
* ```typescript
|
|
269
|
+
* const ip = Fly.IpAssignment.lookup({
|
|
270
|
+
* appName: "my-site",
|
|
271
|
+
* ip: "2a09:8280:1::1",
|
|
272
|
+
* });
|
|
273
|
+
* ```
|
|
274
|
+
*
|
|
250
275
|
* @resource
|
|
251
276
|
* @product App
|
|
252
277
|
*/
|
|
@@ -382,14 +407,14 @@ export const syncOwnedAppAddresses = Effect.fn(function* (
|
|
|
382
407
|
if (!observed.some(isDesiredFlycast)) {
|
|
383
408
|
yield* machines
|
|
384
409
|
.createAppIPAssignment({ app_name: appName, type: "private_v6", network })
|
|
385
|
-
.pipe(Effect.catchTag("Conflict", () => Effect.
|
|
410
|
+
.pipe(Effect.catchTag("Conflict", () => Effect.undefined));
|
|
386
411
|
}
|
|
387
412
|
if (isPublic) {
|
|
388
413
|
for (const type of ["shared_v4", "v6"] as const) {
|
|
389
414
|
if (observed.some((item) => inferType(item) === type)) continue;
|
|
390
415
|
yield* machines
|
|
391
416
|
.createAppIPAssignment({ app_name: appName, type })
|
|
392
|
-
.pipe(Effect.catchTag("Conflict", () => Effect.
|
|
417
|
+
.pipe(Effect.catchTag("Conflict", () => Effect.undefined));
|
|
393
418
|
}
|
|
394
419
|
}
|
|
395
420
|
// Release public addresses of a private Service and Flycast addresses
|
|
@@ -415,7 +440,7 @@ export const ensureFlycastAddress = Effect.fn(function* (appName: string, networ
|
|
|
415
440
|
return;
|
|
416
441
|
yield* machines
|
|
417
442
|
.createAppIPAssignment({ app_name: appName, type: "private_v6", network })
|
|
418
|
-
.pipe(Effect.catchTag("Conflict", () => Effect.
|
|
443
|
+
.pipe(Effect.catchTag("Conflict", () => Effect.undefined));
|
|
419
444
|
});
|
|
420
445
|
|
|
421
446
|
export const IpAssignmentProvider = () =>
|
|
@@ -439,6 +464,11 @@ export const IpAssignmentProvider = () =>
|
|
|
439
464
|
return undefined;
|
|
440
465
|
}),
|
|
441
466
|
|
|
467
|
+
lookup: Effect.fn(function* ({ appName, ip }) {
|
|
468
|
+
const found = yield* findByIp(appName, ip);
|
|
469
|
+
return found?.ip === undefined ? undefined : toAttrs(appName, found);
|
|
470
|
+
}),
|
|
471
|
+
|
|
442
472
|
read: Effect.fn(function* ({ olds, output }) {
|
|
443
473
|
const appName = output?.appName ?? appNameOf(olds?.app);
|
|
444
474
|
if (appName === undefined) return undefined;
|
|
@@ -495,7 +525,7 @@ export const IpAssignmentProvider = () =>
|
|
|
495
525
|
org_slug: props.orgSlug,
|
|
496
526
|
service_name: props.serviceName,
|
|
497
527
|
})
|
|
498
|
-
.pipe(Effect.catchTag("Conflict", () => Effect.
|
|
528
|
+
.pipe(Effect.catchTag("Conflict", () => Effect.undefined));
|
|
499
529
|
if (created !== undefined && created.ip !== undefined) {
|
|
500
530
|
current = created;
|
|
501
531
|
} else {
|
package/src/Machine.ts
CHANGED
|
@@ -33,6 +33,7 @@ import type { Providers } from "./Providers.ts";
|
|
|
33
33
|
import {
|
|
34
34
|
deleteReplicaSet,
|
|
35
35
|
listReplicaSets,
|
|
36
|
+
lookupReplicaSet,
|
|
36
37
|
observeReplicaSet,
|
|
37
38
|
reconcileReplicas,
|
|
38
39
|
resolveCount,
|
|
@@ -379,9 +380,25 @@ export type Machine = Resource<
|
|
|
379
380
|
replicas: Replica[];
|
|
380
381
|
},
|
|
381
382
|
ServiceBinding,
|
|
382
|
-
Providers
|
|
383
|
+
Providers,
|
|
384
|
+
MachineLookup
|
|
383
385
|
>;
|
|
384
386
|
|
|
387
|
+
/**
|
|
388
|
+
* Props for {@link Machine.lookup}: find Machines this stack doesn't
|
|
389
|
+
* manage by their App and name.
|
|
390
|
+
*/
|
|
391
|
+
export interface MachineLookup {
|
|
392
|
+
/** Fly App name the Machines run in. */
|
|
393
|
+
appName: string;
|
|
394
|
+
/**
|
|
395
|
+
* Machine name (unique per App). Replicas named `{name}-{N}` are
|
|
396
|
+
* found with it, as are the replicas of an Alchemy Machine whose base
|
|
397
|
+
* name it is.
|
|
398
|
+
*/
|
|
399
|
+
name: string;
|
|
400
|
+
}
|
|
401
|
+
|
|
385
402
|
/**
|
|
386
403
|
* A Fly.Machine is a Firecracker VM running one image or a named container group.
|
|
387
404
|
*
|
|
@@ -838,13 +855,24 @@ export type Machine = Resource<
|
|
|
838
855
|
* discovers unfinished owned generations.
|
|
839
856
|
* See the [deployment guide](/fly/compute/deployments) for recovery and limits.
|
|
840
857
|
*
|
|
858
|
+
* ### Looking up existing Machines
|
|
859
|
+
* `Machine.lookup` references Machines Alchemy doesn't manage (started with
|
|
860
|
+
* `fly machine run` or by another stack). It is read during plan/deploy,
|
|
861
|
+
* never created, updated or deleted.
|
|
862
|
+
*
|
|
863
|
+
* **Example:** Reference an existing Machine
|
|
864
|
+
* ```typescript
|
|
865
|
+
* const worker = Fly.Machine.lookup({ appName: "my-app", name: "worker" });
|
|
866
|
+
* const address = worker.privateIp;
|
|
867
|
+
* ```
|
|
868
|
+
*
|
|
841
869
|
* @resource
|
|
842
870
|
* @product Machine
|
|
843
871
|
*/
|
|
844
872
|
export const Machine = Resource<Machine>("Fly.Machine");
|
|
845
873
|
|
|
846
874
|
export class MachineNotCreated extends Data.TaggedError("Fly.MachineNotCreated")<{
|
|
847
|
-
|
|
875
|
+
resourceName: string;
|
|
848
876
|
appName: string;
|
|
849
877
|
}> {}
|
|
850
878
|
|
|
@@ -928,7 +956,7 @@ const desiredEnv = (
|
|
|
928
956
|
const desiredMetadata = (
|
|
929
957
|
props: MachineProps,
|
|
930
958
|
alchemy: Record<string, string>,
|
|
931
|
-
): Record<string, string> => ({ ...
|
|
959
|
+
): Record<string, string> => ({ ...props.metadata, ...alchemy });
|
|
932
960
|
|
|
933
961
|
const buildConfig = (input: {
|
|
934
962
|
image: string | undefined;
|
|
@@ -1154,6 +1182,11 @@ export const MachineProvider = () =>
|
|
|
1154
1182
|
return output.rolloutPending ? { action: "update" as const } : undefined;
|
|
1155
1183
|
}),
|
|
1156
1184
|
|
|
1185
|
+
lookup: Effect.fn(function* ({ appName, name }) {
|
|
1186
|
+
const found = yield* lookupReplicaSet({ appName, name });
|
|
1187
|
+
return found === undefined ? undefined : toAttrs(found);
|
|
1188
|
+
}),
|
|
1189
|
+
|
|
1157
1190
|
read: Effect.fn(function* ({ id, fqn, instanceId, olds, output }) {
|
|
1158
1191
|
const appName = appNameOf(olds?.app) ?? output?.appName;
|
|
1159
1192
|
const name = yield* resolveMachineName(id, olds?.name, output?.baseName ?? output?.name);
|
|
@@ -1245,7 +1278,9 @@ export const MachineProvider = () =>
|
|
|
1245
1278
|
}),
|
|
1246
1279
|
}).pipe(
|
|
1247
1280
|
Effect.catchTag("Fly.ReplicaNotCreated", (error) =>
|
|
1248
|
-
Effect.fail(
|
|
1281
|
+
Effect.fail(
|
|
1282
|
+
new MachineNotCreated({ resourceName: error.resourceName, appName: error.appName }),
|
|
1283
|
+
),
|
|
1249
1284
|
),
|
|
1250
1285
|
);
|
|
1251
1286
|
return toAttrs(set);
|
package/src/MountVolume.ts
CHANGED
|
@@ -116,6 +116,11 @@ export interface ServiceBinding {
|
|
|
116
116
|
* published.
|
|
117
117
|
*/
|
|
118
118
|
target?: BoundTarget;
|
|
119
|
+
/**
|
|
120
|
+
* A Service that calls this one, recorded on the target by
|
|
121
|
+
* `Fly.bindService`. Only the Apps recorded here may call its methods.
|
|
122
|
+
*/
|
|
123
|
+
caller?: { appName: string };
|
|
119
124
|
}
|
|
120
125
|
|
|
121
126
|
/** A Service bound by `Fly.bindService` or `Fly.bindEndpoint`. */
|
|
@@ -128,8 +133,6 @@ export interface BoundTarget {
|
|
|
128
133
|
network: string | undefined;
|
|
129
134
|
/** The bound Service's private address; `undefined` when it publishes nothing. */
|
|
130
135
|
privateUrl: string | undefined;
|
|
131
|
-
/** The bound Service's caller token. */
|
|
132
|
-
rpcToken?: import("effect/Redacted").Redacted<string>;
|
|
133
136
|
/** Port `Fly.bindEndpoint` targets; the binding port for `bindService`. */
|
|
134
137
|
port?: number;
|
|
135
138
|
/** Ports the bound Service publishes, for `Fly.bindEndpoint`. */
|
package/src/Postgres.ts
CHANGED
|
@@ -93,6 +93,13 @@ export interface PostgresProps {
|
|
|
93
93
|
* is reachable on the org private network; deploy-time apply needs a
|
|
94
94
|
* route to that network (a WireGuard peer, `fly mpg proxy`, or a
|
|
95
95
|
* machine already on 6PN).
|
|
96
|
+
*
|
|
97
|
+
* History is forward-only and linear. A new file must sort after every
|
|
98
|
+
* applied one, or the deploy fails with `NonLinearMigrationHistoryError`;
|
|
99
|
+
* an edited or deleted applied file fails with
|
|
100
|
+
* `RewrittenMigrationHistoryError`. Renaming an applied file without
|
|
101
|
+
* changing its contents keeps it applied. See
|
|
102
|
+
* [Migrations](https://alchemy.run/sql/effect-sql/migrations#change-migration-history-safely).
|
|
96
103
|
*/
|
|
97
104
|
migrations?: MigrationsInput;
|
|
98
105
|
/**
|
|
@@ -143,9 +150,24 @@ export type Postgres = Resource<
|
|
|
143
150
|
importHashes: Record<string, string>;
|
|
144
151
|
},
|
|
145
152
|
never,
|
|
146
|
-
Providers
|
|
153
|
+
Providers,
|
|
154
|
+
PostgresLookup
|
|
147
155
|
>;
|
|
148
156
|
|
|
157
|
+
/**
|
|
158
|
+
* Props for {@link Postgres.lookup}: find a Managed Postgres cluster
|
|
159
|
+
* this stack doesn't manage by its name.
|
|
160
|
+
*/
|
|
161
|
+
export interface PostgresLookup {
|
|
162
|
+
/** Cluster name (unique in the org). */
|
|
163
|
+
name: string;
|
|
164
|
+
/**
|
|
165
|
+
* Organization slug.
|
|
166
|
+
* @default the current token's org
|
|
167
|
+
*/
|
|
168
|
+
orgSlug?: string;
|
|
169
|
+
}
|
|
170
|
+
|
|
149
171
|
/**
|
|
150
172
|
* A Fly.Postgres is a Managed Postgres (MPG) cluster. It is billed.
|
|
151
173
|
* Do not wrap unmanaged `fly postgres`.
|
|
@@ -280,13 +302,25 @@ export type Postgres = Resource<
|
|
|
280
302
|
* });
|
|
281
303
|
* ```
|
|
282
304
|
*
|
|
305
|
+
* ### Looking up an existing cluster
|
|
306
|
+
* `Postgres.lookup` references a cluster Alchemy doesn't manage (created
|
|
307
|
+
* with `fly mpg create` or by another stack). It is read during
|
|
308
|
+
* plan/deploy, never created, updated or deleted, and its connection URIs
|
|
309
|
+
* come from Fly like a declared cluster's.
|
|
310
|
+
*
|
|
311
|
+
* **Example:** Connect to an existing cluster
|
|
312
|
+
* ```typescript
|
|
313
|
+
* const db = Fly.Postgres.lookup({ name: "my-db" });
|
|
314
|
+
* const sql = yield* Fly.ConnectPostgres(db);
|
|
315
|
+
* ```
|
|
316
|
+
*
|
|
283
317
|
* @resource
|
|
284
318
|
* @product Postgres
|
|
285
319
|
*/
|
|
286
320
|
export const Postgres = Resource<Postgres>("Fly.Postgres");
|
|
287
321
|
|
|
288
322
|
export class PostgresNotCreated extends Data.TaggedError("Fly.PostgresNotCreated")<{
|
|
289
|
-
|
|
323
|
+
resourceName: string;
|
|
290
324
|
orgSlug: string;
|
|
291
325
|
}> {}
|
|
292
326
|
|
|
@@ -369,7 +403,7 @@ const resolveName = (id: string, name: string | undefined, existing?: string) =>
|
|
|
369
403
|
export const getLiveCluster = (clusterId: string) =>
|
|
370
404
|
mpg.getClusterById({ id: clusterId }).pipe(
|
|
371
405
|
Effect.map((res) => (isLiveCluster(res.data) ? res.data : undefined)),
|
|
372
|
-
Effect.catchTag("NotFound", () => Effect.
|
|
406
|
+
Effect.catchTag("NotFound", () => Effect.undefined),
|
|
373
407
|
);
|
|
374
408
|
|
|
375
409
|
export const getClusterResponse = (clusterId: string) =>
|
|
@@ -377,7 +411,7 @@ export const getClusterResponse = (clusterId: string) =>
|
|
|
377
411
|
Effect.map((res): GetClusterResponse | undefined =>
|
|
378
412
|
isLiveCluster(res.data) ? res : undefined,
|
|
379
413
|
),
|
|
380
|
-
Effect.catchTag("NotFound", () => Effect.
|
|
414
|
+
Effect.catchTag("NotFound", () => Effect.undefined),
|
|
381
415
|
);
|
|
382
416
|
|
|
383
417
|
const listOrgClusters = (orgSlug: string) =>
|
|
@@ -535,7 +569,7 @@ export const attachPostgresSecrets = Effect.fn(function* (
|
|
|
535
569
|
times: 10,
|
|
536
570
|
schedule: backoff,
|
|
537
571
|
}),
|
|
538
|
-
Effect.catchTag("Fly.PostgresCredentialsPending", () => Effect.
|
|
572
|
+
Effect.catchTag("Fly.PostgresCredentialsPending", () => Effect.undefined),
|
|
539
573
|
);
|
|
540
574
|
if (creds === undefined) {
|
|
541
575
|
return yield* new PostgresCredentialsMissing({ clusterId });
|
|
@@ -602,6 +636,14 @@ export const PostgresProvider = () =>
|
|
|
602
636
|
return undefined;
|
|
603
637
|
}),
|
|
604
638
|
|
|
639
|
+
lookup: Effect.fn(function* ({ name, orgSlug }) {
|
|
640
|
+
const org = orgSlug ?? (yield* resolveOrgSlug());
|
|
641
|
+
const found = yield* findByName(org, name);
|
|
642
|
+
if (found === undefined) return undefined;
|
|
643
|
+
const hydrated = found.id !== undefined ? yield* getClusterResponse(found.id) : undefined;
|
|
644
|
+
return toAttrs(hydrated?.data ?? found, { name, orgSlug: org }, hydrated?.credentials);
|
|
645
|
+
}),
|
|
646
|
+
|
|
605
647
|
read: Effect.fn(function* ({ id, olds, output }) {
|
|
606
648
|
const found =
|
|
607
649
|
output?.clusterId !== undefined && output.clusterId.length > 0
|
|
@@ -660,7 +702,7 @@ export const PostgresProvider = () =>
|
|
|
660
702
|
|
|
661
703
|
if (current === undefined) {
|
|
662
704
|
if (region === undefined || region.length === 0) {
|
|
663
|
-
return yield* new PostgresNotCreated({ name, orgSlug });
|
|
705
|
+
return yield* new PostgresNotCreated({ resourceName: name, orgSlug });
|
|
664
706
|
}
|
|
665
707
|
const created = yield* mpg
|
|
666
708
|
.createCluster({
|
|
@@ -674,13 +716,13 @@ export const PostgresProvider = () =>
|
|
|
674
716
|
})
|
|
675
717
|
.pipe(
|
|
676
718
|
Effect.map((res) => res.data),
|
|
677
|
-
Effect.catchTag("Conflict", () => Effect.
|
|
719
|
+
Effect.catchTag("Conflict", () => Effect.undefined),
|
|
678
720
|
);
|
|
679
721
|
current = isLiveCluster(created) ? created : yield* findByName(orgSlug, name);
|
|
680
722
|
}
|
|
681
723
|
|
|
682
724
|
if (current === undefined || current.id === undefined) {
|
|
683
|
-
return yield* new PostgresNotCreated({ name, orgSlug });
|
|
725
|
+
return yield* new PostgresNotCreated({ resourceName: name, orgSlug });
|
|
684
726
|
}
|
|
685
727
|
// `current` is reassigned below; the id is stable across the waits.
|
|
686
728
|
const clusterId = current.id;
|
|
@@ -1,52 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
MigrationError,
|
|
3
3
|
runMigrations,
|
|
4
|
+
withPgExecutor,
|
|
4
5
|
type NormalizedMigrationsInput,
|
|
6
|
+
type SqlExecutor,
|
|
5
7
|
type StampedMigrationsState,
|
|
6
8
|
} from "@alchemy.run/sql/Migrations";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import * as Data from "effect/Data";
|
|
9
|
+
import { stripSslQueryParams } from "@alchemy.run/sql/PostgresDriver";
|
|
10
|
+
import { applySqlImports } from "@alchemy.run/sql/SqlFile";
|
|
10
11
|
import * as Effect from "effect/Effect";
|
|
11
12
|
import * as Redacted from "effect/Redacted";
|
|
12
|
-
import * as Schedule from "effect/Schedule";
|
|
13
|
-
import type { Client } from "pg";
|
|
14
13
|
|
|
15
|
-
export
|
|
16
|
-
message: string;
|
|
17
|
-
cause?: unknown;
|
|
18
|
-
}> {}
|
|
14
|
+
export { stripSslQueryParams };
|
|
19
15
|
|
|
20
|
-
/**
|
|
21
|
-
* Strip query-string SSL flags so `pg-connection-string` does not treat
|
|
22
|
-
* `sslmode=require` as `verify-full`. TLS and certificate verification
|
|
23
|
-
* are set on the client (`ssl.rejectUnauthorized: true`).
|
|
24
|
-
*/
|
|
25
|
-
export const stripSslQueryParams = (uri: string): string => {
|
|
26
|
-
try {
|
|
27
|
-
const url = new URL(uri);
|
|
28
|
-
url.searchParams.delete("sslmode");
|
|
29
|
-
url.searchParams.delete("channel_binding");
|
|
30
|
-
return url.toString();
|
|
31
|
-
} catch {
|
|
32
|
-
return uri;
|
|
33
|
-
}
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
const toMigrationError = (cause: unknown) =>
|
|
37
|
-
new PostgresMigrationError({
|
|
38
|
-
message: cause instanceof Error ? cause.message : String(cause),
|
|
39
|
-
cause,
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* A freshly-provisioned Fly Managed Postgres cluster reports `ready` on the
|
|
44
|
-
* API before its hostname is resolvable and its endpoint accepts
|
|
45
|
-
* connections — reconcile's first migration connect can fail with
|
|
46
|
-
* `ENOTFOUND` (DNS still propagating) or a refused/reset socket. These are
|
|
47
|
-
* eventual consistency, not failures: retry the connect on a bounded
|
|
48
|
-
* schedule (~45s) before surfacing the error.
|
|
49
|
-
*/
|
|
50
16
|
const TRANSIENT_CONNECT_CODES = new Set([
|
|
51
17
|
"ENOTFOUND",
|
|
52
18
|
"EAI_AGAIN",
|
|
@@ -55,40 +21,32 @@ const TRANSIENT_CONNECT_CODES = new Set([
|
|
|
55
21
|
"ETIMEDOUT",
|
|
56
22
|
]);
|
|
57
23
|
|
|
58
|
-
const isTransientConnectError = (
|
|
59
|
-
const code = (
|
|
24
|
+
const isTransientConnectError = (cause: unknown): boolean => {
|
|
25
|
+
const code = (cause as { code?: unknown } | null | undefined)?.code;
|
|
60
26
|
return typeof code === "string" && TRANSIENT_CONNECT_CODES.has(code);
|
|
61
27
|
};
|
|
62
28
|
|
|
63
|
-
/**
|
|
64
|
-
|
|
29
|
+
/**
|
|
30
|
+
* A verified-TLS `pg` session on a Fly Managed Postgres URI for the scope
|
|
31
|
+
* of `use`. A fresh cluster's hostname takes a moment to resolve, so
|
|
32
|
+
* connection failures are retried; one that persists is almost always a
|
|
33
|
+
* missing route — MPG hostnames only exist on the org's private network.
|
|
34
|
+
*/
|
|
35
|
+
const withFlyPgExecutor = <A, E, R>(
|
|
65
36
|
connectionUri: Redacted.Redacted<string>,
|
|
66
|
-
use: (
|
|
67
|
-
): Effect.Effect<A,
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
catch: toMigrationError,
|
|
80
|
-
}).pipe(
|
|
81
|
-
Effect.retry({
|
|
82
|
-
while: isTransientConnectError,
|
|
83
|
-
schedule: Schedule.spaced("3 seconds"),
|
|
84
|
-
times: 15,
|
|
85
|
-
}),
|
|
86
|
-
// Still unreachable after the bounded retry: almost always a missing
|
|
87
|
-
// route rather than propagation — MPG hostnames only exist on the org
|
|
88
|
-
// private network. Say so instead of a bare `getaddrinfo ENOTFOUND`.
|
|
89
|
-
Effect.mapError((error) =>
|
|
90
|
-
isTransientConnectError(error)
|
|
91
|
-
? new PostgresMigrationError({
|
|
37
|
+
use: (executor: SqlExecutor) => Effect.Effect<A, E, R>,
|
|
38
|
+
): Effect.Effect<A, MigrationError | E, R> =>
|
|
39
|
+
withPgExecutor(
|
|
40
|
+
{
|
|
41
|
+
connectionString: connectionUri.pipe(Redacted.value, stripSslQueryParams),
|
|
42
|
+
ssl: { rejectUnauthorized: true },
|
|
43
|
+
},
|
|
44
|
+
use,
|
|
45
|
+
{
|
|
46
|
+
retryConnect: { while: isTransientConnectError, times: 15, interval: "3 seconds" },
|
|
47
|
+
onConnectFailure: (error) =>
|
|
48
|
+
isTransientConnectError(error.cause)
|
|
49
|
+
? new MigrationError({
|
|
92
50
|
message:
|
|
93
51
|
`${error.message} — Fly Managed Postgres is only reachable ` +
|
|
94
52
|
"on the org's private network. Run the deploy behind a " +
|
|
@@ -97,10 +55,7 @@ export const withPgClient = <A, E, R>(
|
|
|
97
55
|
cause: error.cause,
|
|
98
56
|
})
|
|
99
57
|
: error,
|
|
100
|
-
|
|
101
|
-
),
|
|
102
|
-
use,
|
|
103
|
-
(client) => Effect.promise(() => client.end().catch(() => {})),
|
|
58
|
+
},
|
|
104
59
|
);
|
|
105
60
|
|
|
106
61
|
/**
|
|
@@ -114,40 +69,25 @@ export const runPgMigrations = (options: {
|
|
|
114
69
|
stamped: StampedMigrationsState;
|
|
115
70
|
}) =>
|
|
116
71
|
runMigrations({
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
72
|
+
input: options.input,
|
|
73
|
+
stamped: options.stamped,
|
|
74
|
+
withExecutor: (apply) => withFlyPgExecutor(options.connectionUri, apply),
|
|
120
75
|
});
|
|
121
76
|
|
|
122
77
|
/** Run a single SQL script against the database (used for `importFiles`). */
|
|
123
78
|
export const runSql = (connectionUri: Redacted.Redacted<string>, sql: string) =>
|
|
124
|
-
|
|
125
|
-
Effect.tryPromise({
|
|
126
|
-
try: () => client.query(sql),
|
|
127
|
-
catch: toMigrationError,
|
|
128
|
-
}),
|
|
129
|
-
).pipe(Effect.asVoid);
|
|
79
|
+
withFlyPgExecutor(connectionUri, (executor) => executor.query(sql)).pipe(Effect.asVoid);
|
|
130
80
|
|
|
81
|
+
/** Apply `importFiles`, each once and again only when it changes. */
|
|
131
82
|
export const runImports = (
|
|
132
83
|
connectionUri: Redacted.Redacted<string>,
|
|
133
84
|
importFiles: ReadonlyArray<string>,
|
|
134
85
|
rootDir: string,
|
|
135
86
|
previous: Record<string, string>,
|
|
136
87
|
) =>
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
hashes[filePath] = file.hash;
|
|
143
|
-
continue;
|
|
144
|
-
}
|
|
145
|
-
yield* runSql(connectionUri, file.sql);
|
|
146
|
-
hashes[filePath] = file.hash;
|
|
147
|
-
}
|
|
148
|
-
const tracked = new Set(importFiles);
|
|
149
|
-
for (const key of Object.keys(hashes)) {
|
|
150
|
-
if (!tracked.has(key)) delete hashes[key];
|
|
151
|
-
}
|
|
152
|
-
return hashes;
|
|
88
|
+
applySqlImports({
|
|
89
|
+
importFiles,
|
|
90
|
+
rootDir,
|
|
91
|
+
previous,
|
|
92
|
+
apply: (file) => runSql(connectionUri, file.sql),
|
|
153
93
|
});
|
package/src/Redis.ts
CHANGED
|
@@ -24,10 +24,17 @@ export const REDIS_ADDON_TYPE = "upstash_redis";
|
|
|
24
24
|
export const REDIS_PROVIDER = "upstash_redis";
|
|
25
25
|
export const REDIS_URL_ENV = "REDIS_URL";
|
|
26
26
|
|
|
27
|
+
// Re-export from the defining module, not the `@alchemy.run/redis` barrel:
|
|
28
|
+
// rolldown's lazyBarrel rejects an unused re-export from an `export *` barrel
|
|
29
|
+
// (https://github.com/rolldown/rolldown/issues/11344).
|
|
27
30
|
export {
|
|
28
31
|
CommandError as RedisCommandError,
|
|
29
32
|
UrlMissing as RedisUrlMissing,
|
|
30
|
-
} from "@alchemy.run/redis";
|
|
33
|
+
} from "@alchemy.run/redis/Errors";
|
|
34
|
+
// Clients' runtime effects require redis's `RuntimeContext` twin, so it shows
|
|
35
|
+
// up in users' inferred types. Exporting it here lets TypeScript name it
|
|
36
|
+
// through this package without users depending on @alchemy.run/redis.
|
|
37
|
+
export type { RuntimeContext as RedisRuntimeContext } from "@alchemy.run/redis/RuntimeContext";
|
|
31
38
|
|
|
32
39
|
export interface RedisProps {
|
|
33
40
|
/**
|
|
@@ -105,9 +112,19 @@ export type Redis = Resource<
|
|
|
105
112
|
url: Redacted.Redacted<string> | undefined;
|
|
106
113
|
},
|
|
107
114
|
never,
|
|
108
|
-
Providers
|
|
115
|
+
Providers,
|
|
116
|
+
RedisLookup
|
|
109
117
|
>;
|
|
110
118
|
|
|
119
|
+
/**
|
|
120
|
+
* Props for {@link Redis.lookup}: find an Upstash Redis this stack
|
|
121
|
+
* doesn't manage by its name.
|
|
122
|
+
*/
|
|
123
|
+
export interface RedisLookup {
|
|
124
|
+
/** Upstash Redis name. */
|
|
125
|
+
name: string;
|
|
126
|
+
}
|
|
127
|
+
|
|
111
128
|
/**
|
|
112
129
|
* Managed Upstash Redis in a Fly org. Bind {@link ReadRedis},
|
|
113
130
|
* {@link WriteRedis}, or {@link ReadWriteRedis} on a {@link Service}.
|
|
@@ -216,13 +233,24 @@ export type Redis = Resource<
|
|
|
216
233
|
* need a specific size.
|
|
217
234
|
* :::
|
|
218
235
|
*
|
|
236
|
+
* ### Looking up an existing Redis
|
|
237
|
+
* `Redis.lookup` references a Redis Alchemy doesn't manage (created with
|
|
238
|
+
* `fly redis create` or by another stack). It is read during plan/deploy,
|
|
239
|
+
* never created, updated or deleted. Bind it like a declared Redis.
|
|
240
|
+
*
|
|
241
|
+
* **Example:** Read an existing Redis
|
|
242
|
+
* ```typescript
|
|
243
|
+
* const cache = Fly.Redis.lookup({ name: "my-cache" });
|
|
244
|
+
* const redis = yield* Fly.ReadRedis(cache);
|
|
245
|
+
* ```
|
|
246
|
+
*
|
|
219
247
|
* @resource
|
|
220
248
|
* @product Redis
|
|
221
249
|
*/
|
|
222
250
|
export const Redis = Resource<Redis>("Fly.Redis");
|
|
223
251
|
|
|
224
252
|
export class RedisNotCreated extends Data.TaggedError("Fly.RedisNotCreated")<{
|
|
225
|
-
|
|
253
|
+
resourceName: string;
|
|
226
254
|
errorMessage?: string;
|
|
227
255
|
}> {}
|
|
228
256
|
|
|
@@ -454,7 +482,7 @@ const resolvePlan = (plan: string | undefined, existingId: string | undefined) =
|
|
|
454
482
|
const resolveOrganization = (orgSlug: string) =>
|
|
455
483
|
addons
|
|
456
484
|
.organization({ slug: orgSlug })
|
|
457
|
-
.pipe(Effect.catchTag("FlyIoParseError", () => Effect.
|
|
485
|
+
.pipe(Effect.catchTag("FlyIoParseError", () => Effect.undefined));
|
|
458
486
|
|
|
459
487
|
const ensureTos = (orgSlug: string, organizationId: string) =>
|
|
460
488
|
Effect.gen(function* () {
|
|
@@ -485,7 +513,7 @@ const waitUntilReady = (id: string, name: string) =>
|
|
|
485
513
|
if (failedStatus(row.status)) {
|
|
486
514
|
return Effect.fail(
|
|
487
515
|
new RedisNotCreated({
|
|
488
|
-
|
|
516
|
+
resourceName: row.name ?? name,
|
|
489
517
|
errorMessage: row.errorMessage ?? row.status ?? undefined,
|
|
490
518
|
}),
|
|
491
519
|
);
|
|
@@ -522,7 +550,7 @@ const waitUntilGone = (id: string, name: string) =>
|
|
|
522
550
|
const desiredOptions = (
|
|
523
551
|
props: RedisProps,
|
|
524
552
|
plan: RedisPlan,
|
|
525
|
-
observed: unknown
|
|
553
|
+
observed: unknown,
|
|
526
554
|
): Record<string, unknown> => {
|
|
527
555
|
const options = recordOf(observed);
|
|
528
556
|
if (props.eviction !== undefined) options.eviction = props.eviction;
|
|
@@ -595,7 +623,7 @@ export const attachRedisSecrets = Effect.fn(function* (
|
|
|
595
623
|
if ((url === undefined || url.length === 0) && row.id !== undefined) {
|
|
596
624
|
const detail = yield* addons
|
|
597
625
|
.addOn({ id: row.id })
|
|
598
|
-
.pipe(Effect.catchTag("FlyIoParseError", () => Effect.
|
|
626
|
+
.pipe(Effect.catchTag("FlyIoParseError", () => Effect.undefined));
|
|
599
627
|
url = unwrapSensitive(detail?.publicUrl);
|
|
600
628
|
}
|
|
601
629
|
if (url === undefined || url.length === 0) {
|
|
@@ -651,6 +679,23 @@ export const RedisProvider = () =>
|
|
|
651
679
|
return undefined;
|
|
652
680
|
}),
|
|
653
681
|
|
|
682
|
+
lookup: Effect.fn(function* ({ name }) {
|
|
683
|
+
const found = yield* findRedisAddOn({ name });
|
|
684
|
+
if (found === undefined) return undefined;
|
|
685
|
+
let url = unwrapSensitive(found.publicUrl);
|
|
686
|
+
if ((url === undefined || url.length === 0) && found.id !== undefined) {
|
|
687
|
+
const detail = yield* addons
|
|
688
|
+
.addOn({ id: found.id })
|
|
689
|
+
.pipe(Effect.catchTag("FlyIoParseError", () => Effect.undefined));
|
|
690
|
+
url = unwrapSensitive(detail?.publicUrl);
|
|
691
|
+
}
|
|
692
|
+
return toAttrs(found, {
|
|
693
|
+
name,
|
|
694
|
+
primaryRegion: DEFAULT_REDIS_REGION,
|
|
695
|
+
url: url === undefined || url.length === 0 ? undefined : Redacted.make(url),
|
|
696
|
+
});
|
|
697
|
+
}),
|
|
698
|
+
|
|
654
699
|
read: Effect.fn(function* ({ id, olds, output }) {
|
|
655
700
|
const name = yield* resolveName(id, olds?.name, output?.name);
|
|
656
701
|
const found =
|
|
@@ -735,7 +780,7 @@ export const RedisProvider = () =>
|
|
|
735
780
|
}
|
|
736
781
|
|
|
737
782
|
if (current === undefined) {
|
|
738
|
-
return yield* new RedisNotCreated({ name });
|
|
783
|
+
return yield* new RedisNotCreated({ resourceName: name });
|
|
739
784
|
}
|
|
740
785
|
|
|
741
786
|
if (pendingStatus(current.status)) {
|
|
@@ -744,7 +789,7 @@ export const RedisProvider = () =>
|
|
|
744
789
|
}
|
|
745
790
|
|
|
746
791
|
if (current === undefined) {
|
|
747
|
-
return yield* new RedisNotCreated({ name });
|
|
792
|
+
return yield* new RedisNotCreated({ resourceName: name });
|
|
748
793
|
}
|
|
749
794
|
|
|
750
795
|
const observedOptions = recordOf(current.options);
|
package/src/RedisBinding.ts
CHANGED
|
@@ -52,7 +52,7 @@ export const makeRedisBinding = <Client>(options: { makeClient: (url: Url) => Cl
|
|
|
52
52
|
|
|
53
53
|
const urlFromResource = yield* redis.url;
|
|
54
54
|
const missing = new RedisUrlMissing({
|
|
55
|
-
|
|
55
|
+
resourceName: asPlain(redis.name) ?? redis.LogicalId,
|
|
56
56
|
});
|
|
57
57
|
const url: Url = Effect.gen(function* () {
|
|
58
58
|
const fromOutput = asPlain(yield* urlFromResource);
|
package/src/RedisHttp.ts
CHANGED
|
@@ -4,8 +4,11 @@
|
|
|
4
4
|
* The RESP client lives in `@alchemy.run/redis`. These aliases keep the
|
|
5
5
|
* `{Op}Http.ts` layers stable.
|
|
6
6
|
*/
|
|
7
|
+
// Re-export from the defining module, not the `@alchemy.run/redis` barrel:
|
|
8
|
+
// rolldown's lazyBarrel rejects an unused re-export from an `export *` barrel
|
|
9
|
+
// (https://github.com/rolldown/rolldown/issues/11344).
|
|
7
10
|
export {
|
|
8
11
|
makeRead as makeReadRedisClient,
|
|
9
12
|
makeReadWrite as makeReadWriteRedisClient,
|
|
10
13
|
makeWrite as makeWriteRedisClient,
|
|
11
|
-
} from "@alchemy.run/redis";
|
|
14
|
+
} from "@alchemy.run/redis/Client";
|