A list over an add-on's own service, typed from its declaration (MICA-313).
The declaration your server half registers, and the actions to use from it. Every name is one of the declaration's, and the store's types follow from the declaration rather than from a type argument you assert:
list answers addonOutput<Row[]>(), and Row — which needs an id: number — is
the store's row type. It is called with no input.create takes the draft, so add(draft) is typed as its input, and answers the
created row (addonOutput<Row>()).update is sent the whole row, so its input declares every field of Row.remove is sent { id }.const notes = createCrudStore(notesService, { list: 'list', create: 'add' });
await notes.add({ text }); // a typo in a field, or in an action name, fails to compile
At run time it is createCrudStore(declaration.id, events, { ...options, service: declaration.id }): the same store, through the same generic service route. The types are a
convenience and a promise about nothing — the server parses every payload against the
declaration it was handed, and the shell refuses any id but your own, whatever this side
believed.
For the paths a list alone cannot express — an incoming message, a local patch.
A store over a list of rows the server owns.
Four stores had written the same load/add/update/delete by hand, and every difference between them was an accident rather than a decision:
sort above.fetchNui swallowed failures, because there
was no other way to feel responsive. It now throws, so the list follows the server
and a refused write no longer leaves the UI asserting something untrue.loaded are left as they were.Neither reads nor writes pass a defaultValue, so fetchNui throws on failure instead
of masking it. Writes propagate the throw to the caller, which already had a value to
fall back to. load catches it itself and keeps the store's last known list — the
store has nothing better to show, and a background refresh that failed should not wipe
out what the player was already looking at. Nor does it set loaded: a first load that
failed leaves the store still loading, and calling load again is the retry.
Optionaloptions: CrudOptions<T, TDraft>For the paths a list alone cannot express — an incoming message, a local patch.
The list-store factory, so an add-on can have one.
It lives in
services/because core's own services use it, and it is exported here because an app cannot import by path (§2.7) — without this line, building a list means reimplementing the ordering, theloadedflag and the no-optimism rule that took several rewrites to settle. Paired withCrudOptions.service, which is how an app reaches its own server without a route table entry.