From 9cc39ad89b3e200167596502bb905c232e335952 Mon Sep 17 00:00:00 2001 From: Roman Khafizianov Date: Mon, 21 Sep 2026 17:39:06 +0200 Subject: [PATCH] GO-7383 Drop the root-level v1 proto compatibility mirrors The four root .proto paths were deterministic mirrors of format/v1/proto, rendered and verified by format/v1/check-proto-compat.sh. Two complete import graphs for the same package symbols only earned their keep while heart's mirror-any-block.yml workflow wrote to this repository's root; a companion PR there retargets it at format/v1/proto, after which the mirrors have no consumer. Remove the mirrors, the script that rendered them, its go:generate directive, and the CI step that verified them. The checked-in Go bindings and JSON Schemas were already generated from the canonical tree alone, so no generated artifact changes. Also drop docs/, which held two superseded design specs. --- .github/workflows/ci.yml | 2 - README.md | 4 - changes.proto | 238 --- .../2026-09-11-uninstalled-types-design.md | 199 -- ...2026-09-11-unresolved-references-design.md | 333 --- events.proto | 1189 ----------- format/v1/README.md | 30 +- format/v1/check-proto-compat.sh | 91 - format/v1/generate.go | 6 +- models.proto | 1783 ----------------- snapshot.proto | 28 - 11 files changed, 13 insertions(+), 3890 deletions(-) delete mode 100644 changes.proto delete mode 100644 docs/superpowers/specs/2026-09-11-uninstalled-types-design.md delete mode 100644 docs/superpowers/specs/2026-09-11-unresolved-references-design.md delete mode 100644 events.proto delete mode 100644 format/v1/check-proto-compat.sh delete mode 100644 models.proto delete mode 100644 snapshot.proto diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 445435d..fa7700d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,8 +22,6 @@ jobs: with: version: "33.4" repo-token: ${{ secrets.GITHUB_TOKEN }} - - name: Verify v1 proto compatibility paths - run: sh format/v1/check-proto-compat.sh - name: Install pinned generators run: | echo "$(go env GOPATH)/bin" >> "$GITHUB_PATH" diff --git a/README.md b/README.md index 3a6134a..e5dfa5b 100644 --- a/README.md +++ b/README.md @@ -57,10 +57,6 @@ is the Go conversion API, and `bundle` and `cmd/anyblock` provide the bundle and command-line surfaces. `Options.TableColumnHeaders` remains opt-in so backup output is stable. -The four historical root-level v1 `.proto` paths are retained as generated -compatibility mirrors. Their canonical editable sources live in -`format/v1/proto/`; see [the v1 mirror rules](format/v1/README.md). - For the v1 side, start with [format/v1/README.md](format/v1/README.md). The normative v2 definition is [format/v2/SPEC.md](format/v2/SPEC.md); it is long, and reading an export does not require it. diff --git a/changes.proto b/changes.proto deleted file mode 100644 index 67b2934..0000000 --- a/changes.proto +++ /dev/null @@ -1,238 +0,0 @@ -syntax = "proto3"; -package anytype; -option go_package = "github.com/anyproto/any-block/format/v1/pb"; - -import "models.proto"; -import "events.proto"; -import "google/protobuf/struct.proto"; - -// the element of change tree used to store and internal apply smartBlock history -message Change { - // set of actions to apply - repeated Content content = 3; - // snapshot - when not null, the Content will be ignored - Snapshot snapshot = 4; - // file keys related to changes content - repeated FileKeys fileKeys = 6; - // creation timestamp - int64 timestamp = 7; - - // version of business logic - uint32 version = 8; - // business-level type of change applied to object - uint32 changeType = 9; - - message Snapshot { - // logId -> lastChangeId - map logHeads = 1; - // snapshot data - anytype.model.SmartBlockSnapshotBase data = 2; - // all file keys related to doc - repeated FileKeys fileKeys = 3; - } - - message FileKeys { - string hash = 1; - map keys = 2; - } - - message Content { - oneof value { - BlockCreate blockCreate = 1; - BlockUpdate blockUpdate = 2; - BlockRemove blockRemove = 3; - BlockMove blockMove = 4; - BlockDuplicate blockDuplicate = 5; - RelationAdd relationAdd = 50; - RelationRemove relationRemove = 51; - DetailsSet detailsSet = 100; - DetailsUnset detailsUnset = 101; - - ObjectTypeAdd objectTypeAdd = 105; - ObjectTypeRemove objectTypeRemove = 106; - StoreKeySet storeKeySet = 107; - StoreKeyUnset storeKeyUnset = 108; - - StoreSliceUpdate storeSliceUpdate = 109; - OriginalCreatedTimestampSet originalCreatedTimestampSet = 110; - SetFileInfo setFileInfo = 111; - NotificationCreate notificationCreate = 112; - NotificationUpdate notificationUpdate = 113; - - DeviceAdd deviceAdd = 114; - DeviceUpdate deviceUpdate = 115; - } - reserved 102,103,104; // old unsupported relation changes - } - - message BlockCreate { - string targetId = 1; - anytype.model.Block.Position position = 2; - repeated anytype.model.Block blocks = 3; - } - - message BlockUpdate { - repeated Event.Message events = 2; - } - - message BlockRemove { - repeated string ids = 1; - } - - message BlockMove { - string targetId = 1; - anytype.model.Block.Position position = 2; - repeated string ids = 3; - } - - message BlockDuplicate { - string targetId = 1; - anytype.model.Block.Position position = 2; - repeated string ids = 3; - } - - message DetailsSet { - string key = 1; - google.protobuf.Value value = 2; - } - - message DetailsUnset { - string key = 1; - } - - message RelationAdd { - repeated anytype.model.RelationLink relationLinks = 1; - } - - message RelationRemove { - repeated string relationKey = 1; - } - - message ObjectTypeAdd { - string url = 1; - string key = 2; - } - - message ObjectTypeRemove { - string url = 1; - string key = 2; - } - - message StoreKeySet { - repeated string path = 1; - google.protobuf.Value value = 2; - } - - message StoreKeyUnset { - repeated string path = 1; - } - - message StoreSliceUpdate { - string key = 1; - oneof operation { - Add add = 2; - Remove remove = 3; - Move move = 4; - } - - message Add { - string afterId = 1; - repeated string ids = 2; - } - - message Remove { - repeated string ids = 1; - } - - message Move { - string afterId = 1; - repeated string ids = 2; - } - } - - message OriginalCreatedTimestampSet { - int64 ts = 1; - } - - message SetFileInfo { - model.FileInfo fileInfo = 1; - } - - message NotificationCreate { - anytype.model.Notification notification = 1; - } - - message NotificationUpdate { - string id = 1; - anytype.model.Notification.Status status = 2; - } - - message DeviceAdd { - anytype.model.DeviceInfo device = 1; - } - - message DeviceUpdate { - string id = 1; - string name = 2; - } -} - -message ChangeNoSnapshot { - // set of actions to apply - repeated Change.Content content = 3; - // file keys related to changes content - repeated Change.FileKeys fileKeys = 6; - // creation timestamp - int64 timestamp = 7; - // version of business logic - uint32 version = 8; - // business-level type of change applied to object - uint32 changeType = 9; -} - -message StoreChange { - repeated StoreChangeContent changeSet = 1; -} - -message StoreChangeContent { - oneof change { - DocumentCreate create = 1; - DocumentModify modify = 2; - DocumentDelete delete = 3; - } -} - -message DocumentCreate { - string collection = 1; - string documentId = 2; - // json - string value = 3; -} - -message DocumentModify { - string collection = 1; - string documentId = 2; - repeated KeyModify keys = 4; -} - -message KeyModify { - // key path; example: [user, email] - repeated string keyPath = 1; - // modify op: set, unset, inc, etc. - ModifyOp modifyOp = 3; - // json value; example: '"new@example.com"' - string modifyValue = 4; -} - -enum ModifyOp { - Set = 0; - Unset = 1; - Inc = 2; - AddToSet = 3; - Pull = 4; -} - -message DocumentDelete { - string collection = 1; - string documentId = 2; -} diff --git a/docs/superpowers/specs/2026-09-11-uninstalled-types-design.md b/docs/superpowers/specs/2026-09-11-uninstalled-types-design.md deleted file mode 100644 index 07cb238..0000000 --- a/docs/superpowers/specs/2026-09-11-uninstalled-types-design.md +++ /dev/null @@ -1,199 +0,0 @@ -# Uninstalled types, and two related repairs - -Date: 2026-09-11. Status: approved in discussion; this note records it. -Follows `2026-09-11-unresolved-references-design.md`. - -## 1. Evidence - -After the unresolved-references change, 9 of 79 corpus bundles still fail -validation on 143 references to 72 type identities no type document -carries. Read against the retained source stores: - -| Class | References | Identities | Store evidence | -| --- | ---: | ---: | --- | -| uninstalled types | 135 | 64 | a full row with `isUninstalled` and `isDeleted`, name and layout intact | -| type documents whose own type is `type` | 3 | 1 | legacy markdown imports; no store in the account holds a type keyed `type` | -| types an old import never created | 5 | 5 | two markdown pages, two template targets, one profile object; the `type` id has no row in its space | -| ambiguous `Tag` declaration | 1 | | six declarations spell `Tag` with `internal_key: "tag"` beside a custom property named `Tag` | - -The first class is a plain export omission. Removing a type from a space -sets `isUninstalled`; `injectDerivedDetails` mirrors that into -`isDeleted`; the exporter enumerates through `List`, which injects -`isDeleted != true`, and its emit skip refuses anything flagged deleted. -The space still holds the definition and the objects still carry the key. - -## 2. Decisions - -1. **An uninstalled type is exported, and restored uninstalled.** It - travels as an ordinary `object_type` document with `uninstalled: true` - on the envelope, the mirror of the member a removed property already - has on its declaration and dictionary entry (§15 #22). Import writes - `isUninstalled` back, the runtime mirrors it into `isDeleted`, and the - type lands hidden, resolvable for its objects, reinstallable from the - library. Restoring it live would change a choice the user made, so that - is not done. When the destination already holds a live type with that - key, the destination wins and stays live. -2. **The planner lets an uninstalled type own its key, not its name.** - `type-` references resolve; a live type with the same display name - is not shadowed by the corpse. Same rule the store resolver applies. -3. **A type document's type is `objectType` by definition.** It is - derivable from the kind, like the layout keys §2a already drops, so - export writes `objectType` and warns when the store holds something - else. -4. **`internal_key` wins over the spelling** in a property declaration. - Export writes an agreeing pair, an author never writes `internal_key`, - and the authoring subset refuses it, so the precedence only ever - decides in favor of what the exporter knew. -5. **The store resolver's vocabulary listing names uninstalled types.** - The type listing passes an explicit `isDeleted` none-filter, as it - already does for `isArchived`, so a double-flag row keeps its - id-to-key naming while staying out of the name namespace. -6. **A bundled spelling a live custom property also carries owes a legend - entry.** Found by the fresh export: with the declarations resolving, 542 - object documents in the Tag space spelled `Tag` with no legend line, - because the writer's vocabulary resolves the spelling bundled-first and - the bundled table binds it, so neither existing question said an entry - was owed. When a scoped vocabulary reports more than one claimant for - the spelling, the export writes the entry. -7. **The comparator reads the two type-document normalizations as - normalizations.** Its own type coming back as `objectType`, and a - stored `isUninstalled: false` coming back absent, are not loss; the - codec exports both predicates and `snapshotdiff` consults them. -8. **A dictionary entry's spelling outranks a display-name claim.** Repair - 6 did not fire on real data: the store resolver withholds a label from - the custom property precisely because it shadows the bundled name, so - its claimant set for "Tag" held the bundled key alone, while the - reader's planner registered the custom entry's `name: "Tag"` as a - claim. The dictionary states outright, through the entry's `property` - member, that the bundled key is spelled "Tag" in this bundle; the - planner now records every entry's spelling and lets it decide the - candidate set before any name claim. This is what makes the Tag space - importable as already exported. Repair 6 stays as defense for a - vocabulary whose claimant set does include the shadowing name. - -9. **A type the space never held imports as a Page** (decided later the - same day, replacing the "keep the key verbatim" recommendation). The - composer declares such derived ids under `unresolved.types`, the - validator admits a declared one as a warning (`unresolved_type`) and - refuses an undeclared one, the export report warns per reference, and - the converter rewrites the object's type — a template's target — to - Page with a warning naming the document and the type. Verbatim, the - object is unsearchable by type and gets a guessed layout; a Page is - what the user can work with. -10. **A stored `_missing_object` in a relation's target list drops from the - dictionary entry.** Found by the first import sweep over fresh - exports: three spaces failed in the converter on `type - "_missing_object" has no declaration`, because an old importer had - written the sentinel into `relationFormatObjectTypes` and the - dictionary path copied it while the document path already dropped - it. The dictionary writer now applies the same predicate, and the - converter drops a sentinel target from an older export with a warning. -11. **An option key with a slash gets an escaped archive id.** The - converter names a native archive entry by the object id, and an - option's key is minted from its name, so "C/C++" refused a whole space - as an unsafe id. The archive id escapes the slash; the option's real - key travels as the snapshot key, and every value naming the option - resolves to the same escaped id. -12. **An absent bookmark object restores as a URL-only bookmark.** The pb - importer logged an error and kept the stale target when a bookmark - block's object was not in the import. The target is now cleared with - a warning, so the bookmark syncer fetches the object again from the - URL — the most a restore can recover, and the harness no longer reads - the log line as a failed import. - -## 5. Defects found by a five-lens review of this work, and fixed - -A five-lens Opus review over the finished change set found these. Each is -now fixed with a test that fails without the fix. The nine-space sweep had -passed green with all four present: the corpus does not exercise them. - -13. **A declared missing type validated and then refused to convert.** The - census declares a type reached through four document slots, but the - repair rewrote only an object's own type and a template's target. A type - named by `query_source.types`, by a dictionary entry's `object_types`, - or by a type's `property_definitions` passed validation with the "imports - as a Page" warning and then hard-failed conversion. The resolver now - treats a declared missing key as a SOFT miss: it returns not-found - without arming the sticky error, so each slot degrades the way §9 already - degrades an absent reference. Identity slots still become Page; a slot - that merely references the type drops the entry with a warning. -14. **An over-declaring index silently retyped live objects.** Nothing - checked a declared type against the documents the bundle carries, so an - index naming a carried type retyped every object of it to Page and - orphaned the type document. The validator now refuses that contradiction, - and the converter ignores a declaration for a type it can see. -15. **The composer never censused the property dictionary.** A relation - document is never written, so a property's target types reach a bundle - only through its dictionary entry — a slot the validator checks and the - composer did not, so the composer could ship a bundle its own validator - refuses. The dictionary's targets now join the type census. -16. **The option archive id was not injective.** `/` was escaped without - escaping the escape character first, so `a/b` and `a%2Fb` collapsed onto - one entry: one option was dropped, and which one survived depended on map - iteration order, breaking byte-determinism. `%` is escaped first. - -## 6. Deferred, with the design settled (2026-09-12) - -An uninstalled BUNDLED type still restores installed. The importer drops the -creation payload for a bundled key and installs the type before the -create-or-reset branch, and that install clears both the uninstalled and the -deleted flag, so decision 2's guard always sees a freshly reinstalled row and -strips the incoming flag. Measured: 0 of the 68 removed type keys in the -corpus are bundled, so this is unobserved there. It is reachable for any user -who removes a shipped type such as Task and then restores. - -The user framed the governing principle: **a backup states what the space was -at the moment it was taken, so restoring it should reproduce that moment.** -Under that rule the matrix is - -| backup | destination | today | principle | -|---|---|---|---| -| live | uninstalled | live | live | -| live | absent | live | live | -| uninstalled | absent | **live** | uninstalled | -| uninstalled | live | **live** | uninstalled | - -The first two rows already hold: a bundled type is reinstalled by the install -path, and a custom one by the state reset, which removes both flags because -the backup carries neither. Rows three and four both fail, and row four is -decision 2's guard, which deliberately lets the destination win. - -The agreed resolution, NOT implemented: scope the guard by the importer's -existing new-space flag. On a restore into a new space the bundle is the -authority in both directions, which makes row three and row four correct. On -an import into an existing space the guard stays, so a bundle can never hide -a type the destination has live — the case that matters when a use case or a -shared experience is installed into a space someone is working in. That is -one condition, not the pre-import liveness capture first proposed. - -Deferred by the user on 2026-09-12: leave the behavior as it is for now. - -## 3. Touch points - -any-block: `codec/anyblockjson` export (envelope lift, type normalization, -`authoredIdentity`), import (write-back), `authoringtypes.go` (claims), -`format/v2/schema/object.schema.json` (gated `uninstalled`), SPEC §2a, §2b -declaration table, §15. Heart: `core/block/export/collection.go` -(enumeration), `core/block/export/anyblock/anyblock.go` (emit gate), -`pkg/lib/anyblockjson/storeresolver/keyvocab.go` (listing filter), -`core/block/import/common/objectcreator` (live type wins). - -## 4. Tests - -Codec: lift and write-back round trip; no member without the flag; the -authoring subset refuses it; the planner keeps the key and yields the -name; type normalization with its warning; `internal_key` tie-break on -the `Tag` shape; a contested bundled spelling owes a legend entry; the -comparator's two normalizations. Heart: an uninstalled type row is exported with the -member; single-document export of one succeeds; the vocabulary names a -double-flag row; an incoming uninstalled type does not uninstall a live -destination type. Corpus: the existing bundles predate the exporter fix, so the check is a -fresh export of the nine failing spaces from the retained account with -the rebuilt exporter, validated with the format's own tool. Result: five -validate outright — the Tag space among them, settled by repair 8 on its -first fresh export — and four fail only on the five never-created types. -With every repair in: all nine fresh bundles validate (the five never-created -types as `unresolved_type` warnings), and the headless import sweep imports -all nine into a fresh account — 5 Page normalizations, 7 dictionary sentinel -drops, 5 bookmarks rebuilt from their URLs, no importer errors. On the old -corpus that is 79 of 79 active spaces, from 17 at the start of the day. diff --git a/docs/superpowers/specs/2026-09-11-unresolved-references-design.md b/docs/superpowers/specs/2026-09-11-unresolved-references-design.md deleted file mode 100644 index a7221cc..0000000 --- a/docs/superpowers/specs/2026-09-11-unresolved-references-design.md +++ /dev/null @@ -1,333 +0,0 @@ -# Unresolved references: deleted, omitted, absent - -Date: 2026-09-11. Status: approved design, awaiting review of this text. - -Repositories: `any-block` (format, composer, validator, converter) and -`anytype-heart` (exporter wiring, export report, importer). One any-sync -dependency is named in §6. - -## 1. Problem - -Real space exports are rejected by `bundle.Validate` before a single object -is restored. On the 79 non-empty bundles of the September account sweep, 62 -failed validation. The dominant cause is `index.json` naming objects the -bundle does not carry: - -| Slot | Bundles | References | -| --- | ---: | ---: | -| homepage | 56 | 56 | -| icon.file | 29 | 29 | -| widget target | 12 | 66 | - -The exporter is behaving as designed. Three rules combine: - -1. The exporter never writes an object flagged deleted (`writeDoc` in - Heart's export service). -2. The format treats a tombstone as an existing row, so document-level - references to deleted objects stay verbatim (SPEC §9). -3. The composer lifts homepage, widget targets and the icon into the index - verbatim, lists what it did not write in `unresolved.targets`, and the - validator refuses every listed id anyway (SPEC §2c, "Stating a target - does not make it legal"). - -The corpus confirms the mechanism: of the missing index targets, 41 of 56 -homepages, 20 of 29 icons and 12 of 62 widget targets are still referenced -verbatim by other documents in the same bundle, which means the store held a -row for them at export time. They are tombstones. - -The user's framing, adopted here: Anytype deliberately does not rewrite -every object that mentions a deleted one. A reference to a deleted object is -normal state and must import without complaint. A reference to an object -the space does not hold at all, typically because it has not synced yet, is -a real loss and must reach the user as a warning at export and at import. - -## 2. Goals and non-goals - -Goals: - -- Every whole-space export whose only defect is dangling index targets - validates and restores. -- The bundle states why each dangling target is dangling, so a reader can - distinguish a known deletion from a loss without the source store. -- Authored bundles keep strict validation: an author's dangling reference is - an authoring error. -- Restore preserves the "deleted" meaning of a reference instead of - degrading it to "not found". -- Export stops manufacturing the `_missing_object` sentinel for references - whose target may merely be unsynced. - -Non-goals: - -- Missing type references (`type-` naming no type document) and the - ambiguous `Tag` declaration. Nine bundles fail on those and they need the - key-aware audit described in the import handoff. §8 lists one likely - contributor. -- Changing what the exporter collects. Deleted objects stay unexported. -- Any change to reserved listings or the auto-widget ledger. - -## 3. Classification at export - -The composer already holds the store resolver set. At `Finish`, for each id -`Index.ReferencedObjectIds` names that no written document carries, it asks -the two existing capabilities, `ObjectExistenceResolver` and -`ObjectDeletionResolver`, and classifies: - -| exists | deleted | class | meaning | -| --- | --- | --- | --- | -| true | true | deleted | tombstone; the space deleted it, by design | -| true | false | omitted | the space holds it and this export did not write it: archived under an export without archived objects, or outside a partial export's scope | -| false | any | absent | the space holds no row; not yet synced or never here | -| unknown | | absent | the store failed to answer; the fail-safe direction is "loss" | - -Only CID-shaped ids are asked. A `type-` widget target, a participant -reference or any other derived id is declared but never classified, and -therefore counts as absent. Reserved listings and the auto-widget ledger are -never declared, as today. The codec exports the classification helper, so -the composer, the comparator and the validator share one predicate and one -id-shape gate. - -One slot is not declared: a space icon whose image object is a tombstone. -The document-level rule already drops a deleted icon because an icon is -optional (`DroppedDeletedIconRef`, SPEC §2b and §9). The composer applies -the same predicate to `icon.file` at `Finish`, drops it with the same -warning, and the index falls through to whatever icon channel is left. An -omitted or absent icon is kept and declared like any other target. - -Both resolver methods ride `GetDetails`, a raw document lookup with no -default filter injection. Tombstones and archived rows are visible on that -path. This was verified against `spaceindex.getDetails` and -`storeresolver.objectRow`. - -## 4. Index format - -`unresolved` gains two members beside the existing `properties` and -`targets`: - -```json -{ "unresolved": { - "targets": ["bafy…A", "bafy…B", "bafy…C"], - "deleted": ["bafy…A"], - "omitted": ["bafy…B"] } } -``` - -- `targets` keeps its current meaning: every id the index names that the - bundle does not carry. It is the admission list. -- `deleted` and `omitted` are subsets of `targets`, disjoint from each - other. An id in neither is absent. -- All three are sorted string lists, folded like every other reference, - and written only when non-empty. The empty `unresolved` object stays - refused. -- The full index schema admits the two members. The authoring index schema - is unchanged: it is closed and has no `unresolved` member, so an authored - bundle cannot declare a loss. - -Why subsets rather than a reason per entry: it keeps the current `targets` -semantics and the current exemption logic intact, and the format already -speaks in sorted lists. - -## 5. Validation - -### 5.1 Structured result - -`bundle.Validate` returns one flat error today. The design adds -`bundle.Inspect(fsys) (*Report, error)` and `bundle.InspectAuthoring`, where -`Report` carries issues with a severity (`error`, `warning`, `info`), a -stable code and a path. `Validate` and `ValidateAuthoring` keep their -signatures and fail exactly when the report holds an error-severity issue, -so existing callers see no change except the admissions in §5.2. - -### 5.2 Rules - -| Case | Full surface | Authoring surface | -| --- | --- | --- | -| index target declared in `targets` and in `deleted` | info, code `deleted_target` | error | -| index target declared in `targets` and in `omitted` | warning, code `omitted_target` | error | -| index target declared in `targets`, in neither subset | warning, code `unresolved_target` | error | -| index target not declared | error | error | -| `deleted` or `omitted` names an id not in `targets` | error | error | -| `deleted` and `omitted` overlap | error | error | -| `manifest.files` names a missing document | error | error | - -The declaration is the only thing that makes a dangling target admissible, -and only on the full surface. An undeclared dangling target stays an error -on both surfaces because it means the exporter shipped a reference without -noticing. - -The reserved-target and auto-widget exemptions are unchanged. - -### 5.3 CLI - -`anyblock validate` prints warnings and info after errors, grouped by -severity, and exits non-zero only on errors. A `-strict` flag makes -warnings fail as well, for CI use on authored bundles. - -## 6. Restore - -### 6.1 Converter - -`bundle/convert.Bundle` runs `Inspect` on the full surface, fails on errors, -forwards warnings and info through `OnWarning`, and returns the three sets -on the result, unfolded to store ids: - -```go -type Result struct { - … - Unresolved struct{ Deleted, Omitted, Absent []string } -} -``` - -Homepage and widgets are converted verbatim as today. - -### 6.2 Importer - -Heart's importer rewrites every reference whose id is not in the import's -old-to-new map to `_missing_object`, in `common.UpdateLinksToObjects` and -`UpdateObjectIDsInDetails`, and `WidgetObject.Init` then strips widget links -to the sentinel. That collapses deleted and absent alike. The change: - -- **Declared deleted.** The importer seeds the old-to-new map with each - declared-deleted id mapped to itself, so every rewrite site keeps the id: - links, bookmarks, file and dataview targets, mention and object marks, - icon images, relation values and widget links. It then ensures a - tombstone for the id in the destination: - - id has a live row in the destination (restore into the same space): - keep the id, write nothing; the reference re-links. - - id has a tombstone already: nothing to do. - - id has no row: write a tombstone. -- **Declared omitted or absent, and undeclared.** Today's behavior: the - sentinel is written, the widget is stripped on next load. Each declared - id produces an import report entry naming the slot and the class, at - warning severity. -- **Import report for deleted ids.** One entry per declared-deleted id at - info severity, so the report stays complete without drowning the - warnings that matter. - -Tombstone route. The durable route is the synced deletion log, so that -every device sees the tombstone and a full reindex rebuilds it: -`reindexDeletedObjects` rebuilds tombstones only from -`AllDeletedTreeIds`. any-sync's `settingsObject.DeleteObject` refuses an id -with no local head entry, so this needs a small any-sync addition: record a -deletion for an id that has no tree in this space. Until that exists, the -importer writes a store-only tombstone through `spaceindex.DeleteObject`. -Its known limits: other devices do not see it, and a full reindex drops it, -after which the reference degrades to absent, which is exactly today's -state. The any-sync change is tracked as a follow-up in §8 and is not a -blocker for this work. - -### 6.3 Round trip - -- Deleted: exported as declared deleted, restored with a tombstone, - re-exported as declared deleted. Stable in one generation. -- Absent: exported verbatim and declared, restored as the sentinel, - re-exported as a stored sentinel. Converges in one generation, as the - current §9 rule already promises for rewrites. -- Omitted: same as absent on restore. On a later export of the source it - either becomes an ordinary document or stays omitted. -- Index slots: the homepage relation is longtext and is never remapped, so - an absent homepage stays the raw id in the restored space and is declared - absent again on every later export, with the same warning each time, - until the user picks a new homepage. That is stable, and it is the - correct signal. - -## 7. Export-side document references - -Approved change to SPEC §9 "References the space cannot serve": - -- **Absent targets are kept verbatim.** Singular slots no longer rewrite - to `_missing_object`; list slots no longer drop the entry. Each keeps its - id and produces a warning naming the slot, the id and the class. The - importer already manufactures the sentinel for anything it cannot - resolve, so nothing that would be lost on restore is preserved by the - rewrite, and a backup taken during a sync gap keeps the id for the day - the object arrives. -- **Stored sentinels are unchanged.** A `_missing_object` the space - already holds is written as is in singular slots and dropped from lists, - as today. -- **The deleted-icon drop is unchanged.** It answers a deletion, which is - decidable, not a sync state. -- `snapshotdiff` and `DroppedMissingObjectRef` follow: the comparator no - longer treats an absent real id as a normalization, because nothing is - dropped. - -The export report gains two codes beside `unresolved_target`: -`omitted_target` (warning) and `deleted_target` (info). Absent index targets -and absent document references share `unresolved_target`. Source-path -attribution stays as it is. - -## 8. Follow-ups and future improvements - -Recorded here so they are not lost. None is in scope for this change. - -1. **Sync-layer refinement of "absent".** The space sync status keeps the - head-sync missing id list per space (`spaceSyncStatus.UpdateMissingIds`). - A new optional resolver capability can sharpen an absent id into - "pending sync", when peers hold it, versus "unknown to the network". The - index would gain a `pending` subset and the warning would say so. -2. **Warn when exporting offline or long unsynced.** Signals exist today: - the space sync status reports `Offline`, a non-zero syncing objects - counter and a non-empty missing id list, and every object carries - `syncStatus` and `syncDate` details. The export should add a report - warning, and the clients a confirmation, when the space is offline, has - objects pending, or has not completed a sync for a long time, since a - backup taken in that state is the one most likely to declare absent - targets. -3. **Vocabulary query hides uninstalled types and relations.** The - resolver's type and relation listing in `keyvocab.go` goes through - `Query`, which injects `isDeleted != true`, and a UI-removed type or - relation carries both `isUninstalled` and `isDeleted` in production - (`injectDerivedDetails`). Those rows fall out of the id-to-key naming, - and `TypeKeyById` has no point-lookup fallback. Fix: pass an explicit - `isDeleted` filter with the none condition, as the listing already does - for `isArchived`, and add a test asserting `TypeKeyById` on a - double-flag row. Likely a contributor to the missing type references - class. -4. **any-sync: record a deletion for an id with no local tree.** Needed for - the durable tombstone route in §6.2. -5. **Missing type references and the ambiguous `Tag` declaration.** The - key-aware audit from the import handoff. -6. **Fidelity verification** of restored spaces beyond absence of import - errors, as the handoff already suggests. - -## 9. Testing - -any-block: - -- Composer with a fake resolver: each of deleted, omitted, absent and - unknown lands in the right list; non-CID targets are declared and not - classified; reserved and ledger entries are never declared; folding is - applied to the subsets; a deleted `icon.file` is dropped with a warning - and not declared, while an omitted or absent one is declared. -- Index codec: round trip of the two new members, schema admission on the - full schema, refusal on the authoring schema, empty-object refusal kept. -- Validator matrix from §5.2 on both surfaces, including the subset - consistency errors and the `manifest.files` error. -- Converter: `Result.Unresolved` populated and unfolded; a bundle with only - declared targets converts. -- Export codec: absent singular and list references kept verbatim with a - warning; stored sentinel behavior unchanged; deleted-icon drop unchanged; - `snapshotdiff` agrees. -- The worked authoring example still validates warning-free. - -Heart: - -- Exporter: report codes per class, source-path attribution kept. -- Importer: declared-deleted id kept in every rewrite site and a tombstone - written; live row in destination re-links without a tombstone; absent id - becomes the sentinel with a report entry; widget with a deleted target is - kept, widget with an absent target is stripped; homepage verbatim. -- Sweep: rerun the 79 active spaces with deleted skips enabled. Expected: - the homepage, icon and widget classes no longer fail validation; the - remaining failures are the type reference and `Tag` classes. - -## 10. Spec text to change - -- SPEC §2c "What the bundle names and cannot answer for": the two new - members, the subset rules, and the reversal of "stating a target does not - make it legal" for the full surface. -- SPEC §9 "References the space cannot serve": absent targets kept - verbatim; the sentinel is the importer's, not the exporter's. -- SPEC §12: the structured report and the severity rules. -- SPEC §15: a decided entry recording this design and the deleted versus - absent principle. -- `bundle/README.md`, `bundle/convert/README.md`, `cmd/anyblock/README.md` - for the new surfaces and flags. diff --git a/events.proto b/events.proto deleted file mode 100644 index 9aca62b..0000000 --- a/events.proto +++ /dev/null @@ -1,1189 +0,0 @@ -syntax = "proto3"; -package anytype; -option go_package = "github.com/anyproto/any-block/format/v1/pb"; - -import "models.proto"; -import "google/protobuf/struct.proto"; - -/* - * Event – type of message, that could be sent from a middleware to the - * corresponding front-end. - */ -message Event { - repeated Message messages = 1; - string contextId = 2; - anytype.model.Account initiator = 3; - string traceId = 4; - - message Message { - string spaceId = 132; - oneof value { - Account.Show accountShow = 1; - Account.Details accountDetails = 201; - Account.Config.Update accountConfigUpdate = 202; - Account.Update accountUpdate = 203; - Account.LinkChallenge accountLinkChallenge = 204; - Account.LinkChallengeHide accountLinkChallengeHide = 205; - - Object.Details.Set objectDetailsSet = 16; - Object.Details.Amend objectDetailsAmend = 50; - Object.Details.Unset objectDetailsUnset = 51; - Object.Relations.Amend objectRelationsAmend = 52; - Object.Relations.Remove objectRelationsRemove = 53; - - Object.Remove objectRemove = 54; - Object.Close objectClose = 65; - - Object.Restrictions.Set objectRestrictionsSet = 55; - - Object.Subscription.Add subscriptionAdd = 60; - Object.Subscription.Remove subscriptionRemove = 61; - Object.Subscription.Position subscriptionPosition = 62; - Object.Subscription.Counters subscriptionCounters = 63; - Object.Subscription.Groups subscriptionGroups = 64; - - Block.Add blockAdd = 2; - Block.Delete blockDelete = 3; - Block.FilesUpload filesUpload = 4; - Block.MarksInfo marksInfo = 5; - - Block.Set.Fields blockSetFields = 6; - Block.Set.ChildrenIds blockSetChildrenIds = 7; - Block.Set.Restrictions blockSetRestrictions = 8; - Block.Set.BackgroundColor blockSetBackgroundColor = 9; - - Block.Set.Text blockSetText = 10; - Block.Set.File blockSetFile = 11; - Block.Set.Link blockSetLink = 13; - Block.Set.Bookmark blockSetBookmark = 14; - Block.Set.Align blockSetAlign = 15; - Block.Set.Div blockSetDiv = 17; - Block.Set.Relation blockSetRelation = 21; - Block.Set.Latex blockSetLatex = 25; - Block.Set.VerticalAlign blockSetVerticalAlign = 36; - Block.Set.TableRow blockSetTableRow = 37; - Block.Set.Widget blockSetWidget = 40; - - Block.Dataview.ViewSet blockDataviewViewSet = 19; - Block.Dataview.ViewDelete blockDataviewViewDelete = 20; - Block.Dataview.ViewOrder blockDataviewViewOrder = 29; - Block.Dataview.SourceSet blockDataviewSourceSet = - 35; // deprecated, source is no longer used - Block.Dataview.GroupOrderUpdate blockDataViewGroupOrderUpdate = 38; - Block.Dataview.ObjectOrderUpdate blockDataViewObjectOrderUpdate = 39; - Block.Dataview.RelationDelete blockDataviewRelationDelete = 124; - Block.Dataview.RelationSet blockDataviewRelationSet = 123; - Block.Dataview.ViewUpdate blockDataviewViewUpdate = 125; - Block.Dataview.TargetObjectIdSet blockDataviewTargetObjectIdSet = 126; - Block.Dataview.IsCollectionSet blockDataviewIsCollectionSet = 127; - - // deprecated - Block.Dataview.OldRelationDelete blockDataviewOldRelationDelete = 24; - // deprecated - Block.Dataview.OldRelationSet blockDataviewOldRelationSet = 23; - - User.Block.Join userBlockJoin = 31; - User.Block.Left userBlockLeft = 32; - User.Block.SelectRange userBlockSelectRange = 33; - User.Block.TextRange userBlockTextRange = 34; - - Ping ping = 100; - - Process.New processNew = 101; - Process.Update processUpdate = 102; - Process.Done processDone = 103; - - Status.Thread threadStatus = 110; - - File.LimitReached fileLimitReached = 111; - File.SpaceUsage fileSpaceUsage = 112; - File.LocalUsage fileLocalUsage = 113; - File.LimitUpdated fileLimitUpdated = 118; - - Notification.Send notificationSend = 114; - Notification.Update notificationUpdate = 115; - - Payload.Broadcast payloadBroadcast = 116; - - Membership.Update membershipUpdate = 117; - Membership.TiersUpdate membershipTiersUpdate = 137; - - Space.SyncStatus.Update spaceSyncStatusUpdate = 119; - - P2PStatus.Update p2pStatusUpdate = 120; - - Import.Finish importFinish = 121; - - Chat.Add chatAdd = 128; - Chat.Update chatUpdate = 129; - Chat.UpdateReactions chatUpdateReactions = 130; - Chat.UpdateMessageReadStatus chatUpdateMessageReadStatus = - 134; // received to update per-message read status (if needed to - // highlight the unread messages in the UI) - Chat.UpdateMentionReadStatus chatUpdateMentionReadStatus = - 135; // received to update per-message mention read status (if needed - // to highlight the unread mentions in the UI) - Chat.UpdateMessageSyncStatus chatUpdateMessageSyncStatus = 136; - Chat.UpdatePinnedStatus chatUpdatePinnedStatus = 140; - Chat.UpdateReactionReadStatus chatUpdateReactionReadStatus = 141; - Object.AutoArchive objectAutoArchive = 142; - Object.AutoRestore objectAutoRestore = 143; - Object.CleanupSuggestion objectCleanupSuggestion = 146; - Debug.ProfileCreated debugProfileCreated = 145; - Chat.UpdateMessageCount chatUpdateMessageCount = - 144; // received whenever the total number of non-deleted messages in - // the chat changes - - Chat.Delete chatDelete = 131; - Chat.UpdateState chatStateUpdate = - 133; // in case new unread messages received or chat state changed - // (e.g. message read on another device) - - MembershipV2.Update membershipV2Update = 138; - MembershipV2.ProductsUpdate membershipV2ProductsUpdate = 139; - } - } - - message Chat { - message Add { - string id = 1; - string orderId = 2; - string afterOrderId = 6; - model.ChatMessage message = 3; - repeated string subIds = 4; - repeated google.protobuf.Struct dependencies = 5; - } - message Delete { - string id = 1; - repeated string subIds = 2; - } - message Update { - string id = 1; - model.ChatMessage message = 2; - repeated string subIds = 3; - } - message UpdateReactions { - string id = 1; - model.ChatMessage.Reactions reactions = 2; - repeated string subIds = 3; - } - - message UpdateMessageReadStatus { - repeated string ids = 1; - bool isRead = 2; - repeated string subIds = 3; - } - message UpdateMentionReadStatus { - repeated string ids = 1; - bool isRead = 2; - repeated string subIds = 3; - } - message UpdateMessageSyncStatus { - repeated string ids = 1; - bool isSynced = 2; - repeated string subIds = 3; - } - - message UpdateState { - model.ChatState state = 1; - repeated string subIds = 2; - } - message UpdatePinnedStatus { - model.ChatMessage message = 1; - bool isPinned = 2; - repeated string subIds = 3; - } - message UpdateReactionReadStatus { - repeated string ids = 1; - bool isUnread = 2; - repeated string subIds = 3; - } - message UpdateMessageCount { - int32 messageCount = 1; // total number of non-deleted messages (includes replies) - repeated string subIds = 2; - } - } - - message Account { - /** - * Message, that will be sent to the front on each account found after an - * AccountRecoverRequest - */ - message Show { - int32 index = 1; // Number of an account in an all found accounts list - anytype.model.Account account = - 2; // An Account, that has been found for the mnemonic - } - - message Details { - string profileId = 1; - google.protobuf.Struct details = 2; - } - - message Config { - message Update { - anytype.model.Account.Config config = 1; - anytype.model.Account.Status status = 2; - } - } - - message Update { - anytype.model.Account.Config config = 1; - anytype.model.Account.Status status = 2; - } - - message LinkChallenge { - message ClientInfo { - string processName = 1; - string processPath = 2; - string name = 4; - bool signatureVerified = 3; - } - string challenge = 1; - ClientInfo clientInfo = 2; - model.Account.Auth.LocalApiScope scope = 3; - } - - message LinkChallengeHide { - string challenge = - 1; // verify code before hiding to protect from MITM attacks - } - } - - message Object { - message Details { - // Amend (i.e. add a new key-value pair or update an existing key-value - // pair) existing state - message Amend { - message KeyValue { - string key = 1; - google.protobuf.Value value = 2; // should not be null - } - string id = 1; // context objectId - repeated KeyValue details = 2; // slice of changed key-values - repeated string subIds = 3; - } - - // Overwrite current state - message Set { - string id = 1; // context objectId - google.protobuf.Struct details = - 2; // can not be a partial state. Should replace client details - // state - repeated string subIds = 3; - } - - // Unset existing detail keys - message Unset { - string id = 1; // context objectId - repeated string keys = 2; - repeated string subIds = 3; - } - } - - message Subscription { - - // Adds new document to subscriptions - message Add { - string id = 1; // object id - string afterId = 2; // id of previous doc in order, empty means first - string subId = 3; // subscription id - } - - // Removes document from subscription - message Remove { - string id = 1; // object id - string subId = 2; // subscription id - } - - // Indicates new position of document - message Position { - string id = 1; // object id - string afterId = 2; // id of previous doc in order, empty means first - string subId = 3; // subscription id - } - - message Counters { - // total available records - int64 total = 1; - // how many records available after - int64 nextCount = 2; - // how many records available before - int64 prevCount = 3; - - string subId = 4; // subscription id - } - - message Groups { - string subId = 1; - anytype.model.Block.Content.Dataview.Group group = 2; - bool remove = 3; - } - } - - message Relations { - - message Amend { - string id = 1; // context objectId - repeated anytype.model.RelationLink relationLinks = 2; - } - - message Remove { - string id = 1; // context objectId - repeated string relationKeys = 2; - } - } - - message Remove { - // notifies that objects were removed - repeated string ids = 1; - } - - message Restrictions { - message Set { - string id = 1; - anytype.model.Restrictions restrictions = 2; - } - } - - message Close { string id = 1; } - - message AutoArchive { - repeated string objectIds = 1; - } - - message AutoRestore { - repeated string objectIds = 1; - } - - message CleanupSuggestion { - // orphan ids (objects at any level + files at level >= 2) created within contextId - repeated string objectIds = 1; - // the object that was archived / deleted / had a link removed - string contextId = 2; - Trigger trigger = 3; - enum Trigger { - archive = 0; - delete = 1; - linkRemoval = 2; - } - } - } - message Block { - /* - * Event to show internal blocks on a client. - * Example Scenarios - * A. Block Creation - * 1. Block A have been created on a client C1 - * 2. Client C2 receives Event.Block.Add(Block A), - * Event.Block.Update(Page.children) B. Partial block load - * 1. Client C1 opens Page1, that contains, for example, 133 blocks. - * 2. M -> F: ShowFullScreen(Root, blocks1-50) - * 3. M -> F: Block.Add(blocks51-100) - * 3. M -> F: Block.Add(blocks101-133) - */ - message Add { - repeated anytype.model.Block blocks = 1; // id -> block - } - - /** - * Middleware to front end event message, that will be sent on one of this - * scenarios: Precondition: user A opened a block - * 1. User A drops a set of files/pictures/videos - * 2. User A creates a MediaBlock and drops a single media, that corresponds - * to its type. - */ - message FilesUpload { - string blockId = 1; // if empty => create new blocks - repeated string filePath = 2; // filepaths to the files - } - - /* - * - */ - message Delete { repeated string blockIds = 1; } - - message MarksInfo { - repeated anytype.model.Block.Content.Text.Mark.Type marksInRange = 1; - } - - message Set { - - message Relation { - string id = 1; - Key key = 2; - message Key { string value = 1; } - } - - message Fields { - string id = 1; - google.protobuf.Struct fields = 2; - } - - message ChildrenIds { - string id = 1; - repeated string childrenIds = 2; - } - - message Restrictions { - string id = 1; - anytype.model.Block.Restrictions restrictions = 2; - } - - message BackgroundColor { - string id = 1; - string backgroundColor = 2; - } - - message Align { - string id = 1; - anytype.model.Block.Align align = 2; - } - - message VerticalAlign { - string id = 1; - anytype.model.Block.VerticalAlign verticalAlign = 2; - } - - message Text { - string id = 1; - Text text = 2; - Style style = 3; - Marks marks = 4; - Checked checked = 5; - Color color = 6; - IconEmoji iconEmoji = 7; - IconImage iconImage = 8; - - message Text { string value = 1; } - - message Style { anytype.model.Block.Content.Text.Style value = 1; } - - message Marks { anytype.model.Block.Content.Text.Marks value = 1; } - - message Checked { bool value = 1; } - - message Color { string value = 1; } - - message IconEmoji { string value = 1; } - - message IconImage { string value = 1; } - } - message Latex { - string id = 1; - Text text = 2; - message Text { string value = 1; } - Processor processor = 3; - message Processor { - anytype.model.Block.Content.Latex.Processor value = 1; - } - } - - message Div { - string id = 1; - Style style = 2; - - message Style { anytype.model.Block.Content.Div.Style value = 1; } - } - - message File { - string id = 1; - Type type = 2; - State state = 3; - Mime mime = 4; - Hash hash = 5; - Name name = 6; - Size size = 7; - Style style = 8; - TargetObjectId targetObjectId = 9; - - message Name { string value = 1; } - - message Width { int32 value = 1; } - - message State { anytype.model.Block.Content.File.State value = 1; } - - message Type { anytype.model.Block.Content.File.Type value = 1; } - - message Style { anytype.model.Block.Content.File.Style value = 1; } - - message Hash { string value = 1; } - - message Mime { string value = 1; } - - message Size { int64 value = 1; } - - message TargetObjectId { string value = 1; } - } - - message Link { - string id = 1; - TargetBlockId targetBlockId = 2; - Style style = 3; - Fields fields = 4; - IconSize iconSize = 5; - CardStyle cardStyle = 6; - Description description = 7; - Relations relations = 8; - - message TargetBlockId { string value = 1; } - - message Style { anytype.model.Block.Content.Link.Style value = 1; } - - message Fields { google.protobuf.Struct value = 1; } - - message IconSize { - anytype.model.Block.Content.Link.IconSize value = 1; - } - - message CardStyle { - anytype.model.Block.Content.Link.CardStyle value = 1; - } - - message Description { - anytype.model.Block.Content.Link.Description value = 1; - } - - message Relations { repeated string value = 1; } - } - - message Bookmark { - string id = 1; - Url url = 2; - Title title = 3; - Description description = 4; - ImageHash imageHash = 5; - FaviconHash faviconHash = 6; - Type type = 7; - TargetObjectId targetObjectId = 8; - State state = 9; - - message Url { string value = 1; } - - message Title { string value = 1; } - - message Description { string value = 1; } - - message ImageHash { string value = 1; } - - message FaviconHash { string value = 1; } - - message Type { anytype.model.LinkPreview.Type value = 1; } - - message TargetObjectId { string value = 1; } - - message State { anytype.model.Block.Content.Bookmark.State value = 1; } - } - - message TableRow { - string id = 1; - IsHeader isHeader = 2; - - message IsHeader { bool value = 1; } - } - - message Widget { - string id = 1; - Layout layout = 2; - Limit limit = 3; - ViewId viewId = 4; - - message Layout { anytype.model.Block.Content.Widget.Layout value = 1; } - - message Limit { int32 value = 1; } - - message ViewId { string value = 1; } - } - } - - message Fill { - - message Details { - string id = 1; - google.protobuf.Struct details = 2; - } - - message DatabaseRecords { - string id = 1; - repeated google.protobuf.Struct records = 2; - } - - message Fields { - string id = 1; - google.protobuf.Struct fields = 2; - } - - message ChildrenIds { - string id = 1; - repeated string childrenIds = 2; - } - - message Restrictions { - string id = 1; - anytype.model.Block.Restrictions restrictions = 2; - } - - message BackgroundColor { - string id = 1; - string backgroundColor = 2; - } - - message Align { - string id = 1; - anytype.model.Block.Align align = 2; - } - - message Text { - string id = 1; - Text text = 2; - Style style = 3; - Marks marks = 4; - Checked checked = 5; - Color color = 6; - - message Text { string value = 1; } - - message Style { anytype.model.Block.Content.Text.Style value = 1; } - - message Marks { anytype.model.Block.Content.Text.Marks value = 1; } - - message Checked { bool value = 1; } - - message Color { string value = 1; } - } - - message Div { - string id = 1; - Style style = 2; - - message Style { anytype.model.Block.Content.Div.Style value = 1; } - } - - message File { - string id = 1; - Type type = 2; - State state = 3; - Mime mime = 4; - Hash hash = 5; - Name name = 6; - Size size = 7; - Style style = 8; - - message Name { string value = 1; } - - message Width { int32 value = 1; } - - message State { anytype.model.Block.Content.File.State value = 1; } - - message Type { anytype.model.Block.Content.File.Type value = 1; } - - message Style { anytype.model.Block.Content.File.Style value = 1; } - - message Hash { string value = 1; } - - message Mime { string value = 1; } - - message Size { int64 value = 1; } - } - - message Link { - string id = 1; - TargetBlockId targetBlockId = 2; - Style style = 3; - Fields fields = 4; - - message TargetBlockId { string value = 1; } - - message Style { anytype.model.Block.Content.Link.Style value = 1; } - - message Fields { google.protobuf.Struct value = 1; } - } - - message Bookmark { - string id = 1; - Url url = 2; - Title title = 3; - Description description = 4; - ImageHash imageHash = 5; - FaviconHash faviconHash = 6; - Type type = 7; - TargetObjectId targetObjectId = 8; - - message Url { string value = 1; } - - message Title { string value = 1; } - - message Description { string value = 1; } - - message ImageHash { string value = 1; } - - message FaviconHash { string value = 1; } - - message Type { anytype.model.LinkPreview.Type value = 1; } - - message TargetObjectId { string value = 1; } - } - } - - message Dataview { - // sent when the view have been changed or added - message ViewSet { - string id = 1; // dataview block's id - string viewId = - 2; // view id, client should double check this to make sure client - // doesn't switch the active view in the middle - anytype.model.Block.Content.Dataview.View view = 3; - } - - message ViewUpdate { - string id = 1; - string viewId = 2; - repeated Filter filter = 3; - repeated Relation relation = 4; - repeated Sort sort = 5; - Fields fields = 6; - - message Fields { - anytype.model.Block.Content.Dataview.View.Type type = 1; - string name = 2; - string coverRelationKey = 3; // Relation used for cover in gallery - bool hideIcon = 4; // Hide icon near name - anytype.model.Block.Content.Dataview.View.Size cardSize = - 5; // Gallery card size - bool coverFit = 6; // Image fits container - string groupRelationKey = 7; // Group view by this relationKey - string endRelationKey = 16; - bool groupBackgroundColors = 8; // Enable backgrounds in groups - int32 pageLimit = 9; // Limit of objects shown in widget - string defaultTemplateId = - 10; // Id of template object set default for the view - string defaultObjectTypeId = - 15; // Default object type that is chosen for new object created - // within the view - bool wrapContent = 17; // Wrap content in view - anytype.model.Block.Content.Dataview.View.ListSize listSize = 18; // List view size setting - bool alternateRows = 19; // Alternate row background colors in grid view - } - - message Filter { - oneof operation { - Add add = 1; - Remove remove = 2; - Update update = 3; - Move move = 4; - } - - message Add { - string afterId = 1; - repeated anytype.model.Block.Content.Dataview.Filter items = 2; - } - message Remove { repeated string ids = 1; } - message Update { - string id = 1; - anytype.model.Block.Content.Dataview.Filter item = 2; - } - message Move { - string afterId = 1; - repeated string ids = 2; - } - } - - message Relation { - oneof operation { - Add add = 1; - Remove remove = 2; - Update update = 3; - Move move = 4; - } - - message Add { - string afterId = 1; - repeated anytype.model.Block.Content.Dataview.Relation items = 2; - } - message Remove { repeated string ids = 1; } - message Update { - string id = 1; - anytype.model.Block.Content.Dataview.Relation item = 2; - } - message Move { - string afterId = 1; - repeated string ids = 2; - } - } - - message Sort { - oneof operation { - Add add = 1; - Remove remove = 2; - Update update = 3; - Move move = 4; - } - - message Add { - string afterId = 1; - repeated anytype.model.Block.Content.Dataview.Sort items = 2; - } - message Remove { repeated string ids = 1; } - message Update { - string id = 1; - anytype.model.Block.Content.Dataview.Sort item = 2; - } - message Move { - string afterId = 1; - repeated string ids = 2; - } - } - } - - message ViewDelete { - string id = 1; // dataview block's id - string viewId = 2; // view id to remove - } - - message ViewOrder { - string id = 1; // dataview block's id - repeated string viewIds = 2; // view ids in new order - } - - message SourceSet { - string id = 1; // dataview block's id - repeated string source = 2; - } - - message OldRelationDelete { - string id = 1; // dataview block's id - string relationKey = 2; // relation key to remove - } - - // sent when the dataview relation has been changed or added - message OldRelationSet { - string id = 1; // dataview block's id - string relationKey = 2; // relation key to update - anytype.model.Relation relation = 3; - } - - message RelationDelete { - string id = 1; // dataview block's id - repeated string relationKeys = 2; // relation key to remove - } - - // sent when the dataview relation has been changed or added - message RelationSet { - string id = 1; // dataview block's id - repeated anytype.model.RelationLink relationLinks = - 2; // relation id to update - } - - message GroupOrderUpdate { - string id = 1; // dataview block's id - anytype.model.Block.Content.Dataview.GroupOrder groupOrder = 2; - } - - message ObjectOrderUpdate { - string id = 1; // dataview block's id - string viewId = 2; - string groupId = 3; - repeated SliceChange sliceChanges = 4; - } - - message SliceChange { - SliceOperation op = 1; - repeated string ids = 2; - string afterId = 3; - } - - enum SliceOperation { - SliceOperationNone = 0; // not used - SliceOperationAdd = 1; - SliceOperationMove = 2; - SliceOperationRemove = 3; - SliceOperationReplace = 4; - } - - message TargetObjectIdSet { - string id = 1; // dataview block's id - string targetObjectId = 2; - } - - message IsCollectionSet { - string id = 1; // dataview block's id - bool value = 2; - } - } - } - - message User { - message Block { - - /** - * Middleware to front end event message, that will be sent in this - * scenario: Precondition: user A opened a block - * 1. User B opens the same block - * 2. User A receives a message about p.1 - */ - message Join { - Account account = 1; // Account of the user, that opened a block - } - - /** - * Middleware to front end event message, that will be sent in this - * scenario: Precondition: user A and user B opened the same block - * 1. User B closes the block - * 2. User A receives a message about p.1 - */ - message Left { - Account account = 1; // Account of the user, that left the block - } - - /** - * Middleware to front end event message, that will be sent in this - * scenario: Precondition: user A and user B opened the same block - * 1. User B sets cursor or selects a text region into a text block - * 2. User A receives a message about p.1 - */ - message TextRange { - Account account = 1; // Account of the user, that selected a text - string blockId = 2; // Id of the text block, that have a selection - anytype.model.Range range = 3; // Range of the selection - } - - /** - * Middleware to front end event message, that will be sent in this - * scenario: Precondition: user A and user B opened the same block - * 1. User B selects some inner blocks - * 2. User A receives a message about p.1 - */ - message SelectRange { - Account account = 1; // Account of the user, that selected blocks - repeated string blockIdsArray = 2; // Ids of selected blocks. - } - } - } - - message Ping { int32 index = 1; } - - message Process { - message New { Model.Process process = 1; } - message Update { Model.Process process = 1; } - message Done { Model.Process process = 1; } - } - - message Status { - message Thread { - Summary summary = 1; - Cafe cafe = 2; - repeated Account accounts = 3; - - message Summary { SyncStatus status = 1; } - - message Cafe { - SyncStatus status = 1; - int64 lastPulled = 2; - bool lastPushSucceed = 3; - PinStatus files = 4; - - message PinStatus { - int32 pinning = 1; - int32 pinned = 2; - int32 failed = 3; - int64 updated = 4; - } - } - - message Account { - string id = 1; - string name = 2; - string imageHash = 3; - bool online = 4; - int64 lastPulled = 5; - int64 lastEdited = 6; - repeated Device devices = 7; - } - - message Device { - string name = 1; - bool online = 2; - int64 lastPulled = 3; - int64 lastEdited = 4; - } - - enum SyncStatus { - Unknown = 0; - Offline = 1; - Syncing = 2; - Synced = 3; - Failed = 4; - IncompatibleVersion = 5; - NetworkNeedsUpdate = 6; - } - } - } - - message File { - message LimitReached { - string spaceId = 1; - string fileId = 2; - } - - message SpaceUsage { - uint64 bytesUsage = 1; - string spaceId = 2; - } - - message LocalUsage { uint64 localBytesUsage = 1; } - - message LimitUpdated { uint64 bytesLimit = 1; } - } - - message Membership { - message Update { anytype.model.Membership data = 1; } - message TiersUpdate { repeated anytype.model.MembershipTierData tiers = 1; } - } - - message MembershipV2 { - message Update { anytype.model.MembershipV2.Data data = 1; } - message ProductsUpdate { repeated anytype.model.MembershipV2.Product products = 1; } - } - - message Notification { - message Send { anytype.model.Notification notification = 1; } - message Update { anytype.model.Notification notification = 1; } - } - - message Payload { - message Broadcast { string payload = 1; } - } - - message Space { - message SyncStatus { - message Update { - string id = 1; - Status status = 2; - Network network = 3; - SyncError error = 4; - int64 syncingObjectsCounter = 5; - int64 notSyncedFilesCounter = 6; - int64 uploadingFilesCounter = 7; - } - } - enum Status { - Synced = 0; - Syncing = 1; - Error = 2; - Offline = 3; - NetworkNeedsUpdate = 4; - } - enum Network { - Anytype = 0; - SelfHost = 1; - LocalOnly = 2; - } - enum SyncError { - Null = 0; - StorageLimitExceed = 1; - IncompatibleVersion = 2; - NetworkError = 3; - } - } - message P2PStatus { - message Update { - string spaceId = 1; - Status status = 2; - int64 devicesCounter = 3; - } - - enum Status { - NotConnected = 0; - NotPossible = 1; - Connected = 2; - Restricted = 3; // only for ios for now, fallback to NotPossible if not - // implemented on client - } - } - - message Import { - message Finish { - string rootCollectionID = 1; - int64 objectsCount = 2; - model.Import.Type importType = 3; - } - } - - message Debug { - // ProfileCreated is emitted whenever the middleware writes a snapshot - // archive on its own (e.g. the memory-growth detector), so clients can - // surface it or queue the archive for upload without polling the - // profiles directory. Archives produced as a result of a client RPC - // (DebugRunProfiler) are NOT re-announced via this event — the RPC - // response already carries the path. - message ProfileCreated { - // reason is a short, stable, UPPER_SNAKE_CASE label describing why the - // middleware produced this report. It is NOT the DebugRunProfiler - // Reason enum — values come from the internal call sites: - // - "MEMORY_GROWTH" : the desktop memory-growth detector tripped - // - "LONG_RPC" : an RPC exceeded the long-execution threshold - // - "DB_CORRUPTION" : an anystore/spacestore database failed to open - // More reasons may be added over time; clients should forward unknown - // values to Sentry as-is. - string reason = 1; - // jsonInfo is JSON-encoded context produced by the middleware (reason- - // specific keys like "sysMemory", "method", "durationMs", "db", "code"). - // Clients forward it verbatim to Sentry as the event context. - string jsonInfo = 2; - // path is the absolute path to the snapshot archive on disk; empty - // when the reason is event-only (e.g. DB_CORRUPTION) and no artifact - // was produced. - string path = 3; - // full is true when the archive carries a timed CPU profile + trace - // in addition to heap and goroutines. False for fast snapshots and - // for event-only reports. - bool full = 4; - } - } -} - -message ResponseEvent { - repeated Event.Message messages = 1; - string contextId = 2; - string traceId = 4; -} - -message Model { - message Process { - string id = 1; - State state = 3; - Progress progress = 4; - string spaceId = 5; - - oneof message { - DropFiles dropFiles = 6; - Import import = 7; - Export export = 8; - SaveFile saveFile = 9; - Migration migration = 10; - PreloadFile preloadFile = 12; - } - - string error = 11; - message DropFiles {} - message Import {} - message Export {} - message SaveFile {} - message Migration {} - message PreloadFile {} - - enum State { - None = 0; - Running = 1; - Done = 2; - Canceled = 3; - Error = 4; - } - - message Progress { - int64 total = 1; - int64 done = 2; - string message = 3; - } - } -} diff --git a/format/v1/README.md b/format/v1/README.md index b1b6b87..fbc0f7f 100644 --- a/format/v1/README.md +++ b/format/v1/README.md @@ -10,33 +10,25 @@ reinterpreted. - [`proto/changes.proto`](proto/changes.proto) defines persisted CRDT changes. - [`proto/snapshot.proto`](proto/snapshot.proto) defines snapshot envelopes. -The files under `format/v1/proto/` are the canonical editable sources. The -historical public paths at repository root (`models.proto`, `events.proto`, -`changes.proto`, and `snapshot.proto`) remain available as deterministic -compatibility mirrors for existing `protoc` commands, imports, Buf inputs, and -raw-file URLs. The mirrors are byte-for-byte canonical content except that -their imports use the historical root paths. +The files under `format/v1/proto/` are the only copies of these definitions in +this repository; the four paths that used to be mirrored at the repository root +were removed. They are synced from `anytype-heart` by its +`mirror-any-block.yml` workflow, which rewrites the import paths and +`go_package` values, so upstream changes to the v1 wire format belong in heart. -Edit only the canonical files, then refresh and verify the mirrors with: +After the sources change, refresh the generated artifacts: ```sh -go generate ./format/v1 -sh format/v1/check-proto-compat.sh +go generate ./format/v1 # JSON Schemas +go generate ./codec/anyblockjson # Go bindings in format/v1/model ``` -Each tree is a complete alternative import graph defining the same protobuf -API. Compile either all root paths or all `format/v1/proto/` paths in one -`protoc`/Buf module; never mix both graphs in the same invocation, because two -physical `.proto` names defining the same package symbols are duplicates. The -check compiles both graphs separately and compares normalized descriptor sets, -while the project's checked-in Go bindings and JSON Schemas continue to be -generated only from the canonical tree. - -The historical root-path smoke command is supported: +A `protoc` smoke compile of the graph: ```sh protoc -I . --descriptor_set_out=/tmp/anyblock-v1.pb \ - models.proto events.proto changes.proto snapshot.proto + format/v1/proto/models.proto format/v1/proto/events.proto \ + format/v1/proto/changes.proto format/v1/proto/snapshot.proto ``` Generated model bindings used by the converter live in `format/v1/model`. diff --git a/format/v1/check-proto-compat.sh b/format/v1/check-proto-compat.sh deleted file mode 100644 index b33c953..0000000 --- a/format/v1/check-proto-compat.sh +++ /dev/null @@ -1,91 +0,0 @@ -#!/bin/sh -set -eu - -repo_root=$(CDPATH= cd -- "$(dirname -- "$0")/../.." && pwd) -canonical_dir="$repo_root/format/v1/proto" -files="models.proto events.proto changes.proto snapshot.proto" -mode=${1:---check} - -render_mirror() { - sed \ - -e 's#"format/v1/proto/models\.proto"#"models.proto"#g' \ - -e 's#"format/v1/proto/events\.proto"#"events.proto"#g' \ - -e 's#"format/v1/proto/changes\.proto"#"changes.proto"#g' \ - -e 's#"format/v1/proto/snapshot\.proto"#"snapshot.proto"#g' \ - "$1" -} - -write_mirrors() { - generated=$(mktemp -d "${TMPDIR:-/tmp}/any-block-root-proto.XXXXXX") - trap 'rm -rf -- "$generated"' EXIT HUP INT TERM - for file in $files; do - render_mirror "$canonical_dir/$file" >"$generated/$file" - if ! cmp -s "$generated/$file" "$repo_root/$file" 2>/dev/null; then - cp "$generated/$file" "$repo_root/$file" - echo "updated $file" - fi - done -} - -case "$mode" in - --write) - write_mirrors - exit 0 - ;; - --check) - ;; - *) - echo "usage: $0 [--check|--write]" >&2 - exit 2 - ;; -esac - -command -v protoc >/dev/null 2>&1 || { - echo "protoc is required for v1 compatibility checks" >&2 - exit 1 -} - -generated=$(mktemp -d "${TMPDIR:-/tmp}/any-block-proto-compat.XXXXXX") -trap 'rm -rf -- "$generated"' EXIT HUP INT TERM -drift=0 -for file in $files; do - render_mirror "$canonical_dir/$file" >"$generated/$file" - if ! cmp -s "$generated/$file" "$repo_root/$file"; then - echo "$file is not the deterministic mirror of format/v1/proto/$file" >&2 - diff -u "$repo_root/$file" "$generated/$file" >&2 || true - drift=1 - fi -done -[ "$drift" -eq 0 ] || exit 1 - -if grep -n 'import "format/v1/proto/' "$repo_root"/*.proto; then - echo "root compatibility protos must import only root compatibility paths" >&2 - exit 1 -fi - -for file in events.proto changes.proto snapshot.proto; do - if grep -E '^import "(models|events|changes|snapshot)\.proto";' "$canonical_dir/$file"; then - echo "canonical $file must import only format/v1/proto paths" >&2 - exit 1 - fi -done - -cd "$repo_root" -protoc -I . --include_imports --descriptor_set_out="$generated/canonical.pb" \ - format/v1/proto/models.proto \ - format/v1/proto/events.proto \ - format/v1/proto/changes.proto \ - format/v1/proto/snapshot.proto -protoc -I . --include_imports --descriptor_set_out="$generated/root.pb" \ - models.proto events.proto changes.proto snapshot.proto - -protoc --decode_raw <"$generated/canonical.pb" | - sed 's#format/v1/proto/##g' >"$generated/canonical.txt" -protoc --decode_raw <"$generated/root.pb" >"$generated/root.txt" -if ! cmp -s "$generated/canonical.txt" "$generated/root.txt"; then - echo "root and canonical proto descriptor APIs differ" >&2 - diff -u "$generated/canonical.txt" "$generated/root.txt" >&2 || true - exit 1 -fi - -echo "v1 root compatibility mirrors and canonical descriptors agree" diff --git a/format/v1/generate.go b/format/v1/generate.go index 44e6a52..7fd271b 100644 --- a/format/v1/generate.go +++ b/format/v1/generate.go @@ -1,7 +1,5 @@ -// Package v1 carries no Go code. It exists to host the go:generate -// directives that refresh the AnyBlock v1 artifacts: the root compatibility -// proto mirrors and the JSON Schemas generated from format/v1/proto. +// Package v1 carries no Go code. It exists to host the go:generate directive +// that refreshes the AnyBlock v1 JSON Schemas generated from format/v1/proto. package v1 -//go:generate sh check-proto-compat.sh --write //go:generate sh generate-jsonschema.sh diff --git a/models.proto b/models.proto deleted file mode 100644 index 3dabc6a..0000000 --- a/models.proto +++ /dev/null @@ -1,1783 +0,0 @@ -syntax = "proto3"; -package anytype.model; -option go_package = "github.com/anyproto/any-block/format/v1/model"; - -import "google/protobuf/struct.proto"; - -message SmartBlockSnapshotBase { - repeated Block blocks = 1; - google.protobuf.Struct details = 2; - google.protobuf.Struct fileKeys = 3 [deprecated=true]; - repeated Relation extraRelations = 4 [deprecated=true]; - repeated string objectTypes = 5; - google.protobuf.Struct collections = 6; - repeated string removedCollectionKeys = 8; - repeated RelationLink relationLinks = 7; - string key = 9; // only used for pb backup purposes, ignored in other cases - int64 originalCreatedTimestamp = 10; // ignored in import/export in favor of createdDate relation. Used to store original user-side object creation timestamp - FileInfo fileInfo = 11; -} - -enum SmartBlockType { - AccountOld = 0; // deprecated - - Page = 0x10; - ProfilePage = 0x11; - Home = 0x20; - Archive = 0x30; - Widget = 0x70; - - File = 0x100; - - Template = 0x120; - BundledTemplate = 0x121; - - BundledRelation = 0x200; // DEPRECATED - SubObject = 0x201; - BundledObjectType = 0x202; // DEPRECATED - AnytypeProfile = 0x203; - Date = 0x204; - Workspace = 0x206; - STRelation = 0x209; - STType = 0x210; - STRelationOption = 0x211; - SpaceView = 0x212; - - Identity = 0x214; - Participant = 0x216; - MissingObject = 0x207; - - FileObject = 0x215; - - NotificationObject = 0x217; - DevicesObject = 0x218; - ChatObjectDeprecated = 0x219; // DEPRECATED Container for any-store based chats - ChatDerivedObject = 0x220; // Any-store based object for chat - AccountObject = 0x221; // Container for account data in tech space - DiscussionObject = 0x222; // Any-store based object for discussion - TechSpaceObject = 0x223; // Any-store based object in tech space for cross-device sync - TechSpaceVirtualObject = 0x224; // Per-space virtual object backed by a tech space CRDT document -} - -message Search { - message Result { - string objectId = 1; - google.protobuf.Struct details = 2; // - repeated Meta meta = 3; // meta information about the search result - } - - message Meta { - string highlight = 1; // truncated text with highlights - repeated Range highlightRanges = 2; // ranges of the highlight in the text (using utf-16 runes) - string blockId = 3; // block id where the highlight has been found - string relationKey = 4; // relation key of the block where the highlight has been found - google.protobuf.Struct relationDetails = 5; // contains details for dependent object. E.g. relation option or type. todo: rename to dependantDetails - } - - message Message { - message Sort { - Key key = 1; - Type type = 2; - - enum Key { - ORDER_ID = 0; - SCORE = 1; - CREATED_AT = 2; - MODIFIED_AT = 3; - } - - enum Type { - Asc = 0; - Desc = 1; - } - } - - message Result { - string chatId = 1; - string messageId = 2; - int64 score = 3; - string highlight = 4; // truncated text with highlights - repeated Range highlightRanges = 5; // ranges of the highlight in the text (using utf-16 runes) - ChatMessage message = 6; - string spaceId = 7; // space the chat belongs to; set in all search scopes - } - } - -} - -message Block { - string id = 1; - google.protobuf.Struct fields = 2; - Restrictions restrictions = 3; - repeated string childrenIds = 4; - string backgroundColor = 5; - Align align = 6; - VerticalAlign verticalAlign = 7; - - oneof content { - Content.Smartblock smartblock = 11; - - Content.Text text = 14; - Content.File file = 15; - Content.Layout layout = 16; - Content.Div div = 17; - Content.Bookmark bookmark = 18; - Content.Icon icon = 19; - Content.Link link = 20; - Content.Dataview dataview = 21; - Content.Relation relation = 22; - Content.FeaturedRelations featuredRelations = 23; - Content.Latex latex = 24; - Content.TableOfContents tableOfContents = 25; - Content.Table table = 26; - Content.TableColumn tableColumn = 27; - Content.TableRow tableRow = 28; - Content.Widget widget = 29; - Content.Chat chat = 30; - } - - message Restrictions { - bool read = 1; - bool edit = 2; - bool remove = 3; - bool drag = 4; - bool dropOn = 5; - } - - enum Position { - None = 0; - // above target block - Top = 1; - // under target block - Bottom = 2; - // to left of target block - Left = 3; - // to right of target block - Right = 4; - // inside target block, as last block - Inner = 5; - // replace target block - Replace = 6; - // inside target block, as first block - InnerFirst = 7; - } - - enum Align { - AlignLeft = 0; - AlignCenter = 1; - AlignRight = 2; - AlignJustify = 3; - } - - enum VerticalAlign { - VerticalAlignTop = 0; - VerticalAlignMiddle = 1; - VerticalAlignBottom = 2; - } - - message Content { - /* - * Layout have no visual representation, but affects on blocks, that it contains. - * Row/Column layout blocks creates only automatically, after some of a D&D operations, for example - */ - message Layout { - Style style = 1; - - enum Style { - Row = 0; - Column = 1; - Div = 2; - Header = 3; - TableRows = 4; - TableColumns = 5; - } - } - - /* - * Link: block to link some content from an external sources. - */ - message Link { - string targetBlockId = 1; // id of the target block - Style style = 2; // deprecated - google.protobuf.Struct fields = 3; - IconSize iconSize = 4; - CardStyle cardStyle = 5; - Description description = 6; - repeated string relations = 7; - - enum IconSize { - SizeNone = 0; - SizeSmall = 1; - SizeMedium = 2; - } - - enum Style { - Page = 0; - Dataview = 1; - Dashboard = 2; - Archive = 3; - // ... - } - - enum Description { - None = 0; - Added = 1; - Content = 2; - } - - enum CardStyle { - Text = 0; - Card = 1; - Inline = 2; - } - } - - /* - * Divider: block, that contains only one horizontal thin line - */ - message Div { - Style style = 1; - - enum Style { - Line = 0; - Dots = 1; - } - } - - /* - * Bookmark is to keep a web-link and to preview a content. - */ - message Bookmark { - string url = 1; - // Deprecated. Get this data from the target object. - string title = 2; - // Deprecated. Get this data from the target object. - string description = 3; - // Deprecated. Get this data from the target object. - string imageHash = 4; - // Deprecated. Get this data from the target object. - string faviconHash = 5; - - LinkPreview.Type type = 6; - string targetObjectId = 7; - - State state = 8; - - enum State { - Empty = 0; - Fetching = 1; - Done = 2; - Error = 3; - } - } - - message Icon { - string name = 1; - } - - message FeaturedRelations { - } - - message Text { - string text = 1; - Style style = 2; - Marks marks = 3; // list of marks to apply to the text - bool checked = 4; - string color = 5; - string iconEmoji = 6; // used with style Callout - string iconImage = 7; // in case both image and emoji are set, image should has a priority in the UI - - message Marks { - repeated Mark marks = 1; - } - - message Mark { - Range range = 1; // range of symbols to apply this mark. From(symbol) To(symbol) - Type type = 2; - string param = 3; // link, color, etc - - enum Type { - Strikethrough = 0; - Keyboard = 1; - Italic = 2; - Bold = 3; - Underscored = 4; - Link = 5; - TextColor = 6; - BackgroundColor = 7; - Mention = 8; - Emoji = 9; - Object = 10; - } - } - - enum Style { - Paragraph = 0; - Header1 = 1; - Header2 = 2; - Header3 = 3; - Header4 = 4; // deprecated - Quote = 5; - Code = 6; - Title = 7; // currently only one block of this style can exists on a page - Checkbox = 8; - Marked = 9; - Numbered = 10; - Toggle = 11; - Description = 12; // currently only one block of this style can exists on a page - Callout = 13; - ToggleHeader1 = 14; - ToggleHeader2 = 15; - ToggleHeader3 = 16; - } - } - - message File { - string hash = 1; - string name = 2; - Type type = 3; - string mime = 4; - int64 size = 5; - int64 addedAt = 6; - string targetObjectId = 9; - - State state = 7; - Style style = 8; - - enum Type { - None = 0; - File = 1; - Image = 2; - Video = 3; - Audio = 4; - PDF = 5; - } - - enum Style { - Auto = 0; // all types expect File and None has Embed style by default - Link = 1; - Embed = 2; - } - - enum State { - Empty = 0; // There is no file and preview, it's an empty block, that waits files. - Uploading = 1; // There is still no file/preview, but file already uploading - Done = 2; // File and preview downloaded - Error = 3; // Error while uploading - } - } - - message Smartblock { - } - - message Dataview { - repeated string source = 1; // can be set for detached(without TargetObjectId) inline sets - repeated View views = 2; - string activeView = 3; // do not generate changes for this field - // deprecated - repeated model.Relation relations = 4; - repeated GroupOrder groupOrders = 12; - repeated ObjectOrder objectOrders = 13; - repeated anytype.model.RelationLink relationLinks = 5; - string TargetObjectId = 6; // empty for original set/collection objects and for detached inline sets - bool isCollection = 14; - - message View { - string id = 1; - Type type = 2; - string name = 3; - repeated Sort sorts = 4; - repeated Filter filters = 5; - repeated Relation relations = 6; // relations fields/columns options, also used to provide the order - string coverRelationKey = 7; // Relation used for cover in gallery - bool hideIcon = 8; // Hide icon near name - Size cardSize = 9; // Gallery card size - bool coverFit = 10; // Image fits container - string groupRelationKey = 11; // Group view by this relationKey - bool groupBackgroundColors = 12; // Enable backgrounds in groups - int32 pageLimit = 13; // Limit of objects shown in widget - string defaultTemplateId = 14; // Default template that is chosen for new object created within the view - string defaultObjectTypeId = 15; // Default object type that is chosen for new object created within the view - string endRelationKey = 16; // Group view by this relationKey - bool wrapContent = 17; // Wrap content in view - ListSize listSize = 18; // List view size setting - bool alternateRows = 19; // Alternate row background colors in grid view - - enum Type { - Table = 0; - List = 1; - Gallery = 2; - Kanban = 3; - Calendar = 4; - Graph = 5; - } - - enum Size { - Small = 0; - Medium = 1; - Large = 2; - } - - enum ListSize { - Compact = 0; // Single-line display (default) - Regular = 1; // Two-line display with description - } - } - - message Relation { - string key = 1; - bool isVisible = 2; - int32 width = 3; // the displayed column % calculated based on other visible relations - // bool isReadOnly = 4; // deprecated - bool dateIncludeTime = 5; // deprecated - TimeFormat timeFormat = 6; // deprecated - DateFormat dateFormat = 7; // deprecated - FormulaType formula = 8; - Block.Align align = 9; - - enum DateFormat { - MonthAbbrBeforeDay = 0; // Jul 30, 2020 - MonthAbbrAfterDay = 1; // 30 Jul 2020 - Short = 2; // 30/07/2020 - ShortUS = 3; // 07/30/2020 - ISO = 4; // 2020-07-30 - } - - enum TimeFormat { - Format12 = 0; - Format24 = 1; - } - - enum FormulaType { - None = 0; - Count = 1; - CountValue = 2; - CountDistinct = 3; - CountEmpty = 4; - CountNotEmpty = 5; - PercentEmpty = 6; - PercentNotEmpty = 7; - MathSum = 8; - MathAverage = 9; - MathMedian = 10; - MathMin = 11; - MathMax = 12; - Range = 13; - } - } - - message Sort { - string RelationKey = 1; - Type type = 2; - repeated google.protobuf.Value customOrder = 3; - RelationFormat format = 4; - bool includeTime = 5; - string id = 6; - EmptyType emptyPlacement = 7; - bool noCollate = 8; - - enum Type { - Asc = 0; - Desc = 1; - Custom = 2; - } - - enum EmptyType { - NotSpecified = 0; - Start = 1; - End = 2; - } - } - - message Filter { - string id = 9; - Operator operator = 1; // looks not applicable? - string RelationKey = 2; - string relationProperty = 5; - Condition condition = 3; - google.protobuf.Value value = 4; - QuickOption quickOption = 6; - RelationFormat format = 7; - bool includeTime = 8; - repeated Filter nestedFilters = 10; - - enum Operator { - No = 0; - Or = 1; - And = 2; - } - - enum Condition { - None = 0; - Equal = 1; - NotEqual = 2; - Greater = 3; - Less = 4; - GreaterOrEqual = 5; - LessOrEqual = 6; - Like = 7; - NotLike = 8; - In = 9; // "at least one value(from the provided list) is IN" - NotIn = 10; // "none of provided values are IN" - Empty = 11; - NotEmpty = 12; - AllIn = 13; - NotAllIn = 14; - ExactIn = 15; - NotExactIn = 16; - Exists = 17; - } - - enum QuickOption { - ExactDate = 0; - Yesterday = 1; - Today = 2; - Tomorrow = 3; - LastWeek = 4; - CurrentWeek = 5; - NextWeek = 6; - LastMonth = 7; - CurrentMonth = 8; - NextMonth = 9; - NumberOfDaysAgo = 10; - NumberOfDaysNow = 11; - LastYear = 12; - CurrentYear = 13; - NextYear = 14; - } - } - - message GroupOrder { - string viewId = 1; - repeated ViewGroup viewGroups = 2; - } - - message ViewGroup { - string groupId = 1; - int32 index = 2; - bool hidden = 3; - string backgroundColor = 4; - } - - message ObjectOrder { - string viewId = 1; - string groupId = 2; - repeated string objectIds = 3; - } - - message Group { - string id = 1; - oneof Value { - Status status = 2; - Tag tag = 3; - Checkbox checkbox = 4; - Date date = 5; - } - } - - message Status { - string id = 1; - } - - message Tag { - repeated string ids = 1; - } - - message Checkbox { - bool checked = 1; - } - - message Date { - } - } - - message Relation { - string key = 1; - } - - message Latex { - string text = 1; - Processor processor = 2; - - enum Processor { - Latex = 0; - Mermaid = 1; - Chart = 2; - Youtube = 3; - Vimeo = 4; - Soundcloud = 5; - GoogleMaps = 6; - Miro = 7; - Figma = 8; - Twitter = 9; - OpenStreetMap = 10; - Reddit = 11; - Facebook = 12; - Instagram = 13; - Telegram = 14; - GithubGist = 15; - Codepen = 16; - Bilibili = 17; - Excalidraw = 18; - Kroki = 19; - Graphviz = 20; - Sketchfab = 21; - Image = 22; - Drawio = 23; - Spotify = 24; - } - } - - message TableOfContents { - } - - message Table {} - message TableColumn {} - message TableRow { - bool isHeader = 1; - } - - message Widget { - Layout layout = 1; - int32 limit = 2; - string viewId = 3; - bool autoAdded = 4; - - enum Layout { - Link = 0; - Tree = 1; - List = 2; - CompactList = 3; - View = 4; - } - } - message Chat { - - } - } -} - -/* -* Used to decode block meta only, without the content itself -*/ -message BlockMetaOnly { - string id = 1; - google.protobuf.Struct fields = 2; -} - -/* -* General purpose structure, uses in Mark. -*/ -message Range { - int32 from = 1; - int32 to = 2; -} - -/** -* Contains basic information about a user account -*/ -message Account { - string id = 1; // User's thread id - Config config = 4; - Status status = 5; - Info info = 6; - - message Config { - bool enableDataview = 1; - bool enableDebug = 2; - bool enablePrereleaseChannel = 3; - bool enableSpaces = 4; - - google.protobuf.Struct extra = 100; - } - - message Status { - StatusType statusType = 1; - int64 deletionDate = 2; - } - - enum StatusType { - Active = 0; - PendingDeletion = 1; - StartedDeletion = 2; - Deleted = 3; - } - - message Info { - string homeObjectId = 2; // home dashboard block id - string archiveObjectId = 3; // archive block id - string profileObjectId = 4; // profile block id - string marketplaceWorkspaceId = 11; // marketplace workspace id - string workspaceObjectId = 15; // workspace object id. used for space-level chat - string spaceChatId = 16; // space-level chat if exists - - string deviceId = 8; - string accountSpaceId = 9; // the first created private space. It's filled only when account is created - string widgetsId = 10; - string spaceViewId = 13; - string techSpaceId = 14; - - string gatewayUrl = 101; // gateway url for fetching static files - string localStoragePath = 103; // path to local storage - string timeZone = 104; // time zone from config - string analyticsId = 105; - string networkId = 106; // network id to which anytype is connected - string ethereumAddress = 107; // we have Any PK AND Ethereum PK derived from one seed phrase - string metaDataKey = 108; // symmetric key for encrypting profile metadata - } - - message Auth { - message AppInfo { - string appHash = 1; - string appName = 2; // either from process or specified manually when creating - string appKey = 4; - int64 createdAt = 5; - int64 expireAt = 6; - LocalApiScope scope = 7; - bool isActive = 8; - } - enum LocalApiScope { - Limited = 0; // Used in WebClipper; AccountSelect(to be deprecated), ObjectSearch, ObjectShow, ObjectCreate, ObjectCreateFromURL, BlockPreview, BlockPaste, BroadcastPayloadEvent - JsonAPI = 1; // JSON API only, no direct grpc api calls allowed - Full = 2; // Full access, not available via LocalLink - } - } - -} - -message LinkPreview { - string url = 1; - string title = 2; - string description = 3; - string imageUrl = 4; - string faviconUrl = 5; - Type type = 6; - - enum Type { - Unknown = 0; - Page = 1; - Image = 2; - Text = 3; - } -} - -message Restrictions { - repeated ObjectRestriction object = 1; - repeated DataviewRestrictions dataview = 2; - - enum ObjectRestriction { - None = 0; - // restricts delete - Delete = 1; - // restricts work with relations - Relations = 2; - // restricts work with blocks - Blocks = 3; - // restricts work with details - Details = 4; - // restricts type changing - TypeChange = 5; - // restricts layout changing - LayoutChange = 6; - // restricts template creation from this object - Template = 7; - // restricts duplicate object - Duplicate = 8; - // can be set only for types. Restricts creating objects of this type - CreateObjectOfThisType = 9; - // object is not allowed to publish - Publish = 10; - } - - - message DataviewRestrictions { - string blockId = 1; - repeated DataviewRestriction restrictions = 2; - } - - enum DataviewRestriction { - DVNone = 0; - DVRelation = 1; - DVCreateObject = 2; - DVViews = 3; - } -} - -message Object { - message ChangePayload { - SmartBlockType smartBlockType = 1; - string key = 2; - bytes data = 3; - } -} - -message SpaceObjectHeader { - string spaceID = 1; -} - -message ObjectType { - string url = 1; // leave empty in case you want to create the new one - string name = 2; // name of objectType in singular form (can be localized for bundled types) - repeated RelationLink relationLinks = 3; // cannot contain more than one Relation with the same RelationType - Layout layout = 4; - string iconEmoji = 5; // emoji symbol - string description = 6; - bool hidden = 7; - bool readonly = 10; - repeated SmartBlockType types = 8; - bool isArchived = 9; // sets locally to hide object type from set and some other places - bool installedByDefault = 11; - string key = 12; // name of objectType (can be localized for bundled types) - int64 revision = 13; // revision of system objectType. Used to check if we should change type content or not - bool restrictObjectCreation = 14; // restricts creating objects of this type for users - int64 iconColor = 15; // color of object type icon - string iconName = 16; // name of object type icon - string pluralName = 17; // name of objectType in plural form (can be localized for bundled types) - - enum Layout { - basic = 0; - profile = 1; - todo = 2; - set = 3; - objectType = 4; - relation = 5; - file = 6; - dashboard = 7; - image = 8; - note = 9; - space = 10; - bookmark = 11; - relationOptionsList = 12; - relationOption = 13; - collection = 14; - audio = 15; - video = 16; - date = 17; - spaceView = 18; - participant = 19; - - pdf = 20; - chatDeprecated = 21; // deprecated - chatDerived = 22; - tag = 23; - - notification = 24; - missingObject = 25; - devices = 26; - discussion = 27; - } -} - -message Layout { - ObjectType.Layout id = 1; - string name = 2; - repeated Relation requiredRelations = 3; // relations required for this object type -} - -message RelationWithValue { - Relation relation = 1; - google.protobuf.Value value = 2; -} - -// Relation describe the human-interpreted relation type. It may be something like "Date of creation, format=date" or "Assignee, format=objectId, objectType=person" -message Relation { - string id = 100; - - // Key under which the value is stored in the map. Must be unique for the object type. - // It usually auto-generated bsonid, but also may be something human-readable in case of prebuilt types. - string key = 1; - - RelationFormat format = 2; // format of the underlying data - string name = 3; // name to show (can be localized for bundled types) - google.protobuf.Value defaultValue = 4; - DataSource dataSource = 5; // where the data is stored - - bool hidden = 6; // internal, not displayed to user (e.g. coverX, coverY) - bool readOnly = 7; // value not editable by user tobe renamed to readonlyValue - bool readOnlyRelation = 15; // relation metadata, eg name and format is not editable by user - - bool multi = 8; // allow multiple values (stored in pb list) - - repeated string objectTypes = 9; // URL of object type, empty to allow link to any object - // index 10, 11 was used in internal-only builds. Can be reused, but may break some test accounts - repeated Option selectDict = 12; // default dictionary with unique values to choose for select/multiSelect format - int32 maxCount = 13; // max number of values can be set for this relation. 0 means no limit. 1 means the value can be stored in non-repeated field - string description = 14; - - // on-store fields, injected only locally - Scope scope = 20; // deprecated, to be removed - string creator = 21; // creator profile id - int64 revision = 22; // revision of system relation. Used to check if we should change relation content or not - bool includeTime = 23; // indicates whether value of relation with date format should be processed with seconds precision - - message Option { - string id = 1; // id generated automatically if omitted - string text = 2; - string color = 3; // stored - // 4 is reserved for old relation format - string relationKey = 5; // stored - string orderId = 6; // lexicographic id of relation option for ordering - } - - enum Scope { - object = 0; // stored within the object - type = 1; // stored within the object type - setOfTheSameType = 2; // aggregated from the dataview of sets of the same object type - objectsOfTheSameType = 3; // aggregated from the dataview of sets of the same object type - library = 4; // aggregated from relations library - } - - enum DataSource { - details = 0; // default, stored inside the object's details - derived = 1; // stored locally, e.g. in badger or generated on the fly - account = 2; // stored in the account DB. means existing only for specific anytype account - local = 3; // stored locally - } -} - -// RelationFormat describes how the underlying data is stored in the google.protobuf.Value and how it should be validated/sanitized -enum RelationFormat { - longtext = 0; // string - shorttext = 1; // string, usually short enough. May be truncated in the future - number = 2; // double - status = 3; // string or list of string(len==1) - tag = 11; // list of string (choose multiple from a list) - date = 4; // float64(pb.Value doesn't have int64) or the string - file = 5; // relation can has objects of specific types: file, image, audio, video - checkbox = 6; // boolean - url = 7; // string with sanity check - email = 8; // string with sanity check - phone = 9; // string with sanity check - emoji = 10; // one emoji, can contains multiple utf-8 symbols - - object = 100; // relation can has objectType to specify objectType - relations = 101; // base64-encoded relation pb model - map = 102; // relation that handle map of values -} - -enum ObjectOrigin { - none = 0; - clipboard = 1; - dragAndDrop = 2; - import = 3; - webclipper = 4; - sharingExtension = 5; - usecase = 6; - builtin = 7; - bookmark = 8; - api = 9; -} - -message RelationLink { - string key = 1; - RelationFormat format = 2; -} - -message Relations { - repeated Relation relations = 1; -} - -message RelationOptions { - repeated Relation.Option options = 1; -} - -message InternalFlag { - Value value = 1; - - // Use such a weird construction due to the issue with imported repeated enum type - // Look https://github.com/golang/protobuf/issues/1135 for more information. - enum Value { - editorDeleteEmpty = 0; - editorSelectType = 1; - editorSelectTemplate = 2; - collectionDontIndexLinks = 3; - } -} - - -/* - * Works with a smart blocks: Page, Dashboard - * Dashboard opened, click on a page, Rpc.Block.open, Block.ShowFullscreen(PageBlock) -*/ -message ObjectView { - string rootId = 1; // Root block id - repeated Block blocks = 2; // dependent simple blocks (descendants) - repeated DetailsSet details = 3; // details for the current and dependent objects - SmartBlockType type = 4; - - message DetailsSet { - string id = 1; // context objectId - google.protobuf.Struct details = 2; // can not be a partial state. Should replace client details state - repeated string subIds = 3; - } - - message RelationWithValuePerObject { - string objectId = 1; - repeated RelationWithValue relations = 2; - } - - repeated Relation relations = 7; // DEPRECATED, use relationLinks instead - repeated RelationLink relationLinks = 10; - - Restrictions restrictions = 8; // object restrictions - HistorySize history = 9; - - message HistorySize { - int32 undo = 1; - int32 redo = 2; - } - - repeated BlockParticipant blockParticipants = 11; - message BlockParticipant { - string blockId = 1; - string participantId = 2; - } -} - -enum SpaceStatus { - // Unknown means the space is not loaded yet - Unknown = 0; - // Loading - the space in progress of loading - Loading = 1; - // Ok - the space loaded and available - Ok = 2; - // Missing - the space is missing - Missing = 3; - // Error - the space loading ended with an error - Error = 4; - - // RemoteWaitingDeletion - network status is "waiting deletion" - RemoteWaitingDeletion = 5; - // RemoteDeleted - the space is deleted in the current network - RemoteDeleted = 6; - // SpaceDeleted - the space should be deleted in the network - SpaceDeleted = 7; - // SpaceActive - the space is active in the network - SpaceActive = 8; - // SpaceJoining - the account is joining the space - SpaceJoining = 9; - // SpaceRemoving - the account is removing from space or the space is removed from network - SpaceRemoving = 10; -} - -message ParticipantPermissionChange { - string identity = 1; - ParticipantPermissions perms = 2; -} - -enum ParticipantPermissions { - Reader = 0; - Writer = 1; - Owner = 2; - NoPermissions = 3; - Admin = 4; -} - -enum InviteType { - Member = 0; // aclKey contains the key to sign the ACL record - Guest = 1; // guestKey contains the privateKey of the guest user - WithoutApprove = 2; // aclKey contains the key to sign the ACL record, but no approval needed -} - -enum ParticipantStatus { - Joining = 0; - Active = 1; - Removed = 2; - Declined = 3; - Removing = 4; - Canceled = 5; -} - -enum SpaceAccessType { - Private = 0; - Personal = 1; - Shared = 2; -} - -enum SpaceUxType { - None = 0; // old value for chat, deprecated - Data = 1; // objects-first UX - Stream = 2; // stream UX (chat with limited amount of owners) - Chat = 3; // chat UX, deprecated - OneToOne = 4; // onetoone UX (space with chat and immutable ACL between two participants) -} - -enum SpaceType { - SpaceTypeUnknown = 0; - SpaceTypeRegular = 1; - SpaceTypeTech = 2; - SpaceTypeChat = 3; - SpaceTypeOneToOne = 4; -} - -message Metadata { - oneof payload { - Payload.IdentityPayload identity = 1; - } - - message Payload { - message IdentityPayload { - bytes profileSymKey = 1; - } - } -} - -message Notification { - string id = 1; - int64 createTime = 2; - Status status = 4; - bool isLocal = 5; - oneof payload { - Import import = 6; - Export export = 8; - GalleryImport galleryImport = 9; - RequestToJoin requestToJoin = 10; - Test test = 11; - ParticipantRequestApproved participantRequestApproved = 13; - RequestToLeave requestToLeave = 15; - ParticipantRemove participantRemove = 16; - ParticipantRequestDecline participantRequestDecline = 17; - ParticipantPermissionsChange participantPermissionsChange = 18; - } - string space = 7; - string aclHeadId = 14; - - message Import { - string processId = 1; - model.Import.ErrorCode errorCode = 2; - model.Import.Type importType = 3; - string spaceId = 4; - string name = 5; - string spaceName = 6; - } - - message Export { - Code errorCode = 2; - model.Export.Format exportType = 3; - enum Code { - NULL = 0; - UNKNOWN_ERROR = 1; - BAD_INPUT = 2; - } - } - - message GalleryImport { - string processId = 1; - model.Import.ErrorCode errorCode = 2; - string spaceId = 3; - string name = 4; - string spaceName = 5; - } - - message RequestToJoin { - string spaceId = 1; - string identity = 2; - string identityName = 3; - string identityIcon = 4; - string spaceName = 5; - } - - message Test {} - - message ParticipantRequestApproved { - string spaceId = 1; - ParticipantPermissions permissions = 2; - string spaceName = 5; - } - - message RequestToLeave { - string spaceId = 1; - string identity = 2; - string identityName = 3; - string identityIcon = 4; - string spaceName = 5; - } - - message ParticipantRemove { - string identity = 1; - string identityName = 2; - string identityIcon = 3; - string spaceId = 4; - string spaceName = 5; - } - - message ParticipantRequestDecline { - string spaceId = 1; - string spaceName = 3; - } - - message ParticipantPermissionsChange { - string spaceId = 1; - ParticipantPermissions permissions = 2; - string spaceName = 3; - } - - enum Status { - Created = 0; - Shown = 1; - Read = 2; - Replied = 3; - } - - enum ActionType { - CLOSE = 0; - } -} - -message Export { - enum Format { - Markdown = 0; - Protobuf = 1; - JSON = 2; - DOT = 3; - SVG = 4; - GRAPH_JSON = 5; - // AnyBlockV2 is the native AnyBlock v2 bundle - // (format/v2/SPEC.md): a directory of `.anyblock.json` documents - // beside an index.json and properties.json. Additive — existing values - // keep their numbers, so a client that does not know it is unaffected. - AnyBlockV2 = 6; - } -} - -message Import { - enum Type { - Notion = 0; - Markdown = 1; - External = 2; // external developers use it - Pb = 3; - Html = 4; - Txt = 5; - Csv = 6; - Obsidian = 7; // Markdown with obsidian improvements - } - - enum ErrorCode { - NULL = 0; - UNKNOWN_ERROR = 1; - BAD_INPUT = 2; - INTERNAL_ERROR = 3; - FILE_LOAD_ERROR = 8; - IMPORT_IS_CANCELED = 6; - - NOTION_NO_OBJECTS_IN_INTEGRATION= 5; - NOTION_SERVER_IS_UNAVAILABLE = 12; - NOTION_RATE_LIMIT_EXCEEDED= 13; - - FILE_IMPORT_NO_OBJECTS_IN_ZIP_ARCHIVE = 14; - FILE_IMPORT_NO_OBJECTS_IN_DIRECTORY = 17; - - HTML_WRONG_HTML_STRUCTURE = 10; - - PB_NOT_ANYBLOCK_FORMAT = 11; - - CSV_LIMIT_OF_ROWS_OR_RELATIONS_EXCEEDED = 7; - - INSUFFICIENT_PERMISSIONS = 9; - } -} - -message Invite { - bytes payload = 1; - bytes signature = 2; -} - -message InvitePayload { - string creatorIdentity = 1; - string creatorName = 2; - string creatorIconCid = 12; - repeated FileEncryptionKey creatorIconEncryptionKeys = 13; - - bytes aclKey = 3; - string spaceId = 4; - string spaceName = 5; - string spaceIconCid = 6; - uint32 spaceIconOption = 10; - uint32 spaceUxType = 11; // deprecated - uint32 spaceType = 14; - repeated FileEncryptionKey spaceIconEncryptionKeys = 7; - - - InviteType inviteType = 8; - bytes guestKey = 9; -} - -message IdentityProfile { - string identity = 1; - string name = 2; - string iconCid = 3; - repeated FileEncryptionKey iconEncryptionKeys = 4; - string description = 5; - string globalName = 6; -} - -message IdentityProfileWithKey { - IdentityProfile identityProfile = 1; - bytes requestMetadata = 2; -} - -message FileInfo { - string fileId = 1; - repeated FileEncryptionKey encryptionKeys = 2; -} - -message FileEncryptionKey { - string path = 1; - string key = 2; -} - -enum ImageKind { - Basic = 0; - Cover = 1; - Icon = 2; - AutomaticallyAdded = 3; -} - -enum FileIndexingStatus { - NotIndexed = 0; - Indexed = 1; - NotFound = 2; -} - -enum SpaceShareableStatus { - StatusUnknown = 0; - StatusShareable = 1; - StatusNotShareable = 2; -} - -message ManifestInfo { - string schema = 1 [json_name="$schema"]; - string id = 2; - string name = 3; - string author = 4; - string license = 5; - string title = 6; - string description = 7; - repeated string screenshots = 8; - string downloadLink = 9; - int32 fileSize = 10; - repeated string categories = 11; - string language = 12; -} - -enum NameserviceNameType { - // .any suffix - AnyName = 0; -} - - -message Membership { - enum Status { - StatusUnknown = 0; - // please wait a bit more, we are still processing your request - // the payment is confirmed, but we need more time to do some side-effects: - // - increase limits - // - send emails - // - allocate names - StatusPending = 1; - // the membership is active, ready to use! - StatusActive = 2; - // in some cases we need to finalize the process: - // - if user has bought membership directly without first calling - // the BuySubscription method - // in this case please call Finalize to finish the process - StatusPendingRequiresFinalization = 3; - } - - enum PaymentMethod { - MethodNone = 0; - MethodStripe = 1; - MethodCrypto = 2; - MethodInappApple = 3; - MethodInappGoogle = 4; - } - - enum EmailVerificationStatus { - // user NEVER comleted the verification of the email - StatusNotVerified = 0; - // user has asked for new code, but did not enter it yet - // (even if email was verified before, you can ask to UPDATE your e-mail) - // please wait, you can not ask for more codes yet - StatusCodeSent = 1; - // the e-mail is finally verified - StatusVerified = 2; - } - - // it was Tier before, changed to int32 to allow dynamic values - uint32 tier = 1; - Status status = 2; - uint64 dateStarted = 3; - uint64 dateEnds = 4; - bool isAutoRenew = 5; - PaymentMethod paymentMethod = 6; - // can be empty if user did not ask for any name - string nsName = 7; - anytype.model.NameserviceNameType nsNameType = 8; - // if the email was verified by the user or set during the checkout - it will be here - string userEmail = 9; - bool subscribeToNewsletter = 10; -} - -message MembershipTierData { - enum PeriodType { - PeriodTypeUnknown = 0; - PeriodTypeUnlimited = 1; - PeriodTypeDays = 2; - PeriodTypeWeeks = 3; - PeriodTypeMonths = 4; - PeriodTypeYears = 5; - } - - // this is a unique Payment Node ID of the tier - // WARNING: tiers can be sorted differently, not according to their IDs! - uint32 id = 1; - // localazied name of the tier - string name = 2; - // just a short technical description - string description = 3; - // is this tier for testing and debugging only? - bool isTest = 4; - // how long is the period of the subscription - PeriodType periodType = 5; - // i.e. "5 days" or "3 years" - uint32 periodValue = 6; - // this one is a price we use ONLY on Stripe platform - uint32 priceStripeUsdCents = 7; - // number of ANY NS names that this tier includes - // also in the "features" list (see below) - uint32 anyNamesCountIncluded = 8; - // somename.any - is of len 8 - uint32 anyNameMinLength = 9; - // localized strings for the features - repeated string features = 10; - // green, blue, red, purple, custom - string colorStr = 11; - - // Stripe platform-specific data: - string stripeProductId = 12; - string stripeManageUrl = 13; - - // iOS platform-specific data: - string iosProductId = 15; - string iosManageUrl = 16; - - // Android platform-specific data: - string androidProductId = 17; - string androidManageUrl = 18; - - // "limited offer" or somehing like that - string offer = 19; -} - -message MembershipV2 { - message Amount { - // ISO 4217 currency code - string currency = 1; - - // $0.01 = 1 - // $1.00 = 100 - // also supports negative amounts! - // some invoices can have negatice amount (refund) - int64 amountCents = 2; - } - - enum Platform { - Unknown = 0; - Desktop = 1; - MobileIOS = 2; - MobileAndroid = 3; - Web = 4; - } - - enum PaymentProvider { - None = 0; - Stripe = 1; - Crypto = 2; - BillingPortal = 3; - AppStore = 4; - GooglePlay = 5; - } - - enum Period { - Unlimited = 0 [deprecated = true]; - Monthly = 1; - Yearly = 2; - ThreeYears = 3; - Lifetime = 4; - } - - message Invoice { - uint64 date = 1; - Amount total = 2; - } - - message Features { - uint64 storageBytes = 1; - uint32 spaceReaders = 2; - uint32 spaceWriters = 3; - uint32 sharedSpaces = 4; - uint32 teamSeats = 5; - uint32 anyNameCount = 6; - uint32 anyNameMinLen = 7; - uint32 privateSpaces = 8; - } - - message Product { - string id = 1; - string name = 2; - string description = 3; - - bool isTopLevel = 4; - bool isHidden = 5; - - // isIntro flag can be used as follows: - // - // 1. if current user's top level product has isIntro flag -> - // then you'd rather show a FULL list of all products - // to enable upgrading from CURRENT product - // 2. but if current user's top level product has no isIntro flag -> - // then it means that this plan was aquired and user need to control it. - // then show "second screen" to control that product instead - bool isIntro = 6; - - // isUpgradeable can be used as follows: - // - // if current user's top level product has isUpgradeable flag -> - // show incentives to buy something else - bool isUpgradeable = 7; - - repeated Amount pricesYearly = 8; - repeated Amount pricesMonthly = 9; - - // green, blue, red, purple, custom, etc - string colorStr = 10; - string offer = 11; - - Features features = 12; - repeated Amount pricesLifetime = 13; - } - - message PurchaseInfo { - uint64 dateStarted = 1; - uint64 dateEnds = 2; - bool isAutoRenew = 3; - Period period = 4; - } - - message ProductStatus { - enum Status { - StatusUnknown = 0; - StatusPending = 1; - StatusActive = 2; - StatusPendingRequiresAnyNameAllocation = 3; - } - Status status = 1; - } - - message PurchasedProduct { - Product product = 1; - PurchaseInfo purchaseInfo = 2; - ProductStatus productStatus = 3; - } - - message CartProduct { - Product product = 1; - // otherwise - monthly or isLifetime - bool isYearly = 2; - // set to true if you want to remove this item from the customer - // it's like setting -1 to some product - bool remove = 3; - // if true - then this is lifetime product (no matter what isYearly above is) - // unfortunately we do not use Period here to keep compatibility with old versions - // that still use isYearly! - bool isLifetime = 4; - } - - message Cart { - // if you add Nx the same product - it will be Nx in the 'products' array, i.e: - // each product instance has a unique index - repeated CartProduct products = 1; - // total amount of the cart (including discounts, etc) - Amount total = 2; - - // in case you are paying in the middle of the period (for existing customers) - // the next invoice amount will also be generated - Amount totalNextInvoice = 3; - uint64 nextInvoiceDate = 4; - repeated string appliedPromocodes = 5; - } - - message CryptoCheckout { - string invoiceURL = 1; - Amount total = 2; - bool isCanCancel = 3; - } - - message Data { - repeated PurchasedProduct products = 1; - Invoice nextInvoice = 2; - string teamOwnerID = 3; - PaymentProvider paymentProvider = 4; - repeated string appliedPromocodes = 5; - } -} - -enum DeviceNetworkType { - WIFI = 0; - CELLULAR = 1; - NOT_CONNECTED = 2; -} - -message Detail { - string key = 1; - google.protobuf.Value value = 2; // NUll - removes key -} - -message DeviceInfo { - string id = 1; - string name = 2; - int64 addDate = 3; - bool archived = 4; - bool isConnected = 5; -} - -message ChatState { - message UnreadState { - string oldestOrderId = 1; // oldest(in the lex sorting) unread message order id. Client should ALWAYS scroll through unread messages from the oldest to the newest - int32 counter = 2; // total number of unread messages - } - UnreadState messages = 1; // unread messages - UnreadState mentions = 2; // unread mentions - string lastStateId = 3; // reflects the state of the chat db at the moment of sending response/event that includes this state - int64 order = 4; // Order is serial number of this state. Client should apply chat state only if its order is greater than previously saved order - string unreadReactionOrderId = 5; // order id of the newest message with any unread reactions -} - -message ChatMessage { - string id = 1; // Unique message identifier - string orderId = 2; // Lexicographical id for message in order of tree traversal - string creator = 3; // Identifier for the message creator - int64 createdAt = 4; - int64 modifiedAt = 9; - - // stateId is ever-increasing id (BSON ObjectId) for this message. Unlike orderId, this ID is ordered by the time messages are added. For example, it's useful to prevent accidental reading of messages from the past when a ChatReadMessages request is sent: a message from the past may appear, but the client is still unaware of it - string stateId = 11; - - string replyToMessageId = 5; // Identifier for the message being replied to - MessageContent message = 6; // Message content - repeated Attachment attachments = 7; // Attachments slice - Reactions reactions = 8; // Reactions to the message - bool read = 10; // Message read status - bool mentionRead = 12; - bool hasMention = 14; - bool synced = 13; - bool pinned = 15; - bool unreadReaction = 16; - repeated MessageBlock blocks = 17; // Ordered list of content blocks - - message MessageContent { - string text = 1; // The text content of the message part - Block.Content.Text.Style style = 2; // The style/type of the message part - repeated Block.Content.Text.Mark marks = 3; // List of marks applied to the text - } - - message Attachment { - string target = 1; // Identifier for the attachment object - AttachmentType type = 2; // Type of attachment - - enum AttachmentType { - FILE = 0; // File attachment - IMAGE = 1; // Image attachment - LINK = 2; // Link attachment - } - } - - message Reactions { - map reactions = 1; // Map of emoji to list of user IDs - - message IdentityList { - repeated string ids = 1; // List of user IDs - } - } - - message MessageBlock { - oneof content { - MessageBlockText text = 1; - MessageBlockLink link = 2; - MessageBlockEmbed embed = 3; - MessageBlockEditorQuote editorQuote = 4; - MessageBlockMessageQuote messageQuote = 5; - } - } - - message MessageBlockText { - string text = 1; - Block.Content.Text.Style style = 2; - repeated Block.Content.Text.Mark marks = 3; - bool checked = 4; - string lang = 5; - } - - message MessageBlockLink { - string targetObjectId = 1; - LinkType type = 2; - - enum LinkType { - Object = 0; - File = 1; - Image = 2; - Bookmark = 3; - } - } - - message MessageBlockEmbed { - string text = 1; - Block.Content.Latex.Processor processor = 2; - } - - message MessageBlockEditorQuote { - string blockId = 1; // id of the quoted block in the source object - MessageBlockText content = 2; // quoted block content (text, style, marks, ...) - } - - message MessageBlockMessageQuote { - string messageId = 1; // id of the quoted chat message - string participantId = 2; // id of the participant who authored the quoted message - MessageBlockText content = 3; // quoted message content (text, style, marks, ...) - } -} - -enum SyncStatus { - SyncStatusSynced = 0; - SyncStatusSyncing = 1; - SyncStatusError = 2; - SyncStatusQueued = 3; -} - -enum SyncError { - SyncErrorNull = 0; - SyncErrorIncompatibleVersion = 2; - SyncErrorNetworkError = 3; - SyncErrorOversized = 4; -} - -enum TemplateNamePrefillType { - Empty = 0; - FromTemplateName = 1; -} - -message Placeholder { - string relationKey = 1; - repeated Value values = 2; - - message Value { - Type type = 1; - google.protobuf.Value value = 2; - } - - enum Type { - PlaceholderValue = 0; - PlaceholderToday = 1; - PlaceholderCurrentUser = 2; - } -} diff --git a/snapshot.proto b/snapshot.proto deleted file mode 100644 index 1b086f2..0000000 --- a/snapshot.proto +++ /dev/null @@ -1,28 +0,0 @@ -syntax = "proto3"; -package anytype; -option go_package = "github.com/anyproto/any-block/format/v1/pb"; - -import "models.proto"; -import "changes.proto"; - -message SnapshotWithType { - anytype.model.SmartBlockType sbType = 1; - anytype.Change.Snapshot snapshot = 2; -} - -message Profile { - string name = 1; - string avatar = 2; - string address = 4; - string spaceDashboardId = 5; // aka homepage - string profileId = 6; - string analyticsId = 7; - string startingPage = 8; // deprecated - repeated WidgetBlock widgets = 9; -} - -message WidgetBlock { - model.Block.Content.Widget.Layout layout = 1; - string targetObjectId = 2; - int32 objectLimit = 3; -}