@onmark/cli 0.1.3

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 (77) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +202 -0
  3. package/onmark-release.json +388 -0
  4. package/package.json +52 -0
  5. package/packages/authoring/dist/src/index.d.ts +3 -0
  6. package/packages/authoring/dist/src/index.js +4 -0
  7. package/packages/authoring/dist/src/motion.d.ts +23 -0
  8. package/packages/authoring/dist/src/motion.js +81 -0
  9. package/packages/authoring/dist/src/presentation.d.ts +12 -0
  10. package/packages/authoring/dist/src/presentation.js +226 -0
  11. package/packages/authoring/dist/src/resource.d.ts +32 -0
  12. package/packages/authoring/dist/src/resource.js +86 -0
  13. package/packages/authoring/dist/src/types.d.ts +1 -0
  14. package/packages/authoring/dist/src/types.js +2 -0
  15. package/packages/bundler/dist/src/authored_html.d.ts +41 -0
  16. package/packages/bundler/dist/src/authored_html.js +387 -0
  17. package/packages/bundler/dist/src/command.d.ts +2 -0
  18. package/packages/bundler/dist/src/command.js +138 -0
  19. package/packages/bundler/dist/src/generated/bundle-manifest.d.ts +65 -0
  20. package/packages/bundler/dist/src/generated/bundle-manifest.js +18 -0
  21. package/packages/bundler/dist/src/html_image.d.ts +20 -0
  22. package/packages/bundler/dist/src/html_image.js +337 -0
  23. package/packages/bundler/dist/src/image_admission.d.ts +2 -0
  24. package/packages/bundler/dist/src/image_admission.js +217 -0
  25. package/packages/bundler/dist/src/index.d.ts +2 -0
  26. package/packages/bundler/dist/src/index.js +3 -0
  27. package/packages/bundler/dist/src/presentation.d.ts +41 -0
  28. package/packages/bundler/dist/src/presentation.js +492 -0
  29. package/packages/launcher/dist/desktop-release.json +21 -0
  30. package/packages/launcher/dist/src/browser-command.d.ts +2 -0
  31. package/packages/launcher/dist/src/browser-command.js +43 -0
  32. package/packages/launcher/dist/src/browser.d.ts +14 -0
  33. package/packages/launcher/dist/src/browser.js +280 -0
  34. package/packages/launcher/dist/src/cache.d.ts +8 -0
  35. package/packages/launcher/dist/src/cache.js +21 -0
  36. package/packages/launcher/dist/src/capture-environment.d.ts +15 -0
  37. package/packages/launcher/dist/src/capture-environment.js +145 -0
  38. package/packages/launcher/dist/src/command.d.ts +2 -0
  39. package/packages/launcher/dist/src/command.js +58 -0
  40. package/packages/launcher/dist/src/native.d.ts +20 -0
  41. package/packages/launcher/dist/src/native.js +31 -0
  42. package/packages/launcher/dist/src/platform.d.ts +13 -0
  43. package/packages/launcher/dist/src/platform.js +29 -0
  44. package/packages/launcher/dist/src/release.d.ts +16 -0
  45. package/packages/launcher/dist/src/release.js +49 -0
  46. package/packages/motion-gsap/dist/src/index.d.ts +16 -0
  47. package/packages/motion-gsap/dist/src/index.js +139 -0
  48. package/packages/runtime/dist/src/clock.d.ts +10 -0
  49. package/packages/runtime/dist/src/clock.js +19 -0
  50. package/packages/runtime/dist/src/generated/browser-request.d.ts +143 -0
  51. package/packages/runtime/dist/src/generated/browser-request.js +2 -0
  52. package/packages/runtime/dist/src/generated/browser-response.d.ts +58 -0
  53. package/packages/runtime/dist/src/generated/browser-response.js +2 -0
  54. package/packages/runtime/dist/src/generated/bundle-layout.d.ts +7 -0
  55. package/packages/runtime/dist/src/generated/bundle-layout.js +14 -0
  56. package/packages/runtime/dist/src/generated/codec.d.ts +8 -0
  57. package/packages/runtime/dist/src/generated/codec.js +22 -0
  58. package/packages/runtime/dist/src/generated/runtime-contract.d.ts +7 -0
  59. package/packages/runtime/dist/src/generated/runtime-contract.js +8 -0
  60. package/packages/runtime/dist/src/generated/validators.d.ts +13 -0
  61. package/packages/runtime/dist/src/generated/validators.js +3327 -0
  62. package/packages/runtime/dist/src/host.d.ts +11 -0
  63. package/packages/runtime/dist/src/host.js +25 -0
  64. package/packages/runtime/dist/src/index.d.ts +13 -0
  65. package/packages/runtime/dist/src/index.js +12 -0
  66. package/packages/runtime/dist/src/media.d.ts +13 -0
  67. package/packages/runtime/dist/src/media.js +28 -0
  68. package/packages/runtime/dist/src/presentation.d.ts +66 -0
  69. package/packages/runtime/dist/src/presentation.js +379 -0
  70. package/packages/runtime/dist/src/resource.d.ts +23 -0
  71. package/packages/runtime/dist/src/resource.js +150 -0
  72. package/packages/runtime/dist/src/session.d.ts +43 -0
  73. package/packages/runtime/dist/src/session.js +450 -0
  74. package/packages/runtime/dist/src/types.d.ts +6 -0
  75. package/packages/runtime/dist/src/types.js +2 -0
  76. package/packages/runtime/dist/src/video.d.ts +40 -0
  77. package/packages/runtime/dist/src/video.js +343 -0
@@ -0,0 +1,40 @@
1
+ import type { RuntimeVideo, VideoFrameSelection } from "./media.js";
2
+ type VideoEvent = "error" | "loadeddata" | "seeked";
3
+ type FrameCallback = (now: number, metadata: {
4
+ readonly mediaTime: number;
5
+ }) => void;
6
+ /** Minimal browser-media capability required from a presentation. */
7
+ export interface BrowserVideoElement {
8
+ currentTime: number;
9
+ src: string;
10
+ addEventListener(type: VideoEvent, listener: () => void): void;
11
+ cancelVideoFrameCallback(handle: number): void;
12
+ load(): void;
13
+ removeAttribute(name: "src"): void;
14
+ removeEventListener(type: VideoEvent, listener: () => void): void;
15
+ requestVideoFrameCallback(callback: FrameCallback): number;
16
+ }
17
+ /** Browser capability and bounded identity owned by one decoded video. */
18
+ export interface DecodedVideoOptions {
19
+ readonly element: BrowserVideoElement;
20
+ readonly nodeId: number;
21
+ readonly readinessTimeoutMilliseconds: number;
22
+ }
23
+ /** One decoded-video resource with exact-frame readiness and terminal cleanup. */
24
+ export declare class DecodedVideo {
25
+ #private;
26
+ constructor(options: DecodedVideoOptions);
27
+ /** Loads one materialized source and waits for decoded data. */
28
+ load(source: string): Promise<void>;
29
+ /** Seeks while registering the callback that will observe compositor output. */
30
+ stage(selection: VideoFrameSelection): Promise<void>;
31
+ /** Confirms staged media reached the compositor before capture is accepted. */
32
+ confirm(selection: VideoFrameSelection): Promise<void>;
33
+ /** Cancels one staged frame that will not be captured. */
34
+ discardStagedFrame(): void;
35
+ /** Releases the media resource and makes this controller terminal. */
36
+ dispose(): void;
37
+ }
38
+ /** Returns the unit-root source for one already-validated video placement. */
39
+ export declare function materializedVideoSource(placement: RuntimeVideo): string;
40
+ export {};
@@ -0,0 +1,343 @@
1
+ // Bounded browser-video lifecycle over a presentation-owned media element.
2
+ // Timeline selection remains Rust-owned; this module only proves decode readiness.
3
+ import { BUNDLE_ASSET_DIRECTORY } from "./generated/bundle-layout.js";
4
+ import { requireReadinessTimeout } from "./resource.js";
5
+ import { RuntimeAdapterError } from "./session.js";
6
+ const FRAME_TOLERANCE_SECONDS = 0.000_001;
7
+ const LOAD_READINESS = {
8
+ event: "loadeddata",
9
+ failureMessage: "video data failed to load",
10
+ timeoutMessage: "video data did not load before its readiness deadline",
11
+ };
12
+ const SEEK_READINESS = {
13
+ event: "seeked",
14
+ failureMessage: "video seek failed",
15
+ timeoutMessage: "video seek did not finish before its readiness deadline",
16
+ };
17
+ /** One decoded-video resource with exact-frame readiness and terminal cleanup. */
18
+ export class DecodedVideo {
19
+ #element;
20
+ #nodeId;
21
+ #timeoutMilliseconds;
22
+ #pendingFrame;
23
+ #presentedMediaTime;
24
+ #state = "empty";
25
+ constructor(options) {
26
+ const { element, nodeId, readinessTimeoutMilliseconds } = options;
27
+ requireVideoNodeId(nodeId);
28
+ requireReadinessTimeout(readinessTimeoutMilliseconds);
29
+ this.#element = element;
30
+ this.#nodeId = nodeId;
31
+ this.#timeoutMilliseconds = readinessTimeoutMilliseconds;
32
+ }
33
+ /** Loads one materialized source and waits for decoded data. */
34
+ async load(source) {
35
+ this.#requireState("empty", "load");
36
+ if (source.length === 0) {
37
+ throw new RuntimeAdapterError("operation", "video source is empty");
38
+ }
39
+ const readiness = new MediaEventReadiness(this.#element, this.#timeoutMilliseconds, LOAD_READINESS, videoResource(this.#nodeId, LOAD_READINESS.event));
40
+ try {
41
+ await readiness.waitAfter(() => {
42
+ this.#element.src = source;
43
+ this.#element.load();
44
+ });
45
+ this.#state = "loaded";
46
+ }
47
+ catch (error) {
48
+ readiness.cancel();
49
+ const cleanupFailure = releaseElement(this.#element);
50
+ if (cleanupFailure !== undefined) {
51
+ // A source that could not be fully released must never be reused.
52
+ this.#state = "disposed";
53
+ }
54
+ throw videoLoadFailure(error, cleanupFailure);
55
+ }
56
+ }
57
+ /** Seeks while registering the callback that will observe compositor output. */
58
+ async stage(selection) {
59
+ this.#requireState("loaded", "stage");
60
+ requireSelection(selection);
61
+ if (selection.mediaTimeSeconds === this.#presentedMediaTime) {
62
+ return;
63
+ }
64
+ if (this.#pendingFrame !== undefined) {
65
+ throw new RuntimeAdapterError("operation", "video cannot stage another frame before confirmation");
66
+ }
67
+ const pending = StagedFrame.observe(this.#element, selection, videoResource(this.#nodeId, "frame"));
68
+ const readiness = new MediaEventReadiness(this.#element, this.#timeoutMilliseconds, SEEK_READINESS, videoResource(this.#nodeId, SEEK_READINESS.event));
69
+ this.#pendingFrame = pending;
70
+ try {
71
+ await readiness.waitAfter(() => {
72
+ this.#element.currentTime = selection.seekTimeSeconds;
73
+ });
74
+ }
75
+ catch (error) {
76
+ readiness.cancel();
77
+ pending.cancel();
78
+ this.#pendingFrame = undefined;
79
+ throw error;
80
+ }
81
+ }
82
+ /** Confirms staged media reached the compositor before capture is accepted. */
83
+ async confirm(selection) {
84
+ this.#requireState("loaded", "confirm");
85
+ requireSelection(selection);
86
+ if (selection.mediaTimeSeconds === this.#presentedMediaTime) {
87
+ return;
88
+ }
89
+ const pending = this.#pendingFrame;
90
+ if (pending === undefined || !pending.matches(selection)) {
91
+ throw new RuntimeAdapterError("operation", "video confirmation requires the staged frame");
92
+ }
93
+ try {
94
+ await pending.confirm(this.#timeoutMilliseconds);
95
+ }
96
+ catch (error) {
97
+ throw RuntimeAdapterError.fromUnknown(error, "video frame confirmation failed");
98
+ }
99
+ finally {
100
+ pending.cancel();
101
+ this.#pendingFrame = undefined;
102
+ }
103
+ this.#presentedMediaTime = selection.mediaTimeSeconds;
104
+ }
105
+ /** Cancels one staged frame that will not be captured. */
106
+ discardStagedFrame() {
107
+ this.#pendingFrame?.cancel();
108
+ this.#pendingFrame = undefined;
109
+ }
110
+ /** Releases the media resource and makes this controller terminal. */
111
+ dispose() {
112
+ if (this.#state === "disposed") {
113
+ return;
114
+ }
115
+ this.#state = "disposed";
116
+ this.discardStagedFrame();
117
+ this.#presentedMediaTime = undefined;
118
+ const failure = releaseElement(this.#element);
119
+ if (failure !== undefined) {
120
+ throw RuntimeAdapterError.fromUnknown(failure, "video resource cleanup failed");
121
+ }
122
+ }
123
+ #requireState(expected, operation) {
124
+ if (this.#state !== expected) {
125
+ throw new RuntimeAdapterError("operation", `video ${operation} requires the ${expected} state`);
126
+ }
127
+ }
128
+ }
129
+ /** Returns the unit-root source for one already-validated video placement. */
130
+ export function materializedVideoSource(placement) {
131
+ const digest = placement.assetId.slice("sha256:".length);
132
+ return `./${BUNDLE_ASSET_DIRECTORY}/${digest}`;
133
+ }
134
+ function releaseElement(element) {
135
+ let failure;
136
+ for (const release of [
137
+ () => element.removeAttribute("src"),
138
+ () => element.load(),
139
+ ]) {
140
+ try {
141
+ release();
142
+ }
143
+ catch (error) {
144
+ failure ??= error;
145
+ }
146
+ }
147
+ return failure;
148
+ }
149
+ function videoLoadFailure(error, cleanupFailure) {
150
+ if (cleanupFailure !== undefined) {
151
+ return new RuntimeAdapterError("operation", "video load failed and cleanup was incomplete");
152
+ }
153
+ return RuntimeAdapterError.fromUnknown(error, "video data failed to load");
154
+ }
155
+ // ── Readiness waits ──
156
+ /** One listener-first media event barrier with a bounded terminal state. */
157
+ class MediaEventReadiness {
158
+ #deadline;
159
+ #contract;
160
+ #element;
161
+ #pendingResource;
162
+ #promise;
163
+ #reject;
164
+ #resolve;
165
+ #settled = false;
166
+ constructor(element, timeoutMilliseconds, contract, pendingResource) {
167
+ this.#element = element;
168
+ this.#contract = contract;
169
+ this.#pendingResource = pendingResource;
170
+ const pending = Promise.withResolvers();
171
+ this.#promise = pending.promise;
172
+ this.#reject = pending.reject;
173
+ this.#resolve = pending.resolve;
174
+ this.#deadline = setTimeout(this.#timeout, timeoutMilliseconds);
175
+ element.addEventListener(contract.event, this.#complete);
176
+ element.addEventListener("error", this.#fail);
177
+ }
178
+ waitAfter(action) {
179
+ try {
180
+ action();
181
+ }
182
+ catch (error) {
183
+ this.#settle();
184
+ throw RuntimeAdapterError.fromUnknown(error, this.#contract.failureMessage);
185
+ }
186
+ return this.#promise;
187
+ }
188
+ cancel() {
189
+ if (this.#settle()) {
190
+ this.#resolve();
191
+ }
192
+ }
193
+ #complete = () => {
194
+ if (this.#settle()) {
195
+ this.#resolve();
196
+ }
197
+ };
198
+ #fail = () => {
199
+ if (!this.#settle()) {
200
+ return;
201
+ }
202
+ this.#reject(new RuntimeAdapterError("operation", this.#contract.failureMessage));
203
+ };
204
+ #timeout = () => {
205
+ if (this.#settle()) {
206
+ this.#reject(new RuntimeAdapterError("readinessTimeout", this.#contract.timeoutMessage, [this.#pendingResource]));
207
+ }
208
+ };
209
+ #settle() {
210
+ if (this.#settled) {
211
+ return false;
212
+ }
213
+ this.#settled = true;
214
+ clearTimeout(this.#deadline);
215
+ this.#element.removeEventListener(this.#contract.event, this.#complete);
216
+ this.#element.removeEventListener("error", this.#fail);
217
+ return true;
218
+ }
219
+ }
220
+ class StagedFrame {
221
+ #element;
222
+ #observation;
223
+ #pendingResource;
224
+ #resolve;
225
+ #selection;
226
+ #frameCallback;
227
+ #settled = false;
228
+ constructor(element, selection, pendingResource) {
229
+ this.#element = element;
230
+ this.#selection = selection;
231
+ this.#pendingResource = pendingResource;
232
+ const pending = Promise.withResolvers();
233
+ this.#observation = pending.promise;
234
+ this.#resolve = pending.resolve;
235
+ element.addEventListener("error", this.#failed);
236
+ }
237
+ static observe(element, selection, pendingResource) {
238
+ const staged = new StagedFrame(element, selection, pendingResource);
239
+ try {
240
+ staged.#requestFrame();
241
+ return staged;
242
+ }
243
+ catch (error) {
244
+ staged.cancel();
245
+ throw RuntimeAdapterError.fromUnknown(error, "video frame callback failed");
246
+ }
247
+ }
248
+ matches(selection) {
249
+ return (selection.mediaTimeSeconds === this.#selection.mediaTimeSeconds &&
250
+ selection.seekTimeSeconds === this.#selection.seekTimeSeconds);
251
+ }
252
+ async confirm(timeoutMilliseconds) {
253
+ const observation = await observedBeforeDeadline(this.#observation, timeoutMilliseconds, this.#pendingResource);
254
+ if (observation.kind === "failed") {
255
+ throw observation.error;
256
+ }
257
+ }
258
+ cancel() {
259
+ if (!this.#settle()) {
260
+ return;
261
+ }
262
+ this.#resolve({
263
+ kind: "failed",
264
+ error: new RuntimeAdapterError("operation", "staged video frame was discarded"),
265
+ });
266
+ }
267
+ #inspectFrame = (_now, metadata) => {
268
+ this.#frameCallback = undefined;
269
+ const exactFrame = Math.abs(metadata.mediaTime - this.#selection.mediaTimeSeconds) <=
270
+ FRAME_TOLERANCE_SECONDS;
271
+ if (exactFrame) {
272
+ this.#finish({ kind: "presented" });
273
+ return;
274
+ }
275
+ try {
276
+ this.#requestFrame();
277
+ }
278
+ catch (error) {
279
+ this.#finish({
280
+ kind: "failed",
281
+ error: RuntimeAdapterError.fromUnknown(error, "video frame callback failed"),
282
+ });
283
+ }
284
+ };
285
+ #failed = () => {
286
+ this.#finish({
287
+ kind: "failed",
288
+ error: new RuntimeAdapterError("operation", "video seek failed"),
289
+ });
290
+ };
291
+ #requestFrame() {
292
+ this.#frameCallback = this.#element.requestVideoFrameCallback(this.#inspectFrame);
293
+ }
294
+ #finish(observation) {
295
+ if (this.#settle()) {
296
+ this.#resolve(observation);
297
+ }
298
+ }
299
+ #settle() {
300
+ if (this.#settled) {
301
+ return false;
302
+ }
303
+ this.#settled = true;
304
+ this.#element.removeEventListener("error", this.#failed);
305
+ if (this.#frameCallback !== undefined) {
306
+ this.#element.cancelVideoFrameCallback(this.#frameCallback);
307
+ }
308
+ return true;
309
+ }
310
+ }
311
+ async function observedBeforeDeadline(observation, timeoutMilliseconds, pendingResource) {
312
+ let deadline;
313
+ const timeout = new Promise((resolve) => {
314
+ deadline = setTimeout(() => {
315
+ resolve({
316
+ kind: "failed",
317
+ error: new RuntimeAdapterError("readinessTimeout", "decoded video frame did not become ready", [pendingResource]),
318
+ });
319
+ }, timeoutMilliseconds);
320
+ });
321
+ try {
322
+ return await Promise.race([observation, timeout]);
323
+ }
324
+ finally {
325
+ clearTimeout(deadline);
326
+ }
327
+ }
328
+ function requireVideoNodeId(nodeId) {
329
+ if (!Number.isSafeInteger(nodeId) || nodeId < 0) {
330
+ throw new TypeError("video node ID must be a nonnegative safe integer");
331
+ }
332
+ }
333
+ function videoResource(nodeId, phase) {
334
+ return `video:${nodeId}:${phase}`;
335
+ }
336
+ function requireSelection(selection) {
337
+ if (!Number.isFinite(selection.mediaTimeSeconds) ||
338
+ selection.mediaTimeSeconds < 0 ||
339
+ !Number.isFinite(selection.seekTimeSeconds) ||
340
+ selection.seekTimeSeconds < selection.mediaTimeSeconds) {
341
+ throw new RuntimeAdapterError("operation", "video frame selection is invalid");
342
+ }
343
+ }