@fias/create-fias-plugin 1.14.0 → 1.16.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fias/create-fias-plugin",
3
- "version": "1.14.0",
3
+ "version": "1.16.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -1,4 +1,4 @@
1
- <!-- fias-sdk-guide-version: 2.22.0 -->
1
+ <!-- fias-sdk-guide-version: 2.24.0 -->
2
2
 
3
3
  # FIAS Plugin Development Guide
4
4
 
@@ -551,6 +551,8 @@ function ModelPicker({ value, onChange }: { value: string; onChange: (id: string
551
551
 
552
552
  Generate images using platform image models. Images are saved to the user's Fias file system and a display URL is returned.
553
553
 
554
+ Most images take 10–60 seconds; high quality at large sizes (the GPT Image models especially) can take several minutes, so `generate` waits up to 320 seconds before rejecting. Show progress for the whole wait (`estimatedDurationMs` from `useImageEntities` sizes it), and do not race `generate` against a shorter timeout of your own — the platform is still rendering, and bills, an image your plugin stopped waiting for.
555
+
554
556
  #### Choosing an image model
555
557
 
556
558
  The `entityId` parameter accepts three forms. **Prefer the selector forms** (`platformRecommended` / `providerRecommended`) over hardcoded entity IDs: the platform's recommendations evolve, models get retired, and a selector lets your plugin pick up the current best automatically without a code edit. Hardcoded IDs are still supported for back-compat and for cases where you want a specific model — but if the platform retires that exact entity, your plugin breaks unless the platform has registered a successor.
@@ -583,7 +585,7 @@ generate({
583
585
 
584
586
  Mode values: `'text-to-image'` (baseline), `'image-to-image'` (requires `referenceImage`), `'vector'` (SVG output). Style values: `'illustration'` (stylized / digital-art) or `'photoreal'`.
585
587
 
586
- **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`):
588
+ **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, the GPT Image models up to 16. 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`):
587
589
 
588
590
  ```tsx
589
591
  generate({
@@ -670,13 +672,14 @@ const { entities, isLoading } = useImageEntities({
670
672
  // entities[i]: { entityId, displayName, provider, modes, supportedSizes,
671
673
  // supportsReferenceImage, maxReferenceImages,
672
674
  // supportedResolutions, defaultResolution, isPlatformRecommended,
673
- // creditsPerImage, creditsPerImageByResolution,
675
+ // pricedBy, pixelSizes, defaultQuality,
676
+ // creditsPerImage, creditsPerImageByResolution, creditsPerImageByTier,
674
677
  // estimatedDurationMs, company, strengths, providerParams, ... }
675
678
  ```
676
679
 
677
680
  Each entry is scrubbed — only fields a UI legitimately needs (no provider model strings, no endpoints, no execution config).
678
681
 
679
- `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.
682
+ `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). The GPT Image models are `pricedBy: 'quality-and-size'`: `creditsPerImage` is the price at `defaultQuality`, the first `supportedSizes` entry and `defaultResolution`, and `creditsPerImageByTier` has every other, keyed `'<quality>@<WIDTHxHEIGHT>'` — find the pixel size in `pixelSizes` (`'16:9/2K'` → `'2048x1152'`), so a request's price is `creditsPerImageByTier[quality + '@' + pixelSizes[size + '/' + resolution]]`. Their prompt and reference images are billed as tokens on top. `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.
680
683
 
681
684
  **Tunable engine parameters.** `providerParams` describes what a model exposes for tuning — enough to render a settings panel without hardcoding a thing:
682
685
 
@@ -688,7 +691,7 @@ const seed = entity.providerParams?.find((p) => p.key === 'seed');
688
691
  await generate({ entityId, prompt, providerParams: { seed: 42 } });
689
692
  ```
690
693
 
691
- 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).
694
+ 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). The GPT Image models expose `output_format` (`'png'` default, `'jpeg'`, `'webp'`) and `output_compression` (0–100, JPEG and WebP only); GPT Image 1.5 and the GPT Image 2.5 models also take `background` (`'auto'`, `'opaque'`, `'transparent'` — transparent needs PNG or WebP).
692
695
 
693
696
  **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**:
694
697
 
@@ -1308,6 +1311,36 @@ Three things to design around:
1308
1311
 
1309
1312
  Only images the user made or uploaded in **their own** Fias files can be published (pass the `fileId` from a `useImageGeneration()` result). PNG/JPEG/WebP only; every image is re-encoded to a canonical PNG with metadata stripped. Publishers can `unpublish()` their own assets, and any user can `report()` one for review.
1310
1313
 
1314
+ ### `useCompanyEmail()` — Send email as the Company the app is used in
1315
+
1316
+ **Permission:** `email:company:send`
1317
+ **Returns:** `CompanyEmailApi`
1318
+
1319
+ For apps built for an approved partner Company: send email **from the Company's own verified domain** (e.g. `team@example.com`), not from Fias. Declaring the permission is not enough — a Company admin must also authorize your app to send, and chooses who it may email: members of the workspace it is used in, members of the whole Company, or anyone. Until then every call is refused with `COMPANY_EMAIL_NOT_AUTHORIZED`.
1320
+
1321
+ ```tsx
1322
+ import { useCompanyEmail } from '@fias/arche-sdk';
1323
+
1324
+ const email = useCompanyEmail();
1325
+ const { results, sent } = await email.send({
1326
+ requestKey: `digest-${weekId}`, // reuse on retry — never double-sends
1327
+ to: members.map((m) => ({ userId: m.userId })),
1328
+ subject: 'Your weekly digest',
1329
+ text: digestText, // required
1330
+ html: digestHtml, // optional, sanitized
1331
+ });
1332
+ // results[i]: { index, status: 'accepted' | 'unknown' | 'failed' | 'muted' | 'not_sent' }
1333
+ ```
1334
+
1335
+ - **You never choose the From address** — it is the address the Company assigned your app. You choose recipients and content.
1336
+ - **Name members by `userId`**; you never see their addresses. Raw `{ email }` recipients work only if the Company let your app email anyone. One recipient outside what the Company allowed refuses the whole call (without saying which).
1337
+ - **The user must be working in one of the Company's workspaces, with edit access.** Each recipient is charged to that user, like any paid call; up to 50 recipients per call, and a daily limit per app per Company.
1338
+ - **Retry with the same `requestKey`.** A recipient already sent under that key is not sent again. `unknown` means it may have gone out — do not resend it under a new key.
1339
+ - **HTML is sanitized** to the email allow-list: tables, inline presentational styles, `https` images and `https`/`mailto` links are kept; `<script>`, `<form>`, `<iframe>` and similar refuse the send (`EMAIL_CONTENT_REJECTED`).
1340
+ - **Attachments:** up to 5 files, 5 MB total, and only `.pdf`, `.png`, `.jpg`/`.jpeg`, `.gif`, `.csv`, `.txt`, `.ics`, `.docx`, `.xlsx`. `contentType` must match the extension (e.g. `application/pdf`), and the bytes must be that format; anything else refuses the send (`EMAIL_CONTENT_REJECTED`).
1341
+ - **Every email carries one-click unsubscribe.** Recipients who used it come back as `muted`; the Company may also show a "Stop emails from this arche" line, which is always shown for `kind: 'marketing'`.
1342
+ - Not available in the builder preview or the local dev harness — it sends real email. Other refusals: `WORKSPACE_REQUIRED`, `RECIPIENT_NOT_ALLOWED`, `COMPANY_EMAIL_UNAVAILABLE` (the Company's domain is not ready or paused), `DAILY_EMAIL_LIMIT_REACHED`, `INSUFFICIENT_CREDITS`.
1343
+
1311
1344
  ### `useFiasStore()` — In-app purchases (IAP)
1312
1345
 
1313
1346
  **Permission:** `store:purchase`
@@ -1405,7 +1438,23 @@ the user moves the host themselves with browser back/forward. So a router
1405
1438
  driven off `currentPath` stays in step with the address bar, and the back
1406
1439
  button works the way your users expect — you do not have to mirror the path in
1407
1440
  your own state. The dev harness echoes navigations the same way, so what you
1408
- see locally is what ships.
1441
+ see locally is what ships. To test a deep link locally, open the harness with
1442
+ `?path=/map` (a root-relative path in your route space) and `currentPath`
1443
+ starts there instead of at `/`.
1444
+
1445
+ **Correcting the URL.** Every `navigateTo` pushes a history entry by default,
1446
+ which is right for navigation the user asked for. When you are CORRECTING the
1447
+ address bar instead — normalizing a link you were opened at (`/Map/` →
1448
+ `/map`), or putting it back after refusing a route — pass `{ replace: true }`
1449
+ so Back does not land on the URL you just corrected:
1450
+
1451
+ ```tsx
1452
+ navigateTo('/map', { replace: true });
1453
+ ```
1454
+
1455
+ A host older than this option ignores it and pushes. If you correct paths
1456
+ automatically as they arrive, never repeat the same correction twice in a
1457
+ row, or a push-only host can trap the Back button between two URLs.
1409
1458
 
1410
1459
  ### Opening external links
1411
1460
 
@@ -1,4 +1,4 @@
1
- <!-- fias-sdk-guide-version: 2.22.0 -->
1
+ <!-- fias-sdk-guide-version: 2.24.0 -->
2
2
 
3
3
  # FIAS Plugin Development Guide
4
4
 
@@ -551,6 +551,8 @@ function ModelPicker({ value, onChange }: { value: string; onChange: (id: string
551
551
 
552
552
  Generate images using platform image models. Images are saved to the user's Fias file system and a display URL is returned.
553
553
 
554
+ Most images take 10–60 seconds; high quality at large sizes (the GPT Image models especially) can take several minutes, so `generate` waits up to 320 seconds before rejecting. Show progress for the whole wait (`estimatedDurationMs` from `useImageEntities` sizes it), and do not race `generate` against a shorter timeout of your own — the platform is still rendering, and bills, an image your plugin stopped waiting for.
555
+
554
556
  #### Choosing an image model
555
557
 
556
558
  The `entityId` parameter accepts three forms. **Prefer the selector forms** (`platformRecommended` / `providerRecommended`) over hardcoded entity IDs: the platform's recommendations evolve, models get retired, and a selector lets your plugin pick up the current best automatically without a code edit. Hardcoded IDs are still supported for back-compat and for cases where you want a specific model — but if the platform retires that exact entity, your plugin breaks unless the platform has registered a successor.
@@ -583,7 +585,7 @@ generate({
583
585
 
584
586
  Mode values: `'text-to-image'` (baseline), `'image-to-image'` (requires `referenceImage`), `'vector'` (SVG output). Style values: `'illustration'` (stylized / digital-art) or `'photoreal'`.
585
587
 
586
- **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`):
588
+ **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, the GPT Image models up to 16. 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`):
587
589
 
588
590
  ```tsx
589
591
  generate({
@@ -670,13 +672,14 @@ const { entities, isLoading } = useImageEntities({
670
672
  // entities[i]: { entityId, displayName, provider, modes, supportedSizes,
671
673
  // supportsReferenceImage, maxReferenceImages,
672
674
  // supportedResolutions, defaultResolution, isPlatformRecommended,
673
- // creditsPerImage, creditsPerImageByResolution,
675
+ // pricedBy, pixelSizes, defaultQuality,
676
+ // creditsPerImage, creditsPerImageByResolution, creditsPerImageByTier,
674
677
  // estimatedDurationMs, company, strengths, providerParams, ... }
675
678
  ```
676
679
 
677
680
  Each entry is scrubbed — only fields a UI legitimately needs (no provider model strings, no endpoints, no execution config).
678
681
 
679
- `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.
682
+ `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). The GPT Image models are `pricedBy: 'quality-and-size'`: `creditsPerImage` is the price at `defaultQuality`, the first `supportedSizes` entry and `defaultResolution`, and `creditsPerImageByTier` has every other, keyed `'<quality>@<WIDTHxHEIGHT>'` — find the pixel size in `pixelSizes` (`'16:9/2K'` → `'2048x1152'`), so a request's price is `creditsPerImageByTier[quality + '@' + pixelSizes[size + '/' + resolution]]`. Their prompt and reference images are billed as tokens on top. `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.
680
683
 
681
684
  **Tunable engine parameters.** `providerParams` describes what a model exposes for tuning — enough to render a settings panel without hardcoding a thing:
682
685
 
@@ -688,7 +691,7 @@ const seed = entity.providerParams?.find((p) => p.key === 'seed');
688
691
  await generate({ entityId, prompt, providerParams: { seed: 42 } });
689
692
  ```
690
693
 
691
- 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).
694
+ 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). The GPT Image models expose `output_format` (`'png'` default, `'jpeg'`, `'webp'`) and `output_compression` (0–100, JPEG and WebP only); GPT Image 1.5 and the GPT Image 2.5 models also take `background` (`'auto'`, `'opaque'`, `'transparent'` — transparent needs PNG or WebP).
692
695
 
693
696
  **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**:
694
697
 
@@ -1308,6 +1311,36 @@ Three things to design around:
1308
1311
 
1309
1312
  Only images the user made or uploaded in **their own** Fias files can be published (pass the `fileId` from a `useImageGeneration()` result). PNG/JPEG/WebP only; every image is re-encoded to a canonical PNG with metadata stripped. Publishers can `unpublish()` their own assets, and any user can `report()` one for review.
1310
1313
 
1314
+ ### `useCompanyEmail()` — Send email as the Company the app is used in
1315
+
1316
+ **Permission:** `email:company:send`
1317
+ **Returns:** `CompanyEmailApi`
1318
+
1319
+ For apps built for an approved partner Company: send email **from the Company's own verified domain** (e.g. `team@example.com`), not from Fias. Declaring the permission is not enough — a Company admin must also authorize your app to send, and chooses who it may email: members of the workspace it is used in, members of the whole Company, or anyone. Until then every call is refused with `COMPANY_EMAIL_NOT_AUTHORIZED`.
1320
+
1321
+ ```tsx
1322
+ import { useCompanyEmail } from '@fias/arche-sdk';
1323
+
1324
+ const email = useCompanyEmail();
1325
+ const { results, sent } = await email.send({
1326
+ requestKey: `digest-${weekId}`, // reuse on retry — never double-sends
1327
+ to: members.map((m) => ({ userId: m.userId })),
1328
+ subject: 'Your weekly digest',
1329
+ text: digestText, // required
1330
+ html: digestHtml, // optional, sanitized
1331
+ });
1332
+ // results[i]: { index, status: 'accepted' | 'unknown' | 'failed' | 'muted' | 'not_sent' }
1333
+ ```
1334
+
1335
+ - **You never choose the From address** — it is the address the Company assigned your app. You choose recipients and content.
1336
+ - **Name members by `userId`**; you never see their addresses. Raw `{ email }` recipients work only if the Company let your app email anyone. One recipient outside what the Company allowed refuses the whole call (without saying which).
1337
+ - **The user must be working in one of the Company's workspaces, with edit access.** Each recipient is charged to that user, like any paid call; up to 50 recipients per call, and a daily limit per app per Company.
1338
+ - **Retry with the same `requestKey`.** A recipient already sent under that key is not sent again. `unknown` means it may have gone out — do not resend it under a new key.
1339
+ - **HTML is sanitized** to the email allow-list: tables, inline presentational styles, `https` images and `https`/`mailto` links are kept; `<script>`, `<form>`, `<iframe>` and similar refuse the send (`EMAIL_CONTENT_REJECTED`).
1340
+ - **Attachments:** up to 5 files, 5 MB total, and only `.pdf`, `.png`, `.jpg`/`.jpeg`, `.gif`, `.csv`, `.txt`, `.ics`, `.docx`, `.xlsx`. `contentType` must match the extension (e.g. `application/pdf`), and the bytes must be that format; anything else refuses the send (`EMAIL_CONTENT_REJECTED`).
1341
+ - **Every email carries one-click unsubscribe.** Recipients who used it come back as `muted`; the Company may also show a "Stop emails from this arche" line, which is always shown for `kind: 'marketing'`.
1342
+ - Not available in the builder preview or the local dev harness — it sends real email. Other refusals: `WORKSPACE_REQUIRED`, `RECIPIENT_NOT_ALLOWED`, `COMPANY_EMAIL_UNAVAILABLE` (the Company's domain is not ready or paused), `DAILY_EMAIL_LIMIT_REACHED`, `INSUFFICIENT_CREDITS`.
1343
+
1311
1344
  ### `useFiasStore()` — In-app purchases (IAP)
1312
1345
 
1313
1346
  **Permission:** `store:purchase`
@@ -1405,7 +1438,23 @@ the user moves the host themselves with browser back/forward. So a router
1405
1438
  driven off `currentPath` stays in step with the address bar, and the back
1406
1439
  button works the way your users expect — you do not have to mirror the path in
1407
1440
  your own state. The dev harness echoes navigations the same way, so what you
1408
- see locally is what ships.
1441
+ see locally is what ships. To test a deep link locally, open the harness with
1442
+ `?path=/map` (a root-relative path in your route space) and `currentPath`
1443
+ starts there instead of at `/`.
1444
+
1445
+ **Correcting the URL.** Every `navigateTo` pushes a history entry by default,
1446
+ which is right for navigation the user asked for. When you are CORRECTING the
1447
+ address bar instead — normalizing a link you were opened at (`/Map/` →
1448
+ `/map`), or putting it back after refusing a route — pass `{ replace: true }`
1449
+ so Back does not land on the URL you just corrected:
1450
+
1451
+ ```tsx
1452
+ navigateTo('/map', { replace: true });
1453
+ ```
1454
+
1455
+ A host older than this option ignores it and pushes. If you correct paths
1456
+ automatically as they arrive, never repeat the same correction twice in a
1457
+ row, or a push-only host can trap the Back button between two URLs.
1409
1458
 
1410
1459
  ### Opening external links
1411
1460