alchemy 0.1.18 → 0.2.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/alchemy.d.ts +67 -0
- package/lib/alchemy.js +40 -0
- package/lib/apply.d.ts +3 -20
- package/lib/apply.js +86 -82
- package/lib/aws/account-id.d.ts +0 -1
- package/lib/aws/account-id.js +0 -1
- package/lib/aws/bucket.d.ts +4 -7
- package/lib/aws/bucket.js +8 -12
- package/lib/aws/function.d.ts +4 -7
- package/lib/aws/function.js +9 -30
- package/lib/aws/index.d.ts +1 -1
- package/lib/aws/index.js +1 -1
- package/lib/aws/oidc/github-oidc-provider.d.ts +2 -6
- package/lib/aws/oidc/github-oidc-provider.js +10 -15
- package/lib/aws/oidc/index.d.ts +0 -1
- package/lib/aws/oidc/index.js +0 -1
- package/lib/aws/oidc/oidc-provider.d.ts +4 -6
- package/lib/aws/oidc/oidc-provider.js +9 -11
- package/lib/aws/policy-attachment.d.ts +9 -0
- package/lib/aws/policy-attachment.js +20 -0
- package/lib/aws/policy.d.ts +4 -16
- package/lib/aws/policy.js +9 -40
- package/lib/aws/queue.d.ts +4 -34
- package/lib/aws/queue.js +10 -20
- package/lib/aws/role.d.ts +4 -7
- package/lib/aws/role.js +17 -26
- package/lib/aws/ses.d.ts +4 -10
- package/lib/aws/ses.js +118 -131
- package/lib/aws/table.d.ts +4 -7
- package/lib/aws/table.js +8 -11
- package/lib/cloudflare/api.d.ts +0 -1
- package/lib/cloudflare/api.js +0 -1
- package/lib/cloudflare/asset-manifest.d.ts +0 -1
- package/lib/cloudflare/asset-manifest.js +0 -1
- package/lib/cloudflare/auth.d.ts +0 -1
- package/lib/cloudflare/auth.js +0 -1
- package/lib/cloudflare/bindings.d.ts +1 -1
- package/lib/cloudflare/bindings.js +3 -2
- package/lib/cloudflare/bound.d.ts +0 -1
- package/lib/cloudflare/bound.js +0 -1
- package/lib/cloudflare/bucket.d.ts +7 -15
- package/lib/cloudflare/bucket.js +12 -19
- package/lib/cloudflare/durable-object-namespace.d.ts +0 -3
- package/lib/cloudflare/durable-object-namespace.js +0 -4
- package/lib/cloudflare/generate-asset-manifest.d.ts +0 -1
- package/lib/cloudflare/generate-asset-manifest.js +1 -2
- package/lib/cloudflare/index.d.ts +0 -1
- package/lib/cloudflare/index.js +0 -1
- package/lib/cloudflare/kv-namespace.d.ts +6 -9
- package/lib/cloudflare/kv-namespace.js +16 -20
- package/lib/cloudflare/state.d.ts +0 -1
- package/lib/cloudflare/state.js +0 -1
- package/lib/cloudflare/static-site-router.d.ts +0 -1
- package/lib/cloudflare/static-site-router.js +0 -4
- package/lib/cloudflare/static-site.d.ts +5 -11
- package/lib/cloudflare/static-site.js +16 -28
- package/lib/cloudflare/types.d.ts +0 -1
- package/lib/cloudflare/types.js +0 -1
- package/lib/cloudflare/upload-asset-manifest.d.ts +0 -1
- package/lib/cloudflare/upload-asset-manifest.js +0 -1
- package/lib/cloudflare/worker-metadata.d.ts +0 -1
- package/lib/cloudflare/worker-metadata.js +0 -1
- package/lib/cloudflare/worker-migration.d.ts +0 -1
- package/lib/cloudflare/worker-migration.js +0 -1
- package/lib/cloudflare/worker.d.ts +8 -14
- package/lib/cloudflare/worker.js +37 -49
- package/lib/context.d.ts +58 -0
- package/lib/context.js +36 -0
- package/lib/destroy.d.ts +10 -13
- package/lib/destroy.js +104 -43
- package/lib/{esbuild.d.ts → esbuild/bundle.d.ts} +4 -18
- package/lib/{esbuild.js → esbuild/bundle.js} +10 -24
- package/lib/esbuild/index.d.ts +1 -0
- package/lib/esbuild/index.js +1 -0
- package/lib/fs/file.d.ts +13 -0
- package/lib/fs/file.js +20 -0
- package/lib/fs/folder.d.ts +10 -0
- package/lib/fs/folder.js +16 -0
- package/lib/fs/index.d.ts +2 -0
- package/lib/fs/index.js +2 -0
- package/lib/github/client.d.ts +0 -1
- package/lib/github/client.js +0 -1
- package/lib/github/index.d.ts +0 -1
- package/lib/github/index.js +0 -1
- package/lib/github/secret.d.ts +4 -6
- package/lib/github/secret.js +10 -12
- package/lib/index.d.ts +6 -10
- package/lib/index.js +5 -9
- package/lib/resource.d.ts +34 -62
- package/lib/resource.js +29 -270
- package/lib/scope.d.ts +35 -14
- package/lib/scope.js +78 -23
- package/lib/secret.d.ts +2 -7
- package/lib/secret.js +7 -20
- package/lib/state.d.ts +14 -10
- package/lib/state.js +15 -14
- package/lib/stripe/index.d.ts +0 -1
- package/lib/stripe/index.js +0 -1
- package/lib/stripe/price.d.ts +4 -5
- package/lib/stripe/price.js +10 -20
- package/lib/stripe/product.d.ts +4 -5
- package/lib/stripe/product.js +10 -20
- package/lib/stripe/webhook.d.ts +4 -6
- package/lib/stripe/webhook.js +12 -25
- package/lib/test/bun.d.ts +19 -0
- package/lib/test/bun.js +55 -0
- package/lib/{utils → util}/content-type.d.ts +0 -1
- package/lib/{utils → util}/content-type.js +0 -1
- package/lib/{encrypt.d.ts → util/encrypt.d.ts} +0 -1
- package/lib/{encrypt.js → util/encrypt.js} +0 -1
- package/lib/{error.d.ts → util/ignore.d.ts} +0 -1
- package/lib/{error.js → util/ignore.js} +0 -1
- package/lib/{utils → util}/retry.d.ts +0 -1
- package/lib/{utils → util}/retry.js +0 -1
- package/lib/util/serde.d.ts +3 -0
- package/lib/{serde.js → util/serde.js} +19 -10
- package/lib/util/slugify.d.ts +1 -0
- package/lib/util/slugify.js +3 -0
- package/package.json +5 -4
- package/src/alchemy.ts +120 -0
- package/src/apply.ts +109 -114
- package/src/aws/bucket.ts +11 -14
- package/src/aws/function.ts +13 -33
- package/src/aws/index.ts +1 -0
- package/src/aws/oidc/github-oidc-provider.ts +14 -19
- package/src/aws/oidc/oidc-provider.ts +19 -12
- package/src/aws/policy-attachment.ts +51 -0
- package/src/aws/policy.ts +15 -60
- package/src/aws/queue.ts +17 -21
- package/src/aws/role.ts +25 -28
- package/src/aws/ses.ts +150 -164
- package/src/aws/table.ts +15 -15
- package/src/cloudflare/bindings.ts +8 -0
- package/src/cloudflare/bucket.ts +39 -47
- package/src/cloudflare/durable-object-namespace.ts +0 -10
- package/src/cloudflare/generate-asset-manifest.ts +1 -1
- package/src/cloudflare/kv-namespace.ts +35 -33
- package/src/cloudflare/static-site-router.ts +0 -3
- package/src/cloudflare/static-site.ts +44 -60
- package/src/cloudflare/worker.ts +105 -111
- package/src/context.ts +122 -0
- package/src/destroy.ts +124 -73
- package/src/{esbuild.ts → esbuild/bundle.ts} +16 -28
- package/src/esbuild/index.ts +1 -0
- package/src/fs/file.ts +39 -0
- package/src/fs/folder.ts +32 -0
- package/src/fs/index.ts +2 -0
- package/src/github/secret.ts +19 -16
- package/src/index.ts +7 -15
- package/src/resource.ts +101 -462
- package/src/scope.ts +101 -36
- package/src/secret.ts +9 -32
- package/src/state.ts +30 -21
- package/src/stripe/price.ts +17 -22
- package/src/stripe/product.ts +17 -22
- package/src/stripe/webhook.ts +21 -27
- package/src/test/bun.ts +105 -0
- package/src/util/serde.ts +53 -0
- package/src/util/slugify.ts +3 -0
- package/lib/$.d.ts +0 -12
- package/lib/$.d.ts.map +0 -1
- package/lib/$.js +0 -42
- package/lib/$.js.map +0 -1
- package/lib/alchemize.d.ts +0 -47
- package/lib/alchemize.d.ts.map +0 -1
- package/lib/alchemize.js +0 -116
- package/lib/alchemize.js.map +0 -1
- package/lib/apply.d.ts.map +0 -1
- package/lib/apply.js.map +0 -1
- package/lib/aws/account-id.d.ts.map +0 -1
- package/lib/aws/account-id.js.map +0 -1
- package/lib/aws/bucket.d.ts.map +0 -1
- package/lib/aws/bucket.js.map +0 -1
- package/lib/aws/function.d.ts.map +0 -1
- package/lib/aws/function.js.map +0 -1
- package/lib/aws/index.d.ts.map +0 -1
- package/lib/aws/index.js.map +0 -1
- package/lib/aws/oidc/github-oidc-provider.d.ts.map +0 -1
- package/lib/aws/oidc/github-oidc-provider.js.map +0 -1
- package/lib/aws/oidc/index.d.ts.map +0 -1
- package/lib/aws/oidc/index.js.map +0 -1
- package/lib/aws/oidc/oidc-provider.d.ts.map +0 -1
- package/lib/aws/oidc/oidc-provider.js.map +0 -1
- package/lib/aws/policy.d.ts.map +0 -1
- package/lib/aws/policy.js.map +0 -1
- package/lib/aws/queue.d.ts.map +0 -1
- package/lib/aws/queue.js.map +0 -1
- package/lib/aws/role.d.ts.map +0 -1
- package/lib/aws/role.js.map +0 -1
- package/lib/aws/ses.d.ts.map +0 -1
- package/lib/aws/ses.js.map +0 -1
- package/lib/aws/table.d.ts.map +0 -1
- package/lib/aws/table.js.map +0 -1
- package/lib/cloudflare/api.d.ts.map +0 -1
- package/lib/cloudflare/api.js.map +0 -1
- package/lib/cloudflare/asset-manifest.d.ts.map +0 -1
- package/lib/cloudflare/asset-manifest.js.map +0 -1
- package/lib/cloudflare/auth.d.ts.map +0 -1
- package/lib/cloudflare/auth.js.map +0 -1
- package/lib/cloudflare/bindings.d.ts.map +0 -1
- package/lib/cloudflare/bindings.js.map +0 -1
- package/lib/cloudflare/bound.d.ts.map +0 -1
- package/lib/cloudflare/bound.js.map +0 -1
- package/lib/cloudflare/bucket.d.ts.map +0 -1
- package/lib/cloudflare/bucket.js.map +0 -1
- package/lib/cloudflare/durable-object-namespace.d.ts.map +0 -1
- package/lib/cloudflare/durable-object-namespace.js.map +0 -1
- package/lib/cloudflare/generate-asset-manifest.d.ts.map +0 -1
- package/lib/cloudflare/generate-asset-manifest.js.map +0 -1
- package/lib/cloudflare/index.d.ts.map +0 -1
- package/lib/cloudflare/index.js.map +0 -1
- package/lib/cloudflare/kv-namespace.d.ts.map +0 -1
- package/lib/cloudflare/kv-namespace.js.map +0 -1
- package/lib/cloudflare/state.d.ts.map +0 -1
- package/lib/cloudflare/state.js.map +0 -1
- package/lib/cloudflare/static-site-router.d.ts.map +0 -1
- package/lib/cloudflare/static-site-router.js.map +0 -1
- package/lib/cloudflare/static-site.d.ts.map +0 -1
- package/lib/cloudflare/static-site.js.map +0 -1
- package/lib/cloudflare/types.d.ts.map +0 -1
- package/lib/cloudflare/types.js.map +0 -1
- package/lib/cloudflare/upload-asset-manifest.d.ts.map +0 -1
- package/lib/cloudflare/upload-asset-manifest.js.map +0 -1
- package/lib/cloudflare/worker-metadata.d.ts.map +0 -1
- package/lib/cloudflare/worker-metadata.js.map +0 -1
- package/lib/cloudflare/worker-migration.d.ts.map +0 -1
- package/lib/cloudflare/worker-migration.js.map +0 -1
- package/lib/cloudflare/worker.d.ts.map +0 -1
- package/lib/cloudflare/worker.js.map +0 -1
- package/lib/destroy.d.ts.map +0 -1
- package/lib/destroy.js.map +0 -1
- package/lib/encrypt.d.ts.map +0 -1
- package/lib/encrypt.js.map +0 -1
- package/lib/error.d.ts.map +0 -1
- package/lib/error.js.map +0 -1
- package/lib/esbuild.d.ts.map +0 -1
- package/lib/esbuild.js.map +0 -1
- package/lib/fs.d.ts +0 -12
- package/lib/fs.d.ts.map +0 -1
- package/lib/fs.js +0 -41
- package/lib/fs.js.map +0 -1
- package/lib/github/client.d.ts.map +0 -1
- package/lib/github/client.js.map +0 -1
- package/lib/github/index.d.ts.map +0 -1
- package/lib/github/index.js.map +0 -1
- package/lib/github/secret.d.ts.map +0 -1
- package/lib/github/secret.js.map +0 -1
- package/lib/global.d.ts +0 -16
- package/lib/global.d.ts.map +0 -1
- package/lib/global.js +0 -42
- package/lib/global.js.map +0 -1
- package/lib/index.d.ts.map +0 -1
- package/lib/index.js.map +0 -1
- package/lib/input.d.ts +0 -7
- package/lib/input.d.ts.map +0 -1
- package/lib/input.js +0 -2
- package/lib/input.js.map +0 -1
- package/lib/main.d.ts +0 -3
- package/lib/main.d.ts.map +0 -1
- package/lib/main.js +0 -32
- package/lib/main.js.map +0 -1
- package/lib/output.d.ts +0 -25
- package/lib/output.d.ts.map +0 -1
- package/lib/output.js +0 -23
- package/lib/output.js.map +0 -1
- package/lib/print.d.ts +0 -7
- package/lib/print.d.ts.map +0 -1
- package/lib/print.js +0 -10
- package/lib/print.js.map +0 -1
- package/lib/resource.d.ts.map +0 -1
- package/lib/resource.js.map +0 -1
- package/lib/scope.d.ts.map +0 -1
- package/lib/scope.js.map +0 -1
- package/lib/secret.d.ts.map +0 -1
- package/lib/secret.js.map +0 -1
- package/lib/serde.d.ts +0 -3
- package/lib/serde.d.ts.map +0 -1
- package/lib/serde.js.map +0 -1
- package/lib/slug.d.ts +0 -2
- package/lib/slug.d.ts.map +0 -1
- package/lib/slug.js +0 -4
- package/lib/slug.js.map +0 -1
- package/lib/state.d.ts.map +0 -1
- package/lib/state.js.map +0 -1
- package/lib/stripe/index.d.ts.map +0 -1
- package/lib/stripe/index.js.map +0 -1
- package/lib/stripe/price.d.ts.map +0 -1
- package/lib/stripe/price.js.map +0 -1
- package/lib/stripe/product.d.ts.map +0 -1
- package/lib/stripe/product.js.map +0 -1
- package/lib/stripe/webhook.d.ts.map +0 -1
- package/lib/stripe/webhook.js.map +0 -1
- package/lib/utils/content-type.d.ts.map +0 -1
- package/lib/utils/content-type.js.map +0 -1
- package/lib/utils/retry.d.ts.map +0 -1
- package/lib/utils/retry.js.map +0 -1
- package/src/$.ts +0 -69
- package/src/alchemize.ts +0 -195
- package/src/fs.ts +0 -47
- package/src/global.ts +0 -63
- package/src/input.ts +0 -19
- package/src/main.ts +0 -34
- package/src/output.ts +0 -75
- package/src/print.ts +0 -18
- package/src/serde.ts +0 -46
- package/src/slug.ts +0 -3
- /package/src/{utils → util}/content-type.ts +0 -0
- /package/src/{encrypt.ts → util/encrypt.ts} +0 -0
- /package/src/{error.ts → util/ignore.ts} +0 -0
- /package/src/{utils → util}/retry.ts +0 -0
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Context } from "../context";
|
|
2
|
+
import { Resource } from "../resource";
|
|
3
|
+
export declare function isKVNamespace(resource: any): resource is KVNamespace;
|
|
2
4
|
/**
|
|
3
5
|
* Properties for creating or updating a KV Namespace
|
|
4
6
|
*/
|
|
@@ -41,12 +43,12 @@ export interface KVPair {
|
|
|
41
43
|
/**
|
|
42
44
|
* Output returned after KV Namespace creation/update
|
|
43
45
|
*/
|
|
44
|
-
export interface
|
|
46
|
+
export interface KVNamespace extends Resource<"cloudflare::KVNamespace">, KVNamespaceProps {
|
|
45
47
|
type: "kv_namespace";
|
|
46
48
|
/**
|
|
47
49
|
* The ID of the namespace
|
|
48
50
|
*/
|
|
49
|
-
|
|
51
|
+
namespaceId: string;
|
|
50
52
|
/**
|
|
51
53
|
* Time at which the namespace was created
|
|
52
54
|
*/
|
|
@@ -56,9 +58,4 @@ export interface KVNamespaceOutput extends KVNamespaceProps {
|
|
|
56
58
|
*/
|
|
57
59
|
modifiedAt: number;
|
|
58
60
|
}
|
|
59
|
-
export declare
|
|
60
|
-
declare const KVNamespace_base: import("../resource").Provider<"cloudflare::KVNamespace", [props: KVNamespaceProps], KVNamespaceOutput>;
|
|
61
|
-
export declare class KVNamespace extends KVNamespace_base {
|
|
62
|
-
}
|
|
63
|
-
export {};
|
|
64
|
-
//# sourceMappingURL=kv-namespace.d.ts.map
|
|
61
|
+
export declare const KVNamespace: ((this: Context<KVNamespace>, id: string, props: KVNamespaceProps) => Promise<KVNamespace>) | (((this: any, id: string, props: KVNamespaceProps) => never) & (new (_: never) => never));
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { Resource } from "../resource";
|
|
2
|
-
import { withExponentialBackoff } from "../
|
|
2
|
+
import { withExponentialBackoff } from "../util/retry";
|
|
3
3
|
import { createCloudflareApi } from "./api";
|
|
4
4
|
export function isKVNamespace(resource) {
|
|
5
5
|
return (resource && typeof resource === "object" && resource.type === "kv_namespace");
|
|
6
6
|
}
|
|
7
|
-
export
|
|
7
|
+
export const KVNamespace = Resource("cloudflare::KVNamespace", async function (id, props) {
|
|
8
8
|
// Create Cloudflare API client with automatic account discovery
|
|
9
9
|
const api = await createCloudflareApi();
|
|
10
|
-
if (
|
|
10
|
+
if (this.phase === "delete") {
|
|
11
11
|
// For delete operations, we need to check if the namespace ID exists in the output
|
|
12
|
-
const namespaceId =
|
|
12
|
+
const namespaceId = this.output?.namespaceId;
|
|
13
13
|
if (namespaceId) {
|
|
14
14
|
// Delete KV namespace
|
|
15
15
|
const deleteResponse = await api.delete(`/accounts/${api.accountId}/storage/kv/namespaces/${namespaceId}`);
|
|
@@ -21,19 +21,20 @@ export class KVNamespace extends Resource("cloudflare::KVNamespace", async (ctx,
|
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
// Return minimal output for deleted state
|
|
24
|
-
return;
|
|
24
|
+
return this.destroy();
|
|
25
25
|
}
|
|
26
26
|
else {
|
|
27
27
|
// For create or update operations
|
|
28
|
-
// If
|
|
29
|
-
let namespaceId =
|
|
30
|
-
let createdAt =
|
|
31
|
-
?
|
|
28
|
+
// If this.phase is "update", we expect this.output to exist
|
|
29
|
+
let namespaceId = this.phase === "update" ? this.output?.namespaceId || "" : "";
|
|
30
|
+
let createdAt = this.phase === "update"
|
|
31
|
+
? this.output?.createdAt || Date.now()
|
|
32
32
|
: Date.now();
|
|
33
|
-
if (
|
|
33
|
+
if (this.phase === "update" && namespaceId) {
|
|
34
34
|
// Can't update a KV namespace title directly, just work with existing ID
|
|
35
35
|
}
|
|
36
36
|
else {
|
|
37
|
+
// TODO: if it already exists, then check the tags to see if we own it and continue
|
|
37
38
|
// Create new KV namespace
|
|
38
39
|
const createResponse = await api.post(`/accounts/${api.accountId}/storage/kv/namespaces`, {
|
|
39
40
|
title: props.title,
|
|
@@ -97,18 +98,13 @@ export class KVNamespace extends Resource("cloudflare::KVNamespace", async (ctx,
|
|
|
97
98
|
}
|
|
98
99
|
}
|
|
99
100
|
}
|
|
100
|
-
|
|
101
|
-
// Construct the output
|
|
102
|
-
const output = {
|
|
101
|
+
return this({
|
|
103
102
|
type: "kv_namespace",
|
|
104
|
-
|
|
103
|
+
namespaceId: namespaceId,
|
|
105
104
|
title: props.title,
|
|
106
105
|
values: props.values,
|
|
107
106
|
createdAt: createdAt,
|
|
108
|
-
modifiedAt: now,
|
|
109
|
-
};
|
|
110
|
-
return output;
|
|
107
|
+
modifiedAt: Date.now(),
|
|
108
|
+
});
|
|
111
109
|
}
|
|
112
|
-
})
|
|
113
|
-
}
|
|
114
|
-
//# sourceMappingURL=kv-namespace.js.map
|
|
110
|
+
});
|
package/lib/cloudflare/state.js
CHANGED
|
@@ -7,14 +7,12 @@ export default {
|
|
|
7
7
|
isInit = true;
|
|
8
8
|
for (const [key, value] of Object.entries(env)) {
|
|
9
9
|
if (key.startsWith("ROUTE_")) {
|
|
10
|
-
console.log(key, value);
|
|
11
10
|
const service = value;
|
|
12
11
|
const patternKey = `__${key}`;
|
|
13
12
|
if (!(patternKey in env)) {
|
|
14
13
|
throw new Error(`Missing pattern key: ${patternKey}`);
|
|
15
14
|
}
|
|
16
15
|
const pattern = env[`__${key}`];
|
|
17
|
-
console.log("pattern", pattern);
|
|
18
16
|
// TODO(sam): we should support narrowing down METHOD
|
|
19
17
|
app.all(pattern, (ctx) => service.fetch(ctx.req.raw));
|
|
20
18
|
}
|
|
@@ -25,7 +23,6 @@ export default {
|
|
|
25
23
|
};
|
|
26
24
|
// fall back to assuming the request is a static file
|
|
27
25
|
app.notFound(async (ctx) => {
|
|
28
|
-
console.log("not found");
|
|
29
26
|
const request = ctx.req;
|
|
30
27
|
const env = ctx.env;
|
|
31
28
|
const url = new URL(request.url);
|
|
@@ -107,4 +104,3 @@ app.notFound(async (ctx) => {
|
|
|
107
104
|
return response;
|
|
108
105
|
}
|
|
109
106
|
});
|
|
110
|
-
//# sourceMappingURL=static-site-router.js.map
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Context } from "../context";
|
|
2
|
+
import type { BundleProps } from "../esbuild/bundle";
|
|
3
|
+
import { Resource } from "../resource";
|
|
2
4
|
import { Worker } from "./worker";
|
|
3
5
|
/**
|
|
4
6
|
* Idea of using KV with timeout inspired by SST: https://github.com/sst/sst/blob/dev/platform/src/components/cloudflare/static-site.ts
|
|
@@ -98,11 +100,7 @@ export interface StaticSiteProps {
|
|
|
98
100
|
/**
|
|
99
101
|
* Output returned after StaticSite creation/update
|
|
100
102
|
*/
|
|
101
|
-
export interface
|
|
102
|
-
/**
|
|
103
|
-
* The ID of the site
|
|
104
|
-
*/
|
|
105
|
-
id: string;
|
|
103
|
+
export interface StaticSite extends Resource<"cloudflare::StaticSite"> {
|
|
106
104
|
/**
|
|
107
105
|
* The ID of the worker
|
|
108
106
|
*/
|
|
@@ -159,8 +157,4 @@ export interface StaticSiteOutput {
|
|
|
159
157
|
*/
|
|
160
158
|
routes?: Record<string, string>;
|
|
161
159
|
}
|
|
162
|
-
declare const
|
|
163
|
-
export declare class StaticSite extends StaticSite_base {
|
|
164
|
-
}
|
|
165
|
-
export {};
|
|
166
|
-
//# sourceMappingURL=static-site.d.ts.map
|
|
160
|
+
export declare const StaticSite: ((this: Context<StaticSite>, id: string, props: StaticSiteProps) => Promise<StaticSite>) | (((this: any, id: string, props: StaticSiteProps) => never) & (new (_: never) => never));
|
|
@@ -2,7 +2,6 @@ import { exec } from "child_process";
|
|
|
2
2
|
import { promises as fs } from "fs";
|
|
3
3
|
import path from "path";
|
|
4
4
|
import { promisify } from "util";
|
|
5
|
-
import { apply } from "../apply";
|
|
6
5
|
import { Resource } from "../resource";
|
|
7
6
|
import { createCloudflareApi } from "./api";
|
|
8
7
|
import { generateAssetManifest } from "./generate-asset-manifest";
|
|
@@ -10,13 +9,13 @@ import { KVNamespace } from "./kv-namespace";
|
|
|
10
9
|
import { uploadAssetManifest } from "./upload-asset-manifest";
|
|
11
10
|
import { Worker } from "./worker";
|
|
12
11
|
const __dirname = import.meta.dirname;
|
|
13
|
-
export
|
|
12
|
+
export const StaticSite = Resource("cloudflare::StaticSite", {
|
|
14
13
|
alwaysUpdate: true,
|
|
15
|
-
}, async (
|
|
16
|
-
if (
|
|
14
|
+
}, async function (id, props) {
|
|
15
|
+
if (this.phase === "delete") {
|
|
17
16
|
// For delete operations, we'll rely on the Worker delete to clean up
|
|
18
17
|
// Return empty output for deleted state
|
|
19
|
-
return;
|
|
18
|
+
return this.destroy();
|
|
20
19
|
}
|
|
21
20
|
// Validate that a name is provided
|
|
22
21
|
if (!props.name) {
|
|
@@ -38,14 +37,14 @@ export class StaticSite extends Resource("cloudflare::StaticSite", {
|
|
|
38
37
|
// Run build command if provided
|
|
39
38
|
if (props.build?.command) {
|
|
40
39
|
try {
|
|
41
|
-
if (!
|
|
40
|
+
if (!this.quiet) {
|
|
42
41
|
console.log(props.build.command);
|
|
43
42
|
}
|
|
44
43
|
const execAsync = promisify(exec);
|
|
45
44
|
const { stdout, stderr } = await execAsync(props.build.command, {
|
|
46
45
|
cwd: process.cwd(),
|
|
47
46
|
});
|
|
48
|
-
if (stdout && !
|
|
47
|
+
if (stdout && !this.quiet)
|
|
49
48
|
console.log(stdout);
|
|
50
49
|
}
|
|
51
50
|
catch (error) {
|
|
@@ -65,17 +64,14 @@ export class StaticSite extends Resource("cloudflare::StaticSite", {
|
|
|
65
64
|
// Create Cloudflare API client with automatic account discovery
|
|
66
65
|
const api = await createCloudflareApi();
|
|
67
66
|
// Step 1: Create or get the KV namespace for assets
|
|
68
|
-
const kv =
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const [{ id: kvNamespaceId }, assetManifest] = await Promise.all([
|
|
72
|
-
apply(kv, {
|
|
73
|
-
quiet: ctx.quiet,
|
|
67
|
+
const [kv, assetManifest] = await Promise.all([
|
|
68
|
+
KVNamespace("assets", {
|
|
69
|
+
title: `${siteName}-assets`,
|
|
74
70
|
}),
|
|
75
71
|
generateAssetManifest(props.dir),
|
|
76
72
|
]);
|
|
77
73
|
// Step 3: Upload assets to KV
|
|
78
|
-
await uploadAssetManifest(api,
|
|
74
|
+
await uploadAssetManifest(api, kv.namespaceId, assetManifest);
|
|
79
75
|
// Prepare the bindings for the worker
|
|
80
76
|
const bindings = {
|
|
81
77
|
ASSETS: kv,
|
|
@@ -115,7 +111,7 @@ export class StaticSite extends Resource("cloudflare::StaticSite", {
|
|
|
115
111
|
catch (error) {
|
|
116
112
|
// TypeScript file doesn't exist, use JavaScript (default)
|
|
117
113
|
}
|
|
118
|
-
const worker =
|
|
114
|
+
const worker = await Worker("worker", {
|
|
119
115
|
name: siteName,
|
|
120
116
|
url: true,
|
|
121
117
|
format: props.format || "esm",
|
|
@@ -133,14 +129,9 @@ export class StaticSite extends Resource("cloudflare::StaticSite", {
|
|
|
133
129
|
key,
|
|
134
130
|
])),
|
|
135
131
|
},
|
|
136
|
-
}
|
|
137
|
-
// place a dependency on the namespace
|
|
138
|
-
kv.id);
|
|
139
|
-
// Get current timestamp
|
|
132
|
+
});
|
|
140
133
|
const now = Date.now();
|
|
141
|
-
|
|
142
|
-
const output = {
|
|
143
|
-
id: siteName,
|
|
134
|
+
return this({
|
|
144
135
|
workerId: worker.id,
|
|
145
136
|
name: siteName,
|
|
146
137
|
directory: props.dir,
|
|
@@ -148,14 +139,11 @@ export class StaticSite extends Resource("cloudflare::StaticSite", {
|
|
|
148
139
|
errorPage: props.errorPage,
|
|
149
140
|
indexPage,
|
|
150
141
|
assets: assetManifest.map((item) => item.key),
|
|
151
|
-
createdAt:
|
|
142
|
+
createdAt: this.output?.createdAt || now,
|
|
152
143
|
updatedAt: now,
|
|
153
144
|
domain: props.domain,
|
|
154
145
|
production: props.production !== false,
|
|
155
146
|
url: worker.url,
|
|
156
147
|
routes,
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
}) {
|
|
160
|
-
}
|
|
161
|
-
//# sourceMappingURL=static-site.js.map
|
|
148
|
+
});
|
|
149
|
+
});
|
package/lib/cloudflare/types.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type
|
|
3
|
-
import
|
|
4
|
-
import type
|
|
1
|
+
import type { Context } from "../context";
|
|
2
|
+
import { type BundleProps } from "../esbuild/bundle";
|
|
3
|
+
import { Resource } from "../resource";
|
|
4
|
+
import { type Bindings } from "./bindings";
|
|
5
5
|
import type { Bound } from "./bound";
|
|
6
6
|
import type { SingleStepMigration } from "./worker-migration";
|
|
7
7
|
/**
|
|
@@ -78,7 +78,7 @@ export interface WorkerProps<B extends Bindings = Bindings> {
|
|
|
78
78
|
/**
|
|
79
79
|
* Output returned after Worker creation/update
|
|
80
80
|
*/
|
|
81
|
-
export interface
|
|
81
|
+
export interface Worker<B extends Bindings = Bindings> extends Resource<"cloudflare::Worker">, Omit<WorkerProps<B>, "url"> {
|
|
82
82
|
type: "service";
|
|
83
83
|
/**
|
|
84
84
|
* The ID of the worker
|
|
@@ -100,15 +100,9 @@ export interface WorkerOutput<B extends Bindings = Bindings> extends Omit<Worker
|
|
|
100
100
|
/**
|
|
101
101
|
* The bindings that were created
|
|
102
102
|
*/
|
|
103
|
-
bindings: B;
|
|
104
|
-
|
|
105
|
-
export declare function isWorker(resource: any): resource is Resolved<Worker>;
|
|
106
|
-
declare const Worker_base: import("../resource").Provider<string, [props: WorkerProps<Bindings>], WorkerOutput<Bindings>>;
|
|
107
|
-
export declare class Worker<B extends Bindings = Bindings> extends Worker_base {
|
|
108
|
-
readonly Env: {
|
|
103
|
+
bindings: B | undefined;
|
|
104
|
+
Env: {
|
|
109
105
|
[bindingName in keyof B]: Bound<B[bindingName]>;
|
|
110
106
|
};
|
|
111
|
-
constructor(id: string, input: Input<WorkerProps<B>>, ...inputs: any[]);
|
|
112
107
|
}
|
|
113
|
-
export
|
|
114
|
-
//# sourceMappingURL=worker.d.ts.map
|
|
108
|
+
export declare const Worker: (<const B extends Bindings>(this: Context<Worker<NoInfer<B>>>, id: string, props: WorkerProps<B>) => Promise<Worker<B>>) | (((this: any, id: string, props: WorkerProps<Bindings>) => never) & (new (_: never) => never));
|
package/lib/cloudflare/worker.js
CHANGED
|
@@ -1,24 +1,15 @@
|
|
|
1
1
|
import * as fs from "fs/promises";
|
|
2
|
-
import {
|
|
3
|
-
import { Bundle } from "../esbuild";
|
|
2
|
+
import { Bundle } from "../esbuild/bundle";
|
|
4
3
|
import { Resource } from "../resource";
|
|
5
|
-
import {
|
|
6
|
-
import { withExponentialBackoff } from "../
|
|
4
|
+
import { isSecret } from "../secret";
|
|
5
|
+
import { withExponentialBackoff } from "../util/retry";
|
|
6
|
+
import { slugify } from "../util/slugify";
|
|
7
7
|
import { createCloudflareApi } from "./api";
|
|
8
|
-
import {
|
|
9
|
-
import { isDurableObjectNamespace } from "./durable-object-namespace";
|
|
8
|
+
import { isDurableObjectNamespace, } from "./bindings";
|
|
10
9
|
import { isKVNamespace } from "./kv-namespace";
|
|
11
|
-
export
|
|
12
|
-
return resource && typeof resource === "object" && resource.type === "worker";
|
|
13
|
-
}
|
|
14
|
-
export class Worker extends Resource("cloudflare::Worker", {
|
|
10
|
+
export const Worker = Resource("cloudflare::Worker", {
|
|
15
11
|
alwaysUpdate: true,
|
|
16
|
-
},
|
|
17
|
-
constructor(id, input, ...inputs) {
|
|
18
|
-
super(id, input, ...inputs);
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
async function applyWorker(ctx, props) {
|
|
12
|
+
}, async function (id, props) {
|
|
22
13
|
// Create Cloudflare API client with automatic account discovery
|
|
23
14
|
const api = await createCloudflareApi();
|
|
24
15
|
// Use the provided name
|
|
@@ -27,30 +18,31 @@ async function applyWorker(ctx, props) {
|
|
|
27
18
|
if (!props.script && !props.entrypoint) {
|
|
28
19
|
throw new Error("One of script or entryPoint must be provided");
|
|
29
20
|
}
|
|
30
|
-
if (
|
|
31
|
-
|
|
21
|
+
if (this.phase === "delete") {
|
|
22
|
+
await deleteWorker(this, api, workerName);
|
|
23
|
+
return this.destroy();
|
|
32
24
|
}
|
|
33
|
-
else if (
|
|
34
|
-
await assertWorkerDoesNotExist(
|
|
25
|
+
else if (this.phase === "create") {
|
|
26
|
+
await assertWorkerDoesNotExist(this, api, workerName);
|
|
35
27
|
}
|
|
36
|
-
const oldBindings = await
|
|
37
|
-
const scriptMetadata = await prepareWorkerMetadata(
|
|
28
|
+
const oldBindings = await this.get("bindings");
|
|
29
|
+
const scriptMetadata = await prepareWorkerMetadata(this, oldBindings, props);
|
|
38
30
|
// Get the script content - either from props.script, or by bundling
|
|
39
|
-
const scriptContent = props.script ?? (await bundleWorkerScript(
|
|
31
|
+
const scriptContent = props.script ?? (await bundleWorkerScript(props));
|
|
40
32
|
// Upload the worker script
|
|
41
|
-
await putWorker(
|
|
33
|
+
await putWorker(api, workerName, scriptContent, scriptMetadata);
|
|
42
34
|
// TODO: it is less than ideal that this can fail, resulting in state problem
|
|
43
|
-
await
|
|
35
|
+
await this.set("bindings", props.bindings);
|
|
44
36
|
// Handle routes if requested
|
|
45
37
|
await setupRoutes(api, workerName, props.routes || []);
|
|
46
38
|
// Handle worker URL if requested
|
|
47
|
-
const workerUrl = await configureURL(
|
|
39
|
+
const workerUrl = await configureURL(this, api, workerName, props.url ?? false);
|
|
48
40
|
// Get current timestamp
|
|
49
41
|
const now = Date.now();
|
|
50
42
|
// Construct the output
|
|
51
|
-
|
|
43
|
+
return this({
|
|
52
44
|
type: "service",
|
|
53
|
-
id
|
|
45
|
+
id,
|
|
54
46
|
name: workerName,
|
|
55
47
|
script: scriptContent,
|
|
56
48
|
format: props.format || "esm", // Include format in the output
|
|
@@ -61,9 +53,10 @@ async function applyWorker(ctx, props) {
|
|
|
61
53
|
createdAt: now,
|
|
62
54
|
updatedAt: now,
|
|
63
55
|
url: workerUrl,
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
56
|
+
// phantom property
|
|
57
|
+
Env: undefined,
|
|
58
|
+
});
|
|
59
|
+
});
|
|
67
60
|
async function deleteWorker(ctx, api, workerName) {
|
|
68
61
|
// Delete worker
|
|
69
62
|
const deleteResponse = await api.delete(`/accounts/${api.accountId}/workers/scripts/${workerName}`);
|
|
@@ -107,7 +100,7 @@ async function deleteWorker(ctx, api, workerName) {
|
|
|
107
100
|
// Return minimal output for deleted state
|
|
108
101
|
return;
|
|
109
102
|
}
|
|
110
|
-
async function putWorker(
|
|
103
|
+
async function putWorker(api, workerName, scriptContent, scriptMetadata) {
|
|
111
104
|
return withExponentialBackoff(async () => {
|
|
112
105
|
const scriptName = scriptMetadata.main_module ?? scriptMetadata.body_part;
|
|
113
106
|
// Create FormData for the upload
|
|
@@ -118,7 +111,6 @@ async function putWorker(ctx, api, workerName, scriptContent, scriptMetadata) {
|
|
|
118
111
|
? "application/javascript+module"
|
|
119
112
|
: "application/javascript",
|
|
120
113
|
}), scriptName);
|
|
121
|
-
// console.log("Script Metadata:", JSON.stringify(scriptMetadata, null, 2));
|
|
122
114
|
// Add metadata as JSON
|
|
123
115
|
formData.append("metadata", new Blob([JSON.stringify(scriptMetadata)], {
|
|
124
116
|
type: "application/json",
|
|
@@ -158,7 +150,8 @@ async function prepareWorkerMetadata(ctx, oldBindings, props) {
|
|
|
158
150
|
observability: {
|
|
159
151
|
enabled: props.observability?.enabled !== false,
|
|
160
152
|
},
|
|
161
|
-
|
|
153
|
+
// TODO(sam): base64 encode instead? 0 collision risk vs readability.
|
|
154
|
+
tags: [`alchemy:id:${slugify(ctx.fqn)}`],
|
|
162
155
|
migrations: {
|
|
163
156
|
new_classes: props.migrations?.new_classes ?? [],
|
|
164
157
|
deleted_classes: props.migrations?.deleted_classes ?? [],
|
|
@@ -175,7 +168,7 @@ async function prepareWorkerMetadata(ctx, oldBindings, props) {
|
|
|
175
168
|
meta.bindings.push({
|
|
176
169
|
type: "kv_namespace",
|
|
177
170
|
name: bindingName,
|
|
178
|
-
namespace_id: binding.
|
|
171
|
+
namespace_id: binding.namespaceId,
|
|
179
172
|
});
|
|
180
173
|
}
|
|
181
174
|
else if (typeof binding === "string") {
|
|
@@ -192,7 +185,7 @@ async function prepareWorkerMetadata(ctx, oldBindings, props) {
|
|
|
192
185
|
service: binding.id,
|
|
193
186
|
});
|
|
194
187
|
}
|
|
195
|
-
else if (
|
|
188
|
+
else if (binding.type === "durable_object_namespace") {
|
|
196
189
|
const stableId = binding.id;
|
|
197
190
|
const className = binding.className;
|
|
198
191
|
meta.bindings.push({
|
|
@@ -221,14 +214,14 @@ async function prepareWorkerMetadata(ctx, oldBindings, props) {
|
|
|
221
214
|
});
|
|
222
215
|
}
|
|
223
216
|
}
|
|
224
|
-
else if (
|
|
217
|
+
else if (binding.type === "r2_bucket") {
|
|
225
218
|
meta.bindings.push({
|
|
226
219
|
type: "r2_bucket",
|
|
227
220
|
name: bindingName,
|
|
228
|
-
bucket_name: binding.name
|
|
221
|
+
bucket_name: binding.name,
|
|
229
222
|
});
|
|
230
223
|
}
|
|
231
|
-
else if (binding
|
|
224
|
+
else if (isSecret(binding)) {
|
|
232
225
|
meta.bindings.push({
|
|
233
226
|
type: "secret_text",
|
|
234
227
|
name: bindingName,
|
|
@@ -273,7 +266,7 @@ async function assertWorkerDoesNotExist(ctx, api, workerName) {
|
|
|
273
266
|
if (!metadata) {
|
|
274
267
|
throw new Error(`Worker exists but failed to fetch metadata: ${response.status} ${response.statusText}`);
|
|
275
268
|
}
|
|
276
|
-
if (metadata.default_environment?.script.tags.includes(`alchemy:id:${ctx.
|
|
269
|
+
if (metadata.default_environment?.script.tags.includes(`alchemy:id:${slugify(ctx.fqn)}`)) {
|
|
277
270
|
return true;
|
|
278
271
|
}
|
|
279
272
|
throw new Error(`Worker with name '${workerName}' already exists. Please use a unique name.`);
|
|
@@ -282,7 +275,7 @@ async function assertWorkerDoesNotExist(ctx, api, workerName) {
|
|
|
282
275
|
throw new Error(`Error checking if worker exists: ${response.status} ${response.statusText}`);
|
|
283
276
|
}
|
|
284
277
|
}
|
|
285
|
-
async function bundleWorkerScript(
|
|
278
|
+
async function bundleWorkerScript(props) {
|
|
286
279
|
// Get the script content - either from props.script, or by bundling
|
|
287
280
|
// Create and use a Bundle resource with worker-optimized configuration
|
|
288
281
|
const defaultBundleOptions = {
|
|
@@ -300,17 +293,13 @@ async function bundleWorkerScript(ctx, props) {
|
|
|
300
293
|
...(props.bundle || {}),
|
|
301
294
|
};
|
|
302
295
|
// Create the bundle
|
|
303
|
-
const bundle =
|
|
296
|
+
const bundle = await Bundle("bundle", {
|
|
304
297
|
entryPoint: props.entrypoint,
|
|
305
298
|
...bundleOptions,
|
|
306
299
|
external: [...(bundleOptions.external ?? []), "cloudflare:workers"],
|
|
307
300
|
});
|
|
308
|
-
// Get the bundled content
|
|
309
|
-
const bundleOutput = await apply(bundle, {
|
|
310
|
-
quiet: ctx.quiet,
|
|
311
|
-
});
|
|
312
301
|
try {
|
|
313
|
-
return await fs.readFile(
|
|
302
|
+
return await fs.readFile(bundle.path, "utf-8");
|
|
314
303
|
}
|
|
315
304
|
catch (error) {
|
|
316
305
|
console.error("Error reading bundle:", error);
|
|
@@ -374,7 +363,7 @@ async function configureURL(ctx, api, workerName, url) {
|
|
|
374
363
|
// Add a delay when the subdomain is first created.
|
|
375
364
|
// This is to prevent an issue where a negative cache-hit
|
|
376
365
|
// causes the subdomain to be unavailable for 30 seconds.
|
|
377
|
-
if (ctx.
|
|
366
|
+
if (ctx.phase === "create" || !ctx.output?.url) {
|
|
378
367
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
379
368
|
}
|
|
380
369
|
}
|
|
@@ -416,4 +405,3 @@ async function getWorkerBindings(api, workerName, environment = "production") {
|
|
|
416
405
|
const data = await response.json();
|
|
417
406
|
return data.result;
|
|
418
407
|
}
|
|
419
|
-
//# sourceMappingURL=worker.js.map
|
package/lib/context.d.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { Resource, ResourceFQN, ResourceID, ResourceKind, ResourceProps } from "./resource";
|
|
2
|
+
import type { Scope } from "./scope";
|
|
3
|
+
import type { State } from "./state";
|
|
4
|
+
export type Context<Out extends Resource> = CreateContext<Out> | UpdateContext<Out> | DeleteContext<Out>;
|
|
5
|
+
export interface CreateContext<Out extends Resource> extends BaseContext<Out> {
|
|
6
|
+
phase: "create";
|
|
7
|
+
output?: undefined;
|
|
8
|
+
}
|
|
9
|
+
export interface UpdateContext<Out extends Resource> extends BaseContext<Out> {
|
|
10
|
+
phase: "update";
|
|
11
|
+
output: Out;
|
|
12
|
+
}
|
|
13
|
+
export interface DeleteContext<Out extends Resource> extends BaseContext<Out> {
|
|
14
|
+
phase: "delete";
|
|
15
|
+
output: Out;
|
|
16
|
+
}
|
|
17
|
+
export interface BaseContext<Out extends Resource> {
|
|
18
|
+
quiet: boolean;
|
|
19
|
+
stage: string;
|
|
20
|
+
id: ResourceID;
|
|
21
|
+
fqn: ResourceFQN;
|
|
22
|
+
scope: Scope;
|
|
23
|
+
get<T>(key: string): Promise<T | undefined>;
|
|
24
|
+
set<T>(key: string, value: T): Promise<void>;
|
|
25
|
+
delete<T>(key: string): Promise<T | undefined>;
|
|
26
|
+
/**
|
|
27
|
+
* Indicate that this resource is being replaced.
|
|
28
|
+
* This will cause the resource to be deleted at the end of the stack's CREATE phase.
|
|
29
|
+
*/
|
|
30
|
+
replace(): void;
|
|
31
|
+
/**
|
|
32
|
+
* Terminate the resource lifecycle handler and destroy the resource.
|
|
33
|
+
*
|
|
34
|
+
* This is the final operation performed during a delete operation.
|
|
35
|
+
*
|
|
36
|
+
* It is so that the resource lifecycle handler can "return never" instead of
|
|
37
|
+
* "return undefined" so that `await MyResource()` always returns a value.
|
|
38
|
+
*/
|
|
39
|
+
destroy(): never;
|
|
40
|
+
/**
|
|
41
|
+
* Create the Resource envelope (with Alchemy + User properties)
|
|
42
|
+
*/
|
|
43
|
+
create(props: Omit<Out, keyof Resource>): Out;
|
|
44
|
+
/**
|
|
45
|
+
* Create the Resource envelope (with Alchemy + User properties)
|
|
46
|
+
*/
|
|
47
|
+
(props: Omit<Out, keyof Resource>): Out;
|
|
48
|
+
}
|
|
49
|
+
export declare function context<Kind extends string, Props extends ResourceProps, Out extends Resource>({ scope, phase, kind, id, fqn, seq, state, replace, }: {
|
|
50
|
+
scope: Scope;
|
|
51
|
+
phase: "create" | "update" | "delete";
|
|
52
|
+
kind: ResourceKind;
|
|
53
|
+
id: ResourceID;
|
|
54
|
+
fqn: ResourceFQN;
|
|
55
|
+
seq: number;
|
|
56
|
+
state: State<Kind, Props, Out>;
|
|
57
|
+
replace: () => void;
|
|
58
|
+
}): Context<Out>;
|