@civitai/app-sdk 0.47.0 → 0.49.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.
Files changed (38) hide show
  1. package/README.md +76 -32
  2. package/dist/blocks/appStorageErrors.d.ts +390 -0
  3. package/dist/blocks/appStorageErrors.d.ts.map +1 -0
  4. package/dist/blocks/appStorageErrors.js +429 -0
  5. package/dist/blocks/appStorageErrors.js.map +1 -0
  6. package/dist/blocks/index.d.ts +52 -7
  7. package/dist/blocks/index.d.ts.map +1 -1
  8. package/dist/blocks/index.js +50 -5
  9. package/dist/blocks/index.js.map +1 -1
  10. package/dist/blocks/manifestError.d.ts +20 -0
  11. package/dist/blocks/manifestError.d.ts.map +1 -0
  12. package/dist/blocks/manifestError.js +22 -0
  13. package/dist/blocks/manifestError.js.map +1 -0
  14. package/dist/blocks/messages.d.ts.map +1 -1
  15. package/dist/blocks/messages.js.map +1 -1
  16. package/dist/blocks/scopes.d.ts +7 -3
  17. package/dist/blocks/scopes.d.ts.map +1 -1
  18. package/dist/blocks/scopes.js +7 -3
  19. package/dist/blocks/scopes.js.map +1 -1
  20. package/dist/blocks/types.d.ts +110 -22
  21. package/dist/blocks/types.d.ts.map +1 -1
  22. package/dist/manifest/defineBlock.d.ts +145 -0
  23. package/dist/manifest/defineBlock.d.ts.map +1 -0
  24. package/dist/manifest/defineBlock.js +390 -0
  25. package/dist/manifest/defineBlock.js.map +1 -0
  26. package/dist/manifest/index.d.ts +19 -0
  27. package/dist/manifest/index.d.ts.map +1 -0
  28. package/dist/manifest/index.js +17 -0
  29. package/dist/manifest/index.js.map +1 -0
  30. package/dist/vite/index.d.ts +14 -0
  31. package/dist/vite/index.d.ts.map +1 -0
  32. package/dist/vite/index.js +61 -0
  33. package/dist/vite/index.js.map +1 -0
  34. package/package.json +42 -2
  35. package/dist/blocks/defineBlock.d.ts +0 -53
  36. package/dist/blocks/defineBlock.d.ts.map +0 -1
  37. package/dist/blocks/defineBlock.js +0 -338
  38. package/dist/blocks/defineBlock.js.map +0 -1
@@ -0,0 +1,390 @@
1
+ /**
2
+ * Build-time validation for a `block.manifest.json`, DERIVED FROM THE CANONICAL
3
+ * SCHEMA rather than hand-mirrored.
4
+ *
5
+ * WHY IT IS SHAPED THIS WAY. #330's proposed fix, item 1, verbatim: "Derive the
6
+ * rules from the schema, not by hand. […] A hand-written mirror of an external
7
+ * schema regenerates this bug every time the schema moves." The previous
8
+ * implementation was that hand-written mirror — 11 required fields against the
9
+ * canonical's 5, `appId` required though the canonical does not declare it, and
10
+ * `iframe.src` required though the platform REFUSES it. It rejected all seven
11
+ * `block.manifest.json` files this repo ships.
12
+ *
13
+ * So the machine-checkable rules are not written down here at all. They are
14
+ * read from `schemas/app-block/v1.json` — a byte-identical vendored copy of
15
+ * https://civitai.com/schemas/app-block/v1.json, kept in lockstep by
16
+ * `scripts/check-canonical-schema.sh` (CI job `schema-drift`) — and compiled
17
+ * with Ajv. Every `required`, `enum`, `pattern`, bound, `additionalProperties`
18
+ * and `allOf` in the canonical is therefore enforced here for free, and moves
19
+ * when the canonical moves.
20
+ *
21
+ * WHY THIS SUBPATH IS NODE-ONLY. Reading the schema needs `node:fs`, and Ajv is
22
+ * a runtime dependency. `@civitai/app-sdk`'s `dependencies` are empty on purpose
23
+ * — it ships into sandboxed browser iframes and every app inherits its install
24
+ * graph — so Ajv is an OPTIONAL PEER (the `@civitai/client` precedent) consumed
25
+ * only from `@civitai/app-sdk/manifest` and `@civitai/app-sdk/vite`. The only
26
+ * real caller is the Vite plugin, which runs in Node at build time. The
27
+ * browser-facing `./blocks` subpath keeps zero runtime dependencies.
28
+ *
29
+ * THE CLAIM YOU CAN CHECK:
30
+ *
31
+ * 1. `defineBlock` REJECTS everything the canonical schema rejects. This holds
32
+ * BY CONSTRUCTION, not by a fixture corpus: Ajv runs first and any error it
33
+ * reports is thrown. There is no relaxation carve-out anywhere below.
34
+ * 2. Beyond that it rejects exactly the rules in {@link SCHEMA_DIVERGENCES} —
35
+ * each one a server rejection the canonical states only in PROSE, each
36
+ * carrying the prose it mirrors.
37
+ * 3. Nothing else.
38
+ *
39
+ * PASSING IS NECESSARY, NOT SUFFICIENT for `civitai app submit` — see
40
+ * {@link KNOWN_GAPS}. And it is not a replacement for `civitai app validate`,
41
+ * the Go CLI's own local pre-check; this exists so a mistake surfaces during
42
+ * `pnpm dev` / `pnpm build` rather than at submit time.
43
+ */
44
+ import { readFileSync } from 'node:fs';
45
+ import Ajv2020 from 'ajv/dist/2020.js';
46
+ import { BlockManifestError } from '../blocks/manifestError.js';
47
+ import { BLOCK_SCOPES } from '../blocks/scopes.js';
48
+ /**
49
+ * Rules `defineBlock` applies that the canonical schema does NOT express in
50
+ * machine-checkable form.
51
+ *
52
+ * EVERY ENTRY IS STRICTLY ADDITIVE — it can only ever REJECT a manifest Ajv
53
+ * accepted. None of them relaxes a canonical rule. That is what makes claim (1)
54
+ * in the module docblock structural rather than a promise, and it is asserted
55
+ * per-entry by `test/manifest/divergences.test.ts`: each entry must name a
56
+ * fixture the canonical schema ACCEPTS and `defineBlock` REJECTS on that
57
+ * entry's own field path. An entry with no such fixture fails the suite.
58
+ *
59
+ * Each `canonical` field quotes the schema text the rule mirrors, so the next
60
+ * person can check the mirror against its source without leaving this file.
61
+ */
62
+ export const SCHEMA_DIVERGENCES = {
63
+ 'iframe.src': {
64
+ rule: 'REJECTED when present.',
65
+ canonical: 'Declared under `iframe.properties` with the description "SERVER-OWNED. Do NOT set ' +
66
+ "iframe.src — the platform assigns it. Present here only so the schema can reject dev-set " +
67
+ 'values." The JSON-Schema `not` is deliberately absent; the top-level `allOf` $comment says ' +
68
+ 'the platform validator and the Go CLI reject it "with clearer error messages than JSON ' +
69
+ "Schema's `not`\".",
70
+ reason: 'Mirrors a server rejection the canonical states in prose and deliberately does not encode. ' +
71
+ 'This is the #330 headline inverted: the old validator REQUIRED `iframe.src`, demanding the ' +
72
+ 'exact value submit refuses.',
73
+ },
74
+ trustTier: {
75
+ rule: 'REJECTED when present.',
76
+ canonical: 'Declared with the description "SERVER-OWNED. Do NOT set this in your manifest — the ' +
77
+ 'platform assigns the trust tier during review. Present here only to reject dev-set values." ' +
78
+ 'Same deliberately-absent `not` as `iframe.src`, per the same `allOf` $comment.',
79
+ reason: 'Mirrors a server rejection the canonical states in prose.',
80
+ },
81
+ 'iframe.sandbox': {
82
+ rule: 'Rejects `allow-same-origin` and every `allow-top-navigation*` token.',
83
+ canonical: 'Only `minLength: 1`. The description carries two separate prose rules: a TIER ALLOWLIST ' +
84
+ '("Unverified tier allows only: allow-scripts, allow-forms") and a flat prohibition ' +
85
+ '("Never combine allow-same-origin with allow-scripts").',
86
+ reason: 'Mirrors the flat prohibition, plus top-navigation (including the -by-user-activation and ' +
87
+ '-to-custom-protocols variants), which lets a block navigate the host frame instead of ' +
88
+ 'routing through the NAVIGATE postMessage. ' +
89
+ '🔴 THE TIER ALLOWLIST IS DELIBERATELY NOT MIRRORED, AND THAT MAKES THIS GATE LOOSER THAN ' +
90
+ 'REVIEW IN THE OTHER DIRECTION: `allow-popups`, `allow-modals`, `allow-downloads` and any ' +
91
+ 'other token outside {allow-scripts, allow-forms} PASS HERE and may be refused at review. ' +
92
+ 'They are not rejected because the tier is assigned server-side during review and is ' +
93
+ 'unknowable locally — `starters/civitai-block-starter` itself ships ' +
94
+ '"allow-scripts allow-forms allow-popups allow-popups-to-escape-sandbox". See ' +
95
+ 'KNOWN_GAPS["tier-dependent-sandbox-allowlist"].',
96
+ },
97
+ scopeJustifications: {
98
+ rule: 'Every key must name a scope also present in `scopes`.',
99
+ canonical: '`additionalProperties: { type: string, minLength: 1, maxLength: 500 }` — any key passes. ' +
100
+ 'The canonical says so itself: "The requirement is enforced imperatively by the manifest ' +
101
+ 'validator (not expressed as JSON-Schema conditionals here)."',
102
+ reason: 'Mirrors a server rejection the canonical states in prose. The OTHER half of that server ' +
103
+ 'rule — justifications being REQUIRED for sensitive scopes — is NOT mirrored; see ' +
104
+ 'KNOWN_GAPS["scopeJustifications-required-for-sensitive-scopes"].',
105
+ },
106
+ settings: {
107
+ rule: 'Validated against the W3 settings meta-schema (scope/type/label/description, snake_case keys, max 32).',
108
+ canonical: 'Not a declared property. The canonical top level is not `additionalProperties: false`, so it passes.',
109
+ reason: "Manifest settings ARE validated server-side, by civitai/civitai's " +
110
+ '`manifest-settings.meta.schema.ts` rather than by the app-block schema, and two of the ' +
111
+ 'seven shipped manifests declare `settings`. ' +
112
+ '🔴 THIS IS THE ONE REMAINING HAND-WRITTEN MIRROR OF AN EXTERNAL SCHEMA IN THIS FILE, i.e. ' +
113
+ 'exactly the construct #330 was filed about — it is here only because that meta-schema is ' +
114
+ 'NOT published at a URL and NOT vendored, so there is nothing to derive from. The fix is ' +
115
+ 'upstream: publish it and vendor it beside `app-block/v1.json` with the same drift-check, ' +
116
+ 'then delete this. See KNOWN_GAPS["settings-meta-schema-not-vendored"].',
117
+ },
118
+ };
119
+ /**
120
+ * Rules the server applies that `defineBlock` deliberately does NOT — stated so
121
+ * nobody reads a green `defineBlock` as "submit will succeed". Run
122
+ * `civitai app validate` (the Go CLI) before `civitai app submit`; it checks
123
+ * against the same canonical and knows things this cannot.
124
+ */
125
+ export const KNOWN_GAPS = {
126
+ 'scopeJustifications-required-for-sensitive-scopes': 'The canonical names the sensitive scope set only in a prose description, not in a ' +
127
+ 'machine-readable form. Hand-mirroring that list would regenerate the #330 drift bug, so ' +
128
+ 'only the SHAPE is checked here (keys must be declared scopes; the canonical checks the ' +
129
+ '1..500-char values). The requirement itself is server-side.',
130
+ 'repository-per-segment-rules': 'The canonical\'s `repository` pattern is documented as "a coarse SHAPE check and NOT the ' +
131
+ 'whole rule"; the server additionally constrains each path segment. Ajv enforces the ' +
132
+ 'canonical pattern exactly, and is therefore necessary-not-sufficient BY THE CANONICAL\'S ' +
133
+ 'OWN DESIGN.',
134
+ 'tier-dependent-sandbox-allowlist': 'The canonical\'s sandbox description is an ALLOWLIST ("Unverified tier allows only: ' +
135
+ 'allow-scripts, allow-forms"), but the tier is assigned server-side during review, so the ' +
136
+ 'applicable allowlist is unknowable at build time. Tokens outside that set pass here and may ' +
137
+ 'be refused at review. See SCHEMA_DIVERGENCES["iframe.sandbox"].',
138
+ 'review-granted-scope-subset': 'Which of the requested `scopes` review actually grants has no schema expression at all.',
139
+ 'slotId-registry': 'Whether a `targets[].slotId` names a slot the host has registered has no schema expression; ' +
140
+ 'the canonical only requires a non-empty string.',
141
+ 'settings-meta-schema-not-vendored': "civitai/civitai's `manifest-settings.meta.schema.ts` is not published at a URL and not " +
142
+ 'vendored here, so `settings` is the one surface still checked by a hand-written mirror. ' +
143
+ 'See SCHEMA_DIVERGENCES.settings.',
144
+ 'format-uri-not-enforced': '`assetBundleUrl` carries `format: "uri"` in addition to `pattern: "^https://"`. Ajv ignores ' +
145
+ 'unknown formats unless `ajv-formats` is installed, which would be a second optional peer for ' +
146
+ 'one field whose pattern is already enforced. The pattern IS enforced; the `uri` format is ' +
147
+ 'not, so a value like "https://" passes here.',
148
+ };
149
+ /**
150
+ * The vendored canonical, resolved relative to THIS module so the same
151
+ * specifier works from `src/` (vitest) and from `dist/` (published):
152
+ * `src/manifest/` -> `../../schemas/`, `dist/manifest/` -> `../../schemas/`.
153
+ * `schemas` is in package.json `files`, so it ships.
154
+ */
155
+ const CANONICAL_SCHEMA_PATH = new URL('../../schemas/app-block/v1.json', import.meta.url);
156
+ /** The canonical schema object, parsed once. Exported for tests and tooling. */
157
+ export function loadCanonicalSchema() {
158
+ return JSON.parse(readFileSync(CANONICAL_SCHEMA_PATH, 'utf8'));
159
+ }
160
+ let compiled;
161
+ function canonicalValidator() {
162
+ if (compiled)
163
+ return compiled;
164
+ // `strict: false` because the canonical uses `$comment` and a `format` Ajv
165
+ // does not know (see KNOWN_GAPS['format-uri-not-enforced']); strict mode would
166
+ // turn those into compile-time throws over a schema this package does not own
167
+ // and must not edit.
168
+ //
169
+ // `logger: false` because that same unknown format makes Ajv log
170
+ // `unknown format "uri" ignored…` to the console AT COMPILE TIME — which, via
171
+ // the Vite plugin, would print on every `pnpm dev` and `pnpm build` in every
172
+ // scaffold. The gap is recorded in KNOWN_GAPS, not shouted at the author on
173
+ // every boot. NOTE this silences Ajv's own diagnostics only; validation
174
+ // errors are returned in `validate.errors`, never logged.
175
+ const ajv = new Ajv2020({ allErrors: true, strict: false, logger: false });
176
+ compiled = ajv.compile(loadCanonicalSchema());
177
+ return compiled;
178
+ }
179
+ /** `/iframe/minHeight` -> `iframe.minHeight`; `/targets/0/slotId` -> `targets[0].slotId`. */
180
+ function toDotPath(instancePath) {
181
+ if (instancePath === '')
182
+ return '';
183
+ return instancePath
184
+ .slice(1)
185
+ .split('/')
186
+ .reduce((acc, segment) => {
187
+ // JSON Pointer escapes: ~1 is '/', ~0 is '~'.
188
+ const key = segment.replace(/~1/g, '/').replace(/~0/g, '~');
189
+ if (/^\d+$/.test(key))
190
+ return `${acc}[${key}]`;
191
+ return acc === '' ? key : `${acc}.${key}`;
192
+ }, '');
193
+ }
194
+ const PASCAL_CASE_PATTERN = /^[A-Z][A-Za-z0-9]*$/;
195
+ /**
196
+ * Turns the FIRST Ajv error into a `BlockManifestError`. Ajv's own wording is
197
+ * kept (it is derived from the schema, so it cannot drift); only the field path
198
+ * is reshaped, and one case gets an extra hint because it is the mistake the
199
+ * docs see most.
200
+ */
201
+ function toManifestError(error, manifest) {
202
+ const base = toDotPath(error.instancePath);
203
+ if (error.keyword === 'required') {
204
+ const missing = error.params.missingProperty;
205
+ const field = base === '' ? missing : `${base}.${missing}`;
206
+ return new BlockManifestError(`manifest.${field} is required`, field);
207
+ }
208
+ const field = base;
209
+ const shown = field === '' ? 'manifest' : `manifest.${field}`;
210
+ let message = `${shown} ${error.message ?? 'is invalid'}`;
211
+ if (error.keyword === 'enum') {
212
+ const allowed = error.params.allowedValues;
213
+ if (allowed)
214
+ message += ` (${allowed.map((v) => JSON.stringify(v)).join(', ')})`;
215
+ }
216
+ if (error.keyword === 'additionalProperties') {
217
+ const extra = error.params.additionalProperty;
218
+ message = `${shown}.${extra} is not a known property here`;
219
+ return new BlockManifestError(message, field === '' ? extra : `${field}.${extra}`);
220
+ }
221
+ // The one hint worth adding: block scopes are colon-separated lowercase, and
222
+ // the OAuth scopes elsewhere in this SDK are PascalCase bitmask names, so
223
+ // authors reach for the wrong spelling.
224
+ if (/^scopes\[\d+\]$/.test(field)) {
225
+ const index = Number(field.slice(field.indexOf('[') + 1, -1));
226
+ const value = manifest?.scopes?.[index];
227
+ if (typeof value === 'string' && PASCAL_CASE_PATTERN.test(value)) {
228
+ message +=
229
+ '. Block scope strings are colon-separated lowercase (e.g. "models:read:self"), ' +
230
+ 'not the PascalCase OAuth bitmask names';
231
+ }
232
+ message += `. Must be one of: ${Object.values(BLOCK_SCOPES).join(', ')}`;
233
+ }
234
+ return new BlockManifestError(message, field);
235
+ }
236
+ /**
237
+ * Validates the manifest against the canonical schema and returns it unchanged.
238
+ * Acts as a typed identity function — call it from the build (see
239
+ * `@civitai/app-sdk/vite`) so violations throw before anything ships. Throws
240
+ * {@link BlockManifestError} (with a `.field` dot-path).
241
+ *
242
+ * Enforces every rule the canonical `block.manifest.json` schema expresses,
243
+ * plus the prose-only server rejections in {@link SCHEMA_DIVERGENCES}. See
244
+ * {@link KNOWN_GAPS} for what only the server can check.
245
+ *
246
+ * The example below is kept byte-identical to `test/manifest/defineBlock.example.ts`,
247
+ * which is compiled and executed by the suite — a guard fails if they drift, so
248
+ * this snippet both type-checks and runs.
249
+ *
250
+ * @example
251
+ * import { defineBlock } from '@civitai/app-sdk/manifest';
252
+ *
253
+ * export const manifest = defineBlock({
254
+ * manifest: {
255
+ * $schema: 'https://civitai.com/schemas/app-block/v1.json',
256
+ * blockId: 'my-block',
257
+ * version: '0.1.0',
258
+ * name: 'My Block',
259
+ * type: 'block',
260
+ * targets: [{ slotId: 'model.sidebar_top', priority: 100 }],
261
+ * scopes: ['models:read:self'],
262
+ * // NOTE: no `iframe.src` — the platform stamps it at build/approve time.
263
+ * iframe: {
264
+ * minHeight: 200,
265
+ * maxHeight: 600,
266
+ * resizable: true,
267
+ * sandbox: 'allow-scripts allow-forms',
268
+ * },
269
+ * contentRating: 'pg',
270
+ * minApiVersion: '1.0',
271
+ * },
272
+ * });
273
+ */
274
+ export function defineBlock(config) {
275
+ const { manifest } = config;
276
+ if (manifest == null || typeof manifest !== 'object' || Array.isArray(manifest)) {
277
+ throw new BlockManifestError('manifest must be an object');
278
+ }
279
+ // (1) Everything the canonical expresses. No carve-outs, no suppression:
280
+ // whatever Ajv rejects, this rejects.
281
+ const validate = canonicalValidator();
282
+ if (!validate(manifest)) {
283
+ const errors = validate.errors ?? [];
284
+ const first = errors[0];
285
+ /* c8 ignore next */
286
+ if (!first)
287
+ throw new BlockManifestError('manifest failed schema validation');
288
+ throw toManifestError(first, manifest);
289
+ }
290
+ // (2) The prose-only server rejections. Strictly additive, one per
291
+ // SCHEMA_DIVERGENCES entry, in the table's order.
292
+ rejectServerOwned(manifest);
293
+ rejectDangerousSandboxTokens(manifest);
294
+ rejectUndeclaredScopeJustifications(manifest);
295
+ if (manifest.settings !== undefined)
296
+ validateSettings(manifest.settings);
297
+ return manifest;
298
+ }
299
+ const SERVER_OWNED_MESSAGE = 'is SERVER-OWNED — the platform assigns it during build/approve. Remove it from the manifest.';
300
+ /** SCHEMA_DIVERGENCES['iframe.src'] and SCHEMA_DIVERGENCES.trustTier. */
301
+ function rejectServerOwned(manifest) {
302
+ if (manifest.trustTier !== undefined) {
303
+ throw new BlockManifestError(`manifest.trustTier ${SERVER_OWNED_MESSAGE}`, 'trustTier');
304
+ }
305
+ const iframe = manifest.iframe;
306
+ if (iframe != null && iframe.src !== undefined) {
307
+ throw new BlockManifestError(`manifest.iframe.src ${SERVER_OWNED_MESSAGE}`, 'iframe.src');
308
+ }
309
+ }
310
+ /**
311
+ * SCHEMA_DIVERGENCES['iframe.sandbox']. A DENYLIST, not the canonical's tier
312
+ * allowlist — see that entry and KNOWN_GAPS['tier-dependent-sandbox-allowlist']
313
+ * for why, and for what therefore passes here and fails at review.
314
+ */
315
+ const BANNED_SANDBOX_TOKENS = new Set([
316
+ 'allow-same-origin',
317
+ 'allow-top-navigation',
318
+ 'allow-top-navigation-by-user-activation',
319
+ 'allow-top-navigation-to-custom-protocols',
320
+ ]);
321
+ function rejectDangerousSandboxTokens(manifest) {
322
+ const sandbox = manifest.iframe?.sandbox;
323
+ if (typeof sandbox !== 'string')
324
+ return;
325
+ const tokens = new Set(sandbox.split(/\s+/).filter(Boolean));
326
+ for (const banned of BANNED_SANDBOX_TOKENS) {
327
+ if (tokens.has(banned)) {
328
+ throw new BlockManifestError(`manifest.iframe.sandbox must not contain "${banned}". ` +
329
+ (banned === 'allow-same-origin'
330
+ ? 'Combined with "allow-scripts" it defeats the sandbox entirely.'
331
+ : 'Blocks must route navigation through the NAVIGATE postMessage so the host can mediate.'), 'iframe.sandbox');
332
+ }
333
+ }
334
+ }
335
+ /** SCHEMA_DIVERGENCES.scopeJustifications. */
336
+ function rejectUndeclaredScopeJustifications(manifest) {
337
+ const justifications = manifest.scopeJustifications;
338
+ if (justifications == null)
339
+ return;
340
+ const declared = new Set(manifest.scopes ?? []);
341
+ for (const scope of Object.keys(justifications)) {
342
+ if (!declared.has(scope)) {
343
+ const path = `scopeJustifications.${scope}`;
344
+ throw new BlockManifestError(`manifest.${path} justifies a scope that is not in manifest.scopes. ` +
345
+ 'Every justification key must be a scope the manifest actually requests.', path);
346
+ }
347
+ }
348
+ }
349
+ /**
350
+ * SCHEMA_DIVERGENCES.settings — the one hand-written mirror left, of
351
+ * civitai/civitai's `manifest-settings.meta.schema.ts`. Keep both sides aligned
352
+ * until that meta-schema is published and vendored; see
353
+ * KNOWN_GAPS['settings-meta-schema-not-vendored'].
354
+ */
355
+ const SETTING_TYPES = ['number', 'string', 'boolean'];
356
+ const SETTING_SCOPES = ['publisher', 'viewer'];
357
+ const SETTING_KEY_PATTERN = /^[a-z][a-z0-9_]{0,40}$/;
358
+ const MAX_SETTINGS_PER_BLOCK = 32;
359
+ function validateSettings(settings) {
360
+ if (settings == null || typeof settings !== 'object' || Array.isArray(settings)) {
361
+ throw new BlockManifestError('manifest.settings must be an object (record keyed by snake_case field name)', 'settings');
362
+ }
363
+ const entries = Object.entries(settings);
364
+ if (entries.length > MAX_SETTINGS_PER_BLOCK) {
365
+ throw new BlockManifestError(`manifest.settings has ${entries.length} entries (max ${MAX_SETTINGS_PER_BLOCK})`, 'settings');
366
+ }
367
+ for (const [key, raw] of entries) {
368
+ const path = `settings.${key}`;
369
+ if (!SETTING_KEY_PATTERN.test(key)) {
370
+ throw new BlockManifestError(`manifest.${path}: key must match ${SETTING_KEY_PATTERN} (snake_case, must start with a letter)`, path);
371
+ }
372
+ if (raw == null || typeof raw !== 'object') {
373
+ throw new BlockManifestError(`manifest.${path} must be an object`, path);
374
+ }
375
+ const s = raw;
376
+ if (typeof s.scope !== 'string' || !SETTING_SCOPES.includes(s.scope)) {
377
+ throw new BlockManifestError(`manifest.${path}.scope must be one of ${SETTING_SCOPES.join(', ')}. Got: ${JSON.stringify(s.scope)}`, `${path}.scope`);
378
+ }
379
+ if (typeof s.type !== 'string' || !SETTING_TYPES.includes(s.type)) {
380
+ throw new BlockManifestError(`manifest.${path}.type must be one of ${SETTING_TYPES.join(', ')}. Got: ${JSON.stringify(s.type)}`, `${path}.type`);
381
+ }
382
+ if (typeof s.label !== 'string' || s.label.length === 0) {
383
+ throw new BlockManifestError(`manifest.${path}.label must be a non-empty string`, `${path}.label`);
384
+ }
385
+ if (typeof s.description !== 'string' || s.description.length === 0) {
386
+ throw new BlockManifestError(`manifest.${path}.description must be a non-empty string`, `${path}.description`);
387
+ }
388
+ }
389
+ }
390
+ //# sourceMappingURL=defineBlock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defineBlock.js","sourceRoot":"","sources":["../../src/manifest/defineBlock.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,OAAO,OAAoD,MAAM,kBAAkB,CAAC;AAEpF,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGnD;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,YAAY,EAAE;QACZ,IAAI,EAAE,wBAAwB;QAC9B,SAAS,EACP,oFAAoF;YACpF,2FAA2F;YAC3F,6FAA6F;YAC7F,yFAAyF;YACzF,mBAAmB;QACrB,MAAM,EACJ,6FAA6F;YAC7F,6FAA6F;YAC7F,6BAA6B;KAChC;IACD,SAAS,EAAE;QACT,IAAI,EAAE,wBAAwB;QAC9B,SAAS,EACP,sFAAsF;YACtF,8FAA8F;YAC9F,gFAAgF;QAClF,MAAM,EAAE,2DAA2D;KACpE;IACD,gBAAgB,EAAE;QAChB,IAAI,EAAE,sEAAsE;QAC5E,SAAS,EACP,0FAA0F;YAC1F,qFAAqF;YACrF,yDAAyD;QAC3D,MAAM,EACJ,2FAA2F;YAC3F,wFAAwF;YACxF,4CAA4C;YAC5C,2FAA2F;YAC3F,2FAA2F;YAC3F,2FAA2F;YAC3F,sFAAsF;YACtF,qEAAqE;YACrE,+EAA+E;YAC/E,iDAAiD;KACpD;IACD,mBAAmB,EAAE;QACnB,IAAI,EAAE,uDAAuD;QAC7D,SAAS,EACP,2FAA2F;YAC3F,0FAA0F;YAC1F,8DAA8D;QAChE,MAAM,EACJ,0FAA0F;YAC1F,mFAAmF;YACnF,kEAAkE;KACrE;IACD,QAAQ,EAAE;QACR,IAAI,EAAE,wGAAwG;QAC9G,SAAS,EAAE,sGAAsG;QACjH,MAAM,EACJ,oEAAoE;YACpE,yFAAyF;YACzF,8CAA8C;YAC9C,4FAA4F;YAC5F,2FAA2F;YAC3F,0FAA0F;YAC1F,2FAA2F;YAC3F,wEAAwE;KAC3E;CACO,CAAC;AAEX;;;;;GAKG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,mDAAmD,EACjD,oFAAoF;QACpF,0FAA0F;QAC1F,yFAAyF;QACzF,6DAA6D;IAC/D,8BAA8B,EAC5B,2FAA2F;QAC3F,sFAAsF;QACtF,2FAA2F;QAC3F,aAAa;IACf,kCAAkC,EAChC,sFAAsF;QACtF,2FAA2F;QAC3F,8FAA8F;QAC9F,iEAAiE;IACnE,6BAA6B,EAC3B,yFAAyF;IAC3F,iBAAiB,EACf,8FAA8F;QAC9F,iDAAiD;IACnD,mCAAmC,EACjC,yFAAyF;QACzF,0FAA0F;QAC1F,kCAAkC;IACpC,yBAAyB,EACvB,8FAA8F;QAC9F,+FAA+F;QAC/F,4FAA4F;QAC5F,8CAA8C;CACxC,CAAC;AAEX;;;;;GAKG;AACH,MAAM,qBAAqB,GAAG,IAAI,GAAG,CAAC,iCAAiC,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAE1F,gFAAgF;AAChF,MAAM,UAAU,mBAAmB;IACjC,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAA4B,CAAC;AAC5F,CAAC;AAED,IAAI,QAAsC,CAAC;AAE3C,SAAS,kBAAkB;IACzB,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAC9B,2EAA2E;IAC3E,+EAA+E;IAC/E,8EAA8E;IAC9E,qBAAqB;IACrB,EAAE;IACF,iEAAiE;IACjE,8EAA8E;IAC9E,6EAA6E;IAC7E,4EAA4E;IAC5E,wEAAwE;IACxE,0DAA0D;IAC1D,MAAM,GAAG,GAAG,IAAI,OAAO,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;IAC3E,QAAQ,GAAG,GAAG,CAAC,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAAC;IAC9C,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,6FAA6F;AAC7F,SAAS,SAAS,CAAC,YAAoB;IACrC,IAAI,YAAY,KAAK,EAAE;QAAE,OAAO,EAAE,CAAC;IACnC,OAAO,YAAY;SAChB,KAAK,CAAC,CAAC,CAAC;SACR,KAAK,CAAC,GAAG,CAAC;SACV,MAAM,CAAC,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE;QACvB,8CAA8C;QAC9C,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QAC5D,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC;YAAE,OAAO,GAAG,GAAG,IAAI,GAAG,GAAG,CAAC;QAC/C,OAAO,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,IAAI,GAAG,EAAE,CAAC;IAC5C,CAAC,EAAE,EAAE,CAAC,CAAC;AACX,CAAC;AAED,MAAM,mBAAmB,GAAG,qBAAqB,CAAC;AAElD;;;;;GAKG;AACH,SAAS,eAAe,CAAC,KAAkB,EAAE,QAAiB;IAC5D,MAAM,IAAI,GAAG,SAAS,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IAC3C,IAAI,KAAK,CAAC,OAAO,KAAK,UAAU,EAAE,CAAC;QACjC,MAAM,OAAO,GAAI,KAAK,CAAC,MAAsC,CAAC,eAAe,CAAC;QAC9E,MAAM,KAAK,GAAG,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,OAAO,EAAE,CAAC;QAC3D,OAAO,IAAI,kBAAkB,CAAC,YAAY,KAAK,cAAc,EAAE,KAAK,CAAC,CAAC;IACxE,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,CAAC;IACnB,MAAM,KAAK,GAAG,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,YAAY,KAAK,EAAE,CAAC;IAC9D,IAAI,OAAO,GAAG,GAAG,KAAK,IAAI,KAAK,CAAC,OAAO,IAAI,YAAY,EAAE,CAAC;IAE1D,IAAI,KAAK,CAAC,OAAO,KAAK,MAAM,EAAE,CAAC;QAC7B,MAAM,OAAO,GAAI,KAAK,CAAC,MAAwC,CAAC,aAAa,CAAC;QAC9E,IAAI,OAAO;YAAE,OAAO,IAAI,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;IACnF,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,KAAK,sBAAsB,EAAE,CAAC;QAC7C,MAAM,KAAK,GAAI,KAAK,CAAC,MAAyC,CAAC,kBAAkB,CAAC;QAClF,OAAO,GAAG,GAAG,KAAK,IAAI,KAAK,+BAA+B,CAAC;QAC3D,OAAO,IAAI,kBAAkB,CAAC,OAAO,EAAE,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,IAAI,KAAK,EAAE,CAAC,CAAC;IACrF,CAAC;IAED,6EAA6E;IAC7E,0EAA0E;IAC1E,wCAAwC;IACxC,IAAI,iBAAiB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAClC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAC9D,MAAM,KAAK,GAAI,QAA+C,EAAE,MAAM,EAAE,CAAC,KAAK,CAAC,CAAC;QAChF,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,mBAAmB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YACjE,OAAO;gBACL,iFAAiF;oBACjF,wCAAwC,CAAC;QAC7C,CAAC;QACD,OAAO,IAAI,qBAAqB,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;IAC3E,CAAC;IAED,OAAO,IAAI,kBAAkB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;AAChD,CAAC;AASD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,MAAM,UAAU,WAAW,CAAC,MAAyB;IACnD,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC;IAE5B,IAAI,QAAQ,IAAI,IAAI,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QAChF,MAAM,IAAI,kBAAkB,CAAC,4BAA4B,CAAC,CAAC;IAC7D,CAAC;IAED,yEAAyE;IACzE,sCAAsC;IACtC,MAAM,QAAQ,GAAG,kBAAkB,EAAE,CAAC;IACtC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QACxB,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,IAAI,EAAE,CAAC;QACrC,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QACxB,oBAAoB;QACpB,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,kBAAkB,CAAC,mCAAmC,CAAC,CAAC;QAC9E,MAAM,eAAe,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IACzC,CAAC;IAED,mEAAmE;IACnE,kDAAkD;IAClD,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IAC5B,4BAA4B,CAAC,QAAQ,CAAC,CAAC;IACvC,mCAAmC,CAAC,QAAQ,CAAC,CAAC;IAC9C,IAAI,QAAQ,CAAC,QAAQ,KAAK,SAAS;QAAE,gBAAgB,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAEzE,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,oBAAoB,GACxB,8FAA8F,CAAC;AAEjG,yEAAyE;AACzE,SAAS,iBAAiB,CAAC,QAAuB;IAChD,IAAK,QAAoC,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;QAClE,MAAM,IAAI,kBAAkB,CAAC,sBAAsB,oBAAoB,EAAE,EAAE,WAAW,CAAC,CAAC;IAC1F,CAAC;IACD,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAuC,CAAC;IAChE,IAAI,MAAM,IAAI,IAAI,IAAI,MAAM,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;QAC/C,MAAM,IAAI,kBAAkB,CAAC,uBAAuB,oBAAoB,EAAE,EAAE,YAAY,CAAC,CAAC;IAC5F,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,qBAAqB,GAAG,IAAI,GAAG,CAAC;IACpC,mBAAmB;IACnB,sBAAsB;IACtB,yCAAyC;IACzC,0CAA0C;CAC3C,CAAC,CAAC;AAEH,SAAS,4BAA4B,CAAC,QAAuB;IAC3D,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzC,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO;IACxC,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IAC7D,KAAK,MAAM,MAAM,IAAI,qBAAqB,EAAE,CAAC;QAC3C,IAAI,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YACvB,MAAM,IAAI,kBAAkB,CAC1B,6CAA6C,MAAM,KAAK;gBACtD,CAAC,MAAM,KAAK,mBAAmB;oBAC7B,CAAC,CAAC,gEAAgE;oBAClE,CAAC,CAAC,wFAAwF,CAAC,EAC/F,gBAAgB,CACjB,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC;AAED,8CAA8C;AAC9C,SAAS,mCAAmC,CAAC,QAAuB;IAClE,MAAM,cAAc,GAAG,QAAQ,CAAC,mBAAmB,CAAC;IACpD,IAAI,cAAc,IAAI,IAAI;QAAE,OAAO;IACnC,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAS,QAAQ,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;IACxD,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,EAAE,CAAC;QAChD,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YACzB,MAAM,IAAI,GAAG,uBAAuB,KAAK,EAAE,CAAC;YAC5C,MAAM,IAAI,kBAAkB,CAC1B,YAAY,IAAI,qDAAqD;gBACnE,yEAAyE,EAC3E,IAAI,CACL,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,aAAa,GAAG,CAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,CAAU,CAAC;AAC/D,MAAM,cAAc,GAAG,CAAC,WAAW,EAAE,QAAQ,CAAU,CAAC;AACxD,MAAM,mBAAmB,GAAG,wBAAwB,CAAC;AACrD,MAAM,sBAAsB,GAAG,EAAE,CAAC;AAIlC,SAAS,gBAAgB,CAAC,QAAmC;IAC3D,IAAI,QAAQ,IAAI,IAAI,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QAChF,MAAM,IAAI,kBAAkB,CAC1B,6EAA6E,EAC7E,UAAU,CACX,CAAC;IACJ,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,QAAmC,CAAC,CAAC;IACpE,IAAI,OAAO,CAAC,MAAM,GAAG,sBAAsB,EAAE,CAAC;QAC5C,MAAM,IAAI,kBAAkB,CAC1B,yBAAyB,OAAO,CAAC,MAAM,iBAAiB,sBAAsB,GAAG,EACjF,UAAU,CACX,CAAC;IACJ,CAAC;IACD,KAAK,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,OAAO,EAAE,CAAC;QACjC,MAAM,IAAI,GAAG,YAAY,GAAG,EAAE,CAAC;QAC/B,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YACnC,MAAM,IAAI,kBAAkB,CAC1B,YAAY,IAAI,oBAAoB,mBAAmB,yCAAyC,EAChG,IAAI,CACL,CAAC;QACJ,CAAC;QACD,IAAI,GAAG,IAAI,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;YAC3C,MAAM,IAAI,kBAAkB,CAAC,YAAY,IAAI,oBAAoB,EAAE,IAAI,CAAC,CAAC;QAC3E,CAAC;QACD,MAAM,CAAC,GAAG,GAKT,CAAC;QACF,IAAI,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAqB,CAAC,EAAE,CAAC;YACrF,MAAM,IAAI,kBAAkB,CAC1B,YAAY,IAAI,yBAAyB,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,EACrG,GAAG,IAAI,QAAQ,CAChB,CAAC;QACJ,CAAC;QACD,IAAI,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAmB,CAAC,EAAE,CAAC;YACjF,MAAM,IAAI,kBAAkB,CAC1B,YAAY,IAAI,wBAAwB,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,EAClG,GAAG,IAAI,OAAO,CACf,CAAC;QACJ,CAAC;QACD,IAAI,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ,IAAI,CAAC,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACxD,MAAM,IAAI,kBAAkB,CAC1B,YAAY,IAAI,mCAAmC,EACnD,GAAG,IAAI,QAAQ,CAChB,CAAC;QACJ,CAAC;QACD,IAAI,OAAO,CAAC,CAAC,WAAW,KAAK,QAAQ,IAAI,CAAC,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACpE,MAAM,IAAI,kBAAkB,CAC1B,YAAY,IAAI,yCAAyC,EACzD,GAAG,IAAI,cAAc,CACtB,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,19 @@
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 type { DefineBlockConfig } from './defineBlock.js';
17
+ export { BlockManifestError } from '../blocks/manifestError.js';
18
+ export type { BlockManifest, BlockManifestV1 } from '../blocks/types.js';
19
+ //# sourceMappingURL=index.d.ts.map
@@ -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.47.0",
3
+ "version": "0.49.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
  }