Item
The Item class: create, find and list items, read their identity fields, delete them, prepare them for publishing, export published ones as archive bags and check who owns them.
An Item is one record of a model, such as one Post. Its model properties are accessors on the instance (post.title), each backed by an ItemProperty. Items live in the local SQLite database until you publish them with @seedprotocol/publish.
import { Item } from '@seedprotocol/sdk'Call client.init() before using Item. The examples on this page use the blog schema from Your first schema and the Post type below. The SDK's types don't know your model's properties, so you describe each model once and cast results to it; see Typing items in TypeScript.
import type { IItem } from '@seedprotocol/sdk'
export type Post = IItem<any> & {
title?: string
published?: boolean | string
wordCount?: number | string
author?: string | null
tags?: string[]
}Item.create
Creates a new item of a model, writes it to SQLite and returns it.
static create<T extends Record<string, unknown> = Record<string, unknown>>(
props: Partial<ItemData> & Partial<T> & { modelInstance?: Model },
options?: ItemCreateOptions,
): Promise<Item<any>>Parameters
| Name | Type | Default | Description |
|---|---|---|---|
props.modelName | string | none | The model to create. Required: without it (and without type) create throws Model name is required to create an item |
props.schemaName | string | none | The schema that defines the model, when several schemas define a model with the same name |
props.modelFileId | string | none | The model's ID (Model.id), as an alternative to schemaName |
props.type | string | none | Legacy. Used as the model name, converted with startCase, when modelName is missing |
props.<property> | the property's value | none | Initial values, keyed by model property name |
options.waitForReady | boolean | true | Wait until the item and its properties have loaded before resolving. create always waits when props has values that need a file save (see below) |
options.readyTimeout | number | 15000 | Milliseconds to wait for the item to load, and for each property that needs a file save to load |
options.validate | boolean | true | false skips the checks described below and writes the values as given. Use it only to import data that's already stored or attested |
options.hold | boolean | true | Take a cache hold on the item, released with unload(). false creates it without one, so it unloads after the grace period unless something holds it |
If props contains seedLocalId or seedUid, create loads that existing item instead of creating a new one. Item.find and Item.all use this path internally. To load an item, call Item.find.
Returns
A promise for the new Item. Its seedLocalId is set, seedUid is undefined and it has one version.
Example
import { Item } from '@seedprotocol/sdk'
import type { Post } from './types'
const post = (await Item.create({
modelName: 'Post',
title: 'Hello, Seed',
published: false,
tags: ['intro'],
})) as Post
console.log(post.seedLocalId, post.title)create writes the item, its first version and its other values as one write in the outbox, applied in one transaction, so a failure there leaves nothing. File, Image and Html values in props, and values of Text properties with ItemStorage, need a file save: once the item is written, create runs each property's normal save, one after another, so the files are written before create resolves. If the item doesn't load in time or one of those saves fails, create deletes what it wrote (the item, its versions and values, and the stored files) and then rejects, so a retry doesn't leave a second draft.
When one of those saves fails for a reason other than its property's rules (those are validation errors), such as a URL that can't be fetched, create rejects with ItemCreateError. For a failed save its message is Item.create: failed to save Post.attachment for item <seedLocalId>: followed by the save error's message. Its fields:
| Field | Type | Description |
|---|---|---|
code | 'save_failed' | 'property_not_loaded' | 'save_failed' when the save failed or was refused; 'property_not_loaded' when the property didn't load within readyTimeout, so nothing was saved |
modelName | string | The model, such as 'Post' |
property | string | The key in props whose value wasn't saved |
seedLocalId | string | The ID of the draft that was removed |
cause | unknown | The save's own error: usually an OutboxOpFailedError, whose errorName is the original class ('TypeError' for a URL that can't be fetched), or an OwnershipError. See ItemProperty: save. Unset for 'property_not_loaded' |
Validation
Before writing anything, create checks its input against the model:
- A
modelNamethat no loaded schema defines (inschemaName, if you pass one) throwsUnknownModelError:Model "Nope" not found. - Keys that aren't properties of the model, and values that fail their property's type or validation rules, throw
ItemCreateValidationError. Itsproblemsarray lists every problem, and its message joins them:Cannot create Author: twitter: Author has no property "twitter". - Values that need a file save are checked by that save. A value that breaks its property's rules rejects with
ItemCreateValidationErrortoo, with one problem per rule and the save'sItemPropertySaveValidationErrorascause. Either way nothing is left written. - A
Listvalue can be an array or the JSON array string it's stored as. requiredisn't checked: a model with a requiredtitlestill creates an item without one.
Relation and List of Relation values are checked when the create's write is applied, as an assignment's are (see Relation and List values). A seedLocalId that names no item rejects the create with OutboxOpFailedError (message Relation target not found: <ids>, errorName: 'RelationTargetError', and a RelationTargetError with the missing IDs in targets as cause), not ItemCreateValidationError, and nothing is written. A target whose own create is still queued is waited for. seedUids and the new item's own seedLocalId pass.
Model#create(values) calls Item.create without options, so it always validates. React's useCreateItem passes its third argument through, so createItem('Post', values, { validate: false }) turns the checks off.
import { Item, ItemCreateValidationError, UnknownModelError } from '@seedprotocol/sdk'
const input = { modelName: 'Post', title: 'x'.repeat(300) }
try {
await Item.create(input)
} catch (error) {
if (error instanceof ItemCreateValidationError) {
for (const problem of error.problems) {
console.warn(`${problem.property}: ${problem.message}`)
}
} else if (error instanceof UnknownModelError) {
console.warn(`No model named ${error.modelName}`)
} else {
throw error
}
}Each problem is { property, message, code }. code names the check: unknown_property, type (a value of the wrong type, such as text given to a Number), array_not_allowed, pattern, min_length, max_length_exceeded, enum_violation or format (a Date that isn't one). A value that misses an enum and breaks the pattern too gets one problem for each.
Item.find
Loads one item by ID. It returns the cached instance if the item is already loaded, and otherwise reads it from SQLite.
static find(props: {
modelName?: string
seedLocalId?: string
seedUid?: string
waitForReady?: boolean
readyTimeout?: number
hold?: boolean
}): Promise<IItem<any> | undefined>Parameters
| Name | Type | Default | Description |
|---|---|---|---|
seedLocalId | string | none | The item's local ID. Checked first when both IDs are given |
seedUid | string | none | The item's EAS attestation UID (0x…) |
modelName | string | none | Narrows the database lookup to one model |
waitForReady | boolean | true | Wait until the item has loaded |
readyTimeout | number | 15000 | Milliseconds to wait for the item to load |
hold | boolean | false | Take a cache hold, released with unload(). Without one, the item unloads after the grace period unless something else holds it |
Returns
The item, or undefined when neither ID is given, nothing matches, or loading fails. With hold: true it keeps no hold when it returns undefined.
A find called while client.init runs waits for the database to open. One called before client.init, after client.stop() or after a failed init rejects once readyTimeout passes, with a ClientNotInitializedError (code: 'CLIENT_NOT_INITIALIZED'), so that a missing client doesn't read as a missing item.
Example
import { Item } from '@seedprotocol/sdk'
import type { Post } from './types'
const post = (await Item.find({ modelName: 'Post', seedLocalId })) as Post | undefined
if (!post) throw new Error('Post not found')A relation value can hold either kind of ID. Pass it through resolveSeedIdsFromRefString(ref) to get { seedLocalId } or { seedUid }, then spread that into find. See Following relations.
Item.all
Lists the items of a model. The arguments are positional.
static all(
modelName?: string,
deleted?: boolean,
options?: {
hold?: boolean
waitForReady?: boolean
readyTimeout?: number
includeEas?: boolean
addressFilter?: 'owned' | 'watched' | 'all'
modelFileId?: string
schemaName?: string
signal?: AbortSignal
},
): Promise<Item<any>[]>Parameters
| Name | Type | Default | Description |
|---|---|---|---|
modelName | string | none | The model to list. Without it, items of every model are returned |
deleted | boolean | false | true lists only deleted items, false only items that aren't deleted |
options.includeEas | boolean | false | false returns local drafts only: items with no EAS attestation UID. Pass true to include published and synced items |
options.addressFilter | 'owned' | 'watched' | 'all' | none | 'owned' keeps the items isItemOwned counts as owned: drafts that have never had a publisher, and items whose publisher (or, when that's blank, the attester) is an owned address. 'watched' keeps items whose publisher is a watched address. 'all' or leaving it out applies no filter |
options.schemaName | string | none | Only items of the model defined by this schema |
options.modelFileId | string | none | Only items of the model with this ID |
options.waitForReady | boolean | false | false returns the items while they load: their status is 'loading' and property values may not be there yet. true waits for all of them in parallel, each within readyTimeout, and rejects if one fails to load in time |
options.readyTimeout | number | 15000 | Milliseconds to wait for each item when waitForReady is true |
options.signal | AbortSignal | none | Once aborted, Item.all stops building items and rejects with the signal's reason |
options.hold | boolean | false | Take a cache hold on each item, released with each item's unload(). When the call rejects (aborted, or an item not loaded in time), it gives back every hold it took. Without holds, the items unload after the grace period unless something else holds them |
Returns
An array of items, newest first by createdAt. Only items with at least one version are included. Revoked items are left out unless deleted is true.
Example
import { Item } from '@seedprotocol/sdk'
import type { Post } from './types'
const drafts = (await Item.all('Post')) as Post[]
const everything = (await Item.all('Post', false, { includeEas: true })) as Post[]Pass waitForReady: true when you read property values right away; with the default, wait on each item's waitForReady() instead. Item.all has no filtering or sorting options beyond these. Filter the array in your code, or query SQLite directly as described in Querying.
Identity and metadata fields
Every item has these read-only fields, read from the item's state:
| Field | Type | Description |
|---|---|---|
seedLocalId | string | The local ID, created with the item. Use it as a React key and to refer to drafts |
seedUid | string | undefined | The EAS attestation UID of the item's Seed. undefined until the item is published or synced |
schemaUid | string | undefined | The EAS schema UID of the item's model, once known |
modelName | string | The model name, for example 'Post' |
latestVersionLocalId | string | Local ID of the newest version |
latestVersionUid | string | null | EAS UID of the newest version. null while that version is unpublished |
versionsCount | number | Number of versions |
createdAt | number | undefined | Creation time in milliseconds |
attestationCreatedAt | number | When the Seed was attested, for published items |
lastVersionPublishedAt | number | When the newest published version was attested |
publisher | string | undefined | The address that published, or claimed, the item. Assigning to it throws |
revokedAt | number | undefined | Unix seconds when the item was unpublished |
isRevoked | boolean | true when revokedAt is set |
status | 'loading' | 'ready' | 'error' | The load status: 'ready' once the item and its properties have loaded, 'error' when that failed |
waitForReady({ timeout }) resolves once status is 'ready'. It rejects with the load's error, or after timeout milliseconds (default 15000) with Entity loading timeout after <timeout>ms.
entityLoadError
function entityLoadError(instance: { getService(): object }): unknownReturns the error the instance's last load failed with while its status is 'error', and undefined otherwise. It takes any instance that has a status: an Item, an ItemProperty, a Schema, a Model or a ModelProperty. Both @seedprotocol/sdk and @seedprotocol/sdk/node export it.
import { entityLoadError } from '@seedprotocol/sdk'
if (post.status === 'error') console.error('Post failed to load', entityLoadError(post))versionLocalId belongs to item properties, not items: each ItemProperty records the version its value was written to.
Property accessors and properties
Each model property is an accessor on the item. Reading post.title returns the property's value. Assigning to it shows the new value and queues its write. The rules for both, including why most values read back as strings, are on the ItemProperty page.
post.title = 'Revised headline'
console.log(post.title)The item also exposes its ItemProperty instances:
| Member | Type | Description |
|---|---|---|
properties | IItemProperty[] | The model's properties, as a new array on each read |
internalProperties | Record<string, IItemProperty> | Built-in properties shared by every item |
allProperties | Record<string, IItemProperty> | Both of the above, keyed by name |
getEditedProperties() | Promise<PropertyData[]> | Rows for the properties whose current value hasn't been published yet, including values still queued in the outbox |
conflicts() | Promise<PropertyConflict[]> | The item's unresolved conflicts, the newest per property. Each is also on that property's conflict |
const titleProp = post.properties.find((p) => p.propertyName === 'title')
await titleProp?.save()Assigning to a name that isn't a model property sets a plain JavaScript field and saves nothing. Assigning to properties throws.
destroy
Deletes the item.
destroy(): Promise<void>destroy first checks that you own the item and rejects with Item is read-only: you do not own this item if you don't. It then drops the item from the cache and queues a delete after the item's pending edits, which marks the item's Seed row as deleted (_markedForDeletion = 1) instead of removing rows; the item stops once that's applied. It rejects if the delete fails. Deleted items drop out of Item.all and come back with Item.all(modelName, true).
import { Item } from '@seedprotocol/sdk'
await post.destroy()
const trash = await Item.all('Post', true)Relations that point to a deleted item keep its ID. Deleting a local copy doesn't change anything already attested on-chain: to revoke a published item, use unpublish.
unload
unload(): voidReleases one hold on the item without touching the database. Call it once for each hold you took: Item.create takes one unless you pass hold: false, Item.find, Item.all and Item.getById take one only with hold: true, and Item.createById and Item.retain take one each. On an item no one holds it does nothing. On an item someone else holds, such as a React hook, it releases their hold, so call it only for holds you took.
Item.create, Item.find, Item.all and Item.getById hand every caller the same instance while it's cached. An item no one holds stays cached for the grace period, entityCache.unloadGraceMs in SeedConfig (60 seconds by default), after its last hold is released or after its last lookup without a hold, whichever is later. A hold taken meanwhile keeps the same instance. Once the grace period ends, the item is removed from the cache and its actor stops, so subscribe callbacks stop too, and the next Item.find loads a new instance. destroy() and client.stop() stop it without waiting for the grace period, whatever holds remain.
With unloadGraceMs: 0, the last unload() stops the item at once, and an item that was never held (created with hold: false, or only looked up without a hold) stays cached until it's destroyed or the client stops.
To keep an item past its grace period, such as across a long-running Node task, hold it and release it when you're done:
import { Item } from '@seedprotocol/sdk'
import type { Post } from './types'
const post = (await Item.find({ modelName: 'Post', seedLocalId, hold: true })) as Post | undefined
if (!post) throw new Error('Post not found')
try {
post.title = 'Revised headline'
await post.properties.find((p) => p.propertyName === 'title')?.save()
} finally {
post.unload()
}Item.retain(item) takes one more hold on an instance you already have and returns true, or returns false and takes none when the instance has stopped.
A stopped instance still reads its last values. Assigning a property on it, its properties' setValue() and save(), and publish() and unpublish() throw EntityUnloadedError, with a message that names the item: Cannot set title on Post <seedLocalId>: this Item instance was unloaded. …. Look the item up again and hold it while you use it.
An item holds each of its properties, so when it stops, its properties stop too, unless you also hold one with ItemProperty.find({ hold: true }) and haven't released that hold (see destroy and unload). A property read through item.properties after the item stopped is stopped as well. In the browser, stopping also revokes the blob: URLs the SDK made for the item's File and Image values.
Publishing
Publishing (uploading files to Arweave and writing EAS attestations) is done by @seedprotocol/publish. Its PublishProvider or initPublish() registers the publish and revoke executors that item.publish() and item.unpublish() hand the item to. The SDK can't publish on its own. See Publishing your first item.
publish
publish(options?: ItemPublishOptions): Promise<void>Checks ownership, then hands the item to the publish executor that @seedprotocol/publish registers, which uploads its files and writes its attestations. Before reading the item's rows, publishing waits, with no time limit, for the writes pending in the outbox on the items the publish covers: the item, the drafts it relates to and the files its values point to. Every edit made to them before you called publish() is included; writes to other items don't hold it up. options are passed to that package unchanged (its CreatePublishOptions); the SDK's ItemPublishOptions type names publishMode ('patch', the default, or 'new_version') and dataItemSigner, an object with address and signMessage such as an ethers Wallet (a string doesn't type-check), confirmUpload and maxUploadBytes, the upload limits (publish overview), and accepts any other key. The promise resolves when the publish has finished, and @seedprotocol/publish holds the item until then, so an item you looked up without a hold doesn't unload mid-publish. If the publish fails after it started, the promise rejects with the error the publish threw, so its class and code are kept: match a ManagedAccountPublishError from @seedprotocol/publish with instanceof or its code. It also rejects when:
| Message | Cause |
|---|---|
Item is read-only: you do not own this item | You don't own the item (OwnershipError) |
Publishing is not configured. … | @seedprotocol/publish hasn't been initialized. The text is exported as PUBLISH_NOT_CONFIGURED_MESSAGE |
Item is already being published. | A publish of this item is running |
Cannot publish Post <seedLocalId>: this Item instance was unloaded. … | The instance has stopped (EntityUnloadedError, see unload) |
Item is not ready to publish (state: …). | The item is still loading or saving |
Item.publish(item, options) does the same as a static method. See Publishing and revoking.
unpublish
unpublish(): Promise<void>Revokes the item's attestations through the revoke function registered by @seedprotocol/publish, then sets revokedAt. It rejects when:
| Message | Cause |
|---|---|
Item is read-only: you do not own this item | You don't own the item |
Cannot unpublish Post <seedLocalId>: this Item instance was unloaded. … | The instance has stopped (EntityUnloadedError) |
Item is not published. Cannot unpublish. | The item has no seedUid |
Item has no schema UID. Cannot unpublish. | The item has no schemaUid |
Revocation is not configured. … | @seedprotocol/publish hasn't been initialized |
Revoked items drop out of Item.all results. See Publishing and revoking.
Lower-level publish methods
@seedprotocol/publish calls these. You only need them to build your own publishing flow:
| Method | Description |
|---|---|
getPublishUploads(options?) | The files and HTML to upload to Arweave for this item and the draft items it relates to, which are published with it |
getPublishPayload(uploadedTransactions, options?) | The attestation payload, given the uploaded transaction IDs. options.publishMode is 'patch' or 'new_version'. options.backReferences is 'defer' (default) or 'link', as on getPublishGraph |
persistSeedUid(publisher?, attestationCreatedAtMs?) | Writes the item's current seedUid (and optionally publisher and attestation time) to SQLite |
getSeedPublishState
To tell a draft from a published item, use the getSeedPublishState function. It reads SQLite and doesn't change anything.
function getSeedPublishState(
arg: { seedLocalId?: string; seedUid?: string } | IItem<any>,
): Promise<SeedPublishState>| Field | Type | Description |
|---|---|---|
status | 'draft' | 'onchain' | 'onchain' when the Seed, its newest published version or any of its property rows has an EAS UID. Revoked items stay 'onchain' |
seedAttestationUid | string | null | The Seed's attestation UID |
versionAttestationUid | string | null | The newest published version's UID |
explorerUid | string | null | The best UID to link to on EAS Scan |
lastAttestedAtMs | number | null | The latest attestation time found, in milliseconds |
revokedAt | number | null | Unix seconds when the Seed was revoked |
import { getSeedPublishState } from '@seedprotocol/sdk'
const { status, revokedAt } = await getSeedPublishState(post)
const label = status === 'draft' ? 'Draft' : revokedAt ? 'Unpublished' : 'Published'status: 'onchain' doesn't mean every local edit is published. getPublishPendingDiff(item) lists the properties changed since the last publish. See Querying.
Export a published item
exportArchive packages a published item as a BagIt bag: its Html, its files and its attested values, with SHA-256 and SHA-512 checksum manifests and Dublin Core and PREMIS metadata, so an archive can validate and ingest it without Seed code.
function exportArchive(
item: Item<any>,
options?: ExportArchiveOptions,
): Promise<ExportArchiveResult>The signature is simplified: the declared type of item accepts an Item instance, as Item.create returns, but not an IItem<any>, which Item.find returns and the Post type above extends. Cast those: exportArchive(post as Item<any>).
| Option | Type | Default | Description |
|---|---|---|---|
fetch | boolean | true | Download Images, Files and Html that aren't on this device. With false, only local files go in the bag; the others are listed in omitted |
sourceOrganization | string | none | The Source-Organization line of bag-info.txt, such as the publication's name. Left out when not given |
baggingDate | Date | now | The Bagging-Date line of bag-info.txt, and the time of the bag's own PREMIS events |
Like publish(), it first waits for the writes pending in the outbox on the item and the items its publish covers. It exports the item's current published state, and the bag's payload is:
| Path | Contents |
|---|---|
data/index.html | The item's Html, with each reference to a file in the bag (a gateway URL or a seed:property/ placeholder) replaced by the file's relative path, so it opens offline. A model with several Html properties gets data/<property>.html for each instead |
data/source/<property>.html | Each Html property exactly as published |
data/assets/<sha256>.<ext> | The item's Images and Files, and the images its Html references |
data/item.json | The Seed and Version UIDs, model, chain and attester, and each property's value, attestation UID and Arweave transaction |
Next to it are bagit.txt, bag-info.txt (Seed and Version UIDs, chain, attester), the manifests, metadata/dc.xml (Dublin Core, filled from the Html's h-entry markup when it has some) and metadata/premis.xml (the item's attested history as PREMIS events). Json storage seeds and the content of related items aren't exported; a Relation or List records the related items' UIDs in data/item.json.
It resolves to { bag, omitted }. bag.files is an array of { path: string; bytes: Uint8Array } with /-separated paths from the bag's base directory, held in memory. omitted lists, as { property, reason }, each file that couldn't be read or downloaded and each Html reference to an Arweave transaction that isn't in the bag (it keeps its gateway URL). These don't fail the export, and data/item.json lists them too.
It throws ArchiveExportError, with code:
code | Cause |
|---|---|
'not_published' | The item has no live published Version: it was never published, or it was unpublished. Message: Post <seedLocalId> has no live published version |
'unpublished_changes' | A property holds a saved value that isn't published yet. Publish first; the bag holds only what attestations back |
On Node, writeBagDirectory from @seedprotocol/archive/node (install @seedprotocol/archive) writes the bag to a directory, which must be empty or not exist yet:
import { Item, exportArchive } from '@seedprotocol/sdk'
import { writeBagDirectory } from '@seedprotocol/archive/node'
const { bag, omitted } = await exportArchive(post as Item<any>, { sourceOrganization: 'The Daily Example' })
await writeBagDirectory(bag, './post-bag')
for (const { property, reason } of omitted) console.warn(`${property}: ${reason}`)Ownership
You can only change items you own. Ownership comes from the owned addresses passed to client.init({ addresses }) or client.setAddresses():
- A draft that has never had a publisher is yours, whatever the owned addresses are. The next wallet to connect becomes its publisher (see claimUnpublishedDrafts).
- Any other item, including a draft that has a publisher, is yours when its publisher (or its attester, when the publisher is blank) is one of the owned addresses. With no owned addresses, only the first kind is yours.
A new draft gets its publisher when it's created: from the getter @seedprotocol/publish registers for the connected wallet, or your own setGetPublisherForNewSeeds getter. Without one, it gets the owned address if exactly one is set, and otherwise none.
These methods check ownership and reject with an OwnershipError (Item is read-only: you do not own this item): destroy(), publish(), unpublish(), ItemProperty#save() and ItemProperty#destroy(). assertItemOwned(item) throws the same error, for your own checks.
Assigning a value is checked when its write is applied. post.title = 'x' on an item you don't own doesn't throw: the write fails in the outbox, nothing is written, the property shows its current value again, and the property's actor records the error as _saveError. An await on that property's save() rejects with the OwnershipError.
isItemOwned
function isItemOwned(item: { seedLocalId?: string; seedUid?: string } | IItem<any>): Promise<boolean>Returns whether the current user owns the item, using the rules above. Use it to disable edit controls.
claimUnpublishedDrafts
client.claimUnpublishedDrafts(publisher: string): Promise<{
seeds: number
versions: number
metadata: number
pendingCreates: number
seedLocalIds: string[]
}>Makes publisher the publisher of every draft that has never had one, and of those drafts' unpublished versions and property values. Rows of an item that already has a publisher, and rows with an attestation, aren't touched. It also gives the publisher to items whose Item.create write is still queued. It returns how many seed, version and property rows it changed, how many queued creates it claimed (pendingCreates), and the seedLocalIds of all the claimed items, queued ones included. Items already loaded get the new publisher straight away.
setPublishWallet in @seedprotocol/publish calls it when a wallet connects. Call it yourself after client.setAddresses() when you manage addresses without that package. It's also exported as a standalone claimUnpublishedDrafts(publisher) function, which only writes the database: on Node, an item that's already loaded keeps the old publisher until it's loaded again.
import { client, isItemOwned } from '@seedprotocol/sdk'
await client.setAddresses({ owned: [address] })
await client.claimUnpublishedDrafts(address)
console.log(await isItemOwned(post)) // trueWith several owned addresses (an EOA and its smart account, say), new drafts get no publisher unless you register a getter with setGetPublisherForNewSeeds. See Addresses.
A refused assignment shows the stored value again, so in an editing UI check isItemOwned first and show a read-only view.
Ownership and drafts explains the model behind these rules.
subscribe and getService
subscribe(callback: (context: any) => void): Subscription
getService(): ActorRefsubscribe calls callback with the item's state on every change, including property values being added and loaded, and returns an XState Subscription. Call unsubscribe() on it when you're done. getService returns the underlying XState actor. In React, use useItem from @seedprotocol/react instead. See React items.
Cache helpers
The SDK keeps one instance per item, so Item.find returns the same object while the item is cached. These static methods work with that cache. Most apps don't need them.
| Method | Description |
|---|---|
Item.getById(id, options?) | The cached item for a seedLocalId or seedUid, or null. Doesn't read the database. Takes a hold, released with unload(), only with { hold: true }; without one the item stays cached for the grace period, as after a find |
Item.peekReady(id) | The cached item if it has finished loading, otherwise undefined. Takes no hold, so it's safe to call during a React render |
Item.createById(id, modelName?) | Like find without the wait options, except that it adds a hold |
Item.retain(item) | Adds a hold on a cached, running instance and returns true. Returns false when the instance has stopped |
Item.dropCachedInstancesForSeedIds(ids) | Removes items from the cache without deleting them |
Related
- Reading and writing data: queries, relations, lists and deletes in practice
- ItemProperty: values, saving and validation
- Querying: SQL and live queries over the local database
- Model:
Model#createand model lookup - React items:
useItem,useItems,useCreateItemanduseDeleteItem - Ownership and drafts
- Publishing and revoking
ModelProperty
Every property type and option you can declare on a model, how each type is stored, published and read back, and the ModelProperty class for working with property definitions in code.
ItemProperty
The ItemProperty class: one property value of an item, how assigning to it writes, how save() reports errors, clearing a value, conflicts after sync, and how file, image, relation and list values behave.