Skip to content

Commit 8873d9a

Browse files
refactor: rename collection → noun in CDC API surface
Aligns with .do ecosystem naming convention (Noun() from digital-objects). Entity types are singular: contact.created, deal.updated, invoice.deleted. - emitChange(op, noun, docId, doc, prev) — was (type, collection, ...) - CDCCollection constructor takes noun (singular entity type name) - cdc-processor uses noun variable (SQL column stays 'collection') Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent b6a471c commit 8873d9a

4 files changed

Lines changed: 38 additions & 29 deletions

File tree

core/src/cdc-delta.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ function parseRecordJson(str: string, fieldName: string): Record<string, unknown
6767

6868
/**
6969
* Extracts operation type from the CDC event name.
70-
* Expected format: `{collection}.{op}` (e.g. 'contacts.created', 'users.deleted')
70+
* Expected format: `{noun}.{op}` (e.g. 'contact.created', 'user.deleted')
7171
*/
7272
export function extractOp(eventName: string): CDCOp {
7373
const dot = eventName.lastIndexOf('.')

core/src/cdc-processor.ts

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -320,18 +320,19 @@ export class CDCProcessorDO extends DurableObject<Env> {
320320
/**
321321
* Process a single CDC event.
322322
* In the new Event format: data contains { type, id, ...fields }, meta may contain prev/bookmark.
323+
* The `type` field in data is the singular noun (e.g. 'contact', 'deal').
323324
*/
324325
private async processEvent(event: CDCEvent): Promise<void> {
325-
const { type: _entityType, id: docId, ...docFields } = event.data
326-
const collection = event.data.type
326+
const { type: _noun, id: docId, ...docFields } = event.data
327+
const noun = event.data.type
327328
const ts = event.ts
328329
const meta = event.meta as Record<string, unknown>
329330
const prev = meta.prev as Record<string, unknown> | undefined
330331
const bookmark = meta.bookmark as string | undefined
331332
const op = this.extractOp(event.event)
332333

333-
// Get existing document state from SQLite
334-
const existing = this.getDocumentState(collection, docId)
334+
// Get existing document state from SQLite (column named 'collection' for backward compat)
335+
const existing = this.getDocumentState(noun, docId)
335336

336337
let newData: Record<string, unknown>
337338
let prevData: Record<string, unknown> | null = null
@@ -364,13 +365,13 @@ export class CDCProcessorDO extends DurableObject<Env> {
364365
bookmark: bookmark ?? undefined,
365366
deleted,
366367
}
367-
this.upsertDocumentState(collection, docState)
368+
this.upsertDocumentState(noun, docState)
368369

369-
// Add to pending_deltas in SQLite
370+
// Add to pending_deltas in SQLite (column named 'collection' for backward compat)
370371
this.ctx.storage.sql.exec(
371372
`INSERT INTO pending_deltas (collection, doc_id, op, data, prev, ts, bookmark)
372373
VALUES (?, ?, ?, ?, ?, ?, ?)`,
373-
collection,
374+
noun,
374375
docId,
375376
op,
376377
JSON.stringify(newData),
@@ -379,13 +380,13 @@ export class CDCProcessorDO extends DurableObject<Env> {
379380
bookmark ?? null,
380381
)
381382

382-
// Ensure manifest exists for this collection
383-
this.ensureManifest(collection)
383+
// Ensure manifest exists for this noun
384+
this.ensureManifest(noun)
384385
}
385386

386387
/**
387388
* Extract operation type from the event name.
388-
* Format: `{collection}.{op}` (e.g. 'contacts.created')
389+
* Format: `{noun}.{op}` (e.g. 'contact.created')
389390
*/
390391
private extractOp(eventName: string): 'created' | 'updated' | 'deleted' {
391392
const dot = eventName.lastIndexOf('.')

core/src/cdc.ts

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,23 +27,25 @@ export interface Collection<T extends Record<string, unknown>> {
2727
* - The previous document state (if trackPrevious is enabled)
2828
* - SQLite bookmark for PITR
2929
*
30+
* @param noun - Singular entity type name (e.g. 'contact', 'deal', 'invoice')
31+
*
3032
* @example
3133
* ```typescript
32-
* const users = new CDCCollection(
33-
* this.collection<User>('users'),
34+
* const contacts = new CDCCollection(
35+
* this.collection<Contact>('contacts'),
3436
* this.events,
35-
* 'users'
37+
* 'contact' // singular noun — events become contact.created, contact.updated, etc.
3638
* )
3739
*
38-
* // This emits a CDC event
39-
* users.put('user-123', { name: 'Alice', active: true })
40+
* // This emits a CDC event: { type: 'cdc', event: 'contact.created', data: { type: 'contact', id, ... } }
41+
* contacts.put('contact-123', { name: 'Alice', active: true })
4042
* ```
4143
*/
4244
export class CDCCollection<T extends Record<string, unknown>> {
4345
constructor(
4446
private collection: Collection<T>,
4547
private emitter: EventEmitter,
46-
private name: string
48+
private noun: string
4749
) {}
4850

4951
/**
@@ -61,9 +63,9 @@ export class CDCCollection<T extends Record<string, unknown>> {
6163
this.collection.put(id, doc)
6264

6365
if (prev) {
64-
this.emitter.emitChange('updated', this.name, id, doc, prev)
66+
this.emitter.emitChange('updated', this.noun, id, doc, prev)
6567
} else {
66-
this.emitter.emitChange('created', this.name, id, doc)
68+
this.emitter.emitChange('created', this.noun, id, doc)
6769
}
6870
}
6971

@@ -75,7 +77,7 @@ export class CDCCollection<T extends Record<string, unknown>> {
7577
const deleted = this.collection.delete(id)
7678

7779
if (deleted && prev) {
78-
this.emitter.emitChange('deleted', this.name, id, undefined, prev)
80+
this.emitter.emitChange('deleted', this.noun, id, undefined, prev)
7981
}
8082

8183
return deleted
@@ -125,7 +127,7 @@ export class CDCCollection<T extends Record<string, unknown>> {
125127

126128
// Emit delete for each document
127129
for (const id of keys) {
128-
this.emitter.emitChange('deleted', this.name, id)
130+
this.emitter.emitChange('deleted', this.noun, id)
129131
}
130132

131133
return count

core/src/emitter.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -198,21 +198,27 @@ export class EventEmitter {
198198
}
199199

200200
/**
201-
* Emit a CDC (Change Data Capture) event for a collection change.
201+
* Emit a CDC (Change Data Capture) event for an entity mutation.
202202
* Automatically captures the SQLite bookmark for PITR (point-in-time recovery).
203203
* Only emits if the `cdc` option is enabled.
204+
*
205+
* @param op - The operation: 'created', 'updated', or 'deleted'
206+
* @param noun - Singular entity type name (e.g. 'contact', 'deal', 'invoice')
207+
* @param docId - Document ID
208+
* @param doc - Current document state (omit for deletes)
209+
* @param prev - Previous document state (when trackPrevious is enabled)
204210
*/
205-
emitChange(type: 'created' | 'updated' | 'deleted', collection: string, docId: string, doc?: Record<string, unknown>, prev?: Record<string, unknown>): void {
211+
emitChange(op: 'created' | 'updated' | 'deleted', noun: string, docId: string, doc?: Record<string, unknown>, prev?: Record<string, unknown>): void {
206212
if (!this.options.cdc) return
207213

208-
this.getBookmarkAndEmit(type, collection, docId, doc, prev).catch((error) => {
209-
this.log.error('Failed to emit CDC event', { error: error instanceof Error ? error.message : String(error), collection, docId })
214+
this.getBookmarkAndEmit(op, noun, docId, doc, prev).catch((error) => {
215+
this.log.error('Failed to emit CDC event', { error: error instanceof Error ? error.message : String(error), noun, docId })
210216
})
211217
}
212218

213219
private async getBookmarkAndEmit(
214-
type: 'created' | 'updated' | 'deleted',
215-
collection: string,
220+
op: 'created' | 'updated' | 'deleted',
221+
noun: string,
216222
docId: string,
217223
doc?: Record<string, unknown>,
218224
prev?: Record<string, unknown>,
@@ -226,8 +232,8 @@ export class EventEmitter {
226232

227233
this.emit({
228234
type: 'cdc',
229-
event: `${collection}.${type}`,
230-
data: { type: collection, id: docId, ...(doc ?? {}) },
235+
event: `${noun}.${op}`,
236+
data: { type: noun, id: docId, ...(doc ?? {}) },
231237
meta: {
232238
...(this.options.trackPrevious && prev ? { prev } : {}),
233239
...(bookmark ? { bookmark } : {}),

0 commit comments

Comments
 (0)