@civitai/app-sdk 0.75.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/messages.d.ts +15 -0
- 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
|
|
@@ -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
|
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