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
package/src/scope.ts
CHANGED
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
import { logger } from "./util/logger.ts";
|
|
30
30
|
import { AsyncMutex } from "./util/mutex.ts";
|
|
31
31
|
import { ALCHEMY_ROOT } from "./util/root-dir.ts";
|
|
32
|
-
import
|
|
32
|
+
import { createAndSendEvent } from "./util/telemetry.ts";
|
|
33
33
|
|
|
34
34
|
export class RootScopeStateAttemptError extends Error {
|
|
35
35
|
constructor() {
|
|
@@ -76,10 +76,11 @@ export interface ScopeOptions extends ProviderCredentials {
|
|
|
76
76
|
*/
|
|
77
77
|
destroyStrategy?: DestroyStrategy;
|
|
78
78
|
/**
|
|
79
|
-
*
|
|
79
|
+
* Whether to disable telemetry for the scope.
|
|
80
80
|
*
|
|
81
|
+
* @default false
|
|
81
82
|
*/
|
|
82
|
-
|
|
83
|
+
noTrack?: boolean;
|
|
83
84
|
/**
|
|
84
85
|
* The logger to use for the scope.
|
|
85
86
|
*/
|
|
@@ -197,7 +198,7 @@ export class Scope {
|
|
|
197
198
|
public readonly adopt: boolean;
|
|
198
199
|
public readonly destroyStrategy: DestroyStrategy;
|
|
199
200
|
public readonly logger: LoggerApi;
|
|
200
|
-
public readonly
|
|
201
|
+
public readonly noTrack: boolean;
|
|
201
202
|
public readonly dataMutex: AsyncMutex;
|
|
202
203
|
public readonly rootDir: string;
|
|
203
204
|
public readonly dotAlchemy: string;
|
|
@@ -235,12 +236,12 @@ export class Scope {
|
|
|
235
236
|
tunnel,
|
|
236
237
|
force,
|
|
237
238
|
destroyStrategy,
|
|
238
|
-
telemetryClient,
|
|
239
239
|
logger,
|
|
240
240
|
adopt,
|
|
241
241
|
dotAlchemy,
|
|
242
242
|
rootDir,
|
|
243
243
|
isSelected,
|
|
244
|
+
noTrack,
|
|
244
245
|
...providerCredentials
|
|
245
246
|
} = options;
|
|
246
247
|
|
|
@@ -273,6 +274,7 @@ export class Scope {
|
|
|
273
274
|
throw new Error("Scope name is required when creating a child scope");
|
|
274
275
|
}
|
|
275
276
|
this.password = password ?? this.parent?.password;
|
|
277
|
+
this.noTrack = noTrack ?? this.parent?.noTrack ?? false;
|
|
276
278
|
const resolvedPhase = phase ?? this.parent?.phase;
|
|
277
279
|
if (resolvedPhase === undefined) {
|
|
278
280
|
throw new Error("Phase is required");
|
|
@@ -307,14 +309,7 @@ export class Scope {
|
|
|
307
309
|
stateStore ??
|
|
308
310
|
this.parent?.stateStore ??
|
|
309
311
|
((scope) => new FileSystemStateStore(scope));
|
|
310
|
-
this.
|
|
311
|
-
this.state = new InstrumentedStateStore(
|
|
312
|
-
this.stateStore(this),
|
|
313
|
-
this.telemetryClient,
|
|
314
|
-
);
|
|
315
|
-
if (!telemetryClient && !this.parent?.telemetryClient) {
|
|
316
|
-
throw new Error("Telemetry client is required");
|
|
317
|
-
}
|
|
312
|
+
this.state = new InstrumentedStateStore(this.stateStore(this));
|
|
318
313
|
this.dataMutex = new AsyncMutex();
|
|
319
314
|
}
|
|
320
315
|
|
|
@@ -413,12 +408,7 @@ export class Scope {
|
|
|
413
408
|
}
|
|
414
409
|
|
|
415
410
|
public async init() {
|
|
416
|
-
await Promise.all([
|
|
417
|
-
this.state.init?.(),
|
|
418
|
-
this.telemetryClient.ready.catch((error) => {
|
|
419
|
-
this.logger.warn("Telemetry initialization failed:", error);
|
|
420
|
-
}),
|
|
421
|
-
]);
|
|
411
|
+
await Promise.all([this.state.init?.()]);
|
|
422
412
|
}
|
|
423
413
|
|
|
424
414
|
public async deinit() {
|
|
@@ -515,26 +505,15 @@ export class Scope {
|
|
|
515
505
|
return this.finalize();
|
|
516
506
|
}
|
|
517
507
|
|
|
518
|
-
/**
|
|
519
|
-
* The telemetry client for the root scope.
|
|
520
|
-
* This is used so that app-level hooks are only called once.
|
|
521
|
-
*/
|
|
522
|
-
private get rootTelemetryClient(): ITelemetryClient | null {
|
|
523
|
-
if (!this.parent) {
|
|
524
|
-
return this.telemetryClient;
|
|
525
|
-
}
|
|
526
|
-
return null;
|
|
527
|
-
}
|
|
528
|
-
|
|
529
508
|
public async finalize(options?: { force?: boolean; noop?: boolean }) {
|
|
530
509
|
const shouldForce =
|
|
531
510
|
options?.force ||
|
|
532
511
|
this.parent === undefined ||
|
|
533
512
|
this?.parent?.scopeName === this.root.scopeName;
|
|
534
513
|
if (this.phase === "read") {
|
|
535
|
-
|
|
536
|
-
event: "
|
|
537
|
-
|
|
514
|
+
createAndSendEvent({
|
|
515
|
+
event: "alchemy.success",
|
|
516
|
+
duration: performance.now() - this.startedAt,
|
|
538
517
|
});
|
|
539
518
|
return;
|
|
540
519
|
}
|
|
@@ -583,23 +562,21 @@ export class Scope {
|
|
|
583
562
|
force: shouldForce,
|
|
584
563
|
noop: options?.noop,
|
|
585
564
|
});
|
|
586
|
-
|
|
587
|
-
event: "
|
|
588
|
-
|
|
565
|
+
createAndSendEvent({
|
|
566
|
+
event: "alchemy.success",
|
|
567
|
+
duration: performance.now() - this.startedAt,
|
|
589
568
|
});
|
|
590
569
|
} else if (this.isErrored) {
|
|
591
570
|
this.logger.warn("Scope is in error, skipping finalize");
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
571
|
+
createAndSendEvent(
|
|
572
|
+
{
|
|
573
|
+
event: "alchemy.error",
|
|
574
|
+
duration: performance.now() - this.startedAt,
|
|
575
|
+
},
|
|
576
|
+
new Error("Scope failed"),
|
|
577
|
+
);
|
|
597
578
|
}
|
|
598
579
|
|
|
599
|
-
await this.rootTelemetryClient?.finalize()?.catch((error) => {
|
|
600
|
-
this.logger.warn("Telemetry finalization failed:", error);
|
|
601
|
-
});
|
|
602
|
-
|
|
603
580
|
if (!this.parent && process.env.ALCHEMY_TEST_KILL_ON_FINALIZE) {
|
|
604
581
|
await this.cleanup();
|
|
605
582
|
process.exit(0);
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { State, StateStore } from "../state.ts";
|
|
2
|
-
import
|
|
3
|
-
|
|
2
|
+
import {
|
|
3
|
+
createAndSendEvent,
|
|
4
|
+
type StateStoreTelemetryData,
|
|
5
|
+
} from "../util/telemetry.ts";
|
|
4
6
|
|
|
5
7
|
//todo(michael): we should also handle serde here
|
|
6
8
|
export class InstrumentedStateStore<T extends StateStore>
|
|
@@ -9,17 +11,15 @@ export class InstrumentedStateStore<T extends StateStore>
|
|
|
9
11
|
/** @internal */
|
|
10
12
|
__phantom?: T;
|
|
11
13
|
private readonly stateStore: StateStore;
|
|
12
|
-
private readonly telemetryClient: ITelemetryClient;
|
|
13
14
|
private readonly stateStoreClass: string;
|
|
14
15
|
|
|
15
|
-
constructor(stateStore: StateStore
|
|
16
|
+
constructor(stateStore: StateStore) {
|
|
16
17
|
this.stateStore = stateStore;
|
|
17
|
-
this.telemetryClient = telemetryClient;
|
|
18
18
|
this.stateStoreClass = stateStore.constructor.name;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
private async callWithTelemetry<T>(
|
|
22
|
-
event:
|
|
22
|
+
event: StateStoreTelemetryData["event"],
|
|
23
23
|
fn: () => Promise<T>,
|
|
24
24
|
): Promise<T> {
|
|
25
25
|
const start = performance.now();
|
|
@@ -30,17 +30,18 @@ export class InstrumentedStateStore<T extends StateStore>
|
|
|
30
30
|
error = err;
|
|
31
31
|
throw err;
|
|
32
32
|
} finally {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
33
|
+
createAndSendEvent(
|
|
34
|
+
{
|
|
35
|
+
event,
|
|
36
|
+
stateStore: this.stateStoreClass,
|
|
37
|
+
duration: performance.now() - start,
|
|
38
|
+
},
|
|
39
|
+
error instanceof Error
|
|
40
|
+
? error
|
|
41
|
+
: error
|
|
42
|
+
? new Error(String(error))
|
|
43
|
+
: undefined,
|
|
44
|
+
);
|
|
44
45
|
}
|
|
45
46
|
}
|
|
46
47
|
|
|
@@ -49,7 +50,7 @@ export class InstrumentedStateStore<T extends StateStore>
|
|
|
49
50
|
return;
|
|
50
51
|
}
|
|
51
52
|
await this.callWithTelemetry(
|
|
52
|
-
"
|
|
53
|
+
"statestore.init",
|
|
53
54
|
this.stateStore.init.bind(this.stateStore),
|
|
54
55
|
);
|
|
55
56
|
}
|
|
@@ -58,49 +59,49 @@ export class InstrumentedStateStore<T extends StateStore>
|
|
|
58
59
|
return;
|
|
59
60
|
}
|
|
60
61
|
await this.callWithTelemetry(
|
|
61
|
-
"
|
|
62
|
+
"statestore.deinit",
|
|
62
63
|
this.stateStore.deinit.bind(this.stateStore),
|
|
63
64
|
);
|
|
64
65
|
}
|
|
65
66
|
async list() {
|
|
66
67
|
return await this.callWithTelemetry(
|
|
67
|
-
"
|
|
68
|
+
"statestore.list",
|
|
68
69
|
this.stateStore.list.bind(this.stateStore),
|
|
69
70
|
);
|
|
70
71
|
}
|
|
71
72
|
async count() {
|
|
72
73
|
return await this.callWithTelemetry(
|
|
73
|
-
"
|
|
74
|
+
"statestore.count",
|
|
74
75
|
this.stateStore.count.bind(this.stateStore),
|
|
75
76
|
);
|
|
76
77
|
}
|
|
77
78
|
async get(key: string) {
|
|
78
79
|
return await this.callWithTelemetry(
|
|
79
|
-
"
|
|
80
|
+
"statestore.get",
|
|
80
81
|
this.stateStore.get.bind(this.stateStore, key),
|
|
81
82
|
);
|
|
82
83
|
}
|
|
83
84
|
async getBatch(ids: string[]) {
|
|
84
85
|
return await this.callWithTelemetry(
|
|
85
|
-
"
|
|
86
|
+
"statestore.getBatch",
|
|
86
87
|
this.stateStore.getBatch.bind(this.stateStore, ids),
|
|
87
88
|
);
|
|
88
89
|
}
|
|
89
90
|
async all() {
|
|
90
91
|
return await this.callWithTelemetry(
|
|
91
|
-
"
|
|
92
|
+
"statestore.all",
|
|
92
93
|
this.stateStore.all.bind(this.stateStore),
|
|
93
94
|
);
|
|
94
95
|
}
|
|
95
96
|
async set(key: string, value: State) {
|
|
96
97
|
await this.callWithTelemetry(
|
|
97
|
-
"
|
|
98
|
+
"statestore.set",
|
|
98
99
|
this.stateStore.set.bind(this.stateStore, key, value),
|
|
99
100
|
);
|
|
100
101
|
}
|
|
101
102
|
async delete(key: string) {
|
|
102
103
|
await this.callWithTelemetry(
|
|
103
|
-
"
|
|
104
|
+
"statestore.delete",
|
|
104
105
|
this.stateStore.delete.bind(this.stateStore, key),
|
|
105
106
|
);
|
|
106
107
|
}
|
package/src/test/bun.ts
CHANGED
|
@@ -4,7 +4,6 @@ import { afterAll, beforeAll, it } from "bun:test";
|
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { alchemy } from "../alchemy.ts";
|
|
6
6
|
import { Scope } from "../scope.ts";
|
|
7
|
-
import { NoopTelemetryClient } from "../util/telemetry/index.ts";
|
|
8
7
|
import type { TestOptions } from "./options.ts";
|
|
9
8
|
|
|
10
9
|
/**
|
|
@@ -103,7 +102,7 @@ export function test(meta: ImportMeta, defaultOptions?: TestOptions): test {
|
|
|
103
102
|
// parent: globalTestScope,
|
|
104
103
|
stateStore: defaultOptions?.stateStore,
|
|
105
104
|
phase: "up",
|
|
106
|
-
|
|
105
|
+
noTrack: true,
|
|
107
106
|
local: defaultOptions.local,
|
|
108
107
|
});
|
|
109
108
|
|
package/src/test/vitest.ts
CHANGED
|
@@ -8,7 +8,6 @@ import {
|
|
|
8
8
|
FileSystemStateStore,
|
|
9
9
|
SQLiteStateStore,
|
|
10
10
|
} from "../state/index.ts";
|
|
11
|
-
import { NoopTelemetryClient } from "../util/telemetry/client.ts";
|
|
12
11
|
import type { TestOptions } from "./options.ts";
|
|
13
12
|
|
|
14
13
|
/**
|
|
@@ -132,7 +131,7 @@ export function test(
|
|
|
132
131
|
scopeName: `${defaultOptions.prefix ? `${defaultOptions.prefix}-` : ""}${path.basename(meta.filename)}`,
|
|
133
132
|
stateStore: defaultOptions?.stateStore,
|
|
134
133
|
phase: "up",
|
|
135
|
-
|
|
134
|
+
noTrack: true,
|
|
136
135
|
quiet: defaultOptions.quiet,
|
|
137
136
|
password: process.env.ALCHEMY_PASSWORD,
|
|
138
137
|
local: defaultOptions.local,
|
package/src/util/diff.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { isDeepStrictEqual } from "node:util";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Returns an array of keys in `b` that are different from `a`.
|
|
5
|
+
*/
|
|
6
|
+
export const diff = <T>(a: T, b: NoInfer<T>) => {
|
|
7
|
+
const keys: (keyof T)[] = [];
|
|
8
|
+
for (const key in a) {
|
|
9
|
+
if (!isDeepStrictEqual(a[key], b[key])) {
|
|
10
|
+
keys.push(key);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return keys;
|
|
14
|
+
};
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import envPaths from "env-paths";
|
|
2
|
+
import { exec } from "node:child_process";
|
|
3
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import path from "pathe";
|
|
6
|
+
import pkg from "../../package.json" with { type: "json" };
|
|
7
|
+
import type { Phase } from "../alchemy.ts";
|
|
8
|
+
import { Scope } from "../scope.ts";
|
|
9
|
+
import { logger } from "./logger.ts";
|
|
10
|
+
import { memoize } from "./memoize.ts";
|
|
11
|
+
|
|
12
|
+
export const CONFIG_PATH = path.join(os.homedir(), ".alchemy", "id");
|
|
13
|
+
export const CONFIG_PATH_LEGACY = path.join(
|
|
14
|
+
envPaths("alchemy", { suffix: "" }).config,
|
|
15
|
+
"id",
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
export const TELEMETRY_DISABLED =
|
|
19
|
+
!!process.env.ALCHEMY_TELEMETRY_DISABLED || !!process.env.DO_NOT_TRACK;
|
|
20
|
+
|
|
21
|
+
export const TELEMETRY_API_URL =
|
|
22
|
+
process.env.ALCHEMY_TELEMETRY_API_URL ?? "https://telemetry.alchemy.run";
|
|
23
|
+
export const SUPPRESS_TELEMETRY_ERRORS =
|
|
24
|
+
!!process.env.ALCHEMY_TELEMETRY_SUPPRESS_ERRORS;
|
|
25
|
+
|
|
26
|
+
async function getOrCreateUserId() {
|
|
27
|
+
async function readUserId(path: string) {
|
|
28
|
+
try {
|
|
29
|
+
return (await readFile(path, "utf-8")).trim();
|
|
30
|
+
} catch {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const id = await readUserId(CONFIG_PATH);
|
|
36
|
+
if (id) {
|
|
37
|
+
return id;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const legacyId = await readUserId(CONFIG_PATH_LEGACY);
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
const id = legacyId ?? crypto.randomUUID();
|
|
44
|
+
await mkdir(path.dirname(CONFIG_PATH), { recursive: true });
|
|
45
|
+
await writeFile(CONFIG_PATH, id);
|
|
46
|
+
if (!legacyId) {
|
|
47
|
+
console.warn(
|
|
48
|
+
[
|
|
49
|
+
"Attention: To help improve Alchemy, we collect anonymous usage, performance, and error data.",
|
|
50
|
+
"You can opt out by setting the ALCHEMY_TELEMETRY_DISABLED or DO_NOT_TRACK environment variable to a truthy value.",
|
|
51
|
+
].join("\n"),
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
return id;
|
|
55
|
+
} catch {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async function getRootCommitHash() {
|
|
61
|
+
return new Promise<string | null>((resolve) => {
|
|
62
|
+
exec("git rev-list --max-parents=0 HEAD", (err, stdout) => {
|
|
63
|
+
if (err) {
|
|
64
|
+
resolve(null);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
resolve(stdout.trim());
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async function getGitOriginUrlHash() {
|
|
73
|
+
return new Promise<string | null>((resolve) => {
|
|
74
|
+
exec("git config --get remote.origin.url", (err, stdout) => {
|
|
75
|
+
if (err) {
|
|
76
|
+
resolve(null);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
resolve(hashString(stdout.trim()));
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function getBranchName() {
|
|
85
|
+
return new Promise<string | null>((resolve) => {
|
|
86
|
+
exec("git rev-parse --abbrev-ref HEAD", (err, stdout) => {
|
|
87
|
+
if (err) {
|
|
88
|
+
resolve(null);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
resolve(stdout.trim());
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function getRuntime() {
|
|
97
|
+
if (globalThis.Bun) return { name: "bun", version: Bun.version };
|
|
98
|
+
// @ts-expect-error
|
|
99
|
+
if (globalThis.Deno)
|
|
100
|
+
// @ts-expect-error
|
|
101
|
+
return { name: "deno", version: Deno.version?.deno ?? null };
|
|
102
|
+
// @ts-expect-error
|
|
103
|
+
if (globalThis.EdgeRuntime) return { name: "workerd", version: null };
|
|
104
|
+
if (globalThis.process?.versions?.node)
|
|
105
|
+
return { name: "node", version: process.versions.node };
|
|
106
|
+
return { name: null, version: null };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const PROVIDERS = [
|
|
110
|
+
{ env: "GITHUB_ACTIONS", provider: "GitHub Actions", isCI: true },
|
|
111
|
+
{ env: "GITLAB_CI", provider: "GitLab CI", isCI: true },
|
|
112
|
+
{ env: "CIRCLECI", provider: "CircleCI", isCI: true },
|
|
113
|
+
{ env: "JENKINS_URL", provider: "Jenkins", isCI: true },
|
|
114
|
+
{ env: "TRAVIS", provider: "Travis CI", isCI: true },
|
|
115
|
+
{ env: "NOW_BUILDER", provider: "Vercel", isCI: true },
|
|
116
|
+
{ env: "VERCEL", provider: "Vercel", isCI: false },
|
|
117
|
+
] as const;
|
|
118
|
+
|
|
119
|
+
function getEnvironment() {
|
|
120
|
+
for (const provider of PROVIDERS) {
|
|
121
|
+
if (process.env[provider.env]) {
|
|
122
|
+
return {
|
|
123
|
+
provider: provider.provider,
|
|
124
|
+
isCI: provider.isCI,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
provider: null,
|
|
130
|
+
isCI: !!process.env.CI,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export const collectData = memoize(async (): Promise<GenericTelemetryData> => {
|
|
135
|
+
const [
|
|
136
|
+
userId,
|
|
137
|
+
rootCommitHash,
|
|
138
|
+
gitOriginUrlHash,
|
|
139
|
+
branchHash,
|
|
140
|
+
runtime,
|
|
141
|
+
environment,
|
|
142
|
+
] = await Promise.all([
|
|
143
|
+
getOrCreateUserId(),
|
|
144
|
+
getRootCommitHash(),
|
|
145
|
+
getGitOriginUrlHash(),
|
|
146
|
+
getBranchName().then(hashString),
|
|
147
|
+
getRuntime(),
|
|
148
|
+
getEnvironment(),
|
|
149
|
+
]);
|
|
150
|
+
return {
|
|
151
|
+
userId: userId ?? "",
|
|
152
|
+
sessionId: crypto.randomUUID(),
|
|
153
|
+
platform: os.platform(),
|
|
154
|
+
osVersion: os.release(),
|
|
155
|
+
arch: os.arch(),
|
|
156
|
+
cpus: os.cpus().length,
|
|
157
|
+
memory: Math.round(os.totalmem() / 1024 / 1024),
|
|
158
|
+
rootCommitHash: rootCommitHash ?? "",
|
|
159
|
+
gitOriginUrl: gitOriginUrlHash ?? "",
|
|
160
|
+
gitBranchHash: branchHash ?? "",
|
|
161
|
+
runtime: runtime.name ?? "",
|
|
162
|
+
runtimeVersion: runtime.version ?? "",
|
|
163
|
+
ciProvider: environment.provider ?? "",
|
|
164
|
+
isCI: environment.isCI,
|
|
165
|
+
alchemyVersion: pkg.version,
|
|
166
|
+
};
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
export type GenericTelemetryData = {
|
|
170
|
+
userId: string;
|
|
171
|
+
sessionId: string;
|
|
172
|
+
platform: string;
|
|
173
|
+
osVersion: string;
|
|
174
|
+
arch: string;
|
|
175
|
+
cpus: number;
|
|
176
|
+
memory: number;
|
|
177
|
+
rootCommitHash: string;
|
|
178
|
+
gitOriginUrl: string;
|
|
179
|
+
gitBranchHash: string;
|
|
180
|
+
runtime: string;
|
|
181
|
+
runtimeVersion: string;
|
|
182
|
+
ciProvider: string;
|
|
183
|
+
isCI: boolean;
|
|
184
|
+
alchemyVersion: string;
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
export type ErrorData = {
|
|
188
|
+
errorTag: string;
|
|
189
|
+
errorMessage: string;
|
|
190
|
+
errorStack: string;
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
export type CliTelemetryData = {
|
|
194
|
+
command: string;
|
|
195
|
+
event: "cli.start" | "cli.success" | "cli.error";
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
export type ResourceTelemetryData = {
|
|
199
|
+
phase: Phase;
|
|
200
|
+
event:
|
|
201
|
+
| "resource.start"
|
|
202
|
+
| "resource.success"
|
|
203
|
+
| "resource.error"
|
|
204
|
+
| "resource.skip"
|
|
205
|
+
| "resource.read";
|
|
206
|
+
resource: string;
|
|
207
|
+
status:
|
|
208
|
+
| "creating"
|
|
209
|
+
| "created"
|
|
210
|
+
| "updating"
|
|
211
|
+
| "updated"
|
|
212
|
+
| "deleting"
|
|
213
|
+
| "deleted"
|
|
214
|
+
| "unknown";
|
|
215
|
+
duration: number;
|
|
216
|
+
replaced: boolean;
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
export type StateStoreTelemetryData = {
|
|
220
|
+
event:
|
|
221
|
+
| "statestore.init"
|
|
222
|
+
| "statestore.deinit"
|
|
223
|
+
| "statestore.list"
|
|
224
|
+
| "statestore.count"
|
|
225
|
+
| "statestore.get"
|
|
226
|
+
| "statestore.getBatch"
|
|
227
|
+
| "statestore.all"
|
|
228
|
+
| "statestore.set"
|
|
229
|
+
| "statestore.delete";
|
|
230
|
+
stateStore: string;
|
|
231
|
+
duration: number;
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
export type AlchemyTelemetryData = {
|
|
235
|
+
event: "alchemy.start" | "alchemy.success" | "alchemy.error";
|
|
236
|
+
duration: number;
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
export async function createAndSendEvent(
|
|
240
|
+
data:
|
|
241
|
+
| CliTelemetryData
|
|
242
|
+
| ResourceTelemetryData
|
|
243
|
+
| StateStoreTelemetryData
|
|
244
|
+
| AlchemyTelemetryData,
|
|
245
|
+
error?: Error,
|
|
246
|
+
) {
|
|
247
|
+
if (Scope.getScope()?.noTrack || TELEMETRY_DISABLED) {
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
try {
|
|
251
|
+
const eventData = {
|
|
252
|
+
...data,
|
|
253
|
+
...("duration" in data
|
|
254
|
+
? { duration: Math.round(data.duration * 1000) }
|
|
255
|
+
: {}),
|
|
256
|
+
...(await collectData()),
|
|
257
|
+
...serializeError(error),
|
|
258
|
+
};
|
|
259
|
+
await fetch(TELEMETRY_API_URL, {
|
|
260
|
+
method: "POST",
|
|
261
|
+
headers: {
|
|
262
|
+
"Content-Type": "application/json",
|
|
263
|
+
},
|
|
264
|
+
body: JSON.stringify(eventData),
|
|
265
|
+
});
|
|
266
|
+
} catch (error) {
|
|
267
|
+
if (!SUPPRESS_TELEMETRY_ERRORS) {
|
|
268
|
+
logger.warn("Failed to send telemetry event:", error);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
async function hashString(input: string | null): Promise<string | null> {
|
|
274
|
+
if (input == null) {
|
|
275
|
+
return null;
|
|
276
|
+
}
|
|
277
|
+
const data = new TextEncoder().encode(input);
|
|
278
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
|
279
|
+
return Array.from(new Uint8Array(hashBuffer), (byte) =>
|
|
280
|
+
byte.toString(16).padStart(2, "0"),
|
|
281
|
+
).join("");
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function serializeError(error: Error | undefined) {
|
|
285
|
+
if (error instanceof Error) {
|
|
286
|
+
return {
|
|
287
|
+
errorTag: error.name ?? "",
|
|
288
|
+
errorMessage: error.message?.replaceAll(os.homedir(), "~") ?? "", // redact home directory
|
|
289
|
+
errorStack: error.stack?.replaceAll(os.homedir(), "~") ?? "",
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
return {
|
|
293
|
+
errorTag: "",
|
|
294
|
+
errorMessage: "",
|
|
295
|
+
errorStack: "",
|
|
296
|
+
};
|
|
297
|
+
}
|