From b0893315ee1cc46f8c3d1b2d7cd8dd30c593b010 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Ad=C3=A1mek?= Date: Fri, 7 Aug 2026 13:04:22 +0200 Subject: [PATCH] refactor: remove the `KeyValueStore.getPublicUrl` override, delegate to the storage backend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Crawlee v4's `KeyValueStore.getPublicUrl()` delegates to the storage backend, and `ApifyKeyValueStoreBackend.getPublicUrl()` (backed by apify-client's `getRecordPublicUrl()`) produces the exact same signed URL as the SDK's manual override did — same public base URL (the client is constructed with `publicBaseUrl: config.apiPublicBaseUrl`) and same HMAC signature from the store's `urlSigningSecretKey`. Local stores keep their behavior too, since both paths run the same core delegation. With the override gone the subclass was an empty shell, so it is removed entirely and `KeyValueStore` is re-exported from `@crawlee/core`, same as `Dataset` and `RequestQueue`. Related: #433 --- src/actor.ts | 3 +- src/index.ts | 2 +- src/key_value_store.ts | 64 ------------------------------------------ 3 files changed, 2 insertions(+), 67 deletions(-) delete mode 100644 src/key_value_store.ts diff --git a/src/actor.ts b/src/actor.ts index 1b29d2e72f..6e78b3f050 100644 --- a/src/actor.ts +++ b/src/actor.ts @@ -8,7 +8,7 @@ import type { StorageOpenOptions, UseStateOptions, } from '@crawlee/core'; -import { Dataset, EventType, purgeDefaultStorages, RequestQueue, serviceLocator } from '@crawlee/core'; +import { Dataset, EventType, KeyValueStore, purgeDefaultStorages, RequestQueue, serviceLocator } from '@crawlee/core'; import type { Awaitable, Constructor, Dictionary, StorageBackend } from '@crawlee/types'; import { sleep } from '@crawlee/utils'; import type { @@ -46,7 +46,6 @@ import { ChargingManager, pushDataAndCharge } from './charging.js'; import type { ConfigurationOptions } from './configuration.js'; import { Configuration } from './configuration.js'; import { getDefaultsFromInputSchema, noActorInputSchemaDefinedMarker, readInputSchema } from './input-schemas.js'; -import { KeyValueStore } from './key_value_store.js'; import { PlatformEventManager } from './platform_event_manager.js'; import type { ProxyConfigurationOptions } from './proxy_configuration.js'; import { ProxyConfiguration } from './proxy_configuration.js'; diff --git a/src/index.ts b/src/index.ts index 552f94417b..2d374d87de 100644 --- a/src/index.ts +++ b/src/index.ts @@ -14,7 +14,6 @@ export { ChargeOptions, ChargeResult, ActorPricingInfo, ChargingManager } from ' export * from './configuration.js'; export * from './proxy_configuration.js'; export * from './platform_event_manager.js'; -export * from './key_value_store.js'; export { Dataset, DatasetDataOptions, @@ -27,6 +26,7 @@ export { RequestQueue, RequestQueueOperationOptions, RequestQueueOptions, + KeyValueStore, KeyConsumer, KeyValueStoreOptions, RecordOptions, diff --git a/src/key_value_store.ts b/src/key_value_store.ts deleted file mode 100644 index a7a7969e45..0000000000 --- a/src/key_value_store.ts +++ /dev/null @@ -1,64 +0,0 @@ -import type { StorageOpenOptions } from '@crawlee/core'; -import { KeyValueStore as CoreKeyValueStore, serviceLocator } from '@crawlee/core'; -import type { KeyValueStoreInfo } from '@crawlee/types'; - -import { createHmacSignature } from '@apify/utilities'; - -import { ApifyKeyValueStoreBackend } from './apify_key_value_store_backend.js'; -import type { Configuration } from './configuration.js'; - -// crawlee v4 dropped the `storageObject` cache from `KeyValueStore`, so the -// per-store `urlSigningSecretKey` (which is part of the platform's metadata -// response but not declared on `@crawlee/types`' `KeyValueStoreInfo`) has to -// be fetched on demand and accessed through a structural-typed augmentation. -type ApifyKeyValueStoreInfo = KeyValueStoreInfo & { - urlSigningSecretKey?: string; -}; - -/** - * @inheritDoc - */ -export class KeyValueStore extends CoreKeyValueStore { - /** - * Returns a URL for the given key that may be used to publicly - * access the value in the remote key-value store. - * - * On the Apify platform the URL is signed with the store's - * `urlSigningSecretKey` so that anyone with the URL can read the record - * without authentication. Locally we delegate to crawlee's default - * implementation (which produces a `file://` URL or returns `undefined`). - */ - override async getPublicUrl(key: string): Promise { - const config = serviceLocator.getConfiguration() as Configuration; - - // Detect a remote (Apify) store by its backend type rather than by - // `isAtHome`, so that a `forceCloud` store opened locally still gets a - // signed Apify URL (matching the platform behaviour). `backend` is - // `private` on `CoreKeyValueStore`, so bypass the visibility check. - const { backend } = this as unknown as { backend: unknown }; - - if (!(backend instanceof ApifyKeyValueStoreBackend)) { - return super.getPublicUrl(key); - } - - const publicUrl = new URL(`${config.apiPublicBaseUrl}/v2/key-value-stores/${this.id}/records/${key}`); - - const metadata = (await backend.getMetadata()) as ApifyKeyValueStoreInfo; - - if (metadata?.urlSigningSecretKey) { - publicUrl.searchParams.append('signature', createHmacSignature(metadata.urlSigningSecretKey, key)); - } - - return publicUrl.toString(); - } - - /** - * @inheritDoc - */ - static override async open( - storeIdOrName?: string | null, - options: StorageOpenOptions = {}, - ): Promise { - return super.open(storeIdOrName, options) as unknown as KeyValueStore; - } -}