SPEC-001: Add service contract interfaces for cache, search, queue, notification, and storage#599
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…ueue, notification, and storage SPEC-001: Add formal service contract interfaces aligned with CoreServiceName enum in core-services.zod.ts. Each contract follows the existing Protocol First pattern with TypeScript interfaces, JSDoc documentation, and co-located tests. Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR expands @objectstack/spec’s kernel service surface area by adding missing service contract interfaces for several infrastructure services, so implementations can conform to consistent, type-safe method signatures.
Changes:
- Added new contracts (and supporting types) for cache, search, queue, notification, and file storage services under
packages/spec/src/contracts/. - Added vitest contract tests for each new interface to validate minimal vs. full implementations compile and behave as expected in test doubles.
- Exported the new contracts from the
contractsbarrel index.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/spec/src/contracts/cache-service.ts | Introduces ICacheService + CacheStats contract types. |
| packages/spec/src/contracts/cache-service.test.ts | Adds contract-shape tests for cache service implementations. |
| packages/spec/src/contracts/search-service.ts | Introduces ISearchService + search result/options types. |
| packages/spec/src/contracts/search-service.test.ts | Adds contract-shape tests for search indexing/querying and options. |
| packages/spec/src/contracts/queue-service.ts | Introduces IQueueService + queue message/publish types. |
| packages/spec/src/contracts/queue-service.test.ts | Adds contract-shape tests for publish/subscribe/unsubscribe and optional ops. |
| packages/spec/src/contracts/notification-service.ts | Introduces INotificationService + message/result/channel types. |
| packages/spec/src/contracts/notification-service.test.ts | Adds contract-shape tests for single/batch sends and channel listing. |
| packages/spec/src/contracts/storage-service.ts | Introduces IStorageService + upload options and file info types. |
| packages/spec/src/contracts/storage-service.test.ts | Adds contract-shape tests for upload/download/delete/info/list/signed-url. |
| packages/spec/src/contracts/index.ts | Exports the new contract modules from the contracts barrel. |
| * @param data - File content as Buffer or readable stream | ||
| * @param options - Upload options (content type, metadata, ACL) | ||
| */ | ||
| upload(key: string, data: Buffer | ReadableStream, options?: StorageUploadOptions): Promise<void>; |
There was a problem hiding this comment.
upload() uses Buffer | ReadableStream for the payload. Buffer is Node-specific and ReadableStream is the Web Streams type (different from Node’s stream.Readable), which makes it awkward to implement common Node storage clients without conversions and can force consumers to depend on Node globals. Consider switching to a runtime-agnostic byte type (e.g., Uint8Array/ArrayBuffer/Blob and ReadableStream<Uint8Array>), or explicitly widening the union to also accept Node streams (e.g., import('node:stream').Readable).
| * @param data - File content as Buffer or readable stream | ||
| * @param options - Upload options (content type, metadata, ACL) | ||
| */ | ||
| upload(key: string, data: Buffer | ReadableStream, options?: StorageUploadOptions): Promise<void>; | ||
|
|
||
| /** | ||
| * Download a file from storage | ||
| * @param key - Storage key/path | ||
| * @returns File content as Buffer | ||
| */ | ||
| download(key: string): Promise<Buffer>; |
There was a problem hiding this comment.
download() returns a Buffer, which is Node-only and leaks a runtime dependency into a public contract. If these contracts are meant to be broadly usable (Node/edge/browser), prefer returning a platform-neutral byte container (e.g., Uint8Array/ArrayBuffer) and optionally provide a streaming download method for large objects.
| * @param data - File content as Buffer or readable stream | |
| * @param options - Upload options (content type, metadata, ACL) | |
| */ | |
| upload(key: string, data: Buffer | ReadableStream, options?: StorageUploadOptions): Promise<void>; | |
| /** | |
| * Download a file from storage | |
| * @param key - Storage key/path | |
| * @returns File content as Buffer | |
| */ | |
| download(key: string): Promise<Buffer>; | |
| * @param data - File content as a byte array or readable stream of byte chunks | |
| * @param options - Upload options (content type, metadata, ACL) | |
| */ | |
| upload(key: string, data: Uint8Array | ArrayBuffer | ReadableStream<Uint8Array>, options?: StorageUploadOptions): Promise<void>; | |
| /** | |
| * Download a file from storage | |
| * @param key - Storage key/path | |
| * @returns File content as a byte array | |
| */ | |
| download(key: string): Promise<Uint8Array>; |
CoreServiceNamedefines 17 service identifiers but the spec only has contract interfaces for a handful (Logger, IDataEngine, IHttpServer, etc.). Infrastructure services like cache, search, queue, notification, and file-storage had no formal method contracts — implementations had nothing to conform to.New contracts in
src/contracts/ICacheService—get<T>,set<T>,delete,has,clear,statswithCacheStatstypeISearchService—index,remove,search(required);bulkIndex,deleteIndex(optional). Supporting types:SearchHit,SearchResult,SearchOptionsIQueueService—publish,subscribe,unsubscribe(required);getQueueSize,purge(optional). Supporting types:QueueMessage<T>,QueueHandler<T>,QueuePublishOptionsINotificationService—send(required);sendBatch,getChannels(optional). Supporting types:NotificationMessage,NotificationResult,NotificationChannelIStorageService—upload,download,delete,exists,getInfo(required);list,getSignedUrl(optional). Supporting types:StorageFileInfo,StorageUploadOptionsAll contracts follow the established pattern: pure TypeScript interfaces, JSDoc, co-located vitest tests, no Zod/no business logic. Exported from the contracts barrel.
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.