@civitai/app-sdk 0.47.0 → 0.48.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 CHANGED
@@ -19,12 +19,19 @@ pnpm add @civitai/app-sdk
19
19
  | `cookies/*` — `sealCookie`, `unsealCookie`, `buildSetCookieHeader`, `readCookie` | AES-256-GCM authenticated cookie crypto. Use to seal a session blob (refresh token, expiry, scope) into an `httpOnly` cookie with zero external session store. |
20
20
  | `orchestrator/*` — `createOrchestratorClient`, `estimateWorkflow`, `submitWorkflow`, `getWorkflow`, `pollWorkflow`, `buildTextToImageBody`, `buildImageGenBody`, `buildWorkflowBody`, `WORKFLOW_STEP_TYPES`, `IMAGE_GEN_ENGINES`, `isTerminal`, `extractImageUrls`, `OrchestratorError`, `WorkflowSnapshot`, `GenerateInput`, `ImageGenInput`, `WorkflowStepType`, `ImageGenEngine`, `DEFAULT_MODEL_AIR` | Orchestrator workflow glue — types, body builders, raw HTTP, and long-poll helper. Client + server safe (fetch-only). `estimateWorkflow` calls `?whatif=true` to preview Buzz cost without spending. `pollWorkflow` long-polls to terminal status. `WORKFLOW_STEP_TYPES` is the catalog of every step `$type` the orchestrator accepts. |
21
21
  | `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. |
22
- | `blocks/*` — `defineBlock`, `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). `defineBlock(config)` validates a `BlockManifestV1` at startup so authoring mistakes surface in `pnpm dev` instead of at `civitai app validate`/submit. 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 — no React or DOM types. Hooks and the iframe transport live in a separate package. |
22
+ | `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. |
23
+ | `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. |
24
+ | `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). |
23
25
 
24
26
  ## Subpath imports
25
27
 
26
28
  ```ts
27
- import { defineBlock, BLOCK_SCOPES } from '@civitai/app-sdk/blocks';
29
+ import { BLOCK_SCOPES, isSignedIn } from '@civitai/app-sdk/blocks';
30
+ // Build-time manifest validation (NODE ONLY — needs the optional peer `ajv`).
31
+ // Most projects want the Vite plugin below rather than calling this directly:
32
+ import { defineBlock } from '@civitai/app-sdk/manifest';
33
+ // The same gate as a Vite plugin (NODE ONLY — optional peers `ajv` + `vite`):
34
+ import { blockManifestPlugin } from '@civitai/app-sdk/vite';
28
35
  // Type-only: the orchestrator's generated per-step shapes. Needs the optional
29
36
  // peer `@civitai/client@beta` — see "Typed step shapes" below:
30
37
  import type { TypedWorkflowTemplate } from '@civitai/app-sdk/orchestrator/steps';
@@ -37,9 +44,11 @@ import manifestSchema from '@civitai/app-sdk/schemas/app-block/v1.json' with { t
37
44
  ## Civitai Apps contract (`@civitai/app-sdk/blocks`)
38
45
 
39
46
  > Building a **Civitai App** (an iframe-embedded UI on a civitai.com page)? This
40
- > subpath is the framework-agnostic contract — manifest types, scope strings,
41
- > the `postMessage` protocol, and the `defineBlock` validator. The React hooks +
42
- > transport that consume it live in
47
+ > subpath is the framework-agnostic contract — manifest types, scope strings and
48
+ > the `postMessage` protocol. (The `defineBlock` validator moved to the node-only
49
+ > [`@civitai/app-sdk/manifest`](#defineblock-validator-rules) subpath; `./blocks`
50
+ > keeps zero runtime dependencies.) The React hooks + transport that consume it
51
+ > live in
43
52
  > [`@civitai/blocks-react`](https://www.npmjs.com/package/@civitai/blocks-react).
44
53
  > Start from the runnable [examples](https://github.com/civitai/civitai-app-starters/tree/main/starters/examples).
45
54
  >
@@ -103,8 +112,7 @@ interface BlockInitPayload {
103
112
 
104
113
  | Export | What |
105
114
  |---|---|
106
- | `defineBlock({ manifest })` | Validates a `BlockManifestV1` (subset of the server checks) and returns it. Call at module scope so authoring mistakes throw before mount. Throws `BlockManifestError` (has a `.field` dot-path). |
107
- | `BLOCK_SCOPES` / `BLOCK_SCOPE_PATTERN` | The 15 known block scope strings (the authoritative enum `defineBlock` validates against) + the `domain:verb:target` format-helper regex. A scope is valid only if it's a member of `BLOCK_SCOPES`, matching the [canonical schema](https://civitai.com/schemas/app-block/v1.json). |
115
+ | `BLOCK_SCOPES` / `BLOCK_SCOPE_PATTERN` | The 15 known block scope strings (the authoritative enum the canonical schema validates `scopes` against) + the `domain:verb:target` format-helper regex. A scope is valid only if it's a member of `BLOCK_SCOPES`, matching the [canonical schema](https://civitai.com/schemas/app-block/v1.json). |
108
116
  | `isMessage(data, type)` | Discriminator-only message narrowing (see above). |
109
117
  | `isModelSlotContext(ctx)` / `isPageSlotContext(ctx)` | Runtime narrowing for the `slotId`-discriminated `BlockContext` union. Real checks on a value that crossed a `postMessage` boundary — they verify every field they assert, not just `slotId`. |
110
118
  | `isSignedIn(viewer)` | **The sign-in gate.** `isSignedIn(useBlockContext().viewer)` — do not open-code it as `viewer !== null` or `viewer?.signedIn === true`. Which of those is correct has already changed once with the host contract, and this is the one place it is decided. It reads neither `viewer.id` nor `viewer.username` (both `@deprecated`), so nothing written through it changes when those are removed. Need the identity rather than the presence? `useViewer()` — scope-gated and audited per call. |
@@ -154,31 +162,67 @@ const body: WorkflowBody = {
154
162
 
155
163
  ### `defineBlock` validator rules
156
164
 
157
- Mirrors a strict subset of the civitai/civitai server gate. It throws on:
158
-
159
- - A missing **required** field: `$schema`, `appId`, `blockId`, `version`, `name`,
160
- `type`, `targets`, `scopes`, `iframe`, `contentRating`, `minApiVersion`.
161
- - `$schema` ≠ `https://civitai.com/schemas/app-block/v1.json`.
162
- - `blockId` not matching the canonical `/^[a-z][a-z0-9-]*[a-z0-9]$/` (DNS-subdomain-safe:
163
- lowercase, starts with a letter, ends alphanumeric) or outside 3–40 chars —
164
- the blockId becomes `<blockId>.civit.ai`; `version` not semver; `name` > 80 chars.
165
- - `type` not `block` | `embed`; `contentRating` not `g|pg|pg13|r|x`.
166
- - **Empty `scopes`** (must be a non-empty array) or any scope that isn't one of
167
- the 15 known block scopes (`BLOCK_SCOPES`). The [canonical schema](https://civitai.com/schemas/app-block/v1.json)
168
- validates `scopes` by **enum membership**, so a well-formed but unknown scope
169
- (e.g. `models:read:all`) is rejected; PascalCase like `ModelsReadSelf` gets a
170
- pointed error.
171
- - **Empty `targets`**, or a target with a non-string `slotId` / non-integer `priority`.
172
- - `iframe.src` not https (http only for `localhost`/`127.0.0.1`/`[::1]`/`*.localhost`);
173
- a banned sandbox token (`allow-same-origin`, any `allow-top-navigation*`);
174
- non-positive integer `minHeight`; bad `maxHeight`; non-boolean `resizable`.
175
- - `settings` with a bad key (must be `snake_case`), > 32 fields, or a field
176
- missing/mis-typed `scope` / `type` / `label` / `description`.
177
-
178
- > The validator does **not** check that `iframe.src` hostname equals
179
- > `<blockId>.<APPS_DOMAIN>` or that the path is root — those are enforced
180
- > **server-side** at submit time (gotcha #33). Keep `iframe.src` =
181
- > `https://<blockId>.civit.ai/` (root, no path prefix) and your Vite `base: '/'`.
165
+ **There is no rule list here, and that is the point.** `defineBlock` does not
166
+ maintain one: it compiles the vendored copy of the
167
+ [canonical schema](https://civitai.com/schemas/app-block/v1.json) with
168
+ [Ajv](https://ajv.js.org) and validates against **that**. Every `required`,
169
+ `enum`, `pattern`, bound, `additionalProperties` and `allOf` the canonical
170
+ expresses is enforced, and moves when the canonical moves. The schema is the
171
+ rule list; read it, or read the errors.
172
+
173
+ That is [#330](https://github.com/civitai/civitai-app-starters/issues/330)'s own
174
+ proposed fix. The previous implementation hand-mirrored the schema, and the
175
+ mirror diverged exactly as the issue predicted: it required 11 fields where the
176
+ canonical requires **five** (`blockId`, `version`, `name`, `contentRating`,
177
+ `scopes`), required `appId` — not a canonical property at all — and *required*
178
+ the SERVER-OWNED `iframe.src` that `civitai app submit` refuses, so it rejected
179
+ every `block.manifest.json` this repo ships.
180
+
181
+ On top of the schema, `defineBlock` applies exactly five extra rules, each
182
+ mirroring a server rejection the canonical states only in **prose**:
183
+
184
+ | Rule | The canonical prose it mirrors |
185
+ |---|---|
186
+ | a dev-set **`iframe.src`** is rejected | *"SERVER-OWNED. Do NOT set `iframe.src` — the platform assigns it."* The JSON-Schema `not` is deliberately absent; the top-level `allOf` `$comment` says the platform validator and the Go CLI reject it instead. |
187
+ | a dev-set **`trustTier`** is rejected | *"SERVER-OWNED … the platform assigns the trust tier during review."* Same shape. |
188
+ | **`iframe.sandbox`** rejects `allow-same-origin` and every `allow-top-navigation*` token | *"Never combine allow-same-origin with allow-scripts."* Top-navigation is refused because a block must route navigation through the `NAVIGATE` postMessage. |
189
+ | every **`scopeJustifications`** key must be a scope in `scopes` | *"The requirement is enforced imperatively by the manifest validator (not expressed as JSON-Schema conditionals here)."* |
190
+ | **`settings`** is validated against the W3 settings meta-schema | Not in the app-block schema at all — settings are validated server-side by `manifest-settings.meta.schema.ts`. |
191
+
192
+ Each is **strictly additive**: it can only reject a manifest Ajv accepted, never
193
+ relax a canonical rule. `test/manifest/divergences.test.ts` asserts exactly that
194
+ per entry — the fixture must be schema-VALID and `defineBlock`-REJECTED — and the
195
+ table and the fixture set must be the same set, so a sixth hand-written rule with
196
+ no entry fails the suite.
197
+
198
+ > **Passing is necessary, not sufficient**, and it is **not** a replacement for
199
+ > `civitai app validate` (the Go CLI's local pre-check against the same
200
+ > canonical). Run that before `civitai app submit`. `KNOWN_GAPS` in
201
+ > `src/manifest/defineBlock.ts` names each thing only the server can check —
202
+ > including one that cuts the *other* way: the canonical's sandbox description is
203
+ > an **allowlist** (*"Unverified tier allows only: allow-scripts, allow-forms"*)
204
+ > while the rule above is a denylist, so `allow-popups`, `allow-modals` and
205
+ > `allow-downloads` **pass here and may be refused at review**. The tier is
206
+ > assigned server-side, so it cannot be enforced locally.
207
+
208
+ **Where it runs.** Every scaffold that ships a `block.manifest.json`
209
+ (`starters/civitai-block-starter` and all six `starters/examples/*`) registers
210
+ `blockManifestPlugin` from `@civitai/app-sdk/vite` in its `vite.config.ts`. It
211
+ fires from Vite's `configResolved`, the one hook called on both the dev-server
212
+ and the build path — so `pnpm dev`, `pnpm dev:harness` and `pnpm build` all fail
213
+ loudly on a bad manifest, with the offending field path in the message. Add
214
+ `ajv` to your devDependencies (it is an optional peer):
215
+
216
+ ```bash
217
+ pnpm add -D ajv
218
+ ```
219
+
220
+ ```ts
221
+ // vite.config.ts — drop this into your existing `defineConfig({ plugins: [...] })`.
222
+ import { blockManifestPlugin } from '@civitai/app-sdk/vite';
223
+
224
+ const plugins = [blockManifestPlugin()];
225
+ ```
182
226
 
183
227
  ### Web storage in a block (`@civitai/app-sdk/safe-storage`)
184
228
 
@@ -1,16 +1,28 @@
1
1
  /**
2
2
  * `@civitai/app-sdk/blocks` — framework-agnostic contract for Civitai Apps.
3
3
  *
4
- * This subpath exports the manifest type, scope strings, postMessage protocol,
5
- * and the `defineBlock` validator. Hooks and transport implementations live in
6
- * a separate package (see `@civitai/blocks-react`) so this module stays usable
7
- * from any runtime — Node, browsers, workers — with no React dependency.
4
+ * This subpath exports the manifest type, scope strings and postMessage
5
+ * protocol. Hooks and transport implementations live in a separate package (see
6
+ * `@civitai/blocks-react`) so this module stays usable from any runtime — Node,
7
+ * browsers, workers — with no React dependency and no runtime dependencies at
8
+ * all. Build-time manifest validation lives at `@civitai/app-sdk/manifest`
9
+ * (node-only); see the note on `BlockManifestError` below.
8
10
  */
9
11
  import '../safe-storage/index.js';
10
12
  export { installSafeStorage, createMemoryStorage } from '../safe-storage/index.js';
11
13
  export type { SafeStorageInstallResult, SafeStorageName } from '../safe-storage/index.js';
12
- export { defineBlock, BlockManifestError } from './defineBlock.js';
13
- export type { DefineBlockConfig } from './defineBlock.js';
14
+ /**
15
+ * `defineBlock` MOVED to `@civitai/app-sdk/manifest` (a NODE-ONLY subpath) in
16
+ * the release that closed #330. It now validates by compiling the vendored
17
+ * canonical schema with Ajv instead of maintaining a hand-written mirror of it,
18
+ * which needs `node:fs` and a runtime dependency — neither of which belongs on
19
+ * this browser-facing, zero-dependency surface. Most callers want the Vite
20
+ * plugin at `@civitai/app-sdk/vite` rather than the function.
21
+ *
22
+ * `BlockManifestError` stays exported here, from its own module, so
23
+ * `instanceof` means the same thing on both subpaths.
24
+ */
25
+ export { BlockManifestError } from './manifestError.js';
14
26
  export { BLOCK_SCOPES, BLOCK_SCOPE_PATTERN, BLOCK_CATEGORIES, BLOCK_TAGLINE_MAX_LENGTH, } from './scopes.js';
15
27
  export type { BlockScope, BlockScopeKey, BlockCategory } from './scopes.js';
16
28
  export { BrowsingLevel, SFW_LEVELS, NSFW_LEVELS, isSfwCeiling, isLevelAllowed, effectiveBrowsingCeiling, } from './browsingLevel.js';
@@ -32,5 +44,5 @@ export { isModelSlotContext, isPageSlotContext } from './types.js';
32
44
  * in `types.ts` for which of the two it uses and why.
33
45
  */
34
46
  export { isSignedIn } from './types.js';
35
- export type { BlockContext, KnownSlotId, ModelSlotId, PageSlotContext, PageSlotId, UnknownSlotContext, BlockManifest, BlockManifestV1, BlockSettings, BlockToken, ContentRating, ManifestAsset, ManifestBooleanField, ManifestIframe, ManifestNumberField, ManifestPreview, ManifestSettingField, ManifestSettings, ManifestStringField, ManifestTarget, ModelSlotContext, SettingScope, SettingWidget, Theme, ViewerInfo, BlockCheckpointInfo, BlockResourceInfo, BlockResourcePickerType, BlockSourceImage, BlockUploadedImageInfo, BlockGenerationSourceImageInfo, BlockPendingImageInfo, BlockImageScanResult, BlockUploadPurpose, BlockTextToImageParams, BlockWorkflowSnapshot, BuzzAccountType, ShowcaseImage, WorkflowBody, WorkflowBodyTextToImage, WorkflowBodyCustomComfy, WorkflowBodyCustomComfyRecipe, WorkflowBodyCustomComfyInline, InlineComfyNode, WorkflowBodyStep, WorkflowBodyPassThroughStep, WorkflowStatus, BlockBuzzTransaction, BlockBuzzAccount, BlockDailyCompensationResource, BlockViewer, BlockWildcardPack, BlockWildcardPackErrorCode, AppWorkflow, AppWorkflowImage, BlockGatedImage, BlockCollectionFollowErrorCode, BlockCollectionFollowResult, BlockPostSource, BlockCreatePostRequest, BlockCreatePostResult, BlockCreatePostHostError, } from './types.js';
47
+ export type { BlockContext, KnownSlotId, ModelSlotId, PageSlotContext, PageSlotId, UnknownSlotContext, BlockManifest, BlockManifestV1, BlockSettings, BlockToken, ContentRating, ManifestBooleanField, ManifestIframe, ManifestNumberField, ManifestPage, ManifestPreview, ManifestSettingField, ManifestSettings, ManifestStringField, ManifestTarget, ModelSlotContext, SettingScope, SettingWidget, Theme, ViewerInfo, BlockCheckpointInfo, BlockResourceInfo, BlockResourcePickerType, BlockSourceImage, BlockUploadedImageInfo, BlockGenerationSourceImageInfo, BlockPendingImageInfo, BlockImageScanResult, BlockUploadPurpose, BlockTextToImageParams, BlockWorkflowSnapshot, BuzzAccountType, ShowcaseImage, WorkflowBody, WorkflowBodyTextToImage, WorkflowBodyCustomComfy, WorkflowBodyCustomComfyRecipe, WorkflowBodyCustomComfyInline, InlineComfyNode, WorkflowBodyStep, WorkflowBodyPassThroughStep, WorkflowStatus, BlockBuzzTransaction, BlockBuzzAccount, BlockDailyCompensationResource, BlockViewer, BlockWildcardPack, BlockWildcardPackErrorCode, AppWorkflow, AppWorkflowImage, BlockGatedImage, BlockCollectionFollowErrorCode, BlockCollectionFollowResult, BlockPostSource, BlockCreatePostRequest, BlockCreatePostResult, BlockCreatePostHostError, } from './types.js';
36
48
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/blocks/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAQH,OAAO,0BAA0B,CAAC;AAElC,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AACnF,YAAY,EAAE,wBAAwB,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAE1F,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACnE,YAAY,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAE1D,OAAO,EACL,YAAY,EACZ,mBAAmB,EACnB,gBAAgB,EAChB,wBAAwB,GACzB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5E,OAAO,EACL,aAAa,EACb,UAAU,EACV,WAAW,EACX,YAAY,EACZ,cAAc,EACd,wBAAwB,GACzB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAE1F,OAAO,EACL,2BAA2B,EAC3B,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACL,8BAA8B,EAC9B,2BAA2B,EAC3B,wBAAwB,EACxB,uBAAuB,EACvB,sBAAsB,EACtB,sBAAsB,GACvB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAE3D,OAAO,EACL,SAAS,EACT,6BAA6B,EAC7B,wBAAwB,EACxB,6BAA6B,GAC9B,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,gBAAgB,EAChB,oBAAoB,EACpB,wBAAwB,EACxB,yBAAyB,EACzB,wBAAwB,EACxB,oBAAoB,EACpB,wBAAwB,EACxB,qBAAqB,EACrB,kBAAkB,EAClB,YAAY,EACZ,2BAA2B,EAC3B,4BAA4B,EAC5B,kBAAkB,GACnB,MAAM,eAAe,CAAC;AAEvB;;;;GAIG;AACH,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEnE;;;;GAIG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,YAAY,EACV,YAAY,EACZ,WAAW,EACX,WAAW,EACX,eAAe,EACf,UAAU,EACV,kBAAkB,EAClB,aAAa,EACb,eAAe,EACf,aAAa,EACb,UAAU,EACV,aAAa,EACb,aAAa,EACb,oBAAoB,EACpB,cAAc,EACd,mBAAmB,EACnB,eAAe,EACf,oBAAoB,EACpB,gBAAgB,EAChB,mBAAmB,EACnB,cAAc,EACd,gBAAgB,EAChB,YAAY,EACZ,aAAa,EACb,KAAK,EACL,UAAU,EACV,mBAAmB,EACnB,iBAAiB,EACjB,uBAAuB,EACvB,gBAAgB,EAChB,sBAAsB,EACtB,8BAA8B,EAC9B,qBAAqB,EACrB,oBAAoB,EACpB,kBAAkB,EAClB,sBAAsB,EACtB,qBAAqB,EACrB,eAAe,EACf,aAAa,EACb,YAAY,EACZ,uBAAuB,EACvB,uBAAuB,EACvB,6BAA6B,EAC7B,6BAA6B,EAC7B,eAAe,EACf,gBAAgB,EAChB,2BAA2B,EAC3B,cAAc,EACd,oBAAoB,EACpB,gBAAgB,EAChB,8BAA8B,EAC9B,WAAW,EACX,iBAAiB,EACjB,0BAA0B,EAC1B,WAAW,EACX,gBAAgB,EAChB,eAAe,EACf,8BAA8B,EAC9B,2BAA2B,EAC3B,eAAe,EACf,sBAAsB,EACtB,qBAAqB,EACrB,wBAAwB,GACzB,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/blocks/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAQH,OAAO,0BAA0B,CAAC;AAElC,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AACnF,YAAY,EAAE,wBAAwB,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAE1F;;;;;;;;;;GAUG;AACH,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAExD,OAAO,EACL,YAAY,EACZ,mBAAmB,EACnB,gBAAgB,EAChB,wBAAwB,GACzB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5E,OAAO,EACL,aAAa,EACb,UAAU,EACV,WAAW,EACX,YAAY,EACZ,cAAc,EACd,wBAAwB,GACzB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAE1F,OAAO,EACL,2BAA2B,EAC3B,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACL,8BAA8B,EAC9B,2BAA2B,EAC3B,wBAAwB,EACxB,uBAAuB,EACvB,sBAAsB,EACtB,sBAAsB,GACvB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAE3D,OAAO,EACL,SAAS,EACT,6BAA6B,EAC7B,wBAAwB,EACxB,6BAA6B,GAC9B,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,gBAAgB,EAChB,oBAAoB,EACpB,wBAAwB,EACxB,yBAAyB,EACzB,wBAAwB,EACxB,oBAAoB,EACpB,wBAAwB,EACxB,qBAAqB,EACrB,kBAAkB,EAClB,YAAY,EACZ,2BAA2B,EAC3B,4BAA4B,EAC5B,kBAAkB,GACnB,MAAM,eAAe,CAAC;AAEvB;;;;GAIG;AACH,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEnE;;;;GAIG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,YAAY,EACV,YAAY,EACZ,WAAW,EACX,WAAW,EACX,eAAe,EACf,UAAU,EACV,kBAAkB,EAClB,aAAa,EACb,eAAe,EACf,aAAa,EACb,UAAU,EACV,aAAa,EACb,oBAAoB,EACpB,cAAc,EACd,mBAAmB,EACnB,YAAY,EACZ,eAAe,EACf,oBAAoB,EACpB,gBAAgB,EAChB,mBAAmB,EACnB,cAAc,EACd,gBAAgB,EAChB,YAAY,EACZ,aAAa,EACb,KAAK,EACL,UAAU,EACV,mBAAmB,EACnB,iBAAiB,EACjB,uBAAuB,EACvB,gBAAgB,EAChB,sBAAsB,EACtB,8BAA8B,EAC9B,qBAAqB,EACrB,oBAAoB,EACpB,kBAAkB,EAClB,sBAAsB,EACtB,qBAAqB,EACrB,eAAe,EACf,aAAa,EACb,YAAY,EACZ,uBAAuB,EACvB,uBAAuB,EACvB,6BAA6B,EAC7B,6BAA6B,EAC7B,eAAe,EACf,gBAAgB,EAChB,2BAA2B,EAC3B,cAAc,EACd,oBAAoB,EACpB,gBAAgB,EAChB,8BAA8B,EAC9B,WAAW,EACX,iBAAiB,EACjB,0BAA0B,EAC1B,WAAW,EACX,gBAAgB,EAChB,eAAe,EACf,8BAA8B,EAC9B,2BAA2B,EAC3B,eAAe,EACf,sBAAsB,EACtB,qBAAqB,EACrB,wBAAwB,GACzB,MAAM,YAAY,CAAC"}
@@ -1,10 +1,12 @@
1
1
  /**
2
2
  * `@civitai/app-sdk/blocks` — framework-agnostic contract for Civitai Apps.
3
3
  *
4
- * This subpath exports the manifest type, scope strings, postMessage protocol,
5
- * and the `defineBlock` validator. Hooks and transport implementations live in
6
- * a separate package (see `@civitai/blocks-react`) so this module stays usable
7
- * from any runtime — Node, browsers, workers — with no React dependency.
4
+ * This subpath exports the manifest type, scope strings and postMessage
5
+ * protocol. Hooks and transport implementations live in a separate package (see
6
+ * `@civitai/blocks-react`) so this module stays usable from any runtime — Node,
7
+ * browsers, workers — with no React dependency and no runtime dependencies at
8
+ * all. Build-time manifest validation lives at `@civitai/app-sdk/manifest`
9
+ * (node-only); see the note on `BlockManifestError` below.
8
10
  */
9
11
  // FIRST import, on purpose. Blocks are framed at an opaque origin (sandbox
10
12
  // without `allow-same-origin`), where touching `localStorage`/`sessionStorage`
@@ -14,7 +16,18 @@
14
16
  // or is absent (Node/SSR/workers); see `../safe-storage/index.ts`.
15
17
  import '../safe-storage/index.js';
16
18
  export { installSafeStorage, createMemoryStorage } from '../safe-storage/index.js';
17
- export { defineBlock, BlockManifestError } from './defineBlock.js';
19
+ /**
20
+ * `defineBlock` MOVED to `@civitai/app-sdk/manifest` (a NODE-ONLY subpath) in
21
+ * the release that closed #330. It now validates by compiling the vendored
22
+ * canonical schema with Ajv instead of maintaining a hand-written mirror of it,
23
+ * which needs `node:fs` and a runtime dependency — neither of which belongs on
24
+ * this browser-facing, zero-dependency surface. Most callers want the Vite
25
+ * plugin at `@civitai/app-sdk/vite` rather than the function.
26
+ *
27
+ * `BlockManifestError` stays exported here, from its own module, so
28
+ * `instanceof` means the same thing on both subpaths.
29
+ */
30
+ export { BlockManifestError } from './manifestError.js';
18
31
  export { BLOCK_SCOPES, BLOCK_SCOPE_PATTERN, BLOCK_CATEGORIES, BLOCK_TAGLINE_MAX_LENGTH, } from './scopes.js';
19
32
  export { BrowsingLevel, SFW_LEVELS, NSFW_LEVELS, isSfwCeiling, isLevelAllowed, effectiveBrowsingCeiling, } from './browsingLevel.js';
20
33
  export { APP_STORAGE_MAX_VALUE_BYTES, APP_STORAGE_MAX_BYTES, APP_STORAGE_MAX_ROWS, } from './appStorageLimits.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/blocks/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,2EAA2E;AAC3E,+EAA+E;AAC/E,6EAA6E;AAC7E,4EAA4E;AAC5E,8EAA8E;AAC9E,mEAAmE;AACnE,OAAO,0BAA0B,CAAC;AAElC,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAGnF,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAGnE,OAAO,EACL,YAAY,EACZ,mBAAmB,EACnB,gBAAgB,EAChB,wBAAwB,GACzB,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,aAAa,EACb,UAAU,EACV,WAAW,EACX,YAAY,EACZ,cAAc,EACd,wBAAwB,GACzB,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EACL,2BAA2B,EAC3B,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACL,8BAA8B,EAC9B,2BAA2B,EAC3B,wBAAwB,EACxB,uBAAuB,EACvB,sBAAsB,EACtB,sBAAsB,GACvB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EACL,SAAS,EACT,6BAA6B,EAC7B,wBAAwB,EACxB,6BAA6B,GAC9B,MAAM,eAAe,CAAC;AAiBvB;;;;GAIG;AACH,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEnE;;;;GAIG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/blocks/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,2EAA2E;AAC3E,+EAA+E;AAC/E,6EAA6E;AAC7E,4EAA4E;AAC5E,8EAA8E;AAC9E,mEAAmE;AACnE,OAAO,0BAA0B,CAAC;AAElC,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAGnF;;;;;;;;;;GAUG;AACH,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAExD,OAAO,EACL,YAAY,EACZ,mBAAmB,EACnB,gBAAgB,EAChB,wBAAwB,GACzB,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,aAAa,EACb,UAAU,EACV,WAAW,EACX,YAAY,EACZ,cAAc,EACd,wBAAwB,GACzB,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EACL,2BAA2B,EAC3B,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACL,8BAA8B,EAC9B,2BAA2B,EAC3B,wBAAwB,EACxB,uBAAuB,EACvB,sBAAsB,EACtB,sBAAsB,GACvB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EACL,SAAS,EACT,6BAA6B,EAC7B,wBAAwB,EACxB,6BAA6B,GAC9B,MAAM,eAAe,CAAC;AAiBvB;;;;GAIG;AACH,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEnE;;;;GAIG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * The manifest-validation error type, in its own zero-dependency module.
3
+ *
4
+ * It lives here rather than beside `defineBlock` because `defineBlock` now
5
+ * lives at the node-only `@civitai/app-sdk/manifest` subpath (it reads the
6
+ * vendored canonical schema off disk and compiles it with Ajv). The error class
7
+ * has to stay importable from the browser-safe `./blocks` surface so that
8
+ * `instanceof BlockManifestError` means the same thing on both sides — one
9
+ * class, one module, no duplicate identity.
10
+ */
11
+ /** Thrown by `defineBlock` for any manifest violation. */
12
+ export declare class BlockManifestError extends Error {
13
+ /** Dot-path to the offending field, e.g. `iframe.sandbox`. */
14
+ readonly field?: string | undefined;
15
+ readonly name = "BlockManifestError";
16
+ constructor(message: string,
17
+ /** Dot-path to the offending field, e.g. `iframe.sandbox`. */
18
+ field?: string | undefined);
19
+ }
20
+ //# sourceMappingURL=manifestError.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifestError.d.ts","sourceRoot":"","sources":["../../src/blocks/manifestError.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,0DAA0D;AAC1D,qBAAa,kBAAmB,SAAQ,KAAK;IAIzC,8DAA8D;IAC9D,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM;IAJzB,SAAkB,IAAI,wBAAwB;gBAE5C,OAAO,EAAE,MAAM;IACf,8DAA8D;IACrD,KAAK,CAAC,EAAE,MAAM,YAAA;CAI1B"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * The manifest-validation error type, in its own zero-dependency module.
3
+ *
4
+ * It lives here rather than beside `defineBlock` because `defineBlock` now
5
+ * lives at the node-only `@civitai/app-sdk/manifest` subpath (it reads the
6
+ * vendored canonical schema off disk and compiles it with Ajv). The error class
7
+ * has to stay importable from the browser-safe `./blocks` surface so that
8
+ * `instanceof BlockManifestError` means the same thing on both sides — one
9
+ * class, one module, no duplicate identity.
10
+ */
11
+ /** Thrown by `defineBlock` for any manifest violation. */
12
+ export class BlockManifestError extends Error {
13
+ field;
14
+ name = 'BlockManifestError';
15
+ constructor(message,
16
+ /** Dot-path to the offending field, e.g. `iframe.sandbox`. */
17
+ field) {
18
+ super(message);
19
+ this.field = field;
20
+ }
21
+ }
22
+ //# sourceMappingURL=manifestError.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifestError.js","sourceRoot":"","sources":["../../src/blocks/manifestError.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,0DAA0D;AAC1D,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IAKhC;IAJO,IAAI,GAAG,oBAAoB,CAAC;IAC9C,YACE,OAAe;IACf,8DAA8D;IACrD,KAAc;QAEvB,KAAK,CAAC,OAAO,CAAC,CAAC;QAFN,UAAK,GAAL,KAAK,CAAS;IAGzB,CAAC;CACF"}
@@ -67,11 +67,15 @@ export type BlockCategory = (typeof BLOCK_CATEGORIES)[number];
67
67
  * `src/server/services/block-manifest-validator.service.ts` (the authoritative
68
68
  * gate) and the `tagline.maxLength` in the canonical schema
69
69
  * (https://civitai.com/schemas/app-block/v1.json). Keep all three in lockstep —
70
- * the schema-parity test asserts this const equals the vendored schema's bound.
70
+ * `test/manifest/canonical-derivation.test.ts` asserts this const equals the
71
+ * vendored schema's bound.
71
72
  *
72
73
  * NOTE: the SERVER measures the TRIMMED length; JSON Schema's `maxLength` counts
73
- * the raw string. `defineBlock` mirrors the server (trimmed) so an author is
74
- * never rejected locally for padding the server would ignore.
74
+ * the RAW string, so a tagline padded with whitespace past 140 is rejected by
75
+ * the schema and accepted by the server. `defineBlock` takes the SCHEMA's
76
+ * verdict — it applies no relaxation to any canonical rule — so that one shape
77
+ * is rejected locally. The canonical documents the asymmetry as deliberate
78
+ * ("this schema is never more permissive than the server"); trim the tagline.
75
79
  */
76
80
  export declare const BLOCK_TAGLINE_MAX_LENGTH = 140;
77
81
  //# sourceMappingURL=scopes.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"scopes.d.ts","sourceRoot":"","sources":["../../src/blocks/scopes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,YAAY;;;;;;;;;;;;;;CAsCf,CAAC;AAEX,MAAM,MAAM,aAAa,GAAG,MAAM,OAAO,YAAY,CAAC;AACtD,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,aAAa,CAAC,CAAC;AAE9D;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,mBAAmB,QAAuC,CAAC;AAExE;;;;;;;;;;GAUG;AACH,eAAO,MAAM,gBAAgB,8FAQnB,CAAC;AAEX,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE9D;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,wBAAwB,MAAM,CAAC"}
1
+ {"version":3,"file":"scopes.d.ts","sourceRoot":"","sources":["../../src/blocks/scopes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,YAAY;;;;;;;;;;;;;;CAsCf,CAAC;AAEX,MAAM,MAAM,aAAa,GAAG,MAAM,OAAO,YAAY,CAAC;AACtD,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,aAAa,CAAC,CAAC;AAE9D;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,mBAAmB,QAAuC,CAAC;AAExE;;;;;;;;;;GAUG;AACH,eAAO,MAAM,gBAAgB,8FAQnB,CAAC;AAEX,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE9D;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,wBAAwB,MAAM,CAAC"}
@@ -96,11 +96,15 @@ export const BLOCK_CATEGORIES = [
96
96
  * `src/server/services/block-manifest-validator.service.ts` (the authoritative
97
97
  * gate) and the `tagline.maxLength` in the canonical schema
98
98
  * (https://civitai.com/schemas/app-block/v1.json). Keep all three in lockstep —
99
- * the schema-parity test asserts this const equals the vendored schema's bound.
99
+ * `test/manifest/canonical-derivation.test.ts` asserts this const equals the
100
+ * vendored schema's bound.
100
101
  *
101
102
  * NOTE: the SERVER measures the TRIMMED length; JSON Schema's `maxLength` counts
102
- * the raw string. `defineBlock` mirrors the server (trimmed) so an author is
103
- * never rejected locally for padding the server would ignore.
103
+ * the RAW string, so a tagline padded with whitespace past 140 is rejected by
104
+ * the schema and accepted by the server. `defineBlock` takes the SCHEMA's
105
+ * verdict — it applies no relaxation to any canonical rule — so that one shape
106
+ * is rejected locally. The canonical documents the asymmetry as deliberate
107
+ * ("this schema is never more permissive than the server"); trim the tagline.
104
108
  */
105
109
  export const BLOCK_TAGLINE_MAX_LENGTH = 140;
106
110
  //# sourceMappingURL=scopes.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"scopes.js","sourceRoot":"","sources":["../../src/blocks/scopes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,gBAAgB,EAAE,kBAAkB;IACpC,cAAc,EAAE,gBAAgB;IAChC,iBAAiB,EAAE,mBAAmB;IACtC,cAAc,EAAE,gBAAgB;IAChC,eAAe,EAAE,iBAAiB;IAClC,0EAA0E;IAC1E,8EAA8E;IAC9E,gEAAgE;IAChE,iBAAiB,EAAE,mBAAmB;IACtC,kBAAkB,EAAE,oBAAoB;IACxC,+EAA+E;IAC/E,+EAA+E;IAC/E,6EAA6E;IAC7E,6EAA6E;IAC7E,wBAAwB,EAAE,0BAA0B;IACpD,yBAAyB,EAAE,2BAA2B;IACtD,8EAA8E;IAC9E,6EAA6E;IAC7E,0EAA0E;IAC1E,4EAA4E;IAC5E,kFAAkF;IAClF,yEAAyE;IACzE,qFAAqF;IACrF,qBAAqB,EAAE,uBAAuB;IAC9C,sBAAsB,EAAE,wBAAwB;IAChD,wBAAwB,EAAE,0BAA0B;IACpD,4EAA4E;IAC5E,qEAAqE;IACrE,8EAA8E;IAC9E,4DAA4D;IAC5D,2EAA2E;IAC3E,uEAAuE;IACvE,2EAA2E;IAC3E,8EAA8E;IAC9E,6EAA6E;IAC7E,8BAA8B;IAC9B,gBAAgB,EAAE,kBAAkB;CAC5B,CAAC;AAKX;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,oCAAoC,CAAC;AAExE;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,YAAY;IACZ,OAAO;IACP,SAAS;IACT,WAAW;IACX,YAAY;IACZ,WAAW;IACX,OAAO;CACC,CAAC;AAIX;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,GAAG,CAAC"}
1
+ {"version":3,"file":"scopes.js","sourceRoot":"","sources":["../../src/blocks/scopes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,gBAAgB,EAAE,kBAAkB;IACpC,cAAc,EAAE,gBAAgB;IAChC,iBAAiB,EAAE,mBAAmB;IACtC,cAAc,EAAE,gBAAgB;IAChC,eAAe,EAAE,iBAAiB;IAClC,0EAA0E;IAC1E,8EAA8E;IAC9E,gEAAgE;IAChE,iBAAiB,EAAE,mBAAmB;IACtC,kBAAkB,EAAE,oBAAoB;IACxC,+EAA+E;IAC/E,+EAA+E;IAC/E,6EAA6E;IAC7E,6EAA6E;IAC7E,wBAAwB,EAAE,0BAA0B;IACpD,yBAAyB,EAAE,2BAA2B;IACtD,8EAA8E;IAC9E,6EAA6E;IAC7E,0EAA0E;IAC1E,4EAA4E;IAC5E,kFAAkF;IAClF,yEAAyE;IACzE,qFAAqF;IACrF,qBAAqB,EAAE,uBAAuB;IAC9C,sBAAsB,EAAE,wBAAwB;IAChD,wBAAwB,EAAE,0BAA0B;IACpD,4EAA4E;IAC5E,qEAAqE;IACrE,8EAA8E;IAC9E,4DAA4D;IAC5D,2EAA2E;IAC3E,uEAAuE;IACvE,2EAA2E;IAC3E,8EAA8E;IAC9E,6EAA6E;IAC7E,8BAA8B;IAC9B,gBAAgB,EAAE,kBAAkB;CAC5B,CAAC;AAKX;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,oCAAoC,CAAC;AAExE;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,YAAY;IACZ,OAAO;IACP,SAAS;IACT,WAAW;IACX,YAAY;IACZ,WAAW;IACX,OAAO;CACC,CAAC;AAIX;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,GAAG,CAAC"}
@@ -1335,20 +1335,47 @@ export type ManifestSettings = Record<string, ManifestSettingField>;
1335
1335
  export type ContentRating = 'g' | 'pg' | 'pg13' | 'r' | 'x';
1336
1336
  export interface ManifestTarget {
1337
1337
  slotId: string;
1338
- priority: number;
1338
+ /**
1339
+ * Slot ordering hint. NOT a property of the canonical schema (whose target
1340
+ * items require only `slotId`), so it is optional and shape-checked only.
1341
+ */
1342
+ priority?: number;
1339
1343
  requiredContext?: string[];
1340
1344
  }
1345
+ /**
1346
+ * iframe envelope. Every field is optional — the canonical schema declares no
1347
+ * `required` list here, and the platform supplies its own defaults.
1348
+ */
1341
1349
  export interface ManifestIframe {
1342
- src: string;
1343
- minHeight: number;
1344
- /** Optional. Omit or set to `null` for no cap. */
1350
+ /**
1351
+ * SERVER-OWNED — the platform stamps the canonical bundle URL at
1352
+ * build/approve time. Typed `never` so setting it is a compile error;
1353
+ * `defineBlock` rejects it at runtime too. Until #330 the SDK REQUIRED this
1354
+ * field, which made every valid manifest fail local validation and every
1355
+ * locally-valid manifest fail `civitai app submit`.
1356
+ */
1357
+ src?: never;
1358
+ /** Canonical bounds: integer, 40–4000 px. */
1359
+ minHeight?: number;
1360
+ /** Optional. Omit or set to `null` for no cap. Canonical bounds: 40–4000 px. */
1345
1361
  maxHeight?: number | null;
1346
- resizable: boolean;
1347
- sandbox: string;
1362
+ resizable?: boolean;
1363
+ sandbox?: string;
1348
1364
  }
1349
- export interface ManifestAsset {
1350
- url: string;
1351
- integrity: string;
1365
+ /** Full-page surface descriptor (W10). Page apps mount at `/apps/run/<slug>`. */
1366
+ export interface ManifestPage {
1367
+ /** Sub-path the page mounts at; must start with `/`. */
1368
+ path: string;
1369
+ /** Title shown in host chrome. */
1370
+ title: string;
1371
+ icon?: string;
1372
+ /**
1373
+ * Per-generation Buzz SAFETY CEILING for `ai:write:budgeted` tokens — a
1374
+ * ceiling against a drained wallet, NOT a cost forecast. Size it well above
1375
+ * your worst-case generation; a budget set to your estimate becomes a hard
1376
+ * outage the moment real cost drifts up.
1377
+ */
1378
+ buzzBudgetPerGen?: number;
1352
1379
  }
1353
1380
  export interface ManifestPreview {
1354
1381
  thumbnail: string;
@@ -1358,18 +1385,42 @@ export interface ManifestPreview {
1358
1385
  /**
1359
1386
  * v1 manifest shape. Mirrors `schemas/app-block/v1.json` — keep them in sync.
1360
1387
  *
1361
- * The trailing `renderMode` / `assetBundle` / `trustTier` fields are
1362
- * forward-compat hooks for v2 inline mode; they are accepted but unused
1363
- * by the v1 iframe runtime.
1388
+ * REQUIRED HERE = REQUIRED THERE. Only `blockId`, `version`, `name`,
1389
+ * `contentRating` and `scopes` are required, because those are exactly the five
1390
+ * entries in the canonical schema's `required` array. Before #330 this
1391
+ * interface required eleven (including `appId`, which the canonical does not
1392
+ * declare at all, and `iframe.src`, which the platform REFUSES), so the type
1393
+ * itself rejected every manifest the starters ship.
1364
1394
  */
1365
1395
  export interface BlockManifestV1 {
1366
- $schema: 'https://civitai.com/schemas/app-block/v1.json';
1367
- appId: string;
1396
+ /**
1397
+ * Optional JSON-Schema reference. The canonical types it as a plain string
1398
+ * and its own description says it is "ignored by the platform validator", so
1399
+ * `defineBlock` does NOT constrain the value — point it at a vendored copy or
1400
+ * a preview draft if that is what your editor needs. Until #330 a mismatch
1401
+ * was a hard throw, which (once the gate was wired into Vite) failed the
1402
+ * build on a field the server provably ignores.
1403
+ *
1404
+ * The union below is an AUTOCOMPLETE NUDGE, not a rule: `string & {}` keeps
1405
+ * the literal visible in editor suggestions while still admitting any string.
1406
+ */
1407
+ $schema?: 'https://civitai.com/schemas/app-block/v1.json' | (string & {});
1408
+ /**
1409
+ * NOT a canonical manifest property, and NOT validated. Your app id lives in
1410
+ * `civitai.app.json` (`{"appId": "..."}`), which is what the `civitai` CLI
1411
+ * reads. The canonical does not forbid extra top-level keys, so the server
1412
+ * ignores this one; the scaffolds still carry `"app_REPLACE_ME"` and it is
1413
+ * inert.
1414
+ */
1415
+ appId?: string;
1368
1416
  blockId: string;
1369
1417
  version: string;
1418
+ /** Human-readable display name. Non-empty; the canonical imposes NO length cap. */
1370
1419
  name: string;
1371
- type: 'block' | 'embed';
1372
- targets: ManifestTarget[];
1420
+ /** Canonical enum — `block` is the only member. */
1421
+ type?: 'block';
1422
+ /** Optional (page-only apps declare none). Canonical cap: 16 entries. */
1423
+ targets?: ManifestTarget[];
1373
1424
  scopes: string[];
1374
1425
  /**
1375
1426
  * Optional per-scope justification: a map of scope-id → free-text rationale
@@ -1381,8 +1432,17 @@ export interface BlockManifestV1 {
1381
1432
  * with the canonical schema's `scopeJustifications` (civitai #3195).
1382
1433
  */
1383
1434
  scopeJustifications?: Record<string, string>;
1384
- iframe: ManifestIframe;
1385
- assets?: ManifestAsset[];
1435
+ /** Optional; the canonical declares no required sub-field. */
1436
+ iframe?: ManifestIframe;
1437
+ /** Full-page surface descriptor (W10). */
1438
+ page?: ManifestPage;
1439
+ /**
1440
+ * The app's shipped `index.html` paints its own loading state inside `#root`,
1441
+ * so the full-page run host stands down its branded overlay. Only declare it
1442
+ * if the markup really exists — with the overlay gone, an empty `#root` is a
1443
+ * blank iframe for the whole load.
1444
+ */
1445
+ bootSkeleton?: boolean;
1386
1446
  /**
1387
1447
  * Per-field settings declaration the platform validates user input
1388
1448
  * against AND renders the publisher/viewer settings UI from. v0 shape;
@@ -1403,26 +1463,54 @@ export interface BlockManifestV1 {
1403
1463
  * + detail page. Manifest-governed: it flows to the store listing on
1404
1464
  * moderator-approve and is re-synced from the manifest on every subsequent
1405
1465
  * approved version — for an ON-SITE app the manifest is the ONLY surface that
1406
- * sets it. Omit it and the store simply shows no tagline. Trimmed and capped at
1466
+ * sets it. Omit it and the store simply shows no tagline. Capped at
1407
1467
  * {@link BLOCK_TAGLINE_MAX_LENGTH} (140) characters, the same bound off-site
1408
1468
  * listings use, so both store kinds render the same slot. Kept in lockstep with
1409
- * the canonical schema's `tagline` (civitai #3441).
1469
+ * the canonical schema's `tagline` (civitai #3441). NOTE the canonical counts
1470
+ * the RAW string while the server measures the trimmed one, and `defineBlock`
1471
+ * takes the canonical's verdict — so trim before you count.
1410
1472
  */
1411
1473
  tagline?: string;
1474
+ /**
1475
+ * Optional PUBLIC source-repository link rendered as a `Source` row on the
1476
+ * app's store detail page. `https://` root URL on github.com, gitlab.com or
1477
+ * codeberg.org, at most 200 chars. `defineBlock` mirrors the canonical's
1478
+ * COARSE pattern; the server applies stricter per-segment rules, so passing
1479
+ * locally is necessary, not sufficient.
1480
+ */
1481
+ repository?: string;
1412
1482
  preview?: ManifestPreview;
1413
1483
  promotionEligible?: boolean;
1414
- minApiVersion: string;
1484
+ /** Optional; dot-separated integers (e.g. `"1"` or `"1.0"`). Informational. */
1485
+ minApiVersion?: string;
1415
1486
  /**
1416
1487
  * Author-declared mode preference. `hybrid` is a manifest-only hint that
1417
1488
  * the host resolves to a concrete `iframe` | `inline` value before sending
1418
1489
  * `BLOCK_INIT` — that's why `BlockInitPayload.renderMode` is narrower.
1419
1490
  */
1420
1491
  renderMode?: 'iframe' | 'inline' | 'hybrid';
1492
+ /**
1493
+ * Config-as-code: the command the platform runs to build the static bundle.
1494
+ * One of an allowlisted set (`npm|pnpm|yarn run <script>`, `vite build`,
1495
+ * `npx vite build`). When set, `outputDir` is REQUIRED.
1496
+ */
1497
+ buildCommand?: string;
1498
+ /** Directory `buildCommand` emits into. Safe relative path; required with `buildCommand`. */
1499
+ outputDir?: string;
1500
+ /** Allowlist of settings keys exposed to anonymous viewers. Max 32 keys, 64 chars each. */
1501
+ publicSettingsKeys?: string[];
1502
+ /** Optional v2 surface — a public `https://` URL to a hosted asset bundle. */
1503
+ assetBundleUrl?: string;
1421
1504
  assetBundle?: {
1422
1505
  url: string | null;
1423
1506
  sha256: string | null;
1424
1507
  };
1425
- trustTier?: 'unverified' | 'verified' | 'internal';
1508
+ /**
1509
+ * SERVER-OWNED — the platform assigns the trust tier during review. Typed
1510
+ * `never` so setting it is a compile error; `defineBlock` rejects it at
1511
+ * runtime too.
1512
+ */
1513
+ trustTier?: never;
1426
1514
  }
1427
1515
  export type BlockManifest = BlockManifestV1;
1428
1516
  /**