@onmark/cli 0.3.0 → 0.4.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/onmark-release.json +83 -53
- package/package.json +4 -4
- package/packages/authoring/dist/src/frame-motion.d.ts +10 -2
- package/packages/authoring/dist/src/frame-motion.js +29 -6
- package/packages/authoring/dist/src/index.d.ts +2 -2
- package/packages/authoring/dist/src/motion.d.ts +16 -4
- package/packages/authoring/dist/src/presentation.js +34 -1
- package/packages/authoring/dist/src/types.d.ts +1 -1
- package/packages/bundler/dist/src/authored_html.d.ts +17 -7
- package/packages/bundler/dist/src/authored_html.js +79 -21
- package/packages/bundler/dist/src/bundle_projection.d.ts +9 -0
- package/packages/bundler/dist/src/bundle_projection.js +66 -0
- package/packages/bundler/dist/src/command.js +28 -8
- package/packages/bundler/dist/src/generated/bundle-projection-validator.d.ts +7 -0
- package/packages/bundler/dist/src/generated/bundle-projection-validator.js +432 -0
- package/packages/bundler/dist/src/generated/bundle-projection.d.ts +25 -0
- package/packages/bundler/dist/src/generated/bundle-projection.js +2 -0
- package/packages/bundler/dist/src/index.d.ts +1 -1
- package/packages/bundler/dist/src/presentation.d.ts +13 -6
- package/packages/bundler/dist/src/presentation.js +58 -28
- package/packages/motion-gsap/dist/src/index.d.ts +10 -2
- package/packages/motion-gsap/dist/src/index.js +27 -5
- package/packages/runtime/dist/src/generated/browser-request.d.ts +15 -2
- package/packages/runtime/dist/src/generated/browser-response.d.ts +1 -1
- package/packages/runtime/dist/src/generated/runtime-contract.d.ts +2 -0
- package/packages/runtime/dist/src/generated/runtime-contract.js +2 -0
- package/packages/runtime/dist/src/generated/validators.d.ts +2 -2
- package/packages/runtime/dist/src/generated/validators.js +331 -128
- package/packages/runtime/dist/src/index.d.ts +2 -2
- package/packages/runtime/dist/src/index.js +1 -1
- package/packages/runtime/dist/src/presentation.d.ts +10 -0
- package/packages/runtime/dist/src/presentation.js +18 -3
- package/packages/runtime/dist/src/session.js +61 -12
- package/packages/runtime/dist/src/types.d.ts +1 -1
|
@@ -119,39 +119,81 @@ function projectShotStructure(source) {
|
|
|
119
119
|
const film = findElement(document, "om-film");
|
|
120
120
|
if (film === undefined) {
|
|
121
121
|
return {
|
|
122
|
-
|
|
122
|
+
shots: Object.freeze([]),
|
|
123
123
|
regionStructure: Object.freeze({ scenes: Object.freeze([]) }),
|
|
124
124
|
};
|
|
125
125
|
}
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
return Object.freeze({
|
|
129
|
-
range: removableElementRange(source, scene),
|
|
130
|
-
shots: Object.freeze(shots),
|
|
131
|
-
});
|
|
132
|
-
});
|
|
133
|
-
const regions = scenes.flatMap((scene, sceneIndex) => scene.shots.map((_, shotIndex) => Object.freeze({ scene: sceneIndex, shot: shotIndex })));
|
|
126
|
+
const shots = [];
|
|
127
|
+
const scenes = childElements(film, "om-scene").map((scene, sceneIndex) => projectSceneStructure(source, scene, sceneIndex, shots));
|
|
134
128
|
return {
|
|
135
|
-
|
|
129
|
+
shots: Object.freeze(shots),
|
|
136
130
|
regionStructure: Object.freeze({ scenes: Object.freeze(scenes) }),
|
|
137
131
|
};
|
|
138
132
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
133
|
+
function projectSceneStructure(source, scene, sceneIndex, documentShots) {
|
|
134
|
+
const shots = [];
|
|
135
|
+
const transitions = [];
|
|
136
|
+
let outgoingShot;
|
|
137
|
+
let pendingTransition;
|
|
138
|
+
for (const element of directElements(scene)) {
|
|
139
|
+
if (element.tagName === "om-transition") {
|
|
140
|
+
if (outgoingShot === undefined || pendingTransition !== undefined) {
|
|
141
|
+
throw transitionProjectionError();
|
|
142
|
+
}
|
|
143
|
+
pendingTransition = removableElementRange(source, element);
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
if (element.tagName !== "om-shot") {
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
const index = documentShots.length;
|
|
150
|
+
const shot = Object.freeze({ scene: sceneIndex, shot: shots.length });
|
|
151
|
+
documentShots.push(shot);
|
|
152
|
+
shots.push(Object.freeze({
|
|
153
|
+
index,
|
|
154
|
+
range: removableElementRange(source, element),
|
|
155
|
+
}));
|
|
156
|
+
if (pendingTransition !== undefined && outgoingShot !== undefined) {
|
|
157
|
+
transitions.push(Object.freeze({
|
|
158
|
+
incomingShot: index,
|
|
159
|
+
outgoingShot,
|
|
160
|
+
range: pendingTransition,
|
|
161
|
+
}));
|
|
162
|
+
pendingTransition = undefined;
|
|
163
|
+
}
|
|
164
|
+
outgoingShot = index;
|
|
145
165
|
}
|
|
166
|
+
if (pendingTransition !== undefined) {
|
|
167
|
+
throw transitionProjectionError();
|
|
168
|
+
}
|
|
169
|
+
return Object.freeze({
|
|
170
|
+
range: removableElementRange(source, scene),
|
|
171
|
+
shots: Object.freeze(shots),
|
|
172
|
+
transitions: Object.freeze(transitions),
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
function transitionProjectionError() {
|
|
176
|
+
return new AuthoredHtmlError("compiled transition boundaries cannot be projected into browser regions");
|
|
177
|
+
}
|
|
178
|
+
/** Materializes one Rust-planned region without retaining every projection. */
|
|
179
|
+
export function projectRegionDocument(html, shotIndices) {
|
|
180
|
+
const selectedShots = selectedShotSet(html, shotIndices);
|
|
146
181
|
const ranges = [];
|
|
147
|
-
for (const
|
|
148
|
-
if (
|
|
182
|
+
for (const scene of html.regionStructure.scenes) {
|
|
183
|
+
if (!scene.shots.some((shot) => selectedShots.has(shot.index))) {
|
|
149
184
|
ranges.push(scene.range);
|
|
150
185
|
continue;
|
|
151
186
|
}
|
|
152
|
-
for (const
|
|
153
|
-
if (
|
|
154
|
-
ranges.push(shot);
|
|
187
|
+
for (const shot of scene.shots) {
|
|
188
|
+
if (!selectedShots.has(shot.index)) {
|
|
189
|
+
ranges.push(shot.range);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
for (const transition of scene.transitions) {
|
|
193
|
+
const retained = selectedShots.has(transition.outgoingShot) &&
|
|
194
|
+
selectedShots.has(transition.incomingShot);
|
|
195
|
+
if (!retained) {
|
|
196
|
+
ranges.push(transition.range);
|
|
155
197
|
}
|
|
156
198
|
}
|
|
157
199
|
}
|
|
@@ -160,6 +202,16 @@ export function projectShotDocument(html, region) {
|
|
|
160
202
|
runtimeOffset: projectOffset(html.runtimeOffset, ranges),
|
|
161
203
|
});
|
|
162
204
|
}
|
|
205
|
+
function selectedShotSet(html, shotIndices) {
|
|
206
|
+
const selected = new Set();
|
|
207
|
+
for (const index of shotIndices) {
|
|
208
|
+
if (html.shots[index] === undefined) {
|
|
209
|
+
throw new AuthoredHtmlError(`browser projection selects unknown shot ${index}`);
|
|
210
|
+
}
|
|
211
|
+
selected.add(index);
|
|
212
|
+
}
|
|
213
|
+
return selected;
|
|
214
|
+
}
|
|
163
215
|
function parseDocument(source) {
|
|
164
216
|
const errors = [];
|
|
165
217
|
const document = parse(source, {
|
|
@@ -234,6 +286,9 @@ function findElement(node, name) {
|
|
|
234
286
|
function childElements(parent, name) {
|
|
235
287
|
return parent.childNodes.filter((child) => "tagName" in child && child.tagName === name);
|
|
236
288
|
}
|
|
289
|
+
function directElements(parent) {
|
|
290
|
+
return parent.childNodes.filter((child) => "tagName" in child);
|
|
291
|
+
}
|
|
237
292
|
function visit(node, visitor) {
|
|
238
293
|
const pending = [node];
|
|
239
294
|
while (pending.length > 0) {
|
|
@@ -328,9 +383,12 @@ function isCompilerOnlyAttribute(tagName, attributeName) {
|
|
|
328
383
|
case "om-title":
|
|
329
384
|
return attributeName === "cue" || attributeName === "delay";
|
|
330
385
|
case "om-shot":
|
|
386
|
+
case "om-transition":
|
|
331
387
|
return attributeName === "duration";
|
|
332
388
|
case "video":
|
|
333
389
|
return (attributeName === "delay" ||
|
|
390
|
+
attributeName === "hold-last" ||
|
|
391
|
+
attributeName === "plays" ||
|
|
334
392
|
attributeName === "speed" ||
|
|
335
393
|
attributeName === "src" ||
|
|
336
394
|
attributeName === "trim");
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { BundleProjection } from "./generated/bundle-projection.js";
|
|
2
|
+
/** Invalid or unreadable projection data at the bundler process boundary. */
|
|
3
|
+
export declare class BundleProjectionError extends Error {
|
|
4
|
+
constructor(message: string, options?: ErrorOptions);
|
|
5
|
+
}
|
|
6
|
+
/** Reads and validates one bounded Rust-owned projection snapshot. */
|
|
7
|
+
export declare function readBundleProjection(path: string): Promise<BundleProjection>;
|
|
8
|
+
/** Decodes one checked projection at an in-process or file boundary. */
|
|
9
|
+
export declare function decodeBundleProjection(value: unknown): BundleProjection;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// Bounded reader for the Rust-owned render-region projection contract.
|
|
2
|
+
// It validates process input before authored HTML is projected or published.
|
|
3
|
+
import { open } from "node:fs/promises";
|
|
4
|
+
import { resolve } from "node:path";
|
|
5
|
+
import { validateBundleProjection } from "./generated/bundle-projection-validator.js";
|
|
6
|
+
const MAX_PROJECTION_BYTES = 1024 * 1024;
|
|
7
|
+
/** Invalid or unreadable projection data at the bundler process boundary. */
|
|
8
|
+
export class BundleProjectionError extends Error {
|
|
9
|
+
constructor(message, options) {
|
|
10
|
+
super(message, options);
|
|
11
|
+
this.name = "BundleProjectionError";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/** Reads and validates one bounded Rust-owned projection snapshot. */
|
|
15
|
+
export async function readBundleProjection(path) {
|
|
16
|
+
const absolute = resolve(path);
|
|
17
|
+
let file;
|
|
18
|
+
try {
|
|
19
|
+
file = await open(absolute, "r");
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
throw new BundleProjectionError(`cannot open bundle projection ${absolute}`, { cause: error });
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
const bytes = await readBoundedProjection(file);
|
|
26
|
+
const value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
|
|
27
|
+
return decodeBundleProjection(value);
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
if (error instanceof BundleProjectionError) {
|
|
31
|
+
throw error;
|
|
32
|
+
}
|
|
33
|
+
throw new BundleProjectionError(`cannot read bundle projection ${absolute}`, { cause: error });
|
|
34
|
+
}
|
|
35
|
+
finally {
|
|
36
|
+
await file.close();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/** Decodes one checked projection at an in-process or file boundary. */
|
|
40
|
+
export function decodeBundleProjection(value) {
|
|
41
|
+
if (!validateBundleProjection(value)) {
|
|
42
|
+
throw invalidProjection();
|
|
43
|
+
}
|
|
44
|
+
return value;
|
|
45
|
+
}
|
|
46
|
+
async function readBoundedProjection(file) {
|
|
47
|
+
const bytes = Buffer.allocUnsafe(MAX_PROJECTION_BYTES + 1);
|
|
48
|
+
let length = 0;
|
|
49
|
+
while (length < bytes.length) {
|
|
50
|
+
const { bytesRead } = await file.read(bytes, length, bytes.length - length, length);
|
|
51
|
+
if (bytesRead === 0) {
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
length += bytesRead;
|
|
55
|
+
}
|
|
56
|
+
if (length > MAX_PROJECTION_BYTES) {
|
|
57
|
+
throw new BundleProjectionError(`bundle projection exceeds the ${MAX_PROJECTION_BYTES}-byte limit`);
|
|
58
|
+
}
|
|
59
|
+
return bytes.subarray(0, length);
|
|
60
|
+
}
|
|
61
|
+
function invalidProjection() {
|
|
62
|
+
const error = validateBundleProjection.errors?.[0];
|
|
63
|
+
const path = error?.instancePath ?? "";
|
|
64
|
+
const detail = error?.message ?? "does not match its schema";
|
|
65
|
+
return new BundleProjectionError(`bundle projection${path} ${detail}`.trim());
|
|
66
|
+
}
|
|
@@ -3,11 +3,13 @@
|
|
|
3
3
|
import process from "node:process";
|
|
4
4
|
import { parseArgs } from "node:util";
|
|
5
5
|
import { BundleError, bundlePresentation, } from "./presentation.js";
|
|
6
|
+
import { BundleProjectionError, readBundleProjection, } from "./bundle_projection.js";
|
|
6
7
|
import { BUNDLE_FRAME_BEHAVIORS, BUNDLE_TEMPORAL_CAPABILITIES, BUNDLE_VISUAL_CAPABILITIES, } from "./generated/bundle-manifest.js";
|
|
7
8
|
const USAGE = [
|
|
8
9
|
"Usage: onmark-bundle",
|
|
9
10
|
" --html <path>",
|
|
10
11
|
" [--resolve-directory <directory>]",
|
|
12
|
+
" [--projection <path> # required for randomAccess]",
|
|
11
13
|
" --output <directory>",
|
|
12
14
|
" --max-output-bytes <bytes>",
|
|
13
15
|
" --frame-behavior <perFrame|placementBounded>",
|
|
@@ -17,7 +19,7 @@ const USAGE = [
|
|
|
17
19
|
].join("\n");
|
|
18
20
|
// ── Execution ──
|
|
19
21
|
try {
|
|
20
|
-
const command = parseArguments(process.argv.slice(2));
|
|
22
|
+
const command = await parseArguments(process.argv.slice(2));
|
|
21
23
|
if (command.kind === "help") {
|
|
22
24
|
process.stdout.write(USAGE);
|
|
23
25
|
}
|
|
@@ -31,7 +33,7 @@ catch (error) {
|
|
|
31
33
|
process.exitCode = failure.kind === "configuration" ? 2 : 1;
|
|
32
34
|
}
|
|
33
35
|
// ── Arguments ──
|
|
34
|
-
function parseArguments(arguments_) {
|
|
36
|
+
async function parseArguments(arguments_) {
|
|
35
37
|
const values = commandValues(arguments_);
|
|
36
38
|
if (values.help !== undefined) {
|
|
37
39
|
if (arguments_.length !== 1) {
|
|
@@ -48,16 +50,25 @@ function parseArguments(arguments_) {
|
|
|
48
50
|
frameBehavior,
|
|
49
51
|
maxOutputBytes,
|
|
50
52
|
outputDirectory,
|
|
51
|
-
temporalCapability,
|
|
52
53
|
visualCapability,
|
|
53
54
|
};
|
|
55
|
+
const common = {
|
|
56
|
+
...controls,
|
|
57
|
+
document: oneValue(values.html, "--html"),
|
|
58
|
+
...optionalResolveDirectory(values["resolve-directory"]),
|
|
59
|
+
};
|
|
60
|
+
if (temporalCapability === "sequential") {
|
|
61
|
+
rejectValue(values.projection, "--projection requires randomAccess");
|
|
62
|
+
return {
|
|
63
|
+
kind: "bundle",
|
|
64
|
+
options: { ...common, temporalCapability },
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
const projectionPath = oneValue(values.projection, "--projection");
|
|
68
|
+
const projection = await readBundleProjection(projectionPath);
|
|
54
69
|
return {
|
|
55
70
|
kind: "bundle",
|
|
56
|
-
options: {
|
|
57
|
-
...controls,
|
|
58
|
-
document: oneValue(values.html, "--html"),
|
|
59
|
-
...optionalResolveDirectory(values["resolve-directory"]),
|
|
60
|
-
},
|
|
71
|
+
options: { ...common, projection, temporalCapability },
|
|
61
72
|
};
|
|
62
73
|
}
|
|
63
74
|
function commandValues(arguments_) {
|
|
@@ -71,6 +82,7 @@ function commandValues(arguments_) {
|
|
|
71
82
|
html: { type: "string", multiple: true },
|
|
72
83
|
"max-output-bytes": { type: "string", multiple: true },
|
|
73
84
|
output: { type: "string", multiple: true },
|
|
85
|
+
projection: { type: "string", multiple: true },
|
|
74
86
|
"resolve-directory": { type: "string", multiple: true },
|
|
75
87
|
"temporal-capability": { type: "string", multiple: true },
|
|
76
88
|
"visual-capability": { type: "string", multiple: true },
|
|
@@ -119,6 +131,11 @@ function oneValue(values, name) {
|
|
|
119
131
|
}
|
|
120
132
|
return value;
|
|
121
133
|
}
|
|
134
|
+
function rejectValue(values, message) {
|
|
135
|
+
if (values !== undefined) {
|
|
136
|
+
throw configuration(message);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
122
139
|
function parseByteLimit(value) {
|
|
123
140
|
const bytes = Number(value);
|
|
124
141
|
if (!Number.isSafeInteger(bytes) || bytes <= 0) {
|
|
@@ -134,5 +151,8 @@ function commandFailure(error) {
|
|
|
134
151
|
if (error instanceof BundleError) {
|
|
135
152
|
return error;
|
|
136
153
|
}
|
|
154
|
+
if (error instanceof BundleProjectionError) {
|
|
155
|
+
return configuration(error.message, error);
|
|
156
|
+
}
|
|
137
157
|
return new BundleError("output", "unexpected bundler failure", error);
|
|
138
158
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const validateBundleProjection: typeof validate20;
|
|
2
|
+
declare function validate20(data: any, { instancePath, parentData, parentDataProperty, rootData, dynamicAnchors, }?: {
|
|
3
|
+
dynamicAnchors?: {} | undefined;
|
|
4
|
+
instancePath?: string | undefined;
|
|
5
|
+
rootData?: any;
|
|
6
|
+
}): boolean;
|
|
7
|
+
export {};
|