rvlib-shipkit 2.1.1 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +50 -0
- package/package.json +2 -1
- package/src/agent-time/mask.ts +43 -0
- package/src/agent-time/render.ts +187 -0
- package/src/agent-time/report.ts +335 -0
- package/src/agent-time/shape.ts +271 -0
- package/src/agent-time/signals.ts +84 -0
- package/src/agent-time/store.ts +379 -0
- package/src/agent-time/timeline.ts +218 -0
- package/src/agent-time/turns.ts +135 -0
- package/src/assets/kits/identity-kit.ts +7 -3
- package/src/assets/run-assets.ts +18 -4
- package/src/checks/SK004-gitignore-policy.check.ts +11 -6
- package/src/checks/SK013-one-way-in-policy.check.ts +13 -8
- package/src/cli/cmd_Calls.ts +53 -0
- package/src/cli/cmd_Commands.ts +163 -43
- package/src/cli/cmd_Review.ts +948 -0
- package/src/cli/cmd_Shot.ts +63 -0
- package/src/cli/cmd_Start.ts +8 -4
- package/src/cli/cmd_Time.ts +112 -0
- package/src/cli/cmd_Trailer.ts +361 -0
- package/src/cli/kit/builtins.ts +40 -0
- package/src/cli/kit/calls.ts +177 -0
- package/src/cli/kit/families.ts +67 -0
- package/src/cli/kit/index.ts +8 -0
- package/src/cli/kit/pages-check.ts +36 -0
- package/src/cli/kit/project-cli.ts +73 -0
- package/src/cli/kit/verb.ts +167 -0
- package/src/cli.ts +15 -2
- package/src/collections/collection-types.ts +6 -1
- package/src/collections/providers.ts +3 -0
- package/src/collections/review.ts +70 -0
- package/src/core/layout.ts +12 -2
- package/src/core/migrate-layout.ts +31 -8
- package/src/core/one-way-in-policy.ts +12 -4
- package/src/reflect/prompt-corpus.ts +15 -114
- package/src/reflect/transcript.ts +254 -0
- package/src/review/anim.ts +228 -0
- package/src/review/auto.ts +141 -0
- package/src/review/format.ts +150 -0
- package/src/review/http.ts +23 -0
- package/src/review/marks.ts +43 -0
- package/src/review/media.ts +45 -0
- package/src/review/model.ts +854 -0
- package/src/review/project.ts +110 -0
- package/src/review/role.ts +48 -0
- package/src/review/routes.ts +228 -0
- package/src/review/store.ts +1002 -0
- package/src/review/threads.ts +237 -0
- package/src/review/trailer-desk.ts +385 -0
- package/src/review/trailer-model.ts +183 -0
- package/src/review/trailer-routes.ts +136 -0
- package/src/review/unit.ts +165 -0
- package/src/review/watchers.ts +54 -0
- package/src/scaffold/cli-skeleton.ts +149 -0
- package/src/scaffold/files/agent-index.md +2 -2
- package/src/scaffold/files/tsconfig.json +3 -0
- package/src/scaffold/files/verify-skill.md +1 -1
- package/src/scaffold/project-scaffold.ts +90 -12
- package/src/serve/embed.ts +35 -0
- package/src/serve/hub-app/activity-view.tsx +1 -1
- package/src/serve/hub-app/activity.css +0 -4
- package/src/serve/hub-app/dock-view.tsx +5 -4
- package/src/serve/hub-app/hash-route.ts +28 -1
- package/src/serve/hub-app/header-bar.tsx +3 -1
- package/src/serve/hub-app/home-st.ts +10 -2
- package/src/serve/hub-app/home-strip.tsx +3 -2
- package/src/serve/hub-app/home.html +1 -0
- package/src/serve/hub-app/hub-app.tsx +80 -11
- package/src/serve/hub-app/hub.css +45 -13
- package/src/serve/hub-app/icons.tsx +9 -0
- package/src/serve/hub-app/index.html +1 -0
- package/src/serve/hub-app/insights-view.tsx +4 -3
- package/src/serve/hub-app/keys.ts +14 -10
- package/src/serve/hub-app/layout-st.ts +63 -2
- package/src/serve/hub-app/planned-view.tsx +3 -2
- package/src/serve/hub-app/planned.css +0 -1
- package/src/serve/hub-app/project-list.css +22 -4
- package/src/serve/hub-app/project-list.tsx +41 -8
- package/src/serve/hub-app/projects-svc.ts +4 -0
- package/src/serve/hub-app/repo-sections.ts +15 -3
- package/src/serve/hub-app/repo-st.ts +45 -3
- package/src/serve/hub-app/review/clip-st.ts +208 -0
- package/src/serve/hub-app/review/kind-view.tsx +171 -0
- package/src/serve/hub-app/review/kit.tsx +113 -0
- package/src/serve/hub-app/review/review-answer.tsx +239 -0
- package/src/serve/hub-app/review/review-board.tsx +46 -0
- package/src/serve/hub-app/review/review-list.tsx +156 -0
- package/src/serve/hub-app/review/review-page.tsx +101 -0
- package/src/serve/hub-app/review/review-st.ts +779 -0
- package/src/serve/hub-app/review/review-views.tsx +503 -0
- package/src/serve/hub-app/review/trailer-page.tsx +416 -0
- package/src/serve/hub-app/review/trailer-st.ts +602 -0
- package/src/serve/hub-app/review/trailer-timeline.tsx +207 -0
- package/src/serve/hub-app/review.css +329 -0
- package/src/serve/hub-app/theme-st.ts +56 -0
- package/src/serve/hub-app/tree-build.ts +4 -0
- package/src/serve/hub-app/tree-row.tsx +1 -1
- package/src/serve/hub-app/tree-svc.ts +4 -0
- package/src/serve/hub-runtime.ts +5 -3
- package/src/serve/same-origin.ts +5 -1
- package/src/serve/serve-hub.ts +8 -0
- package/src/shipkit-files.ts +12 -4
- package/src/shot/client.ts +67 -0
- package/src/shot/clip-encoder.ts +146 -0
- package/src/shot/dom-clip.ts +124 -0
- package/src/shot/serve-shot.ts +54 -0
- package/src/shot/shooter.ts +343 -0
- package/src/shot/shot-ask.ts +189 -0
- package/src/shot/shot.shipkit.ts +14 -0
- package/src/smoke/run-smoke.ts +2 -2
- package/src/start-repo.ts +29 -14
- package/src/suite/judge-suites.ts +56 -10
- package/src/suite/select.ts +2 -5
- package/src/suite/suite-cli.ts +3 -2
- package/src/trailer/adapter.ts +78 -0
- package/src/trailer/build.ts +603 -0
- package/src/trailer/ffmpeg.ts +72 -0
- package/src/trailer/footage.ts +173 -0
- package/src/trailer/grammar.ts +1290 -0
- package/src/trailer/graph.ts +508 -0
- package/src/trailer/holes.ts +72 -0
- package/src/trailer/load-unit.ts +31 -0
- package/src/trailer/look.ts +124 -0
- package/src/trailer/passage.ts +129 -0
- package/src/trailer/plan.ts +192 -0
- package/src/trailer/prune.ts +193 -0
- package/src/trailer/score/compose.ts +240 -0
- package/src/trailer/score/dsp.ts +272 -0
- package/src/trailer/score/palette.ts +188 -0
- package/src/trailer/score/pattern.ts +91 -0
- package/src/trailer/score/score.ts +214 -0
- package/src/trailer/score/sound.ts +112 -0
- package/src/trailer/score/synth.ts +361 -0
- package/src/trailer/states.ts +76 -0
- package/src/trailer/stitch.ts +233 -0
- package/src/trailer/titles.ts +379 -0
- package/src/trailer/url.ts +108 -0
- package/src/trailer/vertical-draw.ts +286 -0
- package/src/trailer/vertical.ts +148 -0
- package/src/trailer/vocab.ts +59 -0
- package/src/trailer/words.ts +254 -0
|
@@ -335,6 +335,18 @@ export function aloneRunsNeeded(suspects: Timed[], parallelCpu: Record<string, n
|
|
|
335
335
|
* every (fail) line under no header of its own: it closes the last file */
|
|
336
336
|
export const BUN_RECAP = /^\s*\d+ tests? failed:\s*$/
|
|
337
337
|
|
|
338
|
+
/** the banner bun prints under a file header for an error thrown outside
|
|
339
|
+
* any test (a top level throw, a throw inside a describe callback), then a
|
|
340
|
+
* rule, the code frame, the error, the stack and a closing rule */
|
|
341
|
+
export const BETWEEN_TESTS = '# Unhandled error between tests'
|
|
342
|
+
export const BUN_RULE = /^-{20,}$/
|
|
343
|
+
/** the line that carries an error's message: bun's own `error:` and a named
|
|
344
|
+
* error class (`ReferenceError: x is not defined`) */
|
|
345
|
+
const ERROR_LINE = /^\s*(?:error|[A-Z]\w*(?:Error|Exception)): /
|
|
346
|
+
const BETWEEN_WORDS = 'an unhandled error between tests'
|
|
347
|
+
/** the line that opens a file in bun's report: its path and a colon */
|
|
348
|
+
const HEADER = /^(\S+\.(?:test|spec)\.[cm]?[jt]sx?):$/
|
|
349
|
+
|
|
338
350
|
/** the suites with a failing test, read off bun's report: a file that failed
|
|
339
351
|
* prints its path as a header line, then its `(fail)` lines under it. A
|
|
340
352
|
* passing file prints nothing at all in a piped run, which is why this reads
|
|
@@ -343,39 +355,73 @@ export function failedSuites(output: string): string[] {
|
|
|
343
355
|
const out = new Set<string>()
|
|
344
356
|
let cur: string | null = null
|
|
345
357
|
for (const line of output.split('\n')) {
|
|
346
|
-
const head = line.match(
|
|
358
|
+
const head = line.match(HEADER)
|
|
347
359
|
if (head != null) { cur = normSuitePath(head[1]!); continue }
|
|
348
360
|
if (BUN_RECAP.test(line)) { cur = null; continue }
|
|
349
361
|
// a file that cannot load prints no `(fail)` line, only this banner
|
|
350
|
-
if (cur != null && (/^\(fail\) /.test(line) || line.startsWith(
|
|
362
|
+
if (cur != null && (/^\(fail\) /.test(line) || line.startsWith(BETWEEN_TESTS))) out.add(cur)
|
|
351
363
|
}
|
|
352
364
|
return [...out]
|
|
353
365
|
}
|
|
354
366
|
|
|
367
|
+
/** the error line inside the banner block that starts at `from`, before its closing rule */
|
|
368
|
+
function blockError(lines: string[], from: number): string | null {
|
|
369
|
+
let rules = 0
|
|
370
|
+
for (let j = from; j < lines.length && rules < 2; j++) {
|
|
371
|
+
const line = lines[j]!.trim()
|
|
372
|
+
if (BUN_RULE.test(line)) { rules++; continue }
|
|
373
|
+
if (HEADER.test(line)) return null
|
|
374
|
+
if (ERROR_LINE.test(line)) return line
|
|
375
|
+
}
|
|
376
|
+
return null
|
|
377
|
+
}
|
|
378
|
+
|
|
355
379
|
/** the FIRST failure of each failing suite, read off the same report as
|
|
356
|
-
* failedSuites: its `(fail)` line, timing dropped, with the first
|
|
357
|
-
*
|
|
358
|
-
*
|
|
380
|
+
* failedSuites: its `(fail)` line, timing dropped, with the first error line
|
|
381
|
+
* bun printed before it in that file, or the error inside the banner of one
|
|
382
|
+
* that threw outside any test. What the record keeps so a page can say why a
|
|
383
|
+
* file is red without a rerun */
|
|
359
384
|
export function failLines(output: string): Map<string, string> {
|
|
360
385
|
const out = new Map<string, string>()
|
|
386
|
+
const lines = output.split('\n')
|
|
361
387
|
let cur: string | null = null
|
|
362
388
|
let err: string | null = null
|
|
363
|
-
for (
|
|
364
|
-
const line =
|
|
365
|
-
const head = line.match(
|
|
389
|
+
for (let i = 0; i < lines.length; i++) {
|
|
390
|
+
const line = lines[i]!.trimEnd()
|
|
391
|
+
const head = line.match(HEADER)
|
|
366
392
|
if (head != null) { cur = normSuitePath(head[1]!); err = null; continue }
|
|
367
393
|
if (BUN_RECAP.test(line)) { cur = null; continue }
|
|
368
394
|
if (cur == null || out.has(cur)) continue
|
|
369
|
-
if (err == null &&
|
|
395
|
+
if (err == null && ERROR_LINE.test(line)) err = line.trim()
|
|
370
396
|
const fail = line.match(/^\(fail\) (.*?)(?: \[[\d.]+m?s\])?$/)
|
|
371
397
|
if (fail != null) out.set(cur, clip(err == null ? fail[1]! : `${fail[1]!}: ${err}`))
|
|
372
|
-
else if (line.startsWith(
|
|
398
|
+
else if (line.startsWith(BETWEEN_TESTS)) {
|
|
399
|
+
const thrown = blockError(lines, i + 1) ?? err
|
|
400
|
+
out.set(cur, clip(thrown == null ? BETWEEN_WORDS : `${BETWEEN_WORDS}: ${thrown}`))
|
|
401
|
+
}
|
|
373
402
|
}
|
|
374
403
|
return out
|
|
375
404
|
}
|
|
376
405
|
|
|
377
406
|
const clip = (s: string): string => (s.length > 300 ? `${s.slice(0, 299)}…` : s)
|
|
378
407
|
|
|
408
|
+
/** the FAIL headline of a run bun ended red with no kill, read off bun's own
|
|
409
|
+
* summary (`N fail`, `N error`) and the report in `out`. bun can count an
|
|
410
|
+
* error thrown outside any test as `0 fail`, so the errors lead, each with
|
|
411
|
+
* the file that threw */
|
|
412
|
+
export function redWords(o: { failedTests: number; errors: number; ran: number; slowest: string; out: string }): string {
|
|
413
|
+
const thrown = [...failLines(o.out)].filter(([, w]) => w.startsWith(BETWEEN_WORDS))
|
|
414
|
+
const parts: string[] = []
|
|
415
|
+
if (o.errors > 0 || thrown.length > 0) {
|
|
416
|
+
const named = thrown.slice(0, 3).map(([file, w]) => `${file} threw ${w.slice(BETWEEN_WORDS.length + 2) || 'with no message'}`)
|
|
417
|
+
const more = thrown.length > 3 ? `; ${thrown.length - 3} more` : ''
|
|
418
|
+
parts.push(`${Math.max(o.errors, thrown.length)} error(s) outside any test ${named.length > 0 ? `(${named.join('; ')}${more})` : '(bun named no file)'}`)
|
|
419
|
+
}
|
|
420
|
+
if (o.failedTests > 0) parts.push(`${o.failedTests} test(s) failed`)
|
|
421
|
+
if (parts.length === 0) parts.push('bun test exited red with no failed test and no error counted')
|
|
422
|
+
return `${parts.join(', ')}, Ran ${o.ran} tests${o.slowest}`
|
|
423
|
+
}
|
|
424
|
+
|
|
379
425
|
/** the clause the headline carries for the gate line: the command phase one
|
|
380
426
|
* ran, and the flags the runner added to the script's own. Per file ignores
|
|
381
427
|
* stay out, the slow and serial counts already say what they leave out */
|
package/src/suite/select.ts
CHANGED
|
@@ -31,7 +31,7 @@ import { blobOid, listingHash } from '../core/read-set.ts'
|
|
|
31
31
|
import { shipkitRoot } from '../ledger/db.ts'
|
|
32
32
|
import { extractSpecs, resolveSpec } from '../ts/source-graph.ts'
|
|
33
33
|
import { type RvtConfig } from '../ts/config.ts'
|
|
34
|
-
import { inDotDir } from './judge-suites.ts'
|
|
34
|
+
import { BETWEEN_TESTS, BUN_RULE, inDotDir } from './judge-suites.ts'
|
|
35
35
|
|
|
36
36
|
export const SELECT_VERSION = 5
|
|
37
37
|
|
|
@@ -403,9 +403,6 @@ export function selectSuites(o: {
|
|
|
403
403
|
return { all: false, run, why: reasons, changed: o.changed.size }
|
|
404
404
|
}
|
|
405
405
|
|
|
406
|
-
const BETWEEN_TESTS = '# Unhandled error between tests'
|
|
407
|
-
const RULE = /^-{20,}$/
|
|
408
|
-
|
|
409
406
|
/** the repo modules named in the frames of every error bun printed between
|
|
410
407
|
* tests (a module whose top level threw), repo-relative to `top` */
|
|
411
408
|
export function loadErrorModules(out: string, top: string): string[] {
|
|
@@ -419,7 +416,7 @@ export function loadErrorModules(out: string, top: string): string[] {
|
|
|
419
416
|
let rules = 0
|
|
420
417
|
for (let j = i + 1; j < lines.length && rules < 2; j++) {
|
|
421
418
|
const line = lines[j]!
|
|
422
|
-
if (
|
|
419
|
+
if (BUN_RULE.test(line.trim())) { rules++; continue }
|
|
423
420
|
const m = /^\s+at .*?\(?((?:\/|[A-Za-z]:\\)[^():]+):\d+(?::\d+)?\)?\s*$/.exec(line)
|
|
424
421
|
if (m == null) continue
|
|
425
422
|
const base = roots.find((r) => m[1]!.startsWith(`${r}/`))
|
package/src/suite/suite-cli.ts
CHANGED
|
@@ -20,7 +20,7 @@ import { SUITE_READS_ENV, SUITE_ROOT_ENV, SUITE_SELECT_ENV } from './reads-env.t
|
|
|
20
20
|
import { changedKeys, dropStaticEdges, extras, isBunSuite, envFilesOf, foldReads, loadErrorModules, loadState, preloadsOf, saveState, selectStatePath, selectSuites, snapshot, suiteFiles, taintedSuites, walkGraph, type GraphEntry, type Selection, type SelectState } from './select.ts'
|
|
21
21
|
import { gitRoot } from '../core/git-root.ts'
|
|
22
22
|
import { loadConfig } from '../ts/config.ts'
|
|
23
|
-
import { aloneRunsNeeded, bunfigTest, type BunfigTest, readPhaseCpu, SUITE_CPU_ENV, type AloneRun, type Confirmed, confirmOver, crashedWorkers, interruptedSuites, overWords, ranAs, retryAlone, smallerRun, stallWords, DEFAULT_BUDGET_MS, FULL_CEILING_MS, failedSuites, failLines, judgeSuites, normSuitePath, parseSuiteFlags, planPhases, QUICK_CEILING_MS, poolWorkers } from './judge-suites.ts'
|
|
23
|
+
import { aloneRunsNeeded, bunfigTest, type BunfigTest, readPhaseCpu, SUITE_CPU_ENV, type AloneRun, type Confirmed, confirmOver, crashedWorkers, interruptedSuites, overWords, ranAs, retryAlone, smallerRun, stallWords, DEFAULT_BUDGET_MS, FULL_CEILING_MS, failedSuites, failLines, judgeSuites, normSuitePath, parseSuiteFlags, planPhases, QUICK_CEILING_MS, poolWorkers, redWords } from './judge-suites.ts'
|
|
24
24
|
|
|
25
25
|
type Phase = { code: number; signal: string | null; stalled: boolean; out: string; timings: Record<string, number>; cpuMs: number | null }
|
|
26
26
|
|
|
@@ -196,6 +196,7 @@ async function runSuites(argv: string[], ctx: SelectCtx): Promise<number> {
|
|
|
196
196
|
const phases = [p1, ...(p2 != null ? [p2] : [])]
|
|
197
197
|
const ran = phases.reduce((n, p) => n + lastCount(p.out, /Ran (\d+) tests?/g), 0)
|
|
198
198
|
const failedTests = phases.reduce((n, p) => n + lastCount(p.out, /^\s*(\d+) fail$/gm), 0)
|
|
199
|
+
const errors = phases.reduce((n, p) => n + lastCount(p.out, /^\s*(\d+) errors?$/gm), 0)
|
|
199
200
|
const timings = Object.assign({}, ...phases.map((p) => p.timings)) as Record<string, number>
|
|
200
201
|
const crashed = crashedWorkers(p1.out)
|
|
201
202
|
const red = new Set([...phases.flatMap((p) => failedSuites(p.out)), ...crashed.map((c) => c.file)])
|
|
@@ -290,7 +291,7 @@ async function runSuites(argv: string[], ctx: SelectCtx): Promise<number> {
|
|
|
290
291
|
...killed.map((k) => `FAIL: ${k}`),
|
|
291
292
|
...(vacuous ? ['FAIL: ran 0 tests: a suite with no test in it proves nothing, write a real one'] : []),
|
|
292
293
|
...(!vacuous && (failedTests > 0 || (phases.some((p) => p.code !== 0 && p.signal == null) && killed.length === 0))
|
|
293
|
-
? [`FAIL: ${
|
|
294
|
+
? [`FAIL: ${redWords({ failedTests, errors, ran, slowest, out: phases.map((p) => p.out).join('\n') })}`] : []),
|
|
294
295
|
...(over.length > 0
|
|
295
296
|
? [`FAIL: ${over.length} suite(s) over the ${budget}ms budget: ${over.map((c) => overWords(c, load, budget)).join(', ')} (run alone, load ${load.load1.toFixed(1)} on ${load.cores} cores before the run). Make each fast, or declare it in ${declaredIn}.slow with what it catches and why it cannot be fast`]
|
|
296
297
|
: []),
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// what a repo adds to the trailer core, all optional (wiki build/trailer.md, The trailer unit). Filming is never here:
|
|
2
|
+
// a film line calls the project CLI's film rows (vocab.ts). A `trailer({...})` unit in any `<name>.shipkit.ts`
|
|
3
|
+
// resolves the sounds that are not files, names the paths a take's world is, checks the content the language cannot
|
|
4
|
+
// know, refines one film line's take, and turns on the guards that read the repo's own words. Discovered like every
|
|
5
|
+
// unit file (load-unit.ts)
|
|
6
|
+
import type { TakeLength } from './grammar.ts'
|
|
7
|
+
|
|
8
|
+
export interface TrailerSounds {
|
|
9
|
+
/** a music id to an audio file of at least `seconds`, cached by the repo */
|
|
10
|
+
music?(id: string, o: { take?: string; seconds: number }): Promise<string>
|
|
11
|
+
/** an sfx id to an audio file */
|
|
12
|
+
sfx?(id: string): Promise<string>
|
|
13
|
+
/** a voice line, found by its text, to an audio file */
|
|
14
|
+
voice?(text: string): Promise<string>
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** a film line of the project CLI as the unit sees it: its words as written, the text of each `@<file>` word */
|
|
18
|
+
export interface TrailerFilmLine {
|
|
19
|
+
line: number
|
|
20
|
+
/** the shot or footage block it films */
|
|
21
|
+
shot: string
|
|
22
|
+
argv: string[]
|
|
23
|
+
output: 'still' | 'clip'
|
|
24
|
+
files: Record<string, string>
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** what `check` reads: the cut's film lines and the sounds it names by id */
|
|
28
|
+
export interface TrailerCheckInput {
|
|
29
|
+
file: string
|
|
30
|
+
dir: string
|
|
31
|
+
size: [number, number]
|
|
32
|
+
fps: number
|
|
33
|
+
films: TrailerFilmLine[]
|
|
34
|
+
/** each music cue that names an id; `opening`: the header's, the one the cut starts on */
|
|
35
|
+
music: { line: number; id: string; take?: string; from: number; opening: boolean }[]
|
|
36
|
+
sfx: { line: number; id: string }[]
|
|
37
|
+
voices: { line: number; text: string }[]
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** an error at its line; `warn`: said, never blocking */
|
|
41
|
+
export interface TrailerFinding {
|
|
42
|
+
line: number
|
|
43
|
+
message: string
|
|
44
|
+
warn?: true
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** one film line's take over its row's defaults: its version, how long it films, a fixed world, what its key reads */
|
|
48
|
+
export interface TakeAnswer {
|
|
49
|
+
version?: number | string
|
|
50
|
+
length?: TakeLength
|
|
51
|
+
/** the take's world for good (`rev:<commit>`): never stale */
|
|
52
|
+
world?: string
|
|
53
|
+
/** what the key reads instead of the words */
|
|
54
|
+
recipe?: unknown
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface TrailerDef {
|
|
58
|
+
sound?: TrailerSounds
|
|
59
|
+
/** the paths a take's world is (`['src', 'public']`); default the whole tree, trailer/ and .shipkit/ left out */
|
|
60
|
+
stamp?: string[]
|
|
61
|
+
/** in process, before filming, in a few ms: the content the language cannot know */
|
|
62
|
+
check?(c: TrailerCheckInput): TrailerFinding[] | Promise<TrailerFinding[]>
|
|
63
|
+
/** one film line's take, when its words change what its row says */
|
|
64
|
+
take?(f: TrailerFilmLine & { version: number | string | null; length: TakeLength }): TakeAnswer | undefined
|
|
65
|
+
/** the `states` a film prints: parts a cut may land on (`ignore`), parts a shot never opens or ends on (`underWay`) */
|
|
66
|
+
states?: { ignore?: string[]; underWay?: string[] }
|
|
67
|
+
/** a clip take that drops its picture for a few frames is filmed again once, then refused (holes.ts) */
|
|
68
|
+
holes?: boolean
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export type TrailerUnit = TrailerDef & { kind: 'trailer' }
|
|
72
|
+
|
|
73
|
+
export function trailer(d: TrailerDef): TrailerUnit {
|
|
74
|
+
for (const [k, f] of Object.entries(d.sound ?? {})) if (typeof f !== 'function') throw new Error(`trailer: sound.${k} is a function returning an audio file`)
|
|
75
|
+
for (const k of ['check', 'take'] as const) if (d[k] !== undefined && typeof d[k] !== 'function') throw new Error(`trailer: ${k} is a function`)
|
|
76
|
+
if (d.stamp !== undefined && (!Array.isArray(d.stamp) || d.stamp.some((p) => typeof p !== 'string' || p === '' || p.startsWith('/')))) throw new Error('trailer: stamp is a list of repo relative paths')
|
|
77
|
+
return { ...d, kind: 'trailer' }
|
|
78
|
+
}
|