@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 +76 -32
- package/dist/blocks/index.d.ts +19 -7
- package/dist/blocks/index.d.ts.map +1 -1
- package/dist/blocks/index.js +18 -5
- package/dist/blocks/index.js.map +1 -1
- package/dist/blocks/manifestError.d.ts +20 -0
- package/dist/blocks/manifestError.d.ts.map +1 -0
- package/dist/blocks/manifestError.js +22 -0
- package/dist/blocks/manifestError.js.map +1 -0
- package/dist/blocks/scopes.d.ts +7 -3
- package/dist/blocks/scopes.d.ts.map +1 -1
- package/dist/blocks/scopes.js +7 -3
- package/dist/blocks/scopes.js.map +1 -1
- package/dist/blocks/types.d.ts +110 -22
- package/dist/blocks/types.d.ts.map +1 -1
- package/dist/manifest/defineBlock.d.ts +145 -0
- package/dist/manifest/defineBlock.d.ts.map +1 -0
- package/dist/manifest/defineBlock.js +390 -0
- package/dist/manifest/defineBlock.js.map +1 -0
- package/dist/manifest/index.d.ts +19 -0
- package/dist/manifest/index.d.ts.map +1 -0
- package/dist/manifest/index.js +17 -0
- package/dist/manifest/index.js.map +1 -0
- package/dist/vite/index.d.ts +14 -0
- package/dist/vite/index.d.ts.map +1 -0
- package/dist/vite/index.js +61 -0
- package/dist/vite/index.js.map +1 -0
- package/package.json +42 -2
- package/dist/blocks/defineBlock.d.ts +0 -53
- package/dist/blocks/defineBlock.d.ts.map +0 -1
- package/dist/blocks/defineBlock.js +0 -338
- package/dist/blocks/defineBlock.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/manifest/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACpG,YAAY,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@civitai/app-sdk/manifest` — NODE-ONLY build-time manifest validation.
|
|
3
|
+
*
|
|
4
|
+
* Separate from `./blocks` on purpose. `./blocks` is the browser contract and
|
|
5
|
+
* carries ZERO runtime dependencies (it ships into sandboxed iframes and every
|
|
6
|
+
* app inherits its install graph). This subpath reads the vendored canonical
|
|
7
|
+
* schema off disk with `node:fs` and compiles it with Ajv, which is an OPTIONAL
|
|
8
|
+
* PEER — install it where you use this:
|
|
9
|
+
*
|
|
10
|
+
* pnpm add -D ajv
|
|
11
|
+
*
|
|
12
|
+
* Most callers want `@civitai/app-sdk/vite` instead, which wraps `defineBlock`
|
|
13
|
+
* in a Vite plugin that runs on every dev-server boot and every build.
|
|
14
|
+
*/
|
|
15
|
+
export { defineBlock, SCHEMA_DIVERGENCES, KNOWN_GAPS, loadCanonicalSchema } from './defineBlock.js';
|
|
16
|
+
export { BlockManifestError } from '../blocks/manifestError.js';
|
|
17
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/manifest/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAEpG,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Plugin } from 'vite';
|
|
2
|
+
export interface BlockManifestPluginOptions {
|
|
3
|
+
/**
|
|
4
|
+
* Path to the manifest. Relative paths resolve against `root`, which defaults
|
|
5
|
+
* to the Vite project root reported by `configResolved`. Defaults to
|
|
6
|
+
* `./block.manifest.json`.
|
|
7
|
+
*/
|
|
8
|
+
manifestPath?: string;
|
|
9
|
+
/** Overrides the Vite project root used to resolve a relative `manifestPath`. */
|
|
10
|
+
root?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function blockManifestPlugin(options?: BlockManifestPluginOptions): Plugin;
|
|
13
|
+
export { BlockManifestError } from '../blocks/manifestError.js';
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vite/index.ts"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAKnC,MAAM,WAAW,0BAA0B;IACzC;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iFAAiF;IACjF,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,0BAA+B,GAAG,MAAM,CA+BpF;AAGD,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@civitai/app-sdk/vite` — NODE-ONLY Vite plugin that validates
|
|
3
|
+
* `block.manifest.json` on every dev-server boot and every build.
|
|
4
|
+
*
|
|
5
|
+
* WHY THIS EXISTS. `defineBlock` shipped for months with zero callers outside
|
|
6
|
+
* markdown (#330), so the manifests this repo ships were never validated by
|
|
7
|
+
* anything and the validator was free to drift into rejecting all of them. A
|
|
8
|
+
* gate that only runs where someone remembers to run it is the defect, not the
|
|
9
|
+
* fix. This is the cheapest place that runs unavoidably: `pnpm dev`,
|
|
10
|
+
* `pnpm dev:harness` and `pnpm build` all pass through `configResolved`.
|
|
11
|
+
*
|
|
12
|
+
* `configResolved` rather than `buildStart` on purpose — it is the one hook
|
|
13
|
+
* Vite calls in BOTH the dev-server and build paths, so there is no mode in
|
|
14
|
+
* which the check silently does not run. It is kept SYNCHRONOUS so the throw
|
|
15
|
+
* cannot depend on whether Vite awaits the hook.
|
|
16
|
+
*
|
|
17
|
+
* This is NOT a substitute for `civitai app validate` (the Go CLI's own local
|
|
18
|
+
* pre-check against the same canonical). It exists so a manifest mistake
|
|
19
|
+
* surfaces in the dev loop rather than at submit time.
|
|
20
|
+
*
|
|
21
|
+
* PEERS: `ajv` (used at runtime by `@civitai/app-sdk/manifest`) and `vite`
|
|
22
|
+
* (types only). Both are optional peers of `@civitai/app-sdk`; a scaffold using
|
|
23
|
+
* this plugin needs both in its devDependencies.
|
|
24
|
+
*/
|
|
25
|
+
import { readFileSync } from 'node:fs';
|
|
26
|
+
import { resolve } from 'node:path';
|
|
27
|
+
import { BlockManifestError, defineBlock } from '../manifest/index.js';
|
|
28
|
+
export function blockManifestPlugin(options = {}) {
|
|
29
|
+
return {
|
|
30
|
+
name: 'civitai-block-manifest',
|
|
31
|
+
// `enforce: 'pre'` so the manifest error is the FIRST thing reported rather
|
|
32
|
+
// than being buried under whatever else a slow plugin logs.
|
|
33
|
+
enforce: 'pre',
|
|
34
|
+
configResolved(config) {
|
|
35
|
+
// Resolve against the Vite root (or an explicit override) rather than
|
|
36
|
+
// this module's own location — this file now lives inside the installed
|
|
37
|
+
// package, not next to the consumer's manifest.
|
|
38
|
+
const root = options.root ?? config?.root ?? process.cwd();
|
|
39
|
+
const manifestPath = resolve(root, options.manifestPath ?? './block.manifest.json');
|
|
40
|
+
let manifest;
|
|
41
|
+
try {
|
|
42
|
+
manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
throw new Error(`block.manifest.json could not be read or parsed (${manifestPath}): ${err.message}`);
|
|
46
|
+
}
|
|
47
|
+
try {
|
|
48
|
+
defineBlock({ manifest });
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
// Re-thrown with the field path up front: Vite prints the message, not
|
|
52
|
+
// the error's own properties, so `.field` would otherwise be invisible.
|
|
53
|
+
const field = err instanceof BlockManifestError && err.field ? ` [${err.field}]` : '';
|
|
54
|
+
throw new Error(`block.manifest.json is invalid${field}: ${err.message}`);
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
// Re-exported so a consumer can catch the typed error without a second import.
|
|
60
|
+
export { BlockManifestError } from '../blocks/manifestError.js';
|
|
61
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/vite/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAcvE,MAAM,UAAU,mBAAmB,CAAC,UAAsC,EAAE;IAC1E,OAAO;QACL,IAAI,EAAE,wBAAwB;QAC9B,4EAA4E;QAC5E,4DAA4D;QAC5D,OAAO,EAAE,KAAK;QACd,cAAc,CAAC,MAAM;YACnB,sEAAsE;YACtE,wEAAwE;YACxE,gDAAgD;YAChD,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,MAAM,EAAE,IAAI,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;YAC3D,MAAM,YAAY,GAAG,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,YAAY,IAAI,uBAAuB,CAAC,CAAC;YAEpF,IAAI,QAAuB,CAAC;YAC5B,IAAI,CAAC;gBACH,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,YAAY,EAAE,MAAM,CAAC,CAAkB,CAAC;YAC7E,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,IAAI,KAAK,CACb,oDAAoD,YAAY,MAAO,GAAa,CAAC,OAAO,EAAE,CAC/F,CAAC;YACJ,CAAC;YACD,IAAI,CAAC;gBACH,WAAW,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC;YAC5B,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,uEAAuE;gBACvE,wEAAwE;gBACxE,MAAM,KAAK,GAAG,GAAG,YAAY,kBAAkB,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;gBACtF,MAAM,IAAI,KAAK,CAAC,iCAAiC,KAAK,KAAM,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;YACvF,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC;AAED,+EAA+E;AAC/E,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@civitai/app-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.48.0",
|
|
4
4
|
"description": "OAuth + PKCE, encrypted-cookie sessions, scopes, and orchestrator helpers for building third-party Civitai apps.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -43,6 +43,14 @@
|
|
|
43
43
|
"types": "./dist/safe-storage/index.d.ts",
|
|
44
44
|
"import": "./dist/safe-storage/index.js"
|
|
45
45
|
},
|
|
46
|
+
"./manifest": {
|
|
47
|
+
"types": "./dist/manifest/index.d.ts",
|
|
48
|
+
"import": "./dist/manifest/index.js"
|
|
49
|
+
},
|
|
50
|
+
"./vite": {
|
|
51
|
+
"types": "./dist/vite/index.d.ts",
|
|
52
|
+
"import": "./dist/vite/index.js"
|
|
53
|
+
},
|
|
46
54
|
"./schemas/app-block/v1.json": "./schemas/app-block/v1.json"
|
|
47
55
|
},
|
|
48
56
|
"files": [
|
|
@@ -74,18 +82,49 @@
|
|
|
74
82
|
"`semver.satisfies('0.1.1-beta.0', '^0.2.0-beta.98')` === false.",
|
|
75
83
|
"Install with the tag: `pnpm add -D @civitai/client@beta`."
|
|
76
84
|
],
|
|
85
|
+
"comment-peerDependencies-ajv-vite": [
|
|
86
|
+
"ajv and vite are OPTIONAL peers of the NODE-ONLY subpaths added when #330",
|
|
87
|
+
"was closed: `./manifest` (defineBlock) and `./vite` (blockManifestPlugin).",
|
|
88
|
+
"",
|
|
89
|
+
"`dependencies` stays EMPTY for the same reason it always has: this package",
|
|
90
|
+
"ships into sandboxed browser iframes and every app inherits its install",
|
|
91
|
+
"graph. A build-time JSON-Schema engine has no business in that graph, so",
|
|
92
|
+
"ajv is a peer a scaffold opts into: `pnpm add -D ajv`.",
|
|
93
|
+
"",
|
|
94
|
+
"ajv: a VALUE import (`ajv/dist/2020.js`, the draft 2020-12 entry — the",
|
|
95
|
+
"canonical schema declares $schema draft/2020-12, so the default `ajv`",
|
|
96
|
+
"entry point would refuse to compile it). FLOOR ^8.17.1: `ajv/dist/2020`",
|
|
97
|
+
"has existed since 8.6.0, but 8.17.x is what this repo builds and tests",
|
|
98
|
+
"against, and 8.x is a single major so the caret admits every later 8.",
|
|
99
|
+
"",
|
|
100
|
+
"vite: TYPES ONLY (`import type { Plugin }`). Nothing is value-imported, so",
|
|
101
|
+
"there is no module-evaluation failure mode -- only a typecheck one. The",
|
|
102
|
+
"range spans 5-8 because `Plugin.configResolved(config)` (the one member",
|
|
103
|
+
"`./vite` reads) has been stable across all of them; this repo builds and",
|
|
104
|
+
"tests against vite 8, which is what every scaffold pins."
|
|
105
|
+
],
|
|
77
106
|
"peerDependencies": {
|
|
78
|
-
"@civitai/client": "^0.2.0-beta.98"
|
|
107
|
+
"@civitai/client": "^0.2.0-beta.98",
|
|
108
|
+
"ajv": "^8.17.1",
|
|
109
|
+
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0"
|
|
79
110
|
},
|
|
80
111
|
"peerDependenciesMeta": {
|
|
81
112
|
"@civitai/client": {
|
|
82
113
|
"optional": true
|
|
114
|
+
},
|
|
115
|
+
"ajv": {
|
|
116
|
+
"optional": true
|
|
117
|
+
},
|
|
118
|
+
"vite": {
|
|
119
|
+
"optional": true
|
|
83
120
|
}
|
|
84
121
|
},
|
|
85
122
|
"devDependencies": {
|
|
86
123
|
"@civitai/client": "0.2.0-beta.98",
|
|
87
124
|
"@types/node": "^25.9.1",
|
|
125
|
+
"ajv": "^8.17.1",
|
|
88
126
|
"typescript": "^5.9.2",
|
|
127
|
+
"vite": "^8.0.14",
|
|
89
128
|
"vitest": "^4.1.11"
|
|
90
129
|
},
|
|
91
130
|
"publishConfig": {
|
|
@@ -113,6 +152,7 @@
|
|
|
113
152
|
"build": "tsc -p tsconfig.json",
|
|
114
153
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
115
154
|
"test:types": "tsc -p tsconfig.typecheck.json",
|
|
155
|
+
"test:exports": "node scripts/check-exports.mjs",
|
|
116
156
|
"test": "vitest run && tsc -p tsconfig.typecheck.json",
|
|
117
157
|
"test:watch": "vitest"
|
|
118
158
|
}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Build-time / startup validation for a `BlockManifestV1`.
|
|
3
|
-
*
|
|
4
|
-
* This is a subset of what the civitai/civitai server enforces — the client
|
|
5
|
-
* runs it so authoring mistakes surface in `pnpm dev` rather than at
|
|
6
|
-
* `civitai deploy` time. Rules here MUST stay a strict subset of the
|
|
7
|
-
* server's; if the server tightens, mirror the change here.
|
|
8
|
-
*
|
|
9
|
-
* Every check guards `typeof` before pattern-testing — the highest-value
|
|
10
|
-
* caller is `JSON.parse(fs.readFileSync('block.manifest.json'))`, where
|
|
11
|
-
* the TypeScript type is a lie.
|
|
12
|
-
*/
|
|
13
|
-
import type { BlockManifest } from './types.js';
|
|
14
|
-
/** Thrown by `defineBlock` for any manifest violation. */
|
|
15
|
-
export declare class BlockManifestError extends Error {
|
|
16
|
-
/** Dot-path to the offending field, e.g. `iframe.sandbox`. */
|
|
17
|
-
readonly field?: string | undefined;
|
|
18
|
-
readonly name = "BlockManifestError";
|
|
19
|
-
constructor(message: string,
|
|
20
|
-
/** Dot-path to the offending field, e.g. `iframe.sandbox`. */
|
|
21
|
-
field?: string | undefined);
|
|
22
|
-
}
|
|
23
|
-
export interface DefineBlockConfig {
|
|
24
|
-
manifest: BlockManifest;
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Validates the manifest and returns it unchanged. Acts as a typed identity
|
|
28
|
-
* function — call it at module scope in the block app so violations throw
|
|
29
|
-
* before the app mounts. Mirrors a strict subset of the civitai/civitai server
|
|
30
|
-
* gate, so authoring mistakes surface in `pnpm dev` instead of at submit time.
|
|
31
|
-
* Throws {@link BlockManifestError} (with a `.field` dot-path) on a bad manifest.
|
|
32
|
-
*
|
|
33
|
-
* @example
|
|
34
|
-
* import { defineBlock } from '@civitai/app-sdk/blocks';
|
|
35
|
-
*
|
|
36
|
-
* export const manifest = defineBlock({
|
|
37
|
-
* manifest: {
|
|
38
|
-
* $schema: 'https://civitai.com/schemas/app-block/v1.json',
|
|
39
|
-
* appId: 'my-app',
|
|
40
|
-
* blockId: 'my-block',
|
|
41
|
-
* version: '1.0.0',
|
|
42
|
-
* name: 'My Block',
|
|
43
|
-
* type: 'block',
|
|
44
|
-
* targets: [{ slotId: 'model.sidebar_top', priority: 100 }],
|
|
45
|
-
* scopes: ['ai:write:budgeted'],
|
|
46
|
-
* iframe: { src: 'https://my-block.civit.ai/', minHeight: 200 },
|
|
47
|
-
* contentRating: 'pg',
|
|
48
|
-
* minApiVersion: '1.0',
|
|
49
|
-
* },
|
|
50
|
-
* });
|
|
51
|
-
*/
|
|
52
|
-
export declare function defineBlock(config: DefineBlockConfig): BlockManifest;
|
|
53
|
-
//# sourceMappingURL=defineBlock.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"defineBlock.d.ts","sourceRoot":"","sources":["../../src/blocks/defineBlock.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAQH,OAAO,KAAK,EAAE,aAAa,EAAiB,MAAM,YAAY,CAAC;AA8D/D,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;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,aAAa,CAAC;CAIzB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,iBAAiB,GAAG,aAAa,CA6HpE"}
|
|
@@ -1,338 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Build-time / startup validation for a `BlockManifestV1`.
|
|
3
|
-
*
|
|
4
|
-
* This is a subset of what the civitai/civitai server enforces — the client
|
|
5
|
-
* runs it so authoring mistakes surface in `pnpm dev` rather than at
|
|
6
|
-
* `civitai deploy` time. Rules here MUST stay a strict subset of the
|
|
7
|
-
* server's; if the server tightens, mirror the change here.
|
|
8
|
-
*
|
|
9
|
-
* Every check guards `typeof` before pattern-testing — the highest-value
|
|
10
|
-
* caller is `JSON.parse(fs.readFileSync('block.manifest.json'))`, where
|
|
11
|
-
* the TypeScript type is a lie.
|
|
12
|
-
*/
|
|
13
|
-
import { BLOCK_CATEGORIES, BLOCK_SCOPES, BLOCK_SCOPE_PATTERN, BLOCK_TAGLINE_MAX_LENGTH, } from './scopes.js';
|
|
14
|
-
/**
|
|
15
|
-
* Canonical `blockId` rule (https://civitai.com/schemas/app-block/v1.json):
|
|
16
|
-
* lowercase, must start with a letter, end alphanumeric, hyphen-separated,
|
|
17
|
-
* 3–40 chars. This is the DNS-subdomain-safe rule — the blockId becomes
|
|
18
|
-
* `<blockId>.civit.ai`, so it must be a valid DNS label.
|
|
19
|
-
*/
|
|
20
|
-
const BLOCK_ID_PATTERN = /^[a-z][a-z0-9-]*[a-z0-9]$/;
|
|
21
|
-
const BLOCK_ID_MIN_LENGTH = 3;
|
|
22
|
-
const BLOCK_ID_MAX_LENGTH = 40;
|
|
23
|
-
/** The 13 canonical block scopes (enum the schema validates `scopes` against). */
|
|
24
|
-
const KNOWN_BLOCK_SCOPES = new Set(Object.values(BLOCK_SCOPES));
|
|
25
|
-
/** The 7 canonical marketplace categories (enum the schema validates `category` against). */
|
|
26
|
-
const KNOWN_CATEGORIES = new Set(BLOCK_CATEGORIES);
|
|
27
|
-
const NAME_MAX_LENGTH = 80;
|
|
28
|
-
const SEMVER_PATTERN = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
|
|
29
|
-
/** Detects the SubresourceIntegrity hash format (sha256/384/512 + base64). */
|
|
30
|
-
const SRI_PATTERN = /^sha(?:256|384|512)-[A-Za-z0-9+/=]+$/;
|
|
31
|
-
/** Heuristic for "looks PascalCase" — used only to pick the pointed error message. */
|
|
32
|
-
const PASCAL_CASE_PATTERN = /^[A-Z][A-Za-z0-9]*$/;
|
|
33
|
-
const EXPECTED_SCHEMA_URL = 'https://civitai.com/schemas/app-block/v1.json';
|
|
34
|
-
const CONTENT_RATINGS = ['g', 'pg', 'pg13', 'r', 'x'];
|
|
35
|
-
/**
|
|
36
|
-
* W3 v0 — settings type set. Mirrors the meta-schema discriminated union
|
|
37
|
-
* in civitai/civitai's `manifest-settings.meta.schema.ts`. Keep aligned.
|
|
38
|
-
*/
|
|
39
|
-
const SETTING_TYPES = ['number', 'string', 'boolean'];
|
|
40
|
-
const SETTING_SCOPES = ['publisher', 'viewer'];
|
|
41
|
-
const SETTING_KEY_PATTERN = /^[a-z][a-z0-9_]{0,40}$/;
|
|
42
|
-
const MAX_SETTINGS_PER_BLOCK = 32;
|
|
43
|
-
/**
|
|
44
|
-
* All `allow-top-navigation*` sandbox tokens are rejected. The
|
|
45
|
-
* `-by-user-activation` and `-to-custom-protocols` variants gate on a user
|
|
46
|
-
* gesture but produce the same threat (block navigates the host frame) —
|
|
47
|
-
* blocks must route navigation through the `NAVIGATE` postMessage so the
|
|
48
|
-
* host can mediate.
|
|
49
|
-
*/
|
|
50
|
-
const BANNED_SANDBOX_TOKENS = new Set([
|
|
51
|
-
'allow-same-origin',
|
|
52
|
-
'allow-top-navigation',
|
|
53
|
-
'allow-top-navigation-by-user-activation',
|
|
54
|
-
'allow-top-navigation-to-custom-protocols',
|
|
55
|
-
]);
|
|
56
|
-
const REQUIRED_FIELDS = [
|
|
57
|
-
'$schema',
|
|
58
|
-
'appId',
|
|
59
|
-
'blockId',
|
|
60
|
-
'version',
|
|
61
|
-
'name',
|
|
62
|
-
'type',
|
|
63
|
-
'targets',
|
|
64
|
-
'scopes',
|
|
65
|
-
'iframe',
|
|
66
|
-
'contentRating',
|
|
67
|
-
'minApiVersion',
|
|
68
|
-
];
|
|
69
|
-
/** Thrown by `defineBlock` for any manifest violation. */
|
|
70
|
-
export class BlockManifestError extends Error {
|
|
71
|
-
field;
|
|
72
|
-
name = 'BlockManifestError';
|
|
73
|
-
constructor(message,
|
|
74
|
-
/** Dot-path to the offending field, e.g. `iframe.sandbox`. */
|
|
75
|
-
field) {
|
|
76
|
-
super(message);
|
|
77
|
-
this.field = field;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
/**
|
|
81
|
-
* Validates the manifest and returns it unchanged. Acts as a typed identity
|
|
82
|
-
* function — call it at module scope in the block app so violations throw
|
|
83
|
-
* before the app mounts. Mirrors a strict subset of the civitai/civitai server
|
|
84
|
-
* gate, so authoring mistakes surface in `pnpm dev` instead of at submit time.
|
|
85
|
-
* Throws {@link BlockManifestError} (with a `.field` dot-path) on a bad manifest.
|
|
86
|
-
*
|
|
87
|
-
* @example
|
|
88
|
-
* import { defineBlock } from '@civitai/app-sdk/blocks';
|
|
89
|
-
*
|
|
90
|
-
* export const manifest = defineBlock({
|
|
91
|
-
* manifest: {
|
|
92
|
-
* $schema: 'https://civitai.com/schemas/app-block/v1.json',
|
|
93
|
-
* appId: 'my-app',
|
|
94
|
-
* blockId: 'my-block',
|
|
95
|
-
* version: '1.0.0',
|
|
96
|
-
* name: 'My Block',
|
|
97
|
-
* type: 'block',
|
|
98
|
-
* targets: [{ slotId: 'model.sidebar_top', priority: 100 }],
|
|
99
|
-
* scopes: ['ai:write:budgeted'],
|
|
100
|
-
* iframe: { src: 'https://my-block.civit.ai/', minHeight: 200 },
|
|
101
|
-
* contentRating: 'pg',
|
|
102
|
-
* minApiVersion: '1.0',
|
|
103
|
-
* },
|
|
104
|
-
* });
|
|
105
|
-
*/
|
|
106
|
-
export function defineBlock(config) {
|
|
107
|
-
const { manifest } = config;
|
|
108
|
-
if (manifest == null || typeof manifest !== 'object') {
|
|
109
|
-
throw new BlockManifestError('manifest must be an object');
|
|
110
|
-
}
|
|
111
|
-
for (const field of REQUIRED_FIELDS) {
|
|
112
|
-
if (manifest[field] === undefined || manifest[field] === null) {
|
|
113
|
-
throw new BlockManifestError(`manifest.${field} is required`, field);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
if (manifest.$schema !== EXPECTED_SCHEMA_URL) {
|
|
117
|
-
throw new BlockManifestError(`manifest.$schema must be "${EXPECTED_SCHEMA_URL}"`, '$schema');
|
|
118
|
-
}
|
|
119
|
-
requireNonEmptyString(manifest.appId, 'appId');
|
|
120
|
-
requireNonEmptyString(manifest.blockId, 'blockId');
|
|
121
|
-
if (manifest.blockId.length < BLOCK_ID_MIN_LENGTH ||
|
|
122
|
-
manifest.blockId.length > BLOCK_ID_MAX_LENGTH ||
|
|
123
|
-
!BLOCK_ID_PATTERN.test(manifest.blockId)) {
|
|
124
|
-
throw new BlockManifestError(`manifest.blockId must match ${BLOCK_ID_PATTERN} and be ${BLOCK_ID_MIN_LENGTH}-${BLOCK_ID_MAX_LENGTH} chars ` +
|
|
125
|
-
`(lowercase, starts with a letter, ends alphanumeric, hyphen-separated — DNS-subdomain-safe since it becomes <blockId>.civit.ai). ` +
|
|
126
|
-
`Got: ${JSON.stringify(manifest.blockId)}`, 'blockId');
|
|
127
|
-
}
|
|
128
|
-
requireNonEmptyString(manifest.version, 'version');
|
|
129
|
-
if (!SEMVER_PATTERN.test(manifest.version)) {
|
|
130
|
-
throw new BlockManifestError(`manifest.version must be semver (e.g. "0.1.0" or "1.2.3-beta.1"). Got: ${JSON.stringify(manifest.version)}`, 'version');
|
|
131
|
-
}
|
|
132
|
-
requireNonEmptyString(manifest.name, 'name');
|
|
133
|
-
if (manifest.name.length > NAME_MAX_LENGTH) {
|
|
134
|
-
throw new BlockManifestError(`manifest.name must be ${NAME_MAX_LENGTH} characters or fewer (got ${manifest.name.length})`, 'name');
|
|
135
|
-
}
|
|
136
|
-
if (manifest.type !== 'block' && manifest.type !== 'embed') {
|
|
137
|
-
throw new BlockManifestError(`manifest.type must be "block" or "embed". Got: ${JSON.stringify(manifest.type)}`, 'type');
|
|
138
|
-
}
|
|
139
|
-
if (!Array.isArray(manifest.scopes) || manifest.scopes.length === 0) {
|
|
140
|
-
throw new BlockManifestError('manifest.scopes must be a non-empty array', 'scopes');
|
|
141
|
-
}
|
|
142
|
-
for (const scope of manifest.scopes) {
|
|
143
|
-
// Authoritative validity = membership in the canonical enum (BLOCK_SCOPES),
|
|
144
|
-
// matching how the published schema validates `scopes`. The format pattern
|
|
145
|
-
// is only used to pick a pointed error message.
|
|
146
|
-
if (typeof scope !== 'string' || !KNOWN_BLOCK_SCOPES.has(scope)) {
|
|
147
|
-
throw new BlockManifestError(buildScopeError(scope), 'scopes');
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
if (!Array.isArray(manifest.targets) || manifest.targets.length === 0) {
|
|
151
|
-
throw new BlockManifestError('manifest.targets must be a non-empty array', 'targets');
|
|
152
|
-
}
|
|
153
|
-
manifest.targets.forEach((target, i) => validateTarget(target, i));
|
|
154
|
-
if (!CONTENT_RATINGS.includes(manifest.contentRating)) {
|
|
155
|
-
throw new BlockManifestError(`manifest.contentRating must be one of ${CONTENT_RATINGS.join(', ')}. Got: ${JSON.stringify(manifest.contentRating)}`, 'contentRating');
|
|
156
|
-
}
|
|
157
|
-
// `category` is OPTIONAL. When present it must be one of the canonical
|
|
158
|
-
// marketplace categories (mirrors the schema's `category` enum + the server's
|
|
159
|
-
// MARKETPLACE_CATEGORIES). Omission is fine — a moderator categorises then.
|
|
160
|
-
if (manifest.category !== undefined) {
|
|
161
|
-
if (typeof manifest.category !== 'string' || !KNOWN_CATEGORIES.has(manifest.category)) {
|
|
162
|
-
throw new BlockManifestError(`manifest.category must be one of ${BLOCK_CATEGORIES.join(', ')} (or omitted). ` +
|
|
163
|
-
`Got: ${JSON.stringify(manifest.category)}`, 'category');
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
// `tagline` is OPTIONAL. When present it must be a string whose TRIMMED length
|
|
167
|
-
// is 1..BLOCK_TAGLINE_MAX_LENGTH — mirroring the server's authoritative
|
|
168
|
-
// BlockManifestValidator check (which also trims), so a padded-but-fitting
|
|
169
|
-
// value is never rejected here and then accepted at submit. Omission is fine —
|
|
170
|
-
// the store simply renders no tagline.
|
|
171
|
-
if (manifest.tagline !== undefined) {
|
|
172
|
-
const tagline = manifest.tagline;
|
|
173
|
-
if (typeof tagline !== 'string' || tagline.trim().length === 0) {
|
|
174
|
-
throw new BlockManifestError(`manifest.tagline must be a non-empty string (or omitted). Got: ${JSON.stringify(tagline)}`, 'tagline');
|
|
175
|
-
}
|
|
176
|
-
if (tagline.trim().length > BLOCK_TAGLINE_MAX_LENGTH) {
|
|
177
|
-
throw new BlockManifestError(`manifest.tagline must be at most ${BLOCK_TAGLINE_MAX_LENGTH} characters. Got: ${tagline.trim().length}`, 'tagline');
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
requireNonEmptyString(manifest.minApiVersion, 'minApiVersion');
|
|
181
|
-
validateIframe(manifest.iframe);
|
|
182
|
-
if (manifest.assets !== undefined)
|
|
183
|
-
validateAssets(manifest.assets);
|
|
184
|
-
if (manifest.settings !== undefined)
|
|
185
|
-
validateSettings(manifest.settings);
|
|
186
|
-
return manifest;
|
|
187
|
-
}
|
|
188
|
-
function requireNonEmptyString(value, field) {
|
|
189
|
-
if (typeof value !== 'string' || value.length === 0) {
|
|
190
|
-
throw new BlockManifestError(`manifest.${field} must be a non-empty string`, field);
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
function buildScopeError(scope) {
|
|
194
|
-
const shown = JSON.stringify(scope);
|
|
195
|
-
const known = Object.values(BLOCK_SCOPES).join(', ');
|
|
196
|
-
if (typeof scope === 'string' && PASCAL_CASE_PATTERN.test(scope)) {
|
|
197
|
-
return (`Block scope strings must use colon-separated lowercase format ` +
|
|
198
|
-
`(e.g. "models:read:self"), not PascalCase (e.g. "ModelsReadSelf"). Got: ${shown}`);
|
|
199
|
-
}
|
|
200
|
-
if (typeof scope === 'string' && !BLOCK_SCOPE_PATTERN.test(scope)) {
|
|
201
|
-
return (`Block scope strings must match ${BLOCK_SCOPE_PATTERN} ` +
|
|
202
|
-
`(three colon-separated lowercase segments, e.g. "models:read:self"). Got: ${shown}`);
|
|
203
|
-
}
|
|
204
|
-
// Well-formed but not one of the known/approved scopes.
|
|
205
|
-
return (`manifest.scopes contains an unknown block scope ${shown}. ` +
|
|
206
|
-
`Must be one of: ${known}.`);
|
|
207
|
-
}
|
|
208
|
-
function validateTarget(target, index) {
|
|
209
|
-
const path = `targets[${index}]`;
|
|
210
|
-
if (target == null || typeof target !== 'object') {
|
|
211
|
-
throw new BlockManifestError(`manifest.${path} must be an object`, path);
|
|
212
|
-
}
|
|
213
|
-
const t = target;
|
|
214
|
-
if (typeof t.slotId !== 'string' || t.slotId.length === 0) {
|
|
215
|
-
throw new BlockManifestError(`manifest.${path}.slotId must be a non-empty string`, `${path}.slotId`);
|
|
216
|
-
}
|
|
217
|
-
if (typeof t.priority !== 'number' || !Number.isInteger(t.priority)) {
|
|
218
|
-
throw new BlockManifestError(`manifest.${path}.priority must be an integer`, `${path}.priority`);
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
function validateIframe(iframe) {
|
|
222
|
-
if (iframe == null || typeof iframe !== 'object') {
|
|
223
|
-
throw new BlockManifestError('manifest.iframe must be an object', 'iframe');
|
|
224
|
-
}
|
|
225
|
-
if (typeof iframe.src !== 'string' || iframe.src.length === 0) {
|
|
226
|
-
throw new BlockManifestError('manifest.iframe.src must be a non-empty string', 'iframe.src');
|
|
227
|
-
}
|
|
228
|
-
if (!isAllowedIframeSrc(iframe.src)) {
|
|
229
|
-
throw new BlockManifestError(`manifest.iframe.src must use https:// (http:// only accepted for localhost/127.0.0.1/[::1]/*.localhost). Got: ${JSON.stringify(iframe.src)}`, 'iframe.src');
|
|
230
|
-
}
|
|
231
|
-
if (typeof iframe.sandbox !== 'string') {
|
|
232
|
-
throw new BlockManifestError('manifest.iframe.sandbox must be a string', 'iframe.sandbox');
|
|
233
|
-
}
|
|
234
|
-
const tokens = new Set(iframe.sandbox.split(/\s+/).filter(Boolean));
|
|
235
|
-
for (const banned of BANNED_SANDBOX_TOKENS) {
|
|
236
|
-
if (tokens.has(banned)) {
|
|
237
|
-
throw new BlockManifestError(`manifest.iframe.sandbox must not contain "${banned}". ` +
|
|
238
|
-
(banned === 'allow-same-origin'
|
|
239
|
-
? 'Combined with "allow-scripts" it defeats the sandbox entirely.'
|
|
240
|
-
: 'Blocks must route navigation through the NAVIGATE postMessage so the host can mediate.'), 'iframe.sandbox');
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
if (typeof iframe.minHeight !== 'number' || !Number.isInteger(iframe.minHeight) || iframe.minHeight <= 0) {
|
|
244
|
-
throw new BlockManifestError('manifest.iframe.minHeight must be a positive integer', 'iframe.minHeight');
|
|
245
|
-
}
|
|
246
|
-
if (iframe.maxHeight !== undefined &&
|
|
247
|
-
iframe.maxHeight !== null &&
|
|
248
|
-
(typeof iframe.maxHeight !== 'number' || !Number.isInteger(iframe.maxHeight) || iframe.maxHeight <= 0)) {
|
|
249
|
-
throw new BlockManifestError('manifest.iframe.maxHeight must be a positive integer, null, or omitted', 'iframe.maxHeight');
|
|
250
|
-
}
|
|
251
|
-
if (typeof iframe.resizable !== 'boolean') {
|
|
252
|
-
throw new BlockManifestError('manifest.iframe.resizable must be a boolean', 'iframe.resizable');
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
function validateAssets(assets) {
|
|
256
|
-
if (!Array.isArray(assets)) {
|
|
257
|
-
throw new BlockManifestError('manifest.assets must be an array', 'assets');
|
|
258
|
-
}
|
|
259
|
-
assets.forEach((asset, i) => {
|
|
260
|
-
const path = `assets[${i}]`;
|
|
261
|
-
if (asset == null || typeof asset !== 'object') {
|
|
262
|
-
throw new BlockManifestError(`manifest.${path} must be an object`, path);
|
|
263
|
-
}
|
|
264
|
-
const a = asset;
|
|
265
|
-
if (typeof a.url !== 'string' || a.url.length === 0) {
|
|
266
|
-
throw new BlockManifestError(`manifest.${path}.url must be a non-empty string`, `${path}.url`);
|
|
267
|
-
}
|
|
268
|
-
if (typeof a.integrity !== 'string' || !SRI_PATTERN.test(a.integrity)) {
|
|
269
|
-
throw new BlockManifestError(`manifest.${path}.integrity must be a SubresourceIntegrity hash ` +
|
|
270
|
-
`(sha256/384/512-<base64>). Got: ${JSON.stringify(a.integrity)}`, `${path}.integrity`);
|
|
271
|
-
}
|
|
272
|
-
});
|
|
273
|
-
}
|
|
274
|
-
/**
|
|
275
|
-
* W3 v0 settings validation. Manifest authors declare fields as a record
|
|
276
|
-
* keyed by snake_case field name; each entry carries scope, type, label,
|
|
277
|
-
* description, and a widget hint. This is a strict subset of the platform
|
|
278
|
-
* meta-schema (`manifestSettingsSchema`) — keep both sides aligned.
|
|
279
|
-
*/
|
|
280
|
-
function validateSettings(settings) {
|
|
281
|
-
if (settings == null || typeof settings !== 'object' || Array.isArray(settings)) {
|
|
282
|
-
throw new BlockManifestError('manifest.settings must be an object (record keyed by snake_case field name)', 'settings');
|
|
283
|
-
}
|
|
284
|
-
const entries = Object.entries(settings);
|
|
285
|
-
if (entries.length > MAX_SETTINGS_PER_BLOCK) {
|
|
286
|
-
throw new BlockManifestError(`manifest.settings has ${entries.length} entries (max ${MAX_SETTINGS_PER_BLOCK})`, 'settings');
|
|
287
|
-
}
|
|
288
|
-
for (const [key, raw] of entries) {
|
|
289
|
-
const path = `settings.${key}`;
|
|
290
|
-
if (!SETTING_KEY_PATTERN.test(key)) {
|
|
291
|
-
throw new BlockManifestError(`manifest.${path}: key must match ${SETTING_KEY_PATTERN} (snake_case, must start with a letter)`, path);
|
|
292
|
-
}
|
|
293
|
-
if (raw == null || typeof raw !== 'object') {
|
|
294
|
-
throw new BlockManifestError(`manifest.${path} must be an object`, path);
|
|
295
|
-
}
|
|
296
|
-
const s = raw;
|
|
297
|
-
if (typeof s.scope !== 'string' || !SETTING_SCOPES.includes(s.scope)) {
|
|
298
|
-
throw new BlockManifestError(`manifest.${path}.scope must be one of ${SETTING_SCOPES.join(', ')}. Got: ${JSON.stringify(s.scope)}`, `${path}.scope`);
|
|
299
|
-
}
|
|
300
|
-
if (typeof s.type !== 'string' || !SETTING_TYPES.includes(s.type)) {
|
|
301
|
-
throw new BlockManifestError(`manifest.${path}.type must be one of ${SETTING_TYPES.join(', ')}. Got: ${JSON.stringify(s.type)}`, `${path}.type`);
|
|
302
|
-
}
|
|
303
|
-
if (typeof s.label !== 'string' || s.label.length === 0) {
|
|
304
|
-
throw new BlockManifestError(`manifest.${path}.label must be a non-empty string`, `${path}.label`);
|
|
305
|
-
}
|
|
306
|
-
if (typeof s.description !== 'string' || s.description.length === 0) {
|
|
307
|
-
throw new BlockManifestError(`manifest.${path}.description must be a non-empty string`, `${path}.description`);
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
function isAllowedIframeSrc(src) {
|
|
312
|
-
let url;
|
|
313
|
-
try {
|
|
314
|
-
url = new URL(src);
|
|
315
|
-
}
|
|
316
|
-
catch {
|
|
317
|
-
return false;
|
|
318
|
-
}
|
|
319
|
-
// Reject URLs with no host even if they parse (e.g. "https://" alone, which
|
|
320
|
-
// some URL parsers permit).
|
|
321
|
-
if (!url.hostname)
|
|
322
|
-
return false;
|
|
323
|
-
if (url.protocol === 'https:')
|
|
324
|
-
return true;
|
|
325
|
-
if (url.protocol !== 'http:')
|
|
326
|
-
return false;
|
|
327
|
-
const host = url.hostname;
|
|
328
|
-
// RFC 6761 reserves `.localhost` for loopback.
|
|
329
|
-
if (host === 'localhost' || host.endsWith('.localhost'))
|
|
330
|
-
return true;
|
|
331
|
-
if (host === '127.0.0.1')
|
|
332
|
-
return true;
|
|
333
|
-
// IPv6 loopback. WHATWG URL parses "[::1]" → hostname "[::1]" or "::1".
|
|
334
|
-
if (host === '[::1]' || host === '::1')
|
|
335
|
-
return true;
|
|
336
|
-
return false;
|
|
337
|
-
}
|
|
338
|
-
//# sourceMappingURL=defineBlock.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"defineBlock.js","sourceRoot":"","sources":["../../src/blocks/defineBlock.ts"],"names":[],"mappings":"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"}
|