alchemy 0.69.0 → 0.70.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/bin/alchemy.js +3810 -3889
- package/bin/commands/init.ts +41 -55
- package/bin/trpc.ts +4 -9
- package/lib/alchemy.d.ts +3 -3
- package/lib/alchemy.d.ts.map +1 -1
- package/lib/alchemy.js +2 -15
- package/lib/alchemy.js.map +1 -1
- package/lib/apply.js +24 -9
- package/lib/apply.js.map +1 -1
- package/lib/aws/utils.d.ts +2 -1
- package/lib/aws/utils.d.ts.map +1 -1
- package/lib/aws/utils.js.map +1 -1
- package/lib/clickhouse/api/client.gen.d.ts +13 -0
- package/lib/clickhouse/api/client.gen.d.ts.map +1 -0
- package/lib/clickhouse/api/client.gen.js +7 -0
- package/lib/clickhouse/api/client.gen.js.map +1 -0
- package/lib/clickhouse/api/sdk.gen.d.ts +322 -0
- package/lib/clickhouse/api/sdk.gen.d.ts.map +1 -0
- package/lib/clickhouse/api/sdk.gen.js +683 -0
- package/lib/clickhouse/api/sdk.gen.js.map +1 -0
- package/lib/clickhouse/api/types.gen.d.ts +5030 -0
- package/lib/clickhouse/api/types.gen.d.ts.map +1 -0
- package/lib/clickhouse/api/types.gen.js +2 -0
- package/lib/clickhouse/api/types.gen.js.map +1 -0
- package/lib/clickhouse/api-key.d.ts +29 -0
- package/lib/clickhouse/api-key.d.ts.map +1 -0
- package/lib/clickhouse/api-key.js +83 -0
- package/lib/clickhouse/api-key.js.map +1 -0
- package/lib/clickhouse/api.d.ts +36 -0
- package/lib/clickhouse/api.d.ts.map +1 -0
- package/lib/clickhouse/api.js +48 -0
- package/lib/clickhouse/api.js.map +1 -0
- package/lib/clickhouse/index.d.ts +4 -0
- package/lib/clickhouse/index.d.ts.map +1 -0
- package/lib/clickhouse/index.js +4 -0
- package/lib/clickhouse/index.js.map +1 -0
- package/lib/clickhouse/organization.d.ts +16 -0
- package/lib/clickhouse/organization.d.ts.map +1 -0
- package/lib/clickhouse/organization.js +21 -0
- package/lib/clickhouse/organization.js.map +1 -0
- package/lib/clickhouse/service.d.ts +275 -0
- package/lib/clickhouse/service.d.ts.map +1 -0
- package/lib/clickhouse/service.js +274 -0
- package/lib/clickhouse/service.js.map +1 -0
- package/lib/cloudflare/account-api-token.d.ts.map +1 -1
- package/lib/cloudflare/account-api-token.js +1 -1
- package/lib/cloudflare/account-api-token.js.map +1 -1
- package/lib/cloudflare/bucket-object.d.ts +5 -0
- package/lib/cloudflare/bucket-object.d.ts.map +1 -1
- package/lib/cloudflare/bucket-object.js +1 -1
- package/lib/cloudflare/bucket-object.js.map +1 -1
- package/lib/cloudflare/bucket.d.ts +6 -3
- package/lib/cloudflare/bucket.d.ts.map +1 -1
- package/lib/cloudflare/bucket.js +60 -8
- package/lib/cloudflare/bucket.js.map +1 -1
- package/lib/cloudflare/bun-spa/bun-spa.d.ts +15 -0
- package/lib/cloudflare/bun-spa/bun-spa.d.ts.map +1 -0
- package/lib/cloudflare/bun-spa/bun-spa.js +95 -0
- package/lib/cloudflare/bun-spa/bun-spa.js.map +1 -0
- package/lib/cloudflare/compatibility-date.gen.d.ts +1 -1
- package/lib/cloudflare/compatibility-date.gen.js +1 -1
- package/lib/cloudflare/container.d.ts +11 -1
- package/lib/cloudflare/container.d.ts.map +1 -1
- package/lib/cloudflare/container.js +1 -1
- package/lib/cloudflare/container.js.map +1 -1
- package/lib/cloudflare/index.d.ts +1 -0
- package/lib/cloudflare/index.d.ts.map +1 -1
- package/lib/cloudflare/index.js +1 -0
- package/lib/cloudflare/index.js.map +1 -1
- package/lib/cloudflare/miniflare/build-worker-options.d.ts.map +1 -1
- package/lib/cloudflare/miniflare/build-worker-options.js +3 -3
- package/lib/cloudflare/miniflare/build-worker-options.js.map +1 -1
- package/lib/cloudflare/miniflare/miniflare-controller.d.ts.map +1 -1
- package/lib/cloudflare/miniflare/miniflare-controller.js +2 -0
- package/lib/cloudflare/miniflare/miniflare-controller.js.map +1 -1
- package/lib/cloudflare/miniflare/miniflare-worker-proxy.d.ts +1 -1
- package/lib/cloudflare/miniflare/miniflare-worker-proxy.d.ts.map +1 -1
- package/lib/cloudflare/miniflare/miniflare-worker-proxy.js +33 -14
- package/lib/cloudflare/miniflare/miniflare-worker-proxy.js.map +1 -1
- package/lib/cloudflare/permission-groups.d.ts +2 -3
- package/lib/cloudflare/permission-groups.d.ts.map +1 -1
- package/lib/cloudflare/permission-groups.js +10 -15
- package/lib/cloudflare/permission-groups.js.map +1 -1
- package/lib/cloudflare/redwood/redwood.d.ts +1 -1
- package/lib/cloudflare/redwood/redwood.js +1 -1
- package/lib/cloudflare/tanstack-start/plugin.d.ts +2 -15
- package/lib/cloudflare/tanstack-start/plugin.d.ts.map +1 -1
- package/lib/cloudflare/tanstack-start/plugin.js +5 -25
- package/lib/cloudflare/tanstack-start/plugin.js.map +1 -1
- package/lib/cloudflare/tanstack-start/tanstack-start.d.ts.map +1 -1
- package/lib/cloudflare/tanstack-start/tanstack-start.js +15 -4
- package/lib/cloudflare/tanstack-start/tanstack-start.js.map +1 -1
- package/lib/cloudflare/util/filter-env-bindings.d.ts +62 -0
- package/lib/cloudflare/util/filter-env-bindings.d.ts.map +1 -0
- package/lib/cloudflare/util/filter-env-bindings.js +81 -0
- package/lib/cloudflare/util/filter-env-bindings.js.map +1 -0
- package/lib/cloudflare/website.d.ts +2 -2
- package/lib/cloudflare/website.d.ts.map +1 -1
- package/lib/cloudflare/website.js +7 -18
- package/lib/cloudflare/website.js.map +1 -1
- package/lib/cloudflare/worker-metadata.d.ts +5 -1
- package/lib/cloudflare/worker-metadata.d.ts.map +1 -1
- package/lib/cloudflare/worker-metadata.js +3 -0
- package/lib/cloudflare/worker-metadata.js.map +1 -1
- package/lib/cloudflare/worker.d.ts +17 -1
- package/lib/cloudflare/worker.d.ts.map +1 -1
- package/lib/cloudflare/worker.js +1 -0
- package/lib/cloudflare/worker.js.map +1 -1
- package/lib/cloudflare/wrangler.json.d.ts +15 -1
- package/lib/cloudflare/wrangler.json.d.ts.map +1 -1
- package/lib/cloudflare/wrangler.json.js +12 -4
- package/lib/cloudflare/wrangler.json.js.map +1 -1
- package/lib/coinbase/client.d.ts +54 -0
- package/lib/coinbase/client.d.ts.map +1 -0
- package/lib/coinbase/client.js +89 -0
- package/lib/coinbase/client.js.map +1 -0
- package/lib/coinbase/evm-account.d.ts +118 -0
- package/lib/coinbase/evm-account.d.ts.map +1 -0
- package/lib/coinbase/evm-account.js +198 -0
- package/lib/coinbase/evm-account.js.map +1 -0
- package/lib/coinbase/evm-smart-account.d.ts +122 -0
- package/lib/coinbase/evm-smart-account.d.ts.map +1 -0
- package/lib/coinbase/evm-smart-account.js +213 -0
- package/lib/coinbase/evm-smart-account.js.map +1 -0
- package/lib/coinbase/faucet.d.ts +11 -0
- package/lib/coinbase/faucet.d.ts.map +1 -0
- package/lib/coinbase/faucet.js +146 -0
- package/lib/coinbase/faucet.js.map +1 -0
- package/lib/coinbase/index.d.ts +4 -0
- package/lib/coinbase/index.d.ts.map +1 -0
- package/lib/coinbase/index.js +4 -0
- package/lib/coinbase/index.js.map +1 -0
- package/lib/coinbase/types.d.ts +28 -0
- package/lib/coinbase/types.d.ts.map +1 -0
- package/lib/coinbase/types.js +4 -0
- package/lib/{util/telemetry → coinbase}/types.js.map +1 -1
- package/lib/coinbase/utils.d.ts +13 -0
- package/lib/coinbase/utils.d.ts.map +1 -0
- package/lib/coinbase/utils.js +17 -0
- package/lib/coinbase/utils.js.map +1 -0
- package/lib/docker/api.d.ts +22 -1
- package/lib/docker/api.d.ts.map +1 -1
- package/lib/docker/api.js +64 -2
- package/lib/docker/api.js.map +1 -1
- package/lib/docker/container.d.ts +88 -0
- package/lib/docker/container.d.ts.map +1 -1
- package/lib/docker/container.js +36 -0
- package/lib/docker/container.js.map +1 -1
- package/lib/neon/api/types.gen.d.ts +7 -2
- package/lib/neon/api/types.gen.d.ts.map +1 -1
- package/lib/planetscale/api/types.gen.d.ts +20 -4
- package/lib/planetscale/api/types.gen.d.ts.map +1 -1
- package/lib/planetscale/api.d.ts.map +1 -1
- package/lib/planetscale/api.js +1 -0
- package/lib/planetscale/api.js.map +1 -1
- package/lib/planetscale/password.d.ts.map +1 -1
- package/lib/planetscale/password.js +1 -13
- package/lib/planetscale/password.js.map +1 -1
- package/lib/resource.d.ts.map +1 -1
- package/lib/resource.js +7 -3
- package/lib/resource.js.map +1 -1
- package/lib/scope.d.ts +4 -9
- package/lib/scope.d.ts.map +1 -1
- package/lib/scope.js +16 -37
- package/lib/scope.js.map +1 -1
- package/lib/state/instrumented-state-store.d.ts +1 -3
- package/lib/state/instrumented-state-store.d.ts.map +1 -1
- package/lib/state/instrumented-state-store.js +19 -21
- package/lib/state/instrumented-state-store.js.map +1 -1
- package/lib/test/bun.d.ts.map +1 -1
- package/lib/test/bun.js +1 -2
- package/lib/test/bun.js.map +1 -1
- package/lib/test/vitest.d.ts.map +1 -1
- package/lib/test/vitest.js +1 -2
- package/lib/test/vitest.js.map +1 -1
- package/lib/util/diff.d.ts +5 -0
- package/lib/util/diff.d.ts.map +1 -0
- package/lib/util/diff.js +14 -0
- package/lib/util/diff.js.map +1 -0
- package/lib/util/telemetry.d.ts +52 -0
- package/lib/util/telemetry.d.ts.map +1 -0
- package/lib/util/telemetry.js +191 -0
- package/lib/util/telemetry.js.map +1 -0
- package/package.json +19 -1
- package/src/alchemy.ts +5 -20
- package/src/apply.ts +29 -12
- package/src/aws/utils.ts +2 -1
- package/src/clickhouse/api/client.gen.ts +28 -0
- package/src/clickhouse/api/sdk.gen.ts +1286 -0
- package/src/clickhouse/api/types.gen.ts +5898 -0
- package/src/clickhouse/api-key.ts +136 -0
- package/src/clickhouse/api.ts +84 -0
- package/src/clickhouse/index.ts +3 -0
- package/src/clickhouse/organization.ts +31 -0
- package/src/clickhouse/service.ts +668 -0
- package/src/cloudflare/account-api-token.ts +1 -4
- package/src/cloudflare/bucket-object.ts +11 -1
- package/src/cloudflare/bucket.ts +72 -6
- package/src/cloudflare/bun-spa/bun-spa.ts +139 -0
- package/src/cloudflare/compatibility-date.gen.ts +1 -1
- package/src/cloudflare/container.ts +22 -3
- package/src/cloudflare/index.ts +1 -0
- package/src/cloudflare/miniflare/build-worker-options.ts +6 -4
- package/src/cloudflare/miniflare/miniflare-controller.ts +2 -0
- package/src/cloudflare/miniflare/miniflare-worker-proxy.ts +42 -15
- package/src/cloudflare/permission-groups.ts +21 -49
- package/src/cloudflare/redwood/redwood.ts +1 -1
- package/src/cloudflare/tanstack-start/plugin.ts +7 -28
- package/src/cloudflare/tanstack-start/tanstack-start.ts +15 -4
- package/src/cloudflare/util/filter-env-bindings.ts +96 -0
- package/src/cloudflare/website.ts +12 -24
- package/src/cloudflare/worker-metadata.ts +8 -1
- package/src/cloudflare/worker.ts +19 -1
- package/src/cloudflare/wrangler.json.ts +27 -9
- package/src/coinbase/client.ts +146 -0
- package/src/coinbase/evm-account.ts +317 -0
- package/src/coinbase/evm-smart-account.ts +339 -0
- package/src/coinbase/faucet.ts +173 -0
- package/src/coinbase/index.ts +3 -0
- package/src/coinbase/types.ts +33 -0
- package/src/coinbase/utils.ts +19 -0
- package/src/docker/api.ts +86 -2
- package/src/docker/container.ts +97 -0
- package/src/neon/api/types.gen.ts +13 -1
- package/src/planetscale/api/types.gen.ts +22 -2
- package/src/planetscale/api.ts +1 -0
- package/src/planetscale/password.ts +1 -14
- package/src/resource.ts +11 -4
- package/src/scope.ts +22 -45
- package/src/state/instrumented-state-store.ts +27 -26
- package/src/test/bun.ts +1 -2
- package/src/test/vitest.ts +1 -2
- package/src/util/diff.ts +14 -0
- package/src/util/telemetry.ts +297 -0
- package/templates/tanstack-start/.cta.json +13 -0
- package/templates/tanstack-start/README.md +267 -49
- package/templates/tanstack-start/_gitignore +9 -18
- package/templates/tanstack-start/alchemy.run.ts +2 -2
- package/templates/tanstack-start/package.json +24 -19
- package/templates/tanstack-start/public/favicon.ico +0 -0
- package/templates/tanstack-start/public/logo192.png +0 -0
- package/templates/tanstack-start/public/logo512.png +0 -0
- package/templates/tanstack-start/public/manifest.json +25 -0
- package/templates/tanstack-start/public/robots.txt +3 -0
- package/templates/tanstack-start/src/components/Header.tsx +21 -0
- package/templates/tanstack-start/src/logo.svg +12 -0
- package/templates/tanstack-start/src/router.tsx +7 -16
- package/templates/tanstack-start/src/routes/__root.tsx +13 -114
- package/templates/tanstack-start/src/routes/api.demo-names.ts +15 -0
- package/templates/tanstack-start/src/routes/demo.start.api-request.tsx +44 -0
- package/templates/tanstack-start/src/routes/demo.start.server-funcs.tsx +97 -0
- package/templates/tanstack-start/src/routes/index.tsx +31 -4
- package/templates/tanstack-start/src/styles.css +15 -0
- package/templates/tanstack-start/tsconfig.json +19 -22
- package/templates/tanstack-start/types/env.d.ts +2 -2
- package/templates/tanstack-start/vite.config.ts +16 -25
- package/workers/tunnel-proxy.js +1 -1
- package/lib/util/telemetry/client.d.ts +0 -31
- package/lib/util/telemetry/client.d.ts.map +0 -1
- package/lib/util/telemetry/client.js +0 -121
- package/lib/util/telemetry/client.js.map +0 -1
- package/lib/util/telemetry/constants.d.ts +0 -5
- package/lib/util/telemetry/constants.d.ts.map +0 -1
- package/lib/util/telemetry/constants.js +0 -8
- package/lib/util/telemetry/constants.js.map +0 -1
- package/lib/util/telemetry/context.d.ts +0 -15
- package/lib/util/telemetry/context.d.ts.map +0 -1
- package/lib/util/telemetry/context.js +0 -128
- package/lib/util/telemetry/context.js.map +0 -1
- package/lib/util/telemetry/index.d.ts +0 -3
- package/lib/util/telemetry/index.d.ts.map +0 -1
- package/lib/util/telemetry/index.js +0 -3
- package/lib/util/telemetry/index.js.map +0 -1
- package/lib/util/telemetry/types.d.ts +0 -68
- package/lib/util/telemetry/types.d.ts.map +0 -1
- package/lib/util/telemetry/types.js +0 -1
- package/src/util/telemetry/client.ts +0 -156
- package/src/util/telemetry/constants.ts +0 -13
- package/src/util/telemetry/context.ts +0 -154
- package/src/util/telemetry/index.ts +0 -2
- package/src/util/telemetry/types.ts +0 -105
- package/templates/tanstack-start/_prettierignore +0 -4
- package/templates/tanstack-start/public/android-chrome-192x192.png +0 -0
- package/templates/tanstack-start/public/android-chrome-512x512.png +0 -0
- package/templates/tanstack-start/public/apple-touch-icon.png +0 -0
- package/templates/tanstack-start/public/favicon-16x16.png +0 -0
- package/templates/tanstack-start/public/favicon-32x32.png +0 -0
- package/templates/tanstack-start/public/favicon.png +0 -0
- package/templates/tanstack-start/public/site.webmanifest +0 -19
- package/templates/tanstack-start/src/components/DefaultCatchBoundary.tsx +0 -53
- package/templates/tanstack-start/src/components/NotFound.tsx +0 -25
- package/templates/tanstack-start/src/components/PostError.tsx +0 -5
- package/templates/tanstack-start/src/components/UserError.tsx +0 -5
- package/templates/tanstack-start/src/routes/_pathlessLayout/_nested-layout/route-a.tsx +0 -10
- package/templates/tanstack-start/src/routes/_pathlessLayout/_nested-layout/route-b.tsx +0 -10
- package/templates/tanstack-start/src/routes/_pathlessLayout/_nested-layout.tsx +0 -34
- package/templates/tanstack-start/src/routes/_pathlessLayout.tsx +0 -16
- package/templates/tanstack-start/src/routes/api/users.$userId.ts +0 -28
- package/templates/tanstack-start/src/routes/api/users.ts +0 -59
- package/templates/tanstack-start/src/routes/customScript[.]js.ts +0 -10
- package/templates/tanstack-start/src/routes/deferred.tsx +0 -62
- package/templates/tanstack-start/src/routes/posts.$postId.tsx +0 -34
- package/templates/tanstack-start/src/routes/posts.index.tsx +0 -8
- package/templates/tanstack-start/src/routes/posts.tsx +0 -38
- package/templates/tanstack-start/src/routes/posts_.$postId.deep.tsx +0 -29
- package/templates/tanstack-start/src/routes/redirect.tsx +0 -9
- package/templates/tanstack-start/src/routes/users.$userId.tsx +0 -44
- package/templates/tanstack-start/src/routes/users.index.tsx +0 -18
- package/templates/tanstack-start/src/routes/users.tsx +0 -49
- package/templates/tanstack-start/src/styles/app.css +0 -12
- package/templates/tanstack-start/src/utils/loggingMiddleware.tsx +0 -41
- package/templates/tanstack-start/src/utils/posts.tsx +0 -40
- package/templates/tanstack-start/src/utils/seo.ts +0 -33
- package/templates/tanstack-start/src/utils/users.tsx +0 -5
|
@@ -15,10 +15,21 @@ export async function TanStackStart<B extends Bindings>(
|
|
|
15
15
|
props?: Partial<TanStackStartProps<B>>,
|
|
16
16
|
): Promise<TanStackStart<B>> {
|
|
17
17
|
return await Vite(id, {
|
|
18
|
-
|
|
18
|
+
entrypoint: "dist/server/index.js",
|
|
19
|
+
assets: "dist/client",
|
|
20
|
+
compatibility: "node",
|
|
19
21
|
noBundle: true,
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
spa: false,
|
|
23
|
+
...props,
|
|
24
|
+
wrangler: {
|
|
25
|
+
...props?.wrangler,
|
|
26
|
+
transform: async (spec) => {
|
|
27
|
+
const transformed = (await props?.wrangler?.transform?.(spec)) ?? spec;
|
|
28
|
+
if (!props?.wrangler?.main) {
|
|
29
|
+
transformed.main = "@tanstack/react-start/server-entry";
|
|
30
|
+
}
|
|
31
|
+
return transformed;
|
|
32
|
+
},
|
|
33
|
+
},
|
|
23
34
|
});
|
|
24
35
|
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { isSecret, type Secret } from "../../secret.ts";
|
|
2
|
+
import type { Bindings } from "../bindings.ts";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Extract string and Secret bindings from a Bindings object
|
|
6
|
+
*
|
|
7
|
+
* This utility filters a Bindings object to return only string values
|
|
8
|
+
* and optionally Secret objects. All other binding types (workers, KV, R2, etc.)
|
|
9
|
+
* are excluded from the result.
|
|
10
|
+
*
|
|
11
|
+
* @param bindings - The bindings object to filter
|
|
12
|
+
* @param includeSecrets - Whether to include Secret objects in the result
|
|
13
|
+
* @returns A record containing only string values and Secret objects (if includeSecrets is true)
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* const filtered = extractStringAndSecretBindings(props.bindings, true);
|
|
18
|
+
* // Returns: { API_KEY: Secret(...), DEBUG: "true" }
|
|
19
|
+
* // Excludes: { CACHE: KVNamespace, STORAGE: R2Bucket }
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export function extractStringAndSecretBindings(
|
|
23
|
+
bindings: Bindings,
|
|
24
|
+
includeSecrets: boolean,
|
|
25
|
+
): Record<string, string | Secret> {
|
|
26
|
+
return Object.fromEntries(
|
|
27
|
+
Object.entries(bindings ?? {}).flatMap(([key, value]) => {
|
|
28
|
+
if (typeof value === "string" || (isSecret(value) && includeSecrets)) {
|
|
29
|
+
return [[key, value]];
|
|
30
|
+
}
|
|
31
|
+
return [];
|
|
32
|
+
}),
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Unencrypt all Secret values in an environment object
|
|
38
|
+
*
|
|
39
|
+
* Takes a record that may contain Secret objects and returns a new record
|
|
40
|
+
* with all Secret values replaced by their unencrypted string values.
|
|
41
|
+
* Non-Secret values are passed through unchanged. Undefined values are filtered out.
|
|
42
|
+
*
|
|
43
|
+
* @param env - The environment object that may contain Secret values
|
|
44
|
+
* @returns A new record with all Secrets replaced by their unencrypted values
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```typescript
|
|
48
|
+
* const env = { API_KEY: Secret("abc123"), DEBUG: "true" };
|
|
49
|
+
* const unencrypted = unencryptSecrets(env);
|
|
50
|
+
* // Returns: { API_KEY: "abc123", DEBUG: "true" }
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export function unencryptSecrets(
|
|
54
|
+
env: Record<string, string | Secret | undefined>,
|
|
55
|
+
): Record<string, string> {
|
|
56
|
+
return Object.fromEntries(
|
|
57
|
+
Object.entries(env ?? {}).flatMap(([key, value]) => {
|
|
58
|
+
if (isSecret(value)) {
|
|
59
|
+
return [[key, value.unencrypted]];
|
|
60
|
+
}
|
|
61
|
+
if (typeof value === "string") {
|
|
62
|
+
return [[key, value]];
|
|
63
|
+
}
|
|
64
|
+
return [];
|
|
65
|
+
}),
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Filter bindings to strings and unencrypt secrets in one operation
|
|
71
|
+
*
|
|
72
|
+
* This is a convenience function that combines extractStringAndSecretBindings
|
|
73
|
+
* and unencryptSecrets. It filters bindings to strings/secrets and immediately
|
|
74
|
+
* unencrypts any Secret values.
|
|
75
|
+
*
|
|
76
|
+
* Useful for preparing environment variables for dev/build commands where
|
|
77
|
+
* secrets need to be decrypted and all resource bindings need to be excluded.
|
|
78
|
+
*
|
|
79
|
+
* @param bindings - The bindings object to filter
|
|
80
|
+
* @param includeSecrets - Whether to include (and unencrypt) Secret objects
|
|
81
|
+
* @returns A record containing only string values with all secrets unencrypted
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* ```typescript
|
|
85
|
+
* const env = filterStringBindings(props.bindings, true);
|
|
86
|
+
* // Input: { API_KEY: Secret("abc"), DEBUG: "true", CACHE: KVNamespace }
|
|
87
|
+
* // Returns: { API_KEY: "abc", DEBUG: "true" }
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
export function filterStringBindings(
|
|
91
|
+
bindings: Bindings,
|
|
92
|
+
includeSecrets: boolean,
|
|
93
|
+
): Record<string, string> {
|
|
94
|
+
const extracted = extractStringAndSecretBindings(bindings, includeSecrets);
|
|
95
|
+
return unencryptSecrets(extracted);
|
|
96
|
+
}
|
|
@@ -3,7 +3,6 @@ import fs from "node:fs/promises";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { Exec } from "../os/index.ts";
|
|
5
5
|
import { Scope } from "../scope.ts";
|
|
6
|
-
import { isSecret } from "../secret.ts";
|
|
7
6
|
import { dedent } from "../util/dedent.ts";
|
|
8
7
|
import { logger } from "../util/logger.ts";
|
|
9
8
|
import { Assets } from "./assets.ts";
|
|
@@ -11,6 +10,10 @@ import type { Bindings } from "./bindings.ts";
|
|
|
11
10
|
import { DEFAULT_COMPATIBILITY_DATE } from "./compatibility-date.gen.ts";
|
|
12
11
|
import { unionCompatibilityFlags } from "./compatibility-presets.ts";
|
|
13
12
|
import { writeMiniflareSymlink } from "./miniflare/symlink-miniflare-state.ts";
|
|
13
|
+
import {
|
|
14
|
+
extractStringAndSecretBindings,
|
|
15
|
+
unencryptSecrets,
|
|
16
|
+
} from "./util/filter-env-bindings.ts";
|
|
14
17
|
import { type AssetsConfig, Worker, type WorkerProps } from "./worker.ts";
|
|
15
18
|
import { WranglerJson, type WranglerJsonSpec } from "./wrangler.json.ts";
|
|
16
19
|
|
|
@@ -67,7 +70,7 @@ export interface WebsiteProps<B extends Bindings>
|
|
|
67
70
|
/**
|
|
68
71
|
* The command to run to start the dev server
|
|
69
72
|
*/
|
|
70
|
-
command
|
|
73
|
+
command?: string;
|
|
71
74
|
/**
|
|
72
75
|
* Additional environment variables to set when running the dev command
|
|
73
76
|
*/
|
|
@@ -226,14 +229,7 @@ export async function Website<B extends Bindings>(
|
|
|
226
229
|
const env = {
|
|
227
230
|
...(process.env ?? {}),
|
|
228
231
|
...(props.env ?? {}),
|
|
229
|
-
...
|
|
230
|
-
Object.entries(props.bindings ?? {}).flatMap(([key, value]) => {
|
|
231
|
-
if (typeof value === "string" || (isSecret(value) && secrets)) {
|
|
232
|
-
return [[key, value]];
|
|
233
|
-
}
|
|
234
|
-
return [];
|
|
235
|
-
}),
|
|
236
|
-
),
|
|
232
|
+
...extractStringAndSecretBindings(props.bindings ?? {}, secrets),
|
|
237
233
|
};
|
|
238
234
|
const worker = {
|
|
239
235
|
...workerProps,
|
|
@@ -302,9 +298,10 @@ export async function Website<B extends Bindings>(
|
|
|
302
298
|
}
|
|
303
299
|
|
|
304
300
|
let url: string | undefined;
|
|
305
|
-
|
|
301
|
+
const devCommand = typeof dev === "string" ? dev : dev?.command;
|
|
302
|
+
if (devCommand && scope.local) {
|
|
306
303
|
url = await scope.spawn(name, {
|
|
307
|
-
cmd:
|
|
304
|
+
cmd: devCommand,
|
|
308
305
|
cwd: paths.cwd,
|
|
309
306
|
extract: (line) => {
|
|
310
307
|
const URL_REGEX =
|
|
@@ -317,23 +314,14 @@ export async function Website<B extends Bindings>(
|
|
|
317
314
|
}
|
|
318
315
|
},
|
|
319
316
|
env: {
|
|
320
|
-
...
|
|
321
|
-
Object.entries(env ?? {}).flatMap(([key, value]) => {
|
|
322
|
-
if (isSecret(value)) {
|
|
323
|
-
return [[key, value.unencrypted]];
|
|
324
|
-
}
|
|
325
|
-
if (typeof value === "string") {
|
|
326
|
-
return [[key, value]];
|
|
327
|
-
}
|
|
328
|
-
return [];
|
|
329
|
-
}),
|
|
330
|
-
),
|
|
317
|
+
...unencryptSecrets(env ?? {}),
|
|
331
318
|
...(typeof dev === "object" ? dev.env : {}),
|
|
332
319
|
FORCE_COLOR: "1",
|
|
333
320
|
...process.env,
|
|
334
321
|
// NOTE: we must set this to ensure the user does not accidentally set `NODE_ENV=production`
|
|
335
322
|
// which breaks `vite dev` (it won't, for example, re-write `process.env.TSS_APP_BASE` in the `.js` client side bundle)
|
|
336
323
|
NODE_ENV: "development",
|
|
324
|
+
ALCHEMY_ROOT: Scope.current.rootDir,
|
|
337
325
|
},
|
|
338
326
|
});
|
|
339
327
|
}
|
|
@@ -370,7 +358,7 @@ export const spreadBuildProps = (
|
|
|
370
358
|
export const spreadDevProps = (
|
|
371
359
|
props: { dev?: WebsiteProps<Bindings>["dev"] } | undefined,
|
|
372
360
|
defaultCommand: string,
|
|
373
|
-
): WebsiteProps<Bindings>["dev"] => {
|
|
361
|
+
): Exclude<WebsiteProps<Bindings>["dev"], undefined> => {
|
|
374
362
|
if (typeof props?.dev === "object") {
|
|
375
363
|
return {
|
|
376
364
|
...props.dev,
|
|
@@ -18,7 +18,7 @@ import type {
|
|
|
18
18
|
MultiStepMigration,
|
|
19
19
|
SingleStepMigration,
|
|
20
20
|
} from "./worker-migration.ts";
|
|
21
|
-
import type
|
|
21
|
+
import { isWorker, type AssetsConfig, type WorkerProps } from "./worker.ts";
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
24
|
* Metadata returned by Cloudflare API for a worker script
|
|
@@ -175,6 +175,7 @@ export interface WorkerDefaultEnvironment extends WorkerEnvironment {
|
|
|
175
175
|
script: WorkerScriptInfo;
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
+
//? (jacob): why is this not using BaseWorkerProps?
|
|
178
179
|
export interface WorkerMetadata {
|
|
179
180
|
compatibility_date: string;
|
|
180
181
|
compatibility_flags?: string[];
|
|
@@ -182,6 +183,7 @@ export interface WorkerMetadata {
|
|
|
182
183
|
observability: {
|
|
183
184
|
enabled: boolean;
|
|
184
185
|
};
|
|
186
|
+
logpush?: boolean;
|
|
185
187
|
migrations?: SingleStepMigration;
|
|
186
188
|
main_module?: string;
|
|
187
189
|
body_part?: string;
|
|
@@ -202,6 +204,7 @@ export interface WorkerMetadata {
|
|
|
202
204
|
limits?: {
|
|
203
205
|
cpu_ms?: number;
|
|
204
206
|
};
|
|
207
|
+
tail_consumers?: Array<Worker | { service: string }>;
|
|
205
208
|
}
|
|
206
209
|
|
|
207
210
|
export async function prepareWorkerMetadata(
|
|
@@ -316,10 +319,14 @@ export async function prepareWorkerMetadata(
|
|
|
316
319
|
const meta: WorkerMetadata = {
|
|
317
320
|
compatibility_date: props.compatibilityDate,
|
|
318
321
|
compatibility_flags: props.compatibilityFlags,
|
|
322
|
+
tail_consumers: props.tailConsumers?.map((consumer) =>
|
|
323
|
+
isWorker(consumer) ? { service: consumer.name } : consumer,
|
|
324
|
+
),
|
|
319
325
|
bindings: [],
|
|
320
326
|
observability: {
|
|
321
327
|
enabled: props.observability?.enabled !== false,
|
|
322
328
|
},
|
|
329
|
+
logpush: props.logpush ?? false,
|
|
323
330
|
// TODO(sam): base64 encode instead? 0 collision risk vs readability.
|
|
324
331
|
tags: [
|
|
325
332
|
// encode a mapping table of Durable Object stable ID -> binding name
|
package/src/cloudflare/worker.ts
CHANGED
|
@@ -179,6 +179,17 @@ export interface BaseWorkerProps<
|
|
|
179
179
|
enabled?: boolean;
|
|
180
180
|
};
|
|
181
181
|
|
|
182
|
+
/**
|
|
183
|
+
* Enable Workers Logpush to export trace events (request/response metadata,
|
|
184
|
+
* console logs, and exceptions) to external destinations.
|
|
185
|
+
*
|
|
186
|
+
* Requires a separate Logpush job configuration via the Cloudflare API.
|
|
187
|
+
*
|
|
188
|
+
* @see https://developers.cloudflare.com/workers/observability/logging/logpush
|
|
189
|
+
* @default false
|
|
190
|
+
*/
|
|
191
|
+
logpush?: boolean;
|
|
192
|
+
|
|
182
193
|
/**
|
|
183
194
|
* Whether to adopt the Worker if it already exists when creating
|
|
184
195
|
*/
|
|
@@ -324,7 +335,7 @@ export interface BaseWorkerProps<
|
|
|
324
335
|
dev?:
|
|
325
336
|
| {
|
|
326
337
|
/**
|
|
327
|
-
* Port to use for local development
|
|
338
|
+
* Port to use for local development.
|
|
328
339
|
*/
|
|
329
340
|
port?: number;
|
|
330
341
|
/**
|
|
@@ -375,6 +386,11 @@ export interface BaseWorkerProps<
|
|
|
375
386
|
*/
|
|
376
387
|
cpu_ms?: number;
|
|
377
388
|
};
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Tail consumers that will receive execution logs from this worker
|
|
392
|
+
*/
|
|
393
|
+
tailConsumers?: Array<Worker | { service: string }>;
|
|
378
394
|
}
|
|
379
395
|
|
|
380
396
|
export interface InlineWorkerProps<
|
|
@@ -1018,6 +1034,7 @@ const _Worker = Resource(
|
|
|
1018
1034
|
props.crons?.map((cron) => ({ cron })) ?? [],
|
|
1019
1035
|
);
|
|
1020
1036
|
}
|
|
1037
|
+
|
|
1021
1038
|
await Promise.all(
|
|
1022
1039
|
options.workflows.map((workflow) =>
|
|
1023
1040
|
upsertWorkflow(api, {
|
|
@@ -1063,6 +1080,7 @@ const _Worker = Resource(
|
|
|
1063
1080
|
url: subdomain?.url,
|
|
1064
1081
|
assets: props.assets,
|
|
1065
1082
|
crons: props.crons,
|
|
1083
|
+
tailConsumers: props.tailConsumers,
|
|
1066
1084
|
routes,
|
|
1067
1085
|
domains,
|
|
1068
1086
|
namespace: props.namespace,
|
|
@@ -4,6 +4,7 @@ import type { Context } from "../context.ts";
|
|
|
4
4
|
import { Resource } from "../resource.ts";
|
|
5
5
|
import { Scope } from "../scope.ts";
|
|
6
6
|
import { isSecret } from "../secret.ts";
|
|
7
|
+
import { unencryptSecrets } from "./util/filter-env-bindings.ts";
|
|
7
8
|
import { assertNever } from "../util/assert-never.ts";
|
|
8
9
|
import type { Bindings, WorkerBindingRateLimit } from "./bindings.ts";
|
|
9
10
|
import type { R2BucketJurisdiction } from "./bucket.ts";
|
|
@@ -11,7 +12,7 @@ import type { DurableObjectNamespace } from "./durable-object-namespace.ts";
|
|
|
11
12
|
import type { EventSource } from "./event-source.ts";
|
|
12
13
|
import { isQueueEventSource } from "./event-source.ts";
|
|
13
14
|
import { isQueue } from "./queue.ts";
|
|
14
|
-
import type
|
|
15
|
+
import { isWorker, type Worker, type WorkerProps } from "./worker.ts";
|
|
15
16
|
|
|
16
17
|
type WranglerJsonRateLimit = Omit<WorkerBindingRateLimit, "type"> & {
|
|
17
18
|
type: "rate_limit";
|
|
@@ -162,6 +163,7 @@ export async function WranglerJson(
|
|
|
162
163
|
: undefined,
|
|
163
164
|
placement: worker.placement,
|
|
164
165
|
limits: worker.limits,
|
|
166
|
+
logpush: worker.logpush,
|
|
165
167
|
};
|
|
166
168
|
|
|
167
169
|
// Process bindings if they exist
|
|
@@ -182,6 +184,15 @@ export async function WranglerJson(
|
|
|
182
184
|
spec.vars = { ...worker.env };
|
|
183
185
|
}
|
|
184
186
|
|
|
187
|
+
if (worker.tailConsumers && worker.tailConsumers.length > 0) {
|
|
188
|
+
spec.tail_consumers = worker.tailConsumers.map((consumer) => {
|
|
189
|
+
if (isWorker(consumer)) {
|
|
190
|
+
return { service: consumer.name };
|
|
191
|
+
}
|
|
192
|
+
return { service: consumer.service };
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
185
196
|
if (worker.crons && worker.crons.length > 0) {
|
|
186
197
|
spec.triggers = { crons: worker.crons };
|
|
187
198
|
}
|
|
@@ -201,14 +212,7 @@ export async function WranglerJson(
|
|
|
201
212
|
// but do not modify `finalSpec` so that way secrets aren't written to state unencrypted.
|
|
202
213
|
const withSecretsUnwrapped = {
|
|
203
214
|
...finalSpec,
|
|
204
|
-
vars: {
|
|
205
|
-
...finalSpec.vars,
|
|
206
|
-
...Object.fromEntries(
|
|
207
|
-
Object.entries(finalSpec.vars ?? {}).map(([key, value]) =>
|
|
208
|
-
isSecret(value) ? [key, value.unencrypted] : [key, value],
|
|
209
|
-
),
|
|
210
|
-
),
|
|
211
|
-
},
|
|
215
|
+
vars: unencryptSecrets(finalSpec.vars ?? {}),
|
|
212
216
|
};
|
|
213
217
|
await writeJSON(filePath, withSecretsUnwrapped);
|
|
214
218
|
} else {
|
|
@@ -266,6 +270,15 @@ export interface WranglerJsonSpec {
|
|
|
266
270
|
cpu_ms?: number;
|
|
267
271
|
};
|
|
268
272
|
|
|
273
|
+
/**
|
|
274
|
+
* Send Trace Events from this Worker to Workers Logpush.
|
|
275
|
+
*
|
|
276
|
+
* This will not configure a corresponding Logpush job automatically.
|
|
277
|
+
*
|
|
278
|
+
* @default false
|
|
279
|
+
*/
|
|
280
|
+
logpush?: boolean;
|
|
281
|
+
|
|
269
282
|
/**
|
|
270
283
|
* Whether to enable a workers.dev URL for this worker
|
|
271
284
|
*/
|
|
@@ -485,6 +498,11 @@ export interface WranglerJsonSpec {
|
|
|
485
498
|
experimental_remote?: boolean;
|
|
486
499
|
}[];
|
|
487
500
|
|
|
501
|
+
/**
|
|
502
|
+
* Tail consumers that will receive execution logs from this worker
|
|
503
|
+
*/
|
|
504
|
+
tail_consumers?: Array<{ service: string }>;
|
|
505
|
+
|
|
488
506
|
/**
|
|
489
507
|
* Unsafe bindings section for experimental features
|
|
490
508
|
*/
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import type { CdpClient } from "@coinbase/cdp-sdk";
|
|
2
|
+
import type { Secret } from "../secret.ts";
|
|
3
|
+
import { logger } from "../util/logger.ts";
|
|
4
|
+
import { importPeer } from "../util/peer.ts";
|
|
5
|
+
|
|
6
|
+
export interface CoinbaseClientOptions {
|
|
7
|
+
/**
|
|
8
|
+
* CDP API Key ID. If not provided, falls back to CDP_API_KEY_ID environment variable.
|
|
9
|
+
*/
|
|
10
|
+
apiKeyId?: Secret<string> | string;
|
|
11
|
+
/**
|
|
12
|
+
* CDP API Key Secret. If not provided, falls back to CDP_API_KEY_SECRET environment variable.
|
|
13
|
+
* Must be wrapped in alchemy.secret() for security.
|
|
14
|
+
*/
|
|
15
|
+
apiKeySecret?: Secret<string>;
|
|
16
|
+
/**
|
|
17
|
+
* CDP Wallet Secret. If not provided, falls back to CDP_WALLET_SECRET environment variable.
|
|
18
|
+
* Must be wrapped in alchemy.secret() for security.
|
|
19
|
+
*/
|
|
20
|
+
walletSecret?: Secret<string>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Create an authenticated CDP client
|
|
25
|
+
*
|
|
26
|
+
* The CDP SDK automatically looks for these environment variables:
|
|
27
|
+
* - CDP_API_KEY_ID
|
|
28
|
+
* - CDP_API_KEY_SECRET
|
|
29
|
+
* - CDP_WALLET_SECRET
|
|
30
|
+
*
|
|
31
|
+
* You can override them by passing values in the options.
|
|
32
|
+
*
|
|
33
|
+
* @param options Options for creating the client
|
|
34
|
+
* @returns An authenticated CDP client
|
|
35
|
+
*/
|
|
36
|
+
export async function createCdpClient(
|
|
37
|
+
options: CoinbaseClientOptions = {},
|
|
38
|
+
): Promise<CdpClient> {
|
|
39
|
+
const { CdpClient } = await importPeer(
|
|
40
|
+
import("@coinbase/cdp-sdk"),
|
|
41
|
+
"Coinbase resources",
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
// Build options object for CdpClient constructor
|
|
45
|
+
const clientOptions: {
|
|
46
|
+
apiKeyId?: string;
|
|
47
|
+
apiKeySecret?: string;
|
|
48
|
+
walletSecret?: string;
|
|
49
|
+
} = {};
|
|
50
|
+
|
|
51
|
+
// Only set values if explicitly provided, otherwise let CDP SDK use its defaults
|
|
52
|
+
if (options.apiKeyId) {
|
|
53
|
+
clientOptions.apiKeyId =
|
|
54
|
+
typeof options.apiKeyId === "string"
|
|
55
|
+
? options.apiKeyId
|
|
56
|
+
: options.apiKeyId.unencrypted;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (options.apiKeySecret) {
|
|
60
|
+
clientOptions.apiKeySecret = options.apiKeySecret.unencrypted;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (options.walletSecret) {
|
|
64
|
+
clientOptions.walletSecret = options.walletSecret.unencrypted;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// CDP SDK will automatically use environment variables if not provided:
|
|
68
|
+
// CDP_API_KEY_ID, CDP_API_KEY_SECRET, CDP_WALLET_SECRET
|
|
69
|
+
return new CdpClient(clientOptions);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Handle CDP SDK deletion errors gracefully
|
|
74
|
+
*
|
|
75
|
+
* @param error The error from CDP SDK
|
|
76
|
+
* @param resourceType The type of resource being deleted
|
|
77
|
+
* @param resourceId The ID or name of the resource
|
|
78
|
+
*/
|
|
79
|
+
export function handleCdpDeleteError(
|
|
80
|
+
_error: any,
|
|
81
|
+
resourceType: string,
|
|
82
|
+
resourceId?: string,
|
|
83
|
+
): void {
|
|
84
|
+
// CDP SDK doesn't support deleting accounts, so we just log
|
|
85
|
+
logger.log(
|
|
86
|
+
`${resourceType} ${resourceId || "unknown"} cannot be deleted from CDP (CDP limitation)`,
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Check if an error indicates the resource already exists
|
|
92
|
+
*
|
|
93
|
+
* @param error The error from CDP SDK
|
|
94
|
+
* @returns True if the error indicates a conflict/already exists
|
|
95
|
+
*/
|
|
96
|
+
export function isCdpConflictError(error: any): boolean {
|
|
97
|
+
return (
|
|
98
|
+
error?.message?.toLowerCase()?.includes("already exists") ||
|
|
99
|
+
error?.message?.toLowerCase()?.includes("duplicate") ||
|
|
100
|
+
error?.code === "ALREADY_EXISTS" ||
|
|
101
|
+
error?.code === "CONFLICT"
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Verifies CDP authentication and provides helpful error messages
|
|
107
|
+
*
|
|
108
|
+
* @param client CDP client
|
|
109
|
+
*/
|
|
110
|
+
export async function verifyCdpAuth(client: CdpClient): Promise<void> {
|
|
111
|
+
try {
|
|
112
|
+
// Make a test request to check authentication
|
|
113
|
+
// List accounts is a good test as it requires valid credentials
|
|
114
|
+
await client.evm.listAccounts();
|
|
115
|
+
} catch (error: any) {
|
|
116
|
+
if (error.errorType === "unauthorized") {
|
|
117
|
+
logger.error(
|
|
118
|
+
"\n⚠️ Coinbase CDP authentication failed. Please check your credentials:",
|
|
119
|
+
);
|
|
120
|
+
logger.error("1. Ensure CDP_API_KEY_ID is set with your API Key ID");
|
|
121
|
+
logger.error(
|
|
122
|
+
"2. Ensure CDP_API_KEY_SECRET is set with your API Key Secret",
|
|
123
|
+
);
|
|
124
|
+
logger.error(
|
|
125
|
+
"3. Ensure CDP_WALLET_SECRET is set with your Wallet Secret",
|
|
126
|
+
);
|
|
127
|
+
logger.error(
|
|
128
|
+
"\nTo create API credentials, visit: https://portal.cdp.coinbase.com/",
|
|
129
|
+
);
|
|
130
|
+
logger.error(
|
|
131
|
+
"Required permissions: Account management and transaction signing\n",
|
|
132
|
+
);
|
|
133
|
+
throw new Error("CDP authentication failed");
|
|
134
|
+
}
|
|
135
|
+
if (error.errorType === "unauthorized") {
|
|
136
|
+
logger.error(
|
|
137
|
+
"\n⚠️ Insufficient permissions. Your API key needs account management permissions.",
|
|
138
|
+
);
|
|
139
|
+
logger.error(
|
|
140
|
+
"Make sure your API key has the required scopes for creating and managing accounts\n",
|
|
141
|
+
);
|
|
142
|
+
throw new Error("Insufficient CDP permissions");
|
|
143
|
+
}
|
|
144
|
+
throw error;
|
|
145
|
+
}
|
|
146
|
+
}
|