@12ui/design 0.2.71 → 0.2.72
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 +5 -0
- package/SKILL.md +1 -1
- package/dist/cli-capabilities.d.ts +9 -0
- package/dist/cli-capabilities.d.ts.map +1 -1
- package/dist/cli-capabilities.js +10 -0
- package/dist/cli-capabilities.js.map +1 -1
- package/dist/cli-help/index.d.ts +93 -0
- package/dist/cli-help/index.d.ts.map +1 -1
- package/dist/cli-help/index.js +2 -0
- package/dist/cli-help/index.js.map +1 -1
- package/dist/cli-help/scene.d.ts +96 -0
- package/dist/cli-help/scene.d.ts.map +1 -0
- package/dist/cli-help/scene.js +162 -0
- package/dist/cli-help/scene.js.map +1 -0
- package/dist/cli-run.d.ts.map +1 -1
- package/dist/cli-run.js +6 -0
- package/dist/cli-run.js.map +1 -1
- package/dist/cli-scene-command.d.ts +3 -0
- package/dist/cli-scene-command.d.ts.map +1 -0
- package/dist/cli-scene-command.js +292 -0
- package/dist/cli-scene-command.js.map +1 -0
- package/dist/cli-usage.d.ts +8 -0
- package/dist/cli-usage.d.ts.map +1 -1
- package/dist/cli-usage.js +26 -1
- package/dist/cli-usage.js.map +1 -1
- package/dist/legacy-skill-catalog.d.ts.map +1 -1
- package/dist/legacy-skill-catalog.js +11 -0
- package/dist/legacy-skill-catalog.js.map +1 -1
- package/dist/prototype-browsers.d.ts +8 -2
- package/dist/prototype-browsers.d.ts.map +1 -1
- package/dist/prototype-browsers.js +8 -3
- package/dist/prototype-browsers.js.map +1 -1
- package/dist/prototype-process.js +1 -1
- package/dist/prototype-process.js.map +1 -1
- package/dist/run-journal-commands.d.ts.map +1 -1
- package/dist/run-journal-commands.js +6 -0
- package/dist/run-journal-commands.js.map +1 -1
- package/dist/scene/camera-model.d.ts +48 -0
- package/dist/scene/camera-model.d.ts.map +1 -0
- package/dist/scene/camera-model.js +89 -0
- package/dist/scene/camera-model.js.map +1 -0
- package/dist/scene/camera-solve.d.ts +58 -0
- package/dist/scene/camera-solve.d.ts.map +1 -0
- package/dist/scene/camera-solve.js +288 -0
- package/dist/scene/camera-solve.js.map +1 -0
- package/dist/scene/capture-plan.d.ts +28 -0
- package/dist/scene/capture-plan.d.ts.map +1 -0
- package/dist/scene/capture-plan.js +79 -0
- package/dist/scene/capture-plan.js.map +1 -0
- package/dist/scene/capture.d.ts +21 -0
- package/dist/scene/capture.d.ts.map +1 -0
- package/dist/scene/capture.js +118 -0
- package/dist/scene/capture.js.map +1 -0
- package/dist/scene/image-colors.d.ts +25 -0
- package/dist/scene/image-colors.d.ts.map +1 -0
- package/dist/scene/image-colors.js +110 -0
- package/dist/scene/image-colors.js.map +1 -0
- package/dist/scene/image-rectify.d.ts +25 -0
- package/dist/scene/image-rectify.d.ts.map +1 -0
- package/dist/scene/image-rectify.js +82 -0
- package/dist/scene/image-rectify.js.map +1 -0
- package/dist/scene/image-sheet.d.ts +19 -0
- package/dist/scene/image-sheet.d.ts.map +1 -0
- package/dist/scene/image-sheet.js +118 -0
- package/dist/scene/image-sheet.js.map +1 -0
- package/dist/scene/least-squares.d.ts +31 -0
- package/dist/scene/least-squares.d.ts.map +1 -0
- package/dist/scene/least-squares.js +173 -0
- package/dist/scene/least-squares.js.map +1 -0
- package/dist/scene/numbers.d.ts +17 -0
- package/dist/scene/numbers.d.ts.map +1 -0
- package/dist/scene/numbers.js +39 -0
- package/dist/scene/numbers.js.map +1 -0
- package/dist/scene/placement.d.ts +43 -0
- package/dist/scene/placement.d.ts.map +1 -0
- package/dist/scene/placement.js +107 -0
- package/dist/scene/placement.js.map +1 -0
- package/dist/scene/raster.d.ts +18 -0
- package/dist/scene/raster.d.ts.map +1 -0
- package/dist/scene/raster.js +31 -0
- package/dist/scene/raster.js.map +1 -0
- package/dist/scene/scene-kit.d.ts +20 -0
- package/dist/scene/scene-kit.d.ts.map +1 -0
- package/dist/scene/scene-kit.js +74 -0
- package/dist/scene/scene-kit.js.map +1 -0
- package/dist/scene/scene-verbs.d.ts +4 -0
- package/dist/scene/scene-verbs.d.ts.map +1 -0
- package/dist/scene/scene-verbs.js +5 -0
- package/dist/scene/scene-verbs.js.map +1 -0
- package/dist/scene/seeded-random.d.ts +20 -0
- package/dist/scene/seeded-random.d.ts.map +1 -0
- package/dist/scene/seeded-random.js +42 -0
- package/dist/scene/seeded-random.js.map +1 -0
- package/dist/scene/spec-expression.d.ts +25 -0
- package/dist/scene/spec-expression.d.ts.map +1 -0
- package/dist/scene/spec-expression.js +234 -0
- package/dist/scene/spec-expression.js.map +1 -0
- package/dist/scene/static-server.d.ts +13 -0
- package/dist/scene/static-server.d.ts.map +1 -0
- package/dist/scene/static-server.js +99 -0
- package/dist/scene/static-server.js.map +1 -0
- package/open-design.json +2 -2
- package/package.json +3 -1
- package/scene-kit/starter/camera.js +10 -0
- package/scene-kit/starter/gobo.js +60 -0
- package/scene-kit/starter/index.html +23 -0
- package/scene-kit/starter/lib.js +84 -0
- package/scene-kit/starter/main.js +78 -0
- package/scene-kit/starter/scene.js +33 -0
- package/scene-kit/vendor/LICENSE +21 -0
- package/scene-kit/vendor/addons/geometries/RoundedBoxGeometry.js +216 -0
- package/scene-kit/vendor/addons/renderers/CSS3DRenderer.js +454 -0
- package/scene-kit/vendor/three.core.js +60586 -0
- package/scene-kit/vendor/three.module.js +19719 -0
- package/skills/12ui-design/SKILL.md +7 -1
- package/skills/12ui-design/scene.md +172 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import sharp from 'sharp';
|
|
2
|
+
export const readRgb = async (file) => {
|
|
3
|
+
const { data, info } = await sharp(file)
|
|
4
|
+
.removeAlpha()
|
|
5
|
+
.toColourspace('srgb')
|
|
6
|
+
.raw()
|
|
7
|
+
.toBuffer({ resolveWithObject: true });
|
|
8
|
+
if (info.channels !== 3)
|
|
9
|
+
throw new Error(`${file}: expected RGB pixels, decoded ${info.channels} channels`);
|
|
10
|
+
return { data, width: info.width, height: info.height };
|
|
11
|
+
};
|
|
12
|
+
const rawInput = (image) => ({
|
|
13
|
+
raw: { width: image.width, height: image.height, channels: 3 },
|
|
14
|
+
});
|
|
15
|
+
/** Lanczos resize to an exact size; the same size is returned untouched. */
|
|
16
|
+
export const resizeRgb = async (image, width, height) => {
|
|
17
|
+
if (image.width === width && image.height === height)
|
|
18
|
+
return image;
|
|
19
|
+
const { data, info } = await sharp(image.data, rawInput(image))
|
|
20
|
+
.resize(width, height, { fit: 'fill', kernel: 'lanczos3' })
|
|
21
|
+
.raw()
|
|
22
|
+
.toBuffer({ resolveWithObject: true });
|
|
23
|
+
return { data, width: info.width, height: info.height };
|
|
24
|
+
};
|
|
25
|
+
export const readRgbAt = async (file, width, height) => (resizeRgb(await readRgb(file), width, height));
|
|
26
|
+
/** PNG bytes of an in-memory image. */
|
|
27
|
+
export const encodePng = async (image) => (sharp(image.data, rawInput(image)).png().toBuffer());
|
|
28
|
+
export const writePng = async (image, file) => {
|
|
29
|
+
await sharp(image.data, rawInput(image)).png().toFile(file);
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=raster.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"raster.js","sourceRoot":"","sources":["../../src/scene/raster.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAS1B,MAAM,CAAC,MAAM,OAAO,GAAG,KAAK,EAAE,IAAY,EAAqB,EAAE;IAC/D,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC;SACrC,WAAW,EAAE;SACb,aAAa,CAAC,MAAM,CAAC;SACrB,GAAG,EAAE;SACL,QAAQ,CAAC,EAAE,iBAAiB,EAAE,IAAI,EAAE,CAAC,CAAC;IACzC,IAAI,IAAI,CAAC,QAAQ,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,kCAAkC,IAAI,CAAC,QAAQ,WAAW,CAAC,CAAC;IAC5G,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;AAC1D,CAAC,CAAC;AAEF,MAAM,QAAQ,GAAG,CAAC,KAAe,EAAE,EAAE,CAAC,CAAC;IACrC,GAAG,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,QAAQ,EAAE,CAAU,EAAE;CACxE,CAAC,CAAC;AAEH,4EAA4E;AAC5E,MAAM,CAAC,MAAM,SAAS,GAAG,KAAK,EAAE,KAAe,EAAE,KAAa,EAAE,MAAc,EAAqB,EAAE;IACnG,IAAI,KAAK,CAAC,KAAK,KAAK,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,MAAM;QAAE,OAAO,KAAK,CAAC;IACnE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC;SAC5D,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;SAC1D,GAAG,EAAE;SACL,QAAQ,CAAC,EAAE,iBAAiB,EAAE,IAAI,EAAE,CAAC,CAAC;IACzC,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;AAC1D,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,SAAS,GAAG,KAAK,EAAE,IAAY,EAAE,KAAa,EAAE,MAAc,EAAqB,EAAE,CAAC,CACjG,SAAS,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,CAC9C,CAAC;AAEF,uCAAuC;AACvC,MAAM,CAAC,MAAM,SAAS,GAAG,KAAK,EAAE,KAAe,EAAmB,EAAE,CAAC,CACnE,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CACpD,CAAC;AAEF,MAAM,CAAC,MAAM,QAAQ,GAAG,KAAK,EAAE,KAAe,EAAE,IAAY,EAAiB,EAAE;IAC7E,MAAM,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;AAC9D,CAAC,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The starter harness and the pinned three.js that ship inside the package
|
|
3
|
+
* (`scene-kit/`), and the one operation on them: copy both into a new scene
|
|
4
|
+
* folder. The payload is stored once in the package, never in agent homes, and
|
|
5
|
+
* nothing is fetched: three.js r186 and its MIT licence are bundled.
|
|
6
|
+
*/
|
|
7
|
+
export declare const sceneKitRoot: () => string;
|
|
8
|
+
/** Every file `init` writes, as [kit source, path inside the scene folder]. */
|
|
9
|
+
export declare const sceneKitFiles: (kitRoot?: string) => Promise<Array<[string, string]>>;
|
|
10
|
+
export declare class SceneInitConflict extends Error {
|
|
11
|
+
readonly conflicts: readonly string[];
|
|
12
|
+
constructor(directory: string, conflicts: readonly string[]);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Copies the starter and the vendored three.js into `directory`. Refuses before
|
|
16
|
+
* writing anything when any destination file already exists; the folder itself
|
|
17
|
+
* may exist (it usually holds the source image).
|
|
18
|
+
*/
|
|
19
|
+
export declare const initSceneFolder: (directory: string, kitRoot?: string) => Promise<string[]>;
|
|
20
|
+
//# sourceMappingURL=scene-kit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scene-kit.d.ts","sourceRoot":"","sources":["../../src/scene/scene-kit.ts"],"names":[],"mappings":"AAKA;;;;;GAKG;AAEH,eAAO,MAAM,YAAY,QAAO,MAAqE,CAAC;AAsBtG,+EAA+E;AAC/E,eAAO,MAAM,aAAa,GAAU,gBAAwB,KAAG,OAAO,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAO7F,CAAC;AAEF,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;gBAE1B,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,MAAM,EAAE;CAS5D;AAED;;;;GAIG;AACH,eAAO,MAAM,eAAe,GAC1B,WAAW,MAAM,EACjB,gBAAwB,KACvB,OAAO,CAAC,MAAM,EAAE,CAclB,CAAC"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { constants } from 'node:fs';
|
|
2
|
+
import { copyFile, mkdir, readdir, stat } from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
/**
|
|
6
|
+
* The starter harness and the pinned three.js that ship inside the package
|
|
7
|
+
* (`scene-kit/`), and the one operation on them: copy both into a new scene
|
|
8
|
+
* folder. The payload is stored once in the package, never in agent homes, and
|
|
9
|
+
* nothing is fetched: three.js r186 and its MIT licence are bundled.
|
|
10
|
+
*/
|
|
11
|
+
export const sceneKitRoot = () => fileURLToPath(new URL('../../scene-kit/', import.meta.url));
|
|
12
|
+
const listFiles = async (root, relative = '') => {
|
|
13
|
+
const entries = await readdir(path.join(root, relative), { withFileTypes: true });
|
|
14
|
+
const nested = await Promise.all(entries.map(async (entry) => {
|
|
15
|
+
const child = path.join(relative, entry.name);
|
|
16
|
+
if (entry.isDirectory())
|
|
17
|
+
return listFiles(root, child);
|
|
18
|
+
return entry.isFile() ? [child] : [];
|
|
19
|
+
}));
|
|
20
|
+
return nested.flat().sort();
|
|
21
|
+
};
|
|
22
|
+
const exists = async (target) => {
|
|
23
|
+
try {
|
|
24
|
+
await stat(target);
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
if (error.code === 'ENOENT')
|
|
29
|
+
return false;
|
|
30
|
+
throw error;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
/** Every file `init` writes, as [kit source, path inside the scene folder]. */
|
|
34
|
+
export const sceneKitFiles = async (kitRoot = sceneKitRoot()) => {
|
|
35
|
+
const starter = await listFiles(path.join(kitRoot, 'starter'));
|
|
36
|
+
const vendor = await listFiles(path.join(kitRoot, 'vendor'));
|
|
37
|
+
return [
|
|
38
|
+
...starter.map((file) => [path.join(kitRoot, 'starter', file), file]),
|
|
39
|
+
...vendor.map((file) => [path.join(kitRoot, 'vendor', file), path.join('vendor', file)]),
|
|
40
|
+
];
|
|
41
|
+
};
|
|
42
|
+
export class SceneInitConflict extends Error {
|
|
43
|
+
conflicts;
|
|
44
|
+
constructor(directory, conflicts) {
|
|
45
|
+
super(`${directory} already has ${conflicts.length === 1 ? 'a file' : 'files'} the starter would write:`
|
|
46
|
+
+ ` ${conflicts.slice(0, 8).join(', ')}${conflicts.length > 8 ? ', ...' : ''}.`
|
|
47
|
+
+ ' Nothing was written. Choose a new folder, or move those files away first.');
|
|
48
|
+
this.name = 'SceneInitConflict';
|
|
49
|
+
this.conflicts = conflicts;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Copies the starter and the vendored three.js into `directory`. Refuses before
|
|
54
|
+
* writing anything when any destination file already exists; the folder itself
|
|
55
|
+
* may exist (it usually holds the source image).
|
|
56
|
+
*/
|
|
57
|
+
export const initSceneFolder = async (directory, kitRoot = sceneKitRoot()) => {
|
|
58
|
+
const target = path.resolve(directory);
|
|
59
|
+
const files = await sceneKitFiles(kitRoot);
|
|
60
|
+
const conflicts = [];
|
|
61
|
+
for (const [, relative] of files) {
|
|
62
|
+
if (await exists(path.join(target, relative)))
|
|
63
|
+
conflicts.push(relative);
|
|
64
|
+
}
|
|
65
|
+
if (conflicts.length > 0)
|
|
66
|
+
throw new SceneInitConflict(target, conflicts);
|
|
67
|
+
for (const [source, relative] of files) {
|
|
68
|
+
const destination = path.join(target, relative);
|
|
69
|
+
await mkdir(path.dirname(destination), { recursive: true });
|
|
70
|
+
await copyFile(source, destination, constants.COPYFILE_EXCL);
|
|
71
|
+
}
|
|
72
|
+
return files.map(([, relative]) => relative);
|
|
73
|
+
};
|
|
74
|
+
//# sourceMappingURL=scene-kit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scene-kit.js","sourceRoot":"","sources":["../../src/scene/scene-kit.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** The `12ui scene` verbs, named once for the dispatcher and the capability record. */
|
|
2
|
+
export declare const SCENE_VERBS: readonly ["init", "solve", "ray", "project", "rect", "serve", "capture", "sheet", "blend", "colors", "rectify"];
|
|
3
|
+
export type SceneVerb = typeof SCENE_VERBS[number];
|
|
4
|
+
//# sourceMappingURL=scene-verbs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scene-verbs.d.ts","sourceRoot":"","sources":["../../src/scene/scene-verbs.ts"],"names":[],"mappings":"AAAA,uFAAuF;AACvF,eAAO,MAAM,WAAW,iHAEd,CAAC;AAEX,MAAM,MAAM,SAAS,GAAG,OAAO,WAAW,CAAC,MAAM,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scene-verbs.js","sourceRoot":"","sources":["../../src/scene/scene-verbs.ts"],"names":[],"mappings":"AAAA,uFAAuF;AACvF,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS;CAC5F,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A small seeded PRNG for the scene solvers' multistart.
|
|
3
|
+
*
|
|
4
|
+
* The camera and rectangle solves draw hundreds of random starting points and
|
|
5
|
+
* keep the best local minimum. Their answer must not depend on the draw (the
|
|
6
|
+
* Python solver gave the identical camera for five different seeds), but a
|
|
7
|
+
* fixed seed keeps two runs of the same spec byte-identical, which is what
|
|
8
|
+
* makes a parity fixture meaningful. sfc32 is fast, has a 128-bit state and no
|
|
9
|
+
* short cycles for any seed.
|
|
10
|
+
*/
|
|
11
|
+
export type SeededRandom = {
|
|
12
|
+
/** Uniform in [0, 1). */
|
|
13
|
+
next: () => number;
|
|
14
|
+
/** Uniform in [low, high). */
|
|
15
|
+
uniform: (low: number, high: number) => number;
|
|
16
|
+
/** Standard normal (Box-Muller, the spare value is kept for the next call). */
|
|
17
|
+
normal: () => number;
|
|
18
|
+
};
|
|
19
|
+
export declare const seededRandom: (seed: number) => SeededRandom;
|
|
20
|
+
//# sourceMappingURL=seeded-random.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seeded-random.d.ts","sourceRoot":"","sources":["../../src/scene/seeded-random.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,yBAAyB;IACzB,IAAI,EAAE,MAAM,MAAM,CAAC;IACnB,8BAA8B;IAC9B,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IAC/C,+EAA+E;IAC/E,MAAM,EAAE,MAAM,MAAM,CAAC;CACtB,CAAC;AAEF,eAAO,MAAM,YAAY,GAAI,MAAM,MAAM,KAAG,YAmC3C,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export const seededRandom = (seed) => {
|
|
2
|
+
let a = 0x9e3779b9 ^ (seed >>> 0);
|
|
3
|
+
let b = 0x243f6a88 ^ Math.imul(seed >>> 0, 0x85ebca6b);
|
|
4
|
+
let c = 0xb7e15162;
|
|
5
|
+
let d = 1;
|
|
6
|
+
const next = () => {
|
|
7
|
+
a >>>= 0;
|
|
8
|
+
b >>>= 0;
|
|
9
|
+
c >>>= 0;
|
|
10
|
+
d >>>= 0;
|
|
11
|
+
const t = (a + b + d) >>> 0;
|
|
12
|
+
d = (d + 1) >>> 0;
|
|
13
|
+
a = b ^ (b >>> 9);
|
|
14
|
+
b = (c + (c << 3)) >>> 0;
|
|
15
|
+
c = (c << 21) | (c >>> 11);
|
|
16
|
+
c = (c + t) >>> 0;
|
|
17
|
+
return t / 4294967296;
|
|
18
|
+
};
|
|
19
|
+
// Discard the warm-up outputs so nearby seeds decorrelate.
|
|
20
|
+
for (let index = 0; index < 16; index += 1)
|
|
21
|
+
next();
|
|
22
|
+
let spare = null;
|
|
23
|
+
return {
|
|
24
|
+
next,
|
|
25
|
+
uniform: (low, high) => low + (high - low) * next(),
|
|
26
|
+
normal: () => {
|
|
27
|
+
if (spare !== null) {
|
|
28
|
+
const value = spare;
|
|
29
|
+
spare = null;
|
|
30
|
+
return value;
|
|
31
|
+
}
|
|
32
|
+
let u = 0;
|
|
33
|
+
while (u <= Number.EPSILON)
|
|
34
|
+
u = next();
|
|
35
|
+
const v = next();
|
|
36
|
+
const radius = Math.sqrt(-2 * Math.log(u));
|
|
37
|
+
spare = radius * Math.sin(2 * Math.PI * v);
|
|
38
|
+
return radius * Math.cos(2 * Math.PI * v);
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
//# sourceMappingURL=seeded-random.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seeded-random.js","sourceRoot":"","sources":["../../src/scene/seeded-random.ts"],"names":[],"mappings":"AAmBA,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,IAAY,EAAgB,EAAE;IACzD,IAAI,CAAC,GAAG,UAAU,GAAG,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC;IAClC,IAAI,CAAC,GAAG,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,EAAE,UAAU,CAAC,CAAC;IACvD,IAAI,CAAC,GAAG,UAAU,CAAC;IACnB,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,MAAM,IAAI,GAAG,GAAW,EAAE;QACxB,CAAC,MAAM,CAAC,CAAC;QAAC,CAAC,MAAM,CAAC,CAAC;QAAC,CAAC,MAAM,CAAC,CAAC;QAAC,CAAC,MAAM,CAAC,CAAC;QACvC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;QAC5B,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;QAClB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;QAClB,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QACzB,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;QAC3B,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;QAClB,OAAO,CAAC,GAAG,UAAU,CAAC;IACxB,CAAC,CAAC;IACF,2DAA2D;IAC3D,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,EAAE,EAAE,KAAK,IAAI,CAAC;QAAE,IAAI,EAAE,CAAC;IACnD,IAAI,KAAK,GAAkB,IAAI,CAAC;IAChC,OAAO;QACL,IAAI;QACJ,OAAO,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,GAAG,IAAI,EAAE;QACnD,MAAM,EAAE,GAAG,EAAE;YACX,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;gBACnB,MAAM,KAAK,GAAG,KAAK,CAAC;gBACpB,KAAK,GAAG,IAAI,CAAC;gBACb,OAAO,KAAK,CAAC;YACf,CAAC;YACD,IAAI,CAAC,GAAG,CAAC,CAAC;YACV,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO;gBAAE,CAAC,GAAG,IAAI,EAAE,CAAC;YACvC,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC;YACjB,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YAC3C,KAAK,GAAG,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;YAC3C,OAAO,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;QAC5C,CAAC;KACF,CAAC;AACJ,CAAC,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A safe compiler for the maths a camera spec writes in its world coordinates.
|
|
3
|
+
*
|
|
4
|
+
* Specs written for the Python solver use Python expression syntax ("D/2",
|
|
5
|
+
* "-0.5*cos(t)+D/2*sin(t)", "W**2"), which Python evaluated with `eval`. This
|
|
6
|
+
* accepts exactly the arithmetic subset: numbers, the spec's free names, the
|
|
7
|
+
* constants pi e tau inf nan, `+ - * / // % **`, unary signs, parentheses, and
|
|
8
|
+
* calls to the math functions below. Anything else (attribute access, strings,
|
|
9
|
+
* subscripts, unknown names) is refused with the landmark and the offending
|
|
10
|
+
* token named, before any solve starts.
|
|
11
|
+
*/
|
|
12
|
+
export type CompiledExpression = (values: ArrayLike<number>) => number;
|
|
13
|
+
export declare class SpecExpressionError extends Error {
|
|
14
|
+
constructor(message: string);
|
|
15
|
+
}
|
|
16
|
+
export declare const SPEC_MATH_FUNCTIONS: readonly string[];
|
|
17
|
+
/**
|
|
18
|
+
* Compiles one coordinate expression. `names` are the spec's free names in the
|
|
19
|
+
* order the solver passes their values; a free name shadows a constant or
|
|
20
|
+
* function of the same spelling, as it did in the Python evaluator.
|
|
21
|
+
*/
|
|
22
|
+
export declare const compileSpecExpression: (source: string, names: readonly string[], where: string) => CompiledExpression;
|
|
23
|
+
/** A spec coordinate: a JSON number, or an expression string. */
|
|
24
|
+
export declare const compileSpecCoordinate: (value: unknown, names: readonly string[], where: string) => CompiledExpression;
|
|
25
|
+
//# sourceMappingURL=spec-expression.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spec-expression.d.ts","sourceRoot":"","sources":["../../src/scene/spec-expression.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,MAAM,kBAAkB,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC,KAAK,MAAM,CAAC;AAEvE,qBAAa,mBAAoB,SAAQ,KAAK;gBAChC,OAAO,EAAE,MAAM;CAI5B;AAqDD,eAAO,MAAM,mBAAmB,EAAE,SAAS,MAAM,EAAgC,CAAC;AA2ClF;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,GAChC,QAAQ,MAAM,EACd,OAAO,SAAS,MAAM,EAAE,EACxB,OAAO,MAAM,KACZ,kBAsHF,CAAC;AAEF,iEAAiE;AACjE,eAAO,MAAM,qBAAqB,GAChC,OAAO,OAAO,EACd,OAAO,SAAS,MAAM,EAAE,EACxB,OAAO,MAAM,KACZ,kBAIF,CAAC"}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A safe compiler for the maths a camera spec writes in its world coordinates.
|
|
3
|
+
*
|
|
4
|
+
* Specs written for the Python solver use Python expression syntax ("D/2",
|
|
5
|
+
* "-0.5*cos(t)+D/2*sin(t)", "W**2"), which Python evaluated with `eval`. This
|
|
6
|
+
* accepts exactly the arithmetic subset: numbers, the spec's free names, the
|
|
7
|
+
* constants pi e tau inf nan, `+ - * / // % **`, unary signs, parentheses, and
|
|
8
|
+
* calls to the math functions below. Anything else (attribute access, strings,
|
|
9
|
+
* subscripts, unknown names) is refused with the landmark and the offending
|
|
10
|
+
* token named, before any solve starts.
|
|
11
|
+
*/
|
|
12
|
+
export class SpecExpressionError extends Error {
|
|
13
|
+
constructor(message) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.name = 'SpecExpressionError';
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
const pythonMod = (a, b) => {
|
|
19
|
+
if (b === 0)
|
|
20
|
+
return Number.NaN;
|
|
21
|
+
const r = a % b;
|
|
22
|
+
return r !== 0 && (r < 0) !== (b < 0) ? r + b : r;
|
|
23
|
+
};
|
|
24
|
+
const MATH_FUNCTIONS = {
|
|
25
|
+
sin: { arity: [1, 1], apply: Math.sin },
|
|
26
|
+
cos: { arity: [1, 1], apply: Math.cos },
|
|
27
|
+
tan: { arity: [1, 1], apply: Math.tan },
|
|
28
|
+
asin: { arity: [1, 1], apply: Math.asin },
|
|
29
|
+
acos: { arity: [1, 1], apply: Math.acos },
|
|
30
|
+
atan: { arity: [1, 1], apply: Math.atan },
|
|
31
|
+
atan2: { arity: [2, 2], apply: Math.atan2 },
|
|
32
|
+
sinh: { arity: [1, 1], apply: Math.sinh },
|
|
33
|
+
cosh: { arity: [1, 1], apply: Math.cosh },
|
|
34
|
+
tanh: { arity: [1, 1], apply: Math.tanh },
|
|
35
|
+
asinh: { arity: [1, 1], apply: Math.asinh },
|
|
36
|
+
acosh: { arity: [1, 1], apply: Math.acosh },
|
|
37
|
+
atanh: { arity: [1, 1], apply: Math.atanh },
|
|
38
|
+
exp: { arity: [1, 1], apply: Math.exp },
|
|
39
|
+
exp2: { arity: [1, 1], apply: (x) => 2 ** x },
|
|
40
|
+
expm1: { arity: [1, 1], apply: Math.expm1 },
|
|
41
|
+
log: { arity: [1, 2], apply: (x, base) => (base === undefined ? Math.log(x) : Math.log(x) / Math.log(base)) },
|
|
42
|
+
log10: { arity: [1, 1], apply: Math.log10 },
|
|
43
|
+
log2: { arity: [1, 1], apply: Math.log2 },
|
|
44
|
+
log1p: { arity: [1, 1], apply: Math.log1p },
|
|
45
|
+
sqrt: { arity: [1, 1], apply: Math.sqrt },
|
|
46
|
+
cbrt: { arity: [1, 1], apply: Math.cbrt },
|
|
47
|
+
pow: { arity: [2, 2], apply: (x, y) => x ** y },
|
|
48
|
+
fabs: { arity: [1, 1], apply: Math.abs },
|
|
49
|
+
floor: { arity: [1, 1], apply: Math.floor },
|
|
50
|
+
ceil: { arity: [1, 1], apply: Math.ceil },
|
|
51
|
+
trunc: { arity: [1, 1], apply: Math.trunc },
|
|
52
|
+
degrees: { arity: [1, 1], apply: (x) => (x * 180) / Math.PI },
|
|
53
|
+
radians: { arity: [1, 1], apply: (x) => (x * Math.PI) / 180 },
|
|
54
|
+
hypot: { arity: [1, 64], apply: Math.hypot },
|
|
55
|
+
copysign: { arity: [2, 2], apply: (x, y) => (Object.is(Math.sign(y), -0) || y < 0 ? -Math.abs(x) : Math.abs(x)) },
|
|
56
|
+
fmod: { arity: [2, 2], apply: (x, y) => x % y },
|
|
57
|
+
};
|
|
58
|
+
const CONSTANTS = {
|
|
59
|
+
pi: Math.PI,
|
|
60
|
+
e: Math.E,
|
|
61
|
+
tau: 2 * Math.PI,
|
|
62
|
+
inf: Number.POSITIVE_INFINITY,
|
|
63
|
+
nan: Number.NaN,
|
|
64
|
+
};
|
|
65
|
+
export const SPEC_MATH_FUNCTIONS = Object.keys(MATH_FUNCTIONS);
|
|
66
|
+
const tokenize = (source, where) => {
|
|
67
|
+
const tokens = [];
|
|
68
|
+
let index = 0;
|
|
69
|
+
while (index < source.length) {
|
|
70
|
+
const rest = source.slice(index);
|
|
71
|
+
const space = /^\s+/u.exec(rest);
|
|
72
|
+
if (space) {
|
|
73
|
+
index += space[0].length;
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
const number = /^(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?/u.exec(rest);
|
|
77
|
+
if (number) {
|
|
78
|
+
tokens.push({ kind: 'number', value: Number(number[0]), text: number[0] });
|
|
79
|
+
index += number[0].length;
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
const name = /^[A-Za-z_][A-Za-z0-9_]*/u.exec(rest);
|
|
83
|
+
if (name) {
|
|
84
|
+
tokens.push({ kind: 'name', text: name[0] });
|
|
85
|
+
index += name[0].length;
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
const op = /^(?:\*\*|\/\/|[-+*/%(),])/u.exec(rest);
|
|
89
|
+
if (op) {
|
|
90
|
+
tokens.push({ kind: 'op', text: op[0] });
|
|
91
|
+
index += op[0].length;
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
throw new SpecExpressionError(`${where}: "${source}" uses ${JSON.stringify(rest[0])}, which a spec expression cannot contain.`
|
|
95
|
+
+ ' Use numbers, the free names, + - * / // % **, parentheses and math functions.');
|
|
96
|
+
}
|
|
97
|
+
return tokens;
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* Compiles one coordinate expression. `names` are the spec's free names in the
|
|
101
|
+
* order the solver passes their values; a free name shadows a constant or
|
|
102
|
+
* function of the same spelling, as it did in the Python evaluator.
|
|
103
|
+
*/
|
|
104
|
+
export const compileSpecExpression = (source, names, where) => {
|
|
105
|
+
const tokens = tokenize(source, where);
|
|
106
|
+
let position = 0;
|
|
107
|
+
const peek = () => tokens[position];
|
|
108
|
+
const isOp = (text) => {
|
|
109
|
+
const token = peek();
|
|
110
|
+
return token?.kind === 'op' && token.text === text;
|
|
111
|
+
};
|
|
112
|
+
const fail = (detail) => {
|
|
113
|
+
throw new SpecExpressionError(`${where}: "${source}" ${detail}`);
|
|
114
|
+
};
|
|
115
|
+
const expect = (text) => {
|
|
116
|
+
if (!isOp(text))
|
|
117
|
+
fail(`expects ${JSON.stringify(text)} at ${describe(peek())}`);
|
|
118
|
+
position += 1;
|
|
119
|
+
};
|
|
120
|
+
const describe = (token) => (token ? JSON.stringify(token.text) : 'the end');
|
|
121
|
+
const expression = () => {
|
|
122
|
+
let left = term();
|
|
123
|
+
while (isOp('+') || isOp('-')) {
|
|
124
|
+
const op = peek().text;
|
|
125
|
+
position += 1;
|
|
126
|
+
const right = term();
|
|
127
|
+
const l = left;
|
|
128
|
+
left = op === '+' ? (v) => l(v) + right(v) : (v) => l(v) - right(v);
|
|
129
|
+
}
|
|
130
|
+
return left;
|
|
131
|
+
};
|
|
132
|
+
const term = () => {
|
|
133
|
+
let left = unary();
|
|
134
|
+
while (isOp('*') || isOp('/') || isOp('//') || isOp('%')) {
|
|
135
|
+
const op = peek().text;
|
|
136
|
+
position += 1;
|
|
137
|
+
const right = unary();
|
|
138
|
+
const l = left;
|
|
139
|
+
left = op === '*' ? (v) => l(v) * right(v)
|
|
140
|
+
: op === '/' ? (v) => l(v) / right(v)
|
|
141
|
+
: op === '//' ? (v) => Math.floor(l(v) / right(v))
|
|
142
|
+
: (v) => pythonMod(l(v), right(v));
|
|
143
|
+
}
|
|
144
|
+
return left;
|
|
145
|
+
};
|
|
146
|
+
const unary = () => {
|
|
147
|
+
if (isOp('-')) {
|
|
148
|
+
position += 1;
|
|
149
|
+
const operand = unary();
|
|
150
|
+
return (v) => -operand(v);
|
|
151
|
+
}
|
|
152
|
+
if (isOp('+')) {
|
|
153
|
+
position += 1;
|
|
154
|
+
return unary();
|
|
155
|
+
}
|
|
156
|
+
return power();
|
|
157
|
+
};
|
|
158
|
+
// Python: power ::= primary ["**" u_expr], so -2**2 is -4 and 2**-1 is 0.5.
|
|
159
|
+
const power = () => {
|
|
160
|
+
const base = atom();
|
|
161
|
+
if (!isOp('**'))
|
|
162
|
+
return base;
|
|
163
|
+
position += 1;
|
|
164
|
+
const exponent = unary();
|
|
165
|
+
return (v) => base(v) ** exponent(v);
|
|
166
|
+
};
|
|
167
|
+
const atom = () => {
|
|
168
|
+
const token = peek();
|
|
169
|
+
if (!token)
|
|
170
|
+
return fail('ends where a value is expected');
|
|
171
|
+
if (token.kind === 'number') {
|
|
172
|
+
position += 1;
|
|
173
|
+
const value = token.value;
|
|
174
|
+
return () => value;
|
|
175
|
+
}
|
|
176
|
+
if (isOp('(')) {
|
|
177
|
+
position += 1;
|
|
178
|
+
const inner = expression();
|
|
179
|
+
expect(')');
|
|
180
|
+
return inner;
|
|
181
|
+
}
|
|
182
|
+
if (token.kind === 'name') {
|
|
183
|
+
position += 1;
|
|
184
|
+
const freeIndex = names.indexOf(token.text);
|
|
185
|
+
if (freeIndex >= 0) {
|
|
186
|
+
if (isOp('('))
|
|
187
|
+
fail(`calls the free name ${JSON.stringify(token.text)} as a function`);
|
|
188
|
+
return (v) => v[freeIndex];
|
|
189
|
+
}
|
|
190
|
+
const fn = Object.hasOwn(MATH_FUNCTIONS, token.text) ? MATH_FUNCTIONS[token.text] : undefined;
|
|
191
|
+
if (fn) {
|
|
192
|
+
if (!isOp('('))
|
|
193
|
+
fail(`uses the function ${JSON.stringify(token.text)} without calling it`);
|
|
194
|
+
position += 1;
|
|
195
|
+
const args = [];
|
|
196
|
+
if (!isOp(')')) {
|
|
197
|
+
args.push(expression());
|
|
198
|
+
while (isOp(',')) {
|
|
199
|
+
position += 1;
|
|
200
|
+
if (isOp(')'))
|
|
201
|
+
break;
|
|
202
|
+
args.push(expression());
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
expect(')');
|
|
206
|
+
const [min, max] = fn.arity;
|
|
207
|
+
if (args.length < min || args.length > max) {
|
|
208
|
+
fail(`calls ${token.text} with ${args.length} argument(s)`);
|
|
209
|
+
}
|
|
210
|
+
return (v) => fn.apply(...args.map((arg) => arg(v)));
|
|
211
|
+
}
|
|
212
|
+
if (Object.hasOwn(CONSTANTS, token.text)) {
|
|
213
|
+
const value = CONSTANTS[token.text];
|
|
214
|
+
return () => value;
|
|
215
|
+
}
|
|
216
|
+
return fail(`names ${JSON.stringify(token.text)}, which is neither a free name`
|
|
217
|
+
+ ` (${names.length ? names.join(', ') : 'none declared'}) nor a math function or constant.`);
|
|
218
|
+
}
|
|
219
|
+
return fail(`has ${describe(token)} where a value is expected`);
|
|
220
|
+
};
|
|
221
|
+
const compiled = expression();
|
|
222
|
+
if (position < tokens.length)
|
|
223
|
+
fail(`has an unexpected ${describe(peek())}`);
|
|
224
|
+
return compiled;
|
|
225
|
+
};
|
|
226
|
+
/** A spec coordinate: a JSON number, or an expression string. */
|
|
227
|
+
export const compileSpecCoordinate = (value, names, where) => {
|
|
228
|
+
if (typeof value === 'number' && Number.isFinite(value))
|
|
229
|
+
return () => value;
|
|
230
|
+
if (typeof value === 'string')
|
|
231
|
+
return compileSpecExpression(value, names, where);
|
|
232
|
+
throw new SpecExpressionError(`${where}: ${JSON.stringify(value)} is not a number or an expression string`);
|
|
233
|
+
};
|
|
234
|
+
//# sourceMappingURL=spec-expression.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spec-expression.js","sourceRoot":"","sources":["../../src/scene/spec-expression.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare const contentTypeFor: (file: string) => string;
|
|
2
|
+
/** The file a request path names inside `root`, or null when it would leave the folder. */
|
|
3
|
+
export declare const resolveInsideRoot: (root: string, requestPath: string) => string | null;
|
|
4
|
+
export type StaticServer = {
|
|
5
|
+
url: string;
|
|
6
|
+
port: number;
|
|
7
|
+
close: () => Promise<void>;
|
|
8
|
+
};
|
|
9
|
+
export declare const startStaticServer: (args: {
|
|
10
|
+
directory: string;
|
|
11
|
+
port: number;
|
|
12
|
+
}) => Promise<StaticServer>;
|
|
13
|
+
//# sourceMappingURL=static-server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"static-server.d.ts","sourceRoot":"","sources":["../../src/scene/static-server.ts"],"names":[],"mappings":"AAgCA,eAAO,MAAM,cAAc,GAAI,MAAM,MAAM,KAAG,MAE7C,CAAC;AAEF,2FAA2F;AAC3F,eAAO,MAAM,iBAAiB,GAAI,MAAM,MAAM,EAAE,aAAa,MAAM,KAAG,MAAM,GAAG,IAW9E,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAC;AAErF,eAAO,MAAM,iBAAiB,GAAU,MAAM;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,KAAG,OAAO,CAAC,YAAY,CA8CvG,CAAC"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { createReadStream } from 'node:fs';
|
|
2
|
+
import { realpath, stat } from 'node:fs/promises';
|
|
3
|
+
import http from 'node:http';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
/**
|
|
6
|
+
* A static file server for one scene folder, bound to 127.0.0.1 only. It
|
|
7
|
+
* serves nothing outside the folder (a `..` or a symlink out of it is a 403)
|
|
8
|
+
* and never caches, so every capture sees the files as they are now.
|
|
9
|
+
*/
|
|
10
|
+
const CONTENT_TYPES = {
|
|
11
|
+
'.html': 'text/html; charset=utf-8',
|
|
12
|
+
'.js': 'text/javascript; charset=utf-8',
|
|
13
|
+
'.mjs': 'text/javascript; charset=utf-8',
|
|
14
|
+
'.css': 'text/css; charset=utf-8',
|
|
15
|
+
'.png': 'image/png',
|
|
16
|
+
'.jpg': 'image/jpeg',
|
|
17
|
+
'.jpeg': 'image/jpeg',
|
|
18
|
+
'.webp': 'image/webp',
|
|
19
|
+
'.svg': 'image/svg+xml',
|
|
20
|
+
'.json': 'application/json',
|
|
21
|
+
'.glb': 'model/gltf-binary',
|
|
22
|
+
'.gltf': 'model/gltf+json',
|
|
23
|
+
'.bin': 'application/octet-stream',
|
|
24
|
+
'.ttf': 'font/ttf',
|
|
25
|
+
'.otf': 'font/otf',
|
|
26
|
+
'.woff': 'font/woff',
|
|
27
|
+
'.woff2': 'font/woff2',
|
|
28
|
+
};
|
|
29
|
+
export const contentTypeFor = (file) => (CONTENT_TYPES[path.extname(file).toLowerCase()] ?? 'application/octet-stream');
|
|
30
|
+
/** The file a request path names inside `root`, or null when it would leave the folder. */
|
|
31
|
+
export const resolveInsideRoot = (root, requestPath) => {
|
|
32
|
+
let decoded;
|
|
33
|
+
try {
|
|
34
|
+
decoded = decodeURIComponent(requestPath.split('?')[0].split('#')[0]);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
if (decoded.includes('\0'))
|
|
40
|
+
return null;
|
|
41
|
+
const resolved = path.resolve(root, `.${path.posix.normalize(`/${decoded}`)}`);
|
|
42
|
+
const withIndex = decoded.endsWith('/') ? path.join(resolved, 'index.html') : resolved;
|
|
43
|
+
return withIndex === root || withIndex.startsWith(`${root}${path.sep}`) ? withIndex : null;
|
|
44
|
+
};
|
|
45
|
+
export const startStaticServer = async (args) => {
|
|
46
|
+
const root = await realpath(path.resolve(args.directory));
|
|
47
|
+
const server = http.createServer((request, response) => {
|
|
48
|
+
const refuse = (status) => {
|
|
49
|
+
response.writeHead(status, { 'cache-control': 'no-store' });
|
|
50
|
+
response.end();
|
|
51
|
+
};
|
|
52
|
+
if (request.method !== 'GET' && request.method !== 'HEAD')
|
|
53
|
+
return refuse(405);
|
|
54
|
+
const candidate = resolveInsideRoot(root, request.url ?? '/');
|
|
55
|
+
if (!candidate)
|
|
56
|
+
return refuse(403);
|
|
57
|
+
void (async () => {
|
|
58
|
+
let file = candidate;
|
|
59
|
+
try {
|
|
60
|
+
if ((await stat(file)).isDirectory())
|
|
61
|
+
file = path.join(file, 'index.html');
|
|
62
|
+
const real = await realpath(file);
|
|
63
|
+
if (real !== root && !real.startsWith(`${root}${path.sep}`))
|
|
64
|
+
return refuse(403);
|
|
65
|
+
const info = await stat(real);
|
|
66
|
+
if (!info.isFile())
|
|
67
|
+
return refuse(404);
|
|
68
|
+
response.writeHead(200, {
|
|
69
|
+
'content-type': contentTypeFor(real),
|
|
70
|
+
'content-length': info.size,
|
|
71
|
+
'cache-control': 'no-store',
|
|
72
|
+
});
|
|
73
|
+
if (request.method === 'HEAD')
|
|
74
|
+
return response.end();
|
|
75
|
+
createReadStream(real).on('error', () => response.destroy()).pipe(response);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
refuse(404);
|
|
79
|
+
}
|
|
80
|
+
})();
|
|
81
|
+
});
|
|
82
|
+
await new Promise((resolve, reject) => {
|
|
83
|
+
server.once('error', reject);
|
|
84
|
+
server.listen(args.port, '127.0.0.1', () => {
|
|
85
|
+
server.off('error', reject);
|
|
86
|
+
resolve();
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
const port = server.address().port;
|
|
90
|
+
return {
|
|
91
|
+
url: `http://127.0.0.1:${port}/`,
|
|
92
|
+
port,
|
|
93
|
+
close: () => new Promise((resolve) => {
|
|
94
|
+
server.close(() => resolve());
|
|
95
|
+
server.closeAllConnections();
|
|
96
|
+
}),
|
|
97
|
+
};
|
|
98
|
+
};
|
|
99
|
+
//# sourceMappingURL=static-server.js.map
|