Skip to content

Commit c8c7dd1

Browse files
committed
fix(sdk): set correct unique value in init event for mids with set relation
1 parent 7e928ab commit c8c7dd1

5 files changed

Lines changed: 807 additions & 28 deletions

File tree

sdk/packages/model-instance-client/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"@ceramic-sdk/events": "workspace:^",
3535
"@ceramic-sdk/identifiers": "workspace:^",
3636
"@ceramic-sdk/model-instance-protocol": "workspace:^",
37+
"@ceramic-sdk/model-protocol": "workspace:^",
3738
"@ceramic-sdk/stream-client": "workspace:^",
3839
"@didtools/codecs": "^3.0.0",
3940
"fast-json-patch": "^3.1.1",

sdk/packages/model-instance-client/src/client.ts

Lines changed: 67 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
DocumentEvent,
1010
getStreamID,
1111
} from '@ceramic-sdk/model-instance-protocol'
12+
import type { ModelDefinition } from '@ceramic-sdk/model-protocol'
1213
import { StreamClient, type StreamState } from '@ceramic-sdk/stream-client'
1314
import type { DIDString } from '@didtools/codecs'
1415
import type { DID } from 'dids'
@@ -38,7 +39,12 @@ export type CreateSingletonParams = {
3839
* Parameters for creating an instance of a model.
3940
*/
4041
export type CreateInstanceParams<T extends UnknownContent = UnknownContent> =
41-
Omit<CreateInitEventParams<T>, 'controller'> & {
42+
Omit<CreateInitEventParams<T>, 'controller' | 'content'> & {
43+
/** The model definition containing account relation info */
44+
modelDefinition?: ModelDefinition
45+
/** The document content */
46+
content?: T
47+
/** The controller DID */
4248
controller?: DID
4349
}
4450

@@ -103,18 +109,70 @@ export class ModelInstanceClient extends StreamClient {
103109
}
104110

105111
/**
106-
* Creates an instance of a model. The model must have an account relation of list or set.
112+
* Creates an instance based on the model definition's account relation type.
113+
* - LIST: Creates a new instance with random unique value
114+
* - SET: Creates a deterministic instance based on specified field values
115+
* - SINGLE: Creates a singleton instance
116+
*
117+
* @param params - Parameters for creating the instance
118+
* @returns The commit ID of the created instance
107119
*/
108120
async createInstance<T extends UnknownContent = UnknownContent>(
109121
params: CreateInstanceParams<T>,
110122
): Promise<CommitID> {
111-
const { controller, ...rest } = params
112-
const event = await createInitEvent({
113-
...rest,
114-
controller: this.getDID(controller),
115-
})
116-
const cid = await this.ceramic.postEventType(SignedEvent, event)
117-
return CommitID.fromStream(getStreamID(cid))
123+
const { model, modelDefinition, content, controller, shouldIndex, modelVersion } = params
124+
125+
// Default to 'list' if no modelDefinition provided (backward compatibility)
126+
const relationType = modelDefinition?.accountRelation?.type || 'list'
127+
128+
switch (relationType) {
129+
case 'list': {
130+
const event = await createInitEvent({
131+
model,
132+
content: content ?? null,
133+
controller: this.getDID(controller),
134+
shouldIndex,
135+
modelVersion,
136+
})
137+
const cid = await this.ceramic.postEventType(SignedEvent, event)
138+
return CommitID.fromStream(getStreamID(cid))
139+
}
140+
141+
case 'single': {
142+
return this.createSingleton({
143+
model,
144+
controller: this.getDID(controller),
145+
})
146+
}
147+
148+
case 'set': {
149+
if (!modelDefinition || !modelDefinition.accountRelation) {
150+
throw new Error('Model definition is required for SET relations')
151+
}
152+
153+
// We know it's a SET relation, so fields must exist
154+
const fields = (modelDefinition.accountRelation as { type: 'set'; fields: string[] }).fields
155+
if (!fields || fields.length === 0) {
156+
throw new Error('SET relation must specify fields')
157+
}
158+
159+
// Extract "unique" components from content
160+
const unique = fields.map((field: string) => {
161+
const value = content && typeof content === 'object' ? (content as any)[field] : undefined
162+
return value == null ? '' : String(value)
163+
})
164+
165+
const uniqueValue = new TextEncoder().encode(unique.join('|'))
166+
return this.createSingleton({
167+
model,
168+
controller: this.getDID(controller),
169+
uniqueValue,
170+
})
171+
}
172+
173+
default:
174+
throw new Error(`Unknown account relation type: ${relationType}`)
175+
}
118176
}
119177

120178
/**

0 commit comments

Comments
 (0)