flare-plugin-sdk 0.0.0-stage → 0.1.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +661 -0
- package/README.md +131 -2
- package/package.json +33 -4
- package/skill/flare-plugin/ABI.md +334 -0
- package/skill/flare-plugin/README.md +204 -0
- package/skill/flare-plugin/SKILL.md +80 -0
- package/skill/flare-plugin/assets/github-template/README.md +71 -0
- package/skill/flare-plugin/assets/github-template/assets/icon.png +0 -0
- package/skill/flare-plugin/assets/github-template/manifest.json +29 -0
- package/skill/flare-plugin/assets/github-template/plugin.js +108 -0
- package/skill/flare-plugin/examples/pixelfed/README.md +78 -0
- package/skill/flare-plugin/examples/pixelfed/assets/icon.png +0 -0
- package/skill/flare-plugin/examples/pixelfed/locales/en.json +24 -0
- package/skill/flare-plugin/examples/pixelfed/locales/zh-Hans.json +24 -0
- package/skill/flare-plugin/examples/pixelfed/manifest.json +211 -0
- package/skill/flare-plugin/examples/pixelfed/plugin.js +793 -0
- package/skill/flare-plugin/flare-plugin.d.ts +501 -0
- package/skill/flare-plugin/pack.mjs +155 -0
- package/skill/flare-plugin/references/authentication.md +239 -0
- package/skill/flare-plugin/references/capabilities.md +88 -0
- package/skill/flare-plugin/references/manifest.md +192 -0
- package/skill/flare-plugin/references/publishing.md +111 -0
- package/skill/flare-plugin/references/recipes.md +328 -0
- package/skill/flare-plugin/references/runtime-http.md +207 -0
- package/skill/flare-plugin/references/testing.md +176 -0
- package/skill/flare-plugin/references/troubleshooting.md +116 -0
- package/skill/flare-plugin/references/wire-data.md +163 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# Validation and acceptance
|
|
2
|
+
|
|
3
|
+
Record checks against the actual plugin revision. Use the smallest checks that
|
|
4
|
+
exercise the edited behavior. Distinguish syntax, mocked behavior, real platform engines,
|
|
5
|
+
installation, live website behavior, and platform UI; none proves all the others.
|
|
6
|
+
No demonstration backend is required for authoring this SDK.
|
|
7
|
+
|
|
8
|
+
## Checks in a generated repository
|
|
9
|
+
|
|
10
|
+
From the generated repository root, using Node:
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
node --check plugin.js
|
|
14
|
+
node -e 'JSON.parse(require("node:fs").readFileSync("manifest.json", "utf8")); console.log("Manifest JSON parses")'
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
These are syntax checks only: Node accepts APIs that the plugin Host does not
|
|
18
|
+
provide, and parsing JSON does not validate the manifest. Also inspect:
|
|
19
|
+
|
|
20
|
+
- Required root files and a valid supported icon; stable IDs and version/tag consistency.
|
|
21
|
+
- Every declared known operation has its required registration and full method
|
|
22
|
+
set. Login negotiates supported subsets with a displayable capability.
|
|
23
|
+
- Top-level initialization has no Host calls; bundled output has no module or
|
|
24
|
+
runtime platform dependency.
|
|
25
|
+
- Each remote response is mapped explicitly. Fixtures cover missing fields,
|
|
26
|
+
invalid JSON, HTTP errors, pagination continuation, and stable identities.
|
|
27
|
+
- Auth/cursor/action data is not accidentally inserted into public content or
|
|
28
|
+
media URLs. No real secrets enter checked-in fixtures.
|
|
29
|
+
|
|
30
|
+
Use the shipped `.d.ts` as editor/reference input, outside the release package.
|
|
31
|
+
For a TypeScript authoring project, reference that file and configure the actual
|
|
32
|
+
bundler to emit one classic script. Keep manifest/Host validation even when the
|
|
33
|
+
type checker passes: TypeScript optional service properties do not express all
|
|
34
|
+
capability requirements, and optionality does not make every null value valid.
|
|
35
|
+
|
|
36
|
+
## Develop locally in Desktop
|
|
37
|
+
|
|
38
|
+
In Settings > Plugins, open the overflow menu and choose **Plugin development**.
|
|
39
|
+
Choose the directory containing `manifest.json`, `plugin.js`, and the plugin assets.
|
|
40
|
+
Click **Start development** and review access to your existing accounts. Both Compose Desktop and native macOS support this flow.
|
|
41
|
+
|
|
42
|
+
Edit `plugin.js`, `manifest.json`, `assets/icon.<extension>`, or `locales/*.json`. Flare
|
|
43
|
+
automatically validates the files; click **Reload plugin** to apply the validated
|
|
44
|
+
snapshot to this plugin's accounts and open pages. An invalid edit keeps the last
|
|
45
|
+
working version running. For TypeScript projects, select your build output directory
|
|
46
|
+
and run your own compiler/watch command; Flare does not execute build scripts.
|
|
47
|
+
|
|
48
|
+
Only one development directory can be active. Reload is refused while publishing,
|
|
49
|
+
uploading, deleting, or sending is in progress; retry after the operation finishes.
|
|
50
|
+
Reload clears this plugin's derived account caches and restarts its login flows;
|
|
51
|
+
drafts, credentials, routes, and other plugins are retained. Changing the plugin ID
|
|
52
|
+
or platform ID requires stopping first. An override must retain the installed
|
|
53
|
+
plugin's platform ID. A credential schema change may require logging in again.
|
|
54
|
+
|
|
55
|
+
**Stop development** restores the version that was running before development.
|
|
56
|
+
Restarting the app loads its installed plugins normally; the last directory is
|
|
57
|
+
remembered but never mounted automatically. Development uses real accounts and
|
|
58
|
+
data: changes made by the plugin are not rolled back. Stop development before
|
|
59
|
+
installing, updating, or uninstalling the same plugin. Recent-call
|
|
60
|
+
diagnostics show method names and outcomes only, never request/response payloads.
|
|
61
|
+
|
|
62
|
+
## Checks with a Flare source checkout
|
|
63
|
+
|
|
64
|
+
Run from the Flare repository root:
|
|
65
|
+
|
|
66
|
+
```sh
|
|
67
|
+
./gradlew :feature:plugin:jvmTest \
|
|
68
|
+
--tests '*GitHubTemplateTest' \
|
|
69
|
+
--tests '*PixelfedSampleTest' \
|
|
70
|
+
--tests '*PluginGitHubInstallTest'
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
`GitHubTemplateTest` constructs a GitHub-style source archive from the **actual
|
|
74
|
+
template files**, passes it through the real importer/registration checks,
|
|
75
|
+
activates it after reopening the store, and invokes real QuickJS. It verifies
|
|
76
|
+
token/profile identity, link DTOs, refresh/older/empty/end paging, typed HTTP
|
|
77
|
+
errors, and malformed website responses using an injected fixture transport.
|
|
78
|
+
It does not download a live GitHub repo or contact a website.
|
|
79
|
+
|
|
80
|
+
`PixelfedSampleTest` packages and exercises the complete Pixelfed sample with
|
|
81
|
+
fixtures; `PluginGitHubInstallTest` covers source archive/version/identity rules.
|
|
82
|
+
For a new adapter, replace/adapt the fixtures to its actual API evidence and
|
|
83
|
+
add checks at this seam. Read the generated test XML/results for executed counts,
|
|
84
|
+
failures, and skips; a cached/no-source task is not evidence of new execution.
|
|
85
|
+
|
|
86
|
+
For changes to manifests or wire contracts, relevant existing suites include
|
|
87
|
+
PluginManifestV1Test and WireV1Test. Runtime/HTTP/login suites cover their own Host
|
|
88
|
+
contracts. Run the affected suites rather than requiring a full app build for a
|
|
89
|
+
documentation edit. The opt-in Pixelfed live test requires PIXELFED_TEST_HOST and
|
|
90
|
+
PIXELFED_TEST_TOKEN; it is read-only unless PIXELFED_TEST_ALLOW_WRITE=1. Do not
|
|
91
|
+
enable live writes without the user's current authorization.
|
|
92
|
+
|
|
93
|
+
## Website and UI acceptance
|
|
94
|
+
|
|
95
|
+
After an adapter has real endpoints and authorized credentials:
|
|
96
|
+
|
|
97
|
+
1. Enter the GitHub repository URL and click **Install**. Confirm that Flare
|
|
98
|
+
downloads the latest stable tag and shows source/version and permissions in
|
|
99
|
+
the same dialog before confirmation. Check retry and cancellation, then
|
|
100
|
+
install and verify the platform/login/timeline entries without restarting Flare.
|
|
101
|
+
2. Verify each supported login method and account identity; exercise cancelled,
|
|
102
|
+
rejected, or expired authentication where practical.
|
|
103
|
+
3. Refresh, paginate, open internal/link details, and verify remote media where
|
|
104
|
+
declared. Exercise only the advertised read/write capabilities, using safe
|
|
105
|
+
test accounts/content authorized for those actions.
|
|
106
|
+
4. For updates, test a new version without restarting, including credential compatibility
|
|
107
|
+
and removed permissions/capabilities that affect cached content.
|
|
108
|
+
5. Record Android, JVM Desktop, iOS, and native macOS evidence separately.
|
|
109
|
+
Simulator/compilation results do not establish physical-device UI behavior.
|
|
110
|
+
|
|
111
|
+
The generic template's API is illustrative. Passing its fixture tests does not
|
|
112
|
+
turn it into a working adapter for an arbitrary website; replace and validate
|
|
113
|
+
the API contract before claiming it is ready for end users.
|
|
114
|
+
|
|
115
|
+
## Skill portability and behavioral checks
|
|
116
|
+
|
|
117
|
+
Copy `feature/plugin/sdk/skill/flare-plugin` alone into a temporary directory.
|
|
118
|
+
Check that its directory name matches the frontmatter `name` and that all relative document/type/template/example references
|
|
119
|
+
stay within that copy. Exercise the pack command from the copy if local packaging
|
|
120
|
+
is relevant; no parent Flare path should be needed for authoring.
|
|
121
|
+
|
|
122
|
+
When testing the skill with an agent, use a concrete request with redacted API
|
|
123
|
+
fixtures: create a token feed, add article navigation, or diagnose a failed
|
|
124
|
+
capability declaration. Check the generated artifacts and behavior, not whether
|
|
125
|
+
the answer repeats the skill's wording. Ensure the agent reads only relevant
|
|
126
|
+
references, reports missing API evidence, and does not publish without authority.
|
|
127
|
+
|
|
128
|
+
## Delivery evidence template
|
|
129
|
+
|
|
130
|
+
```text
|
|
131
|
+
Plugin revision / API version:
|
|
132
|
+
Implemented features and capability mapping:
|
|
133
|
+
Syntax / manifest / registration checks:
|
|
134
|
+
Fixture tests and executed counts:
|
|
135
|
+
Real QuickJS / GitHub importer results:
|
|
136
|
+
Live website and platform UI checks:
|
|
137
|
+
Unverified assumptions / steps still requiring an account or device:
|
|
138
|
+
GitHub repository and stable tag, if publication was authorized and completed:
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Coverage map
|
|
142
|
+
|
|
143
|
+
| Documentation requirement | Resource / example |
|
|
144
|
+
| --- | --- |
|
|
145
|
+
| Quick start and AI workflow | README.md, SKILL.md, GitHub template |
|
|
146
|
+
| Manifest fields, defaults, validation | manifest.md; template and Pixelfed manifests |
|
|
147
|
+
| Capability/UI matrix and all twelve services | capabilities.md, recipes.md |
|
|
148
|
+
| Platform engines, HTTP, credentials, permissions | runtime-http.md; template requestJson helper |
|
|
149
|
+
| Five login methods and detection | authentication.md; template token login and Pixelfed OAuth |
|
|
150
|
+
| Mapping, identity, paging, tokens | wire-data.md; template and Pixelfed |
|
|
151
|
+
| Advanced features, guest, localization, Deep Links | recipes.md |
|
|
152
|
+
| Failure diagnosis, limits, recovery | troubleshooting.md |
|
|
153
|
+
| Verification and evidence boundaries | this document; Host SDK tests |
|
|
154
|
+
| GitHub distribution and schema upgrades | publishing.md; ABI.md |
|
|
155
|
+
|
|
156
|
+
## Platform engine regressions
|
|
157
|
+
|
|
158
|
+
The same engine contract suite runs against actual backends. From the Flare repository:
|
|
159
|
+
|
|
160
|
+
```sh
|
|
161
|
+
./gradlew :feature:plugin:jvmTest
|
|
162
|
+
./gradlew :feature:plugin:connectedAndroidDeviceTest
|
|
163
|
+
./gradlew :feature:plugin:macosArm64Test :feature:plugin:iosSimulatorArm64Test
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Android needs a connected supported device/emulator and its current WebView provider;
|
|
167
|
+
Apple tests need macOS/Xcode and an available iOS simulator. These check string/JSON
|
|
168
|
+
and Unicode transport, Host reply correlation, task draining, awaited versus detached
|
|
169
|
+
errors, isolation, policy enforcement, and close/cancellation. The existing JVM
|
|
170
|
+
package/proxy/account/lifecycle suite still runs against QuickJS. A Desktop fixture
|
|
171
|
+
run does not prove Android or Apple behavior; record OS/provider versions and actual
|
|
172
|
+
executed counts. Test UI/account/login flows separately on each shipping target.
|
|
173
|
+
|
|
174
|
+
Do not assert uniform heap/stack quotas or interruption of infinite loops. Test
|
|
175
|
+
cancellable Host work and retirement without running an unending loop in the test
|
|
176
|
+
process. See [runtime semantics](runtime-http.md#async-completion-and-errors).
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# Troubleshooting and limits
|
|
2
|
+
|
|
3
|
+
First identify the stage: repository/version discovery, archive/manifest
|
|
4
|
+
validation, registration, immediate activation, login, invocation, or UI
|
|
5
|
+
mapping. Record the running plugin version, operation, error code, and a redacted
|
|
6
|
+
input/response. Avoid collecting credentials or private content. `flare` has no
|
|
7
|
+
logging API; do not invent `flare.log` or assume a developer console is available.
|
|
8
|
+
|
|
9
|
+
## Common symptoms
|
|
10
|
+
|
|
11
|
+
| Symptom / message | Check and repair |
|
|
12
|
+
| --- | --- |
|
|
13
|
+
| No stable version tags | Public repo, stable SemVer tag, matching manifest.version; branches/prereleases are not install choices |
|
|
14
|
+
| Missing required file / nested plugin | Put manifest.json, plugin.js, one supported assets/icon.<extension> at the tagged repository root |
|
|
15
|
+
| Unknown field / unsupported schema | Use API/schema 1 and exact supported fields; strict JSON rejects extra keys |
|
|
16
|
+
| Missing method / incomplete capability | Compare full capability ID, required operation set, registration path, and login negotiation |
|
|
17
|
+
| Host API called during registration / undefined global | Keep top-level work to definitions and synchronous registration; move HTTP/crypto into methods |
|
|
18
|
+
| Platform ID already registered | Choose a distinct stable platform ID; do not impersonate a built-in platform |
|
|
19
|
+
| Installed change has no effect | Changes take effect immediately after successful activation; check validation or activation errors and the running version |
|
|
20
|
+
| Login profile identity mismatch | Verify profile.key.id equals accountId, and profile.key.host matches the selected origin |
|
|
21
|
+
| Form login did not complete | Password/CredentialImport/Form begin must return success or throw a typed error |
|
|
22
|
+
| Cookie login pending indefinitely | Declared probe URL/name/path scope, browser HTTPS, actual server login, and check's profile verification |
|
|
23
|
+
| OAuth state/expiry/package error | Use supplied state/redirectUri, resume with pendingPayload, and restart login after a package change or expiry |
|
|
24
|
+
| HTTP origin is not approved | Use an absolute selected-origin API URL; authOrigins adds login origins only |
|
|
25
|
+
| Request header is not allowed | Use structured authorization/cookies and let the Host manage transport headers |
|
|
26
|
+
| JSON parse error | Inspect status/content type/body; handle 204/empty responses and HTML error pages explicitly |
|
|
27
|
+
| Missing images with working text | mediaNetwork, current running grant, valid HTTPS URLs, and correct media DTOs |
|
|
28
|
+
| Missing tabs/search/controls | Required capabilities, static fields, negotiated account subset, default timeline/profileTabs, and operation completeness |
|
|
29
|
+
| Post click does nothing | detailType and target capability; link requires an absolute HTTPS URL |
|
|
30
|
+
| Infinite/repeated history | Correct cursor namespace, refresh handling, server exhaustion, and explicitly declared directions |
|
|
31
|
+
| Notifications collapse together | Use notification IDs, not the referenced post ID, and preserve actor/kind/timestamp |
|
|
32
|
+
| DM polling repeats or crosses rooms | Newer page cursor is separate from roomEntityToken; advance empty successful pages and isolate rooms |
|
|
33
|
+
| Actions show stale state | Return complete authoritative updatedPost/updatedRelation or appropriate invalidation; preserve action tokens |
|
|
34
|
+
| Runtime retired/paused | Find an unmarked exception, invalid Wire output, or exceeded budget before retrying |
|
|
35
|
+
| JavaScript unavailable on Android | Update Android System WebView; the provider must support message ports, Promise results, and large evaluations |
|
|
36
|
+
| Invocation succeeds despite an async error | Put required work in the method's await/return chain; detached rejections do not automatically fail the call |
|
|
37
|
+
|
|
38
|
+
## Typed errors
|
|
39
|
+
|
|
40
|
+
Only errors created with `flare.error` cross as recoverable plugin errors:
|
|
41
|
+
|
|
42
|
+
```js
|
|
43
|
+
throw flare.error({
|
|
44
|
+
code: "RateLimited",
|
|
45
|
+
message: { key: "error.limited", fallback: "Try again in {seconds} seconds", args: { seconds: 30 } },
|
|
46
|
+
retryAfterSeconds: 30,
|
|
47
|
+
});
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
| Code | Typical use |
|
|
51
|
+
| --- | --- |
|
|
52
|
+
| AuthenticationRequired | Rejected/expired credentials; an account call can trigger relogin |
|
|
53
|
+
| NotFound | The requested entity does not exist |
|
|
54
|
+
| Validation | Invalid user input, with a useful non-sensitive message |
|
|
55
|
+
| RateLimited | Website throttling; optional retryAfterSeconds |
|
|
56
|
+
| Network | A classified network failure |
|
|
57
|
+
| Remote | Other website HTTP/API failure |
|
|
58
|
+
| Unsupported | An operation/parameter combination the adapter cannot handle |
|
|
59
|
+
| InvalidResponse | Malformed or semantically invalid website data |
|
|
60
|
+
| Cancelled | An explicitly represented plugin cancellation |
|
|
61
|
+
|
|
62
|
+
Use a WireText message, not a bare string. Retry delay is 0..604800 seconds;
|
|
63
|
+
remoteCode is optional and at most 512 characters. HTTP Retry-After may be a date
|
|
64
|
+
or seconds: the template handles bounded numeric seconds and otherwise omits
|
|
65
|
+
the hint. The field communicates a delay, not a promise of automatic retries.
|
|
66
|
+
Avoid returning raw server messages containing credentials or private data.
|
|
67
|
+
|
|
68
|
+
Unmarked exceptions and invalid DTOs retire the runtime. Three counted fatal
|
|
69
|
+
failures within 60 seconds pause the plugin for that process. Cancellation can
|
|
70
|
+
retire a generation without counting as a failure; a deadline timeout can count.
|
|
71
|
+
An unavailable Android provider or collateral sandbox death does not count as
|
|
72
|
+
plugin misconduct. Retry after the provider/sandbox recovers; writes are not replayed.
|
|
73
|
+
Fix the underlying issue, then use the Host's retry mechanism where available or
|
|
74
|
+
restart. Global state is disposable, so recovery must not depend on it.
|
|
75
|
+
|
|
76
|
+
## Limits
|
|
77
|
+
|
|
78
|
+
These are current implementation bounds, not targets. Byte limits apply to UTF-8
|
|
79
|
+
or serialized payload bytes; string limits are field-length checks. Keep normal
|
|
80
|
+
requests/responses well below them.
|
|
81
|
+
|
|
82
|
+
| Resource | Bound |
|
|
83
|
+
| --- | --- |
|
|
84
|
+
| GitHub source archive | 20 MiB compressed, 50 MiB uncompressed, 4096 entries |
|
|
85
|
+
| Canonical plugin package | 20 MiB compressed, 50 MiB uncompressed, 256 files |
|
|
86
|
+
| manifest / plugin.js / icon | 256 KiB / 4 MiB / 2 MiB |
|
|
87
|
+
| Icon dimensions | 1..1024 for each dimension |
|
|
88
|
+
| Locale files | 32 catalogs; 256 KiB each; 2048 keys each; 4096 characters per value |
|
|
89
|
+
| Registration evaluation | 2-second caller deadline |
|
|
90
|
+
| Runtime initialization | 2 seconds |
|
|
91
|
+
| Runtime memory / stack | Engine/system limits; no uniform Host cap or heap-based pool eviction |
|
|
92
|
+
| Invocation deadline | Detector 10 seconds; normal 30 seconds; extended 120 seconds where selected by the Host |
|
|
93
|
+
| Invocation JSON request / response | 1 MiB / 5 MiB |
|
|
94
|
+
| HTTP response body | 4 MiB |
|
|
95
|
+
| HTTP request headers | 32 headers, 8192 characters/value, 64 KiB aggregate name/value length |
|
|
96
|
+
| HTTP text/JSON/form body | 2 MiB; form also max 256 entries |
|
|
97
|
+
| HTTP redirects | 5; GET/HEAD, same origin only |
|
|
98
|
+
| Page request limit / returned items | 1..100 / at most 200 |
|
|
99
|
+
| ID / host | 512 / 253 characters |
|
|
100
|
+
| Cursor / entity token / action token | 4096 / 8192 / 8192 characters |
|
|
101
|
+
| Credential / OAuth pending JSON | 64 KiB each |
|
|
102
|
+
| OAuth pending lifetime | 15 minutes |
|
|
103
|
+
| Rich text | 1000000 characters |
|
|
104
|
+
| Post media / gallery images | At most 100; gallery must have at least 1 image |
|
|
105
|
+
| Nested repost depth | 4 recursive levels beyond the initial post |
|
|
106
|
+
| Crypto randomHex input / sha256 input | 1..1024 random bytes / 1 MiB UTF-8 text |
|
|
107
|
+
|
|
108
|
+
Multipart calls have at most 64 parts; text parts are at most 256 KiB. Assets have
|
|
109
|
+
their own Host/compose constraints, up to 1 GiB per asset at the bridge boundary;
|
|
110
|
+
the website and negotiated composer commonly impose much smaller limits.
|
|
111
|
+
Repeated HTTP requests do not reset an invocation deadline. See
|
|
112
|
+
[runtime/HTTP](runtime-http.md) before choosing a timeout or upload strategy.
|
|
113
|
+
Deadlines retire a runtime and cancel its Host work; they do not guarantee that
|
|
114
|
+
busy JavaScript stops. A JavaScriptCore loop can require an app restart to release
|
|
115
|
+
its worker. Avoid recursion/large allocations and test bounded computation on all
|
|
116
|
+
three engines.
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# Wire data and pagination
|
|
2
|
+
|
|
3
|
+
All boundary values are JSON. The exact signatures are in
|
|
4
|
+
[flare-plugin.d.ts](../flare-plugin.d.ts). Build DTOs explicitly: returning an
|
|
5
|
+
entire website response introduces unknown fields, and the Host decoder is
|
|
6
|
+
strict. Non-finite numbers, cycles, BigInt, functions, and undefined return values
|
|
7
|
+
are not valid results. Optional nullable fields may be omitted; defaulted
|
|
8
|
+
non-null fields must not be explicitly null. A missing field with no default is
|
|
9
|
+
an error. Preserve string IDs; parsing a large numeric ID into a JavaScript
|
|
10
|
+
Number can already lose precision before mapping.
|
|
11
|
+
|
|
12
|
+
## Identity and a minimal post
|
|
13
|
+
|
|
14
|
+
`EntityKeyV1` is `{ id, host }`: a stable website identifier and an HTTPS authority
|
|
15
|
+
without a scheme/path, with a non-default port when applicable. Use the selected
|
|
16
|
+
instance/API authority for IDs in its namespace. Do not substitute a display
|
|
17
|
+
name, page position, timestamp, random UUID, or cursor as an entity key.
|
|
18
|
+
Federated content may have a public URL on a different website while its ID is
|
|
19
|
+
still local to the selected API. Profile identity at login must match that origin.
|
|
20
|
+
The Host canonicalizes a profile's host to the selected origin when they differ
|
|
21
|
+
only by case. A notification without a post uses its mapped actor's profile
|
|
22
|
+
target, so tapping its row or avatar uses the same account key. Profile navigation
|
|
23
|
+
remains unavailable when the session does not expose that capability.
|
|
24
|
+
|
|
25
|
+
Example website response (illustrative):
|
|
26
|
+
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
"id": "post-17",
|
|
30
|
+
"author": { "id": "alice", "username": "alice", "name": "Alice" },
|
|
31
|
+
"created_at": "2026-01-01T00:00:00Z",
|
|
32
|
+
"text": "Hello",
|
|
33
|
+
"url": "https://example.org/posts/post-17"
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Explicit mapping, assuming the fields have been validated:
|
|
38
|
+
|
|
39
|
+
```js
|
|
40
|
+
const host = context.origin.slice("https://".length);
|
|
41
|
+
const post = {
|
|
42
|
+
key: { id: remote.id, host },
|
|
43
|
+
author: {
|
|
44
|
+
key: { id: remote.author.id, host },
|
|
45
|
+
handle: remote.author.username,
|
|
46
|
+
displayName: remote.author.name,
|
|
47
|
+
},
|
|
48
|
+
createdAt: remote.created_at,
|
|
49
|
+
content: { format: "plain", value: remote.text },
|
|
50
|
+
detailType: "link",
|
|
51
|
+
url: remote.url,
|
|
52
|
+
};
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Use ISO-8601 instants with a timezone, not epoch numbers or locale-formatted dates.
|
|
56
|
+
`ProfileV1` requires key/handle/displayName; `PostV1` requires key/createdAt/content.
|
|
57
|
+
Post author and visibility default to null and may be omitted or explicitly null.
|
|
58
|
+
When an author is provided, it must be a complete valid ProfileV1. The Host does
|
|
59
|
+
not synthesize an author or infer public visibility. Most collections default
|
|
60
|
+
empty and booleans default false. Counts are nonnegative. Do not include unknown
|
|
61
|
+
optional website fields by spreading objects.
|
|
62
|
+
See [navigation](capabilities.md#entry-points-and-actions) before choosing detailType.
|
|
63
|
+
|
|
64
|
+
A minimal post without author or visibility is:
|
|
65
|
+
|
|
66
|
+
```json
|
|
67
|
+
{
|
|
68
|
+
"key": { "id": "post-17", "host": "example.org" },
|
|
69
|
+
"createdAt": "2026-01-01T00:00:00Z",
|
|
70
|
+
"content": { "value": "Hello" }
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The same defaults apply inside `repost`, notifications, and `updatedPost` results.
|
|
75
|
+
An updated post is a complete snapshot: omitting author or visibility clears that
|
|
76
|
+
value rather than retaining it from the cached post.
|
|
77
|
+
|
|
78
|
+
## Text and media
|
|
79
|
+
|
|
80
|
+
Rich text is `{ format: "plain" | "html", value }`; omitted format means plain.
|
|
81
|
+
Flare sanitizes HTML. Relative HTML links/resources resolve against the selected
|
|
82
|
+
instance; URLs must resolve to HTTPS without credentials. Scripts and local/
|
|
83
|
+
data/content resources are not a way to embed a custom UI. Normal DTO URL fields
|
|
84
|
+
should be absolute HTTPS. `detailType: "link"` always requires such a URL.
|
|
85
|
+
|
|
86
|
+
Media requires `{ id, type, url }`, with type `image`, `video`, `gif`, or `audio`.
|
|
87
|
+
Optional fields are previewUrl, description (alt text), width, height, and
|
|
88
|
+
durationMillis. Preserve accurate dimensions/duration and accessibility text.
|
|
89
|
+
Remote media needs the manifest's mediaNetwork permission, including profile
|
|
90
|
+
avatars, article covers, and images inside HTML. Returning text successfully does
|
|
91
|
+
not prove media permission is correct.
|
|
92
|
+
|
|
93
|
+
`repost` holds the nested original PostV1 with its own key and detailType; do not
|
|
94
|
+
overwrite it with the wrapper ID. `replyTo` is an EntityKey, not a full post.
|
|
95
|
+
Spoiler text, sensitive flag, visibility, and action state should reflect the
|
|
96
|
+
website. Post nesting is bounded; see [limits](troubleshooting.md#limits).
|
|
97
|
+
|
|
98
|
+
## Pages
|
|
99
|
+
|
|
100
|
+
Most operations receive `request.page`; some, such as list.page and tabCatalog.page,
|
|
101
|
+
receive PageRequestV1 directly. Consult the signature before destructuring.
|
|
102
|
+
|
|
103
|
+
```json
|
|
104
|
+
{ "direction": "older", "limit": 20, "cursor": "opaque-page-2", "parameters": {} }
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Return a PageV1, not the website's pagination envelope:
|
|
108
|
+
|
|
109
|
+
```json
|
|
110
|
+
{ "items": [], "olderCursor": "opaque-page-3", "newerCursor": "latest-marker", "endReached": false }
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
| Direction | Expected behavior |
|
|
114
|
+
| --- | --- |
|
|
115
|
+
| refresh | Fetch the initial/current page; the Host supplies no old cursor |
|
|
116
|
+
| older | Continue the older/history side using the supplied cursor |
|
|
117
|
+
| newer | Fetch newer items using the newer-side cursor, if declared/supported |
|
|
118
|
+
|
|
119
|
+
The Host maps olderCursor to append and newerCursor to prepend. `endReached`
|
|
120
|
+
suppresses the older cursor; it does not erase newerCursor. Return stable ordering
|
|
121
|
+
consistent with the selected operation and site API. If an API returns newest
|
|
122
|
+
first, keep that convention across pages. Respect the requested limit and map
|
|
123
|
+
website continuation tokens/Link headers/offsets explicitly. A cursor may encode
|
|
124
|
+
an offset or multiple server values, but it is bounded and opaque to the Host.
|
|
125
|
+
|
|
126
|
+
An empty page need not be the end: filtering or long polling may return no items
|
|
127
|
+
and still advance a cursor. Report actual server exhaustion, not merely
|
|
128
|
+
`items.length === 0`. Do not restart at page one when an older cursor is present.
|
|
129
|
+
Only declare directions you implement; an empty manifest direction set currently
|
|
130
|
+
allows all directions. Keep parameter semantics stable across refresh and paging.
|
|
131
|
+
|
|
132
|
+
## Three different tokens
|
|
133
|
+
|
|
134
|
+
| Value | Purpose | Lifecycle |
|
|
135
|
+
| --- | --- | --- |
|
|
136
|
+
| `olderCursor` / `newerCursor` | Continue a page stream | Returned page → later paging request |
|
|
137
|
+
| `entityToken` | Opaque context for an entity lookup/operation | Returned entity → entity request; room and message tokens have distinct fields |
|
|
138
|
+
| `actionToken` | Opaque website data needed for one semantic mutation | Action descriptor → MutationRequestV1; relation uses actionTokens keyed by action |
|
|
139
|
+
|
|
140
|
+
Never use an entity token as a DM polling cursor. Room `messages` polling uses
|
|
141
|
+
the page's newerCursor and can advance after an empty successful page. Opaque
|
|
142
|
+
tokens are serialized/cacheable operation data, not a secret store. Keep account
|
|
143
|
+
credentials in `flare.credential`, and do not encode URLs that bypass origin checks.
|
|
144
|
+
|
|
145
|
+
## Other entities
|
|
146
|
+
|
|
147
|
+
| DTO | Required fields / notable semantics |
|
|
148
|
+
| --- | --- |
|
|
149
|
+
| HashtagV1 | name; optional HTTPS url |
|
|
150
|
+
| NotificationV1 | id, createdAt, kind; optional actor/post/message. Identity belongs to the notification, not the referenced post |
|
|
151
|
+
| RelationV1 | profileKey; following/followedBy/blocking/muting default false; actionTokens map semantic actions to opaque strings |
|
|
152
|
+
| SocialListV1 | id, title; optional memberCount/entityToken |
|
|
153
|
+
| DirectMessageRoomV1 | key, title, participants; lastMessage must belong to this room |
|
|
154
|
+
| DirectMessageV1 | key, roomKey, sender, createdAt, content; fromCurrentAccount defaults false |
|
|
155
|
+
| ArticleV1 | key, title, content; optional author/createdAt/url/coverUrl |
|
|
156
|
+
| GalleryV1 | key, title, createdAt, url, nonempty images; all gallery media must be images; orientation defaults vertical |
|
|
157
|
+
| TimelineDescriptorV1 | id, title as WireText, icon; display defaults List; optional string parameters |
|
|
158
|
+
| TimelineSectionV1 | id, title as WireText, timelines as PageV1 of descriptors |
|
|
159
|
+
|
|
160
|
+
Notification kinds are `Mention`, `Reply`, `Favourite`, `Repost`, `Follow`, `Other`.
|
|
161
|
+
Gallery orientations are `horizontal`/`vertical`. These enum spellings intentionally
|
|
162
|
+
differ from manifest notification filter IDs and List/Grid display values.
|
|
163
|
+
See [recipes](recipes.md) for operation-specific result requirements and mutations.
|