feat: Product Follow Contract Service#342
feat: Product Follow Contract Service#342Ai-chan-0411 wants to merge 4 commits intoStarShopCr:mainfrom
Conversation
Implements comprehensive TypeScript service layer for Product Follow Contract: - Follow management (follow/unfollow/getFollowers/getFollowing/isFollowing) - Notification management with preferences and history - Alert system for price/stock conditions - Rate limiting and input validation - In-memory caching with TTL - Event system for follow state changes
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 11 minutes and 6 seconds. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
📝 WalkthroughWalkthroughA new Product Follow Contract Service layer is introduced with comprehensive TypeScript implementation for managing product following, notifications, and alerts. The service includes follow/unfollow operations, follower/following queries, notification preferences and delivery, alert creation and evaluation, plus supporting infrastructure for rate limiting, caching, validation, and event handling. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant Service as ProductFollowService
participant RateLimit as RateLimiter
participant Cache
participant Events as Event System
Client->>Service: followProduct(productId, userAddress)
Service->>RateLimit: checkRateLimit(userAddress, 'follow')
RateLimit-->>Service: allowed/denied
alt Rate limit exceeded
Service-->>Client: error response
else Rate limit OK
Service->>Cache: check isFollowing cache
Cache-->>Service: cache result or miss
Service->>Service: validate inputs
Service->>Service: create ProductFollow object
Service->>Cache: invalidate follower/following caches
Service->>Events: emit('follow', event)
Events-->>Service: event processed
Service-->>Client: FollowResponse<ProductFollow>
end
sequenceDiagram
participant Client
participant Service as ProductFollowService
participant Cache
participant Validation as Validators
Client->>Service: sendNotification(productId, type, data)
Service->>Validation: validateNotificationFormat(data)
Validation-->>Service: validation result
alt Validation fails
Service-->>Client: error response
else Validation passes
Service->>Service: generate notification ID
Service->>Service: create FollowNotification object
Service->>Cache: store notification
Service->>Cache: invalidate notification history cache
Service-->>Client: FollowResponse<FollowNotification>
end
sequenceDiagram
participant Client
participant Service as ProductFollowService
participant Cache
participant Evaluator as Condition Evaluator
Client->>Service: triggerAlert(alertId, currentValues)
Service->>Cache: retrieve alert from storage
Cache-->>Service: FollowAlert object
Service->>Evaluator: evaluate all conditions
loop For each condition
Evaluator->>Evaluator: compare currentValue vs condition value
Evaluator-->>Service: condition result
end
Service->>Service: build AlertTriggerResult
Service->>Cache: update alert isTriggered flag
Service-->>Client: FollowResponse<AlertTriggerResult>
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (9)
src/shared/services/product_follow/types/follow.types.ts (1)
118-118: Consider usingunknowninstead ofanyfor the default type parameter.ESLint flags the
anytype. Usingunknownas the default provides stricter type safety while maintaining flexibility.♻️ Suggested fix
-export interface FollowResponse<T = any> { +export interface FollowResponse<T = unknown> {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/shared/services/product_follow/types/follow.types.ts` at line 118, Change the default generic type for FollowResponse from any to unknown to improve type safety: update the interface declaration FollowResponse<T = any> to use T = unknown and adjust any downstream usages that rely on implicit any (e.g., places constructing FollowResponse without a type arg) to either assert the correct type parameter or handle T as unknown; ensure related utility types or function signatures that expect FollowResponse use explicit type parameters or proper narrowing where necessary.src/shared/services/product_follow/follow.service.ts (5)
143-143: Remove unusedpublicKeyvariable.The variable is assigned but never used.
🧹 Suggested fix
- const publicKey = await getPublicKey(); const follow: ProductFollow = {If
getPublicKey()is needed for authorization or the follow record, use it; otherwise remove the call entirely.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/shared/services/product_follow/follow.service.ts` at line 143, The assignment to the local variable publicKey (from calling getPublicKey()) is unused; either remove the call and the publicKey variable entirely or, if getPublicKey() is required for auth or to populate the follow record, wire the returned value into the relevant logic (e.g., pass it to the function that creates/saves the follow or include it in the payload). Locate the usage in follow.service.ts where const publicKey = await getPublicKey() appears and either delete that line or replace it by using the publicKey in the appropriate method/field so the value is consumed.
88-88: Replaceanywith specific type in cache Map.The
anytype reduces type safety. Use a union or generic constraint.♻️ Suggested fix
- private cache: Map<string, CacheEntry<any>> = new Map(); + private cache: Map<string, CacheEntry<unknown>> = new Map();🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/shared/services/product_follow/follow.service.ts` at line 88, The cache Map currently uses CacheEntry<any> which weakens type safety; update the declaration of private cache to use a concrete generic type (e.g., CacheEntry<FollowInfo> or a union like CacheEntry<FollowCount | FollowDetails>) that matches the actual values stored, and adjust any usages of cache.get/set and CacheEntry<T> consumers in FollowService to use that concrete type; reference the private cache property and the CacheEntry<T> generic in follow.service.ts and update related methods (e.g., wherever cache.set, cache.get, or cache entries are created) so the compiler enforces correct value shapes.
697-705: FIFO eviction may not be optimal for cache.Using
this.cache.keys().next().valueevicts the first-inserted entry (FIFO), but LRU (least-recently-used) would better retain frequently accessed data. For a simple in-memory cache this is acceptable, but consider if access patterns warrant LRU.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/shared/services/product_follow/follow.service.ts` around lines 697 - 705, The current setCache method uses FIFO eviction by deleting this.cache.keys().next().value; change to LRU by ensuring accesses update insertion order and newly-set keys move to the end: in setCache(key, ...) if the key already exists, delete it first then set it so it becomes most-recently-used; when reading the cache (e.g., getCache or any lookup methods), upon a hit delete the key and re-set it with the same value to mark it as recently used; keep the eviction logic to remove this.cache.keys().next().value when size >= maxSize so the oldest (least-recently-used) entry is removed. Ensure you update both setCache and the corresponding cache read method(s) (getCache/lookup functions) to implement the LRU behavior.
277-295: Cache returnsnullforfalsevalues.In
isFollowing, cachedfalsevalues are indistinguishable from cache misses becausegetFromCachereturnsnullfor missing entries, but the checkif (cached !== null)won't catch cachedfalsesincefalse !== nullistrue. However,if (cached)would skipfalse. The current code is correct but brittle.Consider returning a wrapper object or using a sentinel value to distinguish "not cached" from "cached as false":
const cached = this.getFromCache<boolean>(cacheKey); if (cached !== null) return this.successResponse(cached);This works correctly since
false !== null. Just noting for awareness during future maintenance.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/shared/services/product_follow/follow.service.ts` around lines 277 - 295, The cache ambiguity can bite future maintainers; change getFromCache to return a discriminated CacheResult<T> (e.g., { found: boolean; value?: T }) and update isFollowing to call getFromCache<boolean>(cacheKey) and branch on cached.found — return successResponse(cached.value as boolean) when found, otherwise continue to contract call and setCache; update callers like isFollowing to use the new CacheResult shape and adjust setCache usage accordingly (refer to isFollowing, getFromCache, setCache, and CACHE_KEYS.IS_FOLLOWING).
1-65: Clean up unused imports.ESLint correctly identifies numerous unused imports. Remove them to improve code clarity and prevent confusion about which features are actually implemented.
🧹 Suggested cleanup
import { - signTransaction, getPublicKey, isWalletConnected } from '../../utils/wallet'; import { - NETWORKS, DEFAULT_CONFIG, - CONTRACT_METHODS, CACHE_KEYS, VALIDATION, - ERROR_MESSAGES, DEFAULT_NOTIFICATION_PREFERENCES } from './constants/follow.constants'; import { validateAddress, validateProductId, validateNotificationFormat, validateUserPreferences, validateAlertConditions, - evaluateCondition, generateFollowId, generateAlertId, generateNotificationId, getErrorMessage, - isCacheExpired, - retryWithBackoff + isCacheExpired } from './utils/follow.utils'; import { FollowServiceConfig, - FollowNetworkConfig, FollowResponse, - FollowTransactionResult, ProductFollow, - FollowStatus, Follower, FollowedProduct, FollowErrorCode, FollowEventType, FollowEventData, FollowEventListener, EventSubscription, - PaginationParams, - PaginatedResponse, - RateLimitConfig, - CacheConfig + PaginationParams, + PaginatedResponse } from './types/follow.types'; import { FollowNotification, NotificationType, NotificationPreferences, NotificationTypePreference, - NotificationChannel, - SendNotificationRequest, NotificationHistoryQuery } from './types/notification.types'; import { FollowAlert, - AlertType, AlertCondition, CreateAlertRequest, UpdateAlertRequest, AlertTriggerResult, AlertQuery } from './types/alert.types';Note: Keep
evaluateConditionandretryWithBackoffif you plan to implement the pending functionality soon.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/shared/services/product_follow/follow.service.ts` around lines 1 - 65, Remove the unused imports flagged by ESLint in follow.service.ts to clean up the top-of-file import lists; specifically drop unused symbols from the constants group (e.g., NETWORKS, DEFAULT_CONFIG, CONTRACT_METHODS, CACHE_KEYS, VALIDATION, ERROR_MESSAGES, DEFAULT_NOTIFICATION_PREFERENCES) and from the utils group (e.g., validateAddress, validateProductId, validateNotificationFormat, validateUserPreferences, validateAlertConditions, generateFollowId, generateAlertId, generateNotificationId, getErrorMessage, isCacheExpired), but retain evaluateCondition and retryWithBackoff as noted; ensure the types and notification/alert type imports that are actually referenced by the file (check FollowServiceConfig, FollowResponse, FollowTransactionResult, ProductFollow, FollowStatus, Follower, FollowedProduct, FollowErrorCode, FollowEventType, FollowEventData, FollowEventListener, EventSubscription, PaginationParams, PaginatedResponse, RateLimitConfig, CacheConfig, FollowNotification, NotificationType, NotificationPreferences, NotificationTypePreference, NotificationChannel, SendNotificationRequest, NotificationHistoryQuery, FollowAlert, AlertType, AlertCondition, CreateAlertRequest, UpdateAlertRequest, AlertTriggerResult, AlertQuery) remain, and run ESLint to verify no remaining unused imports.src/shared/services/product_follow/README.md (1)
7-19: Add a language specifier to the fenced code block.The directory structure code block is missing a language specifier. Use
textorplaintextto satisfy linting and improve rendering consistency.📝 Suggested fix
-``` +```text src/shared/services/product_follow/ follow.service.ts // Main service class🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/shared/services/product_follow/README.md` around lines 7 - 19, Update the fenced code block in src/shared/services/product_follow/README.md to include a language specifier (e.g., "text" or "plaintext") after the opening triple backticks so the directory tree renders and lints correctly; locate the triple-backtick block that contains the directory listing (the lines showing follow.service.ts, index.ts, types/, utils/, constants/) and change the opening ``` to ```text (or ```plaintext).src/shared/services/product_follow/constants/follow.constants.ts (1)
3-3: Remove unused import.
AlertTypeis imported but never used in this file.🧹 Suggested fix
import { FollowErrorCode } from '../types/follow.types'; import { NotificationType, NotificationChannel } from '../types/notification.types'; -import { AlertType } from '../types/alert.types';🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/shared/services/product_follow/constants/follow.constants.ts` at line 3, Remove the unused import AlertType from follow.constants.ts: locate the import statement "import { AlertType } from '../types/alert.types';" in the module and delete it so there are no unused imports; ensure no other references to AlertType remain in functions/constants in this file (e.g., any constants exported from follow.constants.ts) before committing.src/shared/services/product_follow/types/alert.types.ts (1)
1-1: Remove unusedu32import.Only
u64is used in this file for timestamp fields.🧹 Suggested fix
-import type { u32, u64 } from '@stellar/stellar-sdk'; +import type { u64 } from '@stellar/stellar-sdk';🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/shared/services/product_follow/types/alert.types.ts` at line 1, Remove the unused import u32 from the import statement that currently brings in both u32 and u64 from '@stellar/stellar-sdk'; keep only u64 since timestamp fields use u64 (remove the u32 symbol from the import to eliminate the unused import warning).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/shared/services/product_follow/constants/follow.constants.ts`:
- Around line 14-19: The mainnet rpcUrl in the exported mainnet config object
(property name "mainnet" and key "rpcUrl" in follow.constants.ts) currently
points to Horizon and must be replaced with a Soroban-compatible RPC provider;
update the rpcUrl value to a verified Soroban endpoint (for example
https://rpc.ankr.com/stellar_soroban or
https://soroban-rpc.mainnet.stellar.gateway.fm/ or another production-grade
provider) and ensure the isTestnet flag remains false; after swapping the URL,
verify any integration tests or calls that use the mainnet.rpcUrl still function
and consider documenting provider choice and rate-limit/reliability
considerations for production.
In `@src/shared/services/product_follow/follow.service.ts`:
- Around line 137-141: The current check uses if (isAlreadyFollowing.data) which
treats both false and undefined as falsy and can miss errors from isFollowing;
update the logic to explicitly handle an undefined result first (e.g., if
(isAlreadyFollowing.data === undefined) return this.errorResponse(/* appropriate
error code or propagate isAlreadyFollowing.error */)), then only treat a true
value as already-following (if (isAlreadyFollowing.data === true) return
this.errorResponse(FollowErrorCode.ALREADY_FOLLOWING)); reference the
isFollowing call and the existing errorResponse/FollowErrorCode usage when
implementing this.
- Around line 516-518: The updateAlert flow currently invalidates cache then
returns an unsafe empty object cast to FollowAlert; instead, have updateAlert
(the method containing this.invalidateCache and return this.successResponse)
return a valid FollowAlert instance or explicitly error: fetch or construct the
updated alert (e.g., reload from storage or use the updated fields) and pass
that object to this.successResponse, or throw a clear error/NotImplemented
exception if the actual update/return is not implemented; ensure references to
CACHE_KEYS.ALERTS(userAddress), FollowAlert, and successResponse are used so
consumers receive a complete alert with required properties like alertId and
conditions.
- Around line 582-603: triggerAlert currently ignores currentValues and never
calls evaluateCondition, so matchedConditions stays empty; modify triggerAlert
to fetch the alert's conditions (via the existing alert lookup method on this,
e.g., this.getAlertById or this.getAlertConditions), iterate over those
AlertCondition items and call evaluateCondition(condition, currentValues) for
each, push any satisfied conditions into matchedConditions, then set
result.triggered = matchedConditions.length > 0 and set checkedAt = Date.now()
(as a number) before returning this.successResponse(result); keep the existing
errorResponse handling unchanged.
- Line 148: The code is illegally coercing BigInt to number for u64 fields
(e.g., the createdAt assignments in follow.service.ts) which bypasses the
u64/UnsignedHyper contract; locate the nine occurrences (createdAt and other
timestamp fields around lines noted) and replace the double-cast pattern
(BigInt(Date.now()) as unknown as number) by either preserving a BigInt (use
BigInt(Date.now())) if your downstream accepts bigint, or by constructing the
Stellar SDK UnsignedHyper explicitly (new UnsignedHyper(Number(Date.now())) or
the appropriate UnsignedHyper constructor) so the value conforms to the u64
type; ensure imports reference UnsignedHyper from the Stellar SDK and update any
call sites that expect a primitive number accordingly.
In `@src/shared/services/product_follow/index.ts`:
- Around line 76-99: The factory functions createTestnetFollowService and
createMainnetFollowService duplicate network configuration; replace the
hardcoded network objects with the corresponding entries from the NETWORKS
constant (imported from follow.constants.ts) when constructing new
ProductFollowService instances so they read e.g. new ProductFollowService({
network: NETWORKS.TESTNET }) and new ProductFollowService({ network:
NETWORKS.MAINNET }); ensure NETWORKS is imported and used for contractId,
networkPassphrase, rpcUrl, and isTestnet to keep configuration DRY.
In `@src/shared/services/product_follow/utils/follow.utils.ts`:
- Around line 181-183: generateFollowId currently builds IDs from productId,
userAddress and Date.now(), which can collide on rapid consecutive calls; update
the generateFollowId function to append a secure random component (e.g., a UUID
v4 or hex from crypto.randomBytes) to the template so the ID becomes globally
unique, and ensure the implementation mirrors the randomness approach used by
generateAlertId/generateNotificationId to avoid collisions.
---
Nitpick comments:
In `@src/shared/services/product_follow/constants/follow.constants.ts`:
- Line 3: Remove the unused import AlertType from follow.constants.ts: locate
the import statement "import { AlertType } from '../types/alert.types';" in the
module and delete it so there are no unused imports; ensure no other references
to AlertType remain in functions/constants in this file (e.g., any constants
exported from follow.constants.ts) before committing.
In `@src/shared/services/product_follow/follow.service.ts`:
- Line 143: The assignment to the local variable publicKey (from calling
getPublicKey()) is unused; either remove the call and the publicKey variable
entirely or, if getPublicKey() is required for auth or to populate the follow
record, wire the returned value into the relevant logic (e.g., pass it to the
function that creates/saves the follow or include it in the payload). Locate the
usage in follow.service.ts where const publicKey = await getPublicKey() appears
and either delete that line or replace it by using the publicKey in the
appropriate method/field so the value is consumed.
- Line 88: The cache Map currently uses CacheEntry<any> which weakens type
safety; update the declaration of private cache to use a concrete generic type
(e.g., CacheEntry<FollowInfo> or a union like CacheEntry<FollowCount |
FollowDetails>) that matches the actual values stored, and adjust any usages of
cache.get/set and CacheEntry<T> consumers in FollowService to use that concrete
type; reference the private cache property and the CacheEntry<T> generic in
follow.service.ts and update related methods (e.g., wherever cache.set,
cache.get, or cache entries are created) so the compiler enforces correct value
shapes.
- Around line 697-705: The current setCache method uses FIFO eviction by
deleting this.cache.keys().next().value; change to LRU by ensuring accesses
update insertion order and newly-set keys move to the end: in setCache(key, ...)
if the key already exists, delete it first then set it so it becomes
most-recently-used; when reading the cache (e.g., getCache or any lookup
methods), upon a hit delete the key and re-set it with the same value to mark it
as recently used; keep the eviction logic to remove
this.cache.keys().next().value when size >= maxSize so the oldest
(least-recently-used) entry is removed. Ensure you update both setCache and the
corresponding cache read method(s) (getCache/lookup functions) to implement the
LRU behavior.
- Around line 277-295: The cache ambiguity can bite future maintainers; change
getFromCache to return a discriminated CacheResult<T> (e.g., { found: boolean;
value?: T }) and update isFollowing to call getFromCache<boolean>(cacheKey) and
branch on cached.found — return successResponse(cached.value as boolean) when
found, otherwise continue to contract call and setCache; update callers like
isFollowing to use the new CacheResult shape and adjust setCache usage
accordingly (refer to isFollowing, getFromCache, setCache, and
CACHE_KEYS.IS_FOLLOWING).
- Around line 1-65: Remove the unused imports flagged by ESLint in
follow.service.ts to clean up the top-of-file import lists; specifically drop
unused symbols from the constants group (e.g., NETWORKS, DEFAULT_CONFIG,
CONTRACT_METHODS, CACHE_KEYS, VALIDATION, ERROR_MESSAGES,
DEFAULT_NOTIFICATION_PREFERENCES) and from the utils group (e.g.,
validateAddress, validateProductId, validateNotificationFormat,
validateUserPreferences, validateAlertConditions, generateFollowId,
generateAlertId, generateNotificationId, getErrorMessage, isCacheExpired), but
retain evaluateCondition and retryWithBackoff as noted; ensure the types and
notification/alert type imports that are actually referenced by the file (check
FollowServiceConfig, FollowResponse, FollowTransactionResult, ProductFollow,
FollowStatus, Follower, FollowedProduct, FollowErrorCode, FollowEventType,
FollowEventData, FollowEventListener, EventSubscription, PaginationParams,
PaginatedResponse, RateLimitConfig, CacheConfig, FollowNotification,
NotificationType, NotificationPreferences, NotificationTypePreference,
NotificationChannel, SendNotificationRequest, NotificationHistoryQuery,
FollowAlert, AlertType, AlertCondition, CreateAlertRequest, UpdateAlertRequest,
AlertTriggerResult, AlertQuery) remain, and run ESLint to verify no remaining
unused imports.
In `@src/shared/services/product_follow/README.md`:
- Around line 7-19: Update the fenced code block in
src/shared/services/product_follow/README.md to include a language specifier
(e.g., "text" or "plaintext") after the opening triple backticks so the
directory tree renders and lints correctly; locate the triple-backtick block
that contains the directory listing (the lines showing follow.service.ts,
index.ts, types/, utils/, constants/) and change the opening ``` to ```text (or
```plaintext).
In `@src/shared/services/product_follow/types/alert.types.ts`:
- Line 1: Remove the unused import u32 from the import statement that currently
brings in both u32 and u64 from '@stellar/stellar-sdk'; keep only u64 since
timestamp fields use u64 (remove the u32 symbol from the import to eliminate the
unused import warning).
In `@src/shared/services/product_follow/types/follow.types.ts`:
- Line 118: Change the default generic type for FollowResponse from any to
unknown to improve type safety: update the interface declaration
FollowResponse<T = any> to use T = unknown and adjust any downstream usages that
rely on implicit any (e.g., places constructing FollowResponse without a type
arg) to either assert the correct type parameter or handle T as unknown; ensure
related utility types or function signatures that expect FollowResponse use
explicit type parameters or proper narrowing where necessary.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: a3ced788-f68c-49df-9154-9753a9ae66c0
📒 Files selected for processing (8)
src/shared/services/product_follow/README.mdsrc/shared/services/product_follow/constants/follow.constants.tssrc/shared/services/product_follow/follow.service.tssrc/shared/services/product_follow/index.tssrc/shared/services/product_follow/types/alert.types.tssrc/shared/services/product_follow/types/follow.types.tssrc/shared/services/product_follow/types/notification.types.tssrc/shared/services/product_follow/utils/follow.utils.ts
- Fix mainnet RPC URL: use soroban-rpc.stellar.org instead of horizon - Fix isFollowing false negative: check success before data - Remove unsafe BigInt casts: use Date.now() directly - Fix updateAlert: return actual updated alert instead of empty object - Fix triggerAlert: evaluate conditions using evaluateCondition utility - Fix generateFollowId: add random suffix to prevent collisions - Fix factory functions: use NETWORKS constant instead of duplicating config
…wProduct, fix mainnet RPC URL
Closes #286
Summary
Implements a comprehensive TypeScript service layer for the Product Follow Contract that manages product following, notifications, alerts, and user preferences within the StarShop marketplace.
Structure
Implemented Features
1. Follow Management
followProduct(productId, user)- Follow a productunfollowProduct(productId, user)- Unfollow a productgetFollowers(productId)- Get product followers (paginated)getFollowing(user)- Get user's followed products (paginated)isFollowing(productId, user)- Check follow status2. Notification Management
setNotificationPreferences(user, preferences)- Set notification preferencesgetNotificationPreferences(user)- Get notification preferencessendNotification(productId, type, data)- Send notification to followersgetNotificationHistory(query)- Get notification history (paginated)3. Alert Management
createAlert(user, request)- Create price/stock/custom alertsupdateAlert(user, request)- Update alert conditionsdeleteAlert(user, alertId)- Delete an alertgetAlerts(query)- Get user alerts (paginated)triggerAlert(alertId, values)- Check alert conditions against current values4. Rate Limiting & Validation
Additional Features
/attempt 286
Summary by CodeRabbit
Release Notes