@fias/arche-sdk 2.20.0 → 2.21.0
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/dist/entity-ops.d.ts +7 -1
- package/dist/entity-ops.d.ts.map +1 -1
- package/dist/entity-ops.js +7 -1
- package/dist/entity-ops.js.map +1 -1
- package/dist/generated/permissions.d.ts +1 -1
- package/dist/generated/permissions.d.ts.map +1 -1
- package/dist/generated/permissions.js +2 -0
- package/dist/generated/permissions.js.map +1 -1
- package/dist/hooks.d.ts +103 -0
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +41 -0
- package/dist/hooks.js.map +1 -1
- package/dist/hooks.test.js +59 -0
- package/dist/hooks.test.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +31 -0
- package/dist/types.d.ts +69 -11
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/templates/default/AGENTS.md +122 -7
- package/templates/default/CLAUDE.md +122 -7
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- fias-sdk-guide-version: 2.
|
|
1
|
+
<!-- fias-sdk-guide-version: 2.21.0 -->
|
|
2
2
|
|
|
3
3
|
# FIAS Plugin Development Guide
|
|
4
4
|
|
|
@@ -12,9 +12,34 @@ package.json # Dependencies and scripts
|
|
|
12
12
|
src/
|
|
13
13
|
index.tsx # Entry point — must render into #root with <FiasProvider> wrapper
|
|
14
14
|
App.tsx # Main component
|
|
15
|
+
public/ # Optional — static images, fonts, sounds referenced as '/name.png'
|
|
16
|
+
content/ # Optional — a content pack read with useFiasContent() (lessons, data, media)
|
|
15
17
|
vite.config.ts # Vite dev server config (port 3100)
|
|
16
18
|
```
|
|
17
19
|
|
|
20
|
+
## Where does each kind of data go?
|
|
21
|
+
|
|
22
|
+
Decide by who writes the data and when, before you write the code: moving data to a different home after you publish means a migration.
|
|
23
|
+
|
|
24
|
+
| What it is | Where it goes | What you get |
|
|
25
|
+
| ------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
26
|
+
| Your code and small constants (config, a short word list) | The bundle (`src/`) | One file, max 5 MB uncompressed. Every user downloads all of it each time the plugin opens. |
|
|
27
|
+
| A few static images, fonts or sounds your UI names directly | `public/`, referenced as literal `'/name.png'` strings | Served from a CDN. Binary only (png, jpg, webp, gif, woff, woff2, ogg), top-level files only, 4 MB each, 600 files and 120 MB in total. |
|
|
28
|
+
| Content you write and ship with each version: lessons, articles, levels, reference data, large image or audio sets, fonts | A content pack: `content/` + `useFiasContent()` | Not bundled, so users download only what they open. Reviewed on every publish and read-only at runtime. You pay its storage and bandwidth; reads are free to users. |
|
|
29
|
+
| Images you want to change without republishing code (logos, maps, photos with alt text) | The asset library: `useArcheAssets()` + `fias-dev assets` | Managed from the CLI and refreshed without a code publish. |
|
|
30
|
+
| Records your app writes while it runs: progress, things users create, shared catalogues, leaderboards | `useFiasDataStore()` collections | Queryable, `user` or `shared` scope, write policies, optional semantic search. |
|
|
31
|
+
| Files only your app uses, for one user: drafts, caches, working data | `useFiasStorage()` | Free-form paths, private to the plugin and the user. Not shown in their Vault. |
|
|
32
|
+
| A small piece of UI state that should survive a reload (a setting, the last tab) | `usePersistentState()` | Debounced writes to `useFiasStorage()`. |
|
|
33
|
+
| Images one user shares with the app's other users | `useCommunityAssets()` | Moderated before anyone else sees them. |
|
|
34
|
+
| Documents the user should keep: files your app makes for them, or files they already have | `useVaultDocuments()` / `useVaultUserDocuments()` | In the user's My Data, where they outlive your plugin. Reading files they already had needs their consent. |
|
|
35
|
+
|
|
36
|
+
**Common mistakes:**
|
|
37
|
+
|
|
38
|
+
- **Inlining content in `src/`** — a JSON file of lessons, a level pack, base64 images. It counts against the 5 MB cap, and every user downloads all of it to use a little. Put it in `content/`.
|
|
39
|
+
- **Seeding content you wrote into the DataStore from the plugin.** Every user's session tries to write it, the first writer of a `shared` document owns it, and nothing reviews it. Ship it in `content/`. If it must also be searchable, keep a searchable copy in a `shared` collection, but display the pack's version.
|
|
40
|
+
- **Using the DataStore or `useFiasStorage()` for data that is the same for every user.** That is content.
|
|
41
|
+
- **Building `public/` paths at runtime**, e.g. `` `/music-${name}.ogg` ``. Only literal paths are rewritten to the CDN when you publish, so a path built in code 404s in production. Use a lookup table of literal strings, or move the files to `content/` and read them by name.
|
|
42
|
+
|
|
18
43
|
## SDK API Reference
|
|
19
44
|
|
|
20
45
|
All hooks require the app to be wrapped in `<FiasProvider>`:
|
|
@@ -479,6 +504,25 @@ generate({
|
|
|
479
504
|
|
|
480
505
|
Mode values: `'text-to-image'` (baseline), `'image-to-image'` (requires `referenceImage`), `'vector'` (SVG output). Style values: `'illustration'` (stylized / digital-art) or `'photoreal'`.
|
|
481
506
|
|
|
507
|
+
**Several reference images, and output resolution.** A model whose `maxReferenceImages` (from `useImageEntities`) is more than one takes them together in `referenceImages` — Nano Banana 2 (`ent_modeldef_nano_banana_2`) takes up to 14. There is no role per image: the model reads them in order, so the prompt says which is which. `resolution` picks one of the model's `supportedResolutions`; higher costs more (`creditsPerImageByResolution`):
|
|
508
|
+
|
|
509
|
+
```tsx
|
|
510
|
+
generate({
|
|
511
|
+
entityId: 'ent_modeldef_nano_banana_2',
|
|
512
|
+
prompt:
|
|
513
|
+
'The knight from the first image, standing on a cliff at dawn, painted in the style of the second and third images',
|
|
514
|
+
referenceImages: [
|
|
515
|
+
{ fileId: knight.fileId },
|
|
516
|
+
{ fileId: styleA.fileId },
|
|
517
|
+
{ fileId: styleB.fileId },
|
|
518
|
+
],
|
|
519
|
+
size: '21:9',
|
|
520
|
+
resolution: '2K', // '512px' | '1K' (default) | '2K' | '4K'
|
|
521
|
+
});
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
Use `referenceImage` for one picture and `referenceImages` for several — not both. More than the model takes returns `TOO_MANY_REFERENCE_IMAGES`; a resolution it does not list returns `INVALID_RESOLUTION`. Prefer `fileId` / `archeAssetId` over `dataUrl` when sending several: each `dataUrl` counts against the bridge message size. The result's `costCents` is what the call actually cost, markup included.
|
|
525
|
+
|
|
482
526
|
Use `npx fias-dev entities --detail <entityId>` to check supported sizes, qualities, and styles for each model.
|
|
483
527
|
|
|
484
528
|
#### Full example
|
|
@@ -545,14 +589,15 @@ const { entities, isLoading } = useImageEntities({
|
|
|
545
589
|
supportsReferenceImage: true,
|
|
546
590
|
});
|
|
547
591
|
// entities[i]: { entityId, displayName, provider, modes, supportedSizes,
|
|
548
|
-
// supportsReferenceImage,
|
|
549
|
-
//
|
|
550
|
-
//
|
|
592
|
+
// supportsReferenceImage, maxReferenceImages,
|
|
593
|
+
// supportedResolutions, defaultResolution, isPlatformRecommended,
|
|
594
|
+
// creditsPerImage, creditsPerImageByResolution,
|
|
595
|
+
// estimatedDurationMs, company, strengths, providerParams, ... }
|
|
551
596
|
```
|
|
552
597
|
|
|
553
598
|
Each entry is scrubbed — only fields a UI legitimately needs (no provider model strings, no endpoints, no execution config).
|
|
554
599
|
|
|
555
|
-
`creditsPerImage` is what one image costs the user in credits, markup already included — it matches what the ledger deducts, so it is the figure to show. `estimatedDurationMs` sizes a progress indicator; `company` and `strengths` are model-info copy. All optional: absent means the model has nothing to declare, not that the data is missing.
|
|
600
|
+
`creditsPerImage` is what one image costs the user in credits, markup already included — it matches what the ledger deducts, so it is the figure to show. For a model priced by resolution it is the price at `defaultResolution`, and `creditsPerImageByResolution` has each resolution's; Nano Banana 2 also bills reference images and thinking as tokens on top (a fraction of a credit to a few credits). `estimatedDurationMs` sizes a progress indicator; `company` and `strengths` are model-info copy. All optional: absent means the model has nothing to declare, not that the data is missing.
|
|
556
601
|
|
|
557
602
|
**Tunable engine parameters.** `providerParams` describes what a model exposes for tuning — enough to render a settings panel without hardcoding a thing:
|
|
558
603
|
|
|
@@ -564,6 +609,8 @@ const seed = entity.providerParams?.find((p) => p.key === 'seed');
|
|
|
564
609
|
await generate({ entityId, prompt, providerParams: { seed: 42 } });
|
|
565
610
|
```
|
|
566
611
|
|
|
612
|
+
Nano Banana 2 exposes `seed`, `temperature` (0–2) and `thinking_level` (`'minimal'` default, or `'high'` — the model plans the picture first: better at layouts and directions like "seen from above", slower, and its thinking tokens are billed).
|
|
613
|
+
|
|
567
614
|
**The `category` trap.** A param carrying `category: 'quality'` or `'style'` does NOT belong in `providerParams` — send it on the matching top-level field. Providers read those two from the top level only, and an unrecognized `providerParams` key is dropped without an error, so the image generates at the model's default **and you are billed for it anyway**:
|
|
568
615
|
|
|
569
616
|
```tsx
|
|
@@ -877,7 +924,10 @@ const saved = await userDocs.saveContent({
|
|
|
877
924
|
|
|
878
925
|
### Sending a document for signature — Document Sign handoff
|
|
879
926
|
|
|
880
|
-
**Permission:** `navigation:open_arche
|
|
927
|
+
**Permission:** `navigation:open_arche` to hand the document over. Reading the
|
|
928
|
+
envelope's status afterwards is a separate act (`docsign:envelopes:read`) —
|
|
929
|
+
your app only ever sees envelopes it started this way, and never their
|
|
930
|
+
document bytes.
|
|
881
931
|
|
|
882
932
|
Hand one of the user's Vault PDFs to Document Sign with recipients pre-filled.
|
|
883
933
|
They place the signature fields and press Send there — a handoff never sends
|
|
@@ -921,7 +971,9 @@ created, which is why the outcome is there.
|
|
|
921
971
|
**Treat the result as a pointer, never as a record.** It tells you which
|
|
922
972
|
envelope to look at; it is not evidence of what happened to it. Match it to an
|
|
923
973
|
outstanding request by `requestId`, consume it once, and never re-point an
|
|
924
|
-
existing association because a `correlationId` matched.
|
|
974
|
+
existing association because a `correlationId` matched. Anything you would act
|
|
975
|
+
on — who signed, what is outstanding — comes from reading the envelope under
|
|
976
|
+
`docsign:envelopes:read`, not from the payload.
|
|
925
977
|
|
|
926
978
|
**A handoff ENDS YOUR APP'S PROCESS. Anything you will need afterwards has to
|
|
927
979
|
be written down before you call `openArche`.** The host navigates away and
|
|
@@ -950,6 +1002,65 @@ local dev. Judging on arrival therefore rejects every genuine reply, not an
|
|
|
950
1002
|
occasional one. Hold the result until you know what was outstanding, then
|
|
951
1003
|
evaluate it.
|
|
952
1004
|
|
|
1005
|
+
### `useDocumentSign()` — Follow an envelope you handed over
|
|
1006
|
+
|
|
1007
|
+
**Permission:** `docsign:envelopes:read`
|
|
1008
|
+
|
|
1009
|
+
The other half of the handoff above. Once the user has pressed Send in
|
|
1010
|
+
Document Sign, this is how your app learns what happened — status, who the
|
|
1011
|
+
recipients are, where each of them has got to, and the audit timeline.
|
|
1012
|
+
|
|
1013
|
+
```tsx
|
|
1014
|
+
import { useDocumentSign } from '@fias/arche-sdk';
|
|
1015
|
+
|
|
1016
|
+
const sign = useDocumentSign();
|
|
1017
|
+
|
|
1018
|
+
const envelope = await sign.getEnvelope(approval.envelopeId);
|
|
1019
|
+
if (envelope.status === 'completed') markApproved(approval.id);
|
|
1020
|
+
|
|
1021
|
+
// The audit timeline, oldest first.
|
|
1022
|
+
const { events } = await sign.listEvents(approval.envelopeId);
|
|
1023
|
+
```
|
|
1024
|
+
|
|
1025
|
+
**The contract:**
|
|
1026
|
+
|
|
1027
|
+
- **Only envelopes YOUR app started**, via a `sign_document` handoff. Not one
|
|
1028
|
+
the user created in Document Sign directly, not another app's. Two
|
|
1029
|
+
independent gates, both the user's: they grant access by pressing Send on an
|
|
1030
|
+
envelope you handed over (named to them on the send screen, revocable in
|
|
1031
|
+
Vault → App Access), and the platform separately requires the envelope to
|
|
1032
|
+
record your app as its origin. Either one missing means no read.
|
|
1033
|
+
- **This is where truth comes from.** The `sign_document_result` handoff is a
|
|
1034
|
+
pointer; it rides navigation state, which is forgeable. Write "signed" into
|
|
1035
|
+
an approval record only on the strength of a read.
|
|
1036
|
+
- **No list verb.** Keep the `envelopeId` on your own record and read by id —
|
|
1037
|
+
the same reason the result has to be matched to an outstanding `requestId`
|
|
1038
|
+
and consumed once.
|
|
1039
|
+
- **Drafts are not readable.** Send is the moment the user grants access, so a
|
|
1040
|
+
`saved_draft` result gives you an id to keep, and `getEnvelope` on it rejects
|
|
1041
|
+
until the envelope is actually sent. Do not poll a draft.
|
|
1042
|
+
- **One error for every refusal.** `ENVELOPE_NOT_FOUND_OR_NOT_GRANTED` covers
|
|
1043
|
+
an unknown id, an envelope you did not start, one on a workspace this user
|
|
1044
|
+
cannot act for, a draft, and one whose access the user has removed. Deliberately
|
|
1045
|
+
indistinguishable — a specific "it exists but you cannot see it" would let
|
|
1046
|
+
anyone probe for envelope ids. Treat it as "we can no longer follow this",
|
|
1047
|
+
not as a bug, and stop asking.
|
|
1048
|
+
- **You never see the document.** No bytes, no download URL — and no recipient
|
|
1049
|
+
email addresses either. Name, role, status and timestamps only. If your app
|
|
1050
|
+
supplied the recipients, it already has their addresses.
|
|
1051
|
+
- **The timeline is a projection, not the stored rows.** `type`, `actorKind`,
|
|
1052
|
+
`recipientId`, `createdAt`. Signers' IP addresses and user agents, the event
|
|
1053
|
+
payloads, and the hash-chain columns stay on the platform — they belong to
|
|
1054
|
+
the signing certificate, not to your app.
|
|
1055
|
+
- **There are no webhooks.** Nothing calls you when a recipient signs. Read
|
|
1056
|
+
when your UI needs to know — on mount, on a refresh the user asked for — not
|
|
1057
|
+
on a timer.
|
|
1058
|
+
- Terminal envelopes (`voided`, `expired`, `declined`) stay readable. A
|
|
1059
|
+
terminal outcome is part of the trail you came for; your UI should render it
|
|
1060
|
+
rather than leaving the record stuck on "in progress".
|
|
1061
|
+
- Not available in the builder preview: a preview tenant has no arche row, so
|
|
1062
|
+
it can never be an envelope's origin.
|
|
1063
|
+
|
|
953
1064
|
### `useArcheHandoff()` — Receive a document the user asked to open in your app
|
|
954
1065
|
|
|
955
1066
|
**Permission:** none to receive. Reading the document is a separate act (`vault:user-documents:read`, via `useVaultUserDocuments().getBytes`).
|
|
@@ -1514,6 +1625,7 @@ When you hit a constraint, say so AND tell the user: file an Entity request on t
|
|
|
1514
1625
|
### Size and File Limits
|
|
1515
1626
|
|
|
1516
1627
|
- **Bundle size:** Max 5 MB, measured **uncompressed** — the raw bytes of the final single HTML file with your JS and CSS inlined. Files in `public/` don't count toward it (they have their own caps: 4 MB per file, 120 MB and 600 files total)
|
|
1628
|
+
- **Content packs:** files in `content/` don't count toward the bundle either. If content (lessons, data, media) is what is pushing you toward the cap, move it there — see "Where does each kind of data go?" above. A pack holds up to 10,000 files and 500 MB (20 MB of text, 2,000 images)
|
|
1517
1629
|
- **Dependencies:** Max 20, exact semver versions only (e.g., `"4.4.7"`, not `"^4.4.7"`)
|
|
1518
1630
|
- Platform packages (`react`, `react-dom`, `@fias/arche-sdk`) are provided — do not include in `dependencies`
|
|
1519
1631
|
|
|
@@ -1523,6 +1635,7 @@ When you hit a constraint, say so AND tell the user: file an Entity request on t
|
|
|
1523
1635
|
- `storage_write`: 120/minute
|
|
1524
1636
|
- `storage_read`: 300/minute
|
|
1525
1637
|
- `storage_list`, `storage_delete`: 60/minute
|
|
1638
|
+
- Content reads (`useFiasContent()`): 120/minute — read related files together with `getMany` (up to 100 per call)
|
|
1526
1639
|
|
|
1527
1640
|
A separate **runaway-loop detector** fires if any method is called >50 times in 5 s and blocks that method for 10 s. Errors include the target, e.g. `Runaway loop detected: "storage_write" (path: __state/gameState) …` — the path tells you where to debounce at the source.
|
|
1528
1641
|
|
|
@@ -1612,6 +1725,8 @@ npm run submit
|
|
|
1612
1725
|
# Builds, validates, packages, uploads, submits for AI review
|
|
1613
1726
|
```
|
|
1614
1727
|
|
|
1728
|
+
If the plugin has a `content/` folder, submit uploads it too — only the files that changed — and the review reads the whole pack on every publish. `npx fias-dev content check` validates it without submitting.
|
|
1729
|
+
|
|
1615
1730
|
The first time you list a plugin in the marketplace there is a one-time
|
|
1616
1731
|
listing fee; subsequent submissions of the same plugin only pay a small
|
|
1617
1732
|
per-review fee. The harness fetches the exact amount from the server and
|