Skip to content

SPEC-001: Add service contract interfaces for cache, search, queue, notification, and storage#599

Merged
hotlong merged 2 commits into
mainfrom
copilot/add-service-contract-interfaces
Feb 11, 2026
Merged

SPEC-001: Add service contract interfaces for cache, search, queue, notification, and storage#599
hotlong merged 2 commits into
mainfrom
copilot/add-service-contract-interfaces

Conversation

Copilot AI commented Feb 11, 2026

Copy link
Copy Markdown
Contributor

CoreServiceName defines 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/

  • ICacheServiceget<T>, set<T>, delete, has, clear, stats with CacheStats type
  • ISearchServiceindex, remove, search (required); bulkIndex, deleteIndex (optional). Supporting types: SearchHit, SearchResult, SearchOptions
  • IQueueServicepublish, subscribe, unsubscribe (required); getQueueSize, purge (optional). Supporting types: QueueMessage<T>, QueueHandler<T>, QueuePublishOptions
  • INotificationServicesend (required); sendBatch, getChannels (optional). Supporting types: NotificationMessage, NotificationResult, NotificationChannel
  • IStorageServiceupload, download, delete, exists, getInfo (required); list, getSignedUrl (optional). Supporting types: StorageFileInfo, StorageUploadOptions

All contracts follow the established pattern: pure TypeScript interfaces, JSDoc, co-located vitest tests, no Zod/no business logic. Exported from the contracts barrel.

import type { ICacheService, ISearchService } from '@objectstack/spec/contracts';

// Implement against the contract
const cache: ICacheService = {
  get: async (key) => redis.get(key),
  set: async (key, value, ttl) => redis.set(key, value, { EX: ttl }),
  delete: async (key) => Boolean(await redis.del(key)),
  has: async (key) => Boolean(await redis.exists(key)),
  clear: async () => { await redis.flushdb(); },
  stats: async () => ({ hits: 0, misses: 0, keyCount: await redis.dbsize() }),
};

kernel.register('cache', cache); // type-safe service registration

💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

@vercel

vercel Bot commented Feb 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
objectstack-play Ready Ready Preview, Comment Feb 11, 2026 2:19am
spec Error Error Feb 11, 2026 2:19am

Request Review

…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>
Copilot AI changed the title [WIP] Add service contract interfaces for core services SPEC-001: Add service contract interfaces for cache, search, queue, notification, and storage Feb 11, 2026
Copilot AI requested a review from hotlong February 11, 2026 02:09
@hotlong
hotlong marked this pull request as ready for review February 11, 2026 02:30
Copilot AI review requested due to automatic review settings February 11, 2026 02:30
@hotlong
hotlong merged commit e972e4c into main Feb 11, 2026
6 of 7 checks passed

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 contracts barrel 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.

Comment on lines +48 to +51
* @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>;

Copilot AI Feb 11, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Copilot uses AI. Check for mistakes.
Comment on lines +48 to +58
* @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>;

Copilot AI Feb 11, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
* @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>;

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants