Skip to content

Commit 2e5c7a7

Browse files
smrz2001claude
andauthored
fix(sdk): set correct unique value for mids with set relation (#730)
* fix(sdk): set correct unique value in init event for mids with set relation * fix(sdk): resolve linting issues in model-instance-client - Replace 'any' types with proper TypeScript types - Use Record<string, unknown> for generic object typing - Add proper type imports for test mocks (CeramicClient, DID, StreamState) - Change encodeMultibase parameter type from 'any' to 'unknown' - Improve code formatting for better readability 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 7e928ab commit 2e5c7a7

5 files changed

Lines changed: 888 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: 79 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,82 @@ 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 {
124+
model,
125+
modelDefinition,
126+
content,
127+
controller,
128+
shouldIndex,
129+
modelVersion,
130+
} = params
131+
132+
// Default to 'list' if no modelDefinition provided (backward compatibility)
133+
const relationType = modelDefinition?.accountRelation?.type || 'list'
134+
135+
switch (relationType) {
136+
case 'list': {
137+
const event = await createInitEvent({
138+
model,
139+
content: content ?? null,
140+
controller: this.getDID(controller),
141+
shouldIndex,
142+
modelVersion,
143+
})
144+
const cid = await this.ceramic.postEventType(SignedEvent, event)
145+
return CommitID.fromStream(getStreamID(cid))
146+
}
147+
148+
case 'single': {
149+
return this.createSingleton({
150+
model,
151+
controller: this.getDID(controller),
152+
})
153+
}
154+
155+
case 'set': {
156+
if (!modelDefinition || !modelDefinition.accountRelation) {
157+
throw new Error('Model definition is required for SET relations')
158+
}
159+
160+
// We know it's a SET relation, so fields must exist
161+
const fields = (
162+
modelDefinition.accountRelation as { type: 'set'; fields: string[] }
163+
).fields
164+
if (!fields || fields.length === 0) {
165+
throw new Error('SET relation must specify fields')
166+
}
167+
168+
// Extract "unique" components from content
169+
const unique = fields.map((field: string) => {
170+
const value =
171+
content && typeof content === 'object'
172+
? (content as Record<string, unknown>)[field]
173+
: undefined
174+
return value == null ? '' : String(value)
175+
})
176+
177+
const uniqueValue = new TextEncoder().encode(unique.join('|'))
178+
return this.createSingleton({
179+
model,
180+
controller: this.getDID(controller),
181+
uniqueValue,
182+
})
183+
}
184+
185+
default:
186+
throw new Error(`Unknown account relation type: ${relationType}`)
187+
}
118188
}
119189

120190
/**

0 commit comments

Comments
 (0)