@cueframe/kernel 0.2.57 → 0.2.58

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 (98) hide show
  1. package/dist/THIRD_PARTY_NOTICES.md +111 -0
  2. package/dist/_members/component-runtime/template/bind.d.ts +22 -0
  3. package/dist/_members/component-runtime/template/bind.d.ts.map +1 -0
  4. package/dist/_members/component-runtime/template/extract.d.ts +31 -0
  5. package/dist/_members/component-runtime/template/extract.d.ts.map +1 -0
  6. package/dist/_members/component-runtime/template/index.d.ts +5 -0
  7. package/dist/_members/component-runtime/template/index.d.ts.map +1 -0
  8. package/dist/_members/component-runtime/template/schema.d.ts +1580 -0
  9. package/dist/_members/component-runtime/template/schema.d.ts.map +1 -0
  10. package/dist/_members/component-runtime/template/testing/fixtures.d.ts +33 -0
  11. package/dist/_members/component-runtime/template/testing/fixtures.d.ts.map +1 -0
  12. package/dist/_members/component-runtime/template/testing/index.d.ts +3 -0
  13. package/dist/_members/component-runtime/template/testing/index.d.ts.map +1 -0
  14. package/dist/_members/component-runtime/template/testing/templateApplyContract.d.ts +54 -0
  15. package/dist/_members/component-runtime/template/testing/templateApplyContract.d.ts.map +1 -0
  16. package/dist/_members/composition/primitive-catalog/index.d.ts +2 -1
  17. package/dist/_members/composition/primitive-catalog/types.d.ts +3 -1
  18. package/dist/_members/composition/validators/brandKit.d.ts +7 -0
  19. package/dist/_members/composition/validators/brandKitBody.d.ts +543 -0
  20. package/dist/_members/errors/registry.d.ts +36 -0
  21. package/dist/_members/mcp/server.d.ts +5 -2
  22. package/dist/_members/primitives/backgrounds/dynamic-grid/config.d.ts +1 -1
  23. package/dist/_members/primitives/effects/blur/config.d.ts +1 -1
  24. package/dist/_members/primitives/effects/color-grade/config.d.ts +2 -2
  25. package/dist/_members/primitives/effects/grain/config.d.ts +3 -3
  26. package/dist/_members/primitives/effects/particles/config.d.ts +2 -2
  27. package/dist/_members/primitives/effects/stage-camera/config.d.ts +6 -6
  28. package/dist/_members/primitives/effects/vignette/config.d.ts +1 -1
  29. package/dist/_members/primitives/layouts/flex-layout/config.d.ts +3 -3
  30. package/dist/_members/primitives/layouts/picture-in-picture/config.d.ts +2 -2
  31. package/dist/_members/primitives/lib/types.d.ts +1 -1
  32. package/dist/_members/primitives/scenes/device-showcase/config.d.ts +7 -7
  33. package/dist/_members/primitives/text/cinematic-title/config.d.ts +3 -3
  34. package/dist/_members/primitives/text/karaoke/config.d.ts +1 -1
  35. package/dist/_members/primitives/text/kinetic-type/config.d.ts +3 -3
  36. package/dist/_members/primitives/text/stat-counter/config.d.ts +1 -1
  37. package/dist/_members/primitives/text/text-effect/config.d.ts +2 -2
  38. package/dist/_members/primitives/transitions/grid-pixelate-wipe/config.d.ts +1 -1
  39. package/dist/_members/primitives/ui-blocks/badge-pop/config.d.ts +3 -3
  40. package/dist/_members/primitives/ui-blocks/terminal-simulator/config.d.ts +2 -2
  41. package/dist/_members/primitives/ui-blocks/toast-notification/config.d.ts +2 -2
  42. package/dist/authoring/build-tools.js +5 -4
  43. package/dist/authoring/context.js +2 -1
  44. package/dist/authoring/tools.js +4 -3
  45. package/dist/authoring.js +7 -6
  46. package/dist/{chunk-R7G2PNEZ.js → chunk-2L33CKIY.js} +11 -13
  47. package/dist/{chunk-N54EGUVM.js → chunk-3VLDQE26.js} +1 -1
  48. package/dist/{chunk-7AZLFN26.js → chunk-4NIJWK45.js} +20 -12
  49. package/dist/{chunk-KSRFYFUZ.js → chunk-5T655LYZ.js} +1 -1
  50. package/dist/{chunk-RERR5JLM.js → chunk-6UZYUWBX.js} +1 -1
  51. package/dist/{chunk-LBN5KQC6.js → chunk-7EDLPIUN.js} +3 -3
  52. package/dist/{chunk-MZB4ZXYW.js → chunk-7NIOFHMH.js} +1 -1
  53. package/dist/{chunk-E63UEO5C.js → chunk-AK5QBHOB.js} +6 -0
  54. package/dist/{chunk-BDFAA747.js → chunk-GQ6XGAFB.js} +1 -1
  55. package/dist/chunk-IG3YAOUZ.js +418 -0
  56. package/dist/{chunk-Z4G3VM3H.js → chunk-JN3GF36B.js} +1 -1
  57. package/dist/{chunk-DV7ZFVL4.js → chunk-MFMR3LHO.js} +1 -1
  58. package/dist/{chunk-JPMVNMF3.js → chunk-MNO6AO2J.js} +1 -1
  59. package/dist/{chunk-VXGA7LQQ.js → chunk-NVXSKKRN.js} +2 -2
  60. package/dist/chunk-PWWZTY3G.js +254 -0
  61. package/dist/chunk-REST55RC.js +366 -0
  62. package/dist/chunk-TR53N6HB.js +196 -0
  63. package/dist/{chunk-4PV3UYJF.js → chunk-UXRSEMJJ.js} +201 -201
  64. package/dist/component-runtime/template/testing.d.ts +1 -0
  65. package/dist/component-runtime/template/testing.js +380 -0
  66. package/dist/component-runtime/template.d.ts +1 -0
  67. package/dist/component-runtime/template.js +99 -0
  68. package/dist/compose.js +20 -20
  69. package/dist/composition/floors.js +1 -1
  70. package/dist/composition/ops.js +2 -1
  71. package/dist/composition/primitive-catalog.js +3 -1
  72. package/dist/composition/projection.js +2 -2
  73. package/dist/composition/validators/brand-kit-body.d.ts +1 -0
  74. package/dist/composition/validators/brand-kit-body.js +17 -0
  75. package/dist/composition/validators.js +10 -6
  76. package/dist/composition-ops.js +7 -6
  77. package/dist/composition.js +12 -8
  78. package/dist/{context-6HPUO36M.js → context-4WSFPRLX.js} +2 -1
  79. package/dist/core/render.js +4 -4
  80. package/dist/core/select.js +2 -2
  81. package/dist/core.js +4 -4
  82. package/dist/director/ai-sdk.js +3 -3
  83. package/dist/director/domain.js +2 -2
  84. package/dist/director.js +3 -3
  85. package/dist/errors.js +1 -1
  86. package/dist/mcp/adapter.js +3 -3
  87. package/dist/mcp/server.js +3 -26
  88. package/dist/mcp.js +3 -26
  89. package/dist/nle-export.js +1 -1
  90. package/dist/primitives/captions.js +3 -3
  91. package/dist/primitives.js +18 -18
  92. package/dist/render-harness/cardAssetAdmission.js +2 -2
  93. package/dist/render-harness/cardFonts.js +2 -2
  94. package/dist/render-harness/renderInputAssets.js +2 -2
  95. package/dist/render-harness.js +8 -8
  96. package/dist/src-ZP6727AA.js +70 -0
  97. package/package.json +22 -10
  98. package/dist/chunk-AXRSCHQX.js +0 -661
@@ -0,0 +1,254 @@
1
+ import {
2
+ GraphicRole,
3
+ OpaqueId
4
+ } from "./chunk-UFD4EZHM.js";
5
+ import {
6
+ brandKitCaptionFaceIssues
7
+ } from "./chunk-OOL7ZUG7.js";
8
+
9
+ // ../composition/src/validators/brandKit.ts
10
+ import { z } from "zod";
11
+ var EasingValue = z.union([
12
+ z.enum(["linear", "ease", "ease-in", "ease-out", "ease-in-out"]),
13
+ z.object({
14
+ kind: z.literal("cubic-bezier"),
15
+ x1: z.number().min(0).max(1),
16
+ y1: z.number(),
17
+ x2: z.number().min(0).max(1),
18
+ y2: z.number()
19
+ })
20
+ ]);
21
+ var AudioPaletteRef = z.string().min(1);
22
+ var FontRef = z.union([
23
+ z.object({
24
+ kind: z.literal("system"),
25
+ family: z.string().min(1),
26
+ weight: z.number().int().min(1).max(1e3).optional(),
27
+ style: z.enum(["normal", "italic"]).optional()
28
+ }),
29
+ z.object({
30
+ kind: z.literal("web"),
31
+ family: z.string().min(1),
32
+ url: z.string().url().optional(),
33
+ weight: z.number().int().min(1).max(1e3).optional(),
34
+ style: z.enum(["normal", "italic"]).optional()
35
+ }),
36
+ z.object({
37
+ kind: z.literal("uploaded"),
38
+ fontId: OpaqueId,
39
+ family: z.string().optional(),
40
+ weight: z.number().int().min(1).max(1e3).optional(),
41
+ style: z.enum(["normal", "italic"]).optional()
42
+ })
43
+ ]);
44
+ var Colors = z.object({
45
+ primary: z.string().min(1),
46
+ secondary: z.string().optional(),
47
+ accent: z.string().min(1),
48
+ background: z.string().min(1),
49
+ text: z.string().min(1),
50
+ textMuted: z.string().optional(),
51
+ border: z.string().optional(),
52
+ surface: z.string().optional(),
53
+ card: z.string().optional(),
54
+ gridLine: z.string().optional(),
55
+ accentMuted: z.string().optional(),
56
+ textOnAccent: z.string().optional(),
57
+ textOnSurface: z.string().optional()
58
+ });
59
+ var Fonts = z.object({
60
+ heading: FontRef,
61
+ body: FontRef.optional(),
62
+ caption: FontRef.optional()
63
+ });
64
+ var Spacing = z.object({
65
+ base: z.number().positive().optional(),
66
+ lg: z.number().positive().optional(),
67
+ sm: z.number().positive().optional()
68
+ });
69
+ var Sizing = z.object({
70
+ fontSizeHeading: z.number().positive().optional(),
71
+ fontSizeBody: z.number().positive().optional(),
72
+ fontSizeLabel: z.number().positive().optional(),
73
+ borderRadius: z.number().nonnegative().optional()
74
+ });
75
+ var Motion = z.object({
76
+ duration: z.number().positive().optional(),
77
+ easing: EasingValue.optional(),
78
+ entrance: z.enum(["none", "fade", "word-pop", "stagger-up"]).optional(),
79
+ staggerMs: z.number().nonnegative().optional(),
80
+ exitMs: z.number().nonnegative().optional()
81
+ });
82
+ var Placement = z.enum(["tl", "tr", "bl", "br"]);
83
+ var Logo = z.object({
84
+ src: z.string().min(1),
85
+ alt: z.string().optional(),
86
+ placement: Placement.optional(),
87
+ sizePct: z.number().positive().optional()
88
+ });
89
+ var IntroOutro = z.object({
90
+ kind: z.enum(["card", "video"]),
91
+ src: z.string().min(1).optional(),
92
+ title: z.string().optional(),
93
+ subtitle: z.string().optional(),
94
+ durationMs: z.number().positive().optional()
95
+ });
96
+ var Watermark = z.object({
97
+ src: z.string().min(1),
98
+ opacity: z.number().min(0).max(1).optional(),
99
+ placement: Placement.optional()
100
+ });
101
+ var MotifBody = z.discriminatedUnion("kind", [
102
+ z.object({
103
+ kind: z.literal("card"),
104
+ html: z.string().min(1),
105
+ tokens: z.record(z.string(), z.string()).optional()
106
+ }),
107
+ z.object({
108
+ kind: z.literal("overlay"),
109
+ primitiveId: z.string().min(1),
110
+ params: z.record(z.string(), z.unknown()).optional()
111
+ }),
112
+ z.object({
113
+ kind: z.literal("component"),
114
+ componentId: z.string().min(1),
115
+ props: z.record(z.string(), z.unknown()).optional()
116
+ })
117
+ ]);
118
+ var MotifMotionPreset = z.enum([
119
+ "fade-up",
120
+ "slide-in-left",
121
+ "slide-in-right",
122
+ "scale-pop",
123
+ "stagger-reveal"
124
+ ]);
125
+ var Motif = z.object({
126
+ id: z.string().min(1).max(40),
127
+ observed: z.string().min(1).max(500),
128
+ body: MotifBody,
129
+ motion: z.object({
130
+ preset: MotifMotionPreset,
131
+ enterMs: z.number().int().min(0).max(5e3).optional(),
132
+ exitMs: z.number().int().min(0).max(5e3).optional(),
133
+ staggerMs: z.number().int().min(0).max(2e3).optional(),
134
+ confidence: z.enum(["high", "low"]).optional()
135
+ }).optional(),
136
+ role: GraphicRole,
137
+ zone: z.object({ x: z.number(), y: z.number(), w: z.number(), h: z.number() }).optional(),
138
+ evidence: z.array(z.number().int().min(0)).max(24).optional(),
139
+ diffScore: z.number().min(0).max(1).optional(),
140
+ coverage: z.enum(["reproduced", "approximated", "skipped"]),
141
+ skipReason: z.string().max(300).optional()
142
+ });
143
+ var AccessControl = z.object({
144
+ public: z.boolean().optional(),
145
+ readerOrgIds: z.array(z.string()).optional(),
146
+ writerOrgIds: z.array(z.string()).optional()
147
+ });
148
+ var LicenseDeclaration = z.object({
149
+ type: z.string().min(1),
150
+ source: z.string().optional(),
151
+ notes: z.string().optional()
152
+ });
153
+ var BrandKit = z.object({
154
+ id: z.string().min(1),
155
+ name: z.string().min(1),
156
+ colors: Colors,
157
+ fonts: Fonts,
158
+ syncCaptions: z.boolean().optional(),
159
+ spacing: Spacing.optional(),
160
+ sizing: Sizing.optional(),
161
+ motion: Motion.optional(),
162
+ captionProfile: z.object({
163
+ color: z.string().optional(),
164
+ highlightColor: z.string().optional(),
165
+ emphasisColor: z.string().optional(),
166
+ plate: z.enum(["glass", "solid", "none"]).optional(),
167
+ entrance: z.enum(["none", "fade", "word-pop", "stagger-up"]).optional(),
168
+ entranceMs: z.number().optional(),
169
+ entranceStaggerMs: z.number().optional(),
170
+ exitMs: z.number().optional(),
171
+ fontFamily: z.string().optional(),
172
+ // The ordinary run's weight. The profile could name a caption family but never how heavy it
173
+ // was drawn, so the only answer to "that weight has no file behind it" was to change the
174
+ // family. A family that does not declare this weight is still refused here — but now at a
175
+ // field the author can set, and a family that ships it needs no setting at all: every
176
+ // bundled family declares a base and an emphasis weight it has bytes for.
177
+ fontWeight: z.number().int().min(100).max(900).multipleOf(100).optional(),
178
+ emphasisFontFamily: z.string().optional(),
179
+ emphasisScale: z.number().positive().optional(),
180
+ // A CSS weight, and the profile is projected straight onto the caption style, so it is held
181
+ // to the same range the composition holds `emphasisFontWeight` to. An unbounded number here
182
+ // was a hole: 0 or 1500 named no face at all.
183
+ emphasisFontWeight: z.number().int().min(100).max(900).optional(),
184
+ emphasisFontStyle: z.enum(["normal", "italic"]).optional(),
185
+ emphasisTextTransform: z.enum(["none", "uppercase"]).optional(),
186
+ emphasisLetterSpacing: z.number().optional(),
187
+ emphasisPlate: z.boolean().optional(),
188
+ emphasisPlateColor: z.string().optional(),
189
+ emphasisPlateTextColor: z.string().optional()
190
+ }).optional(),
191
+ exemplarKeys: z.array(z.string().min(1)).max(24).optional(),
192
+ contactSheetKey: z.string().min(1).optional(),
193
+ motifs: z.array(Motif).max(24).optional(),
194
+ styleRubric: z.string().max(4e3).optional(),
195
+ density: z.object({
196
+ graphicsPerMin: z.number().min(0).max(60),
197
+ maxSimultaneous: z.number().int().min(1).max(6)
198
+ }).optional(),
199
+ inspiration: z.object({
200
+ sources: z.array(z.string()).min(1).max(24),
201
+ extractedAt: z.number(),
202
+ extractionVersion: z.string().min(1).max(60)
203
+ }).optional(),
204
+ logo: Logo.optional(),
205
+ intro: IntroOutro.optional(),
206
+ outro: IntroOutro.optional(),
207
+ watermark: Watermark.optional(),
208
+ audio: z.object({
209
+ sfxIntensity: z.enum(["off", "light", "standard", "dense"]).optional(),
210
+ musicEnabled: z.boolean().optional(),
211
+ musicMood: z.string().max(300).optional(),
212
+ sonicLogo: z.object({
213
+ ref: AudioPaletteRef,
214
+ placement: z.enum(["intro", "outro", "both"]),
215
+ offsetMs: z.number().int().min(0).max(1e4).optional()
216
+ }).optional(),
217
+ uiSoundSet: z.object({
218
+ tick: AudioPaletteRef.optional(),
219
+ pop: AudioPaletteRef.optional(),
220
+ impact: AudioPaletteRef.optional(),
221
+ confirm: AudioPaletteRef.optional(),
222
+ error: AudioPaletteRef.optional(),
223
+ whoosh: AudioPaletteRef.optional(),
224
+ riser: AudioPaletteRef.optional()
225
+ }).optional(),
226
+ bedStyle: z.object({
227
+ bedId: AudioPaletteRef.optional(),
228
+ genre: z.string().max(120).optional(),
229
+ bpmRange: z.object({ min: z.number(), max: z.number() }).optional()
230
+ }).optional(),
231
+ ambiencePreference: z.object({ ref: AudioPaletteRef }).nullable().optional()
232
+ }).optional(),
233
+ license: LicenseDeclaration.optional(),
234
+ licenseVerification: z.enum([
235
+ "attested",
236
+ "cueframe-verified",
237
+ "third-party-verified"
238
+ ]).optional(),
239
+ ownerOrgId: z.string().optional(),
240
+ acl: AccessControl.optional()
241
+ }).superRefine((kit, ctx) => {
242
+ for (const issue of brandKitCaptionFaceIssues(kit)) {
243
+ ctx.addIssue({ code: "custom", path: issue.path.split("."), message: issue.message });
244
+ }
245
+ });
246
+
247
+ export {
248
+ EasingValue,
249
+ AudioPaletteRef,
250
+ FontRef,
251
+ MotifMotionPreset,
252
+ Motif,
253
+ BrandKit
254
+ };
@@ -0,0 +1,366 @@
1
+ import {
2
+ BrandKitBodySchema
3
+ } from "./chunk-TR53N6HB.js";
4
+ import {
5
+ ComponentSourceV2
6
+ } from "./chunk-EC6VFQE6.js";
7
+ import {
8
+ Composition
9
+ } from "./chunk-UFD4EZHM.js";
10
+ import {
11
+ SceneShotSpecV2,
12
+ sceneShotImageSlots,
13
+ sceneShotVideoSlots
14
+ } from "./chunk-CL5Y6V6K.js";
15
+
16
+ // ../execution/component-runtime/src/template/schema.ts
17
+ import { z } from "zod";
18
+ var TEMPLATE_MAX_BYTES = 1048576;
19
+ var MATTE_KEY = /^[a-f0-9]{12}$/;
20
+ var name = z.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/);
21
+ var slug = z.string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, "slug must be kebab-case").max(80);
22
+ var preparedMatte = z.object({
23
+ url: z.string().url(),
24
+ sha256: z.string().regex(/^[a-f0-9]{64}$/),
25
+ mimeType: z.literal("video/mp4"),
26
+ bakeVersion: z.string().min(1),
27
+ presenceFraction: z.number().min(0).max(1).optional()
28
+ }).strict();
29
+ var base = { name, required: z.boolean(), label: z.string().max(120).optional() };
30
+ var TemplateParameterSchema = z.discriminatedUnion("type", [
31
+ z.object({ ...base, type: z.literal("string"), default: z.string().optional(), maxLength: z.number().int().positive().optional() }).strict(),
32
+ z.object({ ...base, type: z.literal("number"), default: z.number().optional(), min: z.number().optional(), max: z.number().optional() }).strict(),
33
+ z.object({ ...base, type: z.literal("color"), default: z.string().optional() }).strict(),
34
+ z.object({ ...base, type: z.literal("url"), default: z.string().optional() }).strict(),
35
+ z.object({ ...base, type: z.literal("enum"), options: z.array(z.string().min(1)).min(1), default: z.string().optional() }).strict().superRefine((p, ctx) => {
36
+ if (p.default !== void 0 && !p.options.includes(p.default)) {
37
+ ctx.addIssue({ code: "custom", path: ["default"], message: `${p.name}: enum default must be one of options` });
38
+ }
39
+ }),
40
+ z.object({ ...base, type: z.literal("boolean"), default: z.boolean().optional() }).strict(),
41
+ z.object({
42
+ ...base,
43
+ type: z.literal("media"),
44
+ kind: z.enum(["video", "image", "audio"]),
45
+ default: z.string().optional(),
46
+ prepared: z.object({ mattes: z.record(z.string().regex(MATTE_KEY, "mattes keys are 12 lowercase hex chars"), preparedMatte).optional() }).strict().optional(),
47
+ sample: z.object({
48
+ url: z.string().url().startsWith("https://"),
49
+ sha256: z.string().regex(/^[a-f0-9]{64}$/, "sample sha256 is 64 hex chars"),
50
+ mimeType: z.string().min(1),
51
+ durationSec: z.number().positive().optional()
52
+ }).strict().optional()
53
+ }).strict()
54
+ ]);
55
+ var PRESET_NAME = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
56
+ function coerceParameterValue(param, raw) {
57
+ switch (param.type) {
58
+ case "string": {
59
+ if (typeof raw !== "string") return { ok: false, reason: "expected a string" };
60
+ if (param.maxLength !== void 0 && raw.length > param.maxLength) return { ok: false, reason: `longer than ${param.maxLength}` };
61
+ return { ok: true, value: raw };
62
+ }
63
+ case "number": {
64
+ const n = typeof raw === "string" && raw.trim() !== "" ? Number(raw) : raw;
65
+ if (typeof n !== "number" || Number.isNaN(n)) return { ok: false, reason: "expected a number" };
66
+ if (param.min !== void 0 && n < param.min) return { ok: false, reason: `below ${param.min}` };
67
+ if (param.max !== void 0 && n > param.max) return { ok: false, reason: `above ${param.max}` };
68
+ return { ok: true, value: n };
69
+ }
70
+ case "color":
71
+ return typeof raw === "string" && /^#[0-9a-fA-F]{6}([0-9a-fA-F]{2})?$/.test(raw) ? { ok: true, value: raw } : { ok: false, reason: "expected #rrggbb" };
72
+ case "url": {
73
+ try {
74
+ const { protocol } = new URL(String(raw));
75
+ if (protocol === "https:" || protocol === "http:") return { ok: true, value: String(raw) };
76
+ } catch {
77
+ }
78
+ return { ok: false, reason: "expected an http(s) URL" };
79
+ }
80
+ case "enum":
81
+ return typeof raw === "string" && param.options.includes(raw) ? { ok: true, value: raw } : { ok: false, reason: `expected one of ${param.options.join(", ")}` };
82
+ case "boolean": {
83
+ if (typeof raw === "boolean") return { ok: true, value: raw };
84
+ if (raw === "true" || raw === "false") return { ok: true, value: raw === "true" };
85
+ return { ok: false, reason: "expected true or false" };
86
+ }
87
+ case "media":
88
+ return typeof raw === "string" && raw.length > 0 ? { ok: true, value: raw } : { ok: false, reason: "expected a media id" };
89
+ }
90
+ }
91
+ var TemplateBrandKitSchema = BrandKitBodySchema.superRefine((kit, ctx) => {
92
+ const sources = { logo: kit.logo?.src, watermark: kit.watermark?.src, intro: kit.intro?.src, outro: kit.outro?.src };
93
+ for (const [field, src] of Object.entries(sources)) {
94
+ if (src !== void 0 && !src.startsWith("https://")) {
95
+ ctx.addIssue({ code: "custom", path: [field, "src"], message: `${field}.src must be an https URL; a template kit never names a storage key` });
96
+ }
97
+ }
98
+ });
99
+ var TemplatePresetSchema = z.object({
100
+ label: z.string().min(1).max(120),
101
+ values: z.record(z.string(), z.unknown()),
102
+ brandKit: TemplateBrandKitSchema.optional(),
103
+ gallery: z.object({ poster: z.string().url().optional(), render: z.string().url().optional(), description: z.string().max(2e3).optional() }).strict().optional()
104
+ }).strict();
105
+ var TemplateComponentSchema = z.object({
106
+ source: ComponentSourceV2,
107
+ name: z.string().min(1).max(120),
108
+ description: z.string().min(1).max(2e3),
109
+ category: z.enum(["text", "effects", "transitions", "layouts", "backgrounds", "ui-blocks", "scenes"]),
110
+ propSchema: z.record(z.string(), z.unknown())
111
+ }).strict();
112
+ var TemplateDocumentSchema = z.object({
113
+ v: z.literal(1),
114
+ slug,
115
+ name: z.string().min(1).max(120),
116
+ description: z.string().max(2e3).optional(),
117
+ composition: Composition,
118
+ parameters: z.array(TemplateParameterSchema).superRefine((ps, ctx) => {
119
+ const seen = /* @__PURE__ */ new Set();
120
+ ps.forEach((p, i) => {
121
+ const issue = (message) => ctx.addIssue({ code: "custom", path: [i], message: `${p.name}: ${message}` });
122
+ if (seen.has(p.name)) issue("duplicate parameter");
123
+ seen.add(p.name);
124
+ if (p.type === "number" && p.min !== void 0 && p.max !== void 0 && p.min > p.max) issue("min is above max");
125
+ if (p.type === "enum") for (const o of new Set(p.options.filter((o2, j) => p.options.indexOf(o2) !== j))) issue(`duplicate option ${o}`);
126
+ if (p.default !== void 0 && p.type !== "media" && p.type !== "enum") {
127
+ const r = coerceParameterValue(p, p.default);
128
+ if (!r.ok) issue(`default ${r.reason}`);
129
+ }
130
+ });
131
+ }),
132
+ components: z.record(z.string(), TemplateComponentSchema),
133
+ brandKit: TemplateBrandKitSchema.optional(),
134
+ rubric: z.string().optional(),
135
+ provenance: z.object({ label: z.string(), href: z.string().url().optional(), notes: z.string().optional() }).strict().optional(),
136
+ gallery: z.object({
137
+ status: z.enum(["published", "draft"]),
138
+ category: z.string().optional(),
139
+ format: z.string().optional(),
140
+ duration: z.string().optional(),
141
+ render: z.string().url().optional(),
142
+ poster: z.string().url().optional(),
143
+ prompt: z.string().optional(),
144
+ details: z.array(z.string()).optional(),
145
+ transcriptExcerpt: z.string().nullable().optional()
146
+ }).strict().optional(),
147
+ presets: z.record(z.string().regex(PRESET_NAME, "preset names are kebab-case"), TemplatePresetSchema).optional()
148
+ }).strict().superRefine((doc, ctx) => {
149
+ const byName = new Map(doc.parameters.map((p) => [p.name, p]));
150
+ for (const [presetName, preset] of Object.entries(doc.presets ?? {})) {
151
+ for (const [key, raw] of Object.entries(preset.values)) {
152
+ const param = byName.get(key);
153
+ if (!param) {
154
+ ctx.addIssue({ code: "custom", path: ["presets", presetName, "values", key], message: `preset ${presetName} names undeclared parameter ${key}` });
155
+ continue;
156
+ }
157
+ if (param.type === "media") {
158
+ ctx.addIssue({ code: "custom", path: ["presets", presetName, "values", key], message: `preset ${presetName}: ${key} is a media parameter; presets never carry media` });
159
+ continue;
160
+ }
161
+ const r = coerceParameterValue(param, raw);
162
+ if (!r.ok) ctx.addIssue({ code: "custom", path: ["presets", presetName, "values", key], message: `preset ${presetName}: ${key} ${r.reason}` });
163
+ }
164
+ }
165
+ });
166
+ var TemplateError = class extends Error {
167
+ constructor(code, message, fields = []) {
168
+ super(message);
169
+ this.code = code;
170
+ this.fields = fields;
171
+ }
172
+ code;
173
+ fields;
174
+ };
175
+ function parseTemplateDocument(raw) {
176
+ const bytes = new TextEncoder().encode(JSON.stringify(raw)).byteLength;
177
+ if (bytes > TEMPLATE_MAX_BYTES) throw new TemplateError("payload_too_large", `template document exceeds 1 MiB (${bytes} bytes)`);
178
+ const r = TemplateDocumentSchema.safeParse(raw);
179
+ if (!r.success) throw new TemplateError("template_values_invalid", r.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; "));
180
+ return r.data;
181
+ }
182
+
183
+ // ../execution/component-runtime/src/template/bind.ts
184
+ var REF = /^\{\{([A-Za-z_][A-Za-z0-9_]*)\}\}$/;
185
+ var INLINE = /\\\{\{|\{\{([A-Za-z_][A-Za-z0-9_]*)\}\}/g;
186
+ var STRING_LEAF_ROOTS = ["source.props", "source.params", "source.html", "source.tokens", "source.mediaId", "source.assets", "metadata", "text"];
187
+ var TYPED_VALUE_SITE = /^(tracks\.\d+\.contents\.\d+\.(source\.props|source\.params|metadata)|markers\.\d+\.metadata)(\.|$)/;
188
+ var HTML_SITE = /^tracks\.\d+\.contents\.\d+\.source\.html$/;
189
+ var MEDIA_SITE = /^tracks\.\d+\.contents\.\d+\.source\.(mediaId|assets\.[^.]+)$/;
190
+ var OMIT = /* @__PURE__ */ Symbol("omit");
191
+ function referenceAllowed(path) {
192
+ const p = path.join(".");
193
+ if (/^captions\./.test(p) && /\.text$/.test(p)) return true;
194
+ if (/^markers\.\d+\.metadata\b/.test(p)) return true;
195
+ const m = p.match(/^tracks\.\d+\.contents\.\d+\.(.*)$/);
196
+ return !!m && STRING_LEAF_ROOTS.some((root) => m[1] === root || m[1].startsWith(root + "."));
197
+ }
198
+ function findReferences(doc) {
199
+ const out = [];
200
+ const walk = (v, path) => {
201
+ if (typeof v === "string") {
202
+ const whole = v.match(REF);
203
+ if (whole) {
204
+ out.push({ path, name: whole[1], wholeLeaf: true });
205
+ return;
206
+ }
207
+ for (const m of v.matchAll(INLINE)) if (m[1] !== void 0) out.push({ path, name: m[1], wholeLeaf: false });
208
+ } else if (Array.isArray(v)) v.forEach((x, i) => walk(x, [...path, String(i)]));
209
+ else if (v && typeof v === "object") for (const [k, x] of Object.entries(v)) walk(x, [...path, k]);
210
+ };
211
+ walk(doc.composition, []);
212
+ return out;
213
+ }
214
+ function slotKind(doc, clip, slot) {
215
+ const component = doc.components[clip.source?.componentId];
216
+ if (component) return { kind: component.source.manifest.assets[slot]?.kind };
217
+ const scene = clip.source?.props?.scene;
218
+ if (scene === void 0) return {};
219
+ const parsed = SceneShotSpecV2.safeParse(scene);
220
+ if (!parsed.success) return { error: `clip ${clip.id} props.scene is not a scene-shot spec` };
221
+ return { kind: sceneShotImageSlots(parsed.data).includes(slot) ? "image" : sceneShotVideoSlots(parsed.data).includes(slot) ? "video" : void 0 };
222
+ }
223
+ function checkReferences(doc) {
224
+ const declared = new Map(doc.parameters.map((p) => [p.name, p]));
225
+ const refs = findReferences(doc);
226
+ const used = new Set(refs.map((r) => r.name));
227
+ const mediaSiteErrors = [];
228
+ for (const r of refs) {
229
+ const p = declared.get(r.name);
230
+ if (!p) continue;
231
+ const at = r.path.join(".");
232
+ const site = at.match(MEDIA_SITE);
233
+ if (p.type !== "media") {
234
+ if (site) mediaSiteErrors.push(`${r.name}: ${at} takes a media parameter`);
235
+ continue;
236
+ }
237
+ if (!site || !r.wholeLeaf) {
238
+ mediaSiteErrors.push(`${r.name}: media parameters bind only as a whole-leaf mediaId or asset slot`);
239
+ continue;
240
+ }
241
+ const clip = doc.composition.tracks[Number(r.path[1])].contents[Number(r.path[3])];
242
+ if (site[1] === "mediaId") {
243
+ if (!p.required) mediaSiteErrors.push(`${r.name}: a clip's mediaId needs a required media parameter`);
244
+ else if (clip.kind !== p.kind) mediaSiteErrors.push(`${r.name}: clip ${clip.id} is ${clip.kind}, parameter is ${p.kind}`);
245
+ continue;
246
+ }
247
+ const slot = r.path[r.path.length - 1];
248
+ const k = slotKind(doc, clip, slot);
249
+ if (k.error) mediaSiteErrors.push(`${r.name}: ${k.error}`);
250
+ else if (k.kind !== p.kind) mediaSiteErrors.push(`${r.name}: slot ${slot} of ${clip.source.componentId} is ${k.kind ?? "undeclared"}, parameter is ${p.kind}`);
251
+ }
252
+ return {
253
+ undeclared: [...new Set(refs.filter((r) => !declared.has(r.name)).map((r) => r.name))],
254
+ unused: doc.parameters.filter((p) => !used.has(p.name)).map((p) => p.name),
255
+ misplaced: refs.filter((r) => !referenceAllowed(r.path)).map((r) => r.path.join(".")),
256
+ mediaSiteErrors
257
+ };
258
+ }
259
+ var escapeHtml = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
260
+ function bindTemplate(doc, values, media, opts = {}) {
261
+ const { undeclared, misplaced, mediaSiteErrors } = checkReferences(doc);
262
+ if (undeclared.length) throw new TemplateError("template_parameter_undeclared", `undeclared: ${undeclared.join(", ")}`);
263
+ if (misplaced.length || mediaSiteErrors.length) throw new TemplateError("template_values_invalid", [...misplaced.map((m) => `reference outside a string leaf: ${m}`), ...mediaSiteErrors].join("; "));
264
+ const byName = new Map(doc.parameters.map((p) => [p.name, p]));
265
+ const stray = [
266
+ ...Object.keys(values).filter((k) => byName.get(k) === void 0 || byName.get(k).type === "media"),
267
+ ...Object.keys(media).filter((k) => byName.get(k)?.type !== "media")
268
+ ];
269
+ if (stray.length) throw new TemplateError("template_parameter_undeclared", `not declared by this template: ${stray.join(", ")}`, stray.map((param) => ({ param, reason: "not declared" })));
270
+ let base2 = {};
271
+ if (opts.preset !== void 0) {
272
+ const preset = doc.presets?.[opts.preset];
273
+ if (!preset) throw new TemplateError("template_values_invalid", `unknown preset ${opts.preset}`, [{ param: "preset", reason: `unknown preset ${opts.preset}` }]);
274
+ base2 = preset.values;
275
+ }
276
+ const merged = { ...base2, ...values };
277
+ const fields = [];
278
+ const resolved = /* @__PURE__ */ new Map();
279
+ const swapped = /* @__PURE__ */ new Map();
280
+ for (const p of doc.parameters) {
281
+ if (p.type === "media") {
282
+ const m = media[p.name];
283
+ if (!m) {
284
+ if (p.required) fields.push({ param: p.name, reason: "required" });
285
+ continue;
286
+ }
287
+ if (m.kind !== p.kind) throw new TemplateError("template_media_kind_mismatch", `${p.name}: expected ${p.kind}, got ${m.kind}`, [{ param: p.name, reason: `expected ${p.kind}` }]);
288
+ resolved.set(p.name, m.mediaId);
289
+ swapped.set(m.mediaId, m);
290
+ continue;
291
+ }
292
+ const raw = merged[p.name];
293
+ if (raw === void 0) {
294
+ if (p.default !== void 0) resolved.set(p.name, p.default);
295
+ else if (p.required) fields.push({ param: p.name, reason: "required" });
296
+ continue;
297
+ }
298
+ const r = coerceParameterValue(p, raw);
299
+ if (r.ok) resolved.set(p.name, r.value);
300
+ else fields.push({ param: p.name, reason: r.reason });
301
+ }
302
+ for (const track of doc.composition.tracks) for (const clip of track.contents) {
303
+ const src = clip.source;
304
+ const ref = typeof src?.mediaId === "string" ? src.mediaId.match(REF)?.[1] : void 0;
305
+ const m = ref ? media[ref] : void 0;
306
+ if (m?.durationSec === void 0 || m.kind === "image" || src.loop) continue;
307
+ const excluded = (src.excludedRanges ?? []).reduce((sum, r) => sum + Math.max(0, r.end - r.start), 0);
308
+ const needs = src.trim ? src.trim.end : clip.duration * (src.playbackRate ?? 1) + excluded;
309
+ if (needs <= m.durationSec) continue;
310
+ fields.push({ param: ref, reason: src.trim ? `media is ${m.durationSec}s, template trims to ${src.trim.end}s` : `media is ${m.durationSec}s, clip needs ${needs}s` });
311
+ }
312
+ if (fields.length) throw new TemplateError("template_values_invalid", fields.map((f) => `${f.param}: ${f.reason}`).join("; "), fields);
313
+ const sub = (v, path) => {
314
+ if (typeof v === "string") {
315
+ const at = path.join(".");
316
+ const text = (x) => HTML_SITE.test(at) ? escapeHtml(String(x)) : String(x);
317
+ const whole = v.match(REF);
318
+ if (whole) {
319
+ const value = resolved.get(whole[1]);
320
+ if (value === void 0) return OMIT;
321
+ return TYPED_VALUE_SITE.test(at) ? value : text(value);
322
+ }
323
+ return v.replace(INLINE, (_, name2) => name2 === void 0 ? "{{" : text(resolved.get(name2) ?? ""));
324
+ }
325
+ if (Array.isArray(v)) {
326
+ return v.map((x, i) => {
327
+ const out2 = sub(x, [...path, String(i)]);
328
+ if (out2 === OMIT) fields.push({ param: String(x).match(REF)[1], reason: "required: it fills an array element" });
329
+ return out2;
330
+ });
331
+ }
332
+ if (v && typeof v === "object") {
333
+ return Object.fromEntries(Object.entries(v).map(([k, x]) => [k, sub(x, [...path, k])]).filter(([, x]) => x !== OMIT));
334
+ }
335
+ return v;
336
+ };
337
+ const out = sub(doc.composition, []);
338
+ if (fields.length) throw new TemplateError("template_values_invalid", fields.map((f) => `${f.param}: ${f.reason}`).join("; "), fields);
339
+ for (const track of out.tracks) for (const clip of track.contents) {
340
+ if (swapped.get(clip.source?.mediaId)?.kind === "image") {
341
+ delete clip.source.trim;
342
+ delete clip.source.excludedRanges;
343
+ delete clip.source.playbackRate;
344
+ delete clip.source.reframe;
345
+ }
346
+ }
347
+ const valid = Composition.safeParse(out);
348
+ if (!valid.success) {
349
+ throw new TemplateError("template_values_invalid", `bound composition is invalid: ${valid.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")}`);
350
+ }
351
+ return out;
352
+ }
353
+
354
+ export {
355
+ TEMPLATE_MAX_BYTES,
356
+ MATTE_KEY,
357
+ TemplateParameterSchema,
358
+ PRESET_NAME,
359
+ coerceParameterValue,
360
+ TemplateDocumentSchema,
361
+ TemplateError,
362
+ parseTemplateDocument,
363
+ findReferences,
364
+ checkReferences,
365
+ bindTemplate
366
+ };