@civitai/app-sdk 0.74.0 → 0.76.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/README.md +10 -1
- package/dist/blocks/index.d.ts +1 -1
- package/dist/blocks/messages.d.ts +37 -2
- package/dist/blocks/messages.js +1 -0
- package/dist/blocks/types.d.ts +53 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/orchestrator/index.d.ts +4 -1
- package/dist/orchestrator/index.js +4 -1
- package/dist/vite/index.d.ts +29 -0
- package/dist/vite/index.js +81 -17
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -32,7 +32,7 @@ pnpm add @civitai/app-sdk
|
|
|
32
32
|
| `orchestrator/steps` — `WorkflowStepTemplates`, `WorkflowStepTemplateFor`, `WorkflowStepInputFor`, `AnyWorkflowStepTemplate`, `TypedWorkflowTemplate` | **Type-only** subpath (0 runtime bytes) keying the orchestrator's generated workflow-step shapes from `@civitai/client` by wire `$type`, so apps compose real step bodies against types that track the spec instead of hand-maintained copies. Requires the optional peer `@civitai/client@beta`. Type surface only — it grants no submit permission; see "Typed step shapes" below. |
|
|
33
33
|
| `blocks/*` — `BlockManifestError`, `BLOCK_SCOPES`, `BLOCK_SCOPE_PATTERN`, `isMessage`, types (`BlockManifestV1`, `BlockContext`, `BlockToken`, `BlockSettings`, `ViewerInfo`, `ThemeInfo`, `BlockWorkflowSnapshot`, `BlockInitPayload`, `ParentToBlockMessage`, `BlockToParentMessage`, …) | Framework-agnostic contract for [Civitai Apps](https://github.com/civitai/civitai-app-starters/blob/main/docs/build-your-first-app-block.md). Ships a byte-identical copy of the server-published canonical JSON Schema (draft 2020-12, https://civitai.com/schemas/app-block/v1.json) at the `./schemas/app-block/v1.json` subpath for offline validation; a CI drift-check keeps it in sync. Runtime-agnostic and **zero runtime dependencies** — no React, no DOM types, nothing in your install graph. Hooks and the iframe transport live in a separate package. |
|
|
34
34
|
| `manifest/*` — `defineBlock`, `SCHEMA_DIVERGENCES`, `KNOWN_GAPS` (**Node only**) | Build-time manifest validation. `defineBlock(config)` compiles the vendored canonical schema with [Ajv](https://ajv.js.org) and validates a `BlockManifestV1` against it, so authoring mistakes surface in `pnpm dev` instead of at `civitai app validate`/submit. It is **derived from the schema, not a hand-written mirror of it** — that mirror is what produced [#330](https://github.com/civitai/civitai-app-starters/issues/330). Needs `node:fs` and the optional peer `ajv`, which is why it is not on the browser-facing `./blocks` surface. |
|
|
35
|
-
| `vite/*` — `blockManifestPlugin` (**Node only**) | A Vite plugin wrapping `defineBlock`, firing from `configResolved` — the one hook Vite calls on both the dev-server and the build path. Every block scaffold registers it, so `pnpm dev`, `pnpm dev:harness` and `pnpm build` all fail on a bad manifest with the offending field path. Optional peers: `ajv` (runtime) and `vite` (types only). |
|
|
35
|
+
| `vite/*` — `blockManifestPlugin` (**Node only**) | A Vite plugin wrapping `defineBlock`, firing from `configResolved` — the one hook Vite calls on both the dev-server and the build path. Every block scaffold registers it, so `pnpm dev`, `pnpm dev:harness` and `pnpm build` all fail on a bad manifest with the offending field path. On the dev server only, it also exposes the manifest's `scopes` + `storage` to `@civitai/sdk` for a dev-tunnel local preview. Optional peers: `ajv` (runtime) and `vite` (types only). |
|
|
36
36
|
|
|
37
37
|
## Entry points, and what the root barrel is
|
|
38
38
|
|
|
@@ -407,6 +407,15 @@ import { blockManifestPlugin } from '@civitai/app-sdk/vite';
|
|
|
407
407
|
const plugins = [blockManifestPlugin()];
|
|
408
408
|
```
|
|
409
409
|
|
|
410
|
+
**On the dev server only**, the plugin also puts the manifest's `scopes` and
|
|
411
|
+
`storage` on the page (a `<script>` at the end of `index.html`'s `<head>`; `vite build` never
|
|
412
|
+
emits it). `@civitai/sdk` reads it in a dev-tunnel preview, where the host marks the
|
|
413
|
+
frame `storageMode: 'local-preview'` and `app.storage.collection(name)` is served by
|
|
414
|
+
an in-memory store that enforces these declarations — see "In a dev-tunnel preview"
|
|
415
|
+
in `@civitai/sdk`'s README. Nothing else of the manifest is exposed. Without the
|
|
416
|
+
plugin, a preview's document store refuses every call `local_preview_no_manifest`:
|
|
417
|
+
this plugin is the only way the manifest reaches `@civitai/sdk`.
|
|
418
|
+
|
|
410
419
|
### Web storage in a block (`@civitai/app-sdk/safe-storage`)
|
|
411
420
|
|
|
412
421
|
Block iframes are sandboxed **without `allow-same-origin`**, so the document has
|
package/dist/blocks/index.d.ts
CHANGED
|
@@ -116,7 +116,7 @@ export { isModelSlotContext, isPageSlotContext } from './types.js';
|
|
|
116
116
|
* in `types.ts` for which of the two it uses and why.
|
|
117
117
|
*/
|
|
118
118
|
export { isSignedIn } from './types.js';
|
|
119
|
-
export type { BlockContext, KnownSlotId, ModelSlotId, PageSlotContext, PageSlotId, UnknownSlotContext, BlockAnalyticsDeclaration, BlockAnalyticsEventDeclaration, BlockAnalyticsPropertyDeclaration, BlockManifest, BlockManifestGood, BlockManifestV1, BlockSettings, BlockStorageCollectionDeclaration, BlockStorageDeclaration, BlockStorageIndexes, BlockStorageIndexType, BlockStoragePrivateCollectionDeclaration, BlockStorageSharedCollectionDeclaration, BlockToken, ContentRating, ManifestBooleanField, ManifestIframe, ManifestNumberField, ManifestPage, ManifestPreview, ManifestSettingField, ManifestSettings, ManifestStringField, ManifestTarget, ModelSlotContext, SettingScope, SettingWidget, Theme, ViewerInfo, BlockCheckpointInfo, BlockResourceInfo, BlockResourcePickerType, BlockSourceImage, BlockUploadedImageInfo, BlockGenerationSourceImageInfo, BlockImagePickerSource, BlockPickedImageInfo, BlockPendingImageInfo, BlockImageScanResult, BlockUploadPurpose, BlockNavigateScope, BlockTextToImageParams, BlockWorkflowSnapshot, BlockEstimateBatchAggregate, BlockModelSubstitution, ModelSubstitutionReason, KnownBlockWorkflowErrorCode, BlockStepToolCall, BlockTrainedEpoch, BlockPublishedModel, BuzzAccountType, ShowcaseImage, WorkflowBody, WorkflowBodyTextToImage, WorkflowBodyCustomComfy, WorkflowBodyCustomComfyRecipe, WorkflowBodyCustomComfyInline, InlineComfyNode, WorkflowBodyStep, WorkflowBodyPassThroughStep, WorkflowBodyWorkflow, WorkflowBodyWorkflowStep, WorkflowStepRef, WithWorkflowStepRefs, BlockWorkflowStepSnapshot, WorkflowStatus, BlockBuzzTransaction, BlockBuzzAccount, BlockDailyCompensationResource, BlockViewer, BlockWildcardPack, BlockWildcardPackErrorCode, AppWorkflow, AppWorkflowImage, BlockGatedImage, BlockCollectionFollowErrorCode, BlockCollectionFollowResult, BlockPostSource, BlockCreatePostRequest, BlockCreatePostResult, BlockCreatePostHostError, WorkflowBodyTraining, AiToolkitTrainingParams, AiToolkitTrainingParamsBase, AiToolkitPlainEcosystem, AiToolkitLrScheduler, AiToolkitOptimizerType, BlockTrainingQuote, BlockTrainingDatasetItem, BlockTrainingDatasetResult, BlockTrainingRejectionReason, BlockPrepareTrainingDatasetHostError, BlockRunTrainingHostError, BlockStepQuote, BlockModel3dOutput, BlockRunConfirmedStepHostError, } from './types.js';
|
|
119
|
+
export type { BlockContext, KnownSlotId, ModelSlotId, PageSlotContext, PageSlotId, UnknownSlotContext, BlockAnalyticsDeclaration, BlockAnalyticsEventDeclaration, BlockAnalyticsPropertyDeclaration, BlockManifest, BlockManifestGood, BlockManifestV1, BlockSettings, BlockStorageCollectionDeclaration, BlockStorageDeclaration, BlockStorageIndexes, BlockStorageIndexType, BlockStoragePrivateCollectionDeclaration, BlockStorageSharedCollectionDeclaration, BlockToken, ContentRating, ManifestBooleanField, ManifestIframe, ManifestNumberField, ManifestPage, ManifestPreview, ManifestSettingField, ManifestSettings, ManifestStringField, ManifestTarget, ModelSlotContext, SettingScope, SettingWidget, Theme, ViewerInfo, BlockCheckpointInfo, BlockResourceInfo, BlockResourcePickerType, BlockSourceImage, BlockUploadedImageInfo, BlockGenerationSourceImageInfo, BlockImagePickerSource, BlockPickedImageInfo, BlockMediaKind, BlockMediaUploadInfo, BlockPendingImageInfo, BlockImageScanResult, BlockUploadPurpose, BlockNavigateScope, BlockTextToImageParams, BlockWorkflowSnapshot, BlockEstimateBatchAggregate, BlockModelSubstitution, ModelSubstitutionReason, KnownBlockWorkflowErrorCode, BlockStepToolCall, BlockTrainedEpoch, BlockPublishedModel, BuzzAccountType, ShowcaseImage, WorkflowBody, WorkflowBodyTextToImage, WorkflowBodyCustomComfy, WorkflowBodyCustomComfyRecipe, WorkflowBodyCustomComfyInline, InlineComfyNode, WorkflowBodyStep, WorkflowBodyPassThroughStep, WorkflowBodyWorkflow, WorkflowBodyWorkflowStep, WorkflowStepRef, WithWorkflowStepRefs, BlockWorkflowStepSnapshot, WorkflowStatus, BlockBuzzTransaction, BlockBuzzAccount, BlockDailyCompensationResource, BlockViewer, BlockWildcardPack, BlockWildcardPackErrorCode, AppWorkflow, AppWorkflowImage, BlockGatedImage, BlockCollectionFollowErrorCode, BlockCollectionFollowResult, BlockPostSource, BlockCreatePostRequest, BlockCreatePostResult, BlockCreatePostHostError, WorkflowBodyTraining, AiToolkitTrainingParams, AiToolkitTrainingParamsBase, AiToolkitPlainEcosystem, AiToolkitLrScheduler, AiToolkitOptimizerType, BlockTrainingQuote, BlockTrainingDatasetItem, BlockTrainingDatasetResult, BlockTrainingRejectionReason, BlockPrepareTrainingDatasetHostError, BlockRunTrainingHostError, BlockStepQuote, BlockModel3dOutput, BlockRunConfirmedStepHostError, } from './types.js';
|
|
120
120
|
/** Host bound for the App Blocks `kind: 'workflow'` body — see `WorkflowBodyWorkflow`. */
|
|
121
121
|
export { BLOCK_WORKFLOW_MAX_STEPS, BLOCK_WORKFLOW_REPEAT_MAX_ITERATIONS } from './types.js';
|
|
122
122
|
/**
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* Wire format: `window.postMessage({ type, payload }, targetOrigin)`.
|
|
9
9
|
*/
|
|
10
10
|
import type { ColorDomain } from './browsingLevel.js';
|
|
11
|
-
import type { BlockCheckpointInfo, BlockResourceInfo, BlockResourcePickerType, BlockUploadedImageInfo, BlockGenerationSourceImageInfo, BlockImagePickerSource, BlockPickedImageInfo, BlockPendingImageInfo, BlockImageScanResult, BlockUploadPurpose, BlockNavigateScope, BlockContext, BlockSettings, Theme, ViewerInfo, WorkflowBody, BlockWorkflowSnapshot, BlockEstimateBatchAggregate, BlockBuzzTransaction, BlockBuzzAccount, BlockDailyCompensationResource, BlockViewer, BlockWildcardPack, BlockWildcardPackErrorCode, AppWorkflow, BlockGatedImage, BlockCollectionFollowErrorCode, BlockCollectionFollowResult, BlockPostSource, BlockCreatePostResult, BlockTrainingDatasetItem, BlockTrainingDatasetResult, WorkflowBodyTraining, WorkflowBodyPassThroughStep } from './types.js';
|
|
11
|
+
import type { BlockCheckpointInfo, BlockResourceInfo, BlockResourcePickerType, BlockUploadedImageInfo, BlockGenerationSourceImageInfo, BlockImagePickerSource, BlockPickedImageInfo, BlockMediaKind, BlockMediaUploadInfo, BlockPendingImageInfo, BlockImageScanResult, BlockUploadPurpose, BlockNavigateScope, BlockContext, BlockSettings, Theme, ViewerInfo, WorkflowBody, BlockWorkflowSnapshot, BlockEstimateBatchAggregate, BlockBuzzTransaction, BlockBuzzAccount, BlockDailyCompensationResource, BlockViewer, BlockWildcardPack, BlockWildcardPackErrorCode, AppWorkflow, BlockGatedImage, BlockCollectionFollowErrorCode, BlockCollectionFollowResult, BlockPostSource, BlockCreatePostResult, BlockTrainingDatasetItem, BlockTrainingDatasetResult, WorkflowBodyTraining, WorkflowBodyPassThroughStep } from './types.js';
|
|
12
12
|
import type { BlockAppReaction, BlockEntityReactionErrorCode, BlockEntityReactionResult, BlockEntityReactionsResult, BlockReactionEntityType } from './reactions.js';
|
|
13
13
|
/**
|
|
14
14
|
* Filter params for `GET_BUZZ_TRANSACTIONS`. All optional; the host validates
|
|
@@ -218,6 +218,21 @@ export interface BlockInitPayload {
|
|
|
218
218
|
* them — ship without React.
|
|
219
219
|
*/
|
|
220
220
|
effectiveBrowsingLevel?: number;
|
|
221
|
+
/**
|
|
222
|
+
* `'local-preview'` when the host mounted this frame as a DEV-TUNNEL preview,
|
|
223
|
+
* which has no server-side storage namespace. The host sets it from the
|
|
224
|
+
* surface it mounted — never from the token's scopes, and never from anything
|
|
225
|
+
* the block sent. Absent on every other surface.
|
|
226
|
+
*
|
|
227
|
+
* `@civitai/sdk`'s `initialize()` reads it once and serves
|
|
228
|
+
* `app.storage.collection(name)` from an in-memory store in the tab
|
|
229
|
+
* (`app.storage.mode === 'local-preview'`). Nothing in this package or in
|
|
230
|
+
* `@civitai/blocks-react` acts on it: their storage hooks keep the real routes.
|
|
231
|
+
*
|
|
232
|
+
* ADDITIVE + OPTIONAL. A host that predates it omits the field; a value other
|
|
233
|
+
* than the exact string must be treated as absent.
|
|
234
|
+
*/
|
|
235
|
+
storageMode?: 'local-preview';
|
|
221
236
|
}
|
|
222
237
|
/**
|
|
223
238
|
* The freeform value carried by one SHARED-storage entry. Unlike the per-user
|
|
@@ -592,6 +607,13 @@ export type ParentToBlockMessage = {
|
|
|
592
607
|
selectedImages?: BlockPickedImageInfo[];
|
|
593
608
|
error?: string;
|
|
594
609
|
};
|
|
610
|
+
} | {
|
|
611
|
+
type: 'MEDIA_UPLOAD_RESULT';
|
|
612
|
+
payload: {
|
|
613
|
+
requestId: string;
|
|
614
|
+
selected?: BlockMediaUploadInfo;
|
|
615
|
+
error?: string;
|
|
616
|
+
};
|
|
595
617
|
} | {
|
|
596
618
|
type: 'USER_CHECKPOINT_SET';
|
|
597
619
|
payload: {
|
|
@@ -973,6 +995,19 @@ export type BlockToParentMessage = {
|
|
|
973
995
|
*/
|
|
974
996
|
publicOnly?: boolean;
|
|
975
997
|
};
|
|
998
|
+
} | {
|
|
999
|
+
type: 'OPEN_MEDIA_UPLOAD';
|
|
1000
|
+
payload: {
|
|
1001
|
+
requestId: string;
|
|
1002
|
+
/** What kind of media to upload. The host refuses any other value. */
|
|
1003
|
+
kind: BlockMediaKind;
|
|
1004
|
+
/** Required. The only accepted value today. */
|
|
1005
|
+
purpose: 'generationSource';
|
|
1006
|
+
/** In-tab bytes: present = bytes mode (no picker), absent = picker mode. */
|
|
1007
|
+
bytes?: ArrayBuffer;
|
|
1008
|
+
/** Bytes mode only: the type the app believes it is sending; must match the content. */
|
|
1009
|
+
mimeType?: string;
|
|
1010
|
+
};
|
|
976
1011
|
} | {
|
|
977
1012
|
type: 'SET_USER_CHECKPOINT';
|
|
978
1013
|
payload: {
|
|
@@ -1194,7 +1229,7 @@ export type BlockToParentMessageType = BlockToParentMessage['type'];
|
|
|
1194
1229
|
* it is a measurement, not an invariant, and civitai's own compile-time gate
|
|
1195
1230
|
* (published SDK union ⊆ `keyof INVENTORY`) is what holds one direction of it.
|
|
1196
1231
|
*/
|
|
1197
|
-
export declare const BLOCK_TO_PARENT_MESSAGE_TYPES: readonly ["BLOCK_HELLO", "BLOCK_READY", "BLOCK_ERROR", "BLOCK_MESSAGE_REJECTED", "REQUEST_TOKEN", "RESIZE_IFRAME", "SUBMIT_WORKFLOW", "ESTIMATE_WORKFLOW", "ESTIMATE_WORKFLOW_BATCH", "POLL_WORKFLOW", "CANCEL_WORKFLOW", "OPEN_BUZZ_PURCHASE", "GET_BUZZ_BALANCE", "GET_VIEWER", "GET_BUZZ_TRANSACTIONS", "GET_BUZZ_ACCOUNTS", "GET_DAILY_COMPENSATION", "GET_WILDCARD_PACK", "QUERY_APP_WORKFLOWS", "PUBLISH_GENERATION_OUTPUTS", "CREATE_POST_FROM_APP", "PREPARE_TRAINING_DATASET", "RUN_TRAINING", "RUN_CONFIRMED_STEP", "GET_IMAGES_BY_IDS", "CANCEL_APP_WORKFLOW", "OPEN_CHECKPOINT_PICKER", "OPEN_RESOURCE_PICKER", "OPEN_IMAGE_UPLOAD", "OPEN_IMAGE_PICKER", "SET_USER_CHECKPOINT", "NAVIGATE", "REQUEST_SIGN_IN", "REQUEST_CONSENT", "TRACK_EVENT", "APP_STORAGE_GET", "APP_STORAGE_SET", "APP_STORAGE_DELETE", "APP_STORAGE_LIST", "APP_STORAGE_QUOTA", "SHARED_LIST", "SHARED_GET_COUNT", "SHARED_GET_COUNTS", "SHARED_APPEND", "SHARED_VOTE", "SHARED_UNVOTE", "SHARED_WITHDRAW", "SHARED_UPDATE", "SHARED_GET", "SHARED_REPORT", "SAVE_IMAGE", "SET_COLLECTION_FOLLOW", "SET_ENTITY_REACTION", "GET_ENTITY_REACTIONS"];
|
|
1232
|
+
export declare const BLOCK_TO_PARENT_MESSAGE_TYPES: readonly ["BLOCK_HELLO", "BLOCK_READY", "BLOCK_ERROR", "BLOCK_MESSAGE_REJECTED", "REQUEST_TOKEN", "RESIZE_IFRAME", "SUBMIT_WORKFLOW", "ESTIMATE_WORKFLOW", "ESTIMATE_WORKFLOW_BATCH", "POLL_WORKFLOW", "CANCEL_WORKFLOW", "OPEN_BUZZ_PURCHASE", "GET_BUZZ_BALANCE", "GET_VIEWER", "GET_BUZZ_TRANSACTIONS", "GET_BUZZ_ACCOUNTS", "GET_DAILY_COMPENSATION", "GET_WILDCARD_PACK", "QUERY_APP_WORKFLOWS", "PUBLISH_GENERATION_OUTPUTS", "CREATE_POST_FROM_APP", "PREPARE_TRAINING_DATASET", "RUN_TRAINING", "RUN_CONFIRMED_STEP", "GET_IMAGES_BY_IDS", "CANCEL_APP_WORKFLOW", "OPEN_CHECKPOINT_PICKER", "OPEN_RESOURCE_PICKER", "OPEN_IMAGE_UPLOAD", "OPEN_IMAGE_PICKER", "OPEN_MEDIA_UPLOAD", "SET_USER_CHECKPOINT", "NAVIGATE", "REQUEST_SIGN_IN", "REQUEST_CONSENT", "TRACK_EVENT", "APP_STORAGE_GET", "APP_STORAGE_SET", "APP_STORAGE_DELETE", "APP_STORAGE_LIST", "APP_STORAGE_QUOTA", "SHARED_LIST", "SHARED_GET_COUNT", "SHARED_GET_COUNTS", "SHARED_APPEND", "SHARED_VOTE", "SHARED_UNVOTE", "SHARED_WITHDRAW", "SHARED_UPDATE", "SHARED_GET", "SHARED_REPORT", "SAVE_IMAGE", "SET_COLLECTION_FOLLOW", "SET_ENTITY_REACTION", "GET_ENTITY_REACTIONS"];
|
|
1198
1233
|
/**
|
|
1199
1234
|
* The bucket a message type falls into when the protocol does not declare it.
|
|
1200
1235
|
*
|
package/dist/blocks/messages.js
CHANGED
package/dist/blocks/types.d.ts
CHANGED
|
@@ -372,6 +372,51 @@ export interface BlockPickedImageInfo {
|
|
|
372
372
|
*/
|
|
373
373
|
imageId?: number;
|
|
374
374
|
}
|
|
375
|
+
/**
|
|
376
|
+
* What kind of media `OPEN_MEDIA_UPLOAD` (via `useMediaUpload`) uploads. The
|
|
377
|
+
* host decides the file's real type from its MAGIC BYTES, never from a declared
|
|
378
|
+
* MIME type, and accepts per kind:
|
|
379
|
+
* - `'image'`: PNG, JPEG or WebP;
|
|
380
|
+
* - `'video'`: MP4 or WebM;
|
|
381
|
+
* - `'audio'`: MP3, WAV or WebM.
|
|
382
|
+
*/
|
|
383
|
+
export type BlockMediaKind = 'image' | 'video' | 'audio';
|
|
384
|
+
/**
|
|
385
|
+
* One media file the host uploaded for the block as a GENERATION INPUT
|
|
386
|
+
* (`MEDIA_UPLOAD_RESULT.selected`, via `useMediaUpload`).
|
|
387
|
+
*
|
|
388
|
+
* `url` is a PRIVATE generation-input URL: pass it into a workflow step input
|
|
389
|
+
* (an image, video or audio input). It is not a public Civitai image and has no
|
|
390
|
+
* `imageId`. The host refuses data URIs and inline
|
|
391
|
+
* base64 in step inputs, so this is how a block gets media it holds (a picked
|
|
392
|
+
* file, a canvas, a recording) into a step.
|
|
393
|
+
*
|
|
394
|
+
* Per kind:
|
|
395
|
+
* - `'image'`: the host RESIZES the picture so both sides are within 64–2048
|
|
396
|
+
* pixels and RE-ENCODES it as JPEG. `mimeType` is always `'image/jpeg'`,
|
|
397
|
+
* and `sizeBytes`, `width` and `height` describe that JPEG.
|
|
398
|
+
* - `'video'`: uploaded as sent. `width` / `height` are always set (the host
|
|
399
|
+
* refuses a file with no video track); `durationMs` is set when the browser
|
|
400
|
+
* reports a finite duration.
|
|
401
|
+
* - `'audio'`: uploaded as sent. No `width` / `height`; `durationMs` as for
|
|
402
|
+
* video. A `MediaRecorder` WebM can report no duration, so do not rely on it.
|
|
403
|
+
*/
|
|
404
|
+
export interface BlockMediaUploadInfo {
|
|
405
|
+
/** The private generation-input URL. Pass it into a step input. */
|
|
406
|
+
url: string;
|
|
407
|
+
/** The kind that was requested. */
|
|
408
|
+
kind: BlockMediaKind;
|
|
409
|
+
/** The type actually uploaded. Always `'image/jpeg'` for `'image'`. */
|
|
410
|
+
mimeType: string;
|
|
411
|
+
/** Bytes actually uploaded (for `'image'`, the re-encoded JPEG's size). */
|
|
412
|
+
sizeBytes: number;
|
|
413
|
+
/** Pixel width. Always set for `'image'` and `'video'`; absent for `'audio'`. */
|
|
414
|
+
width?: number;
|
|
415
|
+
/** Pixel height. Always set for `'image'` and `'video'`; absent for `'audio'`. */
|
|
416
|
+
height?: number;
|
|
417
|
+
/** Duration in milliseconds, for `'video'` / `'audio'` when known. Never set for `'image'`. */
|
|
418
|
+
durationMs?: number;
|
|
419
|
+
}
|
|
375
420
|
/**
|
|
376
421
|
* One of the model version's curated preview images, with the standard
|
|
377
422
|
* gen params extracted from its source meta. Block UIs use these to let
|
|
@@ -1295,8 +1340,14 @@ export type WorkflowBodyPassThroughStep = {
|
|
|
1295
1340
|
*
|
|
1296
1341
|
* Consequently the orchestrator's per-`$type` schema is the ONLY authority for
|
|
1297
1342
|
* what a given `$type` accepts; nothing in this package or on the host mirrors
|
|
1298
|
-
* it.
|
|
1299
|
-
* is a payload-DoS bound and not a shape gate.
|
|
1343
|
+
* it. Its shape is not checked; its size is: at most **262144 bytes** (256 KB)
|
|
1344
|
+
* serialized, which is a payload-DoS bound and not a shape gate.
|
|
1345
|
+
*
|
|
1346
|
+
* 🔴 **No inline media.** The host refuses a `data:image|video|audio` URI or a
|
|
1347
|
+
* long raw base64 string anywhere in here: upload the image, video or audio
|
|
1348
|
+
* with `useMediaUpload()` (an image also works with
|
|
1349
|
+
* `useImageUpload({ purpose: 'generationSource' })` or `useImagePicker()`)
|
|
1350
|
+
* and pass the returned `url`.
|
|
1300
1351
|
*/
|
|
1301
1352
|
input: Record<string, unknown>;
|
|
1302
1353
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -25,11 +25,11 @@
|
|
|
25
25
|
*
|
|
26
26
|
* - **`./blocks`** — importing it RUNS `./safe-storage` for its side effect
|
|
27
27
|
* (repairing `localStorage`/`sessionStorage` at an opaque origin). Folding
|
|
28
|
-
*
|
|
28
|
+
* 198 symbols and that side effect into the root would make every OAuth app
|
|
29
29
|
* that writes `import { exchangeCode } from '@civitai/app-sdk'` pay for the
|
|
30
30
|
* Civitai-Apps contract it does not use. Blocks and OAuth apps are disjoint
|
|
31
31
|
* audiences; see this package's README.
|
|
32
|
-
* 🔴 That
|
|
32
|
+
* 🔴 That 198 is ASSERTED, not merely quoted — by
|
|
33
33
|
* `packages/civitai-app-sdk/test/export-surface.test.ts`, which pins
|
|
34
34
|
* `surface.get('./blocks').size`. It said 119 here against a real 125 until
|
|
35
35
|
* 2026-10-04, because the existing checks pin the root as an exact union and
|
package/dist/index.js
CHANGED
|
@@ -25,11 +25,11 @@
|
|
|
25
25
|
*
|
|
26
26
|
* - **`./blocks`** — importing it RUNS `./safe-storage` for its side effect
|
|
27
27
|
* (repairing `localStorage`/`sessionStorage` at an opaque origin). Folding
|
|
28
|
-
*
|
|
28
|
+
* 198 symbols and that side effect into the root would make every OAuth app
|
|
29
29
|
* that writes `import { exchangeCode } from '@civitai/app-sdk'` pay for the
|
|
30
30
|
* Civitai-Apps contract it does not use. Blocks and OAuth apps are disjoint
|
|
31
31
|
* audiences; see this package's README.
|
|
32
|
-
* 🔴 That
|
|
32
|
+
* 🔴 That 198 is ASSERTED, not merely quoted — by
|
|
33
33
|
* `packages/civitai-app-sdk/test/export-surface.test.ts`, which pins
|
|
34
34
|
* `surface.get('./blocks').size`. It said 119 here against a real 125 until
|
|
35
35
|
* 2026-10-04, because the existing checks pin the root as an exact union and
|
|
@@ -430,11 +430,14 @@ export interface BuildImageGenBodyOptions {
|
|
|
430
430
|
*
|
|
431
431
|
* @example Nano Banana 2 with a reference image
|
|
432
432
|
* ```ts
|
|
433
|
+
* // Pass a hosted image URL, not a data URI or raw base64. In an App Block, the
|
|
434
|
+
* // `url` from `useImageUpload({ purpose: 'generationSource' })` or `useImagePicker()`;
|
|
435
|
+
* // the block host refuses inline media in a step input.
|
|
433
436
|
* const body = buildImageGenBody({
|
|
434
437
|
* engine: 'google',
|
|
435
438
|
* model: 'nano-banana-2',
|
|
436
439
|
* prompt: 'turn this into a cartoon sticker',
|
|
437
|
-
* images: [
|
|
440
|
+
* images: [source.url],
|
|
438
441
|
* aspectRatio: '1:1',
|
|
439
442
|
* numImages: 1,
|
|
440
443
|
* resolution: '1K',
|
|
@@ -393,11 +393,14 @@ export function buildTextToImageBody(input, opts = {}) {
|
|
|
393
393
|
*
|
|
394
394
|
* @example Nano Banana 2 with a reference image
|
|
395
395
|
* ```ts
|
|
396
|
+
* // Pass a hosted image URL, not a data URI or raw base64. In an App Block, the
|
|
397
|
+
* // `url` from `useImageUpload({ purpose: 'generationSource' })` or `useImagePicker()`;
|
|
398
|
+
* // the block host refuses inline media in a step input.
|
|
396
399
|
* const body = buildImageGenBody({
|
|
397
400
|
* engine: 'google',
|
|
398
401
|
* model: 'nano-banana-2',
|
|
399
402
|
* prompt: 'turn this into a cartoon sticker',
|
|
400
|
-
* images: [
|
|
403
|
+
* images: [source.url],
|
|
401
404
|
* aspectRatio: '1:1',
|
|
402
405
|
* numImages: 1,
|
|
403
406
|
* resolution: '1K',
|
package/dist/vite/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Plugin } from 'vite';
|
|
2
|
+
import type { BlockManifest } from '../blocks/types.js';
|
|
2
3
|
export interface BlockManifestPluginOptions {
|
|
3
4
|
/**
|
|
4
5
|
* Path to the manifest. Relative paths resolve against `root`, which defaults
|
|
@@ -9,6 +10,34 @@ export interface BlockManifestPluginOptions {
|
|
|
9
10
|
/** Overrides the Vite project root used to resolve a relative `manifestPath`. */
|
|
10
11
|
root?: string;
|
|
11
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* The global the dev server's HTML sets for `@civitai/sdk` to read. Spelled again
|
|
15
|
+
* in `packages/civitai-sdk/src/doc-store/local-preview.ts` (the two packages share
|
|
16
|
+
* no code); `tests/guards/local-preview-manifest-global.test.mjs` pins the two equal.
|
|
17
|
+
*
|
|
18
|
+
* WHY A GLOBAL, set by a `<script>` the plugin adds to `index.html`, rather than
|
|
19
|
+
* the alternatives:
|
|
20
|
+
* - a VIRTUAL MODULE needs `@civitai/sdk` to import it, which fails to resolve in
|
|
21
|
+
* every bundle not built by this plugin — production included;
|
|
22
|
+
* - a `define` substitution has to reach `@civitai/sdk` inside `node_modules`,
|
|
23
|
+
* where it depends on how the dependency optimizer pre-bundles it, and it would
|
|
24
|
+
* rewrite production builds too;
|
|
25
|
+
* - an `initialize()` option needs every app to import its manifest and pass it,
|
|
26
|
+
* and would be public API.
|
|
27
|
+
* The `<script>` is added on the DEV SERVER only (`vite build` never emits it),
|
|
28
|
+
* needs no change in an app that registers this plugin, and the SDK reads it only
|
|
29
|
+
* when the host marks the frame a local preview. It is the ONLY way the manifest
|
|
30
|
+
* reaches the SDK, so an app that does NOT register the plugin gets the SDK's
|
|
31
|
+
* `local_preview_no_manifest` error. Every scaffold in this repo that ships a
|
|
32
|
+
* manifest registers it; not every scaffold elsewhere does (the CLI's
|
|
33
|
+
* `page-money` template, for one, does not), and those apps must add it.
|
|
34
|
+
*/
|
|
35
|
+
export declare const LOCAL_PREVIEW_MANIFEST_GLOBAL = "__CIVITAI_LOCAL_PREVIEW_MANIFEST__";
|
|
36
|
+
/**
|
|
37
|
+
* The classic `<script>` body: only `scopes` and `storage`, as JSON with `<`
|
|
38
|
+
* escaped so no value can close the element.
|
|
39
|
+
*/
|
|
40
|
+
export declare function localPreviewManifestScript(manifest: Pick<BlockManifest, 'scopes' | 'storage'>): string;
|
|
12
41
|
export declare function blockManifestPlugin(options?: BlockManifestPluginOptions): Plugin;
|
|
13
42
|
export { BlockManifestError } from '../blocks/manifestError.js';
|
|
14
43
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/vite/index.js
CHANGED
|
@@ -18,6 +18,13 @@
|
|
|
18
18
|
* pre-check against the same canonical). It exists so a manifest mistake
|
|
19
19
|
* surfaces in the dev loop rather than at submit time.
|
|
20
20
|
*
|
|
21
|
+
* DEV SERVER ONLY, it also hands the manifest's `scopes` and `storage` to
|
|
22
|
+
* `@civitai/sdk`, for a dev-tunnel preview. A preview has no server-side storage,
|
|
23
|
+
* so when the host marks the frame `storageMode: 'local-preview'` the SDK serves
|
|
24
|
+
* `app.storage.collection(name)` from an in-memory store that enforces THESE
|
|
25
|
+
* declarations — the ones of the code being previewed, which the server has
|
|
26
|
+
* never seen. See {@link LOCAL_PREVIEW_MANIFEST_GLOBAL} for why it is a global.
|
|
27
|
+
*
|
|
21
28
|
* PEERS: `ajv` (used at runtime by `@civitai/app-sdk/manifest`) and `vite`
|
|
22
29
|
* (types only). Both are optional peers of `@civitai/app-sdk`; a scaffold using
|
|
23
30
|
* this plugin needs both in its devDependencies.
|
|
@@ -25,7 +32,60 @@
|
|
|
25
32
|
import { readFileSync } from 'node:fs';
|
|
26
33
|
import { resolve } from 'node:path';
|
|
27
34
|
import { BlockManifestError, defineBlock } from '../manifest/index.js';
|
|
35
|
+
/**
|
|
36
|
+
* The global the dev server's HTML sets for `@civitai/sdk` to read. Spelled again
|
|
37
|
+
* in `packages/civitai-sdk/src/doc-store/local-preview.ts` (the two packages share
|
|
38
|
+
* no code); `tests/guards/local-preview-manifest-global.test.mjs` pins the two equal.
|
|
39
|
+
*
|
|
40
|
+
* WHY A GLOBAL, set by a `<script>` the plugin adds to `index.html`, rather than
|
|
41
|
+
* the alternatives:
|
|
42
|
+
* - a VIRTUAL MODULE needs `@civitai/sdk` to import it, which fails to resolve in
|
|
43
|
+
* every bundle not built by this plugin — production included;
|
|
44
|
+
* - a `define` substitution has to reach `@civitai/sdk` inside `node_modules`,
|
|
45
|
+
* where it depends on how the dependency optimizer pre-bundles it, and it would
|
|
46
|
+
* rewrite production builds too;
|
|
47
|
+
* - an `initialize()` option needs every app to import its manifest and pass it,
|
|
48
|
+
* and would be public API.
|
|
49
|
+
* The `<script>` is added on the DEV SERVER only (`vite build` never emits it),
|
|
50
|
+
* needs no change in an app that registers this plugin, and the SDK reads it only
|
|
51
|
+
* when the host marks the frame a local preview. It is the ONLY way the manifest
|
|
52
|
+
* reaches the SDK, so an app that does NOT register the plugin gets the SDK's
|
|
53
|
+
* `local_preview_no_manifest` error. Every scaffold in this repo that ships a
|
|
54
|
+
* manifest registers it; not every scaffold elsewhere does (the CLI's
|
|
55
|
+
* `page-money` template, for one, does not), and those apps must add it.
|
|
56
|
+
*/
|
|
57
|
+
export const LOCAL_PREVIEW_MANIFEST_GLOBAL = '__CIVITAI_LOCAL_PREVIEW_MANIFEST__';
|
|
58
|
+
/** Reads and validates the manifest, throwing the message Vite should print. */
|
|
59
|
+
function loadManifest(manifestPath) {
|
|
60
|
+
let manifest;
|
|
61
|
+
try {
|
|
62
|
+
manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
throw new Error(`block.manifest.json could not be read or parsed (${manifestPath}): ${err.message}`);
|
|
66
|
+
}
|
|
67
|
+
try {
|
|
68
|
+
defineBlock({ manifest });
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
// Re-thrown with the field path up front: Vite prints the message, not
|
|
72
|
+
// the error's own properties, so `.field` would otherwise be invisible.
|
|
73
|
+
const field = err instanceof BlockManifestError && err.field ? ` [${err.field}]` : '';
|
|
74
|
+
throw new Error(`block.manifest.json is invalid${field}: ${err.message}`);
|
|
75
|
+
}
|
|
76
|
+
return manifest;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* The classic `<script>` body: only `scopes` and `storage`, as JSON with `<`
|
|
80
|
+
* escaped so no value can close the element.
|
|
81
|
+
*/
|
|
82
|
+
export function localPreviewManifestScript(manifest) {
|
|
83
|
+
const exposed = JSON.stringify({ scopes: manifest.scopes, storage: manifest.storage }).replace(/</g, '\\u003c');
|
|
84
|
+
return `globalThis[${JSON.stringify(LOCAL_PREVIEW_MANIFEST_GLOBAL)}] = ${exposed};`;
|
|
85
|
+
}
|
|
28
86
|
export function blockManifestPlugin(options = {}) {
|
|
87
|
+
let manifestPath = '';
|
|
88
|
+
let serving = false;
|
|
29
89
|
return {
|
|
30
90
|
name: 'civitai-block-manifest',
|
|
31
91
|
// `enforce: 'pre'` so the manifest error is the FIRST thing reported rather
|
|
@@ -36,23 +96,27 @@ export function blockManifestPlugin(options = {}) {
|
|
|
36
96
|
// this module's own location — this file now lives inside the installed
|
|
37
97
|
// package, not next to the consumer's manifest.
|
|
38
98
|
const root = options.root ?? config?.root ?? process.cwd();
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
99
|
+
manifestPath = resolve(root, options.manifestPath ?? './block.manifest.json');
|
|
100
|
+
serving = config?.command === 'serve';
|
|
101
|
+
loadManifest(manifestPath);
|
|
102
|
+
},
|
|
103
|
+
transformIndexHtml() {
|
|
104
|
+
// Dev server only: a build never carries the manifest into the page.
|
|
105
|
+
if (!serving)
|
|
106
|
+
return undefined;
|
|
107
|
+
// Re-read per page load, so an edited manifest takes effect on reload.
|
|
108
|
+
const manifest = loadManifest(manifestPath);
|
|
109
|
+
return [
|
|
110
|
+
{
|
|
111
|
+
tag: 'script',
|
|
112
|
+
// End of <head>, NOT `head-prepend`: prepended, a large `storage` block
|
|
113
|
+
// could push `<meta charset>` past the 1024-byte prescan. Nothing needs
|
|
114
|
+
// it earlier: the SDK reads the global inside `initialize()`, after
|
|
115
|
+
// BLOCK_INIT, and a classic script still runs before any module script.
|
|
116
|
+
injectTo: 'head',
|
|
117
|
+
children: localPreviewManifestScript(manifest),
|
|
118
|
+
},
|
|
119
|
+
];
|
|
56
120
|
},
|
|
57
121
|
};
|
|
58
122
|
}
|
package/package.json
CHANGED