@jokerized/decksmith 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +794 -0
- package/dist/cli.js +8906 -0
- package/dist/deck-runtime.js +55 -0
- package/dist/index.js +8590 -0
- package/dist/mcp.js +7809 -0
- package/dist/server/errors.js +73 -0
- package/dist/server/http.js +504 -0
- package/dist/server/main.js +91 -0
- package/dist/server/options.js +198 -0
- package/dist/server/pipeline.js +356 -0
- package/dist/server/queue.js +195 -0
- package/dist/server/ui.js +1614 -0
- package/dist/server/upload.js +232 -0
- package/dist/types/cli.d.ts +1 -0
- package/dist/types/deck/runtime.d.ts +59 -0
- package/dist/types/deck/subtitles.d.ts +101 -0
- package/dist/types/emit/archetypes/annotated-figure.d.ts +103 -0
- package/dist/types/emit/archetypes/bar-compare.d.ts +30 -0
- package/dist/types/emit/archetypes/callout.d.ts +7 -0
- package/dist/types/emit/archetypes/claim-figure.d.ts +9 -0
- package/dist/types/emit/archetypes/data-table.d.ts +21 -0
- package/dist/types/emit/archetypes/equation-walk.d.ts +2 -0
- package/dist/types/emit/archetypes/grid.d.ts +16 -0
- package/dist/types/emit/archetypes/index.d.ts +19 -0
- package/dist/types/emit/archetypes/line-chart.d.ts +22 -0
- package/dist/types/emit/archetypes/pipeline.d.ts +81 -0
- package/dist/types/emit/archetypes/split-compare.d.ts +2 -0
- package/dist/types/emit/archetypes/stack.d.ts +93 -0
- package/dist/types/emit/archetypes/title.d.ts +188 -0
- package/dist/types/emit/camera.d.ts +397 -0
- package/dist/types/emit/composition.d.ts +191 -0
- package/dist/types/emit/island.d.ts +19 -0
- package/dist/types/emit/kit.d.ts +256 -0
- package/dist/types/emit/svg.d.ts +177 -0
- package/dist/types/emit/theme.d.ts +65 -0
- package/dist/types/emit/themes/index.d.ts +40 -0
- package/dist/types/emit/themes/ink.d.ts +12 -0
- package/dist/types/emit/themes/mono.d.ts +20 -0
- package/dist/types/emit/themes/paper.d.ts +18 -0
- package/dist/types/index.d.ts +200 -0
- package/dist/types/mcp/main.d.ts +2 -0
- package/dist/types/mcp/prereqs.d.ts +22 -0
- package/dist/types/mcp/tools.d.ts +212 -0
- package/dist/types/narrate/narrate.d.ts +87 -0
- package/dist/types/narrate/tts.d.ts +134 -0
- package/dist/types/narrate/voices.d.ts +29 -0
- package/dist/types/pack/media.d.ts +61 -0
- package/dist/types/pack/pack.d.ts +16 -0
- package/dist/types/plan/codex.d.ts +24 -0
- package/dist/types/plan/duration.d.ts +394 -0
- package/dist/types/plan/prompt.d.ts +47 -0
- package/dist/types/plan/refs.d.ts +22 -0
- package/dist/types/plan/select.d.ts +116 -0
- package/dist/types/prefs.d.ts +43 -0
- package/dist/types/render/captions.d.ts +108 -0
- package/dist/types/render/ffmpeg.d.ts +129 -0
- package/dist/types/render/render.d.ts +123 -0
- package/dist/types/render/timing.d.ts +290 -0
- package/dist/types/server/errors.d.ts +11 -0
- package/dist/types/server/http.d.ts +57 -0
- package/dist/types/server/main.d.ts +1 -0
- package/dist/types/server/options.d.ts +90 -0
- package/dist/types/server/pipeline.d.ts +21 -0
- package/dist/types/server/queue.d.ts +105 -0
- package/dist/types/server/ui.d.ts +9 -0
- package/dist/types/server/upload.d.ts +107 -0
- package/dist/types/source/assets.d.ts +11 -0
- package/dist/types/source/fonts.d.ts +15 -0
- package/dist/types/source/markdown.d.ts +8 -0
- package/dist/types/types.d.ts +1992 -0
- package/dist/types/verify/budget.d.ts +41 -0
- package/dist/types/verify/check.d.ts +78 -0
- package/dist/types/verify/drift.d.ts +158 -0
- package/dist/types/verify/fidelity.d.ts +247 -0
- package/dist/types/verify/index.d.ts +207 -0
- package/dist/types/verify/overprint.d.ts +133 -0
- package/dist/types/verify/typefloor.d.ts +50 -0
- package/package.json +84 -0
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { explain } from "./errors.js";
|
|
2
|
+
class QueueFullError extends Error {
|
|
3
|
+
hint;
|
|
4
|
+
status = 503;
|
|
5
|
+
constructor(max) {
|
|
6
|
+
super(`The queue is full \u2014 ${max} ${max === 1 ? "job is" : "jobs are"} already waiting.`);
|
|
7
|
+
this.name = "QueueFullError";
|
|
8
|
+
this.hint = "Decks are made one at a time because a render is minutes of CPU and RAM. Try again in a few minutes.";
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
class Queue {
|
|
12
|
+
#jobs = /* @__PURE__ */ new Map();
|
|
13
|
+
#waiting = [];
|
|
14
|
+
#watchers = /* @__PURE__ */ new Map();
|
|
15
|
+
#maxQueued;
|
|
16
|
+
#ttlMs;
|
|
17
|
+
#maxLog;
|
|
18
|
+
#now;
|
|
19
|
+
#onExpire;
|
|
20
|
+
#running;
|
|
21
|
+
constructor(opts = {}) {
|
|
22
|
+
this.#maxQueued = opts.maxQueued ?? 8;
|
|
23
|
+
this.#ttlMs = opts.ttlMs ?? 2 * 60 * 6e4;
|
|
24
|
+
this.#maxLog = opts.maxLog ?? 400;
|
|
25
|
+
this.#now = opts.now ?? Date.now;
|
|
26
|
+
this.#onExpire = opts.onExpire ?? (() => {
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
get depth() {
|
|
30
|
+
return this.#waiting.length;
|
|
31
|
+
}
|
|
32
|
+
get running() {
|
|
33
|
+
return this.#running;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Take a job, or refuse. Refusing is the point: an unbounded queue on a
|
|
37
|
+
* one-at-a-time worker is a promise the server cannot keep, and a caller told
|
|
38
|
+
* "position 400" has been lied to more politely than one told "full".
|
|
39
|
+
*/
|
|
40
|
+
submit(input) {
|
|
41
|
+
if (this.#waiting.length >= this.#maxQueued) throw new QueueFullError(this.#maxQueued);
|
|
42
|
+
const job = {
|
|
43
|
+
id: input.id,
|
|
44
|
+
dir: input.dir,
|
|
45
|
+
state: "queued",
|
|
46
|
+
steps: input.stages.map((name) => ({ name, state: "pending" })),
|
|
47
|
+
log: [],
|
|
48
|
+
dropped: 0,
|
|
49
|
+
createdAt: this.#now(),
|
|
50
|
+
run: input.run
|
|
51
|
+
};
|
|
52
|
+
this.#jobs.set(job.id, job);
|
|
53
|
+
this.#waiting.push(job.id);
|
|
54
|
+
const ahead = this.#waiting.length - 1 + (this.#running ? 1 : 0);
|
|
55
|
+
this.#note(
|
|
56
|
+
job,
|
|
57
|
+
this.#waiting.length === 1 && !this.#running ? "queued" : `queued behind ${ahead} ${ahead === 1 ? "job" : "jobs"}`
|
|
58
|
+
);
|
|
59
|
+
for (const id of this.#waiting) this.#emit(id);
|
|
60
|
+
void this.#pump();
|
|
61
|
+
return this.view(job.id);
|
|
62
|
+
}
|
|
63
|
+
view(id) {
|
|
64
|
+
const job = this.#jobs.get(id);
|
|
65
|
+
if (!job) return void 0;
|
|
66
|
+
const at = this.#waiting.indexOf(id);
|
|
67
|
+
return {
|
|
68
|
+
id: job.id,
|
|
69
|
+
state: job.state,
|
|
70
|
+
...job.stage ? { stage: job.stage } : {},
|
|
71
|
+
steps: job.steps.map((s) => ({ ...s })),
|
|
72
|
+
log: job.dropped ? [`\u2026 ${job.dropped} earlier line(s) dropped`, ...job.log] : [...job.log],
|
|
73
|
+
...job.error ? { error: job.error } : {},
|
|
74
|
+
...job.result ? { result: job.result } : {},
|
|
75
|
+
...at >= 0 ? { queuePosition: at + 1 } : {},
|
|
76
|
+
createdAt: job.createdAt,
|
|
77
|
+
ms: (job.endedAt ?? this.#now()) - job.createdAt
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/** Subscribe to changes for one job. Returns the unsubscribe. */
|
|
81
|
+
watch(id, fn) {
|
|
82
|
+
const set = this.#watchers.get(id) ?? /* @__PURE__ */ new Set();
|
|
83
|
+
set.add(fn);
|
|
84
|
+
this.#watchers.set(id, set);
|
|
85
|
+
return () => {
|
|
86
|
+
set.delete(fn);
|
|
87
|
+
if (set.size === 0) this.#watchers.delete(id);
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Drop every job whose time is up and report their ids, so the caller can
|
|
92
|
+
* delete the directories. A running job is never swept, however old — the TTL
|
|
93
|
+
* measures how long an ANSWER is kept, not how long work may take.
|
|
94
|
+
*/
|
|
95
|
+
sweep() {
|
|
96
|
+
const cutoff = this.#now() - this.#ttlMs;
|
|
97
|
+
const expired = [];
|
|
98
|
+
for (const job of [...this.#jobs.values()]) {
|
|
99
|
+
if (job.state === "running" || job.state === "queued") continue;
|
|
100
|
+
if ((job.endedAt ?? job.createdAt) > cutoff) continue;
|
|
101
|
+
this.#jobs.delete(job.id);
|
|
102
|
+
this.#watchers.delete(job.id);
|
|
103
|
+
expired.push(job.id);
|
|
104
|
+
this.#onExpire(job.id, job.dir);
|
|
105
|
+
}
|
|
106
|
+
return expired;
|
|
107
|
+
}
|
|
108
|
+
/* ---------------------------------------------------------------- internals */
|
|
109
|
+
async #pump() {
|
|
110
|
+
if (this.#running) return;
|
|
111
|
+
const id = this.#waiting.shift();
|
|
112
|
+
if (!id) return;
|
|
113
|
+
const job = this.#jobs.get(id);
|
|
114
|
+
if (!job) return void this.#pump();
|
|
115
|
+
this.#running = id;
|
|
116
|
+
job.state = "running";
|
|
117
|
+
this.#note(job, "running");
|
|
118
|
+
for (const other of this.#waiting) this.#emit(other);
|
|
119
|
+
try {
|
|
120
|
+
job.result = await job.run(this.#handle(job));
|
|
121
|
+
job.state = "done";
|
|
122
|
+
job.stage = void 0;
|
|
123
|
+
this.#note(job, "done");
|
|
124
|
+
} catch (err) {
|
|
125
|
+
job.state = "error";
|
|
126
|
+
job.error = explain(err);
|
|
127
|
+
const active = job.steps.find((s) => s.state === "running");
|
|
128
|
+
if (active) {
|
|
129
|
+
active.state = "error";
|
|
130
|
+
active.ms = this.#now() - (active.startedAt ?? this.#now());
|
|
131
|
+
}
|
|
132
|
+
this.#note(job, `error: ${job.error.message}`);
|
|
133
|
+
} finally {
|
|
134
|
+
job.endedAt = this.#now();
|
|
135
|
+
this.#running = void 0;
|
|
136
|
+
this.#emit(job.id);
|
|
137
|
+
void this.#pump();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
#handle(job) {
|
|
141
|
+
return {
|
|
142
|
+
id: job.id,
|
|
143
|
+
dir: job.dir,
|
|
144
|
+
begin: (stage, detail) => {
|
|
145
|
+
job.stage = stage;
|
|
146
|
+
const step = this.#step(job, stage);
|
|
147
|
+
step.state = "running";
|
|
148
|
+
step.startedAt = this.#now();
|
|
149
|
+
if (detail) step.detail = detail;
|
|
150
|
+
this.#note(job, `${stage}: started`);
|
|
151
|
+
},
|
|
152
|
+
done: (stage, detail) => {
|
|
153
|
+
const step = this.#step(job, stage);
|
|
154
|
+
step.state = "done";
|
|
155
|
+
step.ms = this.#now() - (step.startedAt ?? this.#now());
|
|
156
|
+
if (detail) step.detail = detail;
|
|
157
|
+
this.#emit(job.id);
|
|
158
|
+
},
|
|
159
|
+
skip: (stage, why) => {
|
|
160
|
+
const step = this.#step(job, stage);
|
|
161
|
+
step.state = "skipped";
|
|
162
|
+
step.detail = why;
|
|
163
|
+
this.#emit(job.id);
|
|
164
|
+
},
|
|
165
|
+
log: (line) => this.#note(job, line)
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
/** A stage the caller did not declare still gets a row rather than vanishing. */
|
|
169
|
+
#step(job, stage) {
|
|
170
|
+
const found = job.steps.find((s) => s.name === stage);
|
|
171
|
+
if (found) return found;
|
|
172
|
+
const added = { name: stage, state: "pending" };
|
|
173
|
+
job.steps.push(added);
|
|
174
|
+
return added;
|
|
175
|
+
}
|
|
176
|
+
#note(job, line) {
|
|
177
|
+
job.log.push(line);
|
|
178
|
+
if (job.log.length > this.#maxLog) {
|
|
179
|
+
job.dropped += job.log.length - this.#maxLog;
|
|
180
|
+
job.log.splice(0, job.log.length - this.#maxLog);
|
|
181
|
+
}
|
|
182
|
+
this.#emit(job.id);
|
|
183
|
+
}
|
|
184
|
+
#emit(id) {
|
|
185
|
+
const watchers = this.#watchers.get(id);
|
|
186
|
+
if (!watchers?.size) return;
|
|
187
|
+
const view = this.view(id);
|
|
188
|
+
if (!view) return;
|
|
189
|
+
for (const fn of watchers) fn(view);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
export {
|
|
193
|
+
Queue,
|
|
194
|
+
QueueFullError
|
|
195
|
+
};
|