Transparent multiplayer!?!
This commit is contained in:
parent
bdafb77667
commit
3d6de0b9c8
|
@ -7,3 +7,4 @@
|
|||
/node_modules
|
||||
/src.ts/
|
||||
/src/gen/
|
||||
/tsconfig.tsbuildinfo
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
; Create a dataspace entity, and register it with the gatekeeper with name `"syndicate"` and an
|
||||
; empty secret key:
|
||||
let ?ds = dataspace
|
||||
<MainDataspace $ds>
|
||||
<bind "syndicate" #x"" $ds>
|
||||
|
||||
? <nginx-command ?command> [
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
let ?sceneDs = dataspace
|
||||
|
||||
<require-service <config-watcher "scene" { config: $sceneDs }>>
|
||||
|
||||
? <MainDataspace ?ds> [
|
||||
$ds [
|
||||
<SceneHandle $sceneDs>
|
||||
]
|
||||
]
|
|
@ -22,11 +22,11 @@
|
|||
"devDependencies": {
|
||||
"@preserves/core": "*",
|
||||
"@preserves/schema": "*",
|
||||
"@rollup/plugin-node-resolve": "15.0",
|
||||
"@syndicate-lang/ts-plugin": "*",
|
||||
"@syndicate-lang/tsc": "*",
|
||||
"@rollup/plugin-node-resolve": "15.0",
|
||||
"rollup-plugin-sourcemaps": "0.6",
|
||||
"rollup": "3.8",
|
||||
"rollup-plugin-sourcemaps": "0.6",
|
||||
"tslib": "2.4",
|
||||
"typescript": "4.9",
|
||||
"typescript-language-server": "3.0"
|
||||
|
|
|
@ -1 +1,32 @@
|
|||
version 1 .
|
||||
|
||||
Sprite = <sprite @name string @shape Shape> .
|
||||
|
||||
Shape = Sphere / Box / Light / Ground / Scale / Move / Rotate / @many [Shape ...] / Texture / Color / Name / Floor .
|
||||
|
||||
Sphere = <sphere> .
|
||||
Box = <box> .
|
||||
Light = <hemispheric-light @v Vector3> .
|
||||
Ground = <ground @size Vector2> .
|
||||
|
||||
Vector2 = <v @x double @y double> .
|
||||
Vector3 = <v @x double @y double @z double> .
|
||||
Quaternion = <q @a double @b double @c double @d double> .
|
||||
|
||||
Scale = <scale @v Vector3 @shape Shape> .
|
||||
Move = <move @v Vector3 @shape Shape> .
|
||||
Rotate = @euler <rotate @v Vector3 @shape Shape> / @quaternion <rotate @q Quaternion @shape Shape> .
|
||||
|
||||
Texture =
|
||||
/ @simple <texture @path string @shape Shape>
|
||||
/ @uv <texture @path string @scale Vector2 @offset Vector2 @shape Shape>
|
||||
.
|
||||
|
||||
Color =
|
||||
/ @opaque <color @r double @g double @b double @shape Shape>
|
||||
/ @transparent <color @r double @g double @b double @alpha double @shape Shape>
|
||||
.
|
||||
|
||||
Name = <name @base string @shape Shape> .
|
||||
|
||||
Floor = <floor @shape Shape> .
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
For scene files.
|
|
@ -0,0 +1,19 @@
|
|||
<sprite "light" <hemispheric-light <v 0.0 1.0 0.0>>>
|
||||
|
||||
<sprite "ground"
|
||||
<texture
|
||||
"textures/grass-256x256.jpg"
|
||||
<v 0.1 0.1>
|
||||
<v 0.0 0.0>
|
||||
<floor <ground <v 30.0 30.0>>>>>
|
||||
|
||||
<sprite "box"
|
||||
<move <v 1.0 1.5 3.0>
|
||||
<texture "textures/grass-256x256.jpg" <floor <box>>>>>
|
||||
|
||||
<sprite "box2"
|
||||
<move <v -1.0 0.5 3.0>
|
||||
<floor
|
||||
<color 1.0 0.0 0.0 0.5 <box>>>>>
|
||||
|
||||
[]
|
|
@ -1,11 +1,11 @@
|
|||
import {
|
||||
AbstractMesh,
|
||||
DualShockPad,
|
||||
Engine,
|
||||
FreeCamera,
|
||||
FreeCameraGamepadInput,
|
||||
Gamepad as b_Gamepad,
|
||||
Mesh,
|
||||
Quaternion,
|
||||
Scene,
|
||||
Vector3,
|
||||
} from '@babylonjs/core/Legacy/legacy';
|
||||
|
@ -58,16 +58,17 @@ if ((navigator as any).oscpu?.startsWith('Linux')) {
|
|||
};
|
||||
}
|
||||
|
||||
export type CreateScene = (canvas: HTMLCanvasElement, engine: Engine) => Promise<{
|
||||
export type CreateScene = (canvas: HTMLCanvasElement, engine: Engine) => Promise<CreatedScene>;
|
||||
export type CreatedScene = {
|
||||
scene: Scene,
|
||||
floorMeshes: Mesh[]
|
||||
}>;
|
||||
floorMeshes: () => Mesh[]
|
||||
};
|
||||
|
||||
export async function startEngine(
|
||||
createScene: CreateScene,
|
||||
initialPos = new Vector3(0, 1.6, 0),
|
||||
initialRotation = new Vector3(0, 0, 0).scaleInPlace(2 * Math.PI),
|
||||
): Promise<void> {
|
||||
): Promise<Scene> {
|
||||
const canvas = document.getElementById("renderCanvas") as HTMLCanvasElement;
|
||||
const engine = new Engine(canvas, true);
|
||||
const { scene, floorMeshes } = await createScene(canvas, engine);
|
||||
|
@ -99,12 +100,6 @@ export async function startEngine(
|
|||
camera.applyGravity = true;
|
||||
camera.ellipsoid = new Vector3(0.25, 0.8, 0.25);
|
||||
|
||||
scene.getNodes().forEach(n => {
|
||||
if (n instanceof AbstractMesh) {
|
||||
n.checkCollisions = true;
|
||||
}
|
||||
});
|
||||
|
||||
const enableVR = () => {
|
||||
if (xrAvailable) {
|
||||
xr.baseExperience.enterXRAsync('immersive-vr', 'local').then(() => {
|
||||
|
@ -117,6 +112,7 @@ export async function startEngine(
|
|||
document.body.onclick = enableVR;
|
||||
|
||||
let leanBase: { position: Vector3 } | null = null;
|
||||
let recenterBase: { rotation: Quaternion } | null = null;
|
||||
|
||||
engine.runRenderLoop(() => {
|
||||
|
||||
|
@ -142,9 +138,10 @@ export async function startEngine(
|
|||
|
||||
if (sm && latch(gp, 0)) {
|
||||
const ray = xr.baseExperience.camera.getForwardRay();
|
||||
const hit = scene.pickWithRay(ray, m => floorMeshes.indexOf(m as any) !== -1);
|
||||
const meshes = floorMeshes();
|
||||
const hit = scene.pickWithRay(ray, m => meshes.indexOf(m as any) !== -1);
|
||||
if (hit !== null) {
|
||||
if (floorMeshes.indexOf(hit.pickedMesh as any) !== -1) {
|
||||
if (meshes.indexOf(hit.pickedMesh as any) !== -1) {
|
||||
if (hit.pickedPoint !== null) {
|
||||
xr.baseExperience.camera.position =
|
||||
hit.pickedPoint.add(new Vector3(0, 1.6, 0));
|
||||
|
@ -172,8 +169,14 @@ export async function startEngine(
|
|||
xr.baseExperience.camera.rotationQuaternion.copyFrom(r.toQuaternion());
|
||||
}
|
||||
}
|
||||
if (sm && latch(gp, 4)) {
|
||||
xr.baseExperience.camera.rotationQuaternion.copyFrom(initialRotation.toQuaternion());
|
||||
|
||||
if (sm) {
|
||||
if (latch(gp, 4)) {
|
||||
recenterBase = { rotation: xr.baseExperience.camera.rotationQuaternion.clone() };
|
||||
}
|
||||
if (buttonDown[4] && recenterBase) {
|
||||
xr.baseExperience.camera.rotationQuaternion.copyFrom(recenterBase.rotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -181,4 +184,6 @@ export async function startEngine(
|
|||
scene.render();
|
||||
});
|
||||
window.addEventListener("resize", () => engine.resize());
|
||||
|
||||
return scene;
|
||||
}
|
||||
|
|
62
src/index.ts
62
src/index.ts
|
@ -2,38 +2,38 @@ import { Dataspace, Embedded, Reader, Ref, Schemas, Sturdy } from "@syndicate-la
|
|||
import * as html from "@syndicate-lang/html";
|
||||
import * as wsRelay from "@syndicate-lang/ws-relay";
|
||||
import * as wakeDetector from './wake-detector.js';
|
||||
import * as Shapes from './gen/shapes.js';
|
||||
|
||||
import {
|
||||
Engine,
|
||||
HemisphericLight,
|
||||
Mesh,
|
||||
MeshBuilder,
|
||||
Scene,
|
||||
Vector3,
|
||||
} from '@babylonjs/core/Legacy/legacy';
|
||||
|
||||
import { box } from './shapes.js';
|
||||
import { attachTexture } from './interiors.js';
|
||||
import { startEngine } from './engine.js';
|
||||
import { uuid } from './uuid.js';
|
||||
import { activeFloorMeshes, ShapeTree } from './shapes.js';
|
||||
import { startEngine, CreatedScene } from './engine.js';
|
||||
|
||||
async function createScene(_canvas: HTMLCanvasElement, engine: Engine): Promise<{
|
||||
scene: Scene,
|
||||
floorMeshes: Mesh[]
|
||||
}> {
|
||||
const scene = new Scene(engine);
|
||||
assertion type SceneHandle(ds: Embedded<Ref>);
|
||||
|
||||
new HemisphericLight("light", new Vector3(0, 1, 0), scene);
|
||||
function interpretScene(scene: Scene, sceneDs: Ref) {
|
||||
at sceneDs {
|
||||
during Shapes.Sprite({ "name": $name: string }) => spawn named `sprite:${name}` {
|
||||
console.log('+shape', name);
|
||||
on stop console.log('-shape', name);
|
||||
spriteMain(name, scene, sceneDs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const b = attachTexture(box('b', 1, 1, 1, 0, 2, 2), scene, 'papenweg-textures/individual/floor1.jpg');
|
||||
|
||||
return {
|
||||
scene,
|
||||
floorMeshes: [
|
||||
b,
|
||||
MeshBuilder.CreateGround("ground", {width:30, height:30}),
|
||||
],
|
||||
};
|
||||
function spriteMain(name: string, scene: Scene, sceneDs: Ref) {
|
||||
at sceneDs {
|
||||
let currentShape = ShapeTree.empty(name, scene);
|
||||
on stop currentShape.remove();
|
||||
during Shapes.Sprite({ "name": name, "shape": $shape: Shapes.Shape }) => {
|
||||
console.log('=shape', name, shape);
|
||||
currentShape = currentShape.reconcile(name, shape);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function wsurl(): string {
|
||||
|
@ -41,13 +41,12 @@ function wsurl(): string {
|
|||
return `${scheme}://${document.location.host}/ws`;
|
||||
}
|
||||
|
||||
function bootApp(ds: Ref) {
|
||||
function bootApp(ds: Ref, scene: Scene) {
|
||||
spawn named 'app' {
|
||||
at ds {
|
||||
const url = wsurl();
|
||||
const serverCap = Sturdy.asSturdyRef(new Reader<Ref>(
|
||||
'<ref "syndicate" [] #[pkgN9TBmEd3Q04grVG4Zdw==]>').next());
|
||||
const this_instance = uuid();
|
||||
const relayAddr = wsRelay.RelayAddress(Schemas.transportAddress.WebSocket(url));
|
||||
|
||||
during wsRelay.Resolved({
|
||||
|
@ -60,17 +59,28 @@ function bootApp(ds: Ref) {
|
|||
on message wakeDetector.WakeEvent() => {
|
||||
send message wsRelay.ForceRelayDisconnect(relayAddr);
|
||||
}
|
||||
|
||||
at remoteDs {
|
||||
during SceneHandle($sceneDs_e: Embedded) => {
|
||||
const sceneDs = sceneDs_e.embeddedValue;
|
||||
interpretScene(scene, sceneDs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('load', async () => {
|
||||
await startEngine(createScene);
|
||||
const scene = await startEngine(
|
||||
async (_canvas: HTMLCanvasElement, engine: Engine): Promise<CreatedScene> => ({
|
||||
scene: new Scene(engine),
|
||||
floorMeshes: () => activeFloorMeshes,
|
||||
}));
|
||||
Dataspace.boot(ds => {
|
||||
html.boot(ds);
|
||||
wsRelay.boot(ds, true);
|
||||
wakeDetector.boot(ds);
|
||||
bootApp(ds);
|
||||
bootApp(ds, scene);
|
||||
});
|
||||
});
|
||||
|
|
194
src/shapes.ts
194
src/shapes.ts
|
@ -1,10 +1,25 @@
|
|||
import {
|
||||
AbstractMesh,
|
||||
Color3,
|
||||
CSG,
|
||||
HemisphericLight,
|
||||
Mesh,
|
||||
MeshBuilder,
|
||||
Node,
|
||||
PhotoDome,
|
||||
Quaternion,
|
||||
Scene,
|
||||
StandardMaterial,
|
||||
Texture,
|
||||
TransformNode,
|
||||
Vector2,
|
||||
Vector3,
|
||||
} from '@babylonjs/core/Legacy/legacy';
|
||||
import { Value, is } from "@syndicate-lang/core";
|
||||
|
||||
import * as Shapes from './gen/shapes.js';
|
||||
|
||||
export const activeFloorMeshes: Array<Mesh> = [];
|
||||
|
||||
export function adjust(f: (... csgs: CSG[]) => CSG, ... ms: Mesh[]): Mesh {
|
||||
const csgs = ms.map(m => CSG.FromMesh(m));
|
||||
|
@ -47,3 +62,182 @@ export function box(name: string, width: number, height: number, depth: number,
|
|||
b.position = new Vector3(x ?? 0, y ?? b.scaling.y/2, z ?? 0);
|
||||
return b;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
export class ShapeTree<N extends Node = Node> {
|
||||
shapePreserve: Value;
|
||||
cleanups: Array<() => void> = [];
|
||||
|
||||
constructor (
|
||||
public scene: Scene,
|
||||
public shape: Shapes.Shape,
|
||||
public node: N,
|
||||
) {
|
||||
this.shapePreserve = Shapes.fromShape(this.shape);
|
||||
if (this.node instanceof AbstractMesh) {
|
||||
this.node.checkCollisions = true;
|
||||
}
|
||||
}
|
||||
|
||||
reconcile(name: string, shape: Shapes.Shape): ShapeTree {
|
||||
if (is(Shapes.fromShape(shape), this.shapePreserve)) {
|
||||
return this;
|
||||
} else {
|
||||
this.remove();
|
||||
return build(name, this.scene, shape, {});
|
||||
}
|
||||
}
|
||||
|
||||
remove() {
|
||||
this.node?.dispose(false, true);
|
||||
}
|
||||
|
||||
static empty(name: string, scene: Scene): ShapeTree {
|
||||
return ShapeTree.transform(name, scene, Shapes.Shape.many([]));
|
||||
}
|
||||
|
||||
static transform(name: string, scene: Scene, shape: Shapes.Shape): ShapeTree<TransformNode> {
|
||||
return new ShapeTree(scene, shape, new TransformNode(name, scene));
|
||||
}
|
||||
}
|
||||
|
||||
export function v2(v: Shapes.Vector2): Vector2 {
|
||||
return new Vector2(v.x, v.y);
|
||||
}
|
||||
|
||||
export function v3(v: Shapes.Vector3): Vector3 {
|
||||
return new Vector3(v.x, v.y, v.z);
|
||||
}
|
||||
|
||||
export function q(q: Shapes.Quaternion): Quaternion {
|
||||
return new Quaternion(q.a, q.b, q.c, q.d);
|
||||
}
|
||||
|
||||
export type MeshCustomizer = { [key: string]: ((m: ShapeTree<Mesh>) => void) };
|
||||
|
||||
function applyCustomizer(m: ShapeTree<Mesh>, c: MeshCustomizer) {
|
||||
Object.values(c).forEach(f => f(m));
|
||||
}
|
||||
|
||||
export function build(name: string, scene: Scene, shape: Shapes.Shape, customize: MeshCustomizer): ShapeTree {
|
||||
switch (shape._variant) {
|
||||
case "Sphere": {
|
||||
const t = new ShapeTree<Mesh>(scene, shape, MeshBuilder.CreateSphere(name, {}, scene));
|
||||
applyCustomizer(t, customize);
|
||||
return t;
|
||||
}
|
||||
|
||||
case "Box": {
|
||||
const t = new ShapeTree<Mesh>(scene, shape, MeshBuilder.CreateBox(name, {}, scene));
|
||||
applyCustomizer(t, customize);
|
||||
return t;
|
||||
}
|
||||
|
||||
case "Light":
|
||||
return new ShapeTree(
|
||||
scene,
|
||||
shape,
|
||||
new HemisphericLight(name, v3(shape.value.v), scene));
|
||||
|
||||
case "Ground": {
|
||||
const v = v2(shape.value.size);
|
||||
const t = new ShapeTree(
|
||||
scene,
|
||||
shape,
|
||||
MeshBuilder.CreateGround(name, { width: v.x, height: v.y }, scene));
|
||||
applyCustomizer(t, customize);
|
||||
return t;
|
||||
}
|
||||
|
||||
case "Scale": {
|
||||
const t = ShapeTree.transform(name, scene, shape);
|
||||
t.node.scaling = v3(shape.value.v);
|
||||
build(name + '.inner', scene, shape.value.shape, customize).node.parent = t.node;
|
||||
return t;
|
||||
}
|
||||
|
||||
case "Move": {
|
||||
const t = ShapeTree.transform(name, scene, shape);
|
||||
t.node.position = v3(shape.value.v);
|
||||
build(name + '.inner', scene, shape.value.shape, customize).node.parent = t.node;
|
||||
return t;
|
||||
}
|
||||
|
||||
case "Rotate": {
|
||||
const t = ShapeTree.transform(name, scene, shape);
|
||||
switch (shape.value._variant) {
|
||||
case "euler":
|
||||
t.node.rotation = v3(shape.value.v);
|
||||
break;
|
||||
case "quaternion":
|
||||
t.node.rotationQuaternion = q(shape.value.q);
|
||||
break;
|
||||
}
|
||||
build(name + '.inner', scene, shape.value.shape, customize).node.parent = t.node;
|
||||
return t;
|
||||
}
|
||||
|
||||
case "many": {
|
||||
const t = ShapeTree.transform(name, scene, shape);
|
||||
shape.value.forEach((s, i) => {
|
||||
build(name + '[' + i + ']', scene, s, customize).node.parent = t.node;
|
||||
});
|
||||
return t;
|
||||
}
|
||||
|
||||
case "Texture": {
|
||||
const mat = new StandardMaterial(name + '.texture', scene);
|
||||
const tex = new Texture(shape.value.path, scene);
|
||||
mat.diffuseTexture = tex;
|
||||
switch (shape.value._variant) {
|
||||
case "simple":
|
||||
break;
|
||||
case "uv": {
|
||||
const scale = v2(shape.value.scale);
|
||||
const offset = v2(shape.value.offset);
|
||||
tex.uScale = 1 / scale.x;
|
||||
tex.vScale = 1 / scale.y;
|
||||
tex.uOffset = offset.x;
|
||||
tex.vOffset = offset.y;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return build(name + '.inner', scene, shape.value.shape, {
|
||||
... customize,
|
||||
material: m => m.node.material = mat,
|
||||
});
|
||||
}
|
||||
|
||||
case "Color": {
|
||||
const mat = new StandardMaterial(name + '.texture', scene);
|
||||
mat.diffuseColor = new Color3(shape.value.r, shape.value.g, shape.value.b);
|
||||
if (shape.value._variant === "transparent") mat.alpha = shape.value.alpha;
|
||||
return build(name + '.inner', scene, shape.value.shape, {
|
||||
... customize,
|
||||
material: m => m.node.material = mat,
|
||||
});
|
||||
}
|
||||
|
||||
case "Name":
|
||||
return build(name + '.' + shape.value.base, scene, shape.value.shape, customize);
|
||||
|
||||
case "Floor":
|
||||
return build(name, scene, shape.value.shape, {
|
||||
... customize,
|
||||
floor: m => {
|
||||
activeFloorMeshes.push(m.node);
|
||||
m.cleanups.push(() => {
|
||||
const i = activeFloorMeshes.indexOf(m.node);
|
||||
if (i !== -1) activeFloorMeshes.splice(i, 1);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
default:
|
||||
((_shape: never) => {
|
||||
console.error('Unsupported shape variant', shape);
|
||||
throw new Error("Unsupported shape variant");
|
||||
})(shape);
|
||||
}
|
||||
}
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 61 KiB |
|
@ -12,6 +12,7 @@
|
|||
"module": "es6",
|
||||
"sourceMap": true,
|
||||
"strict": true,
|
||||
"incremental": true,
|
||||
"plugins": [
|
||||
{ "name": "@syndicate-lang/ts-plugin" }
|
||||
]
|
||||
|
|
22
yarn.lock
22
yarn.lock
|
@ -3,21 +3,21 @@
|
|||
|
||||
|
||||
"@babylonjs/core@5":
|
||||
version "5.39.0"
|
||||
resolved "https://registry.yarnpkg.com/@babylonjs/core/-/core-5.39.0.tgz#8343fdc2ef105005bc564fc98bd36008e4301d08"
|
||||
integrity sha512-bGDsxFbIq9GeiwpojdnQB58s1EvDxJSzUK5C8hL3Wj8UqK6tCELNTEHonmJ8KruilLGQRpm2mrrPZWEzUvAKkA==
|
||||
version "5.41.0"
|
||||
resolved "https://registry.yarnpkg.com/@babylonjs/core/-/core-5.41.0.tgz#ebc98f9d338d5dcbb4e81fcd3b6d515419532bca"
|
||||
integrity sha512-PrY12n9IOql+9P/bFhEI7WTUqneTI0W9+ROKkwallqtTYku3XV7O5E7BXpdLJwrB/VufKApu6ErNoUb9Zhj9Cg==
|
||||
|
||||
"@preserves/core@*", "@preserves/core@>=0.20.2", "@preserves/core@^0.20.4":
|
||||
version "0.20.4"
|
||||
resolved "https://registry.yarnpkg.com/@preserves/core/-/core-0.20.4.tgz#b964d31c291a489c2682b5cf35f29b6677885000"
|
||||
integrity sha512-4XSQwcbn66LQWl8ympnhumWboZiMGW3bIQmvSx4Bt04v3m1vtiJz/Pw/mltTgPahWWs/f6ADAZoNywD4w6aY0Q==
|
||||
"@preserves/core@*", "@preserves/core@>=0.20.2", "@preserves/core@^0.20.5":
|
||||
version "0.20.5"
|
||||
resolved "https://registry.yarnpkg.com/@preserves/core/-/core-0.20.5.tgz#3b34693b1f5aff659639690725a703a95689f822"
|
||||
integrity sha512-hnywtmY30swKSuHix3MbEoheyCh6plCERzmhnCQllrkhdT0hFm66iSr2PVjNsXAUNIwd3Rh3Vu2btgdjMK+Q9Q==
|
||||
|
||||
"@preserves/schema@*", "@preserves/schema@>=0.21.2":
|
||||
version "0.21.6"
|
||||
resolved "https://registry.yarnpkg.com/@preserves/schema/-/schema-0.21.6.tgz#572a2731712a6e503ad218639ef58099a61f2956"
|
||||
integrity sha512-0lxqEN5qJDu/Ez84gpZxD3/XHF8LzrBcx2WMfRIqOGl4L4067l4N+xM01YNRTHjiY53weT3xyoWAizlqow5fBg==
|
||||
version "0.21.7"
|
||||
resolved "https://registry.yarnpkg.com/@preserves/schema/-/schema-0.21.7.tgz#09e388b8c582c3dc95018ebdfe990381dddabfa8"
|
||||
integrity sha512-q2gncEOOY3qqs+i+Op5yZx89cckqN601vbEcBDfnQge5PB9HrOrfMk68Wq4w/rpdQpRyVqWhZNxLlKsANjgoOw==
|
||||
dependencies:
|
||||
"@preserves/core" "^0.20.4"
|
||||
"@preserves/core" "^0.20.5"
|
||||
"@types/glob" "^7.1"
|
||||
"@types/minimatch" "^3.0"
|
||||
chalk "^4.1"
|
||||
|
|
Loading…
Reference in New Issue