@aippy/vite-plugins 0.3.0 → 0.4.0-dev.1

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.
@@ -0,0 +1,365 @@
1
+ import { promises as e } from "fs";
2
+ import t from "path";
3
+ import { parse as n } from "@babel/parser";
4
+ import r from "@babel/traverse";
5
+ import * as i from "@babel/types";
6
+ //#region \0@oxc-project+runtime@0.144.0/helpers/esm/typeof.js
7
+ function a(e) {
8
+ "@babel/helpers - typeof";
9
+ return a = typeof Symbol == "function" && typeof Symbol.iterator == "symbol" ? function(e) {
10
+ return typeof e;
11
+ } : function(e) {
12
+ return e && typeof Symbol == "function" && e.constructor === Symbol && e !== Symbol.prototype ? "symbol" : typeof e;
13
+ }, a(e);
14
+ }
15
+ //#endregion
16
+ //#region \0@oxc-project+runtime@0.144.0/helpers/esm/toPrimitive.js
17
+ function o(e, t) {
18
+ if (a(e) != "object" || !e) return e;
19
+ var n = e[Symbol.toPrimitive];
20
+ if (n !== void 0) {
21
+ var r = n.call(e, t || "default");
22
+ if (a(r) != "object") return r;
23
+ throw TypeError("@@toPrimitive must return a primitive value.");
24
+ }
25
+ return (t === "string" ? String : Number)(e);
26
+ }
27
+ //#endregion
28
+ //#region \0@oxc-project+runtime@0.144.0/helpers/esm/toPropertyKey.js
29
+ function s(e) {
30
+ var t = o(e, "string");
31
+ return a(t) == "symbol" ? t : t + "";
32
+ }
33
+ //#endregion
34
+ //#region \0@oxc-project+runtime@0.144.0/helpers/esm/defineProperty.js
35
+ function c(e, t, n) {
36
+ return (t = s(t)) in e ? Object.defineProperty(e, t, {
37
+ value: n,
38
+ enumerable: !0,
39
+ configurable: !0,
40
+ writable: !0
41
+ }) : e[t] = n, e;
42
+ }
43
+ //#endregion
44
+ //#region src/utils/file-scanner.ts
45
+ async function l(n, r) {
46
+ let i = [];
47
+ try {
48
+ let a = await e.readdir(n, { withFileTypes: !0 });
49
+ for (let e of a) {
50
+ let a = t.join(n, e.name);
51
+ if (e.isDirectory()) {
52
+ let e = await l(a, r);
53
+ i.push(...e);
54
+ } else if (e.isFile()) {
55
+ let n = t.extname(e.name);
56
+ r.includes(n) && i.push(a);
57
+ }
58
+ }
59
+ } catch (e) {
60
+ console.warn(`Warning: Failed to read directory ${n}:`, e);
61
+ }
62
+ return i;
63
+ }
64
+ function u(e, n) {
65
+ let r = t.relative(t.dirname(e), n).replace(/\.(ts|tsx|js|jsx|json)$/, "");
66
+ return r.startsWith(".") ? r : `./${r}`;
67
+ }
68
+ function d(e, n) {
69
+ return `@/${t.relative(n, e).replace(/\.(ts|tsx|js|jsx|json)$/, "")}`;
70
+ }
71
+ var f = class {
72
+ constructor() {
73
+ c(this, "cache", /* @__PURE__ */ new Map());
74
+ }
75
+ async read(t) {
76
+ if (this.cache.has(t)) return this.cache.get(t);
77
+ let n = await e.readFile(t, "utf-8");
78
+ return this.cache.set(t, n), n;
79
+ }
80
+ invalidate(e) {
81
+ this.cache.delete(e);
82
+ }
83
+ clear() {
84
+ this.cache.clear();
85
+ }
86
+ }, p = typeof r == "function" ? r : r.default;
87
+ function m(e) {
88
+ return i.isImportDefaultSpecifier(e) || i.isImportSpecifier(e) && (i.isIdentifier(e.imported, { name: "default" }) || i.isStringLiteral(e.imported, { value: "default" }));
89
+ }
90
+ function h(e) {
91
+ return i.isTSAsExpression(e) || i.isTSTypeAssertion(e) || i.isTSNonNullExpression(e) || i.isTSSatisfiesExpression(e) || i.isParenthesizedExpression(e);
92
+ }
93
+ function g(e) {
94
+ for (; h(e);) e = e.expression;
95
+ return e;
96
+ }
97
+ function _(e, t) {
98
+ let n = /* @__PURE__ */ new Set(), r = /* @__PURE__ */ new Set();
99
+ p(e, { ImportDeclaration: (e) => {
100
+ if (!(!t.has(e.node.source.value) || e.node.importKind === "type")) for (let t of e.node.specifiers) {
101
+ if (t.importKind === "type") continue;
102
+ let a = e.scope.getBinding(t.local.name);
103
+ i.isImportNamespaceSpecifier(t) && r.add(a), m(t) && n.add(a);
104
+ }
105
+ } });
106
+ let a = (e, t, o = /* @__PURE__ */ new Set()) => {
107
+ if (e = g(e), i.isIdentifier(e)) {
108
+ let s = t.getBinding(e.name);
109
+ if (n.has(s)) return "map";
110
+ if (r.has(s)) return "namespace";
111
+ if (s?.constant && s.kind === "const" && !o.has(s) && s.path.isVariableDeclarator() && s.path.node.init) {
112
+ o.add(s);
113
+ let t = s.path.node, n = a(t.init, s.path.scope, o);
114
+ if (i.isIdentifier(t.id, { name: e.name })) return n;
115
+ if (n === "namespace" && i.isObjectPattern(t.id) && t.id.properties.find((t) => i.isObjectProperty(t) && i.isIdentifier(t.value, { name: e.name }) && (t.computed ? i.isStringLiteral(g(t.key), { value: "default" }) : i.isIdentifier(t.key, { name: "default" }) || i.isStringLiteral(t.key, { value: "default" })))) return "map";
116
+ }
117
+ }
118
+ if ((i.isMemberExpression(e) || i.isOptionalMemberExpression(e)) && (e.computed ? i.isStringLiteral(g(e.property), { value: "default" }) : i.isIdentifier(e.property, { name: "default" })) && a(e.object, t, o) === "namespace") return "map";
119
+ };
120
+ return (e) => {
121
+ for (; h(e.parent) && e.key === "expression";) e = e.parentPath;
122
+ let t = e.parent;
123
+ if ((i.isMemberExpression(t) || i.isOptionalMemberExpression(t)) && e.key === "property" && t.computed) return a(t.object, e.scope) === "map";
124
+ if (i.isObjectProperty(t) && e.key === "key" && i.isObjectPattern(e.parentPath.parent)) {
125
+ let t = e.parentPath.parentPath;
126
+ if (i.isVariableDeclarator(t.parent) && t.key === "id" && t.parent.init) return a(t.parent.init, e.scope) === "map";
127
+ if (i.isAssignmentExpression(t.parent) && t.key === "left") return a(t.parent.right, e.scope) === "map";
128
+ }
129
+ return !1;
130
+ };
131
+ }
132
+ function v(e, t, n) {
133
+ let r = n.replace(/\.json$/, ".ts"), i = d(r, t), a = u(e, r);
134
+ return /* @__PURE__ */ new Set([
135
+ i,
136
+ i + ".ts",
137
+ a,
138
+ a + ".ts",
139
+ u(e, n) + ".json",
140
+ "@/config/assets.json"
141
+ ]);
142
+ }
143
+ function y(e, t, r, i) {
144
+ let a = n(e, {
145
+ sourceType: "module",
146
+ plugins: [
147
+ "typescript",
148
+ "jsx",
149
+ "decorators-legacy"
150
+ ]
151
+ }), o = _(a, v(t, r, i)), s = [];
152
+ p(a, { StringLiteral: (e) => {
153
+ o(e) && s.push([e.node.start, e.node.end]);
154
+ } });
155
+ for (let [t, n] of s.sort((e, t) => t[0] - e[0])) e = e.slice(0, t) + " ".repeat(n - t) + e.slice(n);
156
+ return e;
157
+ }
158
+ //#endregion
159
+ //#region src/asset-constants/manifest.ts
160
+ var b = 1 << 20, x = [
161
+ "image",
162
+ "image-bg",
163
+ "image-sprite",
164
+ "image-icon",
165
+ "image-tile",
166
+ "sound",
167
+ "music",
168
+ "synth",
169
+ "model",
170
+ "animation",
171
+ "video",
172
+ "file"
173
+ ];
174
+ function S(e) {
175
+ throw Error(`assets: ${e}`);
176
+ }
177
+ function C(e, t, n = []) {
178
+ (!e || typeof e != "object" || Array.isArray(e)) && S("object required");
179
+ let r = e;
180
+ return Object.keys(r).some((e) => !t.includes(e)) && S("unknown field"), n.some((e) => !(e in r) || r[e] === null) && S("required field missing"), r;
181
+ }
182
+ function w(e, t, n = !1) {
183
+ (typeof e != "string" || Buffer.byteLength(e) > t || /[\x00-\x08\x0b\x0c\x0e-\x1f\x7f\uFEFF]/.test(e) || n && !e.trim()) && S("invalid bounded text");
184
+ }
185
+ function T(e, t) {
186
+ (typeof e != "string" || !t.includes(e)) && S("unsupported field value");
187
+ }
188
+ function E(e, t = !1) {
189
+ (typeof e != "number" || !Number.isFinite(e) || e <= 0 || t && !Number.isInteger(e)) && S("positive dimension/duration required");
190
+ }
191
+ function D(e) {
192
+ let t = C(e, [
193
+ "stage",
194
+ "code",
195
+ "message",
196
+ "retryable"
197
+ ], [
198
+ "stage",
199
+ "code",
200
+ "message",
201
+ "retryable"
202
+ ]);
203
+ w(t.stage, 128, !0), w(t.code, 128, !0), w(t.message, 4096, !0), typeof t.retryable != "boolean" && S("retryable boolean required");
204
+ }
205
+ function O(e) {
206
+ return typeof e.schemaVersion == "number";
207
+ }
208
+ function k(e) {
209
+ let t = 0, n = () => {
210
+ for (; /\s/.test(e[t] ?? "") && t < e.length;) t++;
211
+ }, r = () => {
212
+ let n = t++;
213
+ for (; t < e.length;) {
214
+ if (e[t] === "\\") {
215
+ t += 2;
216
+ continue;
217
+ }
218
+ if (e[t++] === "\"") return JSON.parse(e.slice(n, t));
219
+ }
220
+ return S("invalid string");
221
+ }, i = () => {
222
+ if (n(), e[t] === "{") {
223
+ t++, n();
224
+ let a = /* @__PURE__ */ new Set();
225
+ for (; e[t] !== "}";) {
226
+ let o = r();
227
+ if (a.has(o) && S("duplicate object key"), a.add(o), n(), t++, i(), n(), e[t] !== ",") break;
228
+ t++, n();
229
+ }
230
+ t++;
231
+ } else if (e[t] === "[") {
232
+ for (t++, n(); e[t] !== "]" && (i(), n(), e[t] === ",");) t++, n();
233
+ t++;
234
+ } else if (e[t] === "\"") r();
235
+ else for (; t < e.length && !/[\s,\]}]/.test(e[t]);) t++;
236
+ };
237
+ i();
238
+ }
239
+ function A(e) {
240
+ (!e || Buffer.byteLength(e) > b) && S("invalid document size");
241
+ let t;
242
+ try {
243
+ t = JSON.parse(e);
244
+ } catch {
245
+ S("invalid JSON syntax");
246
+ }
247
+ k(e);
248
+ let n = C(t, Object.keys(t));
249
+ if (!("schemaVersion" in n)) return Object.values(n).some((e) => typeof e != "string") && S("legacy inventory must contain URLs"), n;
250
+ C(n, [
251
+ "schemaVersion",
252
+ "artStyle",
253
+ "assets",
254
+ "planningError"
255
+ ], [
256
+ "schemaVersion",
257
+ "artStyle",
258
+ "assets"
259
+ ]), n.schemaVersion !== 1 && S("unsupported schema version"), w(n.artStyle, 2048), n.planningError !== void 0 && (D(n.planningError), n.planningError.stage !== "planning" && S("planningError stage must be planning"));
260
+ let r = C(n.assets, Object.keys(n.assets ?? {}));
261
+ for (let [e, t] of Object.entries(r)) {
262
+ w(e, 256, !0);
263
+ let n = C(t, [
264
+ "type",
265
+ "source",
266
+ "replaceable",
267
+ "description",
268
+ "params",
269
+ "generation",
270
+ "status",
271
+ "url",
272
+ "output",
273
+ "error",
274
+ "implementation"
275
+ ], [
276
+ "type",
277
+ "source",
278
+ "replaceable",
279
+ "description",
280
+ "status"
281
+ ]);
282
+ if (T(n.type, x), T(n.source, [
283
+ "generated",
284
+ "user",
285
+ "existing",
286
+ "code"
287
+ ]), (typeof n.replaceable != "boolean" || n.replaceable !== (n.source !== "code")) && S("source/replaceable mismatch"), w(n.description, 4096, !0), T(n.status, [
288
+ "planned",
289
+ "ready",
290
+ "failed",
291
+ "skipped",
292
+ "degraded"
293
+ ]), n.source === "generated") {
294
+ String(n.type).startsWith("image") || n.type === "sound" || n.type === "music" || S("unsupported generation type");
295
+ let e = C(n.generation, ["action", "prompt"], ["action", "prompt"]);
296
+ T(e.action, ["generate", "reuse"]), w(e.prompt, 4096, !0);
297
+ } else n.generation !== void 0 && S("non-generated assets cannot contain generation");
298
+ if ((n.type === "synth" || n.type === "animation") && n.source !== "code" && S("type requires code source"), n.url !== void 0 && (w(n.url, 4096), (typeof n.url != "string" || !j(n.url)) && S("invalid resource reference")), (n.source === "user" || n.source === "existing") && (!n.url || n.status !== "ready") && S("existing resource requires ready URL"), n.source === "generated" && n.status === "ready" && !n.url && S("ready media requires URL"), n.status === "degraded" && (n.source !== "generated" || !n.url || !n.error) && S("degraded media requires temporary URL and reason"), n.params !== void 0) {
299
+ let e = C(n.params, [
300
+ "width",
301
+ "height",
302
+ "durationSeconds",
303
+ "loop"
304
+ ]);
305
+ for (let t of ["width", "height"]) e[t] !== void 0 && E(e[t], !0);
306
+ e.durationSeconds !== void 0 && E(e.durationSeconds), e.loop !== void 0 && typeof e.loop != "boolean" && S("loop boolean required");
307
+ }
308
+ if (n.output !== void 0) {
309
+ let e = C(n.output, [
310
+ "format",
311
+ "width",
312
+ "height",
313
+ "durationSeconds"
314
+ ]);
315
+ e.format !== void 0 && w(e.format, 32);
316
+ for (let t of ["width", "height"]) e[t] !== void 0 && E(e[t], !0);
317
+ e.durationSeconds !== void 0 && E(e.durationSeconds);
318
+ }
319
+ if (n.error !== void 0 && D(n.error), n.implementation !== void 0) {
320
+ let e = C(n.implementation, [
321
+ "method",
322
+ "description",
323
+ "status",
324
+ "path",
325
+ "symbol"
326
+ ], [
327
+ "method",
328
+ "description",
329
+ "status"
330
+ ]);
331
+ T(e.method, [
332
+ "svg",
333
+ "canvas",
334
+ "css",
335
+ "synth",
336
+ "animation",
337
+ "three"
338
+ ]), T(e.status, ["planned", "ready"]), w(e.description, 4096, !0), e.path !== void 0 && w(e.path, 4096), e.symbol !== void 0 && w(e.symbol, 256), e.status === "ready" && !e.path && !e.symbol && S("ready implementation requires path or symbol"), n.source === "code" && n.status !== e.status && S("code status mismatch");
339
+ }
340
+ n.source === "code" && (!n.implementation || n.url || n.output || n.error || !["planned", "ready"].includes(String(n.status))) && S("code requires implementation without media result");
341
+ }
342
+ return n;
343
+ }
344
+ function j(e) {
345
+ if (!e || e.trim() !== e || /[\r\n\t\\]/.test(e) || e.startsWith("//") || /%(?![0-9a-f]{2})/i.test(e.split("?")[0]) || /%(?![0-9a-f]{2})/i.test(e.split("#")[1] ?? "")) return !1;
346
+ if (!/^[a-z][a-z0-9+.-]*:/i.test(e)) return !0;
347
+ if (!/^https?:\/\//i.test(e)) return !1;
348
+ try {
349
+ let t = e.slice(e.indexOf("://") + 3).split(/[/?#]/, 1)[0];
350
+ if (!t || t.includes("%")) return !1;
351
+ let n = new URL(e);
352
+ return ["http:", "https:"].includes(n.protocol) && !!n.hostname && !n.username && !n.password;
353
+ } catch {
354
+ return !1;
355
+ }
356
+ }
357
+ function M(e) {
358
+ return O(e) ? Object.fromEntries(Object.entries(e.assets).filter(([, e]) => e.replaceable && typeof e.url == "string" && e.url.length > 0).map(([e, t]) => [e, t.url])) : { ...e };
359
+ }
360
+ function N(e) {
361
+ let t = e.split(/[?#]/)[0].toLowerCase();
362
+ return /\.(mp3|wav|ogg|m4a|aac|flac)$/.test(t) ? "sound" : /\.(mp4|webm|mov)$/.test(t) ? "video" : /\.(png|jpg|jpeg|gif|svg|webp|avif)$/.test(t) ? "image" : /\.(glb|gltf|obj)$/.test(t) ? "model" : "file";
363
+ }
364
+ //#endregion
365
+ export { m as a, _ as c, l as d, u as f, A as i, f as l, N as n, y as o, c as p, O as r, v as s, M as t, d as u };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aippy/vite-plugins",
3
- "version": "0.3.0",
3
+ "version": "0.4.0-dev.1",
4
4
  "description": "Vite plugins for Aippy projects - Asset management and component tagging",
5
5
  "private": false,
6
6
  "type": "module",
@@ -27,11 +27,13 @@
27
27
  "./utils": {
28
28
  "import": "./dist/utils/index.js",
29
29
  "types": "./dist/utils.d.ts"
30
- }
30
+ },
31
+ "./assets.schema.json": "./src/asset-constants/assets.schema.json"
31
32
  },
32
33
  "files": [
33
34
  "dist",
34
- "README.md"
35
+ "README.md",
36
+ "src/asset-constants/assets.schema.json"
35
37
  ],
36
38
  "publishConfig": {
37
39
  "access": "public"
@@ -0,0 +1,232 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "Aippy asset manifest",
4
+ "type": "object",
5
+ "additionalProperties": false,
6
+ "required": ["schemaVersion", "artStyle", "assets"],
7
+ "properties": {
8
+ "schemaVersion": { "const": 1 },
9
+ "artStyle": { "type": "string" },
10
+ "planningError": {
11
+ "allOf": [
12
+ { "$ref": "#/$defs/error" },
13
+ { "properties": { "stage": { "const": "planning" } } }
14
+ ]
15
+ },
16
+ "assets": {
17
+ "type": "object",
18
+ "propertyNames": { "minLength": 1 },
19
+ "additionalProperties": { "$ref": "#/$defs/entry" }
20
+ }
21
+ },
22
+ "$defs": {
23
+ "error": {
24
+ "type": "object",
25
+ "additionalProperties": false,
26
+ "required": ["stage", "code", "message", "retryable"],
27
+ "properties": {
28
+ "stage": { "type": "string", "minLength": 1 },
29
+ "code": { "type": "string", "minLength": 1 },
30
+ "message": { "type": "string", "minLength": 1 },
31
+ "retryable": { "type": "boolean" }
32
+ }
33
+ },
34
+ "params": {
35
+ "type": "object",
36
+ "additionalProperties": false,
37
+ "properties": {
38
+ "width": { "type": "integer", "minimum": 1 },
39
+ "height": { "type": "integer", "minimum": 1 },
40
+ "durationSeconds": { "type": "number", "exclusiveMinimum": 0 },
41
+ "loop": { "type": "boolean" }
42
+ }
43
+ },
44
+ "output": {
45
+ "type": "object",
46
+ "additionalProperties": false,
47
+ "properties": {
48
+ "format": { "type": "string" },
49
+ "width": { "type": "integer", "minimum": 1 },
50
+ "height": { "type": "integer", "minimum": 1 },
51
+ "durationSeconds": { "type": "number", "exclusiveMinimum": 0 }
52
+ }
53
+ },
54
+ "implementation": {
55
+ "type": "object",
56
+ "additionalProperties": false,
57
+ "required": ["method", "description", "status"],
58
+ "properties": {
59
+ "method": {
60
+ "enum": ["svg", "canvas", "css", "synth", "animation", "three"]
61
+ },
62
+ "description": { "type": "string", "minLength": 1 },
63
+ "status": { "enum": ["planned", "ready"] },
64
+ "path": { "type": "string" },
65
+ "symbol": { "type": "string" }
66
+ },
67
+ "allOf": [
68
+ {
69
+ "if": { "properties": { "status": { "const": "ready" } } },
70
+ "then": {
71
+ "anyOf": [
72
+ {
73
+ "required": ["path"],
74
+ "properties": { "path": { "minLength": 1 } }
75
+ },
76
+ {
77
+ "required": ["symbol"],
78
+ "properties": { "symbol": { "minLength": 1 } }
79
+ }
80
+ ]
81
+ }
82
+ }
83
+ ]
84
+ },
85
+ "entry": {
86
+ "type": "object",
87
+ "additionalProperties": false,
88
+ "required": ["type", "source", "replaceable", "description", "status"],
89
+ "properties": {
90
+ "type": {
91
+ "enum": [
92
+ "image",
93
+ "image-bg",
94
+ "image-sprite",
95
+ "image-icon",
96
+ "image-tile",
97
+ "sound",
98
+ "music",
99
+ "synth",
100
+ "model",
101
+ "animation",
102
+ "video",
103
+ "file"
104
+ ]
105
+ },
106
+ "source": { "enum": ["generated", "user", "existing", "code"] },
107
+ "replaceable": { "type": "boolean" },
108
+ "description": { "type": "string", "minLength": 1 },
109
+ "params": { "$ref": "#/$defs/params" },
110
+ "generation": {
111
+ "type": "object",
112
+ "additionalProperties": false,
113
+ "required": ["action", "prompt"],
114
+ "properties": {
115
+ "action": { "enum": ["generate", "reuse"] },
116
+ "prompt": { "type": "string", "minLength": 1 }
117
+ }
118
+ },
119
+ "status": {
120
+ "enum": ["planned", "ready", "failed", "skipped", "degraded"]
121
+ },
122
+ "url": { "type": "string" },
123
+ "output": { "$ref": "#/$defs/output" },
124
+ "error": { "$ref": "#/$defs/error" },
125
+ "implementation": { "$ref": "#/$defs/implementation" }
126
+ },
127
+ "allOf": [
128
+ {
129
+ "if": { "properties": { "source": { "const": "generated" } } },
130
+ "then": {
131
+ "required": ["generation"],
132
+ "properties": {
133
+ "replaceable": { "const": true },
134
+ "type": {
135
+ "enum": [
136
+ "image",
137
+ "image-bg",
138
+ "image-sprite",
139
+ "image-icon",
140
+ "image-tile",
141
+ "sound",
142
+ "music"
143
+ ]
144
+ }
145
+ }
146
+ },
147
+ "else": { "not": { "required": ["generation"] } }
148
+ },
149
+ {
150
+ "if": { "properties": { "source": { "const": "code" } } },
151
+ "then": {
152
+ "required": ["implementation"],
153
+ "properties": {
154
+ "replaceable": { "const": false },
155
+ "status": { "enum": ["planned", "ready"] }
156
+ },
157
+ "not": {
158
+ "anyOf": [
159
+ { "required": ["url"] },
160
+ { "required": ["output"] },
161
+ { "required": ["error"] }
162
+ ]
163
+ },
164
+ "allOf": [
165
+ {
166
+ "if": { "properties": { "status": { "const": "ready" } } },
167
+ "then": {
168
+ "properties": {
169
+ "implementation": {
170
+ "properties": { "status": { "const": "ready" } }
171
+ }
172
+ }
173
+ }
174
+ },
175
+ {
176
+ "if": { "properties": { "status": { "const": "planned" } } },
177
+ "then": {
178
+ "properties": {
179
+ "implementation": {
180
+ "properties": { "status": { "const": "planned" } }
181
+ }
182
+ }
183
+ }
184
+ }
185
+ ]
186
+ },
187
+ "else": { "properties": { "replaceable": { "const": true } } }
188
+ },
189
+ {
190
+ "if": {
191
+ "properties": { "type": { "enum": ["synth", "animation"] } }
192
+ },
193
+ "then": { "properties": { "source": { "const": "code" } } }
194
+ },
195
+ {
196
+ "if": {
197
+ "properties": { "source": { "enum": ["user", "existing"] } }
198
+ },
199
+ "then": {
200
+ "required": ["url"],
201
+ "properties": {
202
+ "url": { "minLength": 1 },
203
+ "status": { "const": "ready" }
204
+ }
205
+ }
206
+ },
207
+ {
208
+ "if": {
209
+ "properties": {
210
+ "source": { "const": "generated" },
211
+ "status": { "const": "ready" }
212
+ }
213
+ },
214
+ "then": {
215
+ "required": ["url"],
216
+ "properties": { "url": { "minLength": 1 } }
217
+ }
218
+ },
219
+ {
220
+ "if": { "properties": { "status": { "const": "degraded" } } },
221
+ "then": {
222
+ "required": ["url", "error"],
223
+ "properties": {
224
+ "source": { "const": "generated" },
225
+ "url": { "minLength": 1 }
226
+ }
227
+ }
228
+ }
229
+ ]
230
+ }
231
+ }
232
+ }