# Project storage, reads and assets

Keep a module Block's values and files in its project cloud storage, read the project, add media assets and propose edits people review and undo.

Module Blocks keep their own data in the project, read the project, add media
assets and propose edits that people review.

### Cloud storage

Every Block has the `storage` declaration (new Blocks get it automatically).
`scope` chooses how it is shared:

```json
{"storage": {"schemaVersion": 1, "scope": "project", "desktop": "device-unmetered", "cloud": "metered"}}
```

- `"workspace"` keeps one store per project Step that places the Block.
- `"project"` keeps one store for every placement of the Block in the project
  (for example a brand kit, a glossary or a voice profile).

```js
const kit = await stillmade.storage.get('brand/kit.json');      // undefined when not set
await stillmade.storage.set('brand/kit.json', {name: 'Harbor Films', color: '#1F6FEB'});
await stillmade.storage.putFile('brand/logo.png', bytes, {mediaType: 'image/png'});
const logo = await stillmade.storage.getFile('brand/logo.png');  // {data: ArrayBuffer, mediaType, bytes} or null
const {items, next} = await stillmade.storage.list('brand/');
await stillmade.storage.remove('brand/old.png');
```

| Limit | Value |
| --- | --- |
| Key | Letters, digits, `. _ - /`, up to 200 characters, no `..` or leading `/` |
| Value | JSON, up to 1 MiB |
| File | Up to 64 MiB |
| Per Block per project | 256 MiB and 2,000 keys |

Storage belongs to the Block's saved entity and manifest ID, so its new
versions keep it and other Blocks (including another creator's Block with the
same ID) never see it. Inside a project the bytes live in the project owner's
StillMade cloud storage and count toward it; people who can open the project
read the Block's storage and people who can edit it change it. Outside a
project (for example in the Block preview) the storage is the person's own.
Storage is removed with the project. Block code never receives a storage link
or key: StillMade moves the bytes. `stillmade-block test` and `run` use an
in-memory store with the same rules.

On the desktop app, `StillMade.desktop('storage.*')` from a custom interface
keeps device files without a quota.

SDK helpers (from `@stillmade/block-sdk`): `CLOUD_STORAGE_LIMITS` holds the
limits above; `storageKey`, `storagePrefix`, `storageValueText`,
`storageMediaType` and `storageFileBytes` apply the same checks the host does;
`BLOCK_STORAGE_SCOPES` lists `workspace` and `project`. For tests,
`createMemoryStorage()` is an in-memory store with the same rules and
`storageHostHandlers(store)` turns a module's `storage.*` calls into calls on
it (the local runner uses both). `projectReadSnapshot(manifest, project, fields)`
computes what `project.read` returns from a project snapshot.

### Reading the project

Declare `project.read` plus `context.<field>.read` for each field:

```js
const {timeline, shots, metadata} = await stillmade.project.read(['timeline', 'shots']);
```

`project.read()` with no fields returns the metadata and every field the Block
may read. Media in the result can be used with `files.read`, transforms and
`assets.appendVersion`. Context inputs (`{"type": "timeline", "context": "timeline"}`)
still work and fill the value before the run starts.

### Adding project assets

```js
const saved = await stillmade.files.write(png, {mediaType: 'image/png', name: 'Brand card.png'});
const card = await stillmade.assets.create(saved, {name: 'Brand card'});      // asset.create
await stillmade.assets.appendVersion(card, await stillmade.files.write(next, {mediaType: 'image/png'})); // asset.version.append
```

Assets are images, videos and audio; they appear in the project's media and
the Editor's media bin. Other files (PDF, CSV and so on) become project files
when the Block returns them as outputs. Adding an asset needs edit access to
the project.

### Proposing edits

Edits to the project are proposals the person reviews: a `timeline-edit`
output (permissions `context.timeline.read` and `timeline.propose`) appears in
the Editor's media bin as "Timeline edits from Blocks", where the person
applies it as one edit and can undo it; a `workspace-edit` output
(`context.<field>.read` and `workspace.<field>.propose`) is reviewed in the
Step. Build the `before` values from `project.read`.

`examples/module-brand-kit` keeps a brand kit (name, color, logo and a brand
pack) in project storage, adds a brand card to the project's media and
proposes a title caption for the timeline.
